From 121c63efe5c17cf45bff5bd4dd698ab5d6703798 Mon Sep 17 00:00:00 2001 From: Jan Koscielniak Date: Tue, 5 Nov 2024 15:59:15 +0100 Subject: [PATCH] Add allowed package sources policy Signed-off-by: Jan Koscielniak --- .../modules/ROOT/pages/release_policy.adoc | 15 +++ .../ROOT/partials/release_policy_nav.adoc | 1 + policy/lib/sbom/sbom.rego | 2 + .../sbom_cyclonedx/sbom_cyclonedx.rego | 55 +++++++++++ .../sbom_cyclonedx/sbom_cyclonedx_test.rego | 95 +++++++++++++++++++ 5 files changed, 168 insertions(+) diff --git a/antora/docs/modules/ROOT/pages/release_policy.adoc b/antora/docs/modules/ROOT/pages/release_policy.adoc index a3137224..a8cbae53 100644 --- a/antora/docs/modules/ROOT/pages/release_policy.adoc +++ b/antora/docs/modules/ROOT/pages/release_policy.adoc @@ -75,6 +75,7 @@ Rules included: * xref:release_policy.adoc#rpm_repos__rule_data_provided[RPM Repos: Known repo id list provided] * xref:release_policy.adoc#rpm_signature__rule_data_provided[RPM Signature: Rule data provided] * xref:release_policy.adoc#sbom_cyclonedx__allowed_package_external_references[SBOM CycloneDX: Allowed package external references] +* xref:release_policy.adoc#sbom_cyclonedx__allowed_package_sources[SBOM CycloneDX: Allowed package sources] * xref:release_policy.adoc#sbom_cyclonedx__disallowed_package_attributes[SBOM CycloneDX: Disallowed package attributes] * xref:release_policy.adoc#sbom_cyclonedx__disallowed_package_external_references[SBOM CycloneDX: Disallowed package external references] * xref:release_policy.adoc#sbom__disallowed_packages_provided[SBOM: Disallowed packages list is provided] @@ -142,6 +143,7 @@ Rules included: * xref:release_policy.adoc#rpm_signature__rule_data_provided[RPM Signature: Rule data provided] * xref:release_policy.adoc#sbom_cyclonedx__allowed[SBOM CycloneDX: Allowed] * xref:release_policy.adoc#sbom_cyclonedx__allowed_package_external_references[SBOM CycloneDX: Allowed package external references] +* xref:release_policy.adoc#sbom_cyclonedx__allowed_package_sources[SBOM CycloneDX: Allowed package sources] * xref:release_policy.adoc#sbom_cyclonedx__disallowed_package_attributes[SBOM CycloneDX: Disallowed package attributes] * xref:release_policy.adoc#sbom_cyclonedx__disallowed_package_external_references[SBOM CycloneDX: Disallowed package external references] * xref:release_policy.adoc#sbom_cyclonedx__valid[SBOM CycloneDX: Valid] @@ -1111,6 +1113,19 @@ Confirm the CycloneDX SBOM contains only packages with explicitly allowed extern * Code: `sbom_cyclonedx.allowed_package_external_references` * https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/sbom_cyclonedx/sbom_cyclonedx.rego#L89[Source, window="_blank"] +[#sbom_cyclonedx__allowed_package_sources] +=== link:#sbom_cyclonedx__allowed_package_sources[Allowed package sources] + +For each of the components fetched by Cachi2 which define externalReferences of type distribution, verify they are allowed based on the allowed_package_sources rule data key. By default, allowed_package_sources is empty which means no components with such references are allowed. + +*Solution*: Update the image to not use a package from a disallowed source. + +* Rule type: [rule-type-indicator failure]#FAILURE# +* FAILURE message: `Package %s fetched by cachi2 was sourced from %q which is not allowed` +* Code: `sbom_cyclonedx.allowed_package_sources` +* Effective from: `2024-12-15T00:00:00Z` +* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/sbom_cyclonedx/sbom_cyclonedx.rego#L151[Source, window="_blank"] + [#sbom_cyclonedx__disallowed_package_attributes] === link:#sbom_cyclonedx__disallowed_package_attributes[Disallowed package attributes] diff --git a/antora/docs/modules/ROOT/partials/release_policy_nav.adoc b/antora/docs/modules/ROOT/partials/release_policy_nav.adoc index 38ce517e..770041c8 100644 --- a/antora/docs/modules/ROOT/partials/release_policy_nav.adoc +++ b/antora/docs/modules/ROOT/partials/release_policy_nav.adoc @@ -86,6 +86,7 @@ *** xref:release_policy.adoc#sbom_cyclonedx_package[SBOM CycloneDX] **** xref:release_policy.adoc#sbom_cyclonedx__allowed[Allowed] **** xref:release_policy.adoc#sbom_cyclonedx__allowed_package_external_references[Allowed package external references] +**** xref:release_policy.adoc#sbom_cyclonedx__allowed_package_sources[Allowed package sources] **** xref:release_policy.adoc#sbom_cyclonedx__disallowed_package_attributes[Disallowed package attributes] **** xref:release_policy.adoc#sbom_cyclonedx__disallowed_package_external_references[Disallowed package external references] **** xref:release_policy.adoc#sbom_cyclonedx__valid[Valid] diff --git a/policy/lib/sbom/sbom.rego b/policy/lib/sbom/sbom.rego index 6a0e910d..ef625144 100644 --- a/policy/lib/sbom/sbom.rego +++ b/policy/lib/sbom/sbom.rego @@ -266,3 +266,5 @@ rule_data_attributes_key := "disallowed_attributes" rule_data_allowed_external_references_key := "allowed_external_references" rule_data_disallowed_external_references_key := "disallowed_external_references" + +rule_data_allowed_package_sources_key := "allowed_package_sources" diff --git a/policy/release/sbom_cyclonedx/sbom_cyclonedx.rego b/policy/release/sbom_cyclonedx/sbom_cyclonedx.rego index 690027f2..fae4570a 100644 --- a/policy/release/sbom_cyclonedx/sbom_cyclonedx.rego +++ b/policy/release/sbom_cyclonedx/sbom_cyclonedx.rego @@ -148,6 +148,61 @@ deny contains result if { result := lib.result_helper_with_term(rego.metadata.chain(), [id, reference.url, reference.type, msg], id) } +# METADATA +# title: Allowed package sources +# description: >- +# For each of the components fetched by Cachi2 which define externalReferences of type +# distribution, verify they are allowed based on the allowed_package_sources rule data +# key. By default, allowed_package_sources is empty which means no components with such +# references are allowed. +# custom: +# short_name: allowed_package_sources +# failure_msg: Package %s fetched by cachi2 was sourced from %q which is not allowed +# solution: Update the image to not use a package from a disallowed source. +# collections: +# - redhat +# - policy_data +# effective_on: 2024-12-15T00:00:00Z +deny contains result if { + some s in sbom.cyclonedx_sboms + some component in s.components + + # only look at components that define an externalReferences of type `distribution` + some reference in component.externalReferences + reference.type == "distribution" + + # only look at components fetched by cachi2 + some properties in component.properties + properties.name == "cachi2:found_by" + properties.value == "cachi2" + + purl := component.purl + parsed_purl := ec.purl.parse(purl) + + # patterns are either those defined by the rule for a give purl type, or empty by default + allowed_data := lib.rule_data(sbom.rule_data_allowed_package_sources_key) + patterns := _get_purl_allowed_patterns(parsed_purl.type, allowed_data) + distribution_url := object.get(reference, "url", "") + + # only progress past this point if no matches were found + not _url_matches_any_pattern(distribution_url, patterns) + + result := lib.result_helper_with_term(rego.metadata.chain(), [purl, distribution_url], purl) +} + +# get allowed pattens for given purl type, or empty list if not defined +_get_purl_allowed_patterns(purl_type, allowed_rule_data) := patterns if { + some allowed in allowed_rule_data + purl_type == allowed.type + patterns := allowed.patterns +} else := [] + +# see if any pattern matches given url +_url_matches_any_pattern(url, patterns) if { + some pattern in patterns + regex.match(pattern, url) +} + # _with_effective_on annotates the result with the item's effective_on attribute. If the item does # not have the attribute, result is returned unmodified. _with_effective_on(result, item) := new_result if { diff --git a/policy/release/sbom_cyclonedx/sbom_cyclonedx_test.rego b/policy/release/sbom_cyclonedx/sbom_cyclonedx_test.rego index 6be5daf9..b34935b6 100644 --- a/policy/release/sbom_cyclonedx/sbom_cyclonedx_test.rego +++ b/policy/release/sbom_cyclonedx/sbom_cyclonedx_test.rego @@ -188,6 +188,101 @@ test_external_references_disallowed_no_purl if { }]} } +test_allowed_package_sources if { + expected := {{ + "code": "sbom_cyclonedx.allowed_package_sources", + "term": "pkg:generic/openssl@1.1.10g?download_url=https://openssl.org/source/openssl-1.1.0g.tar.gz", + # regal ignore:line-length + "msg": `Package pkg:generic/openssl@1.1.10g?download_url=https://openssl.org/source/openssl-1.1.0g.tar.gz fetched by cachi2 was sourced from "https://openssl.org/source/openssl-1.1.0g.tar.gz" which is not allowed`, + }} + + att := json.patch(_sbom_attestation, [ + { + "op": "add", + "path": "/statement/predicate/components/-", + "value": { + "type": "file", + "name": "openssl", + "purl": "pkg:generic/openssl@1.1.10g?download_url=https://openssl.org/source/openssl-1.1.0g.tar.gz", + "properties": [{ + "name": "cachi2:found_by", + "value": "cachi2", + }], + "externalReferences": [{"type": "distribution", "url": "https://openssl.org/source/openssl-1.1.0g.tar.gz"}], + }, + }, + { + "op": "add", + "path": "/statement/predicate/components/-", + "value": { + "type": "library", + "name": "batik-anim", + "purl": "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=pom", + "properties": [{ + "name": "cachi2:found_by", + "value": "cachi2", + }], + # regal ignore:line-length + "externalReferences": [{"type": "distribution", "url": "https://repo.maven.apache.org/maven2/org/apache/xmlgraphics/batik-anim/1.9.1/batik-anim-1.9.1.pom"}], + }, + }, + { + "op": "add", + "path": "/statement/predicate/components/-", + "value": { + "type": "file", + "name": "unrelated", + "purl": "pkg:generic/unrelated", + "externalReferences": [{"type": "distribution", "url": "https://irrelevant.org"}], + }, + }, + ]) + + lib.assert_equal_results(expected, sbom_cyclonedx.deny) with input.attestations as [att] + with data.rule_data as {sbom.rule_data_allowed_package_sources_key: [ + { + "type": "maven", + "patterns": [".*apache.org.*", ".*example.com.*"], + }, + { + "type": "generic", + "patterns": [".*apache.org.*", ".*example.com.*"], + }, + ]} +} + +test_allowed_package_sources_no_rule_defined if { + expected := {{ + "code": "sbom_cyclonedx.allowed_package_sources", + "term": "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=pom", + # regal ignore:line-length + "msg": `Package pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=pom fetched by cachi2 was sourced from "https://repo.maven.apache.org/maven2/org/apache/xmlgraphics/batik-anim/1.9.1/batik-anim-1.9.1.pom" which is not allowed`, + }} + + att := json.patch(_sbom_attestation, [{ + "op": "add", + "path": "/statement/predicate/components/-", + "value": { + "type": "library", + "name": "batik-anim", + "purl": "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=pom", + "properties": [{ + "name": "cachi2:found_by", + "value": "cachi2", + }], + # regal ignore:line-length + "externalReferences": [{"type": "distribution", "url": "https://repo.maven.apache.org/maven2/org/apache/xmlgraphics/batik-anim/1.9.1/batik-anim-1.9.1.pom"}], + }, + }]) + + # rule data is defined only for purl of type generic + lib.assert_equal_results(expected, sbom_cyclonedx.deny) with input.attestations as [att] + with data.rule_data as {sbom.rule_data_allowed_package_sources_key: [{ + "type": "generic", + "patterns": [".*example.com.*"], + }]} +} + test_attributes_not_allowed_no_properties if { att := json.patch(_sbom_attestation, [{ "op": "remove",