Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ec-965): verify a set of pullspecs related to an image #1251

Merged
merged 1 commit into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ Create a `policy.yaml` file in your local `ec-cli` repo with something like:
---
sources:
- policy:
- <path-to>/ec-policies/policy/lib
- <path-to>/ec-policies/policy/release
data:
- oci::quay.io/konflux-ci/tekton-catalog/data-acceptable-bundles:latest
- github.com/release-engineering/rhtap-ec-policy//data
- <path-to>/ec-policies/policy/lib
- <path-to>/ec-policies/policy/release
data:
- oci::quay.io/konflux-ci/tekton-catalog/data-acceptable-bundles:latest
- github.com/release-engineering/rhtap-ec-policy//data

Run the locally built `ec-cli` command

Expand Down
17 changes: 15 additions & 2 deletions antora/docs/modules/ROOT/pages/release_policy.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Rules included:
* xref:release_policy.adoc#olm__required_olm_features_annotations_provided[OLM: Required OLM feature annotations list provided]
* xref:release_policy.adoc#olm__subscriptions_annotation_format[OLM: Subscription annotation has expected value]
* xref:release_policy.adoc#olm__inaccessible_snapshot_references[OLM: Unable to access images in the input snapshot]
* xref:release_policy.adoc#olm__inaccessible_related_images[OLM: Unable to access related images for a component]
* xref:release_policy.adoc#olm__unmapped_references[OLM: Unmapped images in OLM bundle]
* xref:release_policy.adoc#olm__unpinned_references[OLM: Unpinned images in OLM bundle]
* xref:release_policy.adoc#olm__unpinned_snapshot_references[OLM: Unpinned images in input snapshot]
Expand Down Expand Up @@ -798,7 +799,7 @@ Each image referenced by the OLM bundle should match an entry in the list of pre
* FAILURE message: `The %q CSV image reference is not from an allowed registry.`
* Code: `olm.allowed_registries`
* Effective from: `2024-09-01T00:00:00Z`
* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L219[Source, window="_blank"]
* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L256[Source, window="_blank"]

