Skip to content

Commit 3f90094

Browse files
Merge pull request #76 from mrjoelkamp/feat-add-provenance
feat: add provenance.jq
2 parents c394a5a + 3385931 commit 3f90094

File tree

4 files changed

+383
-0
lines changed

4 files changed

+383
-0
lines changed

.test/provenance/in.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../builds.json

.test/provenance/out.json

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
[
2+
{
3+
"_type": "https://in-toto.io/Statement/v1",
4+
"subject": [
5+
{
6+
"name": "pkg:docker/docker:24.0.7-cli?platform=linux%2Famd64",
7+
"digest": {
8+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
9+
}
10+
},
11+
{
12+
"name": "pkg:docker/docker:24.0-cli?platform=linux%2Famd64",
13+
"digest": {
14+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
15+
}
16+
},
17+
{
18+
"name": "pkg:docker/docker:24-cli?platform=linux%2Famd64",
19+
"digest": {
20+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
21+
}
22+
},
23+
{
24+
"name": "pkg:docker/docker:cli?platform=linux%2Famd64",
25+
"digest": {
26+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
27+
}
28+
},
29+
{
30+
"name": "pkg:docker/docker:24.0.7-cli-alpine3.18?platform=linux%2Famd64",
31+
"digest": {
32+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
33+
}
34+
},
35+
{
36+
"name": "pkg:docker/amd64/docker:24.0.7-cli?platform=linux%2Famd64",
37+
"digest": {
38+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
39+
}
40+
},
41+
{
42+
"name": "pkg:docker/amd64/docker:24.0-cli?platform=linux%2Famd64",
43+
"digest": {
44+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
45+
}
46+
},
47+
{
48+
"name": "pkg:docker/amd64/docker:24-cli?platform=linux%2Famd64",
49+
"digest": {
50+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
51+
}
52+
},
53+
{
54+
"name": "pkg:docker/amd64/docker:cli?platform=linux%2Famd64",
55+
"digest": {
56+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
57+
}
58+
},
59+
{
60+
"name": "pkg:docker/amd64/docker:24.0.7-cli-alpine3.18?platform=linux%2Famd64",
61+
"digest": {
62+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
63+
}
64+
},
65+
{
66+
"name": "pkg:docker/oisupport/staging-amd64:4b199ac326c74b3058a147e14f553af9e8e1659abc29bd3e82c9c9807b66ee43?platform=linux%2Famd64",
67+
"digest": {
68+
"sha256": "153793dfbac130679ad1eebd9e88b3772c47d3903a3f299c49d5c3f23a6e35d2"
69+
}
70+
}
71+
],
72+
"predicateType": "https://slsa.dev/provenance/v1",
73+
"predicate": {
74+
"buildDefinition": {
75+
"buildType": "https://actions.github.io/buildtypes/workflow/v1",
76+
"externalParameters": {
77+
"workflow": {
78+
"ref": "refs/heads/subset",
79+
"repository": "https://github.com/docker-library/meta",
80+
"path": ".github/workflows/build.yml",
81+
"digest": {
82+
"gitCommit": "0123456789abcdef0123456789abcdef01234567"
83+
}
84+
},
85+
"inputs": {
86+
"buildId": "4b199ac326c74b3058a147e14f553af9e8e1659abc29bd3e82c9c9807b66ee43",
87+
"bashbrewArch": "amd64",
88+
"firstTag": "docker:24.0.7-cli"
89+
}
90+
},
91+
"internalParameters": {
92+
"github": {
93+
"event_name": "workflow_dispatch",
94+
"repository_id": "1234",
95+
"repository_owner_id": "5678",
96+
"runner_environment": "github-hosted"
97+
}
98+
},
99+
"resolvedDependencies": [
100+
{
101+
"uri": "git+https://github.com/docker-library/meta@refs/heads/subset",
102+
"digest": {
103+
"gitCommit": "0123456789abcdef0123456789abcdef01234567"
104+
}
105+
}
106+
]
107+
},
108+
"runDetails": {
109+
"builder": {
110+
"id": "https://github.com/docker-library/meta/.github/workflows/build.yml@refs/heads/subset"
111+
},
112+
"metadata": {
113+
"invocationId": "https://github.com/docker-library/meta/actions/runs/9001/attempts/2"
114+
}
115+
}
116+
}
117+
},
118+
{
119+
"_type": "https://in-toto.io/Statement/v1",
120+
"subject": [
121+
{
122+
"name": "pkg:docker/docker:24.0.7-windowsservercore-ltsc2022?platform=windows%2Famd64",
123+
"digest": {
124+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
125+
}
126+
},
127+
{
128+
"name": "pkg:docker/docker:24.0-windowsservercore-ltsc2022?platform=windows%2Famd64",
129+
"digest": {
130+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
131+
}
132+
},
133+
{
134+
"name": "pkg:docker/docker:24-windowsservercore-ltsc2022?platform=windows%2Famd64",
135+
"digest": {
136+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
137+
}
138+
},
139+
{
140+
"name": "pkg:docker/docker:windowsservercore-ltsc2022?platform=windows%2Famd64",
141+
"digest": {
142+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
143+
}
144+
},
145+
{
146+
"name": "pkg:docker/docker:24.0.7-windowsservercore?platform=windows%2Famd64",
147+
"digest": {
148+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
149+
}
150+
},
151+
{
152+
"name": "pkg:docker/docker:24.0-windowsservercore?platform=windows%2Famd64",
153+
"digest": {
154+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
155+
}
156+
},
157+
{
158+
"name": "pkg:docker/docker:24-windowsservercore?platform=windows%2Famd64",
159+
"digest": {
160+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
161+
}
162+
},
163+
{
164+
"name": "pkg:docker/docker:windowsservercore?platform=windows%2Famd64",
165+
"digest": {
166+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
167+
}
168+
},
169+
{
170+
"name": "pkg:docker/winamd64/docker:24.0.7-windowsservercore-ltsc2022?platform=windows%2Famd64",
171+
"digest": {
172+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
173+
}
174+
},
175+
{
176+
"name": "pkg:docker/winamd64/docker:24.0-windowsservercore-ltsc2022?platform=windows%2Famd64",
177+
"digest": {
178+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
179+
}
180+
},
181+
{
182+
"name": "pkg:docker/winamd64/docker:24-windowsservercore-ltsc2022?platform=windows%2Famd64",
183+
"digest": {
184+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
185+
}
186+
},
187+
{
188+
"name": "pkg:docker/winamd64/docker:windowsservercore-ltsc2022?platform=windows%2Famd64",
189+
"digest": {
190+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
191+
}
192+
},
193+
{
194+
"name": "pkg:docker/winamd64/docker:24.0.7-windowsservercore?platform=windows%2Famd64",
195+
"digest": {
196+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
197+
}
198+
},
199+
{
200+
"name": "pkg:docker/winamd64/docker:24.0-windowsservercore?platform=windows%2Famd64",
201+
"digest": {
202+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
203+
}
204+
},
205+
{
206+
"name": "pkg:docker/winamd64/docker:24-windowsservercore?platform=windows%2Famd64",
207+
"digest": {
208+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
209+
}
210+
},
211+
{
212+
"name": "pkg:docker/winamd64/docker:windowsservercore?platform=windows%2Famd64",
213+
"digest": {
214+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
215+
}
216+
},
217+
{
218+
"name": "pkg:docker/oisupport/staging-windows-amd64:9b405cfa5b88ba65121aabdb95ae90fd2e1fee7582174de82ae861613ae3072e?platform=windows%2Famd64",
219+
"digest": {
220+
"sha256": "69aba7120e3f4014bfa80f4eae2cfc9698dcb6b8a5d64daf06de4039a19846ce"
221+
}
222+
}
223+
],
224+
"predicateType": "https://slsa.dev/provenance/v1",
225+
"predicate": {
226+
"buildDefinition": {
227+
"buildType": "https://actions.github.io/buildtypes/workflow/v1",
228+
"externalParameters": {
229+
"workflow": {
230+
"ref": "refs/heads/subset",
231+
"repository": "https://github.com/docker-library/meta",
232+
"path": ".github/workflows/build.yml",
233+
"digest": {
234+
"gitCommit": "0123456789abcdef0123456789abcdef01234567"
235+
}
236+
},
237+
"inputs": {
238+
"buildId": "9b405cfa5b88ba65121aabdb95ae90fd2e1fee7582174de82ae861613ae3072e",
239+
"bashbrewArch": "windows-amd64",
240+
"firstTag": "docker:24.0.7-windowsservercore-ltsc2022",
241+
"windowsVersion": "2022"
242+
}
243+
},
244+
"internalParameters": {
245+
"github": {
246+
"event_name": "workflow_dispatch",
247+
"repository_id": "1234",
248+
"repository_owner_id": "5678",
249+
"runner_environment": "github-hosted"
250+
}
251+
},
252+
"resolvedDependencies": [
253+
{
254+
"uri": "git+https://github.com/docker-library/meta@refs/heads/subset",
255+
"digest": {
256+
"gitCommit": "0123456789abcdef0123456789abcdef01234567"
257+
}
258+
}
259+
]
260+
},
261+
"runDetails": {
262+
"builder": {
263+
"id": "https://github.com/docker-library/meta/.github/workflows/build.yml@refs/heads/subset"
264+
},
265+
"metadata": {
266+
"invocationId": "https://github.com/docker-library/meta/actions/runs/9001/attempts/2"
267+
}
268+
}
269+
}
270+
}
271+
]

