diff --git a/.github/workflows/test_containers.yml b/.github/workflows/test_containers.yml index a4cdb07..c8d2514 100644 --- a/.github/workflows/test_containers.yml +++ b/.github/workflows/test_containers.yml @@ -36,6 +36,7 @@ jobs: with: artifact_type: 'container' artifact_path: 'ubuntu:14.04' + platform: "linux/arm64" display_vulnerability_findings: "enabled" sbomgen_version: "latest" diff --git a/action.yml b/action.yml index e33c71c..d986748 100644 --- a/action.yml +++ b/action.yml @@ -106,6 +106,10 @@ inputs: required: False default: 600 # 10 minutes + platform: + description: "Specifies the OS and CPU arch of the container image you wish to scan. Valid inputs are of the form 'os/cpu/variant' for example, 'linux/amd64', 'linux/arm64/v8', etc. If no platform is specified, the system will use the same platform as the host that is performing the scan. This argument only affects container image scans. Requires inspector-sbomgen 1.5.1 or later." + required: False + outputs: artifact_sbom: description: "The filepath to the artifact's software bill of materials." diff --git a/entrypoint/entrypoint/cli.py b/entrypoint/entrypoint/cli.py index 384a9dd..133d380 100644 --- a/entrypoint/entrypoint/cli.py +++ b/entrypoint/entrypoint/cli.py @@ -51,6 +51,13 @@ def init(sys_argv=None) -> argparse.Namespace: parser.add_argument("--timeout", type=str, default="600", help="The amount of time in seconds that inspector-sbomgne will run. When this timeout is exceeded, sbomgen will gracefully conclude and present any findings discovered up to that point.") + parser.add_argument("--platform", type=str, + help="Specifies the OS and CPU arch of the container image you wish to scan. Valid inputs are " + "of the form 'os/cpu/variant' for example, 'linux/amd64', 'linux/arm64/v8', etc. If no platform is " + "specified, the system will use the same platform as the host that is performing the " + "scan. This argument only affects container image scans. Requires inspector-sbomgen " + "1.5.1 or later.") + args = "" if sys_argv: args = parser.parse_args(sys_argv) diff --git a/entrypoint/entrypoint/orchestrator.py b/entrypoint/entrypoint/orchestrator.py index 40d5d7f..57f2a4f 100644 --- a/entrypoint/entrypoint/orchestrator.py +++ b/entrypoint/entrypoint/orchestrator.py @@ -6,6 +6,7 @@ import shutil import sys import tempfile +import re from entrypoint import dockerfile, executor, exporter, installer, pkg_vuln @@ -195,6 +196,15 @@ def invoke_sbomgen(args) -> int: sbomgen_args.append("--skip-files") sbomgen_args.append(args.skip_files) + if args.artifact_type == "container": + + if args.platform: + platform_arg = args.platform.lower() + if not is_valid_container_platform(platform_arg): + logging.fatal(f"received invalid container image platform: '{args.platform}'. Platform should be of the form 'os/cpu/variant' such as 'linux/amd64' or 'linux/arm64/v8'") + sbomgen_args.append("--platform") + sbomgen_args.append(platform_arg) + ret = executor.invoke_command(sbomgen, sbomgen_args) if ret != 0: return ret @@ -441,3 +451,9 @@ def require_true(expr: bool, msg: str): if not expr: logging.error(msg) exit(1) + +def is_valid_container_platform(img_platform): + # regex for detecting 'os/cpu/variant' + # os/cpu are required whereas variant is optional + pattern = r'^[^/]+/[^/]+(?:/[^/]+)?$' + return bool(re.match(pattern, img_platform)) diff --git a/entrypoint/tests/test_orchestrator.py b/entrypoint/tests/test_orchestrator.py index d2a2ede..792abc0 100644 --- a/entrypoint/tests/test_orchestrator.py +++ b/entrypoint/tests/test_orchestrator.py @@ -185,6 +185,22 @@ def test_get_sbomgen_arch(self): result = orchestrator.get_sbomgen_arch(each_test["input"]) self.assertEqual(result, each_test["expected"]) + def test_is_valid_container_platform(self): + + test_cases = [ + # valid input + {"input": "linux/amd64", "expected": True}, + {"input": "linux/arm64/v8", "expected": True}, + # test malformed input + {"input": "linux", "expected": False}, + {"input": "garbage garbage garbage", "expected": False}, + {"input": "garbage / garbage / garbage /", "expected": False}, + {"input": "linux/amd64/slim/garbage", "expected": False}, + ] + + for each_test in test_cases: + result = orchestrator.is_valid_container_platform(each_test["input"]) + self.assertEqual(result, each_test["expected"]) if __name__ == "__main__": unittest.main()