Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have SBOM errors/alerts/debug bubble up to scan result #77

Merged
merged 21 commits into from
Nov 20, 2023
Merged
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
3 changes: 2 additions & 1 deletion backend/engine/plugins/veracode_sbom/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"name": "Veracode SBOM",
"type": "sbom",
"image": "$ECR/artemis/veracode:latest",
"enabled": "$ARTEMIS_FEATURE_VERACODE_ENABLED"
"enabled": "$ARTEMIS_FEATURE_VERACODE_ENABLED",
"timeout": 28800
}
19 changes: 12 additions & 7 deletions backend/engine/processor/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,19 @@ def process_plugins(self, images: dict, services: dict) -> None:
logger.info("Plugin %s is disabled", plugin)
else:
logger.info("Plugin %s completed, updating results", plugin)
if results.type != "sbom":
if results.type == "vulnerability":
process_vulns(results, self.scan.get_scan_object(), plugin)
self.scan.create_plugin_result_set(start_time, results)
self._cache_results(results)
else:
# Process SBOM results

if results.type == PluginType.SBOM.value:
process_sbom(results, self.scan.get_scan_object())

# SBOM results should not be returned directly in the scan, so clear details
results.details = []

elif results.type == PluginType.VULN.value:
process_vulns(results, self.scan.get_scan_object(), plugin)

self.scan.create_plugin_result_set(start_time, results)
self._cache_results(results)

logger.info("Plugin %s results updated", plugin)

# Clean and reset the repo in case the plugin wrote any files to disk. This way files created or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from json_report.results.configuration import get_configuration
from json_report.results.inventory import get_inventory
from json_report.results.results import PLUGIN_RESULTS, PluginErrors
from json_report.results.sbom import get_sbom
from json_report.results.secret import get_secrets
from json_report.results.static_analysis import get_static_analysis
from json_report.results.vuln import get_vulns
Expand All @@ -16,6 +17,7 @@ def get_report(scan_id, params=None):

vuln_results = PLUGIN_RESULTS({}, PluginErrors(), True, None)
secret_results = PLUGIN_RESULTS({}, PluginErrors(), True, None)
sbom_results = PLUGIN_RESULTS({}, PluginErrors(), True, None)
sa_results = PLUGIN_RESULTS({}, PluginErrors(), True, None)
inv_results = PLUGIN_RESULTS({}, PluginErrors(), True, None)
config_results = PLUGIN_RESULTS({}, PluginErrors(), True, None)
Expand All @@ -35,6 +37,9 @@ def get_report(scan_id, params=None):
if "results" not in params or "secrets" in params["results"]:
secret_results = get_secrets(scan, params)

if "results" not in params or "sbom" in params["results"]:
sbom_results = get_sbom(scan)

if "results" not in params or "static_analysis" in params["results"]:
sa_results = get_static_analysis(scan, params)

Expand All @@ -46,13 +51,15 @@ def get_report(scan_id, params=None):

errors.update(vuln_results.errors)
errors.update(secret_results.errors)
errors.update(sbom_results.errors)
errors.update(sa_results.errors)
errors.update(inv_results.errors)
errors.update(config_results.errors)

success = (
vuln_results.success
and secret_results.success
and sbom_results.success
and sa_results.success
and inv_results.success
and config_results.success
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def get_configuration(scan: Scan, params: dict) -> PLUGIN_RESULTS:
"""
Unify the output of configuration plugins
NOTE: unit tests are located at api/tests/test_generate_report.py
NOTE: unit tests are located at backend/lambdas/generators/json_report/tests/test_generate_report.py
Inspect tests for expected output format.
:param scan: django object of Artemis repo scan
:param params: Only used for filtering severity
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from artemisdb.artemisdb.consts import PluginType
from artemisdb.artemisdb.models import Scan
from json_report.results.results import PLUGIN_RESULTS, PluginErrors


def get_sbom(scan: Scan) -> PLUGIN_RESULTS:
"""
Unify the output of sbom plugins. We deliberately omit the `details` property in the database
(since SBOM results are stored in s3), so this returns no scan results or summary. This will
always return True for `success`, since SBOM scans are just an inventory (and the existence of a
dependancy cannot "fail" the scan) and the plugin itself failing is not a reason to fail the
larger scan
NOTE: unit tests are located at backend/lambdas/generators/json_report/tests/test_generate_report.py
Inspect tests for expected output format.
:param scan: django object of Artemis repo scan
:return: dictionary of None for findings, list of errors, True for success, and None for summary
"""

errors = PluginErrors()

plugin = object()
for plugin in scan.pluginresult_set.filter(plugin_type=PluginType.SBOM.value):
errors.update(plugin)

return PLUGIN_RESULTS(None, errors, True, None)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
def get_static_analysis(scan: Scan, params: dict) -> PLUGIN_RESULTS:
"""
Unify the output of static analysis plugins
NOTE: unit tests are located at api/tests/test_generate_report.py
NOTE: unit tests are located at backend/lambdas/generators/json_report/tests/test_generate_report.py
Inspect tests for expected output format.
:param scan: django object of Artemis repo scan
:param params: Only used for filtering severity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from json_report.results.configuration import get_configuration
from json_report.results.inventory import get_inventory
from json_report.results.results import PLUGIN_RESULTS, PluginErrors
from json_report.results.sbom import get_sbom
from json_report.results.static_analysis import get_static_analysis
from json_report.util.const import DEFAULT_SCAN_QUERY_PARAMS

Expand Down Expand Up @@ -246,6 +247,22 @@
end_time=datetime(year=2020, month=2, day=19, hour=15, minute=1, second=55, tzinfo=timezone.utc),
)

