diff --git a/bin/db_dump.py b/bin/db_dump.py
index e4d1eaa17..3c57dd18c 100755
--- a/bin/db_dump.py
+++ b/bin/db_dump.py
@@ -16,7 +16,7 @@
import json
from bson import json_util
-from lib import CVEs
+import lib.CVEs as cves
from lib.Config import Configuration
# connect to db
@@ -40,7 +40,7 @@ def dumpallcveid():
vfeedlookup = args.v
capeclookup = args.c
-l = CVEs.last(rankinglookup=rankinglookup, vfeedlookup=vfeedlookup, capeclookup=capeclookup)
+l = cves.last(rankinglookup=rankinglookup, vfeedlookup=vfeedlookup, capeclookup=capeclookup)
for cveid in dumpallcveid():
item = l.getcve(cveid=cveid)
diff --git a/bin/dump_last.py b/bin/dump_last.py
index a96a117ea..70e5cab32 100755
--- a/bin/dump_last.py
+++ b/bin/dump_last.py
@@ -18,7 +18,7 @@
import datetime
import argparse
-import lib.CVEs
+import lib.CVEs as cves
argParser = argparse.ArgumentParser(description='Dump last CVE entries in RSS/Atom format or in HTML tables')
argParser.add_argument('-f', type=str, help='Output format (rss1,rss2,atom,html)', default='rss1')
@@ -35,7 +35,7 @@
last = 10
ref = "http://adulau.github.com/cve-search/"
-cves = CVEs.last(rankinglookup=args.r, namelookup=args.n, capeclookup=args.c)
+cvelist = cves.last(rankinglookup=args.r, namelookup=args.n, capeclookup=args.c)
if not(args.f == "html"):
from feedformatter import Feed
@@ -50,7 +50,7 @@
print ("")
print ("
Last " + str(args.l) + " CVE entries")
print ("")
-for x in cves.get(limit=last):
+for x in cvelist.get(limit=last):
if not(args.f == "html"):
if args.r:
if "ranking" not in x:
@@ -82,7 +82,7 @@
print ("
Vulnerable configuration:
")
print ("
")
for v in x['vulnerable_configuration']:
- sys.stdout.write("
" + cves.getcpe(v) + "
")
+ sys.stdout.write("
" + cvelist.getcpe(v) + "
")
print ("
")
if args.r:
print ("
Ranking:" + str(x['ranking']) + "
")
diff --git a/bin/search_irc.py b/bin/search_irc.py
new file mode 100644
index 000000000..f2150d347
--- /dev/null
+++ b/bin/search_irc.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Simple IRC bot to query for the last entries in the CVE database
+#
+# current command supported is:
+#
+# last
+# cvetweet
+# browse
+# search \
+# get
+#
+# You need to connect the IRC bot to the IRC Server you want to access it from.
+#
+# Software is free software released under the "Modified BSD license"
+#
+# Copyright (c) 2015 Pieter-Jan Moreels - pieterjan.moreels@gmail.com
+
+# Imports
+import os
+import sys
+runPath = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(runPath, ".."))
+
+import argparse
+import json
+# BSON MongoDB include ugly stuff that needs to be processed for standard JSON
+from bson import json_util
+
+import irc.bot
+import irc.strings
+
+from lib.Query import lastentries, apigetcve, apibrowse, apisearch
+
+argParser = argparse.ArgumentParser(description='IRC bot to query cve-search')
+argParser.add_argument('-s', type=str, help='server ip', default='localhost')
+argParser.add_argument('-p', type=int, help='server port)', default=6667)
+argParser.add_argument('-n', type=str, help='nickname', default='cve-search')
+argParser.add_argument('-w', type=str, help='password')
+argParser.add_argument('-u', type=str, help='username', default='cve-search')
+argParser.add_argument('-c', nargs="*", help='channel list', default=['cve-search'])
+argParser.add_argument('-t', type=str, help='trigger prefix', default='.')
+argParser.add_argument('-v', action='store_true', help='channel list', default=['cve-search'])
+argParser.add_argument('-m', type=int, help='maximum query amount', default=20)
+args = argParser.parse_args()
+
+class IRCBot(irc.bot.SingleServerIRCBot):
+ def __init__(self, channel, nickname, server, port, password=None, username=None):
+ if not username:
+ username=nickname
+ irc.bot.SingleServerIRCBot.__init__(self, [(server, port)], nickname, username)
+ self.channel = channel
+
+ def on_nicknameinuse(self, c, e):
+ c.nick(c.get_nickname() + "_")
+
+ def on_welcome(self, c, e):
+ if args.v:
+ print("Server welcomed us")
+ for chan in self.channel:
+ if not chan.startswith('#'):chan=("#%s"%chan)
+ if args.v:
+ print("joining %s"%chan)
+ c.join(chan)
+
+ def on_privmsg(self, c, e):
+ self.do_command(e, e.arguments[0])
+
+ def on_pubmsg(self, c, e):
+ line = e.arguments[0]
+ if line.startswith(args.t):
+ self.do_command(e, line[len(args.t):])
+ return
+
+ def reply(self, e, reply):
+ c = self.connection
+ if e.target == c.nickname:
+ target=e.source.nick
+ else:
+ target=e.target
+ list = reply.split('\n')
+ for r in list:
+ c.privmsg(target, r)
+
+ def do_command(self, e, cmd):
+ words = cmd.split(' ')
+ if len(words)>=2:
+ cmd=words[0]
+ option=words[1]
+ else:
+ option=None
+
+ if cmd == "die":
+ self.die()
+ elif cmd == "last":
+ if option is None:
+ limit = 10
+ else:
+ limit = int(option)
+ if limit > args.m or limit < 1:
+ self.reply(e, "Request not in range 0-%d" % args.m)
+ self.reply(e, json.dumps(lastentries(limit=limit), sort_keys=True, indent=4, default=json_util.default))
+ elif cmd == "get":
+ if option is None:
+ self.reply(e, "A cve-id must be specified")
+ self.reply(e, apigetcve(cveid=option))
+ elif cmd == "browse":
+ self.reply(e, apibrowse(vendor=option))
+ elif cmd == "search":
+ self.reply(e, apisearch(query=option))
+ elif cmd == "cvetweet":
+ text = " "
+ if option is None:
+ limit = 10
+ else:
+ limit = int(option)
+ if limit > args.m or limit < 1:
+ return "Request not in range 0-%d" % args.m
+ for t in lastentries(limit=limit):
+ text = text + str(t['id']) + " , " + str(t['summary']) + " " + " , ".join(t['references']) + "\n"
+ self.reply(e, text)
+ elif cmd == "browse":
+ self.reply(e, apibrowse(vendor=option))
+
+ else:
+ self.reply(e, "Not understood: " + cmd)
+
+import signal
+
+# signal handlers
+def sig_handler(sig, frame):
+ print('Caught signal: %s\nShutting down' % sig)
+ bot.die()
+
+def main():
+ server = args.s
+ port = args.p
+ nick = args.n
+ password = args.w
+ user = args.u
+ chans = args.c
+ global bot
+ bot=IRCBot(chans, nick, server, port, password=password,username=user)
+ signal.signal(signal.SIGTERM, sig_handler)
+ signal.signal(signal.SIGINT, sig_handler)
+ if args.v:
+ print("Connecting to server")
+ bot.start()
+
+if __name__ == "__main__":
+ main()
diff --git a/bin/search_xmpp.py b/bin/search_xmpp.py
index a9ce350c5..2fae19e49 100755
--- a/bin/search_xmpp.py
+++ b/bin/search_xmpp.py
@@ -3,9 +3,13 @@
#
# Simple XMPP bot to query for the last entries in the CVE database
#
-# current command supported is:
+# current commands supported are:
#
# last
+# cvetweet
+# browse
+# search \
+# get
#
# You need to add the XMPP bot in your roster if you want to communicate
# with it.
@@ -26,10 +30,9 @@
from optparse import OptionParser
import sleekxmpp
import json
-import requests
-import urllib.parse
from lib.Config import Configuration
+from lib.Query import lastentries, apigetcve, apibrowse, apisearch
# BSON MongoDB include ugly stuff that needs to be processed for standard JSON
from bson import json_util
@@ -54,64 +57,6 @@
helpmessage = helpmessage + "For more info about cve-search: http://adulau.github.com/cve-search/"
-def lookupcpe(cpeid=None):
- e = db.cpe.find_one({'id': cpeid})
- if e is None:
- return cpeid
- if 'id' in e:
- return e['title']
-
-
-def findranking(cpe=None, loosy=True):
- if cpe is None:
- return False
- r = db.ranking
- result = False
- if loosy:
- for x in cpe.split(':'):
- if x is not '':
- i = r.find_one({'cpe': {'$regex': x}})
- if i is None:
- continue
- if 'rank' in i:
- result = i['rank']
- else:
- i = r.find_one({'cpe': {'$regex': cpe}})
- print (cpe)
- if i is None:
- return result
- if 'rank' in i:
- result = i['rank']
-
- return result
-
-
-def lastentries(limit=5, namelookup=False):
- entries = []
- for item in collection.find({}).sort("Modified", -1).limit(limit):
- if not namelookup and rankinglookup is not True:
- entries.append(item)
- else:
- if "vulnerable_configuration" in item:
- vulconf = []
- ranking = []
- for conf in item['vulnerable_configuration']:
- if namelookup:
- vulconf.append(lookupcpe(cpeid=conf))
- else:
- vulconf.append(conf)
- if rankinglookup:
- rank = findranking(cpe=conf)
- if rank and rank not in ranking:
- ranking.append(rank)
-
- item['vulnerable_configuration'] = vulconf
- if rankinglookup:
- item['ranking'] = ranking
- entries.append(item)
- return entries
-
-
def cvesearch(query="last", option=None):
if query == "last":
if option is None:
@@ -124,11 +69,11 @@ def cvesearch(query="last", option=None):
elif query == "get":
if option is None:
return "A cve-id must be specified"
- return apigetcve(cveid=option)
+ return apigetcve(opts.api,cveid=option)
elif query == "browse":
- return apibrowse(vendor=option)
+ return apibrowse(opts.api, vendor=option)
elif query == "search":
- return apisearch(query=option)
+ return apisearch(opts.api, query=option)
elif query == "cvetweet":
text = " "
@@ -146,46 +91,6 @@ def cvesearch(query="last", option=None):
else:
return False
-
-def apigetcve(cveid=None):
- if cveid is None:
- return False
- url = urllib.parse.urljoin(opts.api, "api/cve/"+cveid)
- urltoget = urllib.parse.urljoin(url, cveid)
- r = requests.get(urltoget)
- if r.status_code is 200:
- return r.text
- else:
- return False
-
-
-def apibrowse(vendor=None):
- url = urllib.parse.urljoin(opts.api, "api/browse")
- if vendor is None:
- r = requests.get(url)
- else:
- urlvendor = url + "/" + vendor
- r = requests.get(urlvendor)
-
- if r.status_code is 200:
- return r.text
- else:
- return False
-
-
-def apisearch(query=None):
- if query is None:
- return False
- url = urllib.parse.urljoin(opts.api, "api/search/")
- url = url+query
-
- r = requests.get(url)
- if r.status_code is 200:
- return r.text
- else:
- return False
-
-
class CVEBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password):
diff --git a/lib/Query.py b/lib/Query.py
new file mode 100644
index 000000000..eea0d5353
--- /dev/null
+++ b/lib/Query.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3.3
+# -*- coding: utf-8 -*-
+#
+# Query tools
+#
+# Software is free software released under the "Modified BSD license"
+#
+# Copyright (c) 2014-2015 Pieter-Jan Moreels - pieterjan.moreels@gmail.com
+
+import urllib.parse
+import json
+import requests
+
+from lib.Config import Configuration
+
+
+db = Configuration.getMongoConnection()
+collection = db.cves
+
+rankinglookup = True
+
+
+def findranking(cpe=None, loosy=True):
+ if cpe is None:
+ return False
+ r = db.ranking
+ result = False
+ if loosy:
+ for x in cpe.split(':'):
+ if x is not '':
+ i = r.find_one({'cpe': {'$regex': x}})
+ if i is None:
+ continue
+ if 'rank' in i:
+ result = i['rank']
+ else:
+ i = r.find_one({'cpe': {'$regex': cpe}})
+ print (cpe)
+ if i is None:
+ return result
+ if 'rank' in i:
+ result = i['rank']
+ return result
+
+def lookupcpe(cpeid=None):
+ e = db.cpe.find_one({'id': cpeid})
+ if e is None:
+ return cpeid
+ if 'id' in e:
+ return e['title']
+
+
+def lastentries(limit=5, namelookup=False):
+ entries = []
+ for item in collection.find({}).sort("Modified", -1).limit(limit):
+ if not namelookup and rankinglookup is not True:
+ entries.append(item)
+ else:
+ if "vulnerable_configuration" in item:
+ vulconf = []
+ ranking = []
+ for conf in item['vulnerable_configuration']:
+ if namelookup:
+ vulconf.append(lookupcpe(cpeid=conf))
+ else:
+ vulconf.append(conf)
+ if rankinglookup:
+ rank = findranking(cpe=conf)
+ if rank and rank not in ranking:
+ ranking.append(rank)
+ item['vulnerable_configuration'] = vulconf
+ if rankinglookup:
+ item['ranking'] = ranking
+ entries.append(item)
+ return entries
+
+def apigetcve(api, cveid=None):
+ if cveid is None:
+ return False
+ url = urllib.parse.urljoin(api, "api/cve/"+cveid)
+ urltoget = urllib.parse.urljoin(url, cveid)
+ r = requests.get(urltoget)
+ if r.status_code is 200:
+ return r.text
+ else:
+ return False
+
+def apibrowse(api, vendor=None):
+ url = urllib.parse.urljoin(api, "api/browse")
+ if vendor is None:
+ r = requests.get(url)
+ else:
+ urlvendor = url + "/" + vendor
+ r = requests.get(urlvendor)
+
+ if r.status_code is 200:
+ return r.text
+ else:
+ return False
+
+def apisearch(api, query=None):
+ if query is None:
+ return False
+ url = urllib.parse.urljoin(api, "api/search/")
+ url = url+query
+
+ r = requests.get(url)
+ if r.status_code is 200:
+ return r.text
+ else:
+ return False
diff --git a/lib/Toolkit.py b/lib/Toolkit.py
index a56868e74..f92470a50 100644
--- a/lib/Toolkit.py
+++ b/lib/Toolkit.py
@@ -8,6 +8,11 @@
# Copyright (c) 2014-2015 Pieter-Jan Moreels - pieterjan.moreels@gmail.com
# Imports
+from dateutil import tz
+import dateutil.parser
+import time
+import re
+
# Note of warning: CPEs like cpe:/o:microsoft:windows_8:-:-:x64 are given to us by Mitre
# x64 will be parsed as Edition in this case, not Architecture
def toStringFormattedCPE(cpe,autofill=False):
@@ -26,14 +31,37 @@ def toStringFormattedCPE(cpe,autofill=False):
cpe+=':-'
return cpe
+# Note of warning: Old CPE's can come in different formats, and are not uniform. Possibilities are:
+# cpe:/a:7-zip:7-zip:4.65::~~~~x64~
+# cpe:/a:7-zip:7-zip:4.65:-:~~~~x64~
+# cpe:/a:7-zip:7-zip:4.65:-:~-~-~-~x64~
+def toOldCPE(cpe):
+ cpe=cpe.strip()
+ if not cpe.startswith('cpe:/'):
+ if not cpe.startswith('cpe:2.3:'): return False
+ cpe=cpe.replace('cpe:2.3:','')
+ parts = cpe.split(':')
+ next = []
+ first= "cpe:/"+":".join(parts[:5])
+ last = parts[5:]
+ if last:
+ for x in last:
+ next.append('~') if x == "-" else next.append(x)
+ if "~" in next:
+ pad(next,6,"~")
+ cpe="%s:%s"%(first,"".join(next))
+ cpe=cpe.replace(':-:','::')
+ cpe=cpe.strip(":")
+ return cpe
+
def impactScore(cve):
score={'NONE':0,'PARTIAL':0.275,'COMPLETE':0.660}
try:
C=((cve['impact'])['confidentiality']).upper()
I=((cve['impact'])['integrity']).upper()
A=((cve['impact'])['availability']).upper()
- print(A)
- return 10.41*(1-(1-score[C])*(1-score[I])*(1-score[A]))
+ res = 10.41*(1-(1-score[C])*(1-score[I])*(1-score[A]))
+ return 10.0 if res > 10.0 else res
except Exception as ex:
print(ex)
return '-'
@@ -56,3 +84,41 @@ def pad(seq, target_length, padding=None):
return seq
seq.extend([padding] * (target_length - length))
return seq
+
+def currentTime(utc):
+ timezone = tz.tzlocal()
+ utc = dateutil.parser.parse(utc)
+ output = utc.astimezone(timezone)
+ output = output.strftime('%d-%m-%Y - %H:%M')
+ return output
+
+def isURL(string):
+ urlTypes= [re.escape(x) for x in ['http://','https://', 'www.']]
+ return re.match("^(" + "|".join(urlTypes) + ")", string)
+
+def vFeedName(string):
+ string=string.replace('map_','')
+ string=string.replace('cve_','')
+ return string.title()
+
+def convertDateToDBFormat(string):
+ result = None
+ try:
+ result = time.strptime(string, "%d-%m-%Y")
+ except:
+ pass
+ try:
+ result = time.strptime(string, "%d-%m-%y")
+ except:
+ pass
+ try:
+ result = time.strptime(string, "%d/%m/%Y")
+ except:
+ pass
+ try:
+ result = time.strptime(string, "%d/%m/%y")
+ except:
+ pass
+ if result is not None:
+ result = time.strftime('%Y-%m-%d', result)
+ return result
diff --git a/bin/db_blacklist.py b/sbin/db_blacklist.py
similarity index 100%
rename from bin/db_blacklist.py
rename to sbin/db_blacklist.py
diff --git a/bin/db_cpe_browser.py b/sbin/db_cpe_browser.py
similarity index 100%
rename from bin/db_cpe_browser.py
rename to sbin/db_cpe_browser.py
diff --git a/bin/db_fulltext.py b/sbin/db_fulltext.py
similarity index 93%
rename from bin/db_fulltext.py
rename to sbin/db_fulltext.py
index c475d49af..88ba4ec7a 100755
--- a/bin/db_fulltext.py
+++ b/sbin/db_fulltext.py
@@ -22,8 +22,9 @@
import argparse
-from lib import CVEs
+import lib.CVEs as cves
from lib.Config import Configuration
+from lib.ProgressBar import progressbar
# connect to db
db = Configuration.getMongoConnection()
@@ -35,7 +36,7 @@
argParser.add_argument('-n', action='store_true', default=False, help='lookup complete cpe (Common Platform Enumeration) name for vulnerable configuration to add in the index')
args = argParser.parse_args()
-c = CVEs.last(namelookup=args.n)
+c = cves.last(namelookup=args.n)
indexpath = Configuration.getIndexdir()
@@ -69,7 +70,7 @@ def getcve(cveid=None):
return False
return collection.find_one({'id': cveid})
-for cveid in dumpallcveid(entry=args.l):
+for cveid in progressbar(dumpallcveid(entry=args.l),prefix="Processing"):
writer = ix.writer()
item = getcve(cveid=cveid)
title = item['summary'][0:70]
diff --git a/bin/db_mgmt.py b/sbin/db_mgmt.py
similarity index 100%
rename from bin/db_mgmt.py
rename to sbin/db_mgmt.py
diff --git a/bin/db_mgmt_admin.py b/sbin/db_mgmt_admin.py
similarity index 100%
rename from bin/db_mgmt_admin.py
rename to sbin/db_mgmt_admin.py
diff --git a/bin/db_mgmt_capec.py b/sbin/db_mgmt_capec.py
similarity index 100%
rename from bin/db_mgmt_capec.py
rename to sbin/db_mgmt_capec.py
diff --git a/bin/db_mgmt_cpe_dictionary.py b/sbin/db_mgmt_cpe_dictionary.py
similarity index 100%
rename from bin/db_mgmt_cpe_dictionary.py
rename to sbin/db_mgmt_cpe_dictionary.py
diff --git a/bin/db_mgmt_cpe_other_dictionary.py b/sbin/db_mgmt_cpe_other_dictionary.py
similarity index 96%
rename from bin/db_mgmt_cpe_other_dictionary.py
rename to sbin/db_mgmt_cpe_other_dictionary.py
index c4d49a849..393fac72d 100644
--- a/bin/db_mgmt_cpe_other_dictionary.py
+++ b/sbin/db_mgmt_cpe_other_dictionary.py
@@ -64,18 +64,19 @@
batch = []
# skip on empty collections
-if not list(collections):
+col=list(collections)
+if not col:
print ("Empty collections, import skipped")
sys.exit(2)
-for item in progressbar(list(collections)):
+for item in progressbar(col):
for cpeentry in item['vulnerable_configuration']:
checkdup = cpeother.find(({'id': cpeentry}))
if checkdup.count() <= 0:
entry = cpe.find(({'id': cpeentry}))
if entry.count() <= 0:
title = cpeentry
- title = title[7:]
+ title = title[10:]
title = title.replace(':-:', ' ',10)
title = title.replace(':', ' ',10)
title = title.replace('_', ' ',10)
diff --git a/bin/db_mgmt_create_index.py b/sbin/db_mgmt_create_index.py
similarity index 100%
rename from bin/db_mgmt_create_index.py
rename to sbin/db_mgmt_create_index.py
diff --git a/bin/db_mgmt_cwe.py b/sbin/db_mgmt_cwe.py
similarity index 100%
rename from bin/db_mgmt_cwe.py
rename to sbin/db_mgmt_cwe.py
diff --git a/bin/db_mgmt_d2sec.py b/sbin/db_mgmt_d2sec.py
similarity index 100%
rename from bin/db_mgmt_d2sec.py
rename to sbin/db_mgmt_d2sec.py
diff --git a/bin/db_mgmt_vendorstatements.py b/sbin/db_mgmt_vendorstatements.py
similarity index 100%
rename from bin/db_mgmt_vendorstatements.py
rename to sbin/db_mgmt_vendorstatements.py
diff --git a/bin/db_mgmt_vfeed.py b/sbin/db_mgmt_vfeed.py
similarity index 100%
rename from bin/db_mgmt_vfeed.py
rename to sbin/db_mgmt_vfeed.py
diff --git a/bin/db_notification.py b/sbin/db_notification.py
similarity index 100%
rename from bin/db_notification.py
rename to sbin/db_notification.py
diff --git a/bin/db_ranking.py b/sbin/db_ranking.py
similarity index 100%
rename from bin/db_ranking.py
rename to sbin/db_ranking.py
diff --git a/bin/db_updater.py b/sbin/db_updater.py
similarity index 100%
rename from bin/db_updater.py
rename to sbin/db_updater.py
diff --git a/bin/db_whitelist.py b/sbin/db_whitelist.py
similarity index 100%
rename from bin/db_whitelist.py
rename to sbin/db_whitelist.py
diff --git a/test/test.py b/test/test.py
index d7122ced4..23512a53d 100644
--- a/test/test.py
+++ b/test/test.py
@@ -13,7 +13,14 @@
runPath = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(runPath, ".."))
-from lib.Toolkit import toStringFormattedCPE
+import argparse
+
+from lib.Toolkit import toStringFormattedCPE, toOldCPE, pad
+
+# parse command line arguments
+argparser = argparse.ArgumentParser(description='Recursive test for functions')
+argparser.add_argument('-v', action='store_true', help='Verbose')
+args = argparser.parse_args()
def resultOf(original, result, expected):
test={'in':original,'out':result,'expect':expected}
@@ -24,10 +31,11 @@ def printResults(test, results):
l = [x['passed'] for x in results]
if False in l:
print('[x] %s failed!'%test)
- for x in [x for x in results if x['passed']==False]:
- print(' in: %s'%x['in'])
- print(' out: %s'%x['out'])
- print(' expected: %s'%x['expect'])
+ if args.v:
+ for x in [x for x in results if x['passed']==False]:
+ print(' in: %s'%x['in'])
+ print(' out: %s'%x['out'])
+ print(' expected: %s'%x['expect'])
else:
print('[ ] %s passed'%test)
@@ -41,22 +49,57 @@ def printResults(test, results):
{'in':'cpe:/a:7-zip:7-zip:4.65::~~~~x64~', 'expect':'cpe:2.3:a:7-zip:7-zip:4.65:-:-:-:-:-:x64:-'},
{'in':'cpe:/a:acl:acl:9.1.0.213', 'expect':'cpe:2.3:a:acl:acl:9.1.0.213:-:-:-:-:-:-:-'}]
-trans= [{'in':'cpe:/o:microsoft:windows_server_2008::sp2:itanium', 'expect':'cpe:2.3:o:microsoft:windows_server_2008:-:sp2:itanium'},
- {'in':'cpe:/a:activehelper:activehelper_livehelp_live_chat:2.7.4::~~~wordpress~~', 'expect':'cpe:2.3:a:activehelper:activehelper_livehelp_live_chat:2.7.4:-:-:-:-:wordpress'},
- {'in':'cpe:/o:microsoft:windows:vista:sp1:x64-enterprise', 'expect':'cpe:2.3:o:microsoft:windows:vista:sp1:x64-enterprise'},
- {'in':'cpe:/o:microsoft:windows-nt:vista::enterprise', 'expect':'cpe:2.3:o:microsoft:windows-nt:vista:-:enterprise'},
- {'in':'cpe:/a:novell:iprint:5.90:-:~~~windows_vista~~', 'expect':'cpe:2.3:a:novell:iprint:5.90:-:-:-:-:windows_vista'},
- {'in':'cpe:/o:linux:linux_kernel:-', 'expect':'cpe:2.3:o:linux:linux_kernel'},
- {'in':'cpe:/a:aokitaka:zip_with_pass_pro:6.3.4:-:~-~-~android~~', 'expect':'cpe:2.3:a:aokitaka:zip_with_pass_pro:6.3.4:-:-:-:-:android'},
- {'in':'cpe:/a:7-zip:7-zip:4.65::~~~~x64~', 'expect':'cpe:2.3:a:7-zip:7-zip:4.65:-:-:-:-:-:x64'},
- {'in':'cpe:/a:acl:acl:9.1.0.213', 'expect':'cpe:2.3:a:acl:acl:9.1.0.213'}]
+trans=[{'in':'cpe:/o:microsoft:windows_server_2008::sp2:itanium', 'expect':'cpe:2.3:o:microsoft:windows_server_2008:-:sp2:itanium'},
+ {'in':'cpe:/a:activehelper:activehelper_livehelp_live_chat:2.7.4::~~~wordpress~~', 'expect':'cpe:2.3:a:activehelper:activehelper_livehelp_live_chat:2.7.4:-:-:-:-:wordpress'},
+ {'in':'cpe:/o:microsoft:windows:vista:sp1:x64-enterprise', 'expect':'cpe:2.3:o:microsoft:windows:vista:sp1:x64-enterprise'},
+ {'in':'cpe:/o:microsoft:windows-nt:vista::enterprise', 'expect':'cpe:2.3:o:microsoft:windows-nt:vista:-:enterprise'},
+ {'in':'cpe:/a:novell:iprint:5.90:-:~~~windows_vista~~', 'expect':'cpe:2.3:a:novell:iprint:5.90:-:-:-:-:windows_vista'},
+ {'in':'cpe:/o:linux:linux_kernel:-', 'expect':'cpe:2.3:o:linux:linux_kernel'},
+ {'in':'cpe:/a:aokitaka:zip_with_pass_pro:6.3.4:-:~-~-~android~~', 'expect':'cpe:2.3:a:aokitaka:zip_with_pass_pro:6.3.4:-:-:-:-:android'},
+ {'in':'cpe:/a:7-zip:7-zip:4.65::~~~~x64~', 'expect':'cpe:2.3:a:7-zip:7-zip:4.65:-:-:-:-:-:x64'},
+ {'in':'cpe:/a:acl:acl:9.1.0.213', 'expect':'cpe:2.3:a:acl:acl:9.1.0.213'}]
+
+old =[{'in':'cpe:2.3:o:microsoft:windows_server_2008:-:sp2:itanium', 'expect':'cpe:/o:microsoft:windows_server_2008::sp2:itanium'},
+ {'in':'cpe:2.3:a:activehelper:activehelper_livehelp_live_chat:2.7.4:-:-:-:-:wordpress', 'expect':'cpe:/a:activehelper:activehelper_livehelp_live_chat:2.7.4::~~~wordpress~~'},
+ {'in':'cpe:2.3:o:microsoft:windows:vista:sp1:x64-enterprise', 'expect':'cpe:/o:microsoft:windows:vista:sp1:x64-enterprise'},
+ {'in':'cpe:2.3:o:microsoft:windows-nt:vista:-:enterprise', 'expect':'cpe:/o:microsoft:windows-nt:vista::enterprise'},
+ {'in':'cpe:2.3:a:novell:iprint:5.90:-:-:-:-:windows_vista', 'expect':'cpe:/a:novell:iprint:5.90::~~~windows_vista~~'},
+ {'in':'cpe:2.3:o:linux:linux_kernel', 'expect':'cpe:/o:linux:linux_kernel'},
+ {'in':'cpe:2.3:a:aokitaka:zip_with_pass_pro:6.3.4:-:-:-:-:android', 'expect':'cpe:/a:aokitaka:zip_with_pass_pro:6.3.4::~~~android~~'},
+ {'in':'cpe:2.3:a:7-zip:7-zip:4.65:-:-:-:-:-:x64', 'expect':'cpe:/a:7-zip:7-zip:4.65::~~~~x64~'},
+ {'in':'cpe:2.3:a:acl:acl:9.1.0.213', 'expect':'cpe:/a:acl:acl:9.1.0.213'}]
+
+pad1=[{'in':['a','b','c'], 'expect':['a','b','c',None,None]},
+ {'in':['a','b','c','d','e'], 'expect':['a','b','c','d','e']},
+ {'in':['a','b','c','d','e','f'], 'expect':['a','b','c','d','e','f']}]
+padtext1=[{'in':['a','b','c'], 'expect':['a','b','c','-','-']},
+ {'in':['a','b','c','d','e'], 'expect':['a','b','c','d','e']},
+ {'in':['a','b','c','d','e','f'], 'expect':['a','b','c','d','e','f']}]
+padtext2=[{'in':['a','b','c'], 'expect':['a','b','c','text','text']},
+ {'in':['a','b','c','d','e'], 'expect':['a','b','c','d','e']},
+ {'in':['a','b','c','d','e','f'], 'expect':['a','b','c','d','e','f']}]
result=[]
for x in extend:
result.append(resultOf(x['in'],toStringFormattedCPE(x['in'],autofill=True),x['expect']))
-printResults('Translate - success/autofill',result)
+printResults('Translate to 2.3 - success/autofill',result)
result=[]
for x in trans:
result.append(resultOf(x['in'],toStringFormattedCPE(x['in']),x['expect']))
-printResults('Translate - success/no autofill',result)
+printResults('Translate to 2.3 - success/no autofill',result)
+
+result=[]
+for x in old:
+ result.append(resultOf(x['in'],toOldCPE(x['in']),x['expect']))
+printResults('Translate to 2.2 - success/no autofill',result)
+
+result=[]
+for x in pad1:
+ result.append(resultOf(x['in'],pad(x['in'],5),x['expect']))
+for x in padtext1:
+ result.append(resultOf(x['in'],pad(x['in'],5,'-'),x['expect']))
+for x in padtext2:
+ result.append(resultOf(x['in'],pad(x['in'],5,'text'),x['expect']))
+printResults('Padding lists - empty, char and text - ',result)
+
diff --git a/web/index.py b/web/index.py
index b1e0571d7..18c63cde5 100644
--- a/web/index.py
+++ b/web/index.py
@@ -27,9 +27,6 @@
from redis import exceptions as redisExceptions
import json
-from dateutil import tz
-import dateutil.parser
-import base64
import re
import argparse
import time
@@ -42,9 +39,10 @@
from lib.User import User
from lib.Config import Configuration
+from lib.Toolkit import toStringFormattedCPE, toOldCPE, currentTime, isURL, vFeedName, convertDateToDBFormat
import lib.CVEs as cves
-from bin.db_whitelist import *
-from bin.db_blacklist import *
+from sbin.db_whitelist import *
+from sbin.db_blacklist import *
# parse command line arguments
argparser = argparse.ArgumentParser(description='Start CVE-Search web component')
@@ -160,9 +158,6 @@ def getBlacklistRegexes():
def addCPEToList(cpe, listType, cpeType=None):
- #cpe = urllib.parse.quote_plus(cpe).lower()
- #cpe = cpe.replace("%3a", ":")
- #cpe = cpe.replace("%2f", "/")
if not cpeType:
cpeType='cpe'
if listType.lower() in ("blacklist", "black", "b", "bl"):
@@ -182,29 +177,6 @@ def getVersionsOfProduct(product):
return sorted(list(p))
-def convertDateToDBFormat(string):
- result = None
- try:
- result = time.strptime(string, "%d-%m-%Y")
- except:
- pass
- try:
- result = time.strptime(string, "%d-%m-%y")
- except:
- pass
- try:
- result = time.strptime(string, "%d/%m/%Y")
- except:
- pass
- try:
- result = time.strptime(string, "%d/%m/%y")
- except:
- pass
- if result is not None:
- result = time.strftime('%Y-%m-%d', result)
- return result
-
-
def adminStats():
cveU = db.info.find_one({'db': 'cve'})
cpeU = db.info.find_one({'db': 'cpe'})
@@ -378,10 +350,24 @@ def filterLast(r):
return render_template('index.html', settings=settings, cve=cve, r=r, pageLength=pageLength)
-@app.route('/api/cvefor/', methods=['GET'])
+@app.route('/api/cpe2.3/', methods=['GET'])
+def cpe23(cpe):
+ cpe = toStringFormattedCPE(cpe)
+ if not cpe: cpe='None'
+ return cpe
+
+@app.route('/api/cpe2.2/', methods=['GET'])
+def cpe22(cpe):
+ cpe = toOldCPE(cpe)
+ if not cpe: cpe='None'
+ return cpe
+
+@app.route('/api/cvefor/', methods=['GET'])
def apiCVEFor(cpe):
col = db['cves']
cpe=urllib.parse.unquote_plus(cpe)
+ cpe=toStringFormattedCPE(cpe)
+ if not cpe: cpe='None'
vulns = col.find({"vulnerable_configuration": {'$regex': cpe}}).sort("Modified", -1)
r = []
for x in vulns:
@@ -487,7 +473,7 @@ def admin():
@app.route('/admin/updatedb')
@login_required
def updatedb():
- os.system("python3 " + os.path.join(_runPath, "../bin/db_updater.py -civ"))
+ os.system("python3 " + os.path.join(_runPath, "../sbin/db_updater.py -civ"))
status = ["db_updated", "success"]
return render_template('admin.html', status=status, stats=adminStats())
@@ -790,17 +776,8 @@ def page_not_found(e):
# filters
@app.template_filter('currentTime')
-def currentTime(utc):
- timezone = tz.tzlocal()
- utc = dateutil.parser.parse(utc)
- output = utc.astimezone(timezone)
- output = output.strftime('%d-%m-%Y - %H:%M')
- return output
-
-
-@app.template_filter('base64Enc')
-def base64Encode(string):
- return base64.b64encode(bytes(string, "utf-8")).decode("utf-8")
+def currentTimeFilter(utc):
+ return currentTime(utc)
@app.template_filter('htmlDecode')
@@ -813,25 +790,13 @@ def htmlEncode(string):
return urllib.parse.quote_plus(string).lower()
-@app.template_filter('impact')
-def impact(string):
- if string.lower() == "none":
- return "good"
- elif string.lower() == "partial":
- return "medium"
- elif string.lower() == "complete":
- return "bad"
-
@app.template_filter('isURL')
-def isURL(string):
- urlTypes= [re.escape(x) for x in ['http://','https://', 'www.']]
- return re.match("^(" + "|".join(urlTypes) + ")", string)
+def isURLFilter(string):
+ return isURL(string)
@app.template_filter('vFeedName')
-def vFeedName(string):
- string=string.replace('map_','')
- string=string.replace('cve_','')
- return string.title()
+def vFeedNameFilter(string):
+ return vFeedName(string)
# signal handlers
def sig_handler(sig, frame):
@@ -866,6 +831,8 @@ def stop_loop():
if Configuration.getLogging():
logfile = Configuration.getLogfile()
pathToLog = logfile.rsplit('/', 1)[0]
+ pathToLog = os.path.join(_runPath, pathToLog)
+ logfile = os.path.join(_runPath, logfile)
if not os.path.exists(pathToLog):
os.makedirs(pathToLog)
maxLogSize = Configuration.getMaxLogSize()
diff --git a/web/minimal-web.py b/web/minimal-web.py
new file mode 100644
index 000000000..ee947d6f8
--- /dev/null
+++ b/web/minimal-web.py
@@ -0,0 +1,396 @@
+#!/usr/bin/env python3.3
+# -*- coding: utf-8 -*-
+#
+# Simple web interface to cve-search to display the last entries
+# and view a specific CVE.
+#
+# Software is free software released under the "Modified BSD license"
+#
+
+# Copyright (c) 2013-2015 Alexandre Dulaunoy - a@foo.be
+# Copyright (c) 2014-2015 Pieter-Jan Moreels - pieterjan.moreels@gmail.com
+
+# imports
+import os
+import sys
+_runPath = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(_runPath, ".."))
+
+from tornado.wsgi import WSGIContainer
+from tornado.httpserver import HTTPServer
+from tornado.ioloop import IOLoop
+from flask import Flask, render_template, request, jsonify
+from flask.ext.pymongo import PyMongo
+from redis import exceptions as redisExceptions
+
+import json
+import re
+import argparse
+import time
+import urllib
+import random
+import signal
+import logging
+from logging.handlers import RotatingFileHandler
+
+from lib.Config import Configuration
+from lib.Toolkit import toStringFormattedCPE, toOldCPE, currentTime, isURL, vFeedName, convertDateToDBFormat
+import lib.CVEs as cves
+
+# parse command line arguments
+argparser = argparse.ArgumentParser(description='Start CVE-Search web component')
+argparser.add_argument('-v', action='store_true', help='verbose output')
+args = argparser.parse_args()
+
+# variables
+app = Flask(__name__, static_folder='static', static_url_path='/static')
+app.config['MONGO_DBNAME'] = Configuration.getMongoDB()
+app.config['SECRET_KEY'] = str(random.getrandbits(256))
+pageLength = Configuration.getPageLength()
+
+# db connectors
+mongo = PyMongo(app)
+db = Configuration.getMongoConnection()
+redisdb = Configuration.getRedisVendorConnection()
+
+# functions
+def getBrowseList(vendor):
+ result = {}
+ if (vendor is None) or type(vendor) == list:
+ v1 = redisdb.smembers("t:/o")
+ v2 = redisdb.smembers("t:/a")
+ v3 = redisdb.smembers("t:/h")
+ vendor = sorted(list(set(list(v1) + list(v2) + list(v3))))
+ cpe = None
+ else:
+ cpenum = redisdb.scard("v:" + vendor)
+ if cpenum < 1:
+ return page_not_found(404)
+ p = redisdb.smembers("v:" + vendor)
+ cpe = sorted(list(p))
+ result["vendor"] = vendor
+ result["product"] = cpe
+ return result
+
+
+def getVersionsOfProduct(product):
+ p = redisdb.smembers("p:" + product)
+ return sorted(list(p))
+
+
+ cveU = db.info.find_one({'db': 'cve'})
+ cpeU = db.info.find_one({'db': 'cpe'})
+ cpeOtherU = db.info.find_one({'db': 'cpeother'})
+ capecU = db.info.find_one({'db': 'capec'})
+ d2secU = db.info.find_one({'db': 'd2sec'})
+ vendorU = db.info.find_one({'db': 'vendor'})
+ vfeedU = db.info.find_one({'db': 'vfeed'})
+ stats = {'cveA': db.cves.count(), 'cveU': cveU['last-modified'] if cveU is not None else None,
+ 'cpeA': db.cpe.count(), 'cpeU': cpeU['last-modified'] if cpeU is not None else None,
+ 'cpeOtherA': db.cpeother.count(), 'cpeOtherU': cpeOtherU['last-modified'] if cpeOtherU is not None else None,
+ 'capecA': db.capec.count(), 'capecU': capecU['last-modified'] if capecU is not None else None,
+ 'd2secA': db.d2sec.count(), 'd2secU': d2secU['last-modified'] if d2secU is not None else None,
+ 'vendorA': db.vendor.count(), 'vendorU': vendorU['last-modified'] if vendorU is not None else None,
+ 'vfeedA': db.vfeed.count(), 'vfeedU': vfeedU['last-modified'] if vfeedU is not None else None,
+ 'blA': db.mgmt_blacklist.count(), 'wlA': db.mgmt_whitelist.count(),
+ 'dbName': Configuration.getMongoDB(), 'dbSize': db.command("dbstats")['dataSize'],
+ 'dbOnDisk': db.command("dbstats")['storageSize']}
+ return stats
+
+
+def filter_logic(unlisted, timeSelect, startDate, endDate,
+ timeTypeSelect, cvssSelect, cvss, rejectedSelect, limit, skip):
+ collection = db.cves
+ query = []
+ # retrieving lists
+ if rejectedSelect == "hide":
+ exp = "^(?!\*\* REJECT \*\*\s+DO NOT USE THIS CANDIDATE NUMBER.*)"
+ query.append({'summary': re.compile(exp)})
+ # cvss logic
+ if cvssSelect != "all":
+ if cvssSelect == "above":
+ query.append({'cvss': {'$gt': float(cvss)}})
+ if cvssSelect == "equals":
+ query.append({'cvss': float(cvss)})
+ if cvssSelect == "below":
+ query.append({'cvss': {'$lt': float(cvss)}})
+ # date logic
+ if timeSelect != "all":
+ startDate = convertDateToDBFormat(startDate)
+ endDate = convertDateToDBFormat(endDate)
+ if timeSelect == "from":
+ query.append({timeTypeSelect: {'$gt': startDate}})
+ if timeSelect == "until":
+ query.append({timeTypeSelect: {'$lt': endDate}})
+ if timeSelect == "between":
+ query.append({timeTypeSelect: {'$gt': startDate, '$lt': endDate}})
+ if timeSelect == "outside":
+ query.append({'$or': [{timeTypeSelect: {'$lt': startDate}}, {timeTypeSelect: {'$gt': endDate}}]})
+ if len(query) == 0:
+ cve = collection.find().sort("Modified", -1).limit(limit).skip(skip)
+ elif len(query) == 1:
+ cve = collection.find(query[0]).sort("Modified", -1).limit(limit).skip(skip)
+ else:
+ cve = collection.find({'$and': query}).sort("Modified", -1).limit(limit).skip(skip)
+ # marking relevant records
+ cve = list(cve)
+ return cve
+
+# routes
+@app.route('/')
+def index():
+ # get default page on HTTP get (navigating to page)
+ unlisted = "show"
+ timeSelect = "all"
+ startDate = None
+ endDate = None
+ timeTypeSelect = "Modified"
+ cvssSelect = "all"
+ cvss = None
+ rejectedSelect = "hide"
+ cve = filter_logic(unlisted, timeSelect, startDate, endDate,
+ timeTypeSelect, cvssSelect, cvss, rejectedSelect, pageLength, 0)
+ return render_template('index-minimal.html', cve=cve, r=0, pageLength=pageLength)
+
+@app.route('/', methods=['POST'])
+def filterPost():
+ unlisted = request.form.get('unlistedSelect')
+ timeSelect = request.form.get('timeSelect')
+ startDate = request.form.get('startDate')
+ endDate = request.form.get('endDate')
+ timeTypeSelect = request.form.get('timeTypeSelect')
+ cvssSelect = request.form.get('cvssSelect')
+ cvss = request.form.get('cvss')
+ rejectedSelect = request.form.get('rejectedSelect')
+ settings = {'unlistedSelect': unlisted, 'timeSelect': timeSelect,
+ 'startDate': startDate, 'endDate': endDate,
+ 'timeTypeSelect': timeTypeSelect, 'cvssSelect': cvssSelect,
+ 'cvss': cvss, 'rejectedSelect': rejectedSelect}
+ # retrieving data
+ cve = filter_logic(unlisted, timeSelect, startDate, endDate,
+ timeTypeSelect, cvssSelect, cvss, rejectedSelect, pageLength, 0)
+ return render_template('index-minimal.html', settings=settings, cve=cve, r=0, pageLength=pageLength)
+
+
+@app.route('/r/', methods=['POST', 'GET'])
+def filterLast(r):
+ if not r:
+ r = 0
+ blacklist = request.form.get('blacklistSelect')
+ whitelist = request.form.get('whitelistSelect')
+ unlisted = request.form.get('unlistedSelect')
+ timeSelect = request.form.get('timeSelect')
+ startDate = request.form.get('startDate')
+ endDate = request.form.get('endDate')
+ timeTypeSelect = request.form.get('timeTypeSelect')
+ cvssSelect = request.form.get('cvssSelect')
+ cvss = request.form.get('cvss')
+ rejectedSelect = request.form.get('rejectedSelect')
+ settings = {'blacklistSelect': blacklist, 'whitelistSelect': whitelist,
+ 'unlistedSelect': unlisted, 'timeSelect': timeSelect,
+ 'startDate': startDate, 'endDate': endDate,
+ 'timeTypeSelect': timeTypeSelect, 'cvssSelect': cvssSelect,
+ 'cvss': cvss, 'rejectedSelect': rejectedSelect}
+ # retrieving data
+ cve = filter_logic(unlisted, timeSelect, startDate, endDate,
+ timeTypeSelect, cvssSelect, cvss, rejectedSelect, pageLength, r)
+ return render_template('index-minimal.html', settings=settings, cve=cve, r=r, pageLength=pageLength)
+
+@app.route('/api/cpe2.3/', methods=['GET'])
+def cpe23(cpe):
+ cpe = toStringFormattedCPE(cpe)
+ if not cpe: cpe='None'
+ return cpe
+
+@app.route('/api/cpe2.2/', methods=['GET'])
+def cpe22(cpe):
+ cpe = toOldCPE(cpe)
+ if not cpe: cpe='None'
+ return cpe
+
+@app.route('/api/cvefor/', methods=['GET'])
+def apiCVEFor(cpe):
+ col = db['cves']
+ cpe=urllib.parse.unquote_plus(cpe)
+ cpe=toStringFormattedCPE(cpe)
+ if not cpe: cpe='None'
+ vulns = col.find({"vulnerable_configuration": {'$regex': cpe}}).sort("Modified", -1)
+ r = []
+ for x in vulns:
+ x.pop('_id')
+ r.append(x)
+ return json.dumps(r)
+
+@app.route('/api/cve/', methods=['GET'])
+def apiCVE(cveid):
+ cvesp = cves.last(rankinglookup=True, namelookup=True, vfeedlookup=True, capeclookup=True)
+ cve = cvesp.getcve(cveid=cveid)
+ if cve is None:
+ cve = {}
+ return (jsonify(cve))
+
+@app.route('/api/browse/', methods=['GET'])
+@app.route('/api/browse/', methods=['GET'])
+@app.route('/api/browse', methods=['GET'])
+def apibrowse(vendor=None):
+ if vendor is not None:
+ vendor = urllib.parse.quote_plus(vendor).lower()
+ browseList = getBrowseList(vendor)
+ if isinstance(browseList, dict):
+ return (jsonify(browseList))
+ else:
+ return (jsonify({}))
+
+
+@app.route('/api/search//', methods=['GET'])
+def apisearch(vendor=None, product=None):
+ if vendor is None or product is None:
+ return (jsonify({}))
+ collection = db.cves
+ search = vendor + ":" + product
+ cves = collection.find({"vulnerable_configuration": {'$regex': search}}).sort("Modified", -1)
+ r = []
+ for cve in cves:
+ cve.pop('_id')
+ r.append(cve)
+ return (json.dumps(r))
+
+@app.route('/cve/')
+def cve(cveid):
+ cvesp = cves.last(rankinglookup=True, namelookup=True, vfeedlookup=True, capeclookup=True,subscorelookup=True)
+ cve = cvesp.getcve(cveid=cveid)
+ if cve is None:
+ return page_not_found(404)
+ return render_template('cve-minimal.html', cve=cve)
+
+
+@app.route('/browse/')
+@app.route('/browse/')
+def browse(vendor=None):
+ try:
+ if vendor is not None:
+ vendor = urllib.parse.quote_plus(vendor).lower()
+ browseList = getBrowseList(vendor)
+ vendor = browseList["vendor"]
+ product = browseList["product"]
+ return render_template('browse-minimal.html', product=product, vendor=vendor)
+ except redisExceptions.ConnectionError:
+ return render_template('error.html',
+ status={'except':'redis-connection',
+ 'info':{'host':Configuration.getRedisHost(),'port':Configuration.getRedisPort()}})
+
+
+@app.route('/search//')
+def search(vendor=None, product=None):
+ collection = db.cves
+ search = vendor + ":" + product
+ cve = collection.find({"vulnerable_configuration": {'$regex': search}}).sort("Modified", -1)
+ return render_template('search-minimal.html', vendor=vendor, product=product, cve=cve)
+
+@app.route('/link///')
+def link(vFeedMap=None,field=None,value=None):
+ vFeedMap=htmlDedode(vFeedMap)
+ field=htmlDedode(field)
+ value=htmlDedode(value)
+ search="%s.%s"%(vFeedMap,field)
+ regex = re.compile(re.escape(value), re.I)
+ cveList=[x['id'] for x in db.vfeed.find({search: regex}).sort("Modified",-1)]
+ cve = list(db.cves.find({'id':{'$in': cveList}}).sort("Modified",-1))
+ cvssList=[float(x['cvss']) for x in cve if 'cvss' in x]
+ stats={'maxCVSS': max(cvssList), 'minCVSS': min(cvssList),'count':len(cve)}
+ return render_template('linked-minimal.html', vFeedMap=vFeedMap, field=field, value=value, cve=cve, stats=stats)
+
+# error handeling
+@app.errorhandler(404)
+def page_not_found(e):
+ return render_template('404.html'), 404
+
+
+# filters
+@app.template_filter('currentTime')
+def currentTimeFilter(utc):
+ return currentTime(utc)
+
+
+@app.template_filter('htmlDecode')
+def htmlDedode(string):
+ return urllib.parse.unquote_plus(string)
+
+
+@app.template_filter('htmlEncode')
+def htmlEncode(string):
+ return urllib.parse.quote_plus(string).lower()
+
+
+@app.template_filter('isURL')
+def isURLFilter(string):
+ return isURL(string)
+
+@app.template_filter('vFeedName')
+def vFeedNameFilter(string):
+ return vFeedName(string)
+
+# signal handlers
+def sig_handler(sig, frame):
+ print('Caught signal: %s' % sig)
+ IOLoop.instance().add_callback(shutdown)
+
+
+def shutdown():
+ MAX_WAIT_SECONDS_BEFORE_SHUTDOWN = 3
+ print('Stopping http server')
+ http_server.stop()
+
+ print('Will shutdown in %s seconds ...' % MAX_WAIT_SECONDS_BEFORE_SHUTDOWN)
+ io_loop = IOLoop.instance()
+ deadline = time.time() + MAX_WAIT_SECONDS_BEFORE_SHUTDOWN
+
+ def stop_loop():
+ now = time.time()
+ if now < deadline and (io_loop._callbacks or io_loop._timeouts):
+ io_loop.add_timeout(now + 1, stop_loop)
+ else:
+ io_loop.stop()
+ print('Shutdown')
+ stop_loop()
+
+if __name__ == '__main__':
+ # get properties
+ flaskHost = Configuration.getFlaskHost()
+ flaskPort = Configuration.getFlaskPort()
+ flaskDebug = Configuration.getFlaskDebug()
+ # logging
+ if Configuration.getLogging():
+ logfile = Configuration.getLogfile()
+ pathToLog = logfile.rsplit('/', 1)[0]
+ if not os.path.exists(pathToLog):
+ os.makedirs(pathToLog)
+ maxLogSize = Configuration.getMaxLogSize()
+ backlog = Configuration.getBacklog()
+ file_handler = RotatingFileHandler(logfile, maxBytes=maxLogSize, backupCount=backlog)
+ file_handler.setLevel(logging.ERROR)
+ formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
+ file_handler.setFormatter(formatter)
+ app.logger.addHandler(file_handler)
+
+ if flaskDebug:
+ # start debug flask server
+ app.run(host=flaskHost, port=flaskPort, debug=flaskDebug)
+ else:
+ # start asynchronous server using tornado wrapper for flask
+ # ssl connection
+ print("Server starting...")
+ if Configuration.useSSL():
+ cert = os.path.join(_runPath, "../", Configuration.getSSLCert())
+ key = os.path.join(_runPath, "../", Configuration.getSSLKey())
+ ssl_options = {"certfile": cert,
+ "keyfile": key}
+ else:
+ ssl_options = None
+ signal.signal(signal.SIGTERM, sig_handler)
+ signal.signal(signal.SIGINT, sig_handler)
+ global http_server
+ http_server = HTTPServer(WSGIContainer(app), ssl_options=ssl_options)
+ http_server.bind(flaskPort, address=flaskHost)
+ http_server.start(0) # Forks multiple sub-processes
+ IOLoop.instance().start()
diff --git a/web/static/css/style.css b/web/static/css/style.css
index 65df65977..7c892b7b2 100644
--- a/web/static/css/style.css
+++ b/web/static/css/style.css
@@ -71,14 +71,14 @@ ul.nav li.dropdown:hover > ul.dropdown-menu {
table-layout: fixed
}
-.good {
+.impact-none {
color:green;
}
-.medium {
+.impact-partial {
color:orange;
}
-.bad {
+.impact-complete {
color:red;
}
diff --git a/web/templates/browse-minimal.html b/web/templates/browse-minimal.html
new file mode 100644
index 000000000..69c09bfca
--- /dev/null
+++ b/web/templates/browse-minimal.html
@@ -0,0 +1,59 @@
+
+
+
+ Vendor browsing
+
+ {% include 'defaultHead.html' %}
+
+
+
+