Skip to content

Commit 25213e3

Browse files
committed
Add builtin func for tag-based artifact discovery
Implement ec.oci.image_tag_refs() builtin to discover artifacts attached to images using legacy cosign tag conventions (.sig, .att, .sbom suffixes). This enables policy rules to locate and validate tag-based signatures, attestations, and SBOMs alongside the newer OCI Referrers API approach. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Ref: https://issues.redhat.com/browse/EC-1655
1 parent 36834a2 commit 25213e3

8 files changed

Lines changed: 536 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package tag_refs
2+
3+
import rego.v1
4+
5+
# METADATA
6+
# custom:
7+
# short_name: count
8+
deny contains result if {
9+
refs := ec.oci.image_tag_refs(input.image.ref)
10+
count(refs) != 2
11+
result := {
12+
"code": "tag_refs.count",
13+
"msg": sprintf("Expected 2 tag-based artifact references, got %d: %v", [count(refs), refs]),
14+
}
15+
}
16+
17+
# METADATA
18+
# custom:
19+
# short_name: format
20+
deny contains result if {
21+
refs := ec.oci.image_tag_refs(input.image.ref)
22+
not all_refs_valid_format(refs)
23+
result := {
24+
"code": "tag_refs.format",
25+
"msg": sprintf("Invalid tag reference format in: %v", [refs]),
26+
}
27+
}
28+
29+
# METADATA
30+
# custom:
31+
# short_name: sig_count
32+
deny contains result if {
33+
refs := ec.oci.image_tag_refs(input.image.ref)
34+
sig_count := count([ref | some ref in refs; contains(ref, ".sig")])
35+
sig_count != 1
36+
result := {
37+
"code": "tag_refs.sig_count",
38+
"msg": sprintf("Expected 1 .sig reference, got %d", [sig_count]),
39+
}
40+
}
41+
42+
# METADATA
43+
# custom:
44+
# short_name: att_count
45+
deny contains result if {
46+
refs := ec.oci.image_tag_refs(input.image.ref)
47+
att_count := count([ref | some ref in refs; contains(ref, ".att")])
48+
att_count != 1
49+
result := {
50+
"code": "tag_refs.att_count",
51+
"msg": sprintf("Expected 1 .att reference, got %d", [att_count]),
52+
}
53+
}
54+
55+
all_refs_valid_format(refs) if {
56+
every ref in refs {
57+
# Each ref should be a valid OCI reference with tag format: registry/repo:sha256-<hex>.<suffix>
58+
contains(ref, ":")
59+
contains(ref, "sha256-")
60+
# Split by : and get the last part (the tag)
61+
parts := split(ref, ":")
62+
tag_part := parts[count(parts) - 1]
63+
# Tag should start with sha256- and end with .sig or .att
64+
startswith(tag_part, "sha256-")
65+
valid_suffix(tag_part)
66+
}
67+
}
68+
69+
valid_suffix(tag) if {
70+
endswith(tag, ".sig")
71+
}
72+
73+
valid_suffix(tag) if {
74+
endswith(tag, ".att")
75+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
= ec.oci.image_tag_refs
2+
3+
Discover artifacts attached to an image via legacy tag-based discovery (cosign .sig, .att, .sbom suffixes).
4+
5+
== Usage
6+
7+
refs = ec.oci.image_tag_refs(ref: string)
8+
9+
== Parameters
10+
11+
* `ref` (`string`): OCI image reference
12+
13+
== Return
14+
15+
`refs` (`array<string>`): list of tag-based artifact references

docs/modules/ROOT/pages/rego_builtins.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ information.
2222
|Fetch an Image Manifest from an OCI registry.
2323
|xref:ec_oci_image_manifests.adoc[ec.oci.image_manifests]
2424
|Fetch Image Manifests from an OCI registry in parallel.
25+
|xref:ec_oci_image_tag_refs.adoc[ec.oci.image_tag_refs]
26+
|Discover artifacts attached to an image via legacy tag-based discovery (cosign .sig, .att, .sbom suffixes).
2527
|xref:ec_purl_is_valid.adoc[ec.purl.is_valid]
2628
|Determine whether or not a given PURL is valid.
2729
|xref:ec_purl_parse.adoc[ec.purl.parse]

docs/modules/ROOT/partials/rego_nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
** xref:ec_oci_image_index.adoc[ec.oci.image_index]
77
** xref:ec_oci_image_manifest.adoc[ec.oci.image_manifest]
88
** xref:ec_oci_image_manifests.adoc[ec.oci.image_manifests]
9+
** xref:ec_oci_image_tag_refs.adoc[ec.oci.image_tag_refs]
910
** xref:ec_purl_is_valid.adoc[ec.purl.is_valid]
1011
** xref:ec_purl_parse.adoc[ec.purl.parse]
1112
** xref:ec_sigstore_verify_attestation.adoc[ec.sigstore.verify_attestation]

features/__snapshots__/validate_image.snap

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5593,3 +5593,98 @@ Error: success criteria not met
55935593
[happy day with skip-image-sig-check flag:stderr - 1]
55945594

55955595
---
5596+
5597+
[discover tag-based artifact references:stdout - 1]
5598+
{
5599+
"success": true,
5600+
"components": [
5601+
{
5602+
"name": "Unnamed",
5603+
"containerImage": "${REGISTRY}/acceptance/image-tag-refs@sha256:${REGISTRY_acceptance/image-tag-refs:latest_DIGEST}",
5604+
"source": {},
5605+
"successes": [
5606+
{
5607+
"msg": "Pass",
5608+
"metadata": {
5609+
"code": "builtin.attestation.signature_check"
5610+
}
5611+
},
5612+
{
5613+
"msg": "Pass",
5614+
"metadata": {
5615+
"code": "builtin.attestation.syntax_check"
5616+
}
5617+
},
5618+
{
5619+
"msg": "Pass",
5620+
"metadata": {
5621+
"code": "builtin.image.signature_check"
5622+
}
5623+
},
5624+
{
5625+
"msg": "Pass",
5626+
"metadata": {
5627+
"code": "tag_refs.att_count"
5628+
}
5629+
},
5630+
{
5631+
"msg": "Pass",
5632+
"metadata": {
5633+
"code": "tag_refs.count"
5634+
}
5635+
},
5636+
{
5637+
"msg": "Pass",
5638+
"metadata": {
5639+
"code": "tag_refs.format"
5640+
}
5641+
},
5642+
{
5643+
"msg": "Pass",
5644+
"metadata": {
5645+
"code": "tag_refs.sig_count"
5646+
}
5647+
}
5648+
],
5649+
"success": true,
5650+
"signatures": [
5651+
{
5652+
"keyid": "",
5653+
"sig": "${IMAGE_SIGNATURE_acceptance/image-tag-refs}"
5654+
}
5655+
],
5656+
"attestations": [
5657+
{
5658+
"type": "https://in-toto.io/Statement/v0.1",
5659+
"predicateType": "https://slsa.dev/provenance/v0.2",
5660+
"predicateBuildType": "https://tekton.dev/attestations/chains/pipelinerun@v2",
5661+
"signatures": [
5662+
{
5663+
"keyid": "",
5664+
"sig": "${ATTESTATION_SIGNATURE_acceptance/image-tag-refs}"
5665+
}
5666+
]
5667+
}
5668+
]
5669+
}
5670+
],
5671+
"key": "${known_PUBLIC_KEY_JSON}",
5672+
"policy": {
5673+
"sources": [
5674+
{
5675+
"policy": [
5676+
"git::${GITHOST}/git/image-tag-refs-policy?ref=${LATEST_COMMIT}"
5677+
]
5678+
}
5679+
],
5680+
"rekorUrl": "${REKOR}",
5681+
"publicKey": "${known_PUBLIC_KEY}"
5682+
},
5683+
"ec-version": "${EC_VERSION}",
5684+
"effective-time": "${TIMESTAMP}"
5685+
}
5686+
---
5687+
5688+
[discover tag-based artifact references:stderr - 1]
5689+
5690+
---

features/validate_image.feature

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,29 @@ Feature: evaluate enterprise contract
11511151
Then the exit status should be 0
11521152
Then the output should match the snapshot
11531153

1154+
Scenario: discover tag-based artifact references
1155+
Given a key pair named "known"
1156+
Given an image named "acceptance/image-tag-refs"
1157+
Given a valid image signature of "acceptance/image-tag-refs" image signed by the "known" key
1158+
Given a valid attestation of "acceptance/image-tag-refs" signed by the "known" key
1159+
Given a git repository named "image-tag-refs-policy" with
1160+
| main.rego | examples/image_tag_refs.rego |
1161+
Given policy configuration named "ec-policy" with specification
1162+
"""
1163+
{
1164+
"sources": [
1165+
{
1166+
"policy": [
1167+
"git::https://${GITHOST}/git/image-tag-refs-policy"
1168+
]
1169+
}
1170+
]
1171+
}
1172+
"""
1173+
When ec command is run with "validate image --image ${REGISTRY}/acceptance/image-tag-refs --policy acceptance/ec-policy --public-key ${known_PUBLIC_KEY} --rekor-url ${REKOR} --show-successes --output json"
1174+
Then the exit status should be 0
1175+
Then the output should match the snapshot
1176+
11541177
Scenario: tracing and debug logging
11551178
Given a key pair named "trace_debug"
11561179
And an image named "acceptance/trace-debug"

0 commit comments

Comments
 (0)