diff --git a/signing/signing.go b/signing/signing.go index cf7c706f..c59998d7 100644 --- a/signing/signing.go +++ b/signing/signing.go @@ -33,6 +33,7 @@ import ( sigOpts "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/ossf/scorecard-action/entrypoint" "github.com/ossf/scorecard-action/options" @@ -52,7 +53,8 @@ var ( // Signing is a signing structure. type Signing struct { - token string + token string + rekorTlogIndex int64 } // New creates a new Signing instance. @@ -78,6 +80,14 @@ func New(token string) (*Signing, error) { // SignScorecardResult signs the results file and uploads the attestation to the Rekor transparency log. func (s *Signing) SignScorecardResult(scorecardResultsFile string) error { + f, err := os.CreateTemp("", "bundle") + if err != nil { + return fmt.Errorf("creating temporary bundle file: %w", err) + } + bundlePath := f.Name() + f.Close() + defer os.Remove(bundlePath) // clean up + // Prepare settings for SignBlobCmd. rootOpts := &sigOpts.RootOptions{Timeout: sigOpts.DefaultTimeout} // Just the timeout. keyOpts := sigOpts.KeyOpts{ @@ -86,9 +96,9 @@ func (s *Signing) SignScorecardResult(scorecardResultsFile string) error { OIDCIssuer: sigOpts.DefaultOIDCIssuerURL, // OIDC provider to get ID token to auth for Fulcio. OIDCClientID: "sigstore", SkipConfirmation: true, // skip cosign's privacy confirmation prompt as we run non-interactively + BundlePath: bundlePath, } - var err error for _, backoff := range backoffSchedule { // This command will use the provided OIDCIssuer to authenticate into Fulcio, which will generate the // signing certificate on the scorecard result. This attestation is then uploaded to the Rekor transparency log. @@ -107,6 +117,12 @@ func (s *Signing) SignScorecardResult(scorecardResultsFile string) error { return fmt.Errorf("error signing payload: %w", err) } + rekorTlogIndex, err := extractTlogIndex(bundlePath) + if err != nil { + return err + } + s.rekorTlogIndex = rekorTlogIndex + return nil } @@ -143,10 +159,12 @@ func (s *Signing) ProcessSignature(jsonPayload []byte, repoName, repoRef string) Result string `json:"result"` Branch string `json:"branch"` AccessToken string `json:"accessToken"` + TlogIndex int64 `json:"tlogIndex"` }{ Result: string(jsonPayload), Branch: repoRef, AccessToken: s.token, + TlogIndex: s.rekorTlogIndex, } payloadBytes, err := json.Marshal(resultsPayload) @@ -209,3 +227,16 @@ func postResults(endpoint *url.URL, payload []byte) error { return nil } + +func extractTlogIndex(bundlePath string) (int64, error) { + contents, err := os.ReadFile(bundlePath) + if err != nil { + return 0, fmt.Errorf("reading cosign bundle file: %w", err) + } + var payload cosign.LocalSignedPayload + err = json.Unmarshal(contents, &payload) + if err != nil || payload.Bundle == nil { + return 0, fmt.Errorf("invalid cosign bundle file: %w", err) + } + return payload.Bundle.Payload.LogIndex, nil +} diff --git a/signing/signing_test.go b/signing/signing_test.go index edfae6b0..22d53858 100644 --- a/signing/signing_test.go +++ b/signing/signing_test.go @@ -128,6 +128,45 @@ func TestProcessSignature(t *testing.T) { } } +func Test_extractTlogIndex(t *testing.T) { + t.Parallel() + tests := []struct { + name string + bundlePath string + want int64 + wantErr bool + }{ + { + name: "valid bundle", + bundlePath: "testdata/cosign.bundle", + want: 23548006, + }, + { + name: "invalid bundle", + bundlePath: "testdata/invalid-cosign.bundle", + wantErr: true, + }, + { + name: "missing bundle", + bundlePath: "testdata/does-not-exist.bundle", + wantErr: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := extractTlogIndex(tt.bundlePath) + if (err != nil) != tt.wantErr { + t.Fatalf("unexpected err: %v, wantErr: %t", err, tt.wantErr) + } + if got != tt.want { + t.Errorf("wrong tlog index: got %d, wanted %d", got, tt.want) + } + }) + } +} + //nolint:paralleltest // we are using t.Setenv func TestProcessSignature_retries(t *testing.T) { tests := []struct { diff --git a/signing/testdata/cosign.bundle b/signing/testdata/cosign.bundle new file mode 100644 index 00000000..1822fbe4 --- /dev/null +++ b/signing/testdata/cosign.bundle @@ -0,0 +1 @@ +{"base64Signature":"MEUCIQD4rAvIUYt0WgrXVL6whlkgJerX2jyRNDct3AwWSsGdJgIgP0DCR56AM9FQD/aEnZwnNT1s22V8PW/ud0YHREr2n0U=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMwakNDQWxlZ0F3SUJBZ0lVYzRvZkdaWUlxSEMvaW0xY0JPQ0FXbk9BQUQ4d0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpNd05qRXlNakV5TlRNMldoY05Nak13TmpFeU1qRXpOVE0yV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVzUGd2b2k0b3ZyN3dLRWxtRkR0R2VqZHBwTXFEMkZtc2tHQ2EKUFNLNlVSRUlhQ3h2Q1NrRlNwQnE1bmNhd0lscWtnMVBnKy9sMHhhcDExd0hrOWplNEtPQ0FYWXdnZ0Z5TUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVBNkI1CmE4TXdjS0M4UFdMNjc4UGF3N0FTY3Rjd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0lRWURWUjBSQVFIL0JCY3dGWUVUYzNOamFISnZZMnRBWjI5dloyeGxMbU52YlRBc0Jnb3JCZ0VFQVlPLwpNQUVCQkI1b2RIUndjem92TDJkcGRHaDFZaTVqYjIwdmJHOW5hVzR2YjJGMWRHZ3dMZ1lLS3dZQkJBR0R2ekFCCkNBUWdEQjVvZEhSd2N6b3ZMMmRwZEdoMVlpNWpiMjB2Ykc5bmFXNHZiMkYxZEdnd2dZa0dDaXNHQVFRQjFua0MKQkFJRWV3UjVBSGNBZFFEZFBUQnF4c2NSTW1NWkhoeVpaemNDb2twZXVONDhyZitIaW5LQUx5bnVqZ0FBQVlpeApnQUl3QUFBRUF3QkdNRVFDSUJTN3lEOERVUTN2b3RnRXFCZzhXekFqbmFkNjJGRVgvZXZCUkNnZTdmeWpBaUJECkZseG8vOC9OVWNvN3VRSkNSazgrbDUvQXN5dG1jNU5vL3l0OW9tYkg5ekFLQmdncWhrak9QUVFEQXdOcEFEQm0KQWpFQWhjT1hBSmNyWGovWnlTYi9DYUY4aldSckE4bkthRWllRkREVFI0dFhKakk2bVE0ZEM2R05mWU8xdnRpMQpDUU1pQWpFQW9PWU44dnRDOEt5b3BWR3F1MkUxaHJuWFlCUXJUK1I4eFN5OEZRMHNTQVhGYzU0aGM0S2ZPemthClJIVXZlUUZSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K","rekorBundle":{"SignedEntryTimestamp":"MEQCIG+3Fj1/9aYBP2fp9gbI9RpOc1/9aawUip5+aflRrtn/AiAo2nuSf3IIFwHpAgQL8eLkA3++gRiUkXDJzRZqjCXRhA==","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiOWJiNzc2MGEyYjNiOWQwZjA4ZTI5OGVjYjg3Y2ZmMmQxMzkxMjJlM2Y2ZDI1ODI5MGZiODA3YmYxYWZlNjkyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUQ0ckF2SVVZdDBXZ3JYVkw2d2hsa2dKZXJYMmp5Uk5EY3QzQXdXU3NHZEpnSWdQMERDUjU2QU05RlFEL2FFblp3bk5UMXMyMlY4UFcvdWQwWUhSRXIybjBVPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTXdha05EUVd4bFowRjNTVUpCWjBsVll6UnZaa2RhV1VseFNFTXZhVzB4WTBKUFEwRlhiazlCUVVRNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcE5kMDVxUlhsTmFrVjVUbFJOTWxkb1kwNU5hazEzVG1wRmVVMXFSWHBPVkUweVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ6VUdkMmIyazBiM1p5TjNkTFJXeHRSa1IwUjJWcVpIQndUWEZFTWtadGMydEhRMkVLVUZOTE5sVlNSVWxoUTNoMlExTnJSbE53UW5FMWJtTmhkMGxzY1d0bk1WQm5LeTlzTUhoaGNERXhkMGhyT1dwbE5FdFBRMEZZV1hkblowWjVUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZCTmtJMUNtRTRUWGRqUzBNNFVGZE1OamM0VUdGM04wRlRZM1JqZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsUldVUldVakJTUVZGSUwwSkNZM2RHV1VWVVl6Tk9hbUZJU25aWk1uUkJXakk1ZGxveWVHeE1iVTUyWWxSQmMwSm5iM0pDWjBWRlFWbFBMd3BOUVVWQ1FrSTFiMlJJVW5kamVtOTJUREprY0dSSGFERlphVFZxWWpJd2RtSkhPVzVoVnpSMllqSkdNV1JIWjNkTVoxbExTM2RaUWtKQlIwUjJla0ZDQ2tOQlVXZEVRalZ2WkVoU2QyTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2M1Ym1GWE5IWmlNa1l4WkVkbmQyZFphMGREYVhOSFFWRlJRakZ1YTBNS1FrRkpSV1YzVWpWQlNHTkJaRkZFWkZCVVFuRjRjMk5TVFcxTldraG9lVnBhZW1ORGIydHdaWFZPTkRoeVppdElhVzVMUVV4NWJuVnFaMEZCUVZscGVBcG5RVWwzUVVGQlJVRjNRa2ROUlZGRFNVSlROM2xFT0VSVlVUTjJiM1JuUlhGQ1p6aFhla0ZxYm1Ga05qSkdSVmd2WlhaQ1VrTm5aVGRtZVdwQmFVSkVDa1pzZUc4dk9DOU9WV052TjNWUlNrTlNhemdyYkRVdlFYTjVkRzFqTlU1dkwzbDBPVzl0WWtnNWVrRkxRbWRuY1docmFrOVFVVkZFUVhkT2NFRkVRbTBLUVdwRlFXaGpUMWhCU21OeVdHb3ZXbmxUWWk5RFlVWTRhbGRTY2tFNGJrdGhSV2xsUmtSRVZGSTBkRmhLYWtrMmJWRTBaRU0yUjA1bVdVOHhkblJwTVFwRFVVMXBRV3BGUVc5UFdVNDRkblJET0V0NWIzQldSM0YxTWtVeGFISnVXRmxDVVhKVUsxSTRlRk41T0VaUk1ITlRRVmhHWXpVMGFHTTBTMlpQZW10aENsSklWWFpsVVVaU0NpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19","integratedTime":1686605136,"logIndex":23548006,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}} \ No newline at end of file diff --git a/signing/testdata/invalid-cosign.bundle b/signing/testdata/invalid-cosign.bundle new file mode 100644 index 00000000..db71b2bc --- /dev/null +++ b/signing/testdata/invalid-cosign.bundle @@ -0,0 +1 @@ +{"base64Signature":"MEUCIQD4rAvIUYt0WgrXVL6whlkgJerX2jyRNDct3AwWSsGdJgIgP0DCR56AM9FQD/aEnZwnNT1s22V8PW/ud0YHREr2n0U=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMwakNDQWxlZ0F3SUJBZ0lVYzRvZkdaWUlxSEMvaW0xY0JPQ0FXbk9BQUQ4d0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpNd05qRXlNakV5TlRNMldoY05Nak13TmpFeU1qRXpOVE0yV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVzUGd2b2k0b3ZyN3dLRWxtRkR0R2VqZHBwTXFEMkZtc2tHQ2EKUFNLNlVSRUlhQ3h2Q1NrRlNwQnE1bmNhd0lscWtnMVBnKy9sMHhhcDExd0hrOWplNEtPQ0FYWXdnZ0Z5TUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVBNkI1CmE4TXdjS0M4UFdMNjc4UGF3N0FTY3Rjd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0lRWURWUjBSQVFIL0JCY3dGWUVUYzNOamFISnZZMnRBWjI5dloyeGxMbU52YlRBc0Jnb3JCZ0VFQVlPLwpNQUVCQkI1b2RIUndjem92TDJkcGRHaDFZaTVqYjIwdmJHOW5hVzR2YjJGMWRHZ3dMZ1lLS3dZQkJBR0R2ekFCCkNBUWdEQjVvZEhSd2N6b3ZMMmRwZEdoMVlpNWpiMjB2Ykc5bmFXNHZiMkYxZEdnd2dZa0dDaXNHQVFRQjFua0MKQkFJRWV3UjVBSGNBZFFEZFBUQnF4c2NSTW1NWkhoeVpaemNDb2twZXVONDhyZitIaW5LQUx5bnVqZ0FBQVlpeApnQUl3QUFBRUF3QkdNRVFDSUJTN3lEOERVUTN2b3RnRXFCZzhXekFqbmFkNjJGRVgvZXZCUkNnZTdmeWpBaUJECkZseG8vOC9OVWNvN3VRSkNSazgrbDUvQXN5dG1jNU5vL3l0OW9tYkg5ekFLQmdncWhrak9QUVFEQXdOcEFEQm0KQWpFQWhjT1hBSmNyWGovWnlTYi9DYUY4aldSckE4bkthRWllRkREVFI0dFhKakk2bVE0ZEM2R05mWU8xdnRpMQpDUU1pQWpFQW9PWU44dnRDOEt5b3BWR3F1MkUxaHJuWFlCUXJUK1I4eFN5OEZRMHNTQVhGYzU0aGM0S2ZPemthClJIVXZlUUZSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"} \ No newline at end of file