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 ("") 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' %} + + + +
+
+
+ + {% include 'menu-minimal.html' %} + + +
+ + + + + + + {% if product != None %} + + {% elif product == None %} + + {% endif %} + + {% if product != None %} + {% for p in product %} + + + + {% endfor %} + {% else %} + {% for v in vendor %} + + + + {% endfor %} + {% endif %} + +
Products for {{ vendor|htmlDecode }}Vendors
{{ p|htmlDecode }}
{{ v|htmlDecode }}
+ Back to Top +
+ +
+
+
+ + diff --git a/web/templates/browse.html b/web/templates/browse.html index f62a1081b..bd55e60b8 100644 --- a/web/templates/browse.html +++ b/web/templates/browse.html @@ -37,7 +37,7 @@ {% if product != None %} {% for p in product %} - {{ p|htmlDecode }} + {{ p|htmlDecode }} {% endfor %} {% else %} diff --git a/web/templates/cve-minimal.html b/web/templates/cve-minimal.html new file mode 100644 index 000000000..ab1bb2259 --- /dev/null +++ b/web/templates/cve-minimal.html @@ -0,0 +1,199 @@ + + + + {{cve['id']}} - {{cve['summary'][:100]}} + + {% include 'defaultHead.html' %} + + + + + + +
+
+
+ + {% include 'menu-minimal.html' %} + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + {% if 'cwe' in cve%} + {% if cve['cwe'] != 'Unknown' %} + + + + + {% endif %} + {% endif %} + {% if 'capec' in cve%} + {% if cve['cwe']|length != 0 %} + + + + + {% endif %} + {% endif %} + {% if 'access' in cve %} + + + + + {% endif %} + {% if 'impact' in cve %} + + + + + {% endif %} + {% set keytype = ['vulnerable_configuration_cpe_2_2','impactCVSS','exploitCVSS' ,'cvss', 'capec', 'access', 'impact', 'cvss-time', 'Modified', 'Published', 'summary', 'vulnerable_configuration', 'references', '_id', 'id', 'last-modified', 'ranking', 'cwe'] %} + {% for k in cve|dictsort %} + {% if not k[0] in keytype %} + + + + + {% endif %} + {% endfor %} + + + + + + + + + {% if 'last-modified' in cve%} + + + + + {% endif %} + +
ID{{ cve['id'] }}
Summary{{ cve['summary'] }}
References +
+
    + {% for ref in cve['references'] %} +
  • {{ ref }}
  • + {% endfor %} +
+
+
Vulnerable Configurations +
+
    + {% for vulconf in cve['vulnerable_configuration'] %} +
  • + {{ vulconf['title'] }} +
    {{ vulconf['id'] }}
    +
  • + {% endfor %} +
+
+
CVSS + + + + +
Base: {{ cve['cvss'] }} {% if 'cvss-time' in cve %}(as of {{ cve['cvss-time']|currentTime }}){% endif %}
Impact: {{ cve['impactCVSS'] }}
Exploitability:{{ cve['exploitCVSS'] }}
+
CWE{{ cve['cwe'] }}
CAPEC +
+
    + {% for c in cve['capec'] %} +
  • + {{c['name']}} +
    + {% if c['summary']|length>1 %} +
      + {% for s in c['summary'] %} +
    • {{ s }}
    • + {% endfor %} +
    + {% else %} + {{c['summary'][0]}} + {% endif%} +
    +
  • + {% endfor %} +
+
+
Access + + + + + + + +
VectorComplexityAuthentication
{{cve['access']['vector']}}{{cve['access']['complexity']}}{{cve['access']['authentication']}}
+
Impact + + + + + + + +
ConfidentialityIntegrityAvailability
{{cve['impact']['confidentiality']}}{{cve['impact']['integrity']}}{{cve['impact']['availability']}}
+
{{ k[0]|vFeedName }} vFeed + + {% for item in k[1]|dictsort %} + + + + + {% endfor %} +
+ + + + {{item[0]}} + + {% if item[1]|isURL%} + {{ item[1] }} + {% else %} + {{ item[1] }} + {% endif %} +
+
Last major update{{ cve['Modified']|currentTime }}
Published{{ cve['Published']|currentTime }}
Last modified{{ cve['last-modified']|currentTime }}
+ Back to Top +
+ +
+
+
+ + diff --git a/web/templates/cve.html b/web/templates/cve.html index 197e3be91..809ed3740 100644 --- a/web/templates/cve.html +++ b/web/templates/cve.html @@ -136,9 +136,9 @@ - - - + + +
ConfidentialityIntegrityAvailability
{{cve['impact']['confidentiality']}}{{cve['impact']['integrity']}}{{cve['impact']['availability']}}{{cve['impact']['confidentiality']}}{{cve['impact']['integrity']}}{{cve['impact']['availability']}}
diff --git a/web/templates/index-minimal.html b/web/templates/index-minimal.html new file mode 100644 index 000000000..926d9f091 --- /dev/null +++ b/web/templates/index-minimal.html @@ -0,0 +1,133 @@ + + + + Common Vulnerability Exposure most recent entries + + {% include 'defaultHead.html' %} + + + + + + + + + + +
+
+
+ + {% include 'menu-minimal.html' %} + + +
+ + +
+ +
+ +
    + {% if r > 0 %} +
  • Previous
  • + {% endif %} + {% if cve|length == pageLength %} +
  • Next
  • + {% endif %} +
+ + {% include 'table.html' %} + +
    + {% if r > 0 %} +
  • Previous
  • + {% endif %} + {% if cve|length == pageLength %} +
  • Next
  • + {% endif %} +
+ +
+
+
+ + diff --git a/web/templates/linked-minimal.html b/web/templates/linked-minimal.html new file mode 100644 index 000000000..07262058d --- /dev/null +++ b/web/templates/linked-minimal.html @@ -0,0 +1,38 @@ + + + + CVE's linked by {{field}} + + {% include 'defaultHead.html' %} + + + + + + +
+
+
+ + {% include 'menu-minimal.html' %} + + +
+ + + + + + +
Max CVSS {{stats['maxCVSS']}}Min CVSS {{stats['minCVSS']}}Total Count{{stats['count']}}
+ {% include 'table.html' %} +
+ +
+
+
+ + diff --git a/web/templates/menu-minimal.html b/web/templates/menu-minimal.html new file mode 100644 index 000000000..499c6579a --- /dev/null +++ b/web/templates/menu-minimal.html @@ -0,0 +1,16 @@ + + + diff --git a/web/templates/search-minimal.html b/web/templates/search-minimal.html new file mode 100644 index 000000000..6ab7f539c --- /dev/null +++ b/web/templates/search-minimal.html @@ -0,0 +1,31 @@ + + + + Vendor browsing + + {% include 'defaultHead.html' %} + + + +
+
+
+ + {% include 'menu-minimal.html' %} + + +
+ + + {% include 'table.html' %} +
+ +
+
+
+ + diff --git a/web/templates/search.html b/web/templates/search.html index cf87f4eac..e620ebcd4 100644 --- a/web/templates/search.html +++ b/web/templates/search.html @@ -19,7 +19,7 @@ {% include 'table.html' %}