Commit e0533e02 authored by PidgeyL's avatar PidgeyL
Browse files

initial commit plugin manager

parent 77a65b6f
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -58,7 +58,9 @@ class Configuration():
               'includeCapec': True,      'includeD2Sec': True,
               'includeVFeed': True,      'includeVendor': True,
               'includeCWE': True,
               'http_proxy': ''
               'http_proxy': '',
               'plugin_load': './etc/plugins.txt',
               'plugin_config': './etc/plugins.ini'
               }

    @classmethod
@@ -343,3 +345,30 @@ class Configuration():
                buf = BytesIO(response.read())
                data = gzip.GzipFile(fileobj=buf)
            return (data, response)

    # Plugins
    @classmethod
    def getPluginLoadSettings(cls):
        return cls.toPath(cls.readSetting("Plugins", "loadSettings", cls.default['plugin_load']))

    @classmethod
    def getPluginsettings(cls):
        return cls.toPath(cls.readSetting("Plugins", "pluginSettings", cls.default['plugin_config']))

class ConfigReader():
    def __init__(self, file):
        self.ConfigParser = configparser.ConfigParser()
        self.ConfigParser.read(file)

    def read(self, section, item, default):
        result = default
        try:
            if type(default == bool):
                result = self.ConfigParser.getboolean(section, item)
            elif type(default) == int:
                result = self.ConfigParser.getint(section, item)
            else:
                result = self.ConfigParser.get(section, item)
        except:
            pass
        return result
+28 −0
Original line number Diff line number Diff line
@@ -382,3 +382,31 @@ def getUsers():

def getUser(user):
  return sanitize(colUSERS.find_one({"username": user}))

# Plugins
def p_queryData(collection, query):
  return list(db['plug_%s'%collection].find(query))

def p_queryOne(collection, query):
  return db['plug_%s'%collection].find_one(query)

def p_addEntry(collection, data):
  db['plug_%s'%collection].insert(data)

def p_removeEntry(collection, query):
  db['plug_%s'%collection].remove(query)

def p_addToList(collection, query, listname, data):
  if type(data) != list: data=[data]
  current = list(p_queryData(collection, query))
  if len(current)==0:
    p_addEntry(collection, query)
  for entry in current:
    if listname in entry:
      data=list(set(entry[listname])-set(data))
    if data:
      db['plug_%s'%collection].update(query, {"$addToSet": {listname: {"$each": data}}})

def p_removeFromList(collection, query, listname, data):
  if type(data) != list: data=[data]
  db['plug_%s'%collection].update(query, {"$pullAll": {listname: data}})

lib/PluginManager.py

0 → 100644
+94 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3.3
# -*- coding: utf-8 -*-
#
# Plugin manager
#
# Software is free software released under the "Modified BSD license"
#
# Copyright (c) 2016 	Pieter-Jan Moreels - pieterjan.moreels@gmail.com

# Imports
import sys
import os
runPath = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(runPath, ".."))

import importlib

from lib.Config import Configuration as conf
from lib.Config import ConfigReader
from lib.Plugins import Plugin, WebPlugin
from flask.ext.login import current_user

class PluginManager():
  def __init__(self):
    self.plugins = {}
  
  def loadPlugins(self):
    settingsReader = ConfigReader(conf.getPluginsettings())
    if not os.path.exists(conf.getPluginLoadSettings()):
        print("[!] Could not find plugin loader file!")
        return
    # Read and parse plugin file
    data = open(conf.getPluginLoadSettings(), "r").read()
    data = [x.split("\t") for x in data.split("\n") if not x.startswith("#") and x]
    data = [[x.strip() for x in y if x.strip()] for y in data]
    uids = []
    for x in [x for x in data if len(x) == 2]:
      try:
        if x[1].lower() == "load" or x[1].lower() == "default":
          # Load plugins
          i = importlib.import_module(x[0].replace("/", "."))
          plugin = getattr(i, x[0].split("/")[-1])()
          plugin.setUID(plugin.getName())
          # Ensure UID's unique
          while True:
            if plugin.getUID() in uids: plugin.setUID(plugin.getUID()+"_")
            else: break
          # Load settings if needed
          if x[1].lower() == "load":
            plugin.loadSettings(settingsReader)
          # Add to list
          self.plugins[plugin.getUID().strip()] = plugin
          print("[+] Loaded plugin %s"%x[0])
      except Exception as e:
        print("[!] Failed to load module %s: "%x[0])
        print("[!]  -> %s"%e)

  def onOpenCVE(self, cve, user):
    for plugin in self.plugins.values(): # Read all plugins
      if plugin.isWebPlugin():           # Check if plugin is web plugin
        return plugin.onJsonCall(action, **args)

  def onCVEAction(self, cve, plugin, action, **args):
    if plugin.strip() in self.plugins.keys(): # Check if plugin exists
      if self.plugins[plugin].isWebPlugin():  # Check if plugin is web plugin
        return self.plugins[plugin].onCVEAction(cve, action, **args)

  def getWebPlugins(self):
    webPlugins = []
    for plugin in self.plugins.values():
      if plugin.isWebPlugin():
        webPlugins.append(plugin)
    return webPlugins

  def getCVEActions(self, **args):
    actions = []
    for plugin in self.getWebPlugins():
      auth = plugin.requiresAuth
      for action in plugin.getCVEActions(**args):
        action['auth'] = auth
        action['plugin'] = plugin.getUID()
        actions.append(action)
    return actions

  def requiresAuth(self, plugin):
    if plugin.strip() in self.plugins.keys(): # Check if plugin exists
      return self.plugins[plugin].requiresAuth
    else: return False

  def openPage(self, name, **args):
    if name.strip() in self.plugins.keys(): # Check if plugin exists
      if self.plugins[name].isWebPlugin():  # Check if plugin is web plugin
        return self.plugins[name].getPage(**args)