.test/provenance/test.jq

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
include "provenance";
2+
include "jenkins";
3+
4+
[
5+
first(.[] | select(.build.arch == "amd64" and .build.resolved)),
6+
first(.[] | select(.build.arch == "windows-amd64" and .build.resolved)),
7+
empty # trailing comma
8+
9+
| (.build.resolved.annotations["org.opencontainers.image.ref.name"] | split("@")[1]) as $digest
10+
11+
# some faked GitHub event data so we can ~test the provenance generation
12+
| gha_payload as $payload
13+
| {
14+
event: $payload,
15+
event_name: "workflow_dispatch",
16+
ref: "refs/heads/\($payload.ref)",
17+
repository: "docker-library/meta",
18+
repository_id: "1234",
19+
repository_owner_id: "5678",
20+
run_attempt: "2",
21+
run_id: "9001",
22+
server_url: "https://github.com",
23+
sha: "0123456789abcdef0123456789abcdef01234567",
24+
workflow_ref: "docker-library/meta/.github/workflows/build.yml@refs/heads/\($payload.ref)",
25+
workflow_sha: "0123456789abcdef0123456789abcdef01234567",
26+
} as $github
27+
| {
28+
environment: "github-hosted",
29+
} as $runner
30+
31+
| github_actions_provenance($github; $runner; $digest)
32+
]

