Skip to content

Commit a6fb24e

Browse files
committed
Add builtin func for OCI Referrers API discovery
Implement ec.oci.image_referrers() builtin to discover artifacts attached to images via the OCI Referrers API. Returns descriptors with mediaType, size, digest, and artifactType for all referrers, enabling policy rules to validate modern OCI artifact associations without relying on legacy tag-based conventions. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Ref: https://issues.redhat.com/browse/EC-1655
1 parent 25213e3 commit a6fb24e

10 files changed

Lines changed: 892 additions & 101 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package referrers
2+
3+
import rego.v1
4+
5+
# METADATA
6+
# custom:
7+
# short_name: count
8+
deny contains result if {
9+
refs := ec.oci.image_referrers(input.image.ref)
10+
count(refs) != 2
11+
result := {
12+
"code": "referrers.count",
13+
"msg": sprintf("Expected 2 referrers, got %d: %v", [count(refs), refs]),
14+
}
15+
}
16+
17+
# METADATA
18+
# custom:
19+
# short_name: format
20+
deny contains result if {
21+
descriptors := ec.oci.image_referrers(input.image.ref)
22+
not all_descriptors_valid_format(descriptors)
23+
result := {
24+
"code": "referrers.format",
25+
"msg": sprintf("Invalid referrer descriptor format in: %v", [descriptors]),
26+
}
27+
}
28+
29+
# METADATA
30+
# custom:
31+
# short_name: content_types
32+
deny contains result if {
33+
descriptors := ec.oci.image_referrers(input.image.ref)
34+
not has_expected_artifact_types(descriptors)
35+
result := {
36+
"code": "referrers.content_types",
37+
"msg": sprintf("Expected one signature and one attestation artifact type in referrers: %v", [descriptors]),
38+
}
39+
}
40+
41+
all_descriptors_valid_format(descriptors) if {
42+
every descriptor in descriptors {
43+
# Each descriptor should have required fields
44+
descriptor.digest != ""
45+
descriptor.mediaType != ""
46+
descriptor.size >= 0
47+
descriptor.artifactType != ""
48+
descriptor.ref != ""
49+
50+
# Digest should be a digest-only format: sha256:<hex>
51+
startswith(descriptor.digest, "sha256:")
52+
not contains(descriptor.digest, "@")
53+
54+
# Ref should be a full OCI reference with digest format: registry/repo@sha256:<hex>
55+
contains(descriptor.ref, "@")
56+
contains(descriptor.ref, "sha256:")
57+
# Split by @ and verify format
58+
parts := split(descriptor.ref, "@")
59+
count(parts) == 2
60+
# Verify digest format matches
61+
parts[1] == descriptor.digest
62+
}
63+
}
64+
65+
has_expected_artifact_types(descriptors) if {
66+
# Check that we have one signature artifact directly from descriptors
67+
signature_artifacts := [d |
68+
some d in descriptors
69+
d.artifactType == "application/vnd.dev.cosign.simplesigning.v1+json"
70+
]
71+
count(signature_artifacts) == 1
72+
73+
# Check that we have one attestation artifact directly from descriptors
74+
attestation_artifacts := [d |
75+
some d in descriptors
76+
d.artifactType == "application/vnd.dsse.envelope.v1+json"
77+
]
78+
count(attestation_artifacts) == 1
79+
}

0 commit comments

Comments
 (0)