def task_screenshots(request, task_id, screenshot=None): folder_path = os.path.join(cwd(), "storage", "analyses", str(task_id), "shots") if os.path.exists(folder_path): if screenshot: screenshot_name = "{0}.jpg".format(screenshot) screenshot_path = os.path.join(folder_path, screenshot_name) if os.path.exists(screenshot_path): response = HttpResponse(FileWrapper( open(screenshot_path, "rb")), content_type='image/jpeg') return response else: return json_error_response("Screenshot not found") else: zip_data = io.BytesIO() zip_file = zipfile.ZipFile(zip_data, "w", zipfile.ZIP_STORED) for shot_name in os.listdir(folder_path): zip_file.write(os.path.join(folder_path, shot_name), shot_name) zip_file.close() zip_data.seek(0) response = file_response( data=zip_data, filename="analysis_screenshots_%s.tar" % str(task_id), content_type="application/zip") return response return json_error_response("Task not found")
def feedback_send(request, body): task_id = body.get("task_id") if not task_id or not task_id.isdigit(): return json_error_response("Task id has to be an integer") task_id = int(task_id) f = CuckooFeedback() try: feedback_id = f.send_form(task_id=task_id, name=body.get("name"), company=body.get("company"), email=body.get("email"), message=body.get("message"), include_files=body.get( "include_analysis", False), memdump=body.get("include_memdump", False)) except CuckooFeedbackError as e: return json_error_response(str(e)) return JsonResponse({ "status": True, "feedback_id": feedback_id, }, safe=False)
def tasks_stats(request, body): """ Fetches the number of analysis over a given period for the "failed" and "successful" states. Values are returned in months. :param days: integer; the amount of days to go back in time starting from today. :return: A list of months and their statistics """ now = datetime.datetime.now() days = body.get("days", 365) if not isinstance(days, int): return json_error_response("parameter \"days\" not an integer") past = now - datetime.timedelta(days=days) tasks = db.list_tasks(filter_by="added_on", operators="between", values=(past, now), order_by="added_on", details=False) def _rtn_structure(start): _data = [] for i in range(0, 12): if (now - start).total_seconds() < 0: return _data _data.append({ "month": start.month, "year": start.year, "month_human": calendar.month_name[start.month], "num": 0 }) start = start + dateutil.relativedelta.relativedelta(months=1) return _data if not tasks: return json_error_response("No tasks found") data = { "analysis": _rtn_structure(tasks[0].added_on), "failed": _rtn_structure(tasks[0].added_on) } for task in tasks: added_on = task.added_on success = "analysis" if task.status == "reported" else "failed" entry = next( (z for z in data[success] if z["month"] == added_on.month and z["year"] == added_on.year ), None) if entry: entry["num"] += 1 return JsonResponse({"status": True, "data": data}, safe=False)
def task_screenshots(request, task_id, screenshot=None): folder_path = os.path.join(cwd(), "storage", "analyses", str(task_id), "shots") if os.path.exists(folder_path): if screenshot: screenshot_name = "{0}.jpg".format(screenshot) screenshot_path = os.path.join(folder_path, screenshot_name) if os.path.exists(screenshot_path): response = HttpResponse(FileWrapper(open(screenshot_path, "rb")), content_type='image/jpeg') return response else: return json_error_response("Screenshot not found") else: zip_data = io.BytesIO() zip_file = zipfile.ZipFile(zip_data, "w", zipfile.ZIP_STORED) for shot_name in os.listdir(folder_path): zip_file.write(os.path.join(folder_path, shot_name), shot_name) zip_file.close() zip_data.seek(0) response = file_response(data=zip_data, filename="analysis_screenshots_%s.tar" % str(task_id), content_type="application/zip") return response return json_error_response("Task not found")
def tasks_stats(request, body): """ Fetches the number of analysis over a given period for the "failed" and "successful" states. Values are returned in months. :param days: integer; the amount of days to go back in time starting from today. :return: A list of months and their statistics """ now = datetime.datetime.now() days = body.get("days", 365) if not isinstance(days, int): return json_error_response("parameter \"days\" not an integer") q = db.Session().query(Task) q = q.filter(Task.added_on.between( now - datetime.timedelta(days=days), now) ) q = q.order_by(sqlalchemy.asc(Task.added_on)) tasks = q.all() def _rtn_structure(start): _data = [] for i in range(0, 12): if (now - start).total_seconds() < 0: return _data _data.append({ "month": start.month, "year": start.year, "month_human": calendar.month_name[start.month], "num": 0 }) start = start + dateutil.relativedelta.relativedelta(months=1) return _data if not tasks: return json_error_response("No tasks found") data = { "analysis": _rtn_structure(tasks[0].added_on), "failed": _rtn_structure(tasks[0].added_on) } for task in tasks: added_on = task.added_on success = "analysis" if task.status == "reported" else "failed" entry = next((z for z in data[success] if z["month"] == added_on.month and z["year"] == added_on.year), None) if entry: entry["num"] += 1 return JsonResponse({"status": True, "data": data}, safe=False)
def task_rereport(request, body): task_id = body.get("task_id") if not task_id: return json_error_response("Task not set") task = db.view_task(task_id) if task: if task.status == TASK_REPORTED: db.set_status(task_id, TASK_COMPLETED) return JsonResponse({"status": True}) return JsonResponse({"status": False}) return json_error_response("Task not found")
def task_rereport(request, body): task_id = body.get("task_id") if not task_id: return json_error_response("Task not set") task = db.view_task(task_id) if task: if task.status == TASK_REPORTED: db.set_status(task_id, TASK_COMPLETED) return JsonResponse({"status": True}) return JsonResponse({"status": False}) return json_error_response("Task not found")
def presubmit(request): files = request.FILES.getlist("files[]") data = [] if files: for f in files: data.append({ "name": f.name, "data": f.file, }) submit_id = submit_manager.pre(submit_type="files", data=data) return redirect("submission/pre", submit_id=submit_id) else: body = json.loads(request.body) submit_type = body["type"] if submit_type != "strings": return json_error_response("type not \"strings\"") submit_id = submit_manager.pre( submit_type=submit_type, data=body["data"].split("\n") ) return JsonResponse({ "status": True, "submit_id": submit_id, }, encoder=JsonSerialize)
def http_data(request, body): task_id = body.get("task_id", None) request_body = body.get("request_body", False) protocol = body.get("protocol", None) request_index = body.get("request_index", None) if not task_id or not isinstance(request_index, int): return json_error_response( "missing task_id or valid request_index" ) report = AnalysisController.get_report(task_id) if request_body: # @TO-DO: parse raw http request data, filter out body req = "" resp = report["analysis"]["network"]["http"][request_index]["data"] else: req, resp = AnalysisNetworkApi._request_response( report, protocol, request_index ) return JsonResponse({ "request": req, "response": resp, }, safe=False)
def store_screenshots(request, task_id, body): if not body or not isinstance(body, list): return json_error_response("screenshots missing") report = ControlApi.get_report(int(task_id)) if not report: return json_error_response("report missing") for scr in body: sid = scr.get("id", None) data = scr.get("data", None) try: if sid is None or not data: raise ValueError ftype, b64 = data.split(",") if ftype != "data:image/png;base64": raise ValueError f = base64.b64decode(b64) if f[:4] != "\x89PNG": raise ValueError except ValueError: return json_error_response("invalid format") shot_path = cwd( "shots", "remotecontrol_%d.png" % int(sid), analysis=int(task_id) ) open(shot_path, "wb").write(f) shot_blob = {} shot = File(shot_path) if shot.valid(): shot_id = mdb.store_file(shot) shot_blob["original"] = shot_id if shot_blob: report["shots"].append(shot_blob) mdb.db.analysis.save(report) return JsonResponse({ "status": "success", })
def get(request, sha256): filepath = cwd("storage", "binaries", sha256) if os.path.exists(filepath): return json_error_response("File not found") return HttpResponse( FileWrapper(open(filepath, "rb")), content_type="application/octet-stream; charset=UTF-8")
def store_screenshots(request, task_id, body): if not body or not isinstance(body, list): return json_error_response("screenshots missing") report = ControlApi.get_report(int(task_id)) if not report: return json_error_response("report missing") for scr in body: sid = scr.get("id", None) data = scr.get("data", None) try: if sid is None or not data: raise ValueError ftype, b64 = data.split(",") if ftype != "data:image/png;base64": raise ValueError f = base64.b64decode(b64) if f[:4] != "\x89PNG": raise ValueError except ValueError: return json_error_response("invalid format") shot_path = cwd("shots", "remotecontrol_%d.png" % int(sid), analysis=int(task_id)) open(shot_path, "wb").write(f) shot_blob = {} shot = File(shot_path) if shot.valid(): shot_id = mdb.store_file(shot) shot_blob["original"] = shot_id if shot_blob: report["shots"].append(shot_blob) mdb.db.analysis.save(report) return JsonResponse({ "status": "success", })
def get_files(request, body): task_id = body.get('task_id', None) if not task_id: return json_error_response("invalid task_id") report = AnalysisController.get_report(task_id) if not report["analysis"].get("info", {}).get("analysis_path"): raise CuckooApiError("old-style analysis") analysis_path = report["analysis"]["info"]["analysis_path"] try: dirs, files = ExportController.get_files(analysis_path) except Exception as e: return json_error_response(message=str(e)) return JsonResponse({"dirs": dirs, "files": files}, safe=False)
def get(request, sha256): filepath = cwd("storage", "binaries", sha256) if os.path.exists(filepath): return json_error_response("File not found") return HttpResponse( FileWrapper(open(filepath, "rb")), content_type="application/octet-stream; charset=UTF-8" )
def tasks_info(request, body): task_ids = body.get("task_ids", []) if not list_of_ints(task_ids): return json_error_response("Invalid task IDs given!") data = {} for task in db.view_tasks(task_ids): data[task.id] = normalize_task(task.to_dict()) return JsonResponse({"status": True, "data": data}, safe=False)
def tasks_info(request, body): task_ids = body.get("task_ids", []) if not list_of_ints(task_ids): return json_error_response("Invalid task IDs given!") data = {} for task in db.view_tasks(task_ids): data[task.id] = normalize_task(task.to_dict()) return JsonResponse({"status": True, "data": data}, safe=False)
def get_files(request, body): task_id = body.get('task_id', None) if not task_id: return json_error_response("invalid task_id") report = AnalysisController.get_report(task_id) if not report["analysis"].get("info", {}).get("analysis_path"): raise CuckooApiError("old-style analysis") analysis_path = report["analysis"]["info"]["analysis_path"] try: dirs, files = ExportController.get_files(analysis_path) except Exception as e: return json_error_response(message=str(e)) return JsonResponse({"dirs": dirs, "files": files}, safe=False)
def misp_export(request, body): task_id = body.get('task_id', None) if not task_id: return json_error_response("invalid task_id") response = ExportController.misp_export(task_id=task_id) return JsonResponse(response, safe=False)
def get(request, task_id): file_path = cwd("dump.pcap", analysis=task_id) if not os.path.exists(file_path): return json_error_response("File not found") return file_response( data=open(file_path, "rb"), filename="analysis_pcap_dump_%s.pcap" % str(task_id), content_type="application/octet-stream; charset=UTF-8" )
def get(request, task_id): file_path = cwd("dump.pcap", analysis=task_id) if not os.path.exists(file_path): return json_error_response("File not found") return file_response( data=open(file_path, "rb"), filename="analysis_pcap_dump_%s.pcap" % str(task_id), content_type="application/octet-stream; charset=UTF-8" )
def task_info(request, task_id): try: return JsonResponse({ "status": True, "data": { # TODO Test correctness of the following. "task": normalize_task(db.view_task(task_id)), }, }, safe=False) except Exception as e: return json_error_response(str(e))
def view(request, name=None): """ Returns information about a machine :param name: machine name :return: Machine information as a dictionary """ machine = db.view_machine(name=name) if machine: return JsonResponse({"status": True, "data": machine.to_dict()}) else: return json_error_response("Machine not found")
def view(request, name=None): """ Returns information about a machine :param name: machine name :return: Machine information as a dictionary """ machine = db.view_machine(name=name) if machine: return JsonResponse({"status": True, "data": machine.to_dict()}) else: return json_error_response("Machine not found")
def task_info(request, task_id): try: return JsonResponse({ "status": True, "data": { # TODO Test correctness of the following. "task": normalize_task(db.view_task(task_id)), }, }, safe=False) except Exception as e: return json_error_response(str(e))
def get_files(request, body): task_id = body.get('task_id', None) if not task_id: return json_error_response("invalid task_id") if not os.path.isfile(cwd("reports", "report.json", analysis=task_id)): raise Http404("Task %s: report.json not found" % task_id) dirs, files = Task.get_files(task_id) return JsonResponse({"dirs": dirs, "files": files}, safe=False)
def tasks_reschedule(request, task_id, priority=None): """ Reschedules a task :param body: required: task_id, priority :return: new task_id """ if not db.view_task(task_id): return json_error_response("There is no analysis with the specified ID") new_task_id = db.reschedule(task_id, priority) if new_task_id: return JsonResponse({"status": True, "task_id": new_task_id}, safe=False) else: return json_fatal_response("An error occurred while trying to " "reschedule the task")
def tasks_reschedule(request, task_id, priority=None): """ Reschedules a task :param body: required: task_id, priority :return: new task_id """ if not db.view_task(task_id): return json_error_response("There is no analysis with the specified ID") new_task_id = db.reschedule(task_id, priority) if new_task_id: return JsonResponse({"status": True, "task_id": new_task_id}, safe=False) else: return json_fatal_response("An error occurred while trying to " "reschedule the task")
def export_estimate_size(request, body): task_id = body.get('task_id') taken_dirs = body.get("dirs", []) taken_files = body.get("files", []) if not taken_dirs and not taken_files: return JsonResponse({"size": 0, "size_human": "-"}, safe=False) if not task_id: return json_error_response("invalid task_id") size = ExportController.estimate_size(task_id=task_id, taken_dirs=taken_dirs, taken_files=taken_files) return JsonResponse(size, safe=False)
def export_estimate_size(request, body): task_id = body.get('task_id') taken_dirs = body.get("dirs", []) taken_files = body.get("files", []) if not taken_dirs and not taken_files: return JsonResponse({"size": 0, "size_human": "-"}, safe=False) if not task_id: return json_error_response("invalid task_id") size = ExportController.estimate_size(task_id=task_id, taken_dirs=taken_dirs, taken_files=taken_files) return JsonResponse(size, safe=False)
def list_tasks(request, status=None, task_id=None): tasks = [] limit = request.GET.get("limit", 100) offset = request.GET.get("offset", 0) category = request.GET.get("category") added_on = request.GET.get("added_on") if added_on: if not added_on.isdigit(): return json_error_response( "added_on should be epoch timestamp") added_on = datetime.datetime.fromtimestamp(int(added_on)) taskslist = db.list_tasks(filter_by="added_on", operators=">=", values=added_on, status=status, category=category, id=task_id, limit=limit, offset=offset, order_by="added_on") id_list = [] for row in taskslist: task = row.to_dict() # For backwards compatibility until web deals with target lists # TODO Remove if row.targets: target = row.targets[0].target task["target"] = target.decode("latin-1") task["category"] = target.category tasks.append(task) id_list.append(row.id) response = { "offset": offset, "limit": limit, "category": category, "date": added_on, "task_id": task_id if task_id is None else int(task_id), "available_ids": id_list, "status": status, "tasks": tasks } return JsonResponse(response, safe=False)
def view(request, md5=None, sha256=None, sample_id=None): data = {} if md5: sample = db.find_sample(md5=md5) elif sha256: sample = db.find_sample(sha256=sha256) elif sample_id: sample = db.view_sample(sample_id) else: return json_fatal_response("Invalid lookup term") if not sample: return json_error_response("File not found") data["sample"] = sample.to_dict() return JsonResponse({"status": True, "data": data})
def view(request, md5=None, sha256=None, sample_id=None): data = {} if md5: sample = db.find_target(md5=md5) elif sha256: sample = db.find_target(sha256=sha256) elif sample_id: sample = db.view_target(id=sample_id) else: return json_fatal_response("Invalid lookup term") if not sample: return json_error_response("File not found") data["sample"] = sample.to_dict() return JsonResponse({"status": True, "data": data})
def export_estimate_size(request, body): task_id = body.get('task_id') taken_dirs = body.get("dirs", []) taken_files = body.get("files", []) if not taken_dirs and not taken_files: return JsonResponse({"size": 0, "size_human": "-"}, safe=False) if not task_id: return json_error_response("invalid task_id") size = Task.estimate_export_size( task_id=task_id, taken_dirs=taken_dirs, taken_files=taken_files ) size_response = { "size": int(size), "size_human": filesizeformat(size) } return JsonResponse(size_response, safe=False)
def task_delete(request, task_id): """ Deletes a task :param body: required: task_id :return: """ task = db.view_task(task_id) if task: if task.status == TASK_RUNNING: return json_fatal_response("The task is currently being " "processed, cannot delete") if db.delete_task(task_id): Folders.delete( os.path.join(cwd(), "storage", "analyses", "%d" % task_id)) else: return json_fatal_response("An error occurred while trying to " "delete the task") else: return json_error_response("Task not found") return JsonResponse({"status": True})
def task_delete(request, task_id): """ Deletes a task :param body: required: task_id :return: """ task = db.view_task(task_id) if task: if task.status == TASK_RUNNING: return json_fatal_response("The task is currently being " "processed, cannot delete") if db.delete_task(task_id): Folders.delete(os.path.join(cwd(), "storage", "analyses", "%d" % task_id)) else: return json_fatal_response("An error occurred while trying to " "delete the task") else: return json_error_response("Task not found") return JsonResponse({"status": True})
def http_data(request, body): task_id = body.get("task_id", None) request_body = body.get("request_body", False) protocol = body.get("protocol", None) request_index = body.get("request_index", None) if not task_id or not isinstance(request_index, int): return json_error_response( "missing task_id or valid request_index") report = AnalysisController.get_report(task_id) if request_body: # @TO-DO: parse raw http request data, filter out body req = "" resp = report["analysis"]["network"]["http"][request_index]["data"] else: req, resp = AnalysisNetworkApi._request_response( report, protocol, request_index) return JsonResponse({ "request": req, "response": resp, }, safe=False)
def feedback_send(request, body): f = CuckooFeedback() task_id = body.get("task_id") if task_id and task_id.isdigit(): task_id = int(task_id) try: feedback_id = f.send_form( task_id=task_id, name=body.get("name"), company=body.get("company"), email=body.get("email"), message=body.get("message"), json_report=body.get("include_analysis", False), memdump=body.get("include_memdump", False) ) except CuckooFeedbackError as e: return json_error_response(str(e)) return JsonResponse({ "status": True, "feedback_id": feedback_id, }, safe=False)
def task_report(request, task_id, report_format="json"): # @TO-DO: test /api/task/report/<task_id>/all/?tarmode=bz2 # duplicate filenames? task_id = int(task_id) tarmode = request.REQUEST.get("tarmode", "bz2") formats = { "json": "report.json", "html": "report.html", } bz_formats = { "all": {"type": "-", "files": ["memory.dmp"]}, "dropped": {"type": "+", "files": ["files"]}, "package_files": {"type": "+", "files": ["package_files"]}, } tar_formats = { "bz2": "w:bz2", "gz": "w:gz", "tar": "w", } if report_format.lower() in formats: report_path = os.path.join(cwd(), "storage", "analyses", str(task_id), "reports", formats[report_format.lower()]) elif report_format.lower() in bz_formats: bzf = bz_formats[report_format.lower()] srcdir = os.path.join(cwd(), "storage", "analyses", str(task_id)) s = io.BytesIO() # By default go for bz2 encoded tar files (for legacy reasons). if tarmode not in tar_formats: tarmode = tar_formats["bz2"] else: tarmode = tar_formats[tarmode] tar = tarfile.open(fileobj=s, mode=tarmode, dereference=True) for filedir in os.listdir(srcdir): filepath = os.path.join(srcdir, filedir) if not os.path.exists(filepath): continue if bzf["type"] == "-" and filedir not in bzf["files"]: tar.add(filepath, arcname=filedir) if bzf["type"] == "+" and filedir in bzf["files"]: tar.add(filepath, arcname=filedir) tar.close() s.seek(0) response = file_response(data=s, filename="analysis_report_%s.tar" % str(task_id), content_type="application/x-tar; charset=UTF-8") return response else: return json_fatal_response("Invalid report format") if os.path.exists(report_path): if report_format == "json": response = file_response(data=open(report_path, "rb"), filename="analysis_report_%s.json" % str(task_id), content_type="application/json; charset=UTF-8") return response else: return open(report_path, "rb").read() else: return json_error_response("Report not found")
def tasks_recent(request, body): limit = body.get("limit", 100) offset = body.get("offset", 0) # Various filters. cats = body.get("cats") packs = body.get("packs") score_range = body.get("score") filters = {} if cats: if not list_of_strings(cats): return json_error_response("invalid categories") filters["info.category"] = {"$in": cats} if packs: if not list_of_strings(packs): return json_error_response("invalid packages") filters["info.package"] = {"$in": packs} if score_range and isinstance(score_range, basestring): if score_range.count("-") != 1: return json_error_response("faulty score") score_min, score_max = score_range.split("-") if not score_min.isdigit() or not score_max.isdigit(): return json_error_response("faulty score") score_min = int(score_min) score_max = int(score_max) if score_min < 0 or score_min > 10: return json_error_response("faulty score") if score_max < 0 or score_max > 10: return json_error_response("faulty score") # Because scores can be higher than 10. # TODO Once we start capping the score, limit this naturally. if score_max == 10: score_max = 999 filters["info.score"] = { "$gte": score_min, "$lte": score_max, } if not isinstance(offset, (int, long)): return json_error_response("invalid offset") if not isinstance(limit, (int, long)): return json_error_response("invalid limit") # TODO Use a mongodb abstraction class once there is one. cursor = mongo.db.analysis.find(filters, ["info", "target"], sort=[("_id", pymongo.DESCENDING) ]).limit(limit).skip(offset) tasks = {} for row in cursor: info = row.get("info", {}) if not info: continue category = info.get("category") if category == "file": f = row.get("target", {}).get("file", {}) if f.get("name"): target = os.path.basename(f["name"]) else: target = None md5 = f.get("md5") or "-" elif category == "url": target = row["target"]["url"] md5 = "-" elif category == "archive": target = row.get("target", {}).get("human", "-") md5 = "-" else: target = None md5 = "-" tasks[info["id"]] = { "id": info["id"], "target": target, "md5": md5, "category": category, "added_on": info.get("added"), "completed_on": info.get("ended"), "status": "reported", "score": info.get("score"), } return JsonResponse( { "tasks": sorted( tasks.values(), key=lambda task: task["id"], reverse=True), }, safe=False)
def task_report(request, task_id, report_format="json"): # @TO-DO: test /api/task/report/<task_id>/all/?tarmode=bz2 # duplicate filenames? task_id = int(task_id) tarmode = request.REQUEST.get("tarmode", "bz2") formats = { "json": "report.json", "html": "report.html", } bz_formats = { "all": { "type": "-", "files": ["memory.dmp"] }, "dropped": { "type": "+", "files": ["files"] }, "package_files": { "type": "+", "files": ["package_files"] }, } tar_formats = { "bz2": "w:bz2", "gz": "w:gz", "tar": "w", } if report_format.lower() in formats: report_path = os.path.join(cwd(), "storage", "analyses", str(task_id), "reports", formats[report_format.lower()]) elif report_format.lower() in bz_formats: bzf = bz_formats[report_format.lower()] srcdir = os.path.join(cwd(), "storage", "analyses", str(task_id)) s = io.BytesIO() # By default go for bz2 encoded tar files (for legacy reasons). if tarmode not in tar_formats: tarmode = tar_formats["bz2"] else: tarmode = tar_formats[tarmode] tar = tarfile.open(fileobj=s, mode=tarmode, dereference=True) for filedir in os.listdir(srcdir): filepath = os.path.join(srcdir, filedir) if not os.path.exists(filepath): continue if bzf["type"] == "-" and filedir not in bzf["files"]: tar.add(filepath, arcname=filedir) if bzf["type"] == "+" and filedir in bzf["files"]: tar.add(filepath, arcname=filedir) tar.close() s.seek(0) response = file_response( data=s, filename="analysis_report_%s.tar" % str(task_id), content_type="application/x-tar; charset=UTF-8") return response else: return json_fatal_response("Invalid report format") if os.path.exists(report_path): if report_format == "json": response = file_response( data=open(report_path, "rb"), filename="analysis_report_%s.json" % str(task_id), content_type="application/json; charset=UTF-8") return response else: return open(report_path, "rb").read() else: return json_error_response("Report not found")
def tasks_recent(request, body): limit = body.get("limit", 100) offset = body.get("offset", 0) # Various filters. cats = body.get("cats") packs = body.get("packs") score_range = body.get("score") filters = {} if cats: if not list_of_strings(cats): return json_error_response("invalid categories") filters["info.category"] = {"$in": cats} if packs: if not list_of_strings(packs): return json_error_response("invalid packages") filters["info.package"] = {"$in": packs} if score_range and isinstance(score_range, basestring): if score_range.count("-") != 1: return json_error_response("faulty score") score_min, score_max = score_range.split("-") if not score_min.isdigit() or not score_max.isdigit(): return json_error_response("faulty score") score_min = int(score_min) score_max = int(score_max) if score_min < 0 or score_min > 10: return json_error_response("faulty score") if score_max < 0 or score_max > 10: return json_error_response("faulty score") # Because scores can be higher than 10. # TODO Once we start capping the score, limit this naturally. if score_max == 10: score_max = 999 filters["info.score"] = { "$gte": score_min, "$lte": score_max, } if not isinstance(offset, (int, long)): return json_error_response("invalid offset") if not isinstance(limit, (int, long)): return json_error_response("invalid limit") # TODO Use a mongodb abstraction class once there is one. cursor = mongo.db.analysis.find( filters, ["info", "target"], sort=[("_id", pymongo.DESCENDING)] ).limit(limit).skip(offset) tasks = {} for row in cursor: info = row.get("info", {}) if not info or info["id"] in tasks: continue category = info.get("category") if category == "file": f = row.get("target", {}).get("file", {}) if f.get("name"): target = os.path.basename(f["name"]) else: target = None md5 = f.get("md5") or "-" elif category == "url": target = row["target"]["url"] md5 = "-" elif category == "archive": target = row.get("target", {}).get("human", "-") md5 = "-" else: target = None md5 = "-" tasks[info["id"]] = { "id": info["id"], "target": target, "md5": md5, "category": category, "added_on": info.get("added"), "completed_on": info.get("ended"), "status": "reported", "score": info.get("score"), } return JsonResponse({ "tasks": sorted( tasks.values(), key=lambda task: task["id"], reverse=True ), }, safe=False)