mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-11 17:07:53 +03:00
✨ Cleanup errors and log (#782)
* cleanup * text * add errors * fixes * more * fixes * linnter * comments * name
This commit is contained in:
parent
9b2f3f5270
commit
6718939a08
@ -31,8 +31,6 @@ type CheckRequest struct {
|
||||
GraphClient *githubv4.Client
|
||||
HTTPClient *http.Client
|
||||
RepoClient clients.RepoClient
|
||||
// UPGRADEv2: Logf will be removed.
|
||||
Logf func(s string, f ...interface{})
|
||||
Dlogger DetailLogger
|
||||
Owner, Repo string
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package checker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
@ -31,11 +30,7 @@ const (
|
||||
// UPGRADEv2: to remove.
|
||||
const migrationThresholdPassValue = 8
|
||||
|
||||
// ErrorDemoninatorZero indicates the denominator for a proportional result is 0.
|
||||
// UPGRADEv2: to remove.
|
||||
var ErrorDemoninatorZero = errors.New("internal error: denominator is 0")
|
||||
|
||||
// DetailType defines types of details.
|
||||
// DetailType is the type of details.
|
||||
type DetailType int
|
||||
|
||||
const (
|
||||
@ -72,12 +67,11 @@ const (
|
||||
// nolint
|
||||
type CheckResult struct {
|
||||
// Old structure
|
||||
Error error `json:"-"`
|
||||
Name string
|
||||
Details []string
|
||||
Confidence int
|
||||
Pass bool
|
||||
ShouldRetry bool `json:"-"`
|
||||
Error error `json:"-"`
|
||||
Name string
|
||||
Details []string
|
||||
Confidence int
|
||||
Pass bool
|
||||
|
||||
// UPGRADEv2: New structure. Omitting unchanged Name field
|
||||
// for simplicity.
|
||||
@ -137,10 +131,9 @@ func CreateResultWithScore(name, reason string, score int) CheckResult {
|
||||
return CheckResult{
|
||||
Name: name,
|
||||
// Old structure.
|
||||
Error: nil,
|
||||
Confidence: MaxResultScore,
|
||||
Pass: pass,
|
||||
ShouldRetry: false,
|
||||
Error: nil,
|
||||
Confidence: MaxResultScore,
|
||||
Pass: pass,
|
||||
// New structure.
|
||||
//nolint
|
||||
Version: 2,
|
||||
@ -164,10 +157,9 @@ func CreateProportionalScoreResult(name, reason string, b, t int) CheckResult {
|
||||
return CheckResult{
|
||||
Name: name,
|
||||
// Old structure.
|
||||
Error: nil,
|
||||
Confidence: MaxResultConfidence,
|
||||
Pass: pass,
|
||||
ShouldRetry: false,
|
||||
Error: nil,
|
||||
Confidence: MaxResultConfidence,
|
||||
Pass: pass,
|
||||
// New structure.
|
||||
//nolint
|
||||
Version: 2,
|
||||
@ -198,9 +190,8 @@ func CreateInconclusiveResult(name, reason string) CheckResult {
|
||||
return CheckResult{
|
||||
Name: name,
|
||||
// Old structure.
|
||||
Confidence: 0,
|
||||
Pass: false,
|
||||
ShouldRetry: false,
|
||||
Confidence: 0,
|
||||
Pass: false,
|
||||
// New structure.
|
||||
//nolint
|
||||
Version: 2,
|
||||
@ -214,10 +205,9 @@ func CreateRuntimeErrorResult(name string, e error) CheckResult {
|
||||
return CheckResult{
|
||||
Name: name,
|
||||
// Old structure.
|
||||
Error: e,
|
||||
Confidence: 0,
|
||||
Pass: false,
|
||||
ShouldRetry: false,
|
||||
Error: e,
|
||||
Confidence: 0,
|
||||
Pass: false,
|
||||
// New structure.
|
||||
//nolint
|
||||
Version: 2,
|
||||
|
@ -16,14 +16,14 @@ package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
opencensusstats "go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
scorecarderrors "github.com/ossf/scorecard/v2/errors"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
"github.com/ossf/scorecard/v2/stats"
|
||||
)
|
||||
|
||||
@ -45,7 +45,6 @@ type CheckNameToFnMap map[string]CheckFn
|
||||
// UPGRADEv2: messages2 will ultimately
|
||||
// be renamed to messages.
|
||||
type logger struct {
|
||||
messages []string
|
||||
messages2 []CheckDetail
|
||||
}
|
||||
|
||||
@ -64,19 +63,15 @@ func (l *logger) Debug(desc string, args ...interface{}) {
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
// UPGRADEv2: to remove.
|
||||
func (l *logger) Logf(s string, f ...interface{}) {
|
||||
l.messages = append(l.messages, fmt.Sprintf(s, f...))
|
||||
}
|
||||
|
||||
func logStats(ctx context.Context, startTime time.Time, result *CheckResult) error {
|
||||
runTimeInSecs := time.Now().Unix() - startTime.Unix()
|
||||
opencensusstats.Record(ctx, stats.CheckRuntimeInSec.M(runTimeInSecs))
|
||||
|
||||
if result.Error != nil {
|
||||
ctx, err := tag.New(ctx, tag.Upsert(stats.ErrorName, scorecarderrors.GetErrorName(result.Error)))
|
||||
ctx, err := tag.New(ctx, tag.Upsert(stats.ErrorName, sce.GetName(result.Error2)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("tag.New: %v", err))
|
||||
}
|
||||
opencensusstats.Record(ctx, stats.CheckErrors.M(1))
|
||||
}
|
||||
@ -97,19 +92,14 @@ func (r *Runner) Run(ctx context.Context, f CheckFn) CheckResult {
|
||||
checkRequest := r.CheckRequest
|
||||
checkRequest.Ctx = ctx
|
||||
l = logger{}
|
||||
// UPGRADEv2: to remove.
|
||||
checkRequest.Logf = l.Logf
|
||||
checkRequest.Dlogger = &l
|
||||
res = f(&checkRequest)
|
||||
// UPGRADEv2: to fix using proper error check.
|
||||
if res.ShouldRetry && !strings.Contains(res.Error.Error(), "invalid header field value") {
|
||||
checkRequest.Logf("error, retrying: %s", res.Error)
|
||||
if res.Error2 != nil && errors.Is(res.Error2, sce.ErrRepoUnreachable) {
|
||||
checkRequest.Dlogger.Warn("%v", res.Error2)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
// UPGRADEv2: to remove.
|
||||
res.Details = l.messages
|
||||
res.Details2 = l.messages2
|
||||
|
||||
if err := logStats(ctx, startTime, &res); err != nil {
|
||||
|
@ -29,6 +29,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v32/github"
|
||||
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -47,7 +49,6 @@ func extractAndValidateArchivePath(path, dest string) (string, error) {
|
||||
// Discard the directory and only keep the actual files.
|
||||
names := strings.SplitN(path, "/", splitLength)
|
||||
if len(names) < splitLength {
|
||||
log.Printf("Unable to split path: %s", path)
|
||||
return dest, nil
|
||||
}
|
||||
if names[1] == "" {
|
||||
@ -56,7 +57,8 @@ func extractAndValidateArchivePath(path, dest string) (string, error) {
|
||||
// Check for ZipSlip: https://snyk.io/research/zip-slip-vulnerability
|
||||
cleanpath := filepath.Join(dest, names[1])
|
||||
if !strings.HasPrefix(cleanpath, filepath.Clean(dest)+string(os.PathSeparator)) {
|
||||
return "", fmt.Errorf("%w: %s", errZipSlip, names[1])
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("%v: %v", errZipSlip, names[1]))
|
||||
}
|
||||
return cleanpath, nil
|
||||
}
|
||||
@ -75,18 +77,13 @@ func (handler *tarballHandler) init(ctx context.Context, repo *github.Repository
|
||||
|
||||
// Setup temp dir/files and download repo tarball.
|
||||
if err := handler.getTarball(ctx, repo); errors.Is(err, errTarballNotFound) {
|
||||
log.Printf("%v", err)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("error getting githurepo tarball: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract file names and content from tarball.
|
||||
if err := handler.extractTarball(); err != nil {
|
||||
return fmt.Errorf("error extracting githubrepo tarball: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return handler.extractTarball()
|
||||
}
|
||||
|
||||
func (handler *tarballHandler) getTarball(ctx context.Context, repo *github.Repository) error {
|
||||
@ -95,32 +92,38 @@ func (handler *tarballHandler) getTarball(ctx context.Context, repo *github.Repo
|
||||
url = strings.Replace(url, "{/ref}", "", 1)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http.NewRequestWithContext: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("http.NewRequestWithContext: %v", err))
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http.DefaultClient.Do: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("http.DefaultClient.Do: %v", err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Handle 400/404 errors
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNotFound, http.StatusBadRequest:
|
||||
return fmt.Errorf("%w: %s", errTarballNotFound, *repo.URL)
|
||||
//nolint:wrapcheck
|
||||
return sce.CreateInternal(errTarballNotFound, fmt.Sprintf("%v: %v: %v", errTarballNotFound, *repo.URL, err))
|
||||
}
|
||||
|
||||
// Create a temp file. This automatically appends a random number to the name.
|
||||
tempDir, err := ioutil.TempDir("", repoDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating TempDir in githubrepo: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("ioutil.TempDir: %v", err))
|
||||
}
|
||||
repoFile, err := ioutil.TempFile(tempDir, repoFilename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error during ioutil.TempFile in githubrepo: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("ioutil.TempFile: %v", err))
|
||||
}
|
||||
defer repoFile.Close()
|
||||
if _, err := io.Copy(repoFile, resp.Body); err != nil {
|
||||
return fmt.Errorf("error during io.Copy in githubrepo tarball: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("io.Copy: %v", err))
|
||||
}
|
||||
|
||||
handler.tempDir = tempDir
|
||||
@ -133,11 +136,13 @@ func (handler *tarballHandler) extractTarball() error {
|
||||
// nolint: gomnd
|
||||
in, err := os.OpenFile(handler.tempTarFile, os.O_RDONLY, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening %s: %w", handler.tempTarFile, err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("os.OpenFile: %v", err))
|
||||
}
|
||||
gz, err := gzip.NewReader(in)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", handler.tempTarFile, err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("gzip.NewReader: %v: %v", handler.tempTarFile, err))
|
||||
}
|
||||
tr := tar.NewReader(gz)
|
||||
for {
|
||||
@ -146,14 +151,15 @@ func (handler *tarballHandler) extractTarball() error {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in tarReader.Next(): %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("tarReader.Next: %v", err))
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
dirpath, err := extractAndValidateArchivePath(header.Name, handler.tempDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error extracting dirpath: %w", err)
|
||||
return err
|
||||
}
|
||||
if dirpath == filepath.Clean(handler.tempDir) {
|
||||
continue
|
||||
@ -168,25 +174,28 @@ func (handler *tarballHandler) extractTarball() error {
|
||||
}
|
||||
filenamepath, err := extractAndValidateArchivePath(header.Name, handler.tempDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error extracting file path: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Dir(filenamepath)); os.IsNotExist(err) {
|
||||
// nolint: gomnd
|
||||
if err := os.Mkdir(filepath.Dir(filenamepath), 0o755); err != nil {
|
||||
return fmt.Errorf("error during os.Mkdir: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("os.Mkdir: %v", err))
|
||||
}
|
||||
}
|
||||
outFile, err := os.Create(filenamepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error during os.Create: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("os.Create: %v", err))
|
||||
}
|
||||
|
||||
// nolint: gosec
|
||||
// Potential for DoS vulnerability via decompression bomb.
|
||||
// Since such an attack will only impact a single shard, ignoring this for now.
|
||||
if _, err := io.Copy(outFile, tr); err != nil {
|
||||
return fmt.Errorf("error during io.Copy: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("io.Copy: %v", err))
|
||||
}
|
||||
outFile.Close()
|
||||
handler.files = append(handler.files,
|
||||
@ -218,14 +227,16 @@ func (handler *tarballHandler) listFiles(predicate func(string) (bool, error)) (
|
||||
func (handler *tarballHandler) getFileContent(filename string) ([]byte, error) {
|
||||
content, err := ioutil.ReadFile(filepath.Join(handler.tempDir, filename))
|
||||
if err != nil {
|
||||
return content, fmt.Errorf("error trying to ReadFile: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return content, sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("ioutil.ReadFile: %v", err))
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (handler *tarballHandler) cleanup() error {
|
||||
if err := os.RemoveAll(handler.tempDir); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("os.Remove: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("os.Remove: %v", err))
|
||||
}
|
||||
// Remove old files so we don't iterate through them.
|
||||
handler.files = nil
|
||||
|
42
cmd/root.go
42
cmd/root.go
@ -18,7 +18,6 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@ -36,6 +35,7 @@ import (
|
||||
"github.com/ossf/scorecard/v2/checker"
|
||||
"github.com/ossf/scorecard/v2/checks"
|
||||
"github.com/ossf/scorecard/v2/clients/githubrepo"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
"github.com/ossf/scorecard/v2/pkg"
|
||||
"github.com/ossf/scorecard/v2/repos"
|
||||
"github.com/ossf/scorecard/v2/roundtripper"
|
||||
@ -52,8 +52,6 @@ var (
|
||||
pypi string
|
||||
rubygems string
|
||||
showDetails bool
|
||||
// ErrorInvalidFormatFlag indicates an invalid option was passed for the 'format' argument.
|
||||
ErrorInvalidFormatFlag = errors.New("invalid format flag")
|
||||
)
|
||||
|
||||
const (
|
||||
@ -165,7 +163,8 @@ or ./scorecard --{npm,pypi,rubgems}=<package_name> [--checks=check1,...] [--show
|
||||
case formatJSON:
|
||||
err = repoResult.AsJSON(showDetails, *logLevel, os.Stdout)
|
||||
default:
|
||||
err = fmt.Errorf("%w %s. allowed values are: [default, csv, json]", ErrorInvalidFormatFlag, format)
|
||||
err = sce.Create(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("invalid format flag: %v. Expected [default, csv, json]", format))
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to output results: %v", err)
|
||||
@ -204,7 +203,7 @@ func Execute() {
|
||||
}
|
||||
|
||||
// Gets the GitHub repository URL for the npm package.
|
||||
//nolint:noctx,goerr113
|
||||
//nolint:noctx
|
||||
func fetchGitRepositoryFromNPM(packageName string) (string, error) {
|
||||
npmSearchURL := "https://registry.npmjs.org/-/v1/search?text=%s&size=1"
|
||||
const timeout = 10
|
||||
@ -213,23 +212,27 @@ func fetchGitRepositoryFromNPM(packageName string) (string, error) {
|
||||
}
|
||||
resp, err := client.Get(fmt.Sprintf(npmSearchURL, packageName))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get npm package json: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("failed to get npm package json: %v", err))
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
v := &npmSearchResults{}
|
||||
err = json.NewDecoder(resp.Body).Decode(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse npm package json: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("failed to parse npm package json: %v", err))
|
||||
}
|
||||
if len(v.Objects) == 0 {
|
||||
return "", fmt.Errorf("could not find source repo for npm package: %s", packageName)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("could not find source repo for npm package: %s", packageName))
|
||||
}
|
||||
return v.Objects[0].Package.Links.Repository, nil
|
||||
}
|
||||
|
||||
// Gets the GitHub repository URL for the pypi package.
|
||||
//nolint:noctx,goerr113
|
||||
//nolint:noctx
|
||||
func fetchGitRepositoryFromPYPI(packageName string) (string, error) {
|
||||
pypiSearchURL := "https://pypi.org/pypi/%s/json"
|
||||
const timeout = 10
|
||||
@ -238,23 +241,27 @@ func fetchGitRepositoryFromPYPI(packageName string) (string, error) {
|
||||
}
|
||||
resp, err := client.Get(fmt.Sprintf(pypiSearchURL, packageName))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get pypi package json: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("failed to get pypi package json: %v", err))
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
v := &pypiSearchResults{}
|
||||
err = json.NewDecoder(resp.Body).Decode(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse pypi package json: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("failed to parse pypi package json: %v", err))
|
||||
}
|
||||
if v.Info.ProjectUrls.Source == "" {
|
||||
return "", fmt.Errorf("could not find source repo for pypi package: %s", packageName)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("could not find source repo for pypi package: %s", packageName))
|
||||
}
|
||||
return v.Info.ProjectUrls.Source, nil
|
||||
}
|
||||
|
||||
// Gets the GitHub repository URL for the rubygems package.
|
||||
//nolint:noctx,goerr113
|
||||
//nolint:noctx
|
||||
func fetchGitRepositoryFromRubyGems(packageName string) (string, error) {
|
||||
rubyGemsSearchURL := "https://rubygems.org/api/v1/gems/%s.json"
|
||||
const timeout = 10
|
||||
@ -263,17 +270,20 @@ func fetchGitRepositoryFromRubyGems(packageName string) (string, error) {
|
||||
}
|
||||
resp, err := client.Get(fmt.Sprintf(rubyGemsSearchURL, packageName))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get ruby gem json: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("failed to get ruby gem json: %v", err))
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
v := &rubyGemsSearchResults{}
|
||||
err = json.NewDecoder(resp.Body).Decode(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse ruby gem json: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("failed to parse ruby gem json: %v", err))
|
||||
}
|
||||
if v.SourceCodeURI == "" {
|
||||
return "", fmt.Errorf("could not find source repo for ruby gem: %s", packageName)
|
||||
//nolint:wrapcheck
|
||||
return "", sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("could not find source repo for ruby gem: %v", err))
|
||||
}
|
||||
return v.SourceCodeURI, nil
|
||||
}
|
||||
|
21
errors/internal.go
Normal file
21
errors/internal.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2021 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package errors
|
||||
|
||||
// CreateInternal creates internal error, not using
|
||||
// any of the errors listed in public.go.
|
||||
func CreateInternal(e error, msg string) error {
|
||||
return Create(e, msg)
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2020 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// RetryError occurs when checks fail after exhausting all retry attempts.
|
||||
RetryError = "RetryError"
|
||||
// LowConfidenceError shows a low-confidence result.
|
||||
LowConfidenceError = "LowConfidenceError"
|
||||
// UnknownError for all error types not handled.
|
||||
UnknownError = "UnknownError"
|
||||
)
|
||||
|
||||
var (
|
||||
errRetry *ErrRetry
|
||||
errLowConfidence *ErrLowConfidence
|
||||
)
|
||||
|
||||
// GetErrorName returns the name of the error.
|
||||
func GetErrorName(err error) string {
|
||||
switch {
|
||||
case errors.As(err, &errRetry):
|
||||
return RetryError
|
||||
case errors.As(err, &errLowConfidence):
|
||||
return LowConfidenceError
|
||||
default:
|
||||
return UnknownError
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2020 Security Scorecard Authors
|
||||
// Copyright 2021 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// UPGRADEv2: delete other files in folder.
|
||||
//nolint
|
||||
var (
|
||||
ErrScorecardInternal = errors.New("internal error")
|
||||
@ -37,8 +36,14 @@ func Create(e error, msg string) error {
|
||||
return fmt.Errorf("%w", e)
|
||||
}
|
||||
|
||||
// CreateInternal creates an internal error, not using
|
||||
// any of the errors listed above.
|
||||
func CreateInternal(e error, msg string) error {
|
||||
return Create(e, msg)
|
||||
// GetName returns the name of the error.
|
||||
func GetName(err error) string {
|
||||
switch {
|
||||
case errors.Is(err, ErrScorecardInternal):
|
||||
return "ErrScorecardInternal"
|
||||
case errors.Is(err, ErrRepoUnreachable):
|
||||
return "ErrRepoUnreachable"
|
||||
default:
|
||||
return "ErrUnknown"
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
// Copyright 2020 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package errors defines all errors returned to users by Scorecard library.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
// ErrRetry is returned when a check failed after maximum num_retries.
|
||||
ErrRetry struct{ wrappedError }
|
||||
// ErrLowConfidence is returned when check result is inconclusive.
|
||||
ErrLowConfidence struct{ wrappedError }
|
||||
)
|
||||
|
||||
// MakeRetryError returns a wrapped error of type ErrRetry.
|
||||
func MakeRetryError(err error) error {
|
||||
return &ErrRetry{
|
||||
wrappedError{
|
||||
msg: "unable to run check, retry",
|
||||
innerError: err,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MakeLowConfidenceError returns a wrapped error of type ErrLowConfidence.
|
||||
func MakeLowConfidenceError(err error) error {
|
||||
return &ErrLowConfidence{
|
||||
wrappedError{
|
||||
msg: "low confidence check result",
|
||||
innerError: err,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type wrappedError struct {
|
||||
innerError error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (err *wrappedError) Error() string {
|
||||
return fmt.Sprintf("%s: %v", err.msg, err.innerError)
|
||||
}
|
||||
|
||||
func (err *wrappedError) Unwrap() error {
|
||||
return err.innerError
|
||||
}
|
1
go.mod
1
go.mod
@ -20,7 +20,6 @@ require (
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/gomega v1.14.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
|
||||
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
|
||||
github.com/spf13/cobra v1.2.1
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
|
||||
"github.com/ossf/scorecard/v2/checker"
|
||||
"github.com/ossf/scorecard/v2/clients"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
"github.com/ossf/scorecard/v2/repos"
|
||||
"github.com/ossf/scorecard/v2/stats"
|
||||
)
|
||||
@ -80,12 +81,15 @@ func RunScorecards(ctx context.Context,
|
||||
graphClient *githubv4.Client) (ScorecardResult, error) {
|
||||
ctx, err := tag.New(ctx, tag.Upsert(stats.Repo, repo.URL()))
|
||||
if err != nil {
|
||||
return ScorecardResult{}, fmt.Errorf("error during tag.New: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return ScorecardResult{}, sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("tag.New: %v", err))
|
||||
}
|
||||
defer logStats(ctx, time.Now())
|
||||
|
||||
if err := repoClient.InitRepo(repo.Owner, repo.Repo); err != nil {
|
||||
return ScorecardResult{}, fmt.Errorf("error during InitRepo for %s: %w", repo.URL(), err)
|
||||
// No need to call sce.Create() since InitRepo will do that for us.
|
||||
//nolint:wrapcheck
|
||||
return ScorecardResult{}, err
|
||||
}
|
||||
|
||||
ret := ScorecardResult{
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/ossf/scorecard/v2/checker"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
// ScorecardResult struct is returned on a successful Scorecard run.
|
||||
@ -43,7 +44,8 @@ func (r *ScorecardResult) AsJSON(showDetails bool, logLevel zapcore.Level, write
|
||||
encoder := json.NewEncoder(writer)
|
||||
if showDetails {
|
||||
if err := encoder.Encode(r); err != nil {
|
||||
return fmt.Errorf("error encoding repo result as detailed JSON: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("encoder.Encode: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -63,7 +65,8 @@ func (r *ScorecardResult) AsJSON(showDetails bool, logLevel zapcore.Level, write
|
||||
out.Checks = append(out.Checks, tmpResult)
|
||||
}
|
||||
if err := encoder.Encode(out); err != nil {
|
||||
return fmt.Errorf("error encoding repo result as JSON: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("encoder.Encode: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -86,11 +89,13 @@ func (r *ScorecardResult) AsCSV(showDetails bool, logLevel zapcore.Level, writer
|
||||
}
|
||||
fmt.Fprintf(writer, "%s\n", strings.Join(columns, ","))
|
||||
if err := w.Write(record); err != nil {
|
||||
return fmt.Errorf("error writing repo result as CSV: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("csv.Write: %v", err))
|
||||
}
|
||||
w.Flush()
|
||||
if err := w.Error(); err != nil {
|
||||
return fmt.Errorf("error flushing repo result as CSV: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("csv.Flush: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -31,7 +33,7 @@ var (
|
||||
ErrorInvalidURL = errors.New("invalid repo flag")
|
||||
)
|
||||
|
||||
// RepoURL represents a URL of a repo.
|
||||
//nolint:revive
|
||||
type RepoURL struct {
|
||||
Host, Owner, Repo string
|
||||
Metadata []string
|
||||
@ -61,13 +63,15 @@ func (r *RepoURL) Set(s string) error {
|
||||
|
||||
u, e := url.Parse(s)
|
||||
if e != nil {
|
||||
return fmt.Errorf("error parsing repo URL: %w", e)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("url.Parse: %v", e))
|
||||
}
|
||||
|
||||
const splitLen = 2
|
||||
split := strings.SplitN(strings.Trim(u.Path, "/"), "/", splitLen)
|
||||
if len(split) != splitLen {
|
||||
return fmt.Errorf("%w: [%s], pass the full repository URL", ErrorInvalidURL, s)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(ErrorInvalidURL, fmt.Sprintf("%v. Exepted full repository url", s))
|
||||
}
|
||||
|
||||
r.Host, r.Owner, r.Repo = u.Host, split[0], split[1]
|
||||
@ -79,11 +83,14 @@ func (r *RepoURL) ValidGitHubURL() error {
|
||||
switch r.Host {
|
||||
case "github.com":
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrorUnsupportedHost, r.Host)
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(ErrorUnsupportedHost, r.Host)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(r.Owner) == "" || strings.TrimSpace(r.Repo) == "" {
|
||||
return fmt.Errorf("%w: [%s], pass the full repository URL", ErrorInvalidGithubURL, r.URL())
|
||||
//nolint:wrapcheck
|
||||
return sce.Create(ErrorInvalidGithubURL,
|
||||
fmt.Sprintf("%v. Expected the full reposiroty url", r.URL()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
opencensusstats "go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
"github.com/ossf/scorecard/v2/stats"
|
||||
)
|
||||
|
||||
@ -44,18 +45,21 @@ type censusTransport struct {
|
||||
func (ct *censusTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
ctx, err := tag.New(r.Context(), tag.Upsert(stats.RequestTag, "requested"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during tag.New: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return nil, sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("tag.New: %v", err))
|
||||
}
|
||||
|
||||
r = r.WithContext(ctx)
|
||||
resp, err := ct.innerTransport.RoundTrip(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in RoundTrip: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return nil, sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("innerTransport.RoundTrip: %v", err))
|
||||
}
|
||||
if resp.Header.Get(httpcache.XFromCache) != "" {
|
||||
ctx, err = tag.New(ctx, tag.Upsert(stats.RequestTag, httpcache.XFromCache))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during tag.New: %w", err)
|
||||
//nolint:wrapcheck
|
||||
return nil, sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("tag.New: %v", err))
|
||||
}
|
||||
}
|
||||
opencensusstats.Record(ctx, stats.HTTPRequests.M(1))
|
||||
|
@ -15,12 +15,14 @@
|
||||
package roundtripper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
// MakeRateLimitedTransport returns a RoundTripper which rate limits GitHub requests.
|
||||
@ -41,7 +43,8 @@ type rateLimitTransport struct {
|
||||
func (gh *rateLimitTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
resp, err := gh.innerTransport.RoundTrip(r)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error in round trip")
|
||||
//nolint:wrapcheck
|
||||
return nil, sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("innerTransport.RoundTrip: %v", err))
|
||||
}
|
||||
rateLimit := resp.Header.Get("X-RateLimit-Remaining")
|
||||
remaining, err := strconv.Atoi(rateLimit)
|
||||
|
Loading…
Reference in New Issue
Block a user