SBOM_ERROR_MSG = "test error"
SBOM_ALERT_MSG = "test alert"
SBOM_DEBUG_MSG = "test debug message"

TEST_VERACODE_SBOM = PluginResult(
plugin_name="Veracode SBOM",
plugin_type="sbom",
success=True,
details=[],
errors=[SBOM_ERROR_MSG],
alerts=[SBOM_ALERT_MSG],
debug=[SBOM_DEBUG_MSG],
start_time=datetime(year=2020, month=2, day=19, hour=15, minute=1, second=54, tzinfo=timezone.utc),
end_time=datetime(year=2020, month=2, day=19, hour=15, minute=1, second=55, tzinfo=timezone.utc),
)


class TestGenerateReport(unittest.TestCase):
def test_get_static_analysis_report_for_brakeman(self):
Expand Down Expand Up @@ -450,6 +467,29 @@ def test_get_configuration_report_for_github_repo_health(self):
configuration = get_configuration(mock_scan, DEFAULT_SCAN_QUERY_PARAMS)
self.assertEqual(expected_configuration, configuration)

def test_get_sbom_report_for_veracode_sbom(self):
expected_sbom = PLUGIN_RESULTS(
None,
get_plugin_errors(
TEST_VERACODE_SBOM.plugin_name, errors=[SBOM_ERROR_MSG], alerts=[SBOM_ALERT_MSG], debug=[SBOM_DEBUG_MSG]
),
True,
None,
)

mock_scan = unittest.mock.MagicMock(side_effect=Scan())
mock_scan.pluginresult_set.filter.return_value = [TEST_VERACODE_SBOM]
sbom = get_sbom(mock_scan)
print("expected")
print(expected_sbom.errors.errors)
print(expected_sbom.errors.alerts)
print(expected_sbom.errors.debug)
print("real")
print(sbom.errors.errors)
print(sbom.errors.alerts)
print(sbom.errors.debug)
self.assertEqual(expected_sbom, sbom)

def test_get_static_analysis_report_diff(self):
expected_report = PLUGIN_RESULTS(
{
Expand Down Expand Up @@ -493,5 +533,15 @@ def run_static_analysis(self, scan, expected_report):
self.assertEqual(expected_report, report)


def get_plugin_errors(name, errors, alerts, debug):
plugin_errors = PluginErrors()

plugin_errors.errors[name] = errors
plugin_errors.alerts[name] = alerts
plugin_errors.debug[name] = debug

return plugin_errors


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions backend/libs/artemisdb/artemisdb/artemisdb/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class PluginType(Enum):
CONFIGURATION = "configuration"
INVENTORY = "inventory"
VULN = "vulnerability"
SBOM = "sbom"
SECRETS = "secrets"
STATIC_ANALYSIS = "static_analysis"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 3.2.23 on 2023-11-15 14:08

import artemisdb.artemisdb.consts
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("artemisdb", "0046_component_component_type"),
]

operations = [
migrations.AlterField(
model_name="plugin",
name="type",
field=models.CharField(
choices=[
(artemisdb.artemisdb.consts.PluginType["CONFIGURATION"], "configuration"),
(artemisdb.artemisdb.consts.PluginType["INVENTORY"], "inventory"),
(artemisdb.artemisdb.consts.PluginType["VULN"], "vulnerability"),
(artemisdb.artemisdb.consts.PluginType["SBOM"], "sbom"),
(artemisdb.artemisdb.consts.PluginType["SECRETS"], "secrets"),
(artemisdb.artemisdb.consts.PluginType["STATIC_ANALYSIS"], "static_analysis"),
],
max_length=64,
),
),
migrations.AlterField(
model_name="pluginresult",
name="plugin_type",
field=models.CharField(
choices=[
(artemisdb.artemisdb.consts.PluginType["CONFIGURATION"], "configuration"),
(artemisdb.artemisdb.consts.PluginType["INVENTORY"], "inventory"),
(artemisdb.artemisdb.consts.PluginType["VULN"], "vulnerability"),
(artemisdb.artemisdb.consts.PluginType["SBOM"], "sbom"),
(artemisdb.artemisdb.consts.PluginType["SECRETS"], "secrets"),
(artemisdb.artemisdb.consts.PluginType["STATIC_ANALYSIS"], "static_analysis"),
],
max_length=64,
),
),
]
Loading