Commit 7abd99c9 authored by Alexandre Dulaunoy's avatar Alexandre Dulaunoy
Browse files

Merge pull request #71 from PidgeyL/master

Development + Bugfixes
parents f51f5c01 fedb3cd8
Loading
Loading
Loading
Loading

bin/search_irc.py

0 → 100644
+152 −0
Original line number Diff line number Diff line
#!/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 <max>
# cvetweet <max>
# browse
# search <vendor>\<product>
# get <cve>
#
# 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()
+9 −104
Original line number Diff line number Diff line
@@ -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 <max>
# cvetweet <max>
# browse
# search <vendor>\<product>
# get <cve>
#
# You need to add the XMPP bot in your roster if you want to communicate
# with it.
@@ -26,10 +30,9 @@ import getpass
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 + "get <cve-id> (output: JSON)\n"
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):

lib/Query.py

0 → 100644
+111 −0
Original line number Diff line number Diff line
#!/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
+3 −3
Original line number Diff line number Diff line
@@ -41,8 +41,8 @@ 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')
@@ -473,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())

+1 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@
              {% if product != None %}
                {% for p in product %}
                  <tr>
                    <td><a href="/search/{{ vendor }}/{{ p|replace("%21","") }}">{{ p|htmlDecode }}</a></td>
                    <td><a href="/search/{{ vendor }}/{{ p|htmlEncode }}">{{ p|htmlDecode }}</a></td>
                  </tr>
                {% endfor %}
              {% else  %}
Loading