[#olm__required_olm_features_annotations_provided]
=== link:#olm__required_olm_features_annotations_provided[Required OLM feature annotations list provided]
Expand Down Expand Up @@ -836,6 +837,18 @@ Check the input snapshot and make sure all the images are accessible.
* Effective from: `2024-08-15T00:00:00Z`
* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L156[Source, window="_blank"]

[#olm__inaccessible_related_images]
=== link:#olm__inaccessible_related_images[Unable to access related images for a component]

Check the input image for the presence of related images. Ensure that all images are accessible.

*Solution*: Ensure all related images are available. The related images are defined by an file containing a json array attached to the validated image. The digest of the attached file is pulled from the RELATED_IMAGES_DIGEST result.

* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `The %q related image reference is not accessible.`
* Code: `olm.inaccessible_related_images`
* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L178[Source, window="_blank"]

[#olm__unmapped_references]
=== link:#olm__unmapped_references[Unmapped images in OLM bundle]

Expand All @@ -847,7 +860,7 @@ Check the OLM bundle image for the presence of unmapped image references. Unmapp
* FAILURE message: `The %q CSV image reference is not in the snapshot or accessible.`
* Code: `olm.unmapped_references`
* Effective from: `2024-08-15T00:00:00Z`
* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L178[Source, window="_blank"]
* https://github.com/enterprise-contract/ec-policies/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L215[Source, window="_blank"]

[#olm__unpinned_references]
=== link:#olm__unpinned_references[Unpinned images in OLM bundle]
Expand Down
1 change: 1 addition & 0 deletions antora/docs/modules/ROOT/partials/release_policy_nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
**** xref:release_policy.adoc#olm__required_olm_features_annotations_provided[Required OLM feature annotations list provided]
**** xref:release_policy.adoc#olm__subscriptions_annotation_format[Subscription annotation has expected value]
**** xref:release_policy.adoc#olm__inaccessible_snapshot_references[Unable to access images in the input snapshot]
**** xref:release_policy.adoc#olm__inaccessible_related_images[Unable to access related images for a component]
**** xref:release_policy.adoc#olm__unmapped_references[Unmapped images in OLM bundle]
**** xref:release_policy.adoc#olm__unpinned_references[Unpinned images in OLM bundle]
**** xref:release_policy.adoc#olm__unpinned_snapshot_references[Unpinned images in input snapshot]
Expand Down
71 changes: 71 additions & 0 deletions policy/release/olm/olm.rego
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,43 @@ deny contains result if {
result := lib.result_helper_with_term(rego.metadata.chain(), [component.containerImage], component.containerImage)
}

# METADATA
# title: Unable to access related images for a component
# description: >-
# Check the input image for the presence of related images.
# Ensure that all images are accessible.
# custom:
# short_name: inaccessible_related_images
# failure_msg: The %q related image reference is not accessible.
# solution: >-
# Ensure all related images are available. The related images are defined by
# an file containing a json array attached to the validated image. The digest
# of the attached file is pulled from the RELATED_IMAGES_DIGEST result.
# collections:
# - redhat
deny contains result if {
_release_restrictions_apply

snapshot_components := input.snapshot.components
component_images_digests := [component_image.digest |
some component in snapshot_components
component_image := image.parse(component.containerImage)
]

some related_images in _related_images(input.image)

unmatched_image_refs := [related |
some related in related_images
not related.digest in component_images_digests
]

some unmatched_image in unmatched_image_refs
unmatched_ref := sprintf("%s@%s", [unmatched_image.repo, unmatched_image.digest])
not ec.oci.descriptor(unmatched_ref)

result := lib.result_helper_with_term(rego.metadata.chain(), [unmatched_ref], unmatched_ref)
}

# METADATA
# title: Unmapped images in OLM bundle
# description: >-
Expand Down Expand Up @@ -249,6 +286,36 @@ deny contains result if {
result := lib.result_helper_with_term(rego.metadata.chain(), [img_str], img.ref.repo)
}

# Extracts the related images attached to the image. The RELATED_IMAGES_DIGEST result
# contains the digest of a referring image manifest containing the related image json
# array. We need to find the blob sha in order to download the related images.
_related_images(tested_image) := [e |
some imgs in [[r |
input_image := image.parse(tested_image.ref)

some related in lib.results_named(_related_images_result_name)
result_digest := object.union(input_image, {"digest": sprintf("%s", [trim_space(related.value)])})
related_image_ref := image.str(result_digest)
related_image_manifest := ec.oci.image_manifest(related_image_ref)

some layer in related_image_manifest.layers
layer.mediaType == _related_images_oci_mime_type
related_image_blob := object.union(input_image, {"digest": layer.digest})
related_image_blob_ref := image.str(related_image_blob)

raw_related_images := json.unmarshal(ec.oci.blob(related_image_blob_ref))

some related_ref in raw_related_images
r := {
"path": "relatedImage",
"ref": image.parse(related_ref),
}
]]
some i in imgs

e := {"ref": i.ref, "path": i.path}
]

_name(o) := n if {
n := o.name
} else := "unnamed"
Expand Down Expand Up @@ -446,3 +513,7 @@ _image_registry_allowed(image_repo, allowed_prefixes) if {
some allowed_prefix in allowed_prefixes
startswith(image_repo, allowed_prefix)
}

_related_images_result_name := "RELATED_IMAGES_DIGEST"

_related_images_oci_mime_type := "application/vnd.konflux-ci.attached-artifact.related-images+json"
66 changes: 63 additions & 3 deletions policy/release/olm/olm_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package olm_test
import rego.v1

import data.lib
import data.lib.tekton_test
import data.lib_test
import data.olm

pinned := "registry.io/repository/image@sha256:cafe"
Expand Down Expand Up @@ -377,13 +379,30 @@ test_unmapped_references_in_operator if {
lib.assert_equal_results(olm.deny, expected) with input.snapshot.components as [component1]
with input.image.files as {"manifests/csv.yaml": manifest}
with data.rule_data as {"pipeline_intention": "release", "allowed_registry_prefixes": ["registry.io"]}
with ec.oci.image_manifest as mock_ec_oci_image_manifest
with ec.oci.image_manifest as _mock_image_manifest
with ec.oci.descriptor as mock_ec_oci_image_descriptor
with input.image.config.Labels as {olm.manifestv1: "manifests/"}
}

mock_ec_oci_image_manifest("registry.io/repository/image@sha256:cafe") := `{"config": {"digest": "sha256:cafe"}}`
test_inaccessible_related_images if {
expected_deny := {{
"code": "olm.inaccessible_related_images",
"msg": "The \"registry.io/repository/image2@sha256:tea\" related image reference is not accessible.",
"term": "registry.io/repository/image2@sha256:tea",
}}

lib.assert_equal_results(olm.deny, expected_deny) with data.rule_data.pipeline_intention as "release"
with input.snapshot.components as [component1]
with input.attestations as _with_related_images
with input.image.ref as "registry.io/repository/image@sha256:image_digest"
with ec.oci.image_manifest as _mock_image_manifest
with ec.oci.blob as _mock_blob
with ec.oci.descriptor as mock_ec_oci_image_descriptor
}

mock_ec_oci_image_descriptor("registry.io/repository/image3@sha256:coffee") := `{"config": {"digest": "sha256:coffee"}}`

mock_ec_oci_image_manifest("registry.io/repository/image2@sha256:tea") := false
mock_ec_oci_image_descriptor("registry.io/repository/image2@sha256:tea") := false

test_olm_ci_pipeline if {
# Make sure no violations are thrown if it isn't a release pipeline
Expand Down Expand Up @@ -427,3 +446,44 @@ test_unallowed_registries if {
with input.image.config.Labels as {olm.manifestv1: "manifests/"}
with input.image.files as {"manifests/csv.yaml": manifest}
}

_related_images := [pinned, pinned2, "registry.io/repository/image3@sha256:coffee"]

_manifests := {
"registry.io/repository/image@sha256:related_digest": {"layers": [{
"mediaType": olm._related_images_oci_mime_type,
"digest": "sha256:related_blob_digest",
}]},
"registry.io/repository/image@sha256:cafe": {"config": {"digest": "sha256:cafe"}},
}

_blobs := {"registry.io/repository/image@sha256:related_blob_digest": json.marshal(_related_images)}

_mock_image_manifest(ref) := _manifests[ref]

_mock_blob(ref) := _blobs[ref]

_bundle := "registry.img/spam@sha256:4e388ab32b10dc8dbc7e28144f552830adc74787c1e2c0824032078a79f227fb"

_with_related_images := _attestations_with_attachment("sha256:related_digest")

_attestations_with_attachment(attachment) := attestations if {
slsav1_task_with_result := tekton_test.slsav1_task_result_ref(
"validate-fbc",
[{
"name": olm._related_images_result_name,
"type": "string",
"value": attachment,
}],
)

attestations := [
lib_test.att_mock_helper_ref(
olm._related_images_result_name,
attachment,
"validate-fbc",
_bundle,
),
lib_test.mock_slsav1_attestation_with_tasks([tekton_test.slsav1_task_bundle(slsav1_task_with_result, _bundle)]),
]
}
Loading