feat: support branch and tag from slsa v1 provenance (#476)

* feat: support branch and tag from slsa v1 provenance

Signed-off-by: Asra Ali <asraa@google.com>

Signed-off-by: Asra Ali <asraa@google.com>
This commit is contained in:
asraa
2023-02-10 21:33:16 +00:00
committed by GitHub
parent 239c4489ce
commit 5d6c770d43
26 changed files with 490 additions and 195 deletions
+6 -6
View File
@@ -7,7 +7,7 @@ require (
github.com/go-openapi/runtime v0.25.0
github.com/google/go-cmp v0.5.9
github.com/google/trillian v1.5.1-0.20220819043421-0a389c4bb8d9 // indirect
github.com/in-toto/in-toto-golang v0.6.1-0.20230207212643-96dcb8c596fb
github.com/in-toto/in-toto-golang v0.6.1-0.20230209184401-f8269f61247e
github.com/secure-systems-lab/go-securesystemslib v0.4.0
github.com/sigstore/rekor v1.0.1
github.com/sigstore/sigstore v1.5.1
@@ -232,18 +232,18 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/exp v0.0.0-20220823124025-807a23277127 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.5.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.28.1
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
+14
View File
@@ -921,6 +921,10 @@ github.com/in-toto/in-toto-golang v0.6.1-0.20230207163334-3493691c6d9c h1:d50sx5
github.com/in-toto/in-toto-golang v0.6.1-0.20230207163334-3493691c6d9c/go.mod h1:AoOsIJkpD8RHrZIAQxXqhGPm7M38IzT4JAdgPpf4OZc=
github.com/in-toto/in-toto-golang v0.6.1-0.20230207212643-96dcb8c596fb h1:lUoNEu/QPf8P52025Pk46e5godp1uRUzSJ3CQknuFdE=
github.com/in-toto/in-toto-golang v0.6.1-0.20230207212643-96dcb8c596fb/go.mod h1:AoOsIJkpD8RHrZIAQxXqhGPm7M38IzT4JAdgPpf4OZc=
github.com/in-toto/in-toto-golang v0.6.1-0.20230209165403-64db848c2ff5 h1:rkgh5ngupN/AKqBOx1Iv6MMGvGjfGHmdp4m1G/V04w4=
github.com/in-toto/in-toto-golang v0.6.1-0.20230209165403-64db848c2ff5/go.mod h1:AoOsIJkpD8RHrZIAQxXqhGPm7M38IzT4JAdgPpf4OZc=
github.com/in-toto/in-toto-golang v0.6.1-0.20230209184401-f8269f61247e h1:GmiHh3s1OVNsSPBNsPSfehT+CcjJN8q6FQCkQphhBOA=
github.com/in-toto/in-toto-golang v0.6.1-0.20230209184401-f8269f61247e/go.mod h1:AoOsIJkpD8RHrZIAQxXqhGPm7M38IzT4JAdgPpf4OZc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -1651,6 +1655,8 @@ golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1778,6 +1784,8 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1956,6 +1964,8 @@ golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1971,6 +1981,8 @@ golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2249,6 +2261,8 @@ google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa h1:qQPhfbPO23fwm/9
google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs=
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+1 -2
View File
@@ -18,7 +18,6 @@ import (
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/options"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
// Load provenance types.
_ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v0.2"
@@ -249,7 +248,7 @@ func VerifyWorkflowInputs(prov slsaprovenance.Provenance, inputs map[string]stri
// Verify all inputs.
for k, v := range inputs {
value, err := utils.GetAsString(pyldInputs, k)
value, err := slsaprovenance.GetAsString(pyldInputs, k)
if err != nil {
return fmt.Errorf("%w: cannot retrieve value of '%s'", serrors.ErrorMismatchWorkflowInputs, k)
}
+127 -11
View File
@@ -3,6 +3,7 @@ package gha
import (
"fmt"
"os"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -586,6 +587,42 @@ func Test_VerifyBranch(t *testing.T) {
branch: "main",
expected: serrors.ErrorMismatchBranch,
},
{
name: "ref main",
path: "./testdata/dsse-main-ref-v1.intoto.jsonl",
branch: "main",
},
{
name: "ref branch3",
path: "./testdata/dsse-branch3-ref-v1.intoto.jsonl",
branch: "branch3",
},
{
name: "invalid ref type",
path: "./testdata/dsse-invalid-ref-type-v1.intoto.jsonl",
expected: serrors.ErrorInvalidDssePayload,
},
{
name: "tag branch2 push trigger",
path: "./testdata/dsse-branch2-tag-v1.intoto.jsonl",
branch: "branch2",
},
{
name: "v10.0.1 release trigger",
path: "./testdata/dsse-v10.0.1-release-v1.intoto.jsonl",
branch: "main",
},
{
name: "from commit push - no branch",
path: "./testdata/dsse-annotated-tag-v1.intoto.jsonl",
branch: "main",
expected: serrors.ErrorMismatchBranch,
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
@@ -671,6 +708,60 @@ func Test_VerifyWorkflowInputs(t *testing.T) {
},
expected: serrors.ErrorMismatchWorkflowInputs,
},
{
name: "match all",
path: "./testdata/dsse-workflow-inputs-v1.intoto.jsonl",
inputs: map[string]string{
"release_version": "v1.2.3",
"some_bool": "true",
"some_integer": "123",
},
},
{
name: "match subset",
path: "./testdata/dsse-workflow-inputs-v1.intoto.jsonl",
inputs: map[string]string{
"release_version": "v1.2.3",
"some_integer": "123",
},
},
{
name: "missing field",
path: "./testdata/dsse-workflow-inputs-v1.intoto.jsonl",
inputs: map[string]string{
"release_version": "v1.2.3",
"missing_field": "123",
},
expected: serrors.ErrorMismatchWorkflowInputs,
},
{
name: "mismatch field release_version",
path: "./testdata/dsse-workflow-inputs-v1.intoto.jsonl",
inputs: map[string]string{
"release_version": "v1.2.4",
"some_integer": "123",
},
expected: serrors.ErrorMismatchWorkflowInputs,
},
{
name: "mismatch field some_integer",
path: "./testdata/dsse-workflow-inputs-v1.intoto.jsonl",
inputs: map[string]string{
"release_version": "v1.2.3",
"some_integer": "124",
},
expected: serrors.ErrorMismatchWorkflowInputs,
},
{
name: "not workflow_dispatch trigger",
path: "./testdata/dsse-workflow-inputs-wrong-trigger-v1.intoto.jsonl",
inputs: map[string]string{
"release_version": "v1.2.3",
"some_bool": "true",
"some_integer": "123",
},
expected: serrors.ErrorMismatchWorkflowInputs,
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
@@ -722,6 +813,26 @@ func Test_VerifyTag(t *testing.T) {
path: "./testdata/dsse-vslsa1-tag.intoto.jsonl",
tag: "vslsa1",
},
{
name: "ref main",
path: "./testdata/dsse-main-ref-v1.intoto.jsonl",
expected: serrors.ErrorMismatchTag,
},
{
name: "ref branch3",
path: "./testdata/dsse-branch3-ref-v1.intoto.jsonl",
expected: serrors.ErrorMismatchTag,
},
{
name: "invalid ref type",
path: "./testdata/dsse-invalid-ref-type-v1.intoto.jsonl",
expected: serrors.ErrorInvalidDssePayload,
},
{
name: "tag vslsa1",
path: "./testdata/dsse-vslsa1-tag-v1.intoto.jsonl",
tag: "vslsa1",
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
@@ -1058,18 +1169,23 @@ func Test_VerifyVersionedTag(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
content, err := os.ReadFile(tt.path)
if err != nil {
panic(fmt.Errorf("os.ReadFile: %w", err))
}
prov, err := provenanceFromBytes(content)
if err != nil {
panic(fmt.Errorf("provenanceFromBytes: %w", err))
}
for _, version := range []string{"", "-v1"} {
pathParts := strings.Split(tt.path, ".intoto")
pathName := []string{pathParts[0] + version}
pathName = append(pathName, pathParts[1:]...)
content, err := os.ReadFile(strings.Join(pathName, ".intoto"))
if err != nil {
panic(fmt.Errorf("os.ReadFile: %w", err))
}
prov, err := provenanceFromBytes(content)
if err != nil {
panic(fmt.Errorf("provenanceFromBytes: %w", err))
}
err = VerifyVersionedTag(prov, tt.tag)
if !errCmp(err, tt.expected) {
t.Errorf(cmp.Diff(err, tt.expected))
err = VerifyVersionedTag(prov, tt.tag)
if !errCmp(err, tt.expected) {
t.Errorf(cmp.Diff(err, tt.expected))
}
}
})
}
@@ -0,0 +1,220 @@
package slsaprovenance
import (
"fmt"
"strings"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
)
// GetWorkflowInputs gets the workflow inputs from the GitHub environment map
// and converts the keys to the necessary casing depending on predicate type.
func GetWorkflowInputs(environment map[string]any, predicateType string) (map[string]any, error) {
// Verify it's a workflow_dispatch trigger.
eventKey, err := convertKey("github_event_name", predicateType)
if err != nil {
return nil, fmt.Errorf("%w: %s",
serrors.ErrorMismatchWorkflowInputs, err)
}
triggerName, err := GetAsString(environment, eventKey)
if err != nil {
return nil, err
}
if triggerName != "workflow_dispatch" {
return nil, fmt.Errorf("%w: expected 'workflow_dispatch' trigger, got %s",
serrors.ErrorMismatchWorkflowInputs, triggerName)
}
payload, err := GetEventPayload(environment, predicateType)
if err != nil {
return nil, err
}
payloadInputs, err := getAsAny(payload, "inputs")
if err != nil {
return nil, fmt.Errorf("%w: error retrieving 'inputs': %v", serrors.ErrorInvalidDssePayload, err)
}
pyldInputs, ok := payloadInputs.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type inputs")
}
return pyldInputs, nil
}
// GetEventPayload retrieves the GitHub event payload from the environment map
// that contains the GitHub context payload.
func GetEventPayload(environment map[string]any, predicateType string) (map[string]any, error) {
eventPayloadKey, err := convertKey("github_event_payload", predicateType)
if err != nil {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
eventPayload, ok := environment[eventPayloadKey]
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type event payload")
}
payload, ok := eventPayload.(map[string]any)
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type payload")
}
return payload, nil
}
func convertKey(key, predicateType string) (string, error) {
switch predicateType {
case ProvenanceV1DraftType:
return strings.ToUpper(key), nil
case ProvenanceV02Type:
return key, nil
default:
return "", fmt.Errorf("unrecognized predicate type %s", predicateType)
}
}
func getAsAny(environment map[string]any, field string) (any, error) {
value, ok := environment[field]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload,
fmt.Sprintf("environment type for %s", field))
}
return value, nil
}
func getBranchForTag(environment map[string]any, predicateType string) (string, error) {
baseRefKey, err := convertKey("github_base_ref", predicateType)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
baseRef, err := GetAsString(environment, baseRefKey)
if err != nil {
return "", err
}
// This `base_ref` seems to always be "".
if baseRef != "" {
return baseRef, nil
}
// Look at the event payload instead.
environmentKey, err := convertKey("github_event_name", predicateType)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
eventName, err := GetAsString(environment, environmentKey)
if err != nil {
return "", err
}
payload, err := GetEventPayload(environment, predicateType)
if err != nil {
return "", err
}
// We don't do that for all triggers because the payload
// is event-specific. Only `push` events seem to have a `base_ref`, and
// `release` events specify a branch in `target_commitish`.
switch eventName {
case "push":
value, err := getAsAny(payload, "base_ref")
if err != nil {
return "", err
}
// The `base_ref` field may be nil if the build was from
// a specific commit rather than a branch.
v, ok := value.(string)
if !ok {
return "", nil
}
return v, nil
case "release":
// For a release event, we look for release.target_commitish.
releasePayload, ok := payload["release"]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "release absent from payload")
}
release, ok := releasePayload.(map[string]interface{})
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type releasePayload")
}
branch, err := GetAsString(release, "target_commitish")
if err != nil {
return "", fmt.Errorf("%w: %s", err, "target_commitish not present")
}
return "refs/heads/" + branch, nil
default:
return "", nil
}
}
func GetTag(environment map[string]any, predicateType string) (string, error) {
refTypeKey, err := convertKey("github_ref_type", predicateType)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
refType, err := GetAsString(environment, refTypeKey)
if err != nil {
return "", err
}
switch refType {
case "branch":
return "", nil
case "tag":
refKey, err := convertKey("github_ref", predicateType)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
return GetAsString(environment, refKey)
default:
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
"unknown ref type", refType)
}
}
func GetBranch(environment map[string]any, predicateType string) (string, error) {
refTypeKey, err := convertKey("github_ref_type", predicateType)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
refType, err := GetAsString(environment, refTypeKey)
if err != nil {
return "", err
}
switch refType {
case "branch":
refKey, err := convertKey("github_ref", predicateType)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
return GetAsString(environment, refKey)
case "tag":
return getBranchForTag(environment, predicateType)
default:
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
"unknown ref type", refType)
}
}
func GetAsString(environment map[string]any, field string) (string, error) {
value, ok := environment[field]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload,
fmt.Sprintf("environment type for %s", field))
}
i, ok := value.(string)
if !ok {
return "", fmt.Errorf("%w: %s '%s'", serrors.ErrorInvalidDssePayload, "environment type string", field)
}
return i, nil
}
@@ -11,6 +11,11 @@ import (
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
)
const (
ProvenanceV1DraftType = "https://slsa.dev/provenance/v1.0?draft"
ProvenanceV02Type = "https://slsa.dev/provenance/v0.2"
)
type Provenance interface {
// BuilderID returns the builder id in the predicate.
BuilderID() (string, error)
@@ -6,7 +6,6 @@ import (
intoto "github.com/in-toto/in-toto-golang/in_toto"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
)
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/473): Use a static mapping.
@@ -14,7 +13,7 @@ import (
//nolint:gochecknoinits
func init() {
slsaprovenance.ProvenanceMap.Store(
"https://slsa.dev/provenance/v0.2",
slsaprovenance.ProvenanceV02Type,
New)
}
@@ -57,20 +56,7 @@ func (prov *ProvenanceV02) GetBranch() (string, error) {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
}
refType, err := utils.GetAsString(environment, "github_ref_type")
if err != nil {
return "", err
}
switch refType {
case "branch":
return utils.GetAsString(environment, "github_ref")
case "tag":
return getBranchForTag(prov)
default:
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
"unknown ref type", refType)
}
return slsaprovenance.GetBranch(environment, prov.PredicateType)
}
func (prov *ProvenanceV02) GetTag() (string, error) {
@@ -79,20 +65,7 @@ func (prov *ProvenanceV02) GetTag() (string, error) {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
}
refType, err := utils.GetAsString(environment, "github_ref_type")
if err != nil {
return "", err
}
switch refType {
case "branch":
return "", nil
case "tag":
return utils.GetAsString(environment, "github_ref")
default:
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
"unknown ref type", refType)
}
return slsaprovenance.GetTag(environment, prov.PredicateType)
}
func (prov *ProvenanceV02) GetWorkflowInputs() (map[string]interface{}, error) {
@@ -102,119 +75,5 @@ func (prov *ProvenanceV02) GetWorkflowInputs() (map[string]interface{}, error) {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
}
triggerName, err := utils.GetAsString(environment, "github_event_name")
if err != nil {
return nil, err
}
if triggerName != "workflow_dispatch" {
return nil, fmt.Errorf("%w: expected 'workflow_dispatch' trigger, got %s",
serrors.ErrorMismatchWorkflowInputs, triggerName)
}
payload, err := getEventPayload(environment)
if err != nil {
return nil, err
}
payloadInputs, err := getAsAny(payload, "inputs")
if err != nil {
return nil, fmt.Errorf("%w: error retrieving 'inputs': %v", serrors.ErrorInvalidDssePayload, err)
}
pyldInputs, ok := payloadInputs.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type inputs")
}
return pyldInputs, nil
}
func getBranchForTag(prov *ProvenanceV02) (string, error) {
// First try the base_ref.
environment, ok := prov.Predicate.Invocation.Environment.(map[string]interface{})
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
}
baseRef, err := utils.GetAsString(environment, "github_base_ref")
if err != nil {
return "", err
}
// This `base_ref` seems to always be "".
if baseRef != "" {
return baseRef, nil
}
// Look at the event payload instead.
eventName, err := utils.GetAsString(environment, "github_event_name")
if err != nil {
return "", err
}
payload, err := getEventPayload(environment)
if err != nil {
return "", err
}
// We don't do that for all triggers because the payload
// is event-specific. Only `push` events seem to have a `base_ref`, and
// `release` events specify a branch in `target_commitish`.
switch eventName {
case "push":
value, err := getAsAny(payload, "base_ref")
if err != nil {
return "", err
}
// The `base_ref` field may be nil if the build was from
// a specific commit rather than a branch.
v, ok := value.(string)
if !ok {
return "", nil
}
return v, nil
case "release":
// For a release event, we look for release.target_commitish.
releasePayload, ok := payload["release"]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "release absent from payload")
}
release, ok := releasePayload.(map[string]interface{})
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type releasePayload")
}
branch, err := utils.GetAsString(release, "target_commitish")
if err != nil {
return "", fmt.Errorf("%w: %s", err, "target_commitish not present")
}
return "refs/heads/" + branch, nil
default:
return "", nil
}
}
func getAsAny(environment map[string]any, field string) (any, error) {
value, ok := environment[field]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload,
fmt.Sprintf("environment type for %s", field))
}
return value, nil
}
func getEventPayload(environment map[string]any) (map[string]any, error) {
eventPayload, ok := environment["github_event_payload"]
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type event payload")
}
payload, ok := eventPayload.(map[string]any)
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type payload")
}
return payload, nil
return slsaprovenance.GetWorkflowInputs(environment, prov.PredicateType)
}
@@ -1,7 +1,6 @@
package v1
import (
"errors"
"fmt"
intoto "github.com/in-toto/in-toto-golang/in_toto"
@@ -15,18 +14,21 @@ import (
//nolint:gochecknoinits
func init() {
slsaprovenance.ProvenanceMap.Store(
"https://slsa.dev/provenance/v1.0?draft",
slsaprovenance.ProvenanceV1DraftType,
New)
}
type ProvenanceV1 struct {
intoto.StatementHeader
Predicate slsa1.ProvenancePredicate `json:"predicate"`
Predicate slsa1.ProvenancePredicate `json:"predicate"`
predicateType string
}
// This returns a new, empty instance of the v0.2 provenance.
func New() slsaprovenance.Provenance {
return &ProvenanceV1{}
return &ProvenanceV1{
predicateType: slsaprovenance.ProvenanceV1DraftType,
}
}
func (prov *ProvenanceV1) BuilderID() (string, error) {
@@ -64,14 +66,26 @@ func (prov *ProvenanceV1) Subjects() ([]intoto.Subject, error) {
func (prov *ProvenanceV1) GetBranch() (string, error) {
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/472): Add GetBranch() support.
return "", errors.New("unimplemented")
sysParams, ok := prov.Predicate.BuildDefinition.SystemParameters.(map[string]interface{})
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
}
return slsaprovenance.GetBranch(sysParams, prov.predicateType)
}
func (prov *ProvenanceV1) GetTag() (string, error) {
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/472): Add GetTag() support.
return "", errors.New("unimplemented")
sysParams, ok := prov.Predicate.BuildDefinition.SystemParameters.(map[string]interface{})
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
}
return slsaprovenance.GetTag(sysParams, prov.predicateType)
}
func (prov *ProvenanceV1) GetWorkflowInputs() (map[string]interface{}, error) {
return nil, errors.New("unimplemented")
sysParams, ok := prov.Predicate.BuildDefinition.SystemParameters.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
}
return slsaprovenance.GetWorkflowInputs(sysParams, prov.predicateType)
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
{
"payloadType": "application/vnd.in-toto+json",
"payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGExIjogIjQ1MDYyOTBlMmU4ZmViMWYzNGIyN2EwNDRmN2NjODYzYzgzMGVmNmIiCiAgICAgIH0sCiAgICAgICJuYW1lIjogImJpbmFyeS1saW51eC1hbWQ2NCIKICAgIH0KICBdLAogICJwcmVkaWNhdGVUeXBlIjogImh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MS4wP2RyYWZ0IiwKICAicHJlZGljYXRlIjogewogICAgImJ1aWxkRGVmaW5pdGlvbiI6IHsKICAgICAgImJ1aWxkTm90U0xTQSI6ICJodHRwczovL2dpdGh1Yi5jb20vQXR0ZXN0YXRpb25zL0dpdEh1YkFjdGlvbnNXb3JrZmxvd0B2MSIsCiAgICAgICJleHRlcm5hbFBhcmFtYXRlcnMiOiB7CiAgICAgICAgInNvdXJjZSI6IHsKICAgICAgICAgICJ1cmkiOiAiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9leGFtcGxlLXBhY2thZ2UiLAogICAgICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAgICAgInNoYTEiOiAiNGU2YzVmNmQwYjRhMTI2ZmEyMzczZDdlNTdiN2EwYWYwNTEwODc5MSIKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgIH0sCiAgICAicnVuRGV0YWlscyI6IHsKICAgICAgImJ1aWxkZXIiOiB7CiAgICAgICAgImlkIjogImh0dHBzOi8vZ2l0aHViLmNvbS9BdHRlc3RhdGlvbnMvR2l0SHViSG9zdGVkQWN0aW9uc0B2MSIKICAgICAgfSwKICAgICAgIm1ldGFkYXRhIjogewogICAgICAgICJpbnZvY2F0aW9uSWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL3Nsc2EtZnJhbWV3b3JrL2V4YW1wbGUtcGFja2FnZS9hY3Rpb25zL3J1bnMvNDEzNTQ2Mzc0MS9hdHRlbXB0cy8xIgogICAgICB9CiAgICB9CiAgfQp9Cg==",
"payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJiaW5hcnktbGludXgtYW1kNjQiCiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjEuMD9kcmFmdCIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZERlZmluaXRpb24iOiB7CiAgICAgICJidWlsZE5vdFNMU0EiOiAiaHR0cHM6Ly9naXRodWIuY29tL0F0dGVzdGF0aW9ucy9HaXRIdWJBY3Rpb25zV29ya2Zsb3dAdjEiLAogICAgICAiZXh0ZXJuYWxQYXJhbWF0ZXJzIjogewogICAgICAgICJzb3VyY2UiOiB7CiAgICAgICAgICAidXJpIjogImdpdCtodHRwczovL2dpdGh1Yi5jb20vc2xzYS1mcmFtZXdvcmsvZXhhbXBsZS1wYWNrYWdlIiwKICAgICAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgICAgICJzaGExIjogIjRlNmM1ZjZkMGI0YTEyNmZhMjM3M2Q3ZTU3YjdhMGFmMDUxMDg3OTEiCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICB9LAogICAgInJ1bkRldGFpbHMiOiB7CiAgICAgICJidWlsZGVyIjogewogICAgICAgICJpZCI6ICJodHRwczovL2dpdGh1Yi5jb20vQXR0ZXN0YXRpb25zL0dpdEh1Ykhvc3RlZEFjdGlvbnNAdjEiCiAgICAgIH0sCiAgICAgICJtZXRhZGF0YSI6IHsKICAgICAgICAiaW52b2NhdGlvbklkIjogImh0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9leGFtcGxlLXBhY2thZ2UvYWN0aW9ucy9ydW5zLzQxMzU0NjM3NDEvYXR0ZW1wdHMvMSIKICAgICAgfQogICAgfQogIH0KfQo=",
"signatures": [
{
"keyid": "",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"payloadType": "application/vnd.in-toto+json",
"payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiMGFlN2U0ZmE3MTY4NjUzODQ0MDAxMmVlMzZhMjYzNGRiYWExOWRmMmRkMTZhNDY2ZjUyNDExZmIzNDhiYmM0ZSIKICAgICAgfSwKICAgICAgIm5hbWUiOiAiYmluYXJ5LWxpbnV4LWFtZDY0IgogICAgfQogIF0sCiAgInByZWRpY2F0ZVR5cGUiOiAiaHR0cHM6Ly9zbHNhLmRldi9wcm92ZW5hbmNlL3YxLjA/ZHJhZnQiLAogICJwcmVkaWNhdGUiOiB7CiAgICAiYnVpbGREZWZpbml0aW9uIjogewogICAgICAiYnVpbGROb3RTTFNBIjogImh0dHBzOi8vZ2l0aHViLmNvbS9BdHRlc3RhdGlvbnMvR2l0SHViQWN0aW9uc1dvcmtmbG93QHYxIiwKICAgICAgImV4dGVybmFsUGFyYW1hdGVycyI6IHsKICAgICAgICAic291cmNlIjogewogICAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL3Nsc2EtZnJhbWV3b3JrL2V4YW1wbGUtcGFja2FnZSIsCiAgICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgICAic2hhMSI6ICI0ZTZjNWY2ZDBiNGExMjZmYTIzNzNkN2U1N2I3YTBhZjA1MTA4NzkxIgogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfSwKICAgICJydW5EZXRhaWxzIjogewogICAgICAiYnVpbGRlciI6IHsKICAgICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL0F0dGVzdGF0aW9ucy9HaXRIdWJIb3N0ZWRBY3Rpb25zQHYxIgogICAgICB9LAogICAgICAibWV0YWRhdGEiOiB7CiAgICAgICAgImludm9jYXRpb25JZCI6ICJodHRwczovL2dpdGh1Yi5jb20vc2xzYS1mcmFtZXdvcmsvZXhhbXBsZS1wYWNrYWdlL2FjdGlvbnMvcnVucy80MTM1NDYzNzQxL2F0dGVtcHRzLzEiCiAgICAgIH0KICAgIH0KICB9Cn0K",
"payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiMGFlN2U0ZmE3MTY4NjUzODQ0MDAxMmVlMzZhMjYzNGRiYWExOWRmMmRkMTZhNDY2ZjUyNDExZmIzNDhiYmM0ZSIKICAgICAgfSwKICAgICAgIm5hbWUiOiAiYmluYXJ5LWxpbnV4LWFtZDY0IgogICAgfQogIF0sCiAgInByZWRpY2F0ZVR5cGUiOiAiaHR0cHM6Ly9zbHNhLmRldi9wcm92ZW5hbmNlL3YxLjA/ZHJhZnQiLAogICJwcmVkaWNhdGUiOiB7CiAgICAiYnVpbGREZWZpbml0aW9uIjogewogICAgICAiYnVpbGRUeXBlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9BdHRlc3RhdGlvbnMvR2l0SHViQWN0aW9uc1dvcmtmbG93QHYxIiwKICAgICAgImV4dGVybmFsUGFyYW1hdGVycyI6IHsKICAgICAgICAic291cmNlIjogewogICAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL3Nsc2EtZnJhbWV3b3JrL2V4YW1wbGUtcGFja2FnZSIsCiAgICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgICAic2hhMSI6ICI0ZTZjNWY2ZDBiNGExMjZmYTIzNzNkN2U1N2I3YTBhZjA1MTA4NzkxIgogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfSwKICAgICJydW5EZXRhaWxzIjogewogICAgICAiYnVpbGRlciI6IHsKICAgICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL0F0dGVzdGF0aW9ucy9HaXRIdWJIb3N0ZWRBY3Rpb25zQHYxIgogICAgICB9LAogICAgICAibWV0YWRhdGEiOiB7CiAgICAgICAgImludm9jYXRpb25JZCI6ICJodHRwczovL2dpdGh1Yi5jb20vc2xzYS1mcmFtZXdvcmsvZXhhbXBsZS1wYWNrYWdlL2FjdGlvbnMvcnVucy80MTM1NDYzNzQxL2F0dGVtcHRzLzEiCiAgICAgIH0KICAgIH0KICB9Cn0K",
"signatures": [
{
"keyid": "",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-21
View File
@@ -1,21 +0,0 @@
package utils
import (
"fmt"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
)
func GetAsString(environment map[string]any, field string) (string, error) {
value, ok := environment[field]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload,
fmt.Sprintf("environment type for %s", field))
}
i, ok := value.(string)
if !ok {
return "", fmt.Errorf("%w: %s '%s'", serrors.ErrorInvalidDssePayload, "environment type string", field)
}
return i, nil
}