def post(self, format_output="json"): """ Free query Api endpoint that can be used to freely (within the allowed parameters) query the cve search database. The request sample payload displays a request body for a single query; multiple request can be combined within a comma separated list and send in a single request. In this case the responses will be send back in a list. For each request a separate list entry with the results. """ headers = parse_headers(request.headers) database_connection = DatabaseHandler() output_format = ["json", "xml"] received_format = request.args.get("format", None) if received_format is None: format_output = format_output else: format_output = str(received_format) if format_output not in output_format: api.abort( 400, "Specified output format is not possible; possible options are: {}!" .format(output_format), ) try: body = request.json except Exception: return "Could not parse request body", 400 if isinstance(body, dict): result = database_connection.handle_api_json_query( JSONApiRequest(headers=headers, body=body)) elif isinstance(body, list): result = [] for each in body: result.append( database_connection.handle_api_json_query( JSONApiRequest(headers=headers, body=each))) if isinstance(result, tuple): # error response; just return it return result else: if format_output == "json": return Response( json.dumps( result, indent=4, sort_keys=True, default=json_util.default, ), mimetype="application/json", ) if format_output == "xml": return Response(dicttoxml(result), mimetype="text/xml")
def __init__(self): with open(os.path.join(runPath, "..", "etc/.schema_version")) as f: self.schema_version = json.loads(f.read()) self.dbh = DatabaseHandler() self.logger = logging.getLogger("SchemaChecker")
plugManager, pluginArgs, generate_full_query, ) from ..helpers.server_side_datatables import ServerSideDataTable from ..run import app logging.setLoggerClass(AppLogger) logger = logging.getLogger(__name__) DATATABLE_FILTER = defaultFilters default_breadcrumb_root(home, ".") dbh = DatabaseHandler() @home.route("/", methods=["GET"]) @register_breadcrumb(home, ".", "Home") def index(): return render_template("index.html", **config_args) def view_vendor_name(*args, **kwargs): try: return [ { "text": "Vendor-List", "url": "/browse",
def __init__(self, **kwargs): self.logger = logging.getLogger(__name__) self.methods = [] self._load_methods() self.api_sessions = {} self.dbh = DatabaseHandler()
def __init__(self): routes = [ { "r": "/api/", "m": ["GET"], "f": self.api_documentation }, { "r": "/api/cpe2.3/<path:cpe>", "m": ["GET"], "f": self.api_cpe23 }, { "r": "/api/cpe2.2/<path:cpe>", "m": ["GET"], "f": self.api_cpe22 }, { "r": "/api/cvefor/<path:cpe>", "m": ["GET"], "f": self.api_cvesFor }, { "r": "/api/cvefor/<path:cpe>/<limit>", "m": ["GET"], "f": self.api_cvesFor, }, { "r": "/api/cve/<cveid>", "m": ["GET"], "f": self.api_cve }, { "r": "/api/cwe", "m": ["GET"], "f": self.api_cwe }, { "r": "/api/cwe/<cwe_id>", "m": ["GET"], "f": self.api_cwe }, { "r": "/api/capec/<cweid>", "m": ["GET"], "f": self.api_capec }, { "r": "/api/capec/show/<capecid>", "m": ["GET"], "f": self.api_capec_by_id }, { "r": "/api/last", "m": ["GET"], "f": self.api_last }, { "r": "/api/last/", "m": ["GET"], "f": self.api_last }, { "r": "/api/last/<int:limit>", "m": ["GET"], "f": self.api_last }, { "r": "/api/query", "m": ["GET"], "f": self.api_query }, { "r": "/api/query", "m": ["POST"], "f": self.api_json_query }, { "r": "/api/browse", "m": ["GET"], "f": self.api_browse }, { "r": "/api/browse/", "m": ["GET"], "f": self.api_browse }, { "r": "/api/browse/<path:vendor>", "m": ["GET"], "f": self.api_browse }, { "r": "/api/search/<vendor>/<path:product>", "m": ["GET"], "f": self.api_search, }, { "r": "/api/search/<path:search>", "m": ["GET"], "f": self.api_text_search }, { "r": "/api/link/<key>/<value>", "m": ["GET"], "f": self.api_link }, { "r": "/api/dbInfo", "m": ["GET"], "f": self.api_dbInfo }, ] for route in routes: self.addRoute(route) self.database_connection = DatabaseHandler()
class API: app = Flask(__name__, static_folder="static", static_url_path="/static") app.config["MONGO_DBNAME"] = Configuration.getMongoDB() app.config["SECRET_KEY"] = str(random.getrandbits(256)) def __init__(self): routes = [ { "r": "/api/", "m": ["GET"], "f": self.api_documentation }, { "r": "/api/cpe2.3/<path:cpe>", "m": ["GET"], "f": self.api_cpe23 }, { "r": "/api/cpe2.2/<path:cpe>", "m": ["GET"], "f": self.api_cpe22 }, { "r": "/api/cvefor/<path:cpe>", "m": ["GET"], "f": self.api_cvesFor }, { "r": "/api/cvefor/<path:cpe>/<limit>", "m": ["GET"], "f": self.api_cvesFor, }, { "r": "/api/cve/<cveid>", "m": ["GET"], "f": self.api_cve }, { "r": "/api/cwe", "m": ["GET"], "f": self.api_cwe }, { "r": "/api/cwe/<cwe_id>", "m": ["GET"], "f": self.api_cwe }, { "r": "/api/capec/<cweid>", "m": ["GET"], "f": self.api_capec }, { "r": "/api/capec/show/<capecid>", "m": ["GET"], "f": self.api_capec_by_id }, { "r": "/api/last", "m": ["GET"], "f": self.api_last }, { "r": "/api/last/", "m": ["GET"], "f": self.api_last }, { "r": "/api/last/<int:limit>", "m": ["GET"], "f": self.api_last }, { "r": "/api/query", "m": ["GET"], "f": self.api_query }, { "r": "/api/query", "m": ["POST"], "f": self.api_json_query }, { "r": "/api/browse", "m": ["GET"], "f": self.api_browse }, { "r": "/api/browse/", "m": ["GET"], "f": self.api_browse }, { "r": "/api/browse/<path:vendor>", "m": ["GET"], "f": self.api_browse }, { "r": "/api/search/<vendor>/<path:product>", "m": ["GET"], "f": self.api_search, }, { "r": "/api/search/<path:search>", "m": ["GET"], "f": self.api_text_search }, { "r": "/api/link/<key>/<value>", "m": ["GET"], "f": self.api_link }, { "r": "/api/dbInfo", "m": ["GET"], "f": self.api_dbInfo }, ] for route in routes: self.addRoute(route) self.database_connection = DatabaseHandler() def addRoute(self, route): self.app.add_url_rule(route["r"], view_func=route["f"], methods=route["m"]) ############# # Decorator # ############# def api(funct): @wraps(funct) def api_wrapper(*args, **kwargs): data = error = None # Get data (and possibly errors) try: data = funct(*args, **kwargs) except APIError as e: error = ({"status": "error", "reason": e.message}, e.status) except Exception as e: print(e) error = ({ "status": "error", "reason": "Internal server error" }, 500) # Check if data should be returned as html or data try: returnType = "application/json" if request.url_rule.rule.lower().startswith( "/api/") or request.url_rule.rule.lower().endswith( ".json"): # Support JSONP if request.args.get("callback", False): data = "%s(%s)" % (request.args.get("callback"), data) # Check API version for backwards compatibility. We'll call the old API v1.0 elif request.headers.get("Version") in ["1.1"]: # Get the requested return type returnType = request.headers.get("Accept", "*/*") # Default to JSON if any(t in returnType for t in ["json", "application/*", "text/*", "*/*"]): data = (error if error else { "status": "success", "data": data }) elif "plain" in returnType: pass # No need to do anything, but needs to be accepted else: data = ( { "status": "error", "reason": "Unknown Content-type requested", }, 415, ) returnType = "application/json" if type(data) is not str: if type(data) is tuple: data = list(data) data[0] = json.dumps( convertDatetime(dct=data[0]), indent=4, sort_keys=True, default=json_util.default, ) else: data = ( json.dumps( convertDatetime(dct=data), indent=4, sort_keys=True, default=json_util.default, ), 200, ) return Response(data[0], mimetype=returnType), data[1] except Exception as e: print(e) pass if error and error[1] == 500: raise (APIError(error[0]["reason"])) return data return api_wrapper ############# # FUNCTIONS # ############# def generate_minimal_query(self, f): query = [] # retrieving lists if f["rejectedSelect"] == "hide": query.append({ "summary": re.compile( r"^(?!\*\* REJECT \*\*\s+DO NOT USE THIS CANDIDATE NUMBER.*)" ) }) # cvss logic if f["cvssSelect"] == "above": query.append({"cvss": {"$gt": float(f["cvss"])}}) elif f["cvssSelect"] == "equals": query.append({"cvss": float(f["cvss"])}) elif f["cvssSelect"] == "below": query.append({"cvss": {"$lt": float(f["cvss"])}}) # date logic if f["timeSelect"] != "all": if f["startDate"]: startDate = parse_datetime(f["startDate"], ignoretz=True, dayfirst=True) if f["endDate"]: endDate = parse_datetime(f["endDate"], ignoretz=True, dayfirst=True) if f["timeSelect"] == "from": query.append({f["timeTypeSelect"]: {"$gt": startDate}}) elif f["timeSelect"] == "until": query.append({f["timeTypeSelect"]: {"$lt": endDate}}) elif f["timeSelect"] == "between": query.append( {f["timeTypeSelect"]: { "$gt": startDate, "$lt": endDate }}) elif f["timeSelect"] == "outside": query.append({ "$or": [ { f["timeTypeSelect"]: { "$lt": startDate } }, { f["timeTypeSelect"]: { "$gt": endDate } }, ] }) return query def filter_logic(self, filters, skip, limit=None): query = self.generate_minimal_query(filters) limit = limit if limit else self.args["pageLength"] return getCVEs(limit=limit, skip=skip, query=query) ########## # ROUTES # ########## # /api def api_documentation(self): return render_template("api.html") # /api/cpe2.3/<cpe> @api def api_cpe23(self, cpe): cpe = toStringFormattedCPE(cpe) return cpe, 200 if cpe else "None", 404 # /api/cpe2.2/<cpe> @api def api_cpe22(self, cpe): cpe = toOldCPE(cpe) return cpe, 200 if cpe else "None", 404 # /api/cvefor/<cpe> # /api/cvefor/<cpe>/<limit> @api def api_cvesFor(self, cpe, limit=0): cpe = urllib.parse.unquote_plus(cpe) return qcvesForCPE(cpe, int(limit)) # /api/cve/<cveid> @api def api_cve(self, cveid): cvesp = CveHandler(rankinglookup=True, namelookup=True, via4lookup=True, capeclookup=True) cve = cvesp.getcve(cveid=cveid.upper()) if not cve: raise (APIError("cve not found", 404)) return cve # /api/cwe # /api/cwe/<cwe_id> @api def api_cwe(self, cwe_id=None): return getCWEs(cwe_id) if cwe_id else getCWEs() # /api/capec/<cweid> @api def api_capec(self, cweid): return getCAPECFor(cweid) # /api/capec/show/<capecid> @api def api_capec_by_id(self, capecid): return getCAPEC(capecid) # /api/last # /api/last/ # /api/last/<limit> @api def api_last(self, limit=None): limit = limit if limit else 30 cvesp = CveHandler(rankinglookup=True, namelookup=True, via4lookup=True, capeclookup=True) cve = cvesp.get(limit=limit) return cve # /query @api def api_query(self): f = { "rejectedSelect": request.headers.get("rejected"), "cvss": request.headers.get("cvss_score"), "cvssSelect": request.headers.get("cvss_modifier"), "startDate": request.headers.get("time_start"), "endDate": request.headers.get("time_end"), "timeSelect": request.headers.get("time_modifier"), "timeTypeSelect": request.headers.get("time_type"), "skip": request.headers.get("skip"), "limit": request.headers.get("limit"), } try: skip = int(f["skip"]) if f["skip"] else 0 except: raise (APIError("skip must be an int", 400)) try: limit = int(f["limit"]) if f["limit"] else 0 except: raise (APIError("limit must be an int", 400)) return self.filter_logic(f, skip, limit) # /api/browse # /api/browse/ # /api/browse/<vendor> @api def api_browse(self, vendor=None): if vendor: vendor = urllib.parse.quote_plus(vendor).lower() try: browseList = getBrowseList(vendor) except redis_connection_error: raise (APIError( "Server could not connect to the browsing repository", 503)) if isinstance(browseList, dict): return browseList else: return {} # /api/search/<vendor>/<path:product> @api def api_search(self, vendor=None, product=None): if not (vendor and product): return {} search = vendor + ":" + product # Not using query.cvesForCPE, because that one gives too much info # return json.dumps(db.cvesForCPE(search), default=json_util.default) return cvesForCPE(search) # /api/search/<path:search> @api def api_text_search(self, search=None): return getSearchResults(search) # /api/link/<key>/<value> @api def api_link(self, key=None, value=None): key = self.htmlDecode(key) value = self.htmlDecode(value) regex = re.compile(re.escape(value), re.I) data = {"cves": via4Linked(key, regex)} cvssList = [float(x["cvss"]) for x in data["cves"] if x.get("cvss")] if cvssList: data["stats"] = { "maxCVSS": max(cvssList), "minCVSS": min(cvssList), "count": len(data["cves"]), } else: data["stats"] = { "maxCVSS": 0, "minCVSS": 0, "count": len(data["cves"]) } return data # /api/dbInfo @api def api_dbInfo(self): return getDBStats() def parse_headers(self, headers): ret_dict = {} for key, val in headers.items(): ret_dict[key] = val return ret_dict # /api/query def api_json_query(self): headers = self.parse_headers(request.headers) try: body = request.json except Exception: return "Could not parse request body", 400 result = self.database_connection.handle_api_json_query( JSONApiRequest(headers=headers, body=body)) return result ######################## # Web Server Functions # ######################## # signal handlers def sig_handler(self, sig, frame): print("Caught signal: %s" % sig) IOLoop.instance().add_callback(self.shutdown) def shutdown(self): MAX_WAIT_SECONDS_BEFORE_SHUTDOWN = 3 print("Stopping http server") self.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() def start(self): # 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) # self.app.logger.addHandler(file_handler) if flaskDebug: # start debug flask server self.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(): ssl_options = { "certfile": os.path.join(_runPath, "../../../", Configuration.getSSLCert()), "keyfile": os.path.join(_runPath, "../../../", Configuration.getSSLKey()), } else: ssl_options = None signal.signal(signal.SIGTERM, self.sig_handler) signal.signal(signal.SIGINT, self.sig_handler) self.http_server = HTTPServer(WSGIContainer(self.app), ssl_options=ssl_options) self.http_server.bind(flaskPort, address=flaskHost) self.http_server.start(0) # Forks multiple sub-processes IOLoop.instance().start()