scorecard/options/options.go
Spencer Schrock 2d9319601e
🌱 use forbidigo linter to prevent print statements (#3585)
* enable forbidigo for print statements.

include reasoning as message exposed to developer.

Signed-off-by: Spencer Schrock <sschrock@google.com>

* remove or grant exceptions for existing print statements

Signed-off-by: Spencer Schrock <sschrock@google.com>

* swap stdout to stderr

Signed-off-by: Spencer Schrock <sschrock@google.com>

* separate msg from regex for better readability.

Signed-off-by: Spencer Schrock <sschrock@google.com>

---------

Signed-off-by: Spencer Schrock <sschrock@google.com>
2023-10-23 13:12:50 -07:00

274 lines
7.2 KiB
Go

// Copyright 2020 OpenSSF 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"
"fmt"
"log"
"os"
"strings"
"github.com/caarlos0/env/v6"
"github.com/ossf/scorecard/v4/clients"
sclog "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
Nuget string
PolicyFile string
ResultsFile string
ChecksToRun []string
Metadata []string
CommitDepth int
ShowDetails bool
// Feature flags.
EnableSarif bool `env:"ENABLE_SARIF"`
EnableScorecardV6 bool `env:"SCORECARD_V6"`
EnableScorecardExperimental bool `env:"SCORECARD_EXPERIMENTAL"`
}
// New creates a new instance of `Options`.
func New() *Options {
opts := &Options{}
if err := env.Parse(opts); err != nil {
log.Printf("could not parse env vars, using default options: %v", err)
}
// Defaulting.
// TODO(options): Consider moving this to a separate function/method.
if opts.Commit == "" {
opts.Commit = DefaultCommit
}
if opts.Format == "" {
opts.Format = FormatDefault
}
if opts.LogLevel == "" {
opts.LogLevel = DefaultLogLevel
}
return opts
}
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"
// FormatFJSON specifies that results should be output in JSON format,
// but with structured findings.
FormatFJSON = "finding"
// FormatPJSON specifies that results should be output in probe JSON format.
FormatPJSON = "probe"
// FormatSJSON specifies that results should be output in structured JSON format.
FormatSJSON = "structured"
// 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"
// EnvVarScorecardExperimental is the environment variable which enables experimental
// features.
EnvVarScorecardExperimental = "SCORECARD_EXPERIMENTAL"
)
var (
// DefaultLogLevel retrieves the default log level.
DefaultLogLevel = sclog.DefaultLevel.String()
errCommitIsEmpty = errors.New("commit should be non-empty")
errFormatNotSupported = errors.New("unsupported format")
errFormatSupportedWithExperimental = errors.New("format supported only with SCORECARD_EXPERIMENTAL=1")
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`, `nuget` or `local` must be set",
)
errSARIFNotSupported = errors.New("SARIF format is not supported yet")
errValidate = errors.New("some options could not be validated")
)
// 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`, `--nuget`, `--local` is enabled.
if boolSum(o.Repo != "",
o.NPM != "",
o.PyPI != "",
o.RubyGems != "",
o.Nuget != "",
o.Local != "") != 1 {
errs = append(
errs,
errRepoOptionMustBeSet,
)
}
// Validate SARIF features are flag-guarded.
if !o.isSarifEnabled() {
if o.Format == FormatSarif {
errs = append(
errs,
errSARIFNotSupported,
)
}
if o.PolicyFile != "" {
errs = append(
errs,
errPolicyFileNotSupported,
)
}
}
// Validate V6 features are flag-guarded.
if !o.isV6Enabled() {
if o.Format == FormatRaw {
errs = append(
errs,
errRawOptionNotSupported,
)
}
}
if !o.isExperimentalEnabled() {
if o.Format == FormatSJSON ||
o.Format == FormatFJSON ||
o.Format == FormatPJSON {
errs = append(
errs,
errFormatSupportedWithExperimental,
)
}
}
// Validate format.
if !validateFormat(o.Format) {
errs = append(
errs,
errFormatNotSupported,
)
}
// Validate `commit` is non-empty.
if o.Commit == "" {
errs = append(
errs,
errCommitIsEmpty,
)
}
if len(errs) != 0 {
return fmt.Errorf(
"%w: %+v",
errValidate,
errs,
)
}
return nil
}
func boolSum(bools ...bool) int {
sum := 0
for _, b := range bools {
if b {
sum++
}
}
return sum
}
// Feature flags.
// GitHub integration support.
// See https://github.com/ossf/scorecard-action/issues/1107.
// NOTE: We don't add a field to to the Option structure to simplify
// integration. If we did, the Action would also need to be aware
// of the integration and pass the relevant values. This
// would add redundancy and complicate maintenance.
func (o *Options) IsInternalGitHubIntegrationEnabled() bool {
return (os.Getenv("CI") == "true") &&
(os.Getenv("SCORECARD_INTERNAL_GITHUB_INTEGRATION") == "1") &&
(os.Getenv("GITHUB_EVENT_NAME") == "dynamic")
}
// Checks returns the list of checks and honours the
// GitHub integration.
func (o *Options) Checks() []string {
if o.IsInternalGitHubIntegrationEnabled() {
// Overwrite the list of checks.
s := os.Getenv("SCORECARD_INTERNAL_GITHUB_CHECKS")
l := strings.Split(s, ",")
for i := range l {
l[i] = strings.TrimSpace(l[i])
}
return l
}
return o.ChecksToRun
}
// isExperimentalEnabled returns true if experimental features were enabled via
// environment variable.
func (o *Options) isExperimentalEnabled() bool {
value, _ := os.LookupEnv(EnvVarScorecardExperimental)
return value == "1"
}
// isSarifEnabled returns true if SARIF format was specified in options or via
// environment variable.
func (o *Options) isSarifEnabled() bool {
// UPGRADEv4: remove.
_, enabled := os.LookupEnv(EnvVarEnableSarif)
return o.EnableSarif || enabled
}
// isV6Enabled returns true if v6 functionality was specified in options or via
// environment variable.
func (o *Options) isV6Enabled() bool {
_, enabled := os.LookupEnv(EnvVarScorecardV6)
return o.EnableScorecardV6 || enabled
}
func validateFormat(format string) bool {
switch format {
case FormatJSON, FormatSJSON, FormatFJSON,
FormatPJSON, FormatSarif, FormatDefault, FormatRaw:
return true
default:
return false
}
}