Skip to content

Commit 7b23434

Browse files
authored
Merge pull request cve-search#289 from Alexandre-Bartel/upto-search-option
New parameter for 'lax' search
2 parents 5021426 + e0cf040 commit 7b23434

File tree

2 files changed

+95
-20
lines changed

2 files changed

+95
-20
lines changed

bin/search.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
# parse command-line arguments
4646
argParser = argparse.ArgumentParser(description='Search for vulnerabilities in the National Vulnerability DB. Data from http://nvd.nist.org.')
4747
argParser.add_argument('-p', type=str, help='S = search product, e.g. o:microsoft:windows_7 or o:cisco:ios:12.1')
48+
argParser.add_argument('--lax', default=False, action='store_true', help='Strict search for software version is disabled. Note that this option only support product description with numerical values only (of the form cisco:ios:1.2.3) ')
4849
argParser.add_argument('-f', type=str, help='F = free text search in vulnerability summary')
4950
argParser.add_argument('-c', action='append', help='search one or more CVE-ID')
5051
argParser.add_argument('-o', type=str, help='O = output format [csv|html|json|xml|cveid]')
@@ -59,6 +60,7 @@
5960
args = argParser.parse_args()
6061

6162
vSearch = args.p
63+
relaxSearch = args.lax
6264
cveSearch = [x.upper() for x in args.c] if args.c else None
6365
vOutput = args.o
6466
vFreeSearch = args.f
@@ -150,7 +152,7 @@ def printCVE_html(item):
150152
print("Ranking:<br>")
151153
for ra in ranking:
152154
for e in ra:
153-
for i in e:
155+
for i in e:
154156
print( i + ": " + str(e[i])+"<br>")
155157
print("<hr><hr>")
156158

@@ -184,7 +186,7 @@ def printCVE_csv(item):
184186
ranking_="[No Ranking Found]"
185187
else:
186188
ranking_ = " ".join(ranking_)
187-
189+
188190
csvoutput = csv.writer(sys.stdout, delimiter='|', quotechar='|', quoting=csv.QUOTE_MINIMAL)
189191
if not rankinglookup:
190192
if not namelookup:
@@ -196,8 +198,8 @@ def printCVE_csv(item):
196198
csvoutput.writerow([item['id'], str(item['Published']), item['cvss'], item['summary'], refs,ranking_])
197199
else:
198200
csvoutput.writerow([item['id'], str(item['Published']), item['cvss'], item['summary'], refs, nl,ranking_ ])
199-
200-
201+
202+
201203
def printCVE_xml(item):
202204
c = SubElement(r, 'id')
203205
c.text = item['id']
@@ -210,7 +212,7 @@ def printCVE_xml(item):
210212
for e in item['references']:
211213
c = SubElement(r, 'references')
212214
c.text = SaxEscape(e)
213-
ranking=[]
215+
ranking=[]
214216
for e in item['vulnerable_configuration']:
215217
c = SubElement(r, 'vulnerable_configuration')
216218
c.text = SaxEscape(e)
@@ -223,7 +225,7 @@ def printCVE_xml(item):
223225
for e in ra:
224226
for i in e:
225227
c = SubElement(r, i)
226-
c.text =str(e[i])
228+
c.text =str(e[i])
227229

228230
def printCVE_id(item):
229231
print(item['id'])
@@ -241,7 +243,7 @@ def printCVE_human(item):
241243
print("-------------------")
242244
ranking=[]
243245
for entry in item['vulnerable_configuration']:
244-
246+
245247
if not namelookup:
246248
print(entry)
247249
else:
@@ -255,7 +257,7 @@ def printCVE_human(item):
255257
print("--------")
256258
for ra in ranking:
257259
for e in ra:
258-
for i in e:
260+
for i in e:
259261
print( i + ": " + str(e[i]))
260262
print("\n\n")
261263

@@ -301,7 +303,7 @@ def search_in_summary(item):
301303
# Search Product (best to use CPE notation, e.g. cisco:ios:12.2
302304
if vSearch:
303305

304-
for item in db.cvesForCPE(vSearch):
306+
for item in db.cvesForCPE(vSearch, lax=relaxSearch):
305307
if not last_ndays:
306308
if csvOutput:
307309
printCVE_csv(item)
@@ -319,7 +321,7 @@ def search_in_summary(item):
319321
printCVE_human(item)
320322
else:
321323
date_n_days_ago = datetime.now() - timedelta(days=last_ndays)
322-
if item['Published'] > date_n_days_ago:
324+
if item['Published'] > date_n_days_ago:
323325

324326
if csvOutput:
325327
printCVE_csv(item)
@@ -339,7 +341,7 @@ def search_in_summary(item):
339341
print("</body></html>")
340342
sys.exit(0)
341343

342-
# Search text in summary
344+
# Search text in summary
343345
if summary_text:
344346
import lib.CVEs as cves
345347

@@ -359,13 +361,13 @@ def search_in_summary(item):
359361
if vOutput:
360362
printCVE_id(item)
361363
else:
362-
print(json.dumps(item, sort_keys=True, default=json_util.default))
364+
print(json.dumps(item, sort_keys=True, default=json_util.default))
363365
else:
364366

