Skip to content

Commit

Permalink
Merge pull request #815 from lcarva/EC-41
Browse files Browse the repository at this point in the history
Add beta.packages rego package
  • Loading branch information
lcarva authored Nov 29, 2023
2 parents 0a43751 + 2c3741c commit 2f48179
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 34 deletions.
18 changes: 18 additions & 0 deletions example/data/rule_data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
24 changes: 14 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
68 changes: 68 additions & 0 deletions policy/beta/packages.rego
Original file line number Diff line number Diff line change
@@ -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")
147 changes: 147 additions & 0 deletions policy/beta/packages_test.rego
Original file line number Diff line number Diff line change
@@ -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",
}],
},
}}
19 changes: 19 additions & 0 deletions policy/lib/sbom.rego
Original file line number Diff line number Diff line change
@@ -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
]
27 changes: 27 additions & 0 deletions policy/lib/sbom_test.rego
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 2f48179

Please sign in to comment.