From c0d86801efcf5bf4966f3ab0455146e87a6869d7 Mon Sep 17 00:00:00 2001 From: Michael Long <31821088+bluesentinelsec@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:56:10 -0400 Subject: [PATCH] Add ScanSbom validator (#30) * testing validator * test scan validator * troubleshooting * extract 'sbom' value * rollout to other integration tests * add inspector id * made step names more consistent --------- Co-authored-by: Michael Long --- .github/workflows/test_archive.yml | 9 ++- .github/workflows/test_binary.yml | 9 ++- .github/workflows/test_containers.yml | 13 +++- .github/workflows/test_repository.yml | 14 +++- validator/validate_inspector_scan.py | 98 +++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 9 deletions(-) create mode 100755 validator/validate_inspector_scan.py diff --git a/.github/workflows/test_archive.yml b/.github/workflows/test_archive.yml index efb4c4d..2c111ba 100644 --- a/.github/workflows/test_archive.yml +++ b/.github/workflows/test_archive.yml @@ -30,13 +30,18 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # TODO: use an IAM role - - name: Test Repository Scan + - name: Test archive scan + id: inspector uses: aws/amazon-inspector-github-actions-plugin@main # TODO: update this to point to public v1.0.0 release with: artifact_type: 'archive' artifact_path: 'entrypoint/tests/test_data/artifacts/archives/testData.zip' - # TODO: read the repository results and validate correctness + - name: Display scan results + run: cat ${{ steps.inspector.outputs.inspector_scan_results }} + + - name: Validate scan content + run: python3 validator/validate_inspector_scan.py --file ${{ steps.inspector.outputs.inspector_scan_results }} # only run if the previous step failed - name: Notify maintainers of validation failure diff --git a/.github/workflows/test_binary.yml b/.github/workflows/test_binary.yml index 30f4076..c82223b 100644 --- a/.github/workflows/test_binary.yml +++ b/.github/workflows/test_binary.yml @@ -30,13 +30,18 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # TODO: use an IAM role - - name: Test Repository Scan + - name: Test binary scan + id: inspector uses: aws/amazon-inspector-github-actions-plugin@main # TODO: update this to point to public v1.0.0 release with: artifact_type: 'binary' artifact_path: 'entrypoint/tests/test_data/artifacts/binaries/inspector-sbomgen' - # TODO: read the repository results and validate correctness + - name: Display scan results + run: cat ${{ steps.inspector.outputs.inspector_scan_results }} + + - name: Validate scan content + run: python3 validator/validate_inspector_scan.py --file ${{ steps.inspector.outputs.inspector_scan_results }} # only run if the previous step failed - name: Notify maintainers of validation failure diff --git a/.github/workflows/test_containers.yml b/.github/workflows/test_containers.yml index 9a82b42..721f1d0 100644 --- a/.github/workflows/test_containers.yml +++ b/.github/workflows/test_containers.yml @@ -18,6 +18,10 @@ jobs: name: plugin-development steps: + + - name: Checkout this repository + uses: actions/checkout@v4 + - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -26,14 +30,19 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # TODO: use an IAM role - - name: Test Repository Scan + - name: Test container scan + id: inspector uses: aws/amazon-inspector-github-actions-plugin@main # TODO: update this to point to public v1.0.0 release with: artifact_type: 'container' artifact_path: 'ubuntu:14.04' - # TODO: read the repository results and validate correctness + - name: Display scan results + run: cat ${{ steps.inspector.outputs.inspector_scan_results }} + + - name: Validate scan content + run: python3 validator/validate_inspector_scan.py --file ${{ steps.inspector.outputs.inspector_scan_results }} # only run if the previous step failed - name: Notify maintainers of validation failure diff --git a/.github/workflows/test_repository.yml b/.github/workflows/test_repository.yml index 339addc..9468047 100644 --- a/.github/workflows/test_repository.yml +++ b/.github/workflows/test_repository.yml @@ -18,6 +18,9 @@ jobs: name: plugin-development steps: + - name: Checkout this repository + uses: actions/checkout@v4 + - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -26,14 +29,19 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # TODO: use an IAM role - - name: Test Repository Scan + - name: Test repository scan + id: inspector uses: aws/amazon-inspector-github-actions-plugin@main # TODO: update this to point to public v1.0.0 release with: artifact_type: 'repository' - artifact_path: './' # TODO: make this a specific sub-directory so we have predictable output + artifact_path: './' + + - name: Display scan results + run: cat ${{ steps.inspector.outputs.inspector_scan_results }} - # TODO: read the repository results and validate correctness + - name: Validate scan content + run: python3 validator/validate_inspector_scan.py --file ${{ steps.inspector.outputs.inspector_scan_results }} # only run if the previous step failed - name: Notify maintainers of validation failure diff --git a/validator/validate_inspector_scan.py b/validator/validate_inspector_scan.py new file mode 100755 index 0000000..7ee118e --- /dev/null +++ b/validator/validate_inspector_scan.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +""" +This script validates the contents of an Inspector ScanSbom +response to ensure that fields needed by the +Inspector GitHub Actions plugin are present. +""" + +import argparse +import json +import logging +import sys + + +def assert_equal(key, want, got): + if want != got: + logging.error(f" expected JSON value of '{want}' from key '{key}', but received '{got}'") + sys.exit(1) + + +def is_valid_prop_name(prop_name): + if 'amazon:inspector:sbom_scanner:critical_vulnerabilities' in prop_name: + return True + elif 'amazon:inspector:sbom_scanner:high_vulnerabilities' in prop_name: + return True + elif 'amazon:inspector:sbom_scanner:medium_vulnerabilities' in prop_name: + return True + elif 'amazon:inspector:sbom_scanner:low_vulnerabilities' in prop_name: + return True + elif 'amazon:inspector:sbom_scanner:other_vulnerabilities' in prop_name: + return True + else: + logging.error(f"received unhandled property name: '{prop_name}'") + sys.exit(1) + + +def validate_inspector_scan(scan_sbom_json): + scan_sbom_json = scan_sbom_json.get("sbom") + assert scan_sbom_json != "" + + want = "CycloneDX" + key = "bomFormat" + got = scan_sbom_json.get(key) + assert_equal(key, want, got) + + want = "1.5" + key = "specVersion" + got = scan_sbom_json.get(key) + assert_equal(key, want, got) + + key = "serialNumber" + got = scan_sbom_json.get(key) + assert got != "" + + components = scan_sbom_json.get("components") + assert len(components) > 0 + + vulns = scan_sbom_json.get("vulnerabilities") + assert len(vulns) > 0 + + props = scan_sbom_json.get("metadata").get("properties") + for each_prop in props: + prop_name = each_prop.get("name") + assert is_valid_prop_name(prop_name) + + +def open_inspector_scan(filepath): + scan_json = "" + f = None + try: + f = open(filepath, "r") + except Exception as e: + logging.error(f"unable to open file for reading: {filepath}\n{e}") + sys.exit(1) + + try: + scan_json = json.load(f) + except Exception as e: + logging.error(f"unable to load file as JSON: {filepath}\n{e}") + sys.exit(1) + + return scan_json + + +def main(): + parser = argparse.ArgumentParser(description="Validate the contents of an Inspector ScanSbom JSON payload") + parser.add_argument("--file", type=str, default="", required=True, + help="The filepath to an Inspector ScanSbom JSON file") + args = parser.parse_args() + + inspector_scan_json = open_inspector_scan(args.file) + validate_inspector_scan(inspector_scan_json) + logging.info("validation successful") + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + main()