Skip to content

Commit e46c21e

Browse files
authored
Merge pull request #3169 from st3penta/EC-1655
Add builtin functions for OCI artifacts discovery
2 parents a5567d7 + a6fb24e commit e46c21e

12 files changed

Lines changed: 1428 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+
}
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+
}

0 commit comments

Comments
 (0)