|
1 |
| -# input: "build" object (with "buildId" top level key) |
2 |
| -# output: list of image tags |
3 |
| -def tags: |
4 |
| - [ |
5 |
| - .source.arches[].tags[], |
6 |
| - .source.arches[].archTags[], |
7 |
| - .build.img |
8 |
| - ] |
9 |
| -; |
10 |
| - |
11 |
| -# input: "tags" object with image digest and platform arguments |
12 |
| -# output: json object for in-toto provenance subject field |
13 |
| -def subjects($platform; $digest): |
14 |
| - ($digest | split(":")) as $splitDigest |
15 |
| - | { |
16 |
| - "name": "pkg:docker/\(.)?platform=\($platform)", |
17 |
| - "digest": { |
18 |
| - ($splitDigest[0]): $splitDigest[1], |
19 |
| - } |
20 |
| - } |
21 |
| -; |
22 |
| - |
23 |
| -# input: GITHUB context |
24 |
| -# output: json object for in-toto provenance external parameters field |
25 |
| -def github_external_parameters($github): |
26 |
| - ($github.workflow_ref | ltrimstr($github.repository + "/") | split("@")) as $workflowRefSplit |
27 |
| - | { |
28 |
| - inputs: $github.event.inputs, |
29 |
| - workflow: { |
30 |
| - ref: $workflowRefSplit[1], |
31 |
| - repository: ($github.server_url + "/" + $github.repository), |
32 |
| - path: $workflowRefSplit[0], |
33 |
| - digest: { gitCommit: $github.workflow_sha }, |
34 |
| - } |
35 |
| - } |
36 |
| -; |
37 |
| - |
38 | 1 | # input: "build" object with platform and image digest
|
39 |
| -# output: json object for in-toto provenance statement |
40 |
| -def github_actions_provenance: |
41 |
| - (env.GITHUB_CONTEXT | fromjson) as $github | |
42 |
| - (.source.arches[].platformString | @uri) as $platform | |
43 |
| - { |
44 |
| - _type: "https://in-toto.io/Statement/v1", |
45 |
| - subject: . | tags | map(subjects($platform; $digest)), |
46 |
| - predicateType: "https://slsa.dev/provenance/v1", |
47 |
| - predicate: { |
48 |
| - buildDefinition: { |
49 |
| - buildType: "https://actions.github.io/buildtypes/workflow/v1", |
50 |
| - externalParameters: github_external_parameters($github), |
51 |
| - internalParameters: { |
52 |
| - github: { |
53 |
| - event_name: $github.event_name, |
54 |
| - repository_id: $github.repository_id, |
55 |
| - repository_owner_id: $github.repository_owner_id, |
56 |
| - runner_environment: "github-hosted" |
57 |
| - } |
| 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 | +# $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) |
| 4 | +# |
| 5 | +# output: in-toto provenance statement (https://slsa.dev/spec/v1.0/provenance) |
| 6 | +# see also: https://github.com/actions/buildtypes/tree/main/workflow/v1 |
| 7 | +def github_actions_provenance($github; $digest): |
| 8 | + if $github.event_name != "workflow_dispatch" then error("error: '\($github.event_name)' is not a supported event type for provenance generation") else |
| 9 | + { |
| 10 | + _type: "https://in-toto.io/Statement/v1", |
| 11 | + subject: [ |
| 12 | + ($digest | split(":")) as $splitDigest |
| 13 | + | (.source.arches[.build.arch].platformString) as $platform |
| 14 | + | ( |
| 15 | + .source.arches[.build.arch].tags[], |
| 16 | + .source.arches[.build.arch].archTags[], |
| 17 | + .build.img, |
| 18 | + empty # trailing comma |
| 19 | + ) |
| 20 | + | { |
| 21 | + # 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) |
| 22 | + name: "pkg:docker/\(.)?platform=\($platform | @uri)", |
| 23 | + digest: { ($splitDigest[0]): $splitDigest[1] }, |
| 24 | + } |
| 25 | + ], |
| 26 | + predicateType: "https://slsa.dev/provenance/v1", |
| 27 | + predicate: { |
| 28 | + buildDefinition: { |
| 29 | + buildType: "https://actions.github.io/buildtypes/workflow/v1", |
| 30 | + externalParameters: { |
| 31 | + workflow: { |
| 32 | + # 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 ?) |
| 33 | + ref: $github.ref, |
| 34 | + repository: ($github.server_url + "/" + $github.repository), |
| 35 | + path: ( |
| 36 | + $github.workflow_ref |
| 37 | + | ltrimstr($github.repository + "/") |
| 38 | + | rtrimstr("@" + $github.ref) |
| 39 | + | if contains("@") then error("parsing 'workflow_ref' failed: '\(.)'") else . end |
| 40 | + ), |
| 41 | + # not required, but useful/important (and potentially but unlikely different from $github.sha used in resolvedDependencies below): |
| 42 | + digest: { gitCommit: $github.workflow_sha }, |
| 43 | + }, |
| 44 | + inputs: $github.event.inputs, # https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch |
| 45 | + }, |
| 46 | + internalParameters: { |
| 47 | + github: { |
| 48 | + event_name: $github.event_name, |
| 49 | + repository_id: $github.repository_id, |
| 50 | + repository_owner_id: $github.repository_owner_id, |
| 51 | + runner_environment: "github-hosted", |
| 52 | + }, |
| 53 | + }, |
| 54 | + resolvedDependencies: [ |
| 55 | + { |
| 56 | + uri: "git+\($github.server_url)/\($github.repository)@\($github.ref)", |
| 57 | + digest: { "gitCommit": $github.sha }, |
| 58 | + }, |
| 59 | + # TODO figure out a way to include resolved action SHAs from "uses:" expressions |
| 60 | + # TODO include more resolved dependencies |
| 61 | + empty # tailing comma |
| 62 | + ], |
58 | 63 | },
|
59 |
| - resolvedDependencies: [{ |
60 |
| - uri: ("git+"+$github.server_url+"/"+$github.repository+"@"+$github.ref), |
61 |
| - digest: { "gitCommit": $github.sha } |
62 |
| - }] |
63 |
| - }, |
64 |
| - runDetails: { |
65 |
| - # builder.id identifies the transitive closure of the trusted build platform evalution. |
66 |
| - # any changes that alter security properties or build level must update this ID and rotate the signing key. |
67 |
| - # https://slsa.dev/spec/v1.0/provenance#builder |
68 |
| - builder: { |
69 |
| - id: ($github.server_url+"/"+$github.workflow_ref), |
| 64 | + runDetails: { |
| 65 | + # builder.id identifies the transitive closure of the trusted build platform evalution. |
| 66 | + # any changes that alter security properties or build level must update this ID and rotate the signing key. |
| 67 | + # https://slsa.dev/spec/v1.0/provenance#builder |
| 68 | + builder: { |
| 69 | + id: ($github.server_url + "/" + $github.workflow_ref), |
| 70 | + }, |
| 71 | + metadata: { |
| 72 | + invocationId: "\($github.server_url)/\($github.repository)/actions/runs/\($github.run_id)/attempts/\($github.run_attempt)", |
| 73 | + }, |
70 | 74 | },
|
71 |
| - metadata: { |
72 |
| - invocationId: ($github.server_url+"/"+$github.repository+"/actions/runs/"+$github.run_id+"/attempts/"+$github.run_attempt), |
73 |
| - } |
74 |
| - } |
| 75 | + }, |
75 | 76 | }
|
76 |
| - } |
| 77 | + end |
77 | 78 | ;
|
0 commit comments