diff --git a/.golangci.yml b/.golangci.yml index 673c0370..09139d08 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,7 +56,6 @@ linters: - misspell - nakedret - nestif - - noctx - nolintlint - paralleltest - predeclared diff --git a/github/github.go b/github/github.go index 90acf4ef..464d020f 100644 --- a/github/github.go +++ b/github/github.go @@ -91,6 +91,7 @@ func (c *Client) ParseFromURL(baseRepoURL, repoName string) (RepoInfo, error) { } log.Printf("getting repo info from URL: %s", repoURL.String()) + //nolint:noctx req, err := http.NewRequestWithContext( c.ctx, http.MethodGet, diff --git a/main.go b/main.go index ef633464..8728ae24 100644 --- a/main.go +++ b/main.go @@ -41,15 +41,20 @@ func main() { } // Sign json results. - if err = signing.SignScorecardResult("results.json"); err != nil { + // Always use the default GitHub token, never a PAT. + accessToken := os.Getenv(options.EnvInputInternalRepoToken) + s, err := signing.New(accessToken) + if err != nil { + log.Fatalf("error SigningNew: %v", err) + } + if err = s.SignScorecardResult("results.json"); err != nil { log.Fatalf("error signing scorecard json results: %v", err) } // Processes json results. repoName := os.Getenv(options.EnvGithubRepository) repoRef := os.Getenv(options.EnvGithubRef) - accessToken := os.Getenv(options.EnvInputRepoToken) - if err := signing.ProcessSignature(jsonPayload, repoName, repoRef, accessToken); err != nil { + if err := s.ProcessSignature(jsonPayload, repoName, repoRef); err != nil { log.Fatalf("error processing signature: %v", err) } } diff --git a/signing/signing.go b/signing/signing.go index 898b40f3..bef7da77 100644 --- a/signing/signing.go +++ b/signing/signing.go @@ -20,11 +20,13 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" "net/url" "os" + "strings" "time" sigOpts "github.com/sigstore/cosign/cmd/cosign/cli/options" @@ -34,15 +36,45 @@ import ( "github.com/ossf/scorecard-action/options" ) -// SignScorecardResult signs the results file and uploads the attestation to the Rekor transparency log. -func SignScorecardResult(scorecardResultsFile string) error { +var ( + errorEmptyToken = errors.New("error token empty") + errorInvalidToken = errors.New("invalid token") +) + +// Signing is a signing structure. +type Signing struct { + token string +} + +// New creates a new Signing instance. +func New(token string) (*Signing, error) { + // Set the default GITHUB_TOKEN, because it's not available by default + // in a GitHub Action. We need it for OIDC. + if token == "" { + return nil, fmt.Errorf("%w", errorEmptyToken) + } + + // Check for a workflow secret. + if !strings.HasPrefix(token, "ghs_") { + return nil, fmt.Errorf("%w: not a default GITHUB_TOKEN", errorInvalidToken) + } + if err := os.Setenv("GITHUB_TOKEN", token); err != nil { + return nil, fmt.Errorf("error setting GITHUB_TOKEN env var: %w", err) + } + if err := os.Setenv("COSIGN_EXPERIMENTAL", "true"); err != nil { - return fmt.Errorf("error setting COSIGN_EXPERIMENTAL env var: %w", err) + return nil, fmt.Errorf("error setting COSIGN_EXPERIMENTAL env var: %w", err) } + return &Signing{ + token: token, + }, nil +} + +// SignScorecardResult signs the results file and uploads the attestation to the Rekor transparency log. +func (s *Signing) SignScorecardResult(scorecardResultsFile string) error { // Prepare settings for SignBlobCmd. rootOpts := &sigOpts.RootOptions{Timeout: sigOpts.DefaultTimeout} // Just the timeout. - keyOpts := sigOpts.KeyOpts{ FulcioURL: sigOpts.DefaultFulcioURL, // Signing certificate provider. RekorURL: sigOpts.DefaultRekorURL, // Transparency log. @@ -87,7 +119,7 @@ func GetJSONScorecardResults() ([]byte, error) { } // ProcessSignature calls scorecard-api to process & upload signed scorecard results. -func ProcessSignature(jsonPayload []byte, repoName, repoRef, accessToken string) error { +func (s *Signing) ProcessSignature(jsonPayload []byte, repoName, repoRef string) error { // Prepare HTTP request body for scorecard-webapp-api call. // TODO: Use the `ScorecardResult` struct from `scorecard-webapp`. resultsPayload := struct { @@ -97,7 +129,7 @@ func ProcessSignature(jsonPayload []byte, repoName, repoRef, accessToken string) }{ Result: string(jsonPayload), Branch: repoRef, - AccessToken: accessToken, + AccessToken: s.token, } payloadBytes, err := json.Marshal(resultsPayload) @@ -113,7 +145,7 @@ func ProcessSignature(jsonPayload []byte, repoName, repoRef, accessToken string) if err != nil { return fmt.Errorf("parsing Scorecard API endpoint: %w", err) } - req, err := http.NewRequest("POST", parsedURL.String(), bytes.NewBuffer(payloadBytes)) //nolint + req, err := http.NewRequest("POST", parsedURL.String(), bytes.NewBuffer(payloadBytes)) if err != nil { return fmt.Errorf("creating HTTP request: %w", err) } diff --git a/signing/signing_test.go b/signing/signing_test.go index dc5c767c..60ce2457 100644 --- a/signing/signing_test.go +++ b/signing/signing_test.go @@ -17,6 +17,7 @@ package signing import ( + "fmt" "os" "testing" @@ -88,7 +89,11 @@ func Test_ProcessSignature(t *testing.T) { t.Errorf("Error reading testdata:, %v", err) } - if err := ProcessSignature(jsonPayload, repoName, repoRef, accessToken); err != nil { + s, err := New(accessToken) + if err != nil { + panic(fmt.Sprintf("error SigningNew: %v", err)) + } + if err := s.ProcessSignature(jsonPayload, repoName, repoRef); err != nil { t.Errorf("ProcessSignature() error:, %v", err) return }