Skip to content
This repository was archived by the owner on Jan 19, 2022. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions minion/backend/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ def session_report_issue(scan_id, session_id, issue):
scans.update({"id": scan_id, "sessions.id": session_id},
{"$push": {"sessions.$.issues": issue}})

@celery.task
def session_report_artifact(scan_id, session_id, artifact):
scans.update({"id": scan_id, "sessions.id": session_id},
{"$push": {"sessions.$.artifacts": artifact}})

@celery.task
def session_finish(scan_id, session_id, state, t, failure=None):
if failure:
Expand Down Expand Up @@ -456,6 +461,12 @@ def signal_handler(signum, frame):
if msg['msg'] == 'progress':
pass # TODO

# Artifact: save the report
if msg['msg'] == 'artifact':
send_task("minion.backend.tasks.session_report_artifact",
args=[scan_id, session_id, msg['data']],
queue='state').get()

# Finish: update the session state, wait for the plugin runner to finish, return the state
if msg['msg'] == 'finish':
finished = msg['data']['state']
Expand Down
4 changes: 4 additions & 0 deletions minion/backend/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json
import pkgutil
import operator
import os

from flask import abort, Flask, jsonify, request, session
from pymongo import MongoClient
Expand Down Expand Up @@ -114,6 +115,9 @@ def sanitize_session(session):
for field in ('created', 'queued', 'started', 'finished'):
if session.get(field) is not None:
session[field] = calendar.timegm(session[field].utctimetuple())
for artifact in session['artifacts']:
for idx, path in enumerate(artifact['paths']):
artifact['paths'][idx] = os.path.basename(path)
return session

def sanitize_time(t):
Expand Down
19 changes: 17 additions & 2 deletions minion/backend/views/scans.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import datetime
import functools
import uuid
from flask import jsonify, request
from flask import jsonify, request, send_file

import minion.backend.utils as backend_utils
import minion.backend.tasks as tasks
Expand Down Expand Up @@ -140,7 +140,7 @@ def post_scan_create():
"plugin": plugins[step['plugin_name']]['descriptor'],
"configuration": session_configuration, # TODO Do recursive merging here, not just at the top level
"description": step["description"],
"artifacts": {},
"artifacts": [],
"issues": [],
"created": now,
"queued": None,
Expand Down Expand Up @@ -187,3 +187,18 @@ def put_scan_control(scan_id):
scans.update({"id": scan_id}, {"$set": {"state": "STOPPING", "queued": datetime.datetime.utcnow()}})
tasks.scan_stop.apply_async([scan['id']], queue='state')
return jsonify(success=True)

@app.route("/scans/<scan_id>/artifact/<artifact_name>", methods=["GET"])
@permission
def get_artifact(scan_id, artifact_name):
# Find the scan
scan = scans.find_one({"id": scan_id})
if not scan:
return jsonify(success=False, error='no-such-scan')
# Find the artifact
for session in scan['sessions']:
for artifact in session['artifacts']:
for path in artifact['paths']:
if artifact_name in path:
return send_file(path)
return jsonify(success=False, error='no-such-artifact')
29 changes: 27 additions & 2 deletions scripts/minion-plugin-runner
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class JSONCallbacks:
self._write({"msg": "issue", "data": issue})

def report_artifacts(self, name, paths):
pass
self._write({"msg": "artifact", "data": {"name": name, "paths": paths}})

def report_finish(self, state = "FINISHED"):
self._write({"msg": "finish", "data": {"state": state}})
Expand Down Expand Up @@ -143,15 +143,40 @@ if __name__ == "__main__":
callbacks = JSONCallbacks()

#
# Setup the work directory if it does not exist yet
# Setup the report directory if it does not exist yet and is specified in configuration
#
if 'report_dir' in configuration:
report_directory = configuration['report_dir']

if not os.path.exists(report_directory):
try:
os.mkdir(report_directory)
except Exception as e:
logging.error("Cannot create report directory (%s): %s" % (report_directory, str(e)))
failure = {
"message": "Failed to create report directory",
"exception": traceback.format_exc(),
"hostname": socket.gethostname()
}
callbacks.report_finish(state = AbstractPlugin.EXIT_STATE_FAILED, failure = failure)
sys.exit(1)

#
# Setup the work directory if it does not exist yet
#
work_directory = os.path.join(options.work_root, plugin_session_id)

if not os.path.exists(work_directory):
try:
os.mkdir(work_directory)
except Exception as e:
logging.error("Cannot create work directory (%s): %s" % (options.work_directory, str(e)))
failure = {
"message": "Failed to create work directory",
"exception": traceback.format_exc(),
"hostname": socket.gethostname()
}
callbacks.report_finish(state = AbstractPlugin.EXIT_STATE_FAILED, failure = failure)
sys.exit(1)

os.chdir(work_directory)
Expand Down