mirror of
https://github.com/ossf/scorecard.git
synced 2024-08-15 03:10:33 +03:00
✨ Miscellaneous refactors to ease downstream consumption (#1645)
* checker: Add `NewLogger` constructor for `DetailLogger` impl * checker: Add `NewRunner` constructor for `Runner` * cmd: Update to use refactored packages * cmd: Move command flags and validation into an `options` package * cmd: Move client accessors to `githubrepo` package * cmd: Move policy and enabled checks to `policy` package * cmd: Move results formatting to `format` package * checker: Prefer `Set` prefixes for setters * checker: Use `DetailLogger` return value for `NewLogger()` * checker: Add `GetClients` accessor * Move `FormatResults` to `pkg/` * checks: Add getter for all checks Signed-off-by: Stephen Augustus <foo@auggie.dev>
This commit is contained in:
parent
76105194da
commit
7956ff4fe7
@ -36,6 +36,30 @@ type Runner struct {
|
||||
CheckRequest CheckRequest
|
||||
}
|
||||
|
||||
// NewRunner creates a new instance of `Runner`.
|
||||
func NewRunner(checkName, repo string, checkReq *CheckRequest) *Runner {
|
||||
return &Runner{
|
||||
CheckName: checkName,
|
||||
Repo: repo,
|
||||
CheckRequest: *checkReq,
|
||||
}
|
||||
}
|
||||
|
||||
// SetCheckName sets the check name.
|
||||
func (r *Runner) SetCheckName(check string) {
|
||||
r.CheckName = check
|
||||
}
|
||||
|
||||
// SetRepo sets the repository.
|
||||
func (r *Runner) SetRepo(repo string) {
|
||||
r.Repo = repo
|
||||
}
|
||||
|
||||
// SetCheckRequest sets the check request.
|
||||
func (r *Runner) SetCheckRequest(checkReq *CheckRequest) {
|
||||
r.CheckRequest = *checkReq
|
||||
}
|
||||
|
||||
// CheckFn defined for convenience.
|
||||
type CheckFn func(*CheckRequest) CheckResult
|
||||
|
||||
@ -79,12 +103,11 @@ func (r *Runner) Run(ctx context.Context, c Check) CheckResult {
|
||||
startTime := time.Now()
|
||||
|
||||
var res CheckResult
|
||||
var l logger
|
||||
l := NewLogger()
|
||||
for retriesRemaining := checkRetries; retriesRemaining > 0; retriesRemaining-- {
|
||||
checkRequest := r.CheckRequest
|
||||
checkRequest.Ctx = ctx
|
||||
l = logger{}
|
||||
checkRequest.Dlogger = &l
|
||||
checkRequest.Dlogger = l
|
||||
res = c.Fn(&checkRequest)
|
||||
if res.Error2 != nil && errors.Is(res.Error2, sce.ErrRepoUnreachable) {
|
||||
checkRequest.Dlogger.Warn(&LogMessage{
|
||||
|
66
checker/client.go
Normal file
66
checker/client.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2022 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 checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ossf/scorecard/v4/clients"
|
||||
ghrepo "github.com/ossf/scorecard/v4/clients/githubrepo"
|
||||
"github.com/ossf/scorecard/v4/clients/localdir"
|
||||
"github.com/ossf/scorecard/v4/log"
|
||||
)
|
||||
|
||||
// GetClients returns a list of clients for running scorecard checks.
|
||||
// TODO(repo): Pass a `http.RoundTripper` here.
|
||||
func GetClients(ctx context.Context, repoURI, localURI string, logger *log.Logger) (
|
||||
clients.Repo, // repo
|
||||
clients.RepoClient, // repoClient
|
||||
clients.RepoClient, // ossFuzzClient
|
||||
clients.CIIBestPracticesClient, // ciiClient
|
||||
clients.VulnerabilitiesClient, // vulnClient
|
||||
error) {
|
||||
var githubRepo clients.Repo
|
||||
if localURI != "" {
|
||||
localRepo, errLocal := localdir.MakeLocalDirRepo(localURI)
|
||||
return localRepo, /*repo*/
|
||||
localdir.CreateLocalDirClient(ctx, logger), /*repoClient*/
|
||||
nil, /*ossFuzzClient*/
|
||||
nil, /*ciiClient*/
|
||||
nil, /*vulnClient*/
|
||||
fmt.Errorf("getting local directory client: %w", errLocal)
|
||||
}
|
||||
|
||||
githubRepo, errGitHub := ghrepo.MakeGithubRepo(repoURI)
|
||||
if errGitHub != nil {
|
||||
return githubRepo,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
fmt.Errorf("getting local directory client: %w", errGitHub)
|
||||
}
|
||||
|
||||
ossFuzzRepoClient, errOssFuzz := ghrepo.CreateOssFuzzRepoClient(ctx, logger)
|
||||
|
||||
// TODO(repo): Should we be handling the OSS-Fuzz client error like this?
|
||||
return githubRepo, /*repo*/
|
||||
ghrepo.CreateGithubRepoClient(ctx, logger), /*repoClient*/
|
||||
ossFuzzRepoClient, /*ossFuzzClient*/
|
||||
clients.DefaultCIIBestPracticesClient(), /*ciiClient*/
|
||||
clients.DefaultVulnerabilitiesClient(), /*vulnClient*/
|
||||
fmt.Errorf("getting OSS-Fuzz repo client: %w", errOssFuzz)
|
||||
}
|
@ -14,10 +14,17 @@
|
||||
|
||||
package checker
|
||||
|
||||
// Logger is an implementation of the `DetailLogger` interface.
|
||||
type logger struct {
|
||||
logs []CheckDetail
|
||||
}
|
||||
|
||||
// NewLogger creates a new instance of `DetailLogger`.
|
||||
func NewLogger() DetailLogger {
|
||||
return &logger{}
|
||||
}
|
||||
|
||||
// Info emits info level logs.
|
||||
func (l *logger) Info(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailInfo,
|
||||
@ -26,6 +33,7 @@ func (l *logger) Info(msg *LogMessage) {
|
||||
l.logs = append(l.logs, cd)
|
||||
}
|
||||
|
||||
// Warn emits warn level logs.
|
||||
func (l *logger) Warn(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailWarn,
|
||||
@ -34,6 +42,7 @@ func (l *logger) Warn(msg *LogMessage) {
|
||||
l.logs = append(l.logs, cd)
|
||||
}
|
||||
|
||||
// Debug emits debug level logs.
|
||||
func (l *logger) Debug(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailDebug,
|
||||
@ -42,8 +51,14 @@ func (l *logger) Debug(msg *LogMessage) {
|
||||
l.logs = append(l.logs, cd)
|
||||
}
|
||||
|
||||
// Flush returns existing logs and resets the logger instance.
|
||||
func (l *logger) Flush() []CheckDetail {
|
||||
ret := l.logs
|
||||
ret := l.Logs()
|
||||
l.logs = nil
|
||||
return ret
|
||||
}
|
||||
|
||||
// Logs returns existing logs.
|
||||
func (l *logger) Logs() []CheckDetail {
|
||||
return l.logs
|
||||
}
|
||||
|
@ -22,6 +22,14 @@ import (
|
||||
// AllChecks is the list of all security checks that will be run.
|
||||
var AllChecks = checker.CheckNameToFnMap{}
|
||||
|
||||
// GetAll returns the full list of checks, given any environment variable
|
||||
// constraints.
|
||||
// TODO(checks): Is this actually necessary given `AllChecks` exists?
|
||||
func GetAll() checker.CheckNameToFnMap {
|
||||
possibleChecks := AllChecks
|
||||
return possibleChecks
|
||||
}
|
||||
|
||||
func registerCheck(name string, fn checker.CheckFn, supportedRequestTypes []checker.RequestType) error {
|
||||
if name == "" {
|
||||
return errInternalNameCannotBeEmpty
|
||||
|
@ -228,7 +228,7 @@ func CreateGithubRepoClient(ctx context.Context, logger *log.Logger) clients.Rep
|
||||
func CreateOssFuzzRepoClient(ctx context.Context, logger *log.Logger) (clients.RepoClient, error) {
|
||||
ossFuzzRepo, err := MakeGithubRepo("google/oss-fuzz")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during githubrepo.MakeGithubRepo: %w", err)
|
||||
return nil, fmt.Errorf("error during MakeGithubRepo: %w", err)
|
||||
}
|
||||
|
||||
ossFuzzRepoClient := CreateGithubRepoClient(ctx, logger)
|
||||
|
31
cmd/flags.go
31
cmd/flags.go
@ -1,31 +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 cmd implements Scorecard commandline.
|
||||
package cmd
|
||||
|
||||
var (
|
||||
flagRepo string
|
||||
flagLocal string
|
||||
flagCommit string
|
||||
flagChecksToRun []string
|
||||
flagMetadata []string
|
||||
flagLogLevel string
|
||||
flagFormat string
|
||||
flagNPM string
|
||||
flagPyPI string
|
||||
flagRubyGems string
|
||||
flagShowDetails bool
|
||||
flagPolicyFile string
|
||||
)
|
346
cmd/root.go
346
cmd/root.go
@ -28,23 +28,14 @@ import (
|
||||
"github.com/ossf/scorecard/v4/checker"
|
||||
"github.com/ossf/scorecard/v4/checks"
|
||||
"github.com/ossf/scorecard/v4/clients"
|
||||
"github.com/ossf/scorecard/v4/clients/githubrepo"
|
||||
"github.com/ossf/scorecard/v4/clients/localdir"
|
||||
docs "github.com/ossf/scorecard/v4/docs/checks"
|
||||
sce "github.com/ossf/scorecard/v4/errors"
|
||||
sclog "github.com/ossf/scorecard/v4/log"
|
||||
"github.com/ossf/scorecard/v4/options"
|
||||
"github.com/ossf/scorecard/v4/pkg"
|
||||
spol "github.com/ossf/scorecard/v4/policy"
|
||||
"github.com/ossf/scorecard/v4/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
formatJSON = "json"
|
||||
formatSarif = "sarif"
|
||||
formatDefault = "default"
|
||||
formatRaw = "raw"
|
||||
|
||||
cliEnableSarif = "ENABLE_SARIF"
|
||||
|
||||
scorecardLong = "A program that shows security scorecard for an open source software."
|
||||
scorecardUse = `./scorecard [--repo=<repo_url>] [--local=folder] [--checks=check1,...]
|
||||
[--show-details] or ./scorecard --{npm,pypi,rubygems}=<package_name>
|
||||
@ -59,42 +50,45 @@ var rootCmd = &cobra.Command{
|
||||
Run: scorecardCmd,
|
||||
}
|
||||
|
||||
var opts = options.New()
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
rootCmd.Flags().StringVar(&flagRepo, "repo", "", "repository to check")
|
||||
rootCmd.Flags().StringVar(&flagLocal, "local", "", "local folder to check")
|
||||
rootCmd.Flags().StringVar(&flagCommit, "commit", clients.HeadSHA, "commit to analyze")
|
||||
rootCmd.Flags().StringVar(&opts.Repo, "repo", "", "repository to check")
|
||||
rootCmd.Flags().StringVar(&opts.Local, "local", "", "local folder to check")
|
||||
rootCmd.Flags().StringVar(&opts.Commit, "commit", options.DefaultCommit, "commit to analyze")
|
||||
rootCmd.Flags().StringVar(
|
||||
&flagLogLevel,
|
||||
&opts.LogLevel,
|
||||
"verbosity",
|
||||
sclog.DefaultLevel.String(),
|
||||
options.DefaultLogLevel,
|
||||
"set the log level",
|
||||
)
|
||||
rootCmd.Flags().StringVar(
|
||||
&flagNPM, "npm", "",
|
||||
&opts.NPM, "npm", "",
|
||||
"npm package to check, given that the npm package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(
|
||||
&flagPyPI, "pypi", "",
|
||||
&opts.PyPI, "pypi", "",
|
||||
"pypi package to check, given that the pypi package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(
|
||||
&flagRubyGems, "rubygems", "",
|
||||
&opts.RubyGems, "rubygems", "",
|
||||
"rubygems package to check, given that the rubygems package has a GitHub repository")
|
||||
rootCmd.Flags().StringSliceVar(
|
||||
&flagMetadata, "metadata", []string{}, "metadata for the project. It can be multiple separated by commas")
|
||||
rootCmd.Flags().BoolVar(&flagShowDetails, "show-details", false, "show extra details about each check")
|
||||
&opts.Metadata, "metadata", []string{}, "metadata for the project. It can be multiple separated by commas")
|
||||
rootCmd.Flags().BoolVar(&opts.ShowDetails, "show-details", false, "show extra details about each check")
|
||||
checkNames := []string{}
|
||||
for checkName := range getAllChecks() {
|
||||
for checkName := range checks.GetAll() {
|
||||
checkNames = append(checkNames, checkName)
|
||||
}
|
||||
rootCmd.Flags().StringSliceVar(&flagChecksToRun, "checks", []string{},
|
||||
rootCmd.Flags().StringSliceVar(&opts.ChecksToRun, "checks", []string{},
|
||||
fmt.Sprintf("Checks to run. Possible values are: %s", strings.Join(checkNames, ",")))
|
||||
|
||||
if isSarifEnabled() {
|
||||
rootCmd.Flags().StringVar(&flagPolicyFile, "policy", "", "policy to enforce")
|
||||
rootCmd.Flags().StringVar(&flagFormat, "format", formatDefault,
|
||||
// TODO(cmd): Extract logic
|
||||
if options.IsSarifEnabled() {
|
||||
rootCmd.Flags().StringVar(&opts.PolicyFile, "policy", "", "policy to enforce")
|
||||
rootCmd.Flags().StringVar(&opts.Format, "format", options.FormatDefault,
|
||||
"output format allowed values are [default, sarif, json]")
|
||||
} else {
|
||||
rootCmd.Flags().StringVar(&flagFormat, "format", formatDefault,
|
||||
rootCmd.Flags().StringVar(&opts.Format, "format", options.FormatDefault,
|
||||
"output format allowed values are [default, json]")
|
||||
}
|
||||
}
|
||||
@ -108,28 +102,39 @@ func Execute() {
|
||||
}
|
||||
|
||||
func scorecardCmd(cmd *cobra.Command, args []string) {
|
||||
validateCmdFlags()
|
||||
RunScorecard(args)
|
||||
}
|
||||
|
||||
// RunScorecard runs scorecard checks given a set of arguments.
|
||||
// TODO(cmd): Is `args` required?
|
||||
func RunScorecard(args []string) {
|
||||
// TODO(cmd): Catch validation errors
|
||||
valErrs := opts.Validate()
|
||||
if len(valErrs) != 0 {
|
||||
log.Panicf(
|
||||
"the following validation errors occurred: %+v",
|
||||
valErrs,
|
||||
)
|
||||
}
|
||||
|
||||
// Set `repo` from package managers.
|
||||
pkgResp, err := fetchGitRepositoryFromPackageManagers(flagNPM, flagPyPI, flagRubyGems)
|
||||
pkgResp, err := fetchGitRepositoryFromPackageManagers(opts.NPM, opts.PyPI, opts.RubyGems)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
if pkgResp.exists {
|
||||
if err := cmd.Flags().Set("repo", pkgResp.associatedRepo); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
opts.Repo = pkgResp.associatedRepo
|
||||
}
|
||||
|
||||
policy, err := readPolicy()
|
||||
pol, err := policy.ParseFromFile(opts.PolicyFile)
|
||||
if err != nil {
|
||||
log.Panicf("readPolicy: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
logger := sclog.NewLogger(sclog.ParseLevel(flagLogLevel))
|
||||
repoURI, repoClient, ossFuzzRepoClient, ciiClient, vulnsClient, err := getRepoAccessors(
|
||||
ctx, flagRepo, flagLocal, logger)
|
||||
logger := sclog.NewLogger(sclog.ParseLevel(opts.LogLevel))
|
||||
repoURI, repoClient, ossFuzzRepoClient, ciiClient, vulnsClient, err := checker.GetClients(
|
||||
ctx, opts.Repo, opts.Local, logger)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
@ -145,279 +150,58 @@ func scorecardCmd(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
var requiredRequestTypes []checker.RequestType
|
||||
if flagLocal != "" {
|
||||
if opts.Local != "" {
|
||||
requiredRequestTypes = append(requiredRequestTypes, checker.FileBased)
|
||||
}
|
||||
if !strings.EqualFold(flagCommit, clients.HeadSHA) {
|
||||
if !strings.EqualFold(opts.Commit, clients.HeadSHA) {
|
||||
requiredRequestTypes = append(requiredRequestTypes, checker.CommitBased)
|
||||
}
|
||||
enabledChecks, err := getEnabledChecks(policy, flagChecksToRun, requiredRequestTypes)
|
||||
enabledChecks, err := policy.GetEnabled(pol, opts.ChecksToRun, requiredRequestTypes)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
if flagFormat == formatDefault {
|
||||
if opts.Format == options.FormatDefault {
|
||||
for checkName := range enabledChecks {
|
||||
fmt.Fprintf(os.Stderr, "Starting [%s]\n", checkName)
|
||||
}
|
||||
}
|
||||
|
||||
repoResult, err := pkg.RunScorecards(ctx, repoURI, flagCommit, flagFormat == formatRaw, enabledChecks, repoClient,
|
||||
ossFuzzRepoClient, ciiClient, vulnsClient)
|
||||
repoResult, err := pkg.RunScorecards(
|
||||
ctx,
|
||||
repoURI,
|
||||
opts.Commit,
|
||||
opts.Format == options.FormatRaw,
|
||||
enabledChecks,
|
||||
repoClient,
|
||||
ossFuzzRepoClient,
|
||||
ciiClient,
|
||||
vulnsClient,
|
||||
)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
repoResult.Metadata = append(repoResult.Metadata, flagMetadata...)
|
||||
repoResult.Metadata = append(repoResult.Metadata, opts.Metadata...)
|
||||
|
||||
// Sort them by name
|
||||
sort.Slice(repoResult.Checks, func(i, j int) bool {
|
||||
return repoResult.Checks[i].Name < repoResult.Checks[j].Name
|
||||
})
|
||||
|
||||
if flagFormat == formatDefault {
|
||||
if opts.Format == options.FormatDefault {
|
||||
for checkName := range enabledChecks {
|
||||
fmt.Fprintf(os.Stderr, "Finished [%s]\n", checkName)
|
||||
}
|
||||
fmt.Println("\nRESULTS\n-------")
|
||||
}
|
||||
|
||||
switch flagFormat {
|
||||
case formatDefault:
|
||||
err = repoResult.AsString(flagShowDetails, sclog.ParseLevel(flagLogLevel), checkDocs, os.Stdout)
|
||||
case formatSarif:
|
||||
// TODO: support config files and update checker.MaxResultScore.
|
||||
err = repoResult.AsSARIF(flagShowDetails, sclog.ParseLevel(flagLogLevel), os.Stdout, checkDocs, policy)
|
||||
case formatJSON:
|
||||
err = repoResult.AsJSON2(flagShowDetails, sclog.ParseLevel(flagLogLevel), checkDocs, os.Stdout)
|
||||
case formatRaw:
|
||||
err = repoResult.AsRawJSON(os.Stdout)
|
||||
default:
|
||||
err = sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("invalid format flag: %v. Expected [default, json]", flagFormat))
|
||||
}
|
||||
if err != nil {
|
||||
resultsErr := pkg.FormatResults(
|
||||
opts,
|
||||
&repoResult,
|
||||
checkDocs,
|
||||
pol,
|
||||
)
|
||||
if resultsErr != nil {
|
||||
log.Panicf("Failed to output results: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func validateCmdFlags() {
|
||||
// Validate exactly one of `--repo`, `--npm`, `--pypi`, `--rubygems`, `--local` is enabled.
|
||||
if boolSum(flagRepo != "",
|
||||
flagNPM != "",
|
||||
flagPyPI != "",
|
||||
flagRubyGems != "",
|
||||
flagLocal != "") != 1 {
|
||||
log.Panic("Exactly one of `--repo`, `--npm`, `--pypi`, `--rubygems` or `--local` must be set")
|
||||
}
|
||||
|
||||
// Validate SARIF features are flag-guarded.
|
||||
if !isSarifEnabled() {
|
||||
if flagFormat == formatSarif {
|
||||
log.Panic("sarif format not supported yet")
|
||||
}
|
||||
if flagPolicyFile != "" {
|
||||
log.Panic("policy file not supported yet")
|
||||
}
|
||||
}
|
||||
|
||||
// Validate V6 features are flag-guarded.
|
||||
if !isV6Enabled() {
|
||||
if flagFormat == formatRaw {
|
||||
log.Panic("raw option not supported yet")
|
||||
}
|
||||
if flagCommit != clients.HeadSHA {
|
||||
log.Panic("--commit option not supported yet")
|
||||
}
|
||||
}
|
||||
|
||||
// Validate format.
|
||||
if !validateFormat(flagFormat) {
|
||||
log.Panicf("unsupported format '%s'", flagFormat)
|
||||
}
|
||||
|
||||
// Validate `commit` is non-empty.
|
||||
if flagCommit == "" {
|
||||
log.Panic("commit should be non-empty")
|
||||
}
|
||||
}
|
||||
|
||||
func boolSum(bools ...bool) int {
|
||||
sum := 0
|
||||
for _, b := range bools {
|
||||
if b {
|
||||
sum++
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func isSarifEnabled() bool {
|
||||
// UPGRADEv4: remove.
|
||||
var sarifEnabled bool
|
||||
_, sarifEnabled = os.LookupEnv(cliEnableSarif)
|
||||
return sarifEnabled
|
||||
}
|
||||
|
||||
func isV6Enabled() bool {
|
||||
var v6 bool
|
||||
_, v6 = os.LookupEnv("SCORECARD_V6")
|
||||
return v6
|
||||
}
|
||||
|
||||
func validateFormat(format string) bool {
|
||||
switch format {
|
||||
case formatJSON, formatSarif, formatDefault, formatRaw:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func readPolicy() (*spol.ScorecardPolicy, error) {
|
||||
if flagPolicyFile != "" {
|
||||
data, err := os.ReadFile(flagPolicyFile)
|
||||
if err != nil {
|
||||
return nil, sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("os.ReadFile: %v", err))
|
||||
}
|
||||
sp, err := spol.ParseFromYAML(data)
|
||||
if err != nil {
|
||||
return nil,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("spol.ParseFromYAML: %v", err))
|
||||
}
|
||||
return sp, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checksHavePolicies(sp *spol.ScorecardPolicy, enabledChecks checker.CheckNameToFnMap) bool {
|
||||
for checkName := range enabledChecks {
|
||||
_, exists := sp.Policies[checkName]
|
||||
if !exists {
|
||||
log.Printf("check %s has no policy declared", checkName)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isSupportedCheck(checkName string, requiredRequestTypes []checker.RequestType) bool {
|
||||
unsupported := checker.ListUnsupported(
|
||||
requiredRequestTypes,
|
||||
checks.AllChecks[checkName].SupportedRequestTypes)
|
||||
return len(unsupported) == 0
|
||||
}
|
||||
|
||||
func getAllChecks() checker.CheckNameToFnMap {
|
||||
// Returns the full list of checks, given any environment variable constraints.
|
||||
possibleChecks := checks.AllChecks
|
||||
return possibleChecks
|
||||
}
|
||||
|
||||
func getEnabledChecks(sp *spol.ScorecardPolicy, argsChecks []string,
|
||||
requiredRequestTypes []checker.RequestType) (checker.CheckNameToFnMap, error) {
|
||||
enabledChecks := checker.CheckNameToFnMap{}
|
||||
|
||||
switch {
|
||||
case len(argsChecks) != 0:
|
||||
// Populate checks to run with the `--repo` CLI argument.
|
||||
for _, checkName := range argsChecks {
|
||||
if !isSupportedCheck(checkName, requiredRequestTypes) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("Unsupported RequestType %s by check: %s",
|
||||
fmt.Sprint(requiredRequestTypes), checkName))
|
||||
}
|
||||
if !enableCheck(checkName, &enabledChecks) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
|
||||
}
|
||||
}
|
||||
case sp != nil:
|
||||
// Populate checks to run with policy file.
|
||||
for checkName := range sp.GetPolicies() {
|
||||
if !isSupportedCheck(checkName, requiredRequestTypes) {
|
||||
// We silently ignore the check, like we do
|
||||
// for the default case when no argsChecks
|
||||
// or policy are present.
|
||||
continue
|
||||
}
|
||||
|
||||
if !enableCheck(checkName, &enabledChecks) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Enable all checks that are supported.
|
||||
for checkName := range getAllChecks() {
|
||||
if !isSupportedCheck(checkName, requiredRequestTypes) {
|
||||
continue
|
||||
}
|
||||
if !enableCheck(checkName, &enabledChecks) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a policy was passed as argument, ensure all checks
|
||||
// to run have a corresponding policy.
|
||||
if sp != nil && !checksHavePolicies(sp, enabledChecks) {
|
||||
return enabledChecks, sce.WithMessage(sce.ErrScorecardInternal, "checks don't have policies")
|
||||
}
|
||||
|
||||
return enabledChecks, nil
|
||||
}
|
||||
|
||||
func getRepoAccessors(ctx context.Context, repoURI, localURI string, logger *sclog.Logger) (
|
||||
clients.Repo, // repo
|
||||
clients.RepoClient, // repoClient
|
||||
clients.RepoClient, // ossFuzzClient
|
||||
clients.CIIBestPracticesClient, // ciiClient
|
||||
clients.VulnerabilitiesClient, // vulnClient
|
||||
error) {
|
||||
var githubRepo clients.Repo
|
||||
var errGitHub error
|
||||
if localURI != "" {
|
||||
localRepo, errLocal := localdir.MakeLocalDirRepo(localURI)
|
||||
return localRepo, /*repo*/
|
||||
localdir.CreateLocalDirClient(ctx, logger), /*repoClient*/
|
||||
nil, /*ossFuzzClient*/
|
||||
nil, /*ciiClient*/
|
||||
nil, /*vulnClient*/
|
||||
errLocal
|
||||
}
|
||||
|
||||
githubRepo, errGitHub = githubrepo.MakeGithubRepo(repoURI)
|
||||
if errGitHub != nil {
|
||||
// nolint: wrapcheck
|
||||
return githubRepo,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
errGitHub
|
||||
}
|
||||
|
||||
ossFuzzRepoClient, errOssFuzz := githubrepo.CreateOssFuzzRepoClient(ctx, logger)
|
||||
return githubRepo, /*repo*/
|
||||
githubrepo.CreateGithubRepoClient(ctx, logger), /*repoClient*/
|
||||
ossFuzzRepoClient, /*ossFuzzClient*/
|
||||
clients.DefaultCIIBestPracticesClient(), /*ciiClient*/
|
||||
clients.DefaultVulnerabilitiesClient(), /*vulnClient*/
|
||||
errOssFuzz
|
||||
}
|
||||
|
||||
// Enables checks by name.
|
||||
func enableCheck(checkName string, enabledChecks *checker.CheckNameToFnMap) bool {
|
||||
if enabledChecks != nil {
|
||||
for key, checkFn := range getAllChecks() {
|
||||
if strings.EqualFold(key, checkName) {
|
||||
(*enabledChecks)[key] = checkFn
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ var serveCmd = &cobra.Command{
|
||||
Short: "Serve the scorecard program over http",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logger := log.NewLogger(log.ParseLevel(flagLogLevel))
|
||||
logger := log.NewLogger(log.ParseLevel(opts.LogLevel))
|
||||
|
||||
t, err := template.New("webpage").Parse(tpl)
|
||||
if err != nil {
|
||||
@ -79,7 +79,7 @@ var serveCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if r.Header.Get("Content-Type") == "application/json" {
|
||||
if err := repoResult.AsJSON(flagShowDetails, log.ParseLevel(flagLogLevel), rw); err != nil {
|
||||
if err := repoResult.AsJSON(opts.ShowDetails, log.ParseLevel(opts.LogLevel), rw); err != nil {
|
||||
// TODO(log): Improve error message
|
||||
logger.Error(err, "")
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
|
187
options/options.go
Normal file
187
options/options.go
Normal file
@ -0,0 +1,187 @@
|
||||
// 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 options implements Scorecard options.
|
||||
package options
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/ossf/scorecard/v4/clients"
|
||||
"github.com/ossf/scorecard/v4/log"
|
||||
)
|
||||
|
||||
// Options define common options for configuring scorecard.
|
||||
type Options struct {
|
||||
Repo string
|
||||
Local string
|
||||
Commit string
|
||||
LogLevel string
|
||||
Format string
|
||||
NPM string
|
||||
PyPI string
|
||||
RubyGems string
|
||||
PolicyFile string
|
||||
ChecksToRun []string
|
||||
Metadata []string
|
||||
ShowDetails bool
|
||||
}
|
||||
|
||||
// New creates a new instance of `Options`.
|
||||
func New() *Options {
|
||||
return &Options{}
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultCommit specifies the default commit reference to use.
|
||||
DefaultCommit = clients.HeadSHA
|
||||
|
||||
// Formats.
|
||||
|
||||
// FormatJSON specifies that results should be output in JSON format.
|
||||
FormatJSON = "json"
|
||||
// FormatSarif specifies that results should be output in SARIF format.
|
||||
FormatSarif = "sarif"
|
||||
// FormatDefault specifies that results should be output in default format.
|
||||
FormatDefault = "default"
|
||||
// FormatRaw specifies that results should be output in raw format.
|
||||
FormatRaw = "raw"
|
||||
|
||||
// Environment variables.
|
||||
|
||||
// EnvVarEnableSarif is the environment variable which controls enabling
|
||||
// SARIF logging.
|
||||
EnvVarEnableSarif = "ENABLE_SARIF"
|
||||
// EnvVarScorecardV6 is the environment variable which enables scorecard v6
|
||||
// options.
|
||||
EnvVarScorecardV6 = "SCORECARD_V6"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultLogLevel retrieves the default log level.
|
||||
DefaultLogLevel = log.DefaultLevel.String()
|
||||
|
||||
errCommitIsEmpty = errors.New("commit should be non-empty")
|
||||
errCommitOptionNotSupported = errors.New("commit option is not supported yet")
|
||||
errFormatNotSupported = errors.New("unsupported format")
|
||||
errPolicyFileNotSupported = errors.New("policy file is not supported yet")
|
||||
errRawOptionNotSupported = errors.New("raw option is not supported yet")
|
||||
errRepoOptionMustBeSet = errors.New(
|
||||
"exactly one of `repo`, `npm`, `pypi`, `rubygems` or `local` must be set",
|
||||
)
|
||||
errSARIFNotSupported = errors.New("SARIF format is not supported yet")
|
||||
)
|
||||
|
||||
// Validate validates scorecard configuration options.
|
||||
// TODO(options): Cleanup error messages.
|
||||
func (o *Options) Validate() []error {
|
||||
var errs []error
|
||||
|
||||
// Validate exactly one of `--repo`, `--npm`, `--pypi`, `--rubygems`, `--local` is enabled.
|
||||
if boolSum(o.Repo != "",
|
||||
o.NPM != "",
|
||||
o.PyPI != "",
|
||||
o.RubyGems != "",
|
||||
o.Local != "") != 1 {
|
||||
errs = append(
|
||||
errs,
|
||||
errRepoOptionMustBeSet,
|
||||
)
|
||||
}
|
||||
|
||||
// Validate SARIF features are flag-guarded.
|
||||
if !IsSarifEnabled() {
|
||||
if o.Format == FormatSarif {
|
||||
errs = append(
|
||||
errs,
|
||||
errSARIFNotSupported,
|
||||
)
|
||||
}
|
||||
if o.PolicyFile != "" {
|
||||
errs = append(
|
||||
errs,
|
||||
errPolicyFileNotSupported,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate V6 features are flag-guarded.
|
||||
if !isV6Enabled() {
|
||||
if o.Format == FormatRaw {
|
||||
errs = append(
|
||||
errs,
|
||||
errRawOptionNotSupported,
|
||||
)
|
||||
}
|
||||
if o.Commit != clients.HeadSHA {
|
||||
errs = append(
|
||||
errs,
|
||||
errCommitOptionNotSupported,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate format.
|
||||
if !validateFormat(o.Format) {
|
||||
errs = append(
|
||||
errs,
|
||||
errFormatNotSupported,
|
||||
)
|
||||
}
|
||||
|
||||
// Validate `commit` is non-empty.
|
||||
if o.Commit == "" {
|
||||
errs = append(
|
||||
errs,
|
||||
errCommitIsEmpty,
|
||||
)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func boolSum(bools ...bool) int {
|
||||
sum := 0
|
||||
for _, b := range bools {
|
||||
if b {
|
||||
sum++
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// IsSarifEnabled returns true if `EnvVarEnableSarif` is specified.
|
||||
// TODO(options): This probably doesn't need to be exported.
|
||||
func IsSarifEnabled() bool {
|
||||
// UPGRADEv4: remove.
|
||||
var sarifEnabled bool
|
||||
_, sarifEnabled = os.LookupEnv(EnvVarEnableSarif)
|
||||
return sarifEnabled
|
||||
}
|
||||
|
||||
func isV6Enabled() bool {
|
||||
var v6 bool
|
||||
_, v6 = os.LookupEnv(EnvVarScorecardV6)
|
||||
return v6
|
||||
}
|
||||
|
||||
func validateFormat(format string) bool {
|
||||
switch format {
|
||||
case FormatJSON, FormatSarif, FormatDefault, FormatRaw:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
@ -48,11 +48,12 @@ func runEnabledChecks(ctx context.Context,
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
runner := checker.Runner{
|
||||
Repo: repo.URI(),
|
||||
CheckName: checkName,
|
||||
CheckRequest: request,
|
||||
}
|
||||
runner := checker.NewRunner(
|
||||
checkName,
|
||||
repo.URI(),
|
||||
&request,
|
||||
)
|
||||
|
||||
resultsCh <- runner.Run(ctx, checkFn)
|
||||
}()
|
||||
}
|
||||
|
@ -23,9 +23,11 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/ossf/scorecard/v4/checker"
|
||||
docs "github.com/ossf/scorecard/v4/docs/checks"
|
||||
"github.com/ossf/scorecard/v4/docs/checks"
|
||||
sce "github.com/ossf/scorecard/v4/errors"
|
||||
"github.com/ossf/scorecard/v4/log"
|
||||
"github.com/ossf/scorecard/v4/options"
|
||||
spol "github.com/ossf/scorecard/v4/policy"
|
||||
)
|
||||
|
||||
// ScorecardInfo contains information about the scorecard code that was run.
|
||||
@ -58,7 +60,7 @@ func scoreToString(s float64) string {
|
||||
}
|
||||
|
||||
// GetAggregateScore returns the aggregate score.
|
||||
func (r *ScorecardResult) GetAggregateScore(checkDocs docs.Doc) (float64, error) {
|
||||
func (r *ScorecardResult) GetAggregateScore(checkDocs checks.Doc) (float64, error) {
|
||||
// TODO: calculate the score and make it a field
|
||||
// of ScorecardResult
|
||||
weights := map[string]float64{"Critical": 10, "High": 7.5, "Medium": 5, "Low": 2.5}
|
||||
@ -97,9 +99,45 @@ func (r *ScorecardResult) GetAggregateScore(checkDocs docs.Doc) (float64, error)
|
||||
return score / total, nil
|
||||
}
|
||||
|
||||
// FormatResults formats scorecard results.
|
||||
func FormatResults(
|
||||
opts *options.Options,
|
||||
results *ScorecardResult,
|
||||
doc checks.Doc,
|
||||
policy *spol.ScorecardPolicy,
|
||||
) error {
|
||||
var err error
|
||||
|
||||
switch opts.Format {
|
||||
case options.FormatDefault:
|
||||
err = results.AsString(opts.ShowDetails, log.ParseLevel(opts.LogLevel), doc, os.Stdout)
|
||||
case options.FormatSarif:
|
||||
// TODO: support config files and update checker.MaxResultScore.
|
||||
err = results.AsSARIF(opts.ShowDetails, log.ParseLevel(opts.LogLevel), os.Stdout, doc, policy)
|
||||
case options.FormatJSON:
|
||||
err = results.AsJSON2(opts.ShowDetails, log.ParseLevel(opts.LogLevel), doc, os.Stdout)
|
||||
case options.FormatRaw:
|
||||
err = results.AsRawJSON(os.Stdout)
|
||||
default:
|
||||
err = sce.WithMessage(
|
||||
sce.ErrScorecardInternal,
|
||||
fmt.Sprintf(
|
||||
"invalid format flag: %v. Expected [default, json]",
|
||||
opts.Format,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to output results: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsString returns ScorecardResult in string format.
|
||||
func (r *ScorecardResult) AsString(showDetails bool, logLevel log.Level,
|
||||
checkDocs docs.Doc, writer io.Writer) error {
|
||||
checkDocs checks.Doc, writer io.Writer) error {
|
||||
data := make([][]string, len(r.Checks))
|
||||
//nolint
|
||||
for i, row := range r.Checks {
|
||||
|
121
policy/policy.go
121
policy/policy.go
@ -17,9 +17,13 @@ package policy
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/ossf/scorecard/v4/checker"
|
||||
"github.com/ossf/scorecard/v4/checks"
|
||||
sce "github.com/ossf/scorecard/v4/errors"
|
||||
)
|
||||
@ -62,9 +66,29 @@ func modeToProto(m string) CheckPolicy_Mode {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseFromYAML parses a policy file and returns
|
||||
// a scorecardPolicy.
|
||||
func ParseFromYAML(b []byte) (*ScorecardPolicy, error) {
|
||||
// ParseFromFile takes a policy file and returns a `ScorecardPolicy`.
|
||||
func ParseFromFile(policyFile string) (*ScorecardPolicy, error) {
|
||||
if policyFile != "" {
|
||||
data, err := os.ReadFile(policyFile)
|
||||
if err != nil {
|
||||
return nil, sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("os.ReadFile: %v", err))
|
||||
}
|
||||
|
||||
sp, err := parseFromYAML(data)
|
||||
if err != nil {
|
||||
return nil,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("spol.ParseFromYAML: %v", err))
|
||||
}
|
||||
|
||||
return sp, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// parseFromYAML parses a policy file and returns a `ScorecardPolicy`.
|
||||
func parseFromYAML(b []byte) (*ScorecardPolicy, error) {
|
||||
// Internal golang for unmarshalling the policy file.
|
||||
sp := scorecardPolicy{}
|
||||
// Protobuf-defined policy (policy.proto and policy.pb.go).
|
||||
@ -112,3 +136,94 @@ func ParseFromYAML(b []byte) (*ScorecardPolicy, error) {
|
||||
|
||||
return &retPolicy, nil
|
||||
}
|
||||
|
||||
// GetEnabled returns the list of enabled checks.
|
||||
func GetEnabled(
|
||||
sp *ScorecardPolicy,
|
||||
argsChecks []string,
|
||||
requiredRequestTypes []checker.RequestType,
|
||||
) (checker.CheckNameToFnMap, error) {
|
||||
enabledChecks := checker.CheckNameToFnMap{}
|
||||
|
||||
switch {
|
||||
case len(argsChecks) != 0:
|
||||
// Populate checks to run with the `--repo` CLI argument.
|
||||
for _, checkName := range argsChecks {
|
||||
if !isSupportedCheck(checkName, requiredRequestTypes) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("Unsupported RequestType %s by check: %s",
|
||||
fmt.Sprint(requiredRequestTypes), checkName))
|
||||
}
|
||||
if !enableCheck(checkName, &enabledChecks) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
|
||||
}
|
||||
}
|
||||
case sp != nil:
|
||||
// Populate checks to run with policy file.
|
||||
for checkName := range sp.GetPolicies() {
|
||||
if !isSupportedCheck(checkName, requiredRequestTypes) {
|
||||
// We silently ignore the check, like we do
|
||||
// for the default case when no argsChecks
|
||||
// or policy are present.
|
||||
continue
|
||||
}
|
||||
|
||||
if !enableCheck(checkName, &enabledChecks) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Enable all checks that are supported.
|
||||
for checkName := range checks.GetAll() {
|
||||
if !isSupportedCheck(checkName, requiredRequestTypes) {
|
||||
continue
|
||||
}
|
||||
if !enableCheck(checkName, &enabledChecks) {
|
||||
return enabledChecks,
|
||||
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a policy was passed as argument, ensure all checks
|
||||
// to run have a corresponding policy.
|
||||
if sp != nil && !checksHavePolicies(sp, enabledChecks) {
|
||||
return enabledChecks, sce.WithMessage(sce.ErrScorecardInternal, "checks don't have policies")
|
||||
}
|
||||
|
||||
return enabledChecks, nil
|
||||
}
|
||||
|
||||
func checksHavePolicies(sp *ScorecardPolicy, enabledChecks checker.CheckNameToFnMap) bool {
|
||||
for checkName := range enabledChecks {
|
||||
_, exists := sp.Policies[checkName]
|
||||
if !exists {
|
||||
log.Printf("check %s has no policy declared", checkName)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isSupportedCheck(checkName string, requiredRequestTypes []checker.RequestType) bool {
|
||||
unsupported := checker.ListUnsupported(
|
||||
requiredRequestTypes,
|
||||
checks.AllChecks[checkName].SupportedRequestTypes)
|
||||
return len(unsupported) == 0
|
||||
}
|
||||
|
||||
// Enables checks by name.
|
||||
func enableCheck(checkName string, enabledChecks *checker.CheckNameToFnMap) bool {
|
||||
if enabledChecks != nil {
|
||||
for key, checkFn := range checks.GetAll() {
|
||||
if strings.EqualFold(key, checkName) {
|
||||
(*enabledChecks)[key] = checkFn
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ func TestPolicyRead(t *testing.T) {
|
||||
t.Fatalf("cannot read file: %v", err)
|
||||
}
|
||||
|
||||
p, err := ParseFromYAML(content)
|
||||
p, err := parseFromYAML(content)
|
||||
|
||||
if !errors.Is(err, tt.err) {
|
||||
t.Fatalf("%s: expected %v, got %v", tt.name, tt.err, err)
|
||||
|
Loading…
Reference in New Issue
Block a user