From 6510a842fd8e2319b47f35959f33e0e03180920d Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Sun, 19 Jan 2025 16:09:59 +0000 Subject: [PATCH 1/6] Modernize attest statements attest no longer uses the cosign library to generate the Statement used in the attestation. This lets us: 1. Use `gitCommit` as the digest type (which is what we were doing anyways) 2. Use the correct `_type` value for modern in-toto statements In the future it will let us add annotations on the subject. Note that this also changes the user behavior in that it: 1. Changes the things mentioned above. 2. Doesn't let users use the convenience attestation types from the command line. They must instead specify the full predicate type. refs #611 Signed-off-by: Tom Hennen --- docs/cli/gitsign_attest.md | 2 +- go.mod | 1 + internal/attest/attest.go | 39 +++++++++++++------ internal/attest/attest_test.go | 14 +++---- internal/attest/testdata/test.json.provenance | 2 +- internal/commands/attest/attest.go | 2 +- 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/docs/cli/gitsign_attest.md b/docs/cli/gitsign_attest.md index aa3fb0fc..6e4d0648 100644 --- a/docs/cli/gitsign_attest.md +++ b/docs/cli/gitsign_attest.md @@ -12,7 +12,7 @@ gitsign attest [flags] -f, --filepath string attestation filepath -h, --help help for attest --objtype string [commit | tree] - Git object type to attest (default "commit") - --type string specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom") + --type string specify a predicate type URI ``` ### SEE ALSO diff --git a/go.mod b/go.mod index c66cb786..d2d8c0f5 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-openapi/strfmt v0.23.0 github.com/go-openapi/swag v0.23.0 github.com/google/go-cmp v0.6.0 + github.com/in-toto/attestation v1.1.0 github.com/in-toto/in-toto-golang v0.9.0 github.com/jonboulle/clockwork v0.5.0 github.com/mattn/go-tty v0.0.7 diff --git a/internal/attest/attest.go b/internal/attest/attest.go index cd74b53b..8f75baa3 100644 --- a/internal/attest/attest.go +++ b/internal/attest/attest.go @@ -32,9 +32,9 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage" "github.com/go-openapi/strfmt" + spb "github.com/in-toto/attestation/go/v1" "github.com/jonboulle/clockwork" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" - "github.com/sigstore/cosign/v2/pkg/cosign/attestation" "github.com/sigstore/cosign/v2/pkg/types" utils "github.com/sigstore/gitsign/internal" gitsignconfig "github.com/sigstore/gitsign/internal/config" @@ -42,6 +42,8 @@ import ( "github.com/sigstore/rekor/pkg/generated/models" dssesig "github.com/sigstore/sigstore/pkg/signature/dsse" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" ) const ( @@ -111,7 +113,7 @@ func NewNamedReader(r io.Reader, name string) Reader { // refName: What ref to write to (e.g. refs/attestations/commits) // sha: Commit SHA you are attesting to. // input: Attestation file input. -// attType: Attestation type. See [attestation.GenerateStatement] for allowed values. +// attType: Attestation (predicate) type URI corresponding to the input (predicate). func (a *Attestor) WriteAttestation(ctx context.Context, refName string, sha plumbing.Hash, input Reader, attType string) (plumbing.Hash, error) { b, err := io.ReadAll(input) if err != nil { @@ -226,18 +228,32 @@ func encode(store storage.Storer, enc Encoder) (plumbing.Hash, error) { return store.SetEncodedObject(obj) } +func generateStatement(pred []byte, attType string, sha plumbing.Hash) (*spb.Statement, error) { + sub := []*spb.ResourceDescriptor{{ + Digest: map[string]string{"gitCommit": sha.String()}, + }} + + var predPb structpb.Struct + err := json.Unmarshal(pred, &predPb) + if err != nil { + return nil, err + } + + return &spb.Statement{ + Type: spb.StatementTypeUri, + Subject: sub, + PredicateType: attType, + Predicate: &predPb, + }, nil +} + func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte, attType string) ([]byte, error) { - // Generate attestation - sh, err := attestation.GenerateStatement(attestation.GenerateOpts{ - Predicate: bytes.NewBuffer(b), - Type: attType, - Digest: sha.String(), - Time: clock.Now, - }) + sh, err := generateStatement(b, attType, sha) + if err != nil { return nil, err } - payload, err := json.Marshal(sh) + payload, err := protojson.Marshal(sh) if err != nil { return nil, err } @@ -248,7 +264,8 @@ func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte, } rekorHost, rekorBasePath := utils.StripURL(a.config.Rekor) - tc := &rekorclient.TransportConfig{ + +tc := &rekorclient.TransportConfig{ Host: rekorHost, BasePath: rekorBasePath, Schemes: []string{"https"}, diff --git a/internal/attest/attest_test.go b/internal/attest/attest_test.go index 96e3d0d1..1133d339 100644 --- a/internal/attest/attest_test.go +++ b/internal/attest/attest_test.go @@ -90,7 +90,7 @@ func TestAttestCommitRef(t *testing.T) { }, } t.Run("base", func(t *testing.T) { - attest1, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest1, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } @@ -99,7 +99,7 @@ func TestAttestCommitRef(t *testing.T) { t.Run("noop", func(t *testing.T) { // Write same attestation to the same commit - should be a no-op. - attest2, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest2, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } @@ -117,7 +117,7 @@ func TestAttestCommitRef(t *testing.T) { t.Fatal(err) } - attest3, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest3, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } @@ -170,7 +170,7 @@ func TestAttestTreeRef(t *testing.T) { }, } t.Run("base", func(t *testing.T) { - attest1, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest1, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } @@ -179,7 +179,7 @@ func TestAttestTreeRef(t *testing.T) { t.Run("noop", func(t *testing.T) { // Write same attestation to the same commit - should be a no-op. - attest2, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest2, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } @@ -197,7 +197,7 @@ func TestAttestTreeRef(t *testing.T) { } sha = resolveTree(t, repo, sha) - attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } @@ -208,7 +208,7 @@ func TestAttestTreeRef(t *testing.T) { // Make a new commit, write new attestation. sha = resolveTree(t, repo, writeRepo(t, w, fs, "testdata/bar.txt")) - attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom") + attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } diff --git a/internal/attest/testdata/test.json.provenance b/internal/attest/testdata/test.json.provenance index bf5ca61b..d0f698cf 100644 --- a/internal/attest/testdata/test.json.provenance +++ b/internal/attest/testdata/test.json.provenance @@ -1 +1 @@ -{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://cosign.sigstore.dev/attestation/v1","subject":[{"name":"","digest":{"sha256":"{{.}}"}}],"predicate":{"Data":"{\"foo\":\"bar\"}","Timestamp":"1984-04-04T00:00:00Z"}} +{"_type":"https://in-toto.io/Statement/v1","subject":[{"digest":{"gitCommit":"{{.}}"}}],"predicateType":"custom-pred-type","predicate":{"foo":"bar"}} diff --git a/internal/commands/attest/attest.go b/internal/commands/attest/attest.go index 76a7fcbe..13791141 100644 --- a/internal/commands/attest/attest.go +++ b/internal/commands/attest/attest.go @@ -46,7 +46,7 @@ type options struct { func (o *options) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.FlagObjectType, "objtype", FlagObjectTypeCommit, "[commit | tree] - Git object type to attest") cmd.Flags().StringVarP(&o.FlagPath, "filepath", "f", "", "attestation filepath") - cmd.Flags().StringVar(&o.FlagAttestationType, "type", "", `specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom")`) + cmd.Flags().StringVar(&o.FlagAttestationType, "type", "", `specify a predicate type URI`) } func (o *options) Run(ctx context.Context) error { From 3253c739e96717ca63580f737040e953190974cc Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Sun, 19 Jan 2025 18:11:41 +0000 Subject: [PATCH 2/6] gofmt Signed-off-by: Tom Hennen --- internal/attest/attest.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/attest/attest.go b/internal/attest/attest.go index 8f75baa3..b314107a 100644 --- a/internal/attest/attest.go +++ b/internal/attest/attest.go @@ -236,7 +236,7 @@ func generateStatement(pred []byte, attType string, sha plumbing.Hash) (*spb.Sta var predPb structpb.Struct err := json.Unmarshal(pred, &predPb) if err != nil { - return nil, err + return nil, err } return &spb.Statement{ @@ -265,7 +265,7 @@ func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte, rekorHost, rekorBasePath := utils.StripURL(a.config.Rekor) -tc := &rekorclient.TransportConfig{ + tc := &rekorclient.TransportConfig{ Host: rekorHost, BasePath: rekorBasePath, Schemes: []string{"https"}, From 57c77c163457a2bef896e78d3f9b4cace9662567 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Mon, 20 Jan 2025 18:56:56 +0000 Subject: [PATCH 3/6] Restructure test to allow deep comparison of the JSON The prior setup needed the contents to be exactly equal. Unfortunately protojson explicitly doesn't maintain byte-for-byte equality. To address that the test now deeply examines the 'attestation' (.sig) file produced parses the payload JSON into a struct and then compares that. This provides somewhat easier to understand error diffs when things go wrong, but most importantly it appears stable even if field order or spacing change. Signed-off-by: Tom Hennen --- internal/attest/attest_test.go | 171 +++++++++++++++++++++------------ 1 file changed, 107 insertions(+), 64 deletions(-) diff --git a/internal/attest/attest_test.go b/internal/attest/attest_test.go index 1133d339..6e153a7b 100644 --- a/internal/attest/attest_test.go +++ b/internal/attest/attest_test.go @@ -19,6 +19,7 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "io" "os" "path/filepath" @@ -79,22 +80,22 @@ func TestAttestCommitRef(t *testing.T) { attestor := NewAttestor(repo, sv, fakeRekor, cfg) - fc := []fileContent{ + ad := []gitAttestData{ { - Name: filepath.Join(sha.String(), "test.json"), - Content: readFile(t, "testdata/test.json"), - }, - { - Name: filepath.Join(sha.String(), "test.json.sig"), - Content: generateAttestation(t, sha), + sha: sha, + predName: "test.json", + predicate: readFile(t, "testdata/test.json"), + attName: "test.json.sig", + attestation: generateAttestation(t, sha), }, } + t.Run("base", func(t *testing.T) { attest1, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type") if err != nil { t.Fatalf("WriteAttestation: %v", err) } - verifyContent(t, repo, attest1, fc) + verifyContent(t, repo, attest1, ad) }) t.Run("noop", func(t *testing.T) { @@ -103,7 +104,7 @@ func TestAttestCommitRef(t *testing.T) { if err != nil { t.Fatalf("WriteAttestation: %v", err) } - verifyContent(t, repo, attest2, fc) + verifyContent(t, repo, attest2, ad) }) t.Run("new commit", func(t *testing.T) { @@ -121,17 +122,16 @@ func TestAttestCommitRef(t *testing.T) { if err != nil { t.Fatalf("WriteAttestation: %v", err) } - fc = append(fc, - fileContent{ - Name: filepath.Join(sha.String(), "test.json"), - Content: content, - }, - fileContent{ - Name: filepath.Join(sha.String(), "test.json.sig"), - Content: generateAttestation(t, sha), + ad = append(ad, + gitAttestData{ + sha: sha, + predName: "test.json", + predicate: readFile(t, "testdata/test.json"), + attName: "test.json.sig", + attestation: generateAttestation(t, sha), }, ) - verifyContent(t, repo, attest3, fc) + verifyContent(t, repo, attest3, ad) }) } @@ -159,14 +159,13 @@ func TestAttestTreeRef(t *testing.T) { attestor := NewAttestor(repo, sv, fakeRekor, cfg) - fc := []fileContent{ - { - Name: filepath.Join(sha.String(), "test.json"), - Content: readFile(t, "testdata/test.json"), - }, + ad := []gitAttestData{ { - Name: filepath.Join(sha.String(), "test.json.sig"), - Content: generateAttestation(t, sha), + sha: sha, + predName: "test.json", + predicate: readFile(t, "testdata/test.json"), + attName: "test.json.sig", + attestation: generateAttestation(t, sha), }, } t.Run("base", func(t *testing.T) { @@ -174,7 +173,7 @@ func TestAttestTreeRef(t *testing.T) { if err != nil { t.Fatalf("WriteAttestation: %v", err) } - verifyContent(t, repo, attest1, fc) + verifyContent(t, repo, attest1, ad) }) t.Run("noop", func(t *testing.T) { @@ -183,7 +182,7 @@ func TestAttestTreeRef(t *testing.T) { if err != nil { t.Fatalf("WriteAttestation: %v", err) } - verifyContent(t, repo, attest2, fc) + verifyContent(t, repo, attest2, ad) }) t.Run("new commit same tree", func(t *testing.T) { @@ -201,7 +200,7 @@ func TestAttestTreeRef(t *testing.T) { if err != nil { t.Fatalf("WriteAttestation: %v", err) } - verifyContent(t, repo, attest3, fc) + verifyContent(t, repo, attest3, ad) }) t.Run("new commit new tree", func(t *testing.T) { @@ -213,26 +212,28 @@ func TestAttestTreeRef(t *testing.T) { t.Fatalf("WriteAttestation: %v", err) } - fc = append(fc, - fileContent{ - Name: filepath.Join(sha.String(), "test.json"), - Content: content, - }, - fileContent{ - Name: filepath.Join(sha.String(), "test.json.sig"), - Content: generateAttestation(t, sha), + ad = append(ad, + gitAttestData{ + sha: sha, + predName: "test.json", + predicate: readFile(t, "testdata/test.json"), + attName: "test.json.sig", + attestation: generateAttestation(t, sha), }, ) - verifyContent(t, repo, attest3, fc) + verifyContent(t, repo, attest3, ad) }) } -type fileContent struct { - Name string - Content string +type gitAttestData struct { + sha plumbing.Hash + predName string + predicate string + attName string + attestation string } -func verifyContent(t *testing.T, repo *git.Repository, sha plumbing.Hash, want []fileContent) { +func verifyContent(t *testing.T, repo *git.Repository, sha plumbing.Hash, want []gitAttestData) { t.Helper() commit, err := repo.CommitObject(sha) @@ -240,32 +241,40 @@ func verifyContent(t *testing.T, repo *git.Repository, sha plumbing.Hash, want [ t.Fatal(err) } - files, err := commit.Files() - if err != nil { - t.Fatal(err) - } - - got := []fileContent{} - if err := files.ForEach(func(c *object.File) error { - content, err := c.Contents() + for _, w := range want { + // We'll just check the raw predicate file that was written, + // that doesn't get marshaled so it should be untouched. + fname := fmt.Sprintf("%v/%v", w.sha, w.predName) + gotPredFile, err := commit.File(fname) if err != nil { - return err + t.Fatal(err) + } + gotPred, err := gotPredFile.Contents() + if err != nil { + t.Fatal(err) + } + diff := cmp.Diff(w.predicate, gotPred) + if diff != "" { + t.Errorf("fname %v does not match: %v", fname, diff) } - got = append(got, fileContent{ - Name: c.Name, - Content: content, - }) - - return nil - }); err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(want, got, cmpopts.SortSlices(func(i, j fileContent) bool { - return i.Name < j.Name - })); diff != "" { - t.Error(diff) + // The attestation does get marshalled though, so we can't do + // a simple diff, instead we'll need to parse things... + fname = fmt.Sprintf("%v/%v", w.sha, w.attName) + gotE := readDsse(t, commit, fname) + wantE := parseDsse(t, w.attestation) + // Ignore payload because we're going to handle that special + diff = cmp.Diff(gotE, wantE, cmpopts.IgnoreFields(dsse.Envelope{}, "Payload")) + if diff != "" { + t.Errorf("fname %v does not match: %v", fname, diff) + } + // Now let's check the payload. + gotJ := parsePayload(t, gotE) + wantJ := parsePayload(t, wantE) + diff = cmp.Diff(gotJ, wantJ) + if diff != "" { + t.Errorf("fname payload %v does not match: %v", fname, diff) + } } } @@ -286,6 +295,40 @@ func fakeRekor(_ context.Context, _ *client.Rekor, _, _ []byte) (*models.LogEntr }, nil } +func parsePayload(t *testing.T, d *dsse.Envelope) interface{} { + p, err := base64.StdEncoding.DecodeString(d.Payload) + if err != nil { + t.Fatal(err) + } + var j interface{} + err = json.Unmarshal([]byte(p), &j) + if err != nil { + t.Fatal(err) + } + return j +} + +func parseDsse(t *testing.T, content string) *dsse.Envelope { + var e dsse.Envelope + err := json.Unmarshal([]byte(content), &e) + if err != nil { + t.Fatal(err) + } + return &e +} + +func readDsse(t *testing.T, commit *object.Commit, fname string) *dsse.Envelope { + f, err := commit.File(fname) + if err != nil { + t.Fatal(err) + } + c, err := f.Contents() + if err != nil { + t.Fatal(err) + } + return parseDsse(t, c) +} + func readFile(t *testing.T, path string) string { t.Helper() From bd93a6a1dafab8de1f8991785e205f3b86156f3a Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Wed, 22 Jan 2025 16:02:46 +0000 Subject: [PATCH 4/6] remove byte conversion to make linter happy Signed-off-by: Tom Hennen --- internal/attest/attest_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/attest/attest_test.go b/internal/attest/attest_test.go index 6e153a7b..c34f76db 100644 --- a/internal/attest/attest_test.go +++ b/internal/attest/attest_test.go @@ -301,7 +301,7 @@ func parsePayload(t *testing.T, d *dsse.Envelope) interface{} { t.Fatal(err) } var j interface{} - err = json.Unmarshal([]byte(p), &j) + err = json.Unmarshal(p, &j) if err != nil { t.Fatal(err) } From a6c986a5b65423f3e24ad7a72f998b576025421a Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Wed, 22 Jan 2025 17:12:37 +0000 Subject: [PATCH 5/6] use gitCommit/gitTree as appropriate Signed-off-by: Tom Hennen --- internal/attest/attest.go | 32 +++++++++++++++++------------- internal/attest/attest_test.go | 24 +++++++++++----------- internal/commands/attest/attest.go | 4 +++- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/internal/attest/attest.go b/internal/attest/attest.go index b314107a..26d73744 100644 --- a/internal/attest/attest.go +++ b/internal/attest/attest.go @@ -47,8 +47,10 @@ import ( ) const ( - CommitRef = "refs/attestations/commits" - TreeRef = "refs/attestations/trees" + CommitRef = "refs/attestations/commits" + TreeRef = "refs/attestations/trees" + DigestTypeCommit = "gitCommit" + DigestTypeTree = "gitTree" ) var ( @@ -59,18 +61,20 @@ var ( type rekorUpload func(ctx context.Context, rekorClient *rekorclient.Rekor, signature []byte, pemBytes []byte) (*models.LogEntryAnon, error) type Attestor struct { - repo *git.Repository - sv *sign.SignerVerifier - rekorFn rekorUpload - config *gitsignconfig.Config + repo *git.Repository + sv *sign.SignerVerifier + rekorFn rekorUpload + config *gitsignconfig.Config + digestType string } -func NewAttestor(repo *git.Repository, sv *sign.SignerVerifier, rekorFn rekorUpload, config *gitsignconfig.Config) *Attestor { +func NewAttestor(repo *git.Repository, sv *sign.SignerVerifier, rekorFn rekorUpload, config *gitsignconfig.Config, digestType string) *Attestor { return &Attestor{ - repo: repo, - sv: sv, - rekorFn: rekorFn, - config: config, + repo: repo, + sv: sv, + rekorFn: rekorFn, + config: config, + digestType: digestType, } } @@ -228,9 +232,9 @@ func encode(store storage.Storer, enc Encoder) (plumbing.Hash, error) { return store.SetEncodedObject(obj) } -func generateStatement(pred []byte, attType string, sha plumbing.Hash) (*spb.Statement, error) { +func generateStatement(pred []byte, attType string, digestType string, sha plumbing.Hash) (*spb.Statement, error) { sub := []*spb.ResourceDescriptor{{ - Digest: map[string]string{"gitCommit": sha.String()}, + Digest: map[string]string{digestType: sha.String()}, }} var predPb structpb.Struct @@ -248,7 +252,7 @@ func generateStatement(pred []byte, attType string, sha plumbing.Hash) (*spb.Sta } func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte, attType string) ([]byte, error) { - sh, err := generateStatement(b, attType, sha) + sh, err := generateStatement(b, attType, a.digestType, sha) if err != nil { return nil, err diff --git a/internal/attest/attest_test.go b/internal/attest/attest_test.go index c34f76db..3a936570 100644 --- a/internal/attest/attest_test.go +++ b/internal/attest/attest_test.go @@ -78,7 +78,7 @@ func TestAttestCommitRef(t *testing.T) { t.Fatal(err) } - attestor := NewAttestor(repo, sv, fakeRekor, cfg) + attestor := NewAttestor(repo, sv, fakeRekor, cfg, DigestTypeCommit) ad := []gitAttestData{ { @@ -86,7 +86,7 @@ func TestAttestCommitRef(t *testing.T) { predName: "test.json", predicate: readFile(t, "testdata/test.json"), attName: "test.json.sig", - attestation: generateAttestation(t, sha), + attestation: generateAttestation(t, "gitCommit", sha), }, } @@ -128,7 +128,7 @@ func TestAttestCommitRef(t *testing.T) { predName: "test.json", predicate: readFile(t, "testdata/test.json"), attName: "test.json.sig", - attestation: generateAttestation(t, sha), + attestation: generateAttestation(t, "gitCommit", sha), }, ) verifyContent(t, repo, attest3, ad) @@ -157,7 +157,7 @@ func TestAttestTreeRef(t *testing.T) { cfg, _ := gitsignconfig.Get() - attestor := NewAttestor(repo, sv, fakeRekor, cfg) + attestor := NewAttestor(repo, sv, fakeRekor, cfg, DigestTypeTree) ad := []gitAttestData{ { @@ -165,7 +165,7 @@ func TestAttestTreeRef(t *testing.T) { predName: "test.json", predicate: readFile(t, "testdata/test.json"), attName: "test.json.sig", - attestation: generateAttestation(t, sha), + attestation: generateAttestation(t, "gitTree", sha), }, } t.Run("base", func(t *testing.T) { @@ -218,7 +218,7 @@ func TestAttestTreeRef(t *testing.T) { predName: "test.json", predicate: readFile(t, "testdata/test.json"), attName: "test.json.sig", - attestation: generateAttestation(t, sha), + attestation: generateAttestation(t, "gitTree", sha), }, ) verifyContent(t, repo, attest3, ad) @@ -360,17 +360,17 @@ func writeRepo(t *testing.T, w *git.Worktree, fs billy.Filesystem, path string) return sha } -func generateAttestation(t *testing.T, h plumbing.Hash) string { +func generateAttestation(t *testing.T, digestType string, h plumbing.Hash) string { t.Helper() - b := new(bytes.Buffer) - if err := tmpl.Execute(b, h); err != nil { - t.Fatal(err) - } + statement := fmt.Sprintf( + `{"_type":"https://in-toto.io/Statement/v1","subject":[{"digest":{"%s":"%s"}}],"predicateType":"custom-pred-type","predicate":{"foo":"bar"}}`, + digestType, + h.String()) att := dsse.Envelope{ PayloadType: "application/vnd.in-toto+json", - Payload: base64.StdEncoding.EncodeToString(bytes.TrimSpace(b.Bytes())), + Payload: base64.StdEncoding.EncodeToString([]byte(statement)), Signatures: []dsse.Signature{{Sig: "dGFjb2NhdA=="}}, } diff --git a/internal/commands/attest/attest.go b/internal/commands/attest/attest.go index 13791141..78735fe2 100644 --- a/internal/commands/attest/attest.go +++ b/internal/commands/attest/attest.go @@ -63,6 +63,7 @@ func (o *options) Run(ctx context.Context) error { // If we're attaching the attestation to a tree, resolve the tree SHA. sha := head.Hash() refName := attCommitRef + digestType := attest.DigestTypeCommit if o.FlagObjectType == FlagObjectTypeTree { commit, err := repo.CommitObject(head.Hash()) if err != nil { @@ -71,6 +72,7 @@ func (o *options) Run(ctx context.Context) error { sha = commit.TreeHash refName = attTreeRef + digestType = attest.DigestTypeTree } sv, err := sign.SignerFromKeyOpts(ctx, "", "", cosignopts.KeyOpts{ @@ -84,7 +86,7 @@ func (o *options) Run(ctx context.Context) error { } defer sv.Close() - attestor := attest.NewAttestor(repo, sv, cosign.TLogUploadInTotoAttestation, o.Config) + attestor := attest.NewAttestor(repo, sv, cosign.TLogUploadInTotoAttestation, o.Config, digestType) out, err := attestor.WriteFile(ctx, refName, sha, o.FlagPath, o.FlagAttestationType) if err != nil { From b4d4af6994092a84f902952cf7dfeba0b1c3763f Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Wed, 22 Jan 2025 17:58:52 +0000 Subject: [PATCH 6/6] remove old provenance template Signed-off-by: Tom Hennen --- internal/attest/attest_test.go | 5 ----- internal/attest/testdata/test.json.provenance | 1 - 2 files changed, 6 deletions(-) delete mode 100644 internal/attest/testdata/test.json.provenance diff --git a/internal/attest/attest_test.go b/internal/attest/attest_test.go index 3a936570..4f8d0b39 100644 --- a/internal/attest/attest_test.go +++ b/internal/attest/attest_test.go @@ -24,7 +24,6 @@ import ( "os" "path/filepath" "testing" - "text/template" "time" "github.com/go-git/go-billy/v5" @@ -44,10 +43,6 @@ import ( "github.com/sigstore/sigstore/pkg/signature" ) -var ( - tmpl = template.Must(template.ParseFiles("testdata/test.json.provenance")) -) - func TestMain(m *testing.M) { clock = clockwork.NewFakeClockAt(time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC)) os.Exit(m.Run()) diff --git a/internal/attest/testdata/test.json.provenance b/internal/attest/testdata/test.json.provenance deleted file mode 100644 index d0f698cf..00000000 --- a/internal/attest/testdata/test.json.provenance +++ /dev/null @@ -1 +0,0 @@ -{"_type":"https://in-toto.io/Statement/v1","subject":[{"digest":{"gitCommit":"{{.}}"}}],"predicateType":"custom-pred-type","predicate":{"foo":"bar"}}