lib/Plugins.py

0 → 100644
+28 −0
Original line number Diff line number Diff line
from flask import render_template

class Plugin():
  def __init__(self):
    self.name = None
    self.uid = None
    self.requiresAuth = False

  # Get
  def getName(self):      return self.name
  def getUID(self):       return self.uid
  # Set
  def setUID(self, uid): self.uid = uid
  # Don't override
  def isWebPlugin(self):  return False
  # To override
  def loadSettings(self): pass
  

class WebPlugin(Plugin):
  # Don't override
  def isWebPlugin(self):           return True
  # To override
  def getPage(self, **args):       return None
  def getCVEActions(self, **args): return []
  # Functions based on user interaction
  def onCVEAction(self, action, **args): pass
  
+40 −2
Original line number Diff line number Diff line
@@ -19,9 +19,8 @@ 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, redirect, jsonify, send_file
from flask import Flask, render_template, request, redirect, jsonify, send_file, abort
from flask.ext.login import LoginManager, current_user, login_user, logout_user, login_required
from werkzeug import secure_filename
from passlib.hash import pbkdf2_sha256
from redis import exceptions as redisExceptions

@@ -39,6 +38,7 @@ from logging.handlers import RotatingFileHandler

from lib.User import User
from lib.Config import Configuration
from lib.PluginManager import PluginManager
from lib.Toolkit import toStringFormattedCPE, toOldCPE, currentTime, isURL, vFeedName, convertDateToDBFormat
import lib.CVEs as cves
import lib.DatabaseLayer as db
@@ -55,6 +55,7 @@ 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()
plugManager = PluginManager()

# login manager
login_manager = LoginManager()
@@ -328,7 +329,42 @@ def unseen(r):
    settings, cve = getFilterSettingsFromPost(r)
    return render_template('index.html', settings=settings, cve=cve, r=r, pageLength=pageLength)

# Plugins
@app.route('/_get_plugins', methods=['GET'])
def get_plugins():
    if not current_user.is_authenticated(): # Don't show plugins requiring auth if not authenticated
        plugins = [{"name": x.getName(), "link": x.getUID()} for x in plugManager.getWebPlugins() if not x.requiresAuth]
    else:
        plugins = [{"name": x.getName(), "link": x.getUID()} for x in plugManager.getWebPlugins()]
    return jsonify({"plugins": plugins})

@app.route('/plugin/_get_cve_actions', methods=['GET'])
def get_cve_actions():
    cve = request.args.get('cve', type=str)
    if not current_user.is_authenticated(): # Don't show actions requiring auth if not authenticated
        actions = [x for x in plugManager.getCVEActions(current_user=current_user, cve=cve) if not x['auth']]
    else:
        actions = plugManager.getCVEActions(current_user=current_user, cve=cve)
    return jsonify({"actions": actions})

@app.route('/plugin/<plugin>', methods=['GET'])
def openPlugin(plugin):
    if plugManager.requiresAuth(plugin) and not current_user.is_authenticated():
        return render_template("requiresAuth.html")
    else:
        page, args = plugManager.openPage(plugin, current_user=current_user)
        if page: return render_template("plugins/%s"%page, **args)
        else: abort(404)

@app.route('/plugin/<plugin>/_cve_action/<action>', methods=['GET'])
def jsonCVEAction(plugin, action):
    cve = request.args.get('cve', type=str).split(",")
    if plugManager.onCVEAction(cve, plugin, action, current_user=current_user):
        return jsonify({'status': 'plugin_action_complete'})
    else:
        return jsonify({'status': 'plugin_action_failed'})

# API
@app.route('/api/cpe2.3/<path:cpe>', methods=['GET'])
def cpe23(cpe):
    cpe = toStringFormattedCPE(cpe)
@@ -805,6 +841,8 @@ if __name__ == '__main__':
        file_handler.setFormatter(formatter)
        app.logger.addHandler(file_handler)

    plugManager.loadPlugins()

    if flaskDebug:
        # start debug flask server
        app.run(host=flaskHost, port=flaskPort, debug=flaskDebug)
Loading