From 2c3741ca1b60e66457a851f86b2df8f13ba8f184 Mon Sep 17 00:00:00 2001 From: Luiz Carvalho Date: Tue, 28 Nov 2023 15:40:57 -0500 Subject: [PATCH] Add beta.packages rego package This commit adds new policy rules under the new namespace, beta. This is done to avoid compatibility issues with older versions of the ec-cli which do not provide the ec.purl.parse rego function. Ref: EC-41 Resolves: 732 Signed-off-by: Luiz Carvalho --- example/data/rule_data.yml | 18 ++++ go.mod | 7 +- go.sum | 24 +++-- policy/beta/packages.rego | 68 +++++++++++++ policy/beta/packages_test.rego | 147 +++++++++++++++++++++++++++++ policy/lib/sbom.rego | 19 ++++ policy/lib/sbom_test.rego | 27 ++++++ policy/release/sbom_cyclonedx.rego | 27 ++---- 8 files changed, 303 insertions(+), 34 deletions(-) create mode 100644 policy/beta/packages.rego create mode 100644 policy/beta/packages_test.rego create mode 100644 policy/lib/sbom.rego create mode 100644 policy/lib/sbom_test.rego diff --git a/example/data/rule_data.yml b/example/data/rule_data.yml index e841ca24..e77abd98 100644 --- a/example/data/rule_data.yml +++ b/example/data/rule_data.yml @@ -74,3 +74,21 @@ rule_data: fbc_disallowed_inherited_labels: - name: description - name: summary + + disallowed_packages: + # Any version of the package greater than or equal to v50.28.3 will not be allowed. + - purl: pkg:golang/k8s.io/client-go + # "semverv" is a made up name to describe golang's variation of the semver standard. The format + # "semver" is also supported. Both behave exactly the same. + format: semverv + min: v50.28.3 + # Any version of the package lower than or equal to v50.28.3 will not be allowed. + - purl: pkg:golang/k8s.io/client-go + format: semverv + max: v50.28.3 + # Any version of the package greater than or equal to v50.20.2, AND lower than or equal to + # v50.28.3 will be not allowed. + - purl: pkg:golang/k8s.io/client-go + format: semverv + min: v50.20.2 + max: v50.28.3 diff --git a/go.mod b/go.mod index e087cc04..aa94266e 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/enterprise-contract/ec-policies -go 1.21.2 +go 1.21.4 require ( - github.com/enterprise-contract/ec-cli v0.0.0-20231110093630-4a1afa30abc7 + github.com/enterprise-contract/ec-cli v0.0.0-20231127194641-1b0ff5216773 github.com/open-policy-agent/conftest v0.46.0 github.com/styrainc/regal v0.13.0 github.com/tektoncd/cli v0.33.0 @@ -276,6 +276,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/package-url/packageurl-go v0.1.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/peterh/liner v1.2.2 // indirect @@ -399,7 +400,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.28.3 // indirect k8s.io/apiextensions-apiserver v0.28.3 // indirect - k8s.io/apimachinery v0.28.3 // indirect + k8s.io/apimachinery v0.28.4 // indirect k8s.io/cli-runtime v0.26.10 // indirect k8s.io/client-go v0.28.3 // indirect k8s.io/klog/v2 v2.110.1 // indirect diff --git a/go.sum b/go.sum index fad7ca40..c83e01f0 100644 --- a/go.sum +++ b/go.sum @@ -531,8 +531,8 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/proto v1.12.1 h1:6n/Z2pZAnBwuhU66Gs8160B8rrrYKo7h2F2sCOnNceE= github.com/emicklei/proto v1.12.1/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/enterprise-contract/ec-cli v0.0.0-20231110093630-4a1afa30abc7 h1:Bb0UC442300DDkI6KOGqigDANKZp2iaW5GBFCHwp4QM= -github.com/enterprise-contract/ec-cli v0.0.0-20231110093630-4a1afa30abc7/go.mod h1:oUJ3ccy01uGw480bdNb695llHRhLTRsWHVy8u9Oe9Ug= +github.com/enterprise-contract/ec-cli v0.0.0-20231127194641-1b0ff5216773 h1:mH1EL/IbDps73PCBBPkEwIQgeWfrJTlerv2S1k8iTWc= +github.com/enterprise-contract/ec-cli v0.0.0-20231127194641-1b0ff5216773/go.mod h1:EpLJ17EKcpsdQnKBZJmv6mFAHIyAPzIs86xYs7/RlEQ= github.com/enterprise-contract/enterprise-contract-controller/api v0.0.0-20231027095011-f06fe20fb615 h1:NAawZ0uB21/QvU+XHjh5mYKiYg1vKyZjl9F2fV5G4bc= github.com/enterprise-contract/enterprise-contract-controller/api v0.0.0-20231027095011-f06fe20fb615/go.mod h1:Zy2h6ld9aeicWvvCSofdCX7/RSVPD3OHV0263HMCA1U= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -575,12 +575,12 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0= github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gkampitakis/ciinfo v0.2.5 h1:K0mac90lGguc1conc46l0YEsB7/nioWCqSnJp/6z8Eo= -github.com/gkampitakis/ciinfo v0.2.5/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8= +github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.4.11 h1:7qKaozbTQEvHeG0bt6osdjdTDTnWYdIrLx43a7DEDu4= -github.com/gkampitakis/go-snaps v0.4.11/go.mod h1:N4TpqxI4CqKUfHzDFqrqZ5UP0I0ESz2g2NMslh7MiJw= +github.com/gkampitakis/go-snaps v0.4.12 h1:YeMgKOm0XW3f/Pt2rYpUlpyF8nG6lYGe9oXFJw5LdME= +github.com/gkampitakis/go-snaps v0.4.12/go.mod h1:PpnF1KPXQAHBdb/DHoi/1VmlwE+ZkVHzl+QHmgzMSz8= github.com/go-akka/configuration v0.0.0-20200606091224-a002c0330665 h1:Iz3aEheYgn+//VX7VisgCmF/wW3BMtXCLbvHV4jMQJA= github.com/go-akka/configuration v0.0.0-20200606091224-a002c0330665/go.mod h1:19bUnum2ZAeftfwwLZ/wRe7idyfoW2MfmXO464Hrfbw= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= @@ -1042,6 +1042,8 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ= +github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -1149,6 +1151,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.3.0 h1:XtuXmOLIXLjiU2XduuWREDT0LOKtSgos/g7i7RYyoZQ= github.com/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= +github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4= +github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= @@ -1351,8 +1355,8 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.6.1 h1:6J89fGjQf7s0mLmTG7p7pO/MbKOg+bIXhaLyQdmbKuE= github.com/theupdateframework/go-tuf v0.6.1/go.mod h1:LAFusuQsFNBnEyYoTuA5zZrF7iaQ4TEgBXm8lb6Vj18= -github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= -github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -2161,8 +2165,8 @@ k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= k8s.io/cli-runtime v0.26.10 h1:a5t8ejLCCjWBEny70uMDyPfOyOJH1qAxrrEo2a9fopU= k8s.io/cli-runtime v0.26.10/go.mod h1:i1UCYrl+n32ej4N2n2eacOMv4T94vRL0/ooOLopN23Q= k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= diff --git a/policy/beta/packages.rego b/policy/beta/packages.rego new file mode 100644 index 00000000..3285f7a6 --- /dev/null +++ b/policy/beta/packages.rego @@ -0,0 +1,68 @@ +# +# METADATA +# description: >- +# Checks the CycloneDX SBOMs associated with the image being validated do not include packages +# that have been deemed not allowed. +# NOTE: The policy rules in this package will eventually move to the release.sbom_cyclonedx +# package once the required ec.purl.parse rego function is widely available. +# +package policy.beta.packages + +import future.keywords.contains +import future.keywords.if +import future.keywords.in + +import data.lib +import data.lib.sbom + +# METADATA +# title: Allowed +# description: >- +# Confirm the CycloneDX SBOM contains only allowed packages. By default all packages are allowed. +# Use the "disallowed_packages" rule data key to provide a list of disallowed packages. +# custom: +# short_name: allowed +# failure_msg: "Package is not allowed: %s" +# solution: >- +# Update the image to not use a disallowed package. +# collections: +# - redhat +# +deny contains result if { + some s in sbom.cyclonedx_sboms + some component in s.components + _contains(component.purl, lib.rule_data("disallowed_packages")) + result := lib.result_helper(rego.metadata.chain(), [component.purl]) +} + +_contains(needle, haystack) if { + needle_purl := ec.purl.parse(needle) + + some hay in haystack + hay_purl := ec.purl.parse(hay.purl) + + needle_purl.type == hay_purl.type + needle_purl.namespace == hay_purl.namespace + needle_purl.name == hay_purl.name + _matches_version(needle_purl.version, hay) +} else := false + +_matches_version(version, matcher) if { + matcher.format in {"semverv", "semver"} + matcher.min != "" + matcher.max != "" + semver.compare(_to_semver(version), _to_semver(matcher.min)) != -1 + semver.compare(_to_semver(version), _to_semver(matcher.max)) != 1 +} else if { + matcher.format in {"semverv", "semver"} + matcher.min != "" + object.get(matcher, "max", "") == "" + semver.compare(_to_semver(version), _to_semver(matcher.min)) != -1 +} else if { + matcher.format in {"semverv", "semver"} + matcher.max != "" + object.get(matcher, "min", "") == "" + semver.compare(_to_semver(version), _to_semver(matcher.max)) != 1 +} else := false + +_to_semver(v) := trim_prefix(v, "v") diff --git a/policy/beta/packages_test.rego b/policy/beta/packages_test.rego new file mode 100644 index 00000000..9ee08e51 --- /dev/null +++ b/policy/beta/packages_test.rego @@ -0,0 +1,147 @@ +package policy.beta.packages_test + +import future.keywords.if +import future.keywords.in + +import data.lib +import data.policy.beta.packages + +test_allowed_by_default if { + assert_allowed("pkg:golang/k8s.io/client-go@v0.28.3", []) +} + +test_not_allowed_with_min if { + disallowed_packages := [{ + "purl": "pkg:golang/k8s.io/client-go", + "format": "semverv", + "min": "v50.28.3", + }] + + # Much lower than min version + assert_allowed("pkg:golang/k8s.io/client-go@v0.29.4", disallowed_packages) + + # Lower than min version + assert_allowed("pkg:golang/k8s.io/client-go@v50.28.2", disallowed_packages) + + # Exact match to min version + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.28.3", disallowed_packages) + + # Higher than min version + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.28.4", disallowed_packages) + + # Much higher than min version + assert_not_allowed("pkg:golang/k8s.io/client-go@v99.99.99", disallowed_packages) +} + +test_not_allowed_with_max if { + disallowed_packages := [{ + "purl": "pkg:golang/k8s.io/client-go", + "format": "semverv", + "max": "v50.28.3", + }] + + # Much lower than max version + assert_not_allowed("pkg:golang/k8s.io/client-go@v0.29.4", disallowed_packages) + + # Lower than max version + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.28.2", disallowed_packages) + + # Exact match to max version + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.28.3", disallowed_packages) + + # Higher than max version + assert_allowed("pkg:golang/k8s.io/client-go@v50.28.4", disallowed_packages) + + # Much higher than max version + assert_allowed("pkg:golang/k8s.io/client-go@v99.99.99", disallowed_packages) +} + +test_not_allowed_with_min_max if { + disallowed_packages := [{ + "purl": "pkg:golang/k8s.io/client-go", + "format": "semverv", + "min": "v50.20.2", + "max": "v50.28.3", + }] + + # Much lower than min version + assert_allowed("pkg:golang/k8s.io/client-go@v0.29.4", disallowed_packages) + + # Lower than min version + assert_allowed("pkg:golang/k8s.io/client-go@v50.20.1", disallowed_packages) + + # Exact match to min version + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.20.2", disallowed_packages) + + # Mid-range + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.24.9", disallowed_packages) + + # Exact match to max version + assert_not_allowed("pkg:golang/k8s.io/client-go@v50.28.3", disallowed_packages) + + # Higher than max version + assert_allowed("pkg:golang/k8s.io/client-go@v50.28.4", disallowed_packages) + + # Much higher than max version + assert_allowed("pkg:golang/k8s.io/client-go@v99.99.99", disallowed_packages) +} + +assert_allowed(purl, disallowed_packages) if { + att := json.patch(_sbom_attestation, [{ + "op": "add", + "path": "/statement/predicate/components/0/purl", + "value": purl, + }]) + lib.assert_empty(packages.deny) with input.attestations as [att] + with data.rule_data.disallowed_packages as disallowed_packages +} + +assert_not_allowed(purl, disallowed_packages) if { + expected := {{ + "code": "packages.allowed", + "msg": sprintf("Package is not allowed: %s", [purl]), + }} + att := json.patch(_sbom_attestation, [{ + "op": "add", + "path": "/statement/predicate/components/0/purl", + "value": purl, + }]) + lib.assert_equal_results(packages.deny, expected) with input.attestations as [att] + with data.rule_data.disallowed_packages as disallowed_packages +} + +_sbom_attestation := {"statement": { + "predicateType": "https://cyclonedx.org/bom", + "predicate": { + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:cf1a2c3d-bcf8-45c4-9d0f-b2b59a0753f0", + "version": 1, + "metadata": { + "timestamp": "2023-11-20T17:32:41Z", + "tools": [{ + "vendor": "anchore", + "name": "syft", + "version": "0.96.0", + }], + "component": { + "bom-ref": "158c8a990fbd4038", + "type": "file", + "name": "/var/lib/containers/storage/vfs/dir/dfd74fe178f4ea0472b5569bff38a4df69d05e7a81b538c98d731566aec15a69", + }, + }, + "components": [{ + # regal ignore:line-length + "bom-ref": "pkg:rpm/rhel/coreutils-single@8.32-34.el9?arch=x86_64&upstream=coreutils-8.32-34.el9.src.rpm&distro=rhel-9.3&package-id=f4f4e3cc2a6d9c37", + "type": "library", + "publisher": "Red Hat, Inc.", + "name": "coreutils-single", + "version": "8.32-34.el9", + "licenses": [{"license": {"name": "GPLv3+"}}], + "cpe": "cpe:2.3:a:coreutils-single:coreutils-single:8.32-34.el9:*:*:*:*:*:*:*", + # regal ignore:line-length + "purl": "pkg:rpm/rhel/coreutils-single@8.32-34.el9?arch=x86_64&upstream=coreutils-8.32-34.el9.src.rpm&distro=rhel-9.3", + }], + }, +}} diff --git a/policy/lib/sbom.rego b/policy/lib/sbom.rego new file mode 100644 index 00000000..d4555253 --- /dev/null +++ b/policy/lib/sbom.rego @@ -0,0 +1,19 @@ +package lib.sbom + +import future.keywords.in + +cyclonedx_sboms := array.concat(_cyclonedx_sboms_from_image, _cyclonedx_sboms_from_attestations) + +_cyclonedx_sboms_from_image := [sbom | + some path in ["root/buildinfo/content_manifests/sbom-cyclonedx.json"] + sbom := input.image.files[path] +] + +_cyclonedx_sboms_from_attestations := [sbom | + some att in input.attestations + statement := att.statement + + # https://cyclonedx.org/specification/overview/#recognized-predicate-type + statement.predicateType == "https://cyclonedx.org/bom" + sbom := statement.predicate +] diff --git a/policy/lib/sbom_test.rego b/policy/lib/sbom_test.rego new file mode 100644 index 00000000..6de976f3 --- /dev/null +++ b/policy/lib/sbom_test.rego @@ -0,0 +1,27 @@ +package lib.sbom_test + +import future.keywords.if +import future.keywords.in + +import data.lib +import data.lib.sbom + +test_cyclonedx_sboms if { + attestations := [ + {"statement": { + "predicateType": "https://cyclonedx.org/bom", + "predicate": "sbom from attestation", + }}, + {"statement": { + "predicateType": "https://example.org/boom", + "predicate": "not an sbom", + }}, + ] + image := {"files": { + "root/buildinfo/content_manifests/sbom-cyclonedx.json": "sbom from image", + "root/foo": "not an sbom", + }} + expected := ["sbom from image", "sbom from attestation"] + lib.assert_equal(sbom.cyclonedx_sboms, expected) with input.attestations as attestations + with input.image as image +} diff --git a/policy/release/sbom_cyclonedx.rego b/policy/release/sbom_cyclonedx.rego index 0e5a4b47..5a2db050 100644 --- a/policy/release/sbom_cyclonedx.rego +++ b/policy/release/sbom_cyclonedx.rego @@ -12,6 +12,7 @@ import future.keywords.if import future.keywords.in import data.lib +import data.lib.sbom # METADATA # title: Found @@ -26,7 +27,7 @@ import data.lib # - redhat # deny contains result if { - count(_sboms) == 0 + count(sbom.cyclonedx_sboms) == 0 result := lib.result_helper(rego.metadata.chain(), []) } @@ -44,8 +45,8 @@ deny contains result if { # - redhat # deny contains result if { - some index, sbom in _sboms - some violation in json.match_schema(sbom, schema_1_5)[1] + some index, s in sbom.cyclonedx_sboms + some violation in json.match_schema(s, schema_1_5)[1] error := violation.error result := lib.result_helper(rego.metadata.chain(), [index, error]) } @@ -63,23 +64,7 @@ deny contains result if { # - redhat # deny contains result if { - some sbom in _sboms - count(object.get(sbom, "components", [])) == 0 + some s in sbom.cyclonedx_sboms + count(object.get(s, "components", [])) == 0 result := lib.result_helper(rego.metadata.chain(), []) } - -_sboms := array.concat(_sboms_from_image, _sboms_from_attestations) - -_sboms_from_image := [sbom | - some path in ["root/buildinfo/content_manifests/sbom-cyclonedx.json"] - sbom := input.image.files[path] -] - -_sboms_from_attestations := [sbom | - some att in input.attestations - statement := att.statement - - # https://cyclonedx.org/specification/overview/#recognized-predicate-type - statement.predicateType == "https://cyclonedx.org/bom" - sbom := statement.predicate -]