365367
date_n_days_ago = datetime.now() - timedelta(days=last_ndays)
366368
# print(item['Published'])
367369
# print(type (item['Published']))
368-
# print("Last n day " +str(last_ndays))
370+
# print("Last n day " +str(last_ndays))
369371
try:
370372
if datetime.strptime(item['Published'], '%Y-%m-%d %H:%M:%S.%f') > date_n_days_ago:
371373
if vOutput:
@@ -386,4 +388,4 @@ def search_in_summary(item):
386388

387389
else:
388390
argParser.print_help()
389-
argParser.exit()
391+
argParser.exit()

lib/DatabaseLayer.py

+79-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33
#
4-
# Database layer
5-
# translates database calls to functions
4+
# Database layer translates database calls to functions
65
#
76
# Software is free software released under the "Modified BSD license"
87
#
98

10-
# Copyright (c) 2014-2016 Pieter-Jan Moreels - [email protected]
9+
# Copyright (c) 2014-2016 Pieter-Jan Moreels -
10+
1111

1212
# imports
1313
import ast
@@ -86,10 +86,83 @@ def dropCollection(col):
8686
def getTableNames():
8787
return db.collection_names()
8888

89+
# returns True if 'target_version' is less or equal than
90+
# 'cpe_version'
91+
# returns False otherwise
92+
def target_version_is_included(target_version, cpe_version):
93+
sp_target = target_version.split(".")
94+
sp_cpe = cpe_version.split(".")
95+
if len(sp_target) > len(sp_cpe):
96+
sp_cpe += [0]*(len(sp_target) - len(sp_cpe))
97+
if len(sp_cpe) > len(sp_target):
98+
sp_cpe += [0]*(len(sp_cpe) - len(sp_target))
99+
for i in range(len(sp_target)):
100+
# target version smaller than cpe version
101+
if int(sp_target[i]) < int(sp_cpe[i]):
102+
return True
103+
# target version greater than cpe version
104+
if int(sp_target[i]) > int(sp_cpe[i]):
105+
return False
106+
# target version same version as cpe version
107+
return True
108+
109+
89110
# API Functions
90-
def cvesForCPE(cpe):
111+
def cvesForCPE(cpe, lax=False):
91112
if not cpe: return []
92-
return sanitize(colCVE.find({"vulnerable_configuration": {"$regex": cpe}}).sort("Modified", -1))
113+
cpe_regex = cpe
114+
final_cves = []
115+
if lax:
116+
# get target version from product description provided by the user
117+
target_version = cpe.split(":")[-1]
118+
product = cpe.rsplit(":", 1)[0]
119+
# perform checks on the target version
120+
if None is target_version or [] is target_version:
121+
print ("Error, target version not found at the end of product description '{}'".format(cpe))
122+
sys.exit(-1)
123+
for i in target_version.split("."):
124+
try:
125+
int(i)
126+
except:
127+
print ("Error, target version should be of the form '1.2.3'. Current form is '{}'".format(target_version))
128+
sys.exit(-1)
129+
130+
# over-approximate versions
131+
final_cves = []
132+
cpe_regex = product
133+
cves = colCVE.find({"vulnerable_configuration": {"$regex": cpe_regex}}).sort("Modified", -1)
134+
i = 0
135+
for cve in cves:
136+
vuln_confs = cve["vulnerable_configuration"]
137+
vuln_confs += cve["vulnerable_configuration_cpe_2_2"]
138+
i += 1
139+
for vc in vuln_confs:
140+
if not cpe_regex in vc:
141+
continue
142+
143+
re_from_start = re.compile("^.*{}:".format(cpe_regex))
144+
cpe_version = re_from_start.sub("", vc)
145+
146+
# TODO: handle versions such as "1.1.3:p2"
147+
cpe_version = cpe_version.split(":")[0]
148+
149+
# TODO: handle versions such as "1.1.3p2"
150+
cpe_version = re.search("([0-9\.]*)", cpe_version).group(0)
151+
if len(cpe_version) is 0:
152+
# TODO: print warnings
153+
#print ("Warning, missing cpe version for {}: '{}'. Skipping cpe.".format(cve["id"], vc))
154+
continue
155+
if target_version_is_included(target_version, cpe_version):
156+
final_cves.append(cve)
157+
break
158+
159+
160+
else:
161+
# default strict search
162+
cves = colCVE.find({"vulnerable_configuration": {"$regex": cpe_regex}}).sort("Modified", -1)
163+
final_cves = cves
164+
165+
return sanitize(final_cves)
93166

94167
# User Functions
95168
def addUser(user, pwd, admin=False, localOnly=False):
@@ -406,7 +479,7 @@ def p_addToList(collection, query, listname, data):
406479
db['plug_%s'%collection].update(query, {"$addToSet": {listname: {"$each": data}}})
407480

408481
def p_removeFromList(collection, query, listname, data):
409-
if type(data) == dict:
482+
if type(data) == dict:
410483
db['plug_%s'%collection].update(query, {"$pull": {listname: data}})
411484
elif type(data) != list: data=[data]
412485
db['plug_%s'%collection].update(query, {"$pullAll": {listname: data}})

0 commit comments

Comments
 (0)