From 46b8bdccbc8ff960e1aa6d6011392f3a68567180 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Tue, 13 Apr 2021 06:24:12 -0400 Subject: [PATCH] add TaskConfig to crash_reported and regression_reported events (#793) resolves #757 and #758 --- docs/webhook_events.md | 698 ++++++++++++++++++ .../__app__/onefuzzlib/notifications/main.py | 39 +- src/pytypes/onefuzztypes/events.py | 2 + 3 files changed, 733 insertions(+), 6 deletions(-) diff --git a/docs/webhook_events.md b/docs/webhook_events.md index 25f690c58..56cec24fa 100644 --- a/docs/webhook_events.md +++ b/docs/webhook_events.md @@ -109,6 +109,24 @@ Each event will be submitted via HTTP POST to the user provided URL. "title": "BlobRef", "type": "object" }, + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, "Report": { "properties": { "asan_log": { @@ -202,6 +220,331 @@ Each event will be submitted via HTTP POST to the user provided URL. ], "title": "Report", "type": "object" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" } }, "properties": { @@ -215,6 +558,9 @@ Each event will be submitted via HTTP POST to the user provided URL. }, "report": { "$ref": "#/definitions/Report" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" } }, "required": [ @@ -1149,6 +1495,24 @@ Each event will be submitted via HTTP POST to the user provided URL. "title": "BlobRef", "type": "object" }, + "ContainerType": { + "description": "An enumeration.", + "enum": [ + "analysis", + "coverage", + "crashes", + "inputs", + "no_repro", + "readonly_inputs", + "reports", + "setup", + "tools", + "unique_inputs", + "unique_reports", + "regression_reports" + ], + "title": "ContainerType" + }, "CrashTestResult": { "properties": { "crash_report": { @@ -1311,6 +1675,331 @@ Each event will be submitted via HTTP POST to the user provided URL. ], "title": "Report", "type": "object" + }, + "StatsFormat": { + "description": "An enumeration.", + "enum": [ + "AFL" + ], + "title": "StatsFormat" + }, + "TaskConfig": { + "properties": { + "colocate": { + "title": "Colocate", + "type": "boolean" + }, + "containers": { + "items": { + "$ref": "#/definitions/TaskContainers" + }, + "title": "Containers", + "type": "array" + }, + "debug": { + "items": { + "$ref": "#/definitions/TaskDebugFlag" + }, + "type": "array" + }, + "job_id": { + "format": "uuid", + "title": "Job Id", + "type": "string" + }, + "pool": { + "$ref": "#/definitions/TaskPool" + }, + "prereq_tasks": { + "items": { + "format": "uuid", + "type": "string" + }, + "title": "Prereq Tasks", + "type": "array" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "title": "Tags", + "type": "object" + }, + "task": { + "$ref": "#/definitions/TaskDetails" + }, + "vm": { + "$ref": "#/definitions/TaskVm" + } + }, + "required": [ + "job_id", + "task", + "containers", + "tags" + ], + "title": "TaskConfig", + "type": "object" + }, + "TaskContainers": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "name" + ], + "title": "TaskContainers", + "type": "object" + }, + "TaskDebugFlag": { + "description": "An enumeration.", + "enum": [ + "keep_node_on_failure", + "keep_node_on_completion" + ], + "title": "TaskDebugFlag" + }, + "TaskDetails": { + "properties": { + "analyzer_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Analyzer Env", + "type": "object" + }, + "analyzer_exe": { + "title": "Analyzer Exe", + "type": "string" + }, + "analyzer_options": { + "items": { + "type": "string" + }, + "title": "Analyzer Options", + "type": "array" + }, + "check_asan_log": { + "title": "Check Asan Log", + "type": "boolean" + }, + "check_debugger": { + "default": true, + "title": "Check Debugger", + "type": "boolean" + }, + "check_fuzzer_help": { + "title": "Check Fuzzer Help", + "type": "boolean" + }, + "check_retry_count": { + "title": "Check Retry Count", + "type": "integer" + }, + "duration": { + "title": "Duration", + "type": "integer" + }, + "ensemble_sync_delay": { + "title": "Ensemble Sync Delay", + "type": "integer" + }, + "expect_crash_on_failure": { + "title": "Expect Crash On Failure", + "type": "boolean" + }, + "generator_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Generator Env", + "type": "object" + }, + "generator_exe": { + "title": "Generator Exe", + "type": "string" + }, + "generator_options": { + "items": { + "type": "string" + }, + "title": "Generator Options", + "type": "array" + }, + "minimized_stack_depth": { + "title": "Minimized Stack Depth", + "type": "integer" + }, + "preserve_existing_outputs": { + "title": "Preserve Existing Outputs", + "type": "boolean" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "rename_output": { + "title": "Rename Output", + "type": "boolean" + }, + "report_list": { + "items": { + "type": "string" + }, + "title": "Report List", + "type": "array" + }, + "stats_file": { + "title": "Stats File", + "type": "string" + }, + "stats_format": { + "$ref": "#/definitions/StatsFormat" + }, + "supervisor_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Supervisor Env", + "type": "object" + }, + "supervisor_exe": { + "title": "Supervisor Exe", + "type": "string" + }, + "supervisor_input_marker": { + "title": "Supervisor Input Marker", + "type": "string" + }, + "supervisor_options": { + "items": { + "type": "string" + }, + "title": "Supervisor Options", + "type": "array" + }, + "target_env": { + "additionalProperties": { + "type": "string" + }, + "title": "Target Env", + "type": "object" + }, + "target_exe": { + "title": "Target Exe", + "type": "string" + }, + "target_options": { + "items": { + "type": "string" + }, + "title": "Target Options", + "type": "array" + }, + "target_options_merge": { + "title": "Target Options Merge", + "type": "boolean" + }, + "target_timeout": { + "title": "Target Timeout", + "type": "integer" + }, + "target_workers": { + "title": "Target Workers", + "type": "integer" + }, + "type": { + "$ref": "#/definitions/TaskType" + }, + "wait_for_files": { + "$ref": "#/definitions/ContainerType" + } + }, + "required": [ + "type", + "duration" + ], + "title": "TaskDetails", + "type": "object" + }, + "TaskPool": { + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "pool_name": { + "title": "Pool Name", + "type": "string" + } + }, + "required": [ + "count", + "pool_name" + ], + "title": "TaskPool", + "type": "object" + }, + "TaskType": { + "description": "An enumeration.", + "enum": [ + "libfuzzer_fuzz", + "libfuzzer_coverage", + "libfuzzer_crash_report", + "libfuzzer_merge", + "libfuzzer_regression", + "generic_analysis", + "generic_supervisor", + "generic_merge", + "generic_generator", + "generic_crash_report", + "generic_regression" + ], + "title": "TaskType" + }, + "TaskVm": { + "properties": { + "count": { + "default": 1, + "title": "Count", + "type": "integer" + }, + "image": { + "title": "Image", + "type": "string" + }, + "reboot_after_setup": { + "title": "Reboot After Setup", + "type": "boolean" + }, + "region": { + "title": "Region", + "type": "string" + }, + "sku": { + "title": "Sku", + "type": "string" + }, + "spot_instances": { + "default": false, + "title": "Spot Instances", + "type": "boolean" + } + }, + "required": [ + "region", + "sku", + "image" + ], + "title": "TaskVm", + "type": "object" } }, "properties": { @@ -1324,6 +2013,9 @@ Each event will be submitted via HTTP POST to the user provided URL. }, "regression_report": { "$ref": "#/definitions/RegressionReport" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" } }, "required": [ @@ -3927,6 +4619,9 @@ Each event will be submitted via HTTP POST to the user provided URL. }, "report": { "$ref": "#/definitions/Report" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" } }, "required": [ @@ -4228,6 +4923,9 @@ Each event will be submitted via HTTP POST to the user provided URL. }, "regression_report": { "$ref": "#/definitions/RegressionReport" + }, + "task_config": { + "$ref": "#/definitions/TaskConfig" } }, "required": [ diff --git a/src/api-service/__app__/onefuzzlib/notifications/main.py b/src/api-service/__app__/onefuzzlib/notifications/main.py index e32172f5b..f619efc55 100644 --- a/src/api-service/__app__/onefuzzlib/notifications/main.py +++ b/src/api-service/__app__/onefuzzlib/notifications/main.py @@ -97,6 +97,26 @@ def get_notifications(container: Container) -> List[Notification]: return Notification.search(query={"container": [container]}) +def get_regression_report_task(report: RegressionReport) -> Optional[Task]: + # crash_test_result is required, but report & no_repro are not + if report.crash_test_result.crash_report: + return Task.get( + report.crash_test_result.crash_report.job_id, + report.crash_test_result.crash_report.task_id, + ) + if report.crash_test_result.no_repro: + return Task.get( + report.crash_test_result.no_repro.job_id, + report.crash_test_result.no_repro.task_id, + ) + + logging.error( + "unable to find crash_report or no_repro entry for report: %s", + report.json(include_none=False), + ) + return None + + @cached(ttl=10) def get_queue_tasks() -> Sequence[Tuple[Task, Sequence[str]]]: results = [] @@ -143,14 +163,21 @@ def new_files(container: Container, filename: str) -> None: send_message(task.task_id, bytes(url, "utf-8"), StorageType.corpus) if isinstance(report, Report): - send_event( - EventCrashReported(report=report, container=container, filename=filename) + crash_report_event = EventCrashReported( + report=report, container=container, filename=filename ) + report_task = Task.get(report.job_id, report.task_id) + if report_task: + crash_report_event.task_config = report_task.config + send_event(crash_report_event) elif isinstance(report, RegressionReport): - send_event( - EventRegressionReported( - regression_report=report, container=container, filename=filename - ) + regression_event = EventRegressionReported( + regression_report=report, container=container, filename=filename ) + + report_task = get_regression_report_task(report) + if report_task: + regression_event.task_config = report_task.config + send_event(regression_event) else: send_event(EventFileAdded(container=container, filename=filename)) diff --git a/src/pytypes/onefuzztypes/events.py b/src/pytypes/onefuzztypes/events.py index 9e43e3296..d99bd8310 100644 --- a/src/pytypes/onefuzztypes/events.py +++ b/src/pytypes/onefuzztypes/events.py @@ -162,12 +162,14 @@ class EventCrashReported(BaseEvent): report: Report container: Container filename: str + task_config: Optional[TaskConfig] class EventRegressionReported(BaseEvent): regression_report: RegressionReport container: Container filename: str + task_config: Optional[TaskConfig] class EventFileAdded(BaseEvent):