mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-17 11:57:12 +03:00
Refactor to improve readability (#1394)
Co-authored-by: Azeem Shaikh <azeems@google.com>
This commit is contained in:
parent
bbbca2bd87
commit
ecc96576f4
@ -20,32 +20,33 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// UPGRADEv2: to remove.
|
||||
const (
|
||||
MaxResultConfidence = 10
|
||||
// MaxResultConfidence implies full certainty about a check result.
|
||||
// TODO(#1393): remove after deprecation.
|
||||
MaxResultConfidence = 10
|
||||
// HalfResultConfidence signifies uncertainty about a check's score.
|
||||
// TODO(#1393): remove after deprecation.
|
||||
HalfResultConfidence = 5
|
||||
MinResultConfidence = 0
|
||||
)
|
||||
// MinResultConfidence signifies no confidence in the check result.
|
||||
// TODO(#1393): remove after deprecation.
|
||||
MinResultConfidence = 0
|
||||
// TODO(#1393): remove after deprecation.
|
||||
migrationThresholdPassValue = 8
|
||||
|
||||
// UPGRADEv2: to remove.
|
||||
const migrationThresholdPassValue = 8
|
||||
// MaxResultScore is the best score that can be given by a check.
|
||||
MaxResultScore = 10
|
||||
// MinResultScore is the worst score that can be given by a check.
|
||||
MinResultScore = 0
|
||||
// InconclusiveResultScore is returned when no reliable information can be retrieved by a check.
|
||||
InconclusiveResultScore = -1
|
||||
|
||||
// DetailType is the type of details.
|
||||
type DetailType int
|
||||
|
||||
const (
|
||||
// DetailInfo is info-level log.
|
||||
DetailInfo DetailType = iota
|
||||
// DetailWarn is warn log.
|
||||
DetailWarn
|
||||
// DetailDebug is debug log.
|
||||
DetailDebug
|
||||
)
|
||||
|
||||
// FileType is the type of a file.
|
||||
type FileType int
|
||||
|
||||
const (
|
||||
// FileTypeNone is a default, not defined.
|
||||
FileTypeNone FileType = iota
|
||||
// FileTypeSource is for source code files.
|
||||
@ -56,55 +57,16 @@ const (
|
||||
FileTypeText
|
||||
// FileTypeURL for URLs.
|
||||
FileTypeURL
|
||||
)
|
||||
|
||||
// OffsetDefault is used if we can't determine the offset, for example when referencing a file but not a
|
||||
// specific location in the file.
|
||||
const OffsetDefault = 1
|
||||
|
||||
// LogMessage is a structure that encapsulates detail's information.
|
||||
// This allows updating the definition easily.
|
||||
//nolint
|
||||
type LogMessage struct {
|
||||
Text string // A short string explaining why the detail was recorded/logged.
|
||||
Path string // Fullpath to the file.
|
||||
Type FileType // Type of file.
|
||||
Offset int // Offset in the file of Path (line for source/text files).
|
||||
Snippet string // Snippet of code
|
||||
// UPGRADEv3: to remove.
|
||||
Version int // `3` to indicate the detail was logged using new structure.
|
||||
}
|
||||
|
||||
// CheckDetail contains information for each detail.
|
||||
type CheckDetail struct {
|
||||
Msg LogMessage
|
||||
Type DetailType // Any of DetailWarn, DetailInfo, DetailDebug.
|
||||
}
|
||||
|
||||
// DetailLogger logs a CheckDetail struct.
|
||||
type DetailLogger interface {
|
||||
Info(desc string, args ...interface{})
|
||||
Warn(desc string, args ...interface{})
|
||||
Debug(desc string, args ...interface{})
|
||||
|
||||
// Functions to use for moving to SARIF format.
|
||||
// UPGRADEv3: to rename.
|
||||
Info3(msg *LogMessage)
|
||||
Warn3(msg *LogMessage)
|
||||
Debug3(msg *LogMessage)
|
||||
}
|
||||
|
||||
//nolint
|
||||
const (
|
||||
MaxResultScore = 10
|
||||
MinResultScore = 0
|
||||
InconclusiveResultScore = -1
|
||||
// OffsetDefault is used if we can't determine the offset, for example when referencing a file but not a
|
||||
// specific location in the file.
|
||||
OffsetDefault = 1
|
||||
)
|
||||
|
||||
// CheckResult captures result from a check run.
|
||||
// nolint
|
||||
// nolint:govet
|
||||
type CheckResult struct {
|
||||
// Old structure
|
||||
// TODO(#1393): Remove old structure after deprecation.
|
||||
Error error `json:"-"`
|
||||
Name string
|
||||
Details []string
|
||||
@ -118,81 +80,32 @@ type CheckResult struct {
|
||||
Details2 []CheckDetail `json:"-"` // Details of tests and sub-checks
|
||||
Score int `json:"-"` // {[-1,0...10], -1 = Inconclusive}
|
||||
Reason string `json:"-"` // A sentence describing the check result (score, etc)
|
||||
|
||||
}
|
||||
|
||||
// ====== Raw results for checks =========.
|
||||
// CheckDetail contains information for each detail.
|
||||
type CheckDetail struct {
|
||||
Msg LogMessage
|
||||
Type DetailType // Any of DetailWarn, DetailInfo, DetailDebug.
|
||||
}
|
||||
|
||||
// File represents a file.
|
||||
type File struct {
|
||||
Path string
|
||||
Snippet string // Snippet of code
|
||||
Offset int // Offset in the file of Path (line for source/text files).
|
||||
// LogMessage is a structure that encapsulates detail's information.
|
||||
// This allows updating the definition easily.
|
||||
// nolint:govet
|
||||
type LogMessage struct {
|
||||
Text string // A short string explaining why the detail was recorded/logged.
|
||||
Path string // Fullpath to the file.
|
||||
Type FileType // Type of file.
|
||||
// TODO: add hash.
|
||||
Offset int // Offset in the file of Path (line for source/text files).
|
||||
Snippet string // Snippet of code
|
||||
// UPGRADEv3: to remove.
|
||||
Version int // `3` to indicate the detail was logged using new structure.
|
||||
}
|
||||
|
||||
// SecurityPolicyData contains the raw results
|
||||
// for the Security-Policy check.
|
||||
type SecurityPolicyData struct {
|
||||
// Files contains a list of files.
|
||||
Files []File
|
||||
}
|
||||
// DetailType is the type of details.
|
||||
type DetailType int
|
||||
|
||||
// Run represents a run.
|
||||
type Run struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., Result=["success", "failure"]
|
||||
}
|
||||
|
||||
// Issue represents an issue.
|
||||
type Issue struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., state=[opened|closed]
|
||||
}
|
||||
|
||||
// MergeRequest represents a merge request.
|
||||
type MergeRequest struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., State=["merged"|"closed"]
|
||||
}
|
||||
|
||||
// Tool represents a tool.
|
||||
type Tool struct {
|
||||
// Runs of the tool.
|
||||
Runs []Run
|
||||
// Issues created by the tool.
|
||||
Issues []Issue
|
||||
// Merges requests created by the tool.
|
||||
MergeRequests []MergeRequest
|
||||
Name string
|
||||
URL string
|
||||
Desc string
|
||||
ConfigFiles []File
|
||||
}
|
||||
|
||||
// BinaryArtifactData contains the raw results
|
||||
// for the Binary-Artifact check.
|
||||
type BinaryArtifactData struct {
|
||||
// Files contains a list of files.
|
||||
Files []File
|
||||
}
|
||||
|
||||
// DependencyUpdateToolData contains the raw results
|
||||
// for the Dependency-Update-Tool check.
|
||||
type DependencyUpdateToolData struct {
|
||||
// Tools contains a list of tools.
|
||||
// Note: we only populate one entry at most.
|
||||
Tools []Tool
|
||||
}
|
||||
|
||||
// RawResults contains results before a policy
|
||||
// is applied.
|
||||
type RawResults struct {
|
||||
BinaryArtifactResults BinaryArtifactData
|
||||
SecurityPolicyResults SecurityPolicyData
|
||||
DependencyUpdateToolResults DependencyUpdateToolData
|
||||
}
|
||||
// FileType is the type of a file.
|
||||
type FileType int
|
||||
|
||||
// CreateProportionalScore creates a proportional score.
|
||||
func CreateProportionalScore(success, total int) int {
|
||||
|
@ -42,55 +42,6 @@ type CheckFn func(*CheckRequest) CheckResult
|
||||
// CheckNameToFnMap defined here for convenience.
|
||||
type CheckNameToFnMap map[string]CheckFn
|
||||
|
||||
// UPGRADEv2: messages2 will ultimately
|
||||
// be renamed to messages.
|
||||
type logger struct {
|
||||
messages2 []CheckDetail
|
||||
}
|
||||
|
||||
func (l *logger) Info(desc string, args ...interface{}) {
|
||||
cd := CheckDetail{Type: DetailInfo, Msg: LogMessage{Text: fmt.Sprintf(desc, args...)}}
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Warn(desc string, args ...interface{}) {
|
||||
cd := CheckDetail{Type: DetailWarn, Msg: LogMessage{Text: fmt.Sprintf(desc, args...)}}
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Debug(desc string, args ...interface{}) {
|
||||
cd := CheckDetail{Type: DetailDebug, Msg: LogMessage{Text: fmt.Sprintf(desc, args...)}}
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
// UPGRADEv3: to rename.
|
||||
func (l *logger) Info3(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailInfo,
|
||||
Msg: *msg,
|
||||
}
|
||||
cd.Msg.Version = 3
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Warn3(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailWarn,
|
||||
Msg: *msg,
|
||||
}
|
||||
cd.Msg.Version = 3
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Debug3(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailDebug,
|
||||
Msg: *msg,
|
||||
}
|
||||
cd.Msg.Version = 3
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func logStats(ctx context.Context, startTime time.Time, result *CheckResult) error {
|
||||
runTimeInSecs := time.Now().Unix() - startTime.Unix()
|
||||
opencensusstats.Record(ctx, stats.CheckRuntimeInSec.M(runTimeInSecs))
|
||||
|
28
checker/detail_logger.go
Normal file
28
checker/detail_logger.go
Normal file
@ -0,0 +1,28 @@
|
||||
// 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 checker
|
||||
|
||||
// DetailLogger logs a CheckDetail struct.
|
||||
type DetailLogger interface {
|
||||
Info(desc string, args ...interface{})
|
||||
Warn(desc string, args ...interface{})
|
||||
Debug(desc string, args ...interface{})
|
||||
|
||||
// Functions to use for moving to SARIF format.
|
||||
// UPGRADEv3: to rename.
|
||||
Info3(msg *LogMessage)
|
||||
Warn3(msg *LogMessage)
|
||||
Debug3(msg *LogMessage)
|
||||
}
|
68
checker/detail_logger_impl.go
Normal file
68
checker/detail_logger_impl.go
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 checker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// UPGRADEv2: messages2 will ultimately
|
||||
// be renamed to messages.
|
||||
type logger struct {
|
||||
messages2 []CheckDetail
|
||||
}
|
||||
|
||||
func (l *logger) Info(desc string, args ...interface{}) {
|
||||
cd := CheckDetail{Type: DetailInfo, Msg: LogMessage{Text: fmt.Sprintf(desc, args...)}}
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Warn(desc string, args ...interface{}) {
|
||||
cd := CheckDetail{Type: DetailWarn, Msg: LogMessage{Text: fmt.Sprintf(desc, args...)}}
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Debug(desc string, args ...interface{}) {
|
||||
cd := CheckDetail{Type: DetailDebug, Msg: LogMessage{Text: fmt.Sprintf(desc, args...)}}
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
// UPGRADEv3: to rename.
|
||||
func (l *logger) Info3(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailInfo,
|
||||
Msg: *msg,
|
||||
}
|
||||
cd.Msg.Version = 3
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Warn3(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailWarn,
|
||||
Msg: *msg,
|
||||
}
|
||||
cd.Msg.Version = 3
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
||||
|
||||
func (l *logger) Debug3(msg *LogMessage) {
|
||||
cd := CheckDetail{
|
||||
Type: DetailDebug,
|
||||
Msg: *msg,
|
||||
}
|
||||
cd.Msg.Version = 3
|
||||
l.messages2 = append(l.messages2, cd)
|
||||
}
|
86
checker/raw_result.go
Normal file
86
checker/raw_result.go
Normal file
@ -0,0 +1,86 @@
|
||||
// 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 checker
|
||||
|
||||
// RawResults contains results before a policy
|
||||
// is applied.
|
||||
type RawResults struct {
|
||||
BinaryArtifactResults BinaryArtifactData
|
||||
SecurityPolicyResults SecurityPolicyData
|
||||
DependencyUpdateToolResults DependencyUpdateToolData
|
||||
}
|
||||
|
||||
// BinaryArtifactData contains the raw results
|
||||
// for the Binary-Artifact check.
|
||||
type BinaryArtifactData struct {
|
||||
// Files contains a list of files.
|
||||
Files []File
|
||||
}
|
||||
|
||||
// DependencyUpdateToolData contains the raw results
|
||||
// for the Dependency-Update-Tool check.
|
||||
type DependencyUpdateToolData struct {
|
||||
// Tools contains a list of tools.
|
||||
// Note: we only populate one entry at most.
|
||||
Tools []Tool
|
||||
}
|
||||
|
||||
// SecurityPolicyData contains the raw results
|
||||
// for the Security-Policy check.
|
||||
type SecurityPolicyData struct {
|
||||
// Files contains a list of files.
|
||||
Files []File
|
||||
}
|
||||
|
||||
// File represents a file.
|
||||
type File struct {
|
||||
Path string
|
||||
Snippet string // Snippet of code
|
||||
Offset int // Offset in the file of Path (line for source/text files).
|
||||
Type FileType // Type of file.
|
||||
// TODO: add hash.
|
||||
}
|
||||
|
||||
// Run represents a run.
|
||||
type Run struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., Result=["success", "failure"]
|
||||
}
|
||||
|
||||
// Issue represents an issue.
|
||||
type Issue struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., state=[opened|closed]
|
||||
}
|
||||
|
||||
// MergeRequest represents a merge request.
|
||||
type MergeRequest struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., State=["merged"|"closed"]
|
||||
}
|
||||
|
||||
// Tool represents a tool.
|
||||
type Tool struct {
|
||||
// Runs of the tool.
|
||||
Runs []Run
|
||||
// Issues created by the tool.
|
||||
Issues []Issue
|
||||
// Merges requests created by the tool.
|
||||
MergeRequests []MergeRequest
|
||||
Name string
|
||||
URL string
|
||||
Desc string
|
||||
ConfigFiles []File
|
||||
}
|
414
cmd/root.go
414
cmd/root.go
@ -55,22 +55,24 @@ var (
|
||||
rubygems string
|
||||
showDetails bool
|
||||
policyFile string
|
||||
rootCmd = &cobra.Command{
|
||||
Use: scorecardUse,
|
||||
Short: scorecardShort,
|
||||
Long: scorecardLong,
|
||||
Run: scorecardCmd,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
formatJSON = "json"
|
||||
formatSarif = "sarif"
|
||||
formatDefault = "default"
|
||||
)
|
||||
|
||||
// These strings must be the same as the ones used in
|
||||
// checks.yaml for the "repos" field.
|
||||
const (
|
||||
// These strings must be the same as the ones used in
|
||||
// checks.yaml for the "repos" field.
|
||||
repoTypeLocal = "local"
|
||||
repoTypeGitHub = "GitHub"
|
||||
)
|
||||
|
||||
const (
|
||||
scorecardLong = "A program that shows security scorecard for an open source software."
|
||||
scorecardUse = `./scorecard [--repo=<repo_url>] [--local=folder] [--checks=check1,...]
|
||||
[--show-details] [--policy=file] or ./scorecard --{npm,pypi,rubygems}=<package_name>
|
||||
@ -78,6 +80,207 @@ const (
|
||||
scorecardShort = "Security Scorecards"
|
||||
)
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
// Add the zap flag manually
|
||||
rootCmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)
|
||||
rootCmd.Flags().StringVar(&repo, "repo", "", "repository to check")
|
||||
rootCmd.Flags().StringVar(&local, "local", "", "local folder to check")
|
||||
rootCmd.Flags().StringVar(
|
||||
&npm, "npm", "",
|
||||
"npm package to check, given that the npm package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(
|
||||
&pypi, "pypi", "",
|
||||
"pypi package to check, given that the pypi package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(
|
||||
&rubygems, "rubygems", "",
|
||||
"rubygems package to check, given that the rubygems package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(&format, "format", formatDefault,
|
||||
"output format. allowed values are [default, sarif, json]")
|
||||
rootCmd.Flags().StringSliceVar(
|
||||
&metaData, "metadata", []string{}, "metadata for the project. It can be multiple separated by commas")
|
||||
rootCmd.Flags().BoolVar(&showDetails, "show-details", false, "show extra details about each check")
|
||||
checkNames := []string{}
|
||||
for checkName := range getAllChecks() {
|
||||
checkNames = append(checkNames, checkName)
|
||||
}
|
||||
rootCmd.Flags().StringSliceVar(&checksToRun, "checks", []string{},
|
||||
fmt.Sprintf("Checks to run. Possible values are: %s", strings.Join(checkNames, ",")))
|
||||
rootCmd.Flags().StringVar(&policyFile, "policy", "", "policy to enforce")
|
||||
|
||||
var v6 bool
|
||||
_, v6 = os.LookupEnv("SCORECARD_V6")
|
||||
if v6 {
|
||||
rootCmd.Flags().BoolVar(&raw, "raw", false, "generate raw results")
|
||||
}
|
||||
}
|
||||
|
||||
// Execute runs the Scorecard commandline.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: gocognit, gocyclo
|
||||
func scorecardCmd(cmd *cobra.Command, args []string) {
|
||||
// UPGRADEv4: remove.
|
||||
var v4 bool
|
||||
_, v4 = os.LookupEnv("SCORECARD_V4")
|
||||
|
||||
if format == formatSarif && !v4 {
|
||||
log.Panic("sarif not supported yet")
|
||||
}
|
||||
|
||||
if policyFile != "" && !v4 {
|
||||
log.Panic("policy not supported yet")
|
||||
}
|
||||
|
||||
if local != "" && !v4 {
|
||||
log.Panic("--local option not supported yet")
|
||||
}
|
||||
|
||||
var v6 bool
|
||||
_, v6 = os.LookupEnv("SCORECARD_V6")
|
||||
if raw && !v6 {
|
||||
log.Panic("--raw option not supported yet")
|
||||
}
|
||||
|
||||
// Validate format.
|
||||
if !validateFormat(format) {
|
||||
log.Panicf("unsupported format '%s'", format)
|
||||
}
|
||||
|
||||
policy, err := readPolicy()
|
||||
if err != nil {
|
||||
log.Panicf("readPolicy: %v", err)
|
||||
}
|
||||
|
||||
// Get the URI.
|
||||
uri, err := getURI(repo, local)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
// Set `repo` from package managers.
|
||||
exists, gitRepo, err := fetchGitRepositoryFromPackageManagers(npm, pypi, rubygems)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
if exists {
|
||||
if err := cmd.Flags().Set("repo", gitRepo); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check that `repo` is set.
|
||||
if err := cmd.MarkFlagRequired("repo"); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
logger, err := githubrepo.NewLogger(*logLevel)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
// nolint: errcheck
|
||||
defer logger.Sync() // Flushes buffer, if any.
|
||||
|
||||
repoURI, repoClient, ossFuzzRepoClient, ciiClient, repoType, err := getRepoAccessors(ctx, uri, logger)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer repoClient.Close()
|
||||
if ossFuzzRepoClient != nil {
|
||||
defer ossFuzzRepoClient.Close()
|
||||
}
|
||||
|
||||
// Read docs.
|
||||
checkDocs, err := docs.Read()
|
||||
if err != nil {
|
||||
log.Panicf("cannot read yaml file: %v", err)
|
||||
}
|
||||
|
||||
supportedChecks, err := getSupportedChecks(repoType, checkDocs)
|
||||
if err != nil {
|
||||
log.Panicf("cannot read supported checks: %v", err)
|
||||
}
|
||||
|
||||
enabledChecks, err := getEnabledChecks(policy, checksToRun, supportedChecks, repoType)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
if format == formatDefault {
|
||||
for checkName := range enabledChecks {
|
||||
fmt.Fprintf(os.Stderr, "Starting [%s]\n", checkName)
|
||||
}
|
||||
}
|
||||
|
||||
if raw && format != "json" {
|
||||
log.Panicf("only json format is supported")
|
||||
}
|
||||
|
||||
repoResult, err := pkg.RunScorecards(ctx, repoURI, raw, enabledChecks, repoClient, ossFuzzRepoClient, ciiClient)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
repoResult.Metadata = append(repoResult.Metadata, metaData...)
|
||||
|
||||
// Sort them by name
|
||||
sort.Slice(repoResult.Checks, func(i, j int) bool {
|
||||
return repoResult.Checks[i].Name < repoResult.Checks[j].Name
|
||||
})
|
||||
|
||||
if format == formatDefault {
|
||||
for checkName := range enabledChecks {
|
||||
fmt.Fprintf(os.Stderr, "Finished [%s]\n", checkName)
|
||||
}
|
||||
fmt.Println("\nRESULTS\n-------")
|
||||
}
|
||||
|
||||
switch format {
|
||||
case formatDefault:
|
||||
err = repoResult.AsString(showDetails, *logLevel, checkDocs, os.Stdout)
|
||||
case formatSarif:
|
||||
// TODO: support config files and update checker.MaxResultScore.
|
||||
err = repoResult.AsSARIF(showDetails, *logLevel, os.Stdout, checkDocs, policy)
|
||||
case formatJSON:
|
||||
if raw {
|
||||
err = repoResult.AsRawJSON(os.Stdout)
|
||||
} else {
|
||||
err = repoResult.AsJSON2(showDetails, *logLevel, checkDocs, os.Stdout)
|
||||
}
|
||||
|
||||
default:
|
||||
err = sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("invalid format flag: %v. Expected [default, json]", format))
|
||||
}
|
||||
if err != nil {
|
||||
log.Panicf("Failed to output results: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchGitRepositoryFromPackageManagers(npm, pypi, rubygems string) (bool, string, error) {
|
||||
if npm != "" {
|
||||
gitRepo, err := fetchGitRepositoryFromNPM(npm)
|
||||
return true, gitRepo, err
|
||||
}
|
||||
|
||||
if pypi != "" {
|
||||
gitRepo, err := fetchGitRepositoryFromPYPI(pypi)
|
||||
return true, gitRepo, err
|
||||
}
|
||||
|
||||
if rubygems != "" {
|
||||
gitRepo, err := fetchGitRepositoryFromRubyGems(rubygems)
|
||||
return false, gitRepo, err
|
||||
}
|
||||
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
func readPolicy() (*spol.ScorecardPolicy, error) {
|
||||
if policyFile != "" {
|
||||
data, err := os.ReadFile(policyFile)
|
||||
@ -251,162 +454,6 @@ func getURI(repo, local string) (string, error) {
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: scorecardUse,
|
||||
Short: scorecardShort,
|
||||
Long: scorecardLong,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// UPGRADEv4: remove.
|
||||
var v4 bool
|
||||
_, v4 = os.LookupEnv("SCORECARD_V4")
|
||||
|
||||
if format == formatSarif && !v4 {
|
||||
log.Fatal("sarif not supported yet")
|
||||
}
|
||||
|
||||
if policyFile != "" && !v4 {
|
||||
log.Fatal("policy not supported yet")
|
||||
}
|
||||
|
||||
if local != "" && !v4 {
|
||||
log.Fatal("--local option not supported yet")
|
||||
}
|
||||
|
||||
var v6 bool
|
||||
_, v6 = os.LookupEnv("SCORECARD_V6")
|
||||
if raw && !v6 {
|
||||
log.Fatal("--raw option not supported yet")
|
||||
}
|
||||
|
||||
// Validate format.
|
||||
if !validateFormat(format) {
|
||||
log.Fatalf("unsupported format '%s'", format)
|
||||
}
|
||||
|
||||
policy, err := readPolicy()
|
||||
if err != nil {
|
||||
log.Fatalf("readPolicy: %v", err)
|
||||
}
|
||||
|
||||
// Get the URI.
|
||||
uri, err := getURI(repo, local)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if npm != "" {
|
||||
if git, err := fetchGitRepositoryFromNPM(npm); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
if err := cmd.Flags().Set("repo", git); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
} else if pypi != "" {
|
||||
if git, err := fetchGitRepositoryFromPYPI(pypi); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
if err := cmd.Flags().Set("repo", git); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
} else if rubygems != "" {
|
||||
if git, err := fetchGitRepositoryFromRubyGems(rubygems); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
if err := cmd.Flags().Set("repo", git); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := cmd.MarkFlagRequired("repo"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
logger, err := githubrepo.NewLogger(*logLevel)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// nolint
|
||||
defer logger.Sync() // Flushes buffer, if any.
|
||||
|
||||
repoURI, repoClient, ossFuzzRepoClient, ciiClient, repoType, err := getRepoAccessors(ctx, uri, logger)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer repoClient.Close()
|
||||
if ossFuzzRepoClient != nil {
|
||||
defer ossFuzzRepoClient.Close()
|
||||
}
|
||||
|
||||
// Read docs.
|
||||
checkDocs, err := docs.Read()
|
||||
if err != nil {
|
||||
log.Fatalf("cannot read yaml file: %v", err)
|
||||
}
|
||||
|
||||
supportedChecks, err := getSupportedChecks(repoType, checkDocs)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot read supported checks: %v", err)
|
||||
}
|
||||
|
||||
enabledChecks, err := getEnabledChecks(policy, checksToRun, supportedChecks, repoType)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if format == formatDefault {
|
||||
for checkName := range enabledChecks {
|
||||
fmt.Fprintf(os.Stderr, "Starting [%s]\n", checkName)
|
||||
}
|
||||
}
|
||||
|
||||
if raw && format != "json" {
|
||||
log.Fatalf("only json format is supported")
|
||||
}
|
||||
|
||||
repoResult, err := pkg.RunScorecards(ctx, repoURI, raw, enabledChecks, repoClient, ossFuzzRepoClient, ciiClient)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
repoResult.Metadata = append(repoResult.Metadata, metaData...)
|
||||
|
||||
// Sort them by name
|
||||
sort.Slice(repoResult.Checks, func(i, j int) bool {
|
||||
return repoResult.Checks[i].Name < repoResult.Checks[j].Name
|
||||
})
|
||||
|
||||
if format == formatDefault {
|
||||
for checkName := range enabledChecks {
|
||||
fmt.Fprintf(os.Stderr, "Finished [%s]\n", checkName)
|
||||
}
|
||||
fmt.Println("\nRESULTS\n-------")
|
||||
}
|
||||
|
||||
switch format {
|
||||
case formatDefault:
|
||||
err = repoResult.AsString(showDetails, *logLevel, checkDocs, os.Stdout)
|
||||
case formatSarif:
|
||||
// TODO: support config files and update checker.MaxResultScore.
|
||||
err = repoResult.AsSARIF(showDetails, *logLevel, os.Stdout, checkDocs, policy)
|
||||
case formatJSON:
|
||||
if raw {
|
||||
err = repoResult.AsRawJSON(os.Stdout)
|
||||
} else {
|
||||
err = repoResult.AsJSON2(showDetails, *logLevel, checkDocs, os.Stdout)
|
||||
}
|
||||
|
||||
default:
|
||||
err = sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("invalid format flag: %v. Expected [default, json]", format))
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to output results: %v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
type npmSearchResults struct {
|
||||
Objects []struct {
|
||||
Package struct {
|
||||
@ -429,14 +476,6 @@ type rubyGemsSearchResults struct {
|
||||
SourceCodeURI string `json:"source_code_uri"`
|
||||
}
|
||||
|
||||
// Execute runs the Scorecard commandline.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the GitHub repository URL for the npm package.
|
||||
//nolint:noctx
|
||||
func fetchGitRepositoryFromNPM(packageName string) (string, error) {
|
||||
@ -526,38 +565,3 @@ func enableCheck(checkName string, enabledChecks *checker.CheckNameToFnMap) bool
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
// Add the zap flag manually
|
||||
rootCmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)
|
||||
rootCmd.Flags().StringVar(&repo, "repo", "", "repository to check")
|
||||
rootCmd.Flags().StringVar(&local, "local", "", "local folder to check")
|
||||
rootCmd.Flags().StringVar(
|
||||
&npm, "npm", "",
|
||||
"npm package to check, given that the npm package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(
|
||||
&pypi, "pypi", "",
|
||||
"pypi package to check, given that the pypi package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(
|
||||
&rubygems, "rubygems", "",
|
||||
"rubygems package to check, given that the rubygems package has a GitHub repository")
|
||||
rootCmd.Flags().StringVar(&format, "format", formatDefault,
|
||||
"output format. allowed values are [default, sarif, json]")
|
||||
rootCmd.Flags().StringSliceVar(
|
||||
&metaData, "metadata", []string{}, "metadata for the project. It can be multiple separated by commas")
|
||||
rootCmd.Flags().BoolVar(&showDetails, "show-details", false, "show extra details about each check")
|
||||
checkNames := []string{}
|
||||
for checkName := range getAllChecks() {
|
||||
checkNames = append(checkNames, checkName)
|
||||
}
|
||||
rootCmd.Flags().StringSliceVar(&checksToRun, "checks", []string{},
|
||||
fmt.Sprintf("Checks to run. Possible values are: %s", strings.Join(checkNames, ",")))
|
||||
rootCmd.Flags().StringVar(&policyFile, "policy", "", "policy to enforce")
|
||||
|
||||
var v6 bool
|
||||
_, v6 = os.LookupEnv("SCORECARD_V6")
|
||||
if v6 {
|
||||
rootCmd.Flags().BoolVar(&raw, "raw", false, "generate raw results")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user