mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-11 00:45:36 +03:00
Cleanup documentation code (#981)
* draft 1 * unit tests * fix * fixes * fix * mod * comments * fixes * rename * fix * linter
This commit is contained in:
parent
1da121da29
commit
870db56814
6
Makefile
6
Makefile
@ -97,11 +97,11 @@ clients/branch.pb.go: clients/branch.proto | $(PROTOC)
|
||||
|
||||
generate-docs: ## Generates docs
|
||||
generate-docs: docs/checks.md
|
||||
docs/checks.md: docs/checks/checks.yaml docs/checks/*.go docs/checks/generate/*.go
|
||||
docs/checks.md: docs/checks/internal/checks.yaml docs/checks/internal/*.go docs/checks/internal/generate/*.go
|
||||
# Validating checks.yaml
|
||||
go run ./docs/checks/validate/main.go
|
||||
go run ./docs/checks/internal/validate/main.go
|
||||
# Generating checks.md
|
||||
cd ./docs/checks/generate && go run main.go
|
||||
go run ./docs/checks/internal/generate/main.go docs/checks/checks.md
|
||||
|
||||
build-scorecard: ## Runs go build on repo
|
||||
# Run go build and generate scorecard executable
|
||||
|
@ -168,7 +168,11 @@ or ./scorecard --{npm,pypi,rubgems}=<package_name> [--checks=check1,...] [--show
|
||||
err = repoResult.AsCSV(showDetails, *logLevel, os.Stdout)
|
||||
case formatJSON:
|
||||
// UPGRADEv2: rename.
|
||||
err = repoResult.AsJSON2(showDetails, *logLevel, os.Stdout)
|
||||
checkDocs, e := docs.Read()
|
||||
if e != nil {
|
||||
log.Fatalf("cannot read yaml file: %v", err)
|
||||
}
|
||||
err = repoResult.AsJSON2(showDetails, *logLevel, checkDocs, os.Stdout)
|
||||
default:
|
||||
err = sce.Create(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("invalid format flag: %v. Expected [default, csv, json]", format))
|
||||
|
34
docs/checks/doc.go
Normal file
34
docs/checks/doc.go
Normal file
@ -0,0 +1,34 @@
|
||||
// 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 checks contains documentation about checks.
|
||||
package checks
|
||||
|
||||
// Doc defines the documentation interface.
|
||||
type Doc interface {
|
||||
GetCheck(name string) (CheckDoc, error)
|
||||
GetChecks() []CheckDoc
|
||||
CheckExists(name string) bool
|
||||
}
|
||||
|
||||
// CheckDoc defines the documentation interface for a check.
|
||||
type CheckDoc interface {
|
||||
GetName() string
|
||||
GetRisk() string
|
||||
GetShort() string
|
||||
GetDescription() string
|
||||
GetRemediation() []string
|
||||
GetTags() []string
|
||||
GetDocumentationURL(commitish string) string
|
||||
}
|
127
docs/checks/impl.go
Normal file
127
docs/checks/impl.go
Normal file
@ -0,0 +1,127 @@
|
||||
// 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 checks contains documentation about checks.
|
||||
package checks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ossf/scorecard/v2/docs/checks/internal"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
var errCheckNotExist = errors.New("check does not exist")
|
||||
|
||||
const docURL = "https://github.com/ossf/scorecard/blob/%s/docs/checks.md"
|
||||
|
||||
// DocImpl implements `Doc` interface and
|
||||
// contains checks' documentation.
|
||||
type DocImpl struct {
|
||||
internaldoc internal.Doc
|
||||
}
|
||||
|
||||
// Read loads the checks' documentation.
|
||||
func Read() (Doc, error) {
|
||||
m, e := internal.ReadDoc()
|
||||
if e != nil {
|
||||
d := DocImpl{}
|
||||
return &d, fmt.Errorf("internal.ReadDoc: %w", e)
|
||||
}
|
||||
|
||||
d := DocImpl{internaldoc: m}
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// GetCheck returns the information for check `name`.
|
||||
func (d *DocImpl) GetCheck(name string) (CheckDoc, error) {
|
||||
ic, exists := d.internaldoc.InternalChecks[name]
|
||||
if !exists {
|
||||
//nolint: wrapcheck
|
||||
return nil, sce.CreateInternal(errCheckNotExist, "")
|
||||
}
|
||||
// Set the name and URL.
|
||||
ic.Name = name
|
||||
ic.URL = fmt.Sprintf("%s#%s", docURL, strings.ToLower(name))
|
||||
return &CheckDocImpl{internalCheck: ic}, nil
|
||||
}
|
||||
|
||||
// GetChecks returns the information for check `name`.
|
||||
func (d *DocImpl) GetChecks() []CheckDoc {
|
||||
var checks []CheckDoc
|
||||
for k := range d.internaldoc.InternalChecks {
|
||||
//nolint: errcheck
|
||||
check, _ := d.GetCheck(k)
|
||||
checks = append(checks, check)
|
||||
}
|
||||
|
||||
return checks
|
||||
}
|
||||
|
||||
// CheckExists returns whether the check `name` exists or not.
|
||||
func (d DocImpl) CheckExists(name string) bool {
|
||||
_, exists := d.internaldoc.InternalChecks[name]
|
||||
return exists
|
||||
}
|
||||
|
||||
// CheckDocImpl implementts `CheckDoc` interface and
|
||||
// stores documentation about a check.
|
||||
type CheckDocImpl struct {
|
||||
internalCheck internal.Check
|
||||
}
|
||||
|
||||
// GetName returns the name of the check.
|
||||
func (c *CheckDocImpl) GetName() string {
|
||||
return c.internalCheck.Name
|
||||
}
|
||||
|
||||
// GetRisk returns the risk of the check.
|
||||
func (c *CheckDocImpl) GetRisk() string {
|
||||
return c.internalCheck.Risk
|
||||
}
|
||||
|
||||
// GetShort returns the short description of the check.
|
||||
func (c *CheckDocImpl) GetShort() string {
|
||||
return c.internalCheck.Short
|
||||
}
|
||||
|
||||
// GetDescription returns the full description of the check.
|
||||
func (c *CheckDocImpl) GetDescription() string {
|
||||
return c.internalCheck.Description
|
||||
}
|
||||
|
||||
// GetRemediation returns the remediation of the check.
|
||||
func (c *CheckDocImpl) GetRemediation() []string {
|
||||
return c.internalCheck.Remediation
|
||||
}
|
||||
|
||||
// GetTags returns the list of tags or the check.
|
||||
func (c *CheckDocImpl) GetTags() []string {
|
||||
l := strings.Split(c.internalCheck.Tags, ",")
|
||||
for i := range l {
|
||||
l[i] = strings.TrimSpace(l[i])
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// GetDocumentationURL returns the URL for the documentation of check `name`.
|
||||
func (c *CheckDocImpl) GetDocumentationURL(commitish string) string {
|
||||
com := commitish
|
||||
if com == "" || com == "unknown" {
|
||||
com = "main"
|
||||
}
|
||||
return fmt.Sprintf(c.internalCheck.URL, com)
|
||||
}
|
@ -14,6 +14,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
@ -21,17 +22,24 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("usage: %s filename", os.Args[0]))
|
||||
}
|
||||
yamlFile := os.Args[1]
|
||||
|
||||
m, err := docs.Read()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keys := make([]string, 0, len(m.Checks))
|
||||
for k := range m.Checks {
|
||||
keys = append(keys, k)
|
||||
checks := m.GetChecks()
|
||||
keys := make([]string, 0, len(checks))
|
||||
for _, v := range checks {
|
||||
keys = append(keys, v.GetName())
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
f, err := os.Create("../../checks.md")
|
||||
f, err := os.Create(yamlFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -52,11 +60,15 @@ please contribute!
|
||||
panic(err)
|
||||
}
|
||||
for _, k := range keys {
|
||||
_, err = f.WriteString("## " + k + " \n\n")
|
||||
_, err := f.WriteString("## " + k + " \n\n")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = f.WriteString(m.Checks[k].Description + " \n\n")
|
||||
c, err := m.GetCheck(k)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = f.WriteString(c.GetDescription() + " \n\n")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -64,7 +76,7 @@ please contribute!
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, r := range m.Checks[k].Remediation {
|
||||
for _, r := range c.GetRemediation() {
|
||||
_, err = f.WriteString("- " + r + "\n")
|
||||
if err != nil {
|
||||
panic(err)
|
@ -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.
|
||||
@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package checks contains util fns for reading input YAML file.
|
||||
package checks
|
||||
// Package internal contains internal functions for reading input YAML file.
|
||||
package internal
|
||||
|
||||
import (
|
||||
|
||||
@ -27,22 +27,25 @@ import (
|
||||
//go:embed checks.yaml
|
||||
var checksYAML []byte
|
||||
|
||||
// Check defines expected check definition in checks.yaml.
|
||||
// Check stores a check's information.
|
||||
// nolint:govet
|
||||
type Check struct {
|
||||
Risk string `yaml:"-"`
|
||||
Short string `yaml:"short"`
|
||||
Description string `yaml:"description"`
|
||||
Tags string `yaml:"tags"`
|
||||
Remediation []string `yaml:"remediation"`
|
||||
Name string `yaml:"-"`
|
||||
URL string `yaml:"-"`
|
||||
}
|
||||
|
||||
// Doc maps to checks.yaml file.
|
||||
// Doc stores the documentation for all checks.
|
||||
type Doc struct {
|
||||
Checks map[string]Check
|
||||
InternalChecks map[string]Check `yaml:"checks"`
|
||||
}
|
||||
|
||||
// Read parses `checks.yaml` file and returns a `Doc` struct.
|
||||
func Read() (Doc, error) {
|
||||
// ReadDoc reads documentation from the `checks.yaml` file.
|
||||
func ReadDoc() (Doc, error) {
|
||||
var m Doc
|
||||
if err := yaml.Unmarshal(checksYAML, &m); err != nil {
|
||||
return Doc{}, fmt.Errorf("yaml.Unmarshal: %w", err)
|
@ -29,32 +29,32 @@ func main() {
|
||||
|
||||
allChecks := checks.AllChecks
|
||||
for check := range allChecks {
|
||||
doc, exists := m.Checks[check]
|
||||
if !exists {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("could not find checkName: %s in checks.yaml", check))
|
||||
c, e := m.GetCheck(check)
|
||||
if e != nil {
|
||||
panic(fmt.Errorf("GetCheck: %w: %s", e, check))
|
||||
}
|
||||
if doc.Description == "" {
|
||||
|
||||
if c.GetDescription() == "" {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("description for checkName: %s is empty", check))
|
||||
}
|
||||
if strings.TrimSpace(strings.Join(doc.Remediation, "")) == "" {
|
||||
if strings.TrimSpace(strings.Join(c.GetRemediation(), "")) == "" {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("remediation for checkName: %s is empty", check))
|
||||
}
|
||||
if doc.Short == "" {
|
||||
if c.GetShort() == "" {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("short for checkName: %s is empty", check))
|
||||
}
|
||||
if doc.Tags == "" {
|
||||
if len(c.GetTags()) == 0 {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("tags for checkName: %s is empty", check))
|
||||
}
|
||||
}
|
||||
for check := range m.Checks {
|
||||
if _, exists := allChecks[check]; !exists {
|
||||
for _, check := range m.GetChecks() {
|
||||
if _, exists := allChecks[check.GetName()]; !exists {
|
||||
// nolint: goerr113
|
||||
panic(fmt.Errorf("check present in checks.yaml is not part of `checks.AllChecks`: %s", check))
|
||||
panic(fmt.Errorf("check present in checks.yaml is not part of `checks.AllChecks`: %s", check.GetName()))
|
||||
}
|
||||
}
|
||||
}
|
@ -81,11 +81,3 @@ func typeToString(cd checker.DetailType) string {
|
||||
return "Debug"
|
||||
}
|
||||
}
|
||||
|
||||
func tagsAsList(tags string) []string {
|
||||
l := strings.Split(tags, ",")
|
||||
for i := range l {
|
||||
l[i] = strings.TrimSpace(l[i])
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
30
pkg/json.go
30
pkg/json.go
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
docs "github.com/ossf/scorecard/v2/docs/checks"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
@ -39,12 +40,19 @@ type jsonScorecardResult struct {
|
||||
Metadata []string
|
||||
}
|
||||
|
||||
type jsonCheckDocumentationV2 struct {
|
||||
URL string `json:"url"`
|
||||
Short string `json:"short"`
|
||||
// Can be extended if needed.
|
||||
}
|
||||
|
||||
//nolint
|
||||
type jsonCheckResultV2 struct {
|
||||
Details []string `json:"details"`
|
||||
Score int `json:"score"`
|
||||
Reason string `json:"reason"`
|
||||
Name string `json:"name"`
|
||||
Details []string `json:"details"`
|
||||
Score int `json:"score"`
|
||||
Reason string `json:"reason"`
|
||||
Name string `json:"name"`
|
||||
Doc jsonCheckDocumentationV2 `json:"documentation"`
|
||||
}
|
||||
|
||||
type jsonRepoV2 struct {
|
||||
@ -102,7 +110,8 @@ func (r *ScorecardResult) AsJSON(showDetails bool, logLevel zapcore.Level, write
|
||||
}
|
||||
|
||||
// AsJSON2 exports results as JSON for new detail format.
|
||||
func (r *ScorecardResult) AsJSON2(showDetails bool, logLevel zapcore.Level, writer io.Writer) error {
|
||||
func (r *ScorecardResult) AsJSON2(showDetails bool,
|
||||
logLevel zapcore.Level, checkDocs docs.Doc, writer io.Writer) error {
|
||||
encoder := json.NewEncoder(writer)
|
||||
|
||||
out := jsonScorecardResultV2{
|
||||
@ -120,8 +129,17 @@ func (r *ScorecardResult) AsJSON2(showDetails bool, logLevel zapcore.Level, writ
|
||||
|
||||
//nolint
|
||||
for _, checkResult := range r.Checks {
|
||||
doc, e := checkDocs.GetCheck(checkResult.Name)
|
||||
if e != nil {
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("GetCheck: %s: %v", checkResult.Name, e))
|
||||
}
|
||||
|
||||
tmpResult := jsonCheckResultV2{
|
||||
Name: checkResult.Name,
|
||||
Name: checkResult.Name,
|
||||
Doc: jsonCheckDocumentationV2{
|
||||
URL: doc.GetDocumentationURL(r.Scorecard.CommitSHA),
|
||||
Short: doc.GetShort(),
|
||||
},
|
||||
Reason: checkResult.Reason,
|
||||
Score: checkResult.Score,
|
||||
}
|
||||
|
@ -1,81 +1,97 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"checks": {
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"score": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"details",
|
||||
"score",
|
||||
"reason",
|
||||
"name"
|
||||
]
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
},
|
||||
"documentation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"short": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"repo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"commit"
|
||||
"url",
|
||||
"short"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"score": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"scorecard": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version",
|
||||
"commit"
|
||||
]
|
||||
}
|
||||
"required": [
|
||||
"details",
|
||||
"score",
|
||||
"reason",
|
||||
"name",
|
||||
"documentation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"date",
|
||||
"repo",
|
||||
"scorecard",
|
||||
"checks",
|
||||
"metadata"
|
||||
]
|
||||
"date": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"repo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"commit"
|
||||
]
|
||||
},
|
||||
"scorecard": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version",
|
||||
"commit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"date",
|
||||
"repo",
|
||||
"scorecard",
|
||||
"checks",
|
||||
"metadata"
|
||||
]
|
||||
}
|
||||
|
@ -30,6 +30,41 @@ import (
|
||||
"github.com/ossf/scorecard/v2/checker"
|
||||
)
|
||||
|
||||
func jsonMockDocRead() *mockDoc {
|
||||
d := map[string]mockCheck{
|
||||
"Check-Name": {
|
||||
name: "Check-Name",
|
||||
risk: "not used",
|
||||
short: "short description for Check-Name",
|
||||
description: "not used",
|
||||
url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
tags: []string{"not-used1", "not-used2"},
|
||||
remediation: []string{"not-used1", "not-used2"},
|
||||
},
|
||||
"Check-Name2": {
|
||||
name: "Check-Name2",
|
||||
risk: "not used",
|
||||
short: "short description for Check-Name2",
|
||||
description: "not used",
|
||||
url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name2",
|
||||
tags: []string{"not-used1", "not-used2"},
|
||||
remediation: []string{"not-used1", "not-used2"},
|
||||
},
|
||||
"Check-Name3": {
|
||||
name: "Check-Name3",
|
||||
risk: "not used",
|
||||
short: "short description for Check-Name3",
|
||||
description: "not used",
|
||||
url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name3",
|
||||
tags: []string{"not-used1", "not-used2"},
|
||||
remediation: []string{"not-used1", "not-used2"},
|
||||
},
|
||||
}
|
||||
|
||||
m := mockDoc{checks: d}
|
||||
return &m
|
||||
}
|
||||
|
||||
//nolint
|
||||
func TestJSONOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -37,12 +72,14 @@ func TestJSONOutput(t *testing.T) {
|
||||
repoCommit := "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
scorecardCommit := "ccbc59901773ab4c051dfcea0cc4201a1567abdd"
|
||||
scorecardVersion := "1.2.3"
|
||||
repoName := "repo not used"
|
||||
repoName := "org/name"
|
||||
date, e := time.Parse("2006-01-02", "2021-08-25")
|
||||
if e != nil {
|
||||
panic(fmt.Errorf("time.Parse: %w", e))
|
||||
}
|
||||
|
||||
checkDocs := jsonMockDocRead()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expected string
|
||||
@ -436,7 +473,7 @@ func TestJSONOutput(t *testing.T) {
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
err = tt.result.AsJSON2(tt.showDetails, tt.logLevel, &result)
|
||||
err = tt.result.AsJSON2(tt.showDetails, tt.logLevel, checkDocs, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: AsJSON2: %v", tt.name, err)
|
||||
}
|
||||
|
77
pkg/mock_doc.go
Normal file
77
pkg/mock_doc.go
Normal file
@ -0,0 +1,77 @@
|
||||
// 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 pkg
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
docs "github.com/ossf/scorecard/v2/docs/checks"
|
||||
)
|
||||
|
||||
type mockCheck struct {
|
||||
name, risk, short, description, url string
|
||||
tags, remediation []string
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetName() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetRisk() string {
|
||||
return c.risk
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetShort() string {
|
||||
return c.short
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetDescription() string {
|
||||
return c.description
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetRemediation() []string {
|
||||
return c.remediation
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetTags() []string {
|
||||
l := make([]string, len(c.tags))
|
||||
for i := range c.tags {
|
||||
l[i] = strings.TrimSpace(c.tags[i])
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (c *mockCheck) GetDocumentationURL(commitish string) string {
|
||||
return c.url
|
||||
}
|
||||
|
||||
type mockDoc struct {
|
||||
checks map[string]mockCheck
|
||||
}
|
||||
|
||||
func (d *mockDoc) GetCheck(name string) (docs.CheckDoc, error) {
|
||||
// nolint: gosimple
|
||||
m, _ := d.checks[name]
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (d *mockDoc) GetChecks() []docs.CheckDoc {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *mockDoc) CheckExists(name string) bool {
|
||||
_, exists := d.checks[name]
|
||||
return exists
|
||||
}
|
17
pkg/sarif.go
17
pkg/sarif.go
@ -29,6 +29,7 @@ import (
|
||||
|
||||
"github.com/ossf/scorecard/v2/checker"
|
||||
docs "github.com/ossf/scorecard/v2/docs/checks"
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
type text struct {
|
||||
@ -403,10 +404,9 @@ func (r *ScorecardResult) AsSARIF(showDetails bool, logLevel zapcore.Level,
|
||||
|
||||
// nolint
|
||||
for i, check := range r.Checks {
|
||||
|
||||
doc, exists := checkDocs.Checks[check.Name]
|
||||
if !exists {
|
||||
panic(fmt.Sprintf("invalid doc: %s not present", check.Name))
|
||||
doc, e := checkDocs.GetCheck(check.Name)
|
||||
if e != nil {
|
||||
return sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("GetCheck: %v: %s", e, check.Name))
|
||||
}
|
||||
|
||||
// Unclear what to use for PartialFingerprints.
|
||||
@ -430,9 +430,9 @@ func (r *ScorecardResult) AsSARIF(showDetails bool, logLevel zapcore.Level,
|
||||
// Create a header's rule.
|
||||
// TODO: verify `\n` is viewable in GitHub.
|
||||
rule := createSARIFRule(check.Name, checkID,
|
||||
fmt.Sprintf("https://github.com/ossf/scorecard/blob/main/docs/checks.md#%s", strings.ToLower(check.Name)),
|
||||
doc.Description, doc.Short,
|
||||
tagsAsList(doc.Tags))
|
||||
doc.GetDocumentationURL(r.Scorecard.CommitSHA),
|
||||
doc.GetDescription(), doc.GetShort(),
|
||||
doc.GetTags())
|
||||
rules = append(rules, rule)
|
||||
|
||||
// Add default location if no locations are present.
|
||||
@ -453,7 +453,8 @@ func (r *ScorecardResult) AsSARIF(showDetails bool, logLevel zapcore.Level,
|
||||
encoder := json.NewEncoder(writer)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(sarif); err != nil {
|
||||
panic(err.Error())
|
||||
// nolint: wrapcheck
|
||||
return sce.Create(sce.ErrScorecardInternal, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -24,9 +24,43 @@ import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/ossf/scorecard/v2/checker"
|
||||
"github.com/ossf/scorecard/v2/docs/checks"
|
||||
)
|
||||
|
||||
func sarifMockDocRead() *mockDoc {
|
||||
d := map[string]mockCheck{
|
||||
"Check-Name": {
|
||||
name: "Check-Name",
|
||||
risk: "not used",
|
||||
short: "short description",
|
||||
description: "long description\n other line",
|
||||
url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
tags: []string{"tag1", "tag2"},
|
||||
remediation: []string{"not-used1", "not-used2"},
|
||||
},
|
||||
"Check-Name2": {
|
||||
name: "Check-Name2",
|
||||
risk: "not used",
|
||||
short: "short description 2",
|
||||
description: "long description\n other line 2",
|
||||
url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name2",
|
||||
tags: []string{" tag1 ", " tag2 ", "tag3"},
|
||||
remediation: []string{"not-used1", "not-used2"},
|
||||
},
|
||||
"Check-Name3": {
|
||||
name: "Check-Name3",
|
||||
risk: "not used",
|
||||
short: "short description 3",
|
||||
description: "long description\n other line 3",
|
||||
url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name3",
|
||||
tags: []string{" tag1", " tag2", "tag3", "tag 4 "},
|
||||
remediation: []string{"not-used1", "not-used2"},
|
||||
},
|
||||
}
|
||||
|
||||
m := mockDoc{checks: d}
|
||||
return &m
|
||||
}
|
||||
|
||||
//nolint
|
||||
func TestSARIFOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -48,13 +82,14 @@ func TestSARIFOutput(t *testing.T) {
|
||||
panic(fmt.Errorf("time.Parse: %w", e))
|
||||
}
|
||||
|
||||
checkDocs := sarifMockDocRead()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expected string
|
||||
showDetails bool
|
||||
logLevel zapcore.Level
|
||||
result ScorecardResult
|
||||
checkDocs checks.Doc
|
||||
minScore int
|
||||
}{
|
||||
{
|
||||
@ -63,17 +98,6 @@ func TestSARIFOutput(t *testing.T) {
|
||||
expected: "./testdata/check1.sarif",
|
||||
logLevel: zapcore.DebugLevel,
|
||||
minScore: checker.MaxResultScore,
|
||||
checkDocs: checks.Doc{
|
||||
Checks: map[string]checks.Check{
|
||||
"Check-Name": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description",
|
||||
Description: "long description\n other line",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: ScorecardResult{
|
||||
Repo: RepoInfo{
|
||||
Name: repoName,
|
||||
@ -112,17 +136,6 @@ func TestSARIFOutput(t *testing.T) {
|
||||
expected: "./testdata/check2.sarif",
|
||||
logLevel: zapcore.DebugLevel,
|
||||
minScore: checker.MaxResultScore,
|
||||
checkDocs: checks.Doc{
|
||||
Checks: map[string]checks.Check{
|
||||
"Check-Name": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description",
|
||||
Description: "long description\n other line",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: ScorecardResult{
|
||||
Repo: RepoInfo{
|
||||
Name: repoName,
|
||||
@ -160,31 +173,6 @@ func TestSARIFOutput(t *testing.T) {
|
||||
expected: "./testdata/check3.sarif",
|
||||
logLevel: zapcore.InfoLevel,
|
||||
minScore: checker.MaxResultScore,
|
||||
checkDocs: checks.Doc{
|
||||
Checks: map[string]checks.Check{
|
||||
"Check-Name": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description",
|
||||
Description: "long description\n other line",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2 ",
|
||||
},
|
||||
"Check-Name2": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description 2",
|
||||
Description: "long description\n other line 2",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2, tag3 ",
|
||||
},
|
||||
"Check-Name3": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description 3",
|
||||
Description: "long description\n other line 3",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2, tag3, tag4 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: ScorecardResult{
|
||||
Repo: RepoInfo{
|
||||
Name: repoName,
|
||||
@ -276,31 +264,6 @@ func TestSARIFOutput(t *testing.T) {
|
||||
expected: "./testdata/check4.sarif",
|
||||
logLevel: zapcore.DebugLevel,
|
||||
minScore: checker.MaxResultScore,
|
||||
checkDocs: checks.Doc{
|
||||
Checks: map[string]checks.Check{
|
||||
"Check-Name": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description",
|
||||
Description: "long description\n other line",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2 ",
|
||||
},
|
||||
"Check-Name2": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description 2",
|
||||
Description: "long description\n other line 2",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2, tag3 ",
|
||||
},
|
||||
"Check-Name3": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description 3",
|
||||
Description: "long description\n other line 3",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2, tag3, tag4 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: ScorecardResult{
|
||||
Repo: RepoInfo{
|
||||
Name: repoName,
|
||||
@ -392,17 +355,6 @@ func TestSARIFOutput(t *testing.T) {
|
||||
expected: "./testdata/check5.sarif",
|
||||
logLevel: zapcore.WarnLevel,
|
||||
minScore: 5,
|
||||
checkDocs: checks.Doc{
|
||||
Checks: map[string]checks.Check{
|
||||
"Check-Name": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description",
|
||||
Description: "long description\n other line",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: ScorecardResult{
|
||||
Repo: RepoInfo{
|
||||
Name: repoName,
|
||||
@ -441,17 +393,6 @@ func TestSARIFOutput(t *testing.T) {
|
||||
expected: "./testdata/check6.sarif",
|
||||
logLevel: zapcore.WarnLevel,
|
||||
minScore: checker.MaxResultScore,
|
||||
checkDocs: checks.Doc{
|
||||
Checks: map[string]checks.Check{
|
||||
"Check-Name": {
|
||||
Risk: "risk not used",
|
||||
Short: "short description",
|
||||
Description: "long description\n other line",
|
||||
Remediation: []string{"remediation not used"},
|
||||
Tags: "tag1, tag2 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: ScorecardResult{
|
||||
Repo: RepoInfo{
|
||||
Name: repoName,
|
||||
@ -504,7 +445,7 @@ func TestSARIFOutput(t *testing.T) {
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
err = tt.result.AsSARIF(tt.showDetails, tt.logLevel, &result, tt.checkDocs, tt.minScore)
|
||||
err = tt.result.AsSARIF(tt.showDetails, tt.logLevel, &result, checkDocs, tt.minScore)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: AsSARIF: %v", tt.name, err)
|
||||
}
|
||||
|
@ -100,6 +100,10 @@ func RunScorecards(ctx context.Context,
|
||||
Name: repo.URL(),
|
||||
CommitSHA: commitSHA,
|
||||
},
|
||||
Scorecard: ScorecardInfo{
|
||||
Version: GetVersion(),
|
||||
CommitSHA: GetCommit(),
|
||||
},
|
||||
Date: time.Now(),
|
||||
}
|
||||
resultsCh := make(chan checker.CheckResult)
|
||||
|
@ -14,7 +14,9 @@
|
||||
|
||||
package pkg
|
||||
|
||||
import "runtime"
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Base version information.
|
||||
//
|
||||
|
8
pkg/testdata/check1.json
vendored
8
pkg/testdata/check1.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"date": "2021-08-25",
|
||||
"repo": {
|
||||
"name": "repo not used",
|
||||
"name": "org/name",
|
||||
"commit": "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
},
|
||||
"scorecard": {
|
||||
@ -15,7 +15,11 @@
|
||||
],
|
||||
"score": 5,
|
||||
"reason": "half score reason",
|
||||
"name": "Check-Name"
|
||||
"name": "Check-Name",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
"short": "short description for Check-Name"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": []
|
||||
|
8
pkg/testdata/check2.json
vendored
8
pkg/testdata/check2.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"date": "2021-08-25",
|
||||
"repo": {
|
||||
"name": "repo not used",
|
||||
"name": "org/name",
|
||||
"commit": "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
},
|
||||
"scorecard": {
|
||||
@ -15,7 +15,11 @@
|
||||
],
|
||||
"score": 0,
|
||||
"reason": "min score reason",
|
||||
"name": "Check-Name"
|
||||
"name": "Check-Name",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
"short": "short description for Check-Name"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": []
|
||||
|
20
pkg/testdata/check3.json
vendored
20
pkg/testdata/check3.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"date": "2021-08-25",
|
||||
"repo": {
|
||||
"name": "repo not used",
|
||||
"name": "org/name",
|
||||
"commit": "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
},
|
||||
"scorecard": {
|
||||
@ -15,7 +15,11 @@
|
||||
],
|
||||
"score": 0,
|
||||
"reason": "min result reason",
|
||||
"name": "Check-Name"
|
||||
"name": "Check-Name",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
"short": "short description for Check-Name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"details": [
|
||||
@ -23,7 +27,11 @@
|
||||
],
|
||||
"score": 0,
|
||||
"reason": "min result reason",
|
||||
"name": "Check-Name2"
|
||||
"name": "Check-Name2",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name2",
|
||||
"short": "short description for Check-Name2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"details": [
|
||||
@ -32,7 +40,11 @@
|
||||
],
|
||||
"score": -1,
|
||||
"reason": "inconclusive reason",
|
||||
"name": "Check-Name3"
|
||||
"name": "Check-Name3",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name3",
|
||||
"short": "short description for Check-Name3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": []
|
||||
|
2
pkg/testdata/check3.sarif
vendored
2
pkg/testdata/check3.sarif
vendored
@ -86,7 +86,7 @@
|
||||
"tag1",
|
||||
"tag2",
|
||||
"tag3",
|
||||
"tag4"
|
||||
"tag 4"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
20
pkg/testdata/check4.json
vendored
20
pkg/testdata/check4.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"date": "2021-08-25",
|
||||
"repo": {
|
||||
"name": "repo not used",
|
||||
"name": "org/name",
|
||||
"commit": "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
},
|
||||
"scorecard": {
|
||||
@ -15,7 +15,11 @@
|
||||
],
|
||||
"score": 0,
|
||||
"reason": "min result reason",
|
||||
"name": "Check-Name"
|
||||
"name": "Check-Name",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
"short": "short description for Check-Name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"details": [
|
||||
@ -23,7 +27,11 @@
|
||||
],
|
||||
"score": 0,
|
||||
"reason": "min result reason",
|
||||
"name": "Check-Name2"
|
||||
"name": "Check-Name2",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name2",
|
||||
"short": "short description for Check-Name2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"details": [
|
||||
@ -33,7 +41,11 @@
|
||||
],
|
||||
"score": -1,
|
||||
"reason": "inconclusive reason",
|
||||
"name": "Check-Name3"
|
||||
"name": "Check-Name3",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name3",
|
||||
"short": "short description for Check-Name3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": []
|
||||
|
2
pkg/testdata/check4.sarif
vendored
2
pkg/testdata/check4.sarif
vendored
@ -86,7 +86,7 @@
|
||||
"tag1",
|
||||
"tag2",
|
||||
"tag3",
|
||||
"tag4"
|
||||
"tag 4"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
8
pkg/testdata/check5.json
vendored
8
pkg/testdata/check5.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"date": "2021-08-25",
|
||||
"repo": {
|
||||
"name": "repo not used",
|
||||
"name": "org/name",
|
||||
"commit": "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
},
|
||||
"scorecard": {
|
||||
@ -15,7 +15,11 @@
|
||||
],
|
||||
"score": 6,
|
||||
"reason": "six score reason",
|
||||
"name": "Check-Name"
|
||||
"name": "Check-Name",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
"short": "short description for Check-Name"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": []
|
||||
|
8
pkg/testdata/check6.json
vendored
8
pkg/testdata/check6.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"date": "2021-08-25",
|
||||
"repo": {
|
||||
"name": "repo not used",
|
||||
"name": "org/name",
|
||||
"commit": "68bc59901773ab4c051dfcea0cc4201a1567ab32"
|
||||
},
|
||||
"scorecard": {
|
||||
@ -15,7 +15,11 @@
|
||||
],
|
||||
"score": 6,
|
||||
"reason": "six score reason",
|
||||
"name": "Check-Name"
|
||||
"name": "Check-Name",
|
||||
"documentation": {
|
||||
"url": "https://github.com/ossf/scorecard/blob/main/docs/checks.md#check-name",
|
||||
"short": "short description for Check-Name"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": []
|
||||
|
Loading…
Reference in New Issue
Block a user