provenance.jq

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# input: "build" object with platform and image digest
2+
# $github: "github" context; CONTAINS SENSITIVE INFORMATION (https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context)
3+
# $runner: "runner" context; https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#runner-context
4+
# $digest: the OCI image digest for the just-built image (normally in .build.resolved.annotations["org.opencontainers.image.ref.name"] but only post-push/regeneration and we haven't pushed yet)
5+
#
6+
# output: in-toto provenance statement (https://slsa.dev/spec/v1.0/provenance)
7+
# see also: https://github.com/actions/buildtypes/tree/main/workflow/v1
8+
def github_actions_provenance($github; $runner; $digest):
9+
if $github.event_name != "workflow_dispatch" then error("error: '\($github.event_name)' is not a supported event type for provenance generation") else
10+
{
11+
_type: "https://in-toto.io/Statement/v1",
12+
subject: [
13+
($digest | split(":")) as $splitDigest
14+
| (.source.arches[.build.arch].platformString) as $platform
15+
| (
16+
.source.arches[.build.arch].tags[],
17+
.source.arches[.build.arch].archTags[],
18+
.build.img,
19+
empty # trailing comma
20+
)
21+
| {
22+
# https://github.com/package-url/purl-spec/blob/b33dda1cf4515efa8eabbbe8e9b140950805f845/PURL-TYPES.rst#docker (this matches what BuildKit generates as of 2024-09-18; "oci" would also be a reasonable choice, but would require signer and policy changes to support, and be more complex to generate accurately)
23+
name: "pkg:docker/\(.)?platform=\($platform | @uri)",
24+
digest: { ($splitDigest[0]): $splitDigest[1] },
25+
}
26+
],
27+
predicateType: "https://slsa.dev/provenance/v1",
28+
predicate: {
29+
buildDefinition: {
30+
buildType: "https://actions.github.io/buildtypes/workflow/v1",
31+
externalParameters: {
32+
workflow: {
33+
# TODO this matches how this is documented/suggested in GitHub's buildType documentation, but does not account for the workflow file being in a separate repository at a separate ref from the "source" (which the "workflow_ref" field *does* account for), so that would/will change how we need to calculate these values if we ever do that (something like "^(?<repo>[^/]+/[^/]+)/(?<path>.*)@(?<ref>refs/.*)$" on $github.workflow_ref ?)
34+
ref: $github.ref,
35+
repository: ($github.server_url + "/" + $github.repository),
36+
path: (
37+
$github.workflow_ref
38+
| ltrimstr($github.repository + "/")
39+
| rtrimstr("@" + $github.ref)
40+
| if contains("@") then error("parsing 'workflow_ref' failed: '\(.)'") else . end
41+
),
42+
# not required, but useful/important (and potentially but unlikely different from $github.sha used in resolvedDependencies below):
43+
digest: { gitCommit: $github.workflow_sha },
44+
},
45+
inputs: $github.event.inputs, # https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch
46+
},
47+
internalParameters: {
48+
github: {
49+
event_name: $github.event_name,
50+
repository_id: $github.repository_id,
51+
repository_owner_id: $github.repository_owner_id,
52+
runner_environment: $runner.environment,
53+
},
54+
},
55+
resolvedDependencies: [
56+
{
57+
uri: "git+\($github.server_url)/\($github.repository)@\($github.ref)",
58+
digest: { "gitCommit": $github.sha },
59+
},
60+
# TODO figure out a way to include resolved action SHAs from "uses:" expressions
61+
# TODO include more resolved dependencies
62+
empty # tailing comma
63+
],
64+
},
65+
runDetails: {
66+
# builder.id identifies the transitive closure of the trusted build platform evalution.
67+
# any changes that alter security properties or build level must update this ID and rotate the signing key.
68+
# https://slsa.dev/spec/v1.0/provenance#builder
69+
builder: {
70+
id: ($github.server_url + "/" + $github.workflow_ref),
71+
},
72+
metadata: {
73+
invocationId: ($github.server_url + "/" + $github.repository + "/actions/runs/" + $github.run_id + "/attempts/" + $github.run_attempt)
74+
},
75+
},
76+
},
77+
}
78+
end
79+
;

0 commit comments

Comments
 (0)