Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clients/azuredevopsrepo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@
return r.metadata
}

// Type implements Repo.Type.
func (r *Repo) Type() clients.RepoType {
return clients.RepoTypeAzureDevOps

Check warning on line 105 in clients/azuredevopsrepo/repo.go

View check run for this annotation

Codecov / codecov/patch

clients/azuredevopsrepo/repo.go#L104-L105

Added lines #L104 - L105 were not covered by tests
}

// Path() implements RepoClient.Path.
func (r *Repo) Path() string {
return fmt.Sprintf("%s/%s/%s/%s", r.organization, r.project, "_git", r.name)
Expand Down
5 changes: 5 additions & 0 deletions clients/githubrepo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ func (r *Repo) Metadata() []string {
return r.metadata
}

// Type implements Repo.Type.
func (r *Repo) Type() clients.RepoType {
return clients.RepoTypeGitHub
}

func (r *Repo) commitExpression() string {
if strings.EqualFold(r.commitSHA, clients.HeadSHA) {
// TODO(#575): Confirm that this works as expected.
Expand Down
5 changes: 5 additions & 0 deletions clients/gitlabrepo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@
return r.metadata
}

// Type implements Repo.Type.
func (r *Repo) Type() clients.RepoType {
return clients.RepoTypeGitLab

Check warning on line 171 in clients/gitlabrepo/repo.go

View check run for this annotation

Codecov / codecov/patch

clients/gitlabrepo/repo.go#L170-L171

Added lines #L170 - L171 were not covered by tests
}

// Path() implements RepoClient.Path.
func (r *Repo) Path() string {
return fmt.Sprintf("%s/%s", r.owner, r.project)
Expand Down
5 changes: 5 additions & 0 deletions clients/localdir/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@
r.metadata = append(r.metadata, m...)
}

// Type implements Repo.Type.
func (r *Repo) Type() clients.RepoType {
return clients.RepoTypeLocal

Check warning on line 74 in clients/localdir/repo.go

View check run for this annotation

Codecov / codecov/patch

clients/localdir/repo.go#L73-L74

Added lines #L73 - L74 were not covered by tests
}

// Path() implements RepoClient.Path.
func (r *Repo) Path() string {
return r.path
Expand Down
16 changes: 16 additions & 0 deletions clients/mockclients/repo.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions clients/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@

package clients

// RepoType identifies the hosting platform of a repository.
type RepoType string

const (
// RepoTypeGitHub represents a GitHub-hosted repository.
RepoTypeGitHub RepoType = "GitHub"
// RepoTypeGitLab represents a GitLab-hosted repository.
RepoTypeGitLab RepoType = "GitLab"
// RepoTypeAzureDevOps represents an Azure DevOps-hosted repository.
RepoTypeAzureDevOps RepoType = "Azure DevOps"
// RepoTypeLocal represents a local directory.
RepoTypeLocal RepoType = "local"
)

// Repo interface uniquely identifies a repo.
type Repo interface {
// Path returns the specifier of the repository within its forge
Expand All @@ -29,4 +43,6 @@ type Repo interface {
IsValid() error
Metadata() []string
AppendMetadata(metadata ...string)
// Type returns the hosting platform type.
Type() RepoType
}
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
// this call to policy is different from the one in scorecard.Run
// this one is concerned with a policy file, while the scorecard.Run call is
// more concerned with the supported request types
enabledChecks, err := policy.GetEnabled(pol, o.Checks(), requiredRequestTypes)
enabledChecks, err := policy.GetEnabled(pol, o.Checks(), requiredRequestTypes, "")

Check warning on line 163 in cmd/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/root.go#L163

Added line #L163 was not covered by tests
if err != nil {
return fmt.Errorf("GetEnabled: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
if !strings.EqualFold(opts.Commit, clients.HeadSHA) {
requiredRequestTypes = append(requiredRequestTypes, checker.CommitBased)
}
enabledChecks, err := policy.GetEnabled(nil, opts.Checks(), requiredRequestTypes)
enabledChecks, err := policy.GetEnabled(nil, opts.Checks(), requiredRequestTypes, repo.Type())

Check warning on line 157 in cmd/serve.go

View check run for this annotation

Codecov / codecov/patch

cmd/serve.go#L157

Added line #L157 was not covered by tests
if err != nil {
http.Error(w, fmt.Sprintf("GetEnabled: %v", err), http.StatusInternalServerError)
return
Expand Down
2 changes: 1 addition & 1 deletion cron/internal/worker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func processRequest(ctx context.Context,
commitSHA = repoReq.GetCommit()
requiredRequestType = append(requiredRequestType, checker.CommitBased)
}
checksToRun, err := policy.GetEnabled(nil /*policy*/, nil /*checks*/, requiredRequestType)
checksToRun, err := policy.GetEnabled(nil /*policy*/, nil /*checks*/, requiredRequestType, repo.Type())
if err != nil {
return fmt.Errorf("error during policy.GetEnabled: %w", err)
}
Expand Down
26 changes: 18 additions & 8 deletions docs/checks/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"strings"
"sync"

"github.com/ossf/scorecard/v5/docs/checks/internal"
sce "github.com/ossf/scorecard/v5/errors"
Expand All @@ -28,6 +29,12 @@ var errCheckNotExist = errors.New("check does not exist")

const docURL = "https://github.com/ossf/scorecard/blob/%s/docs/checks.md"

var (
readOnce sync.Once
errCachedRead error
cachedDoc Doc
)

// DocImpl implements `Doc` interface and
// contains checks' documentation.
//
Expand All @@ -37,15 +44,18 @@ type DocImpl struct {
}

// Read loads the checks' documentation.
// The embedded YAML is parsed once and cached for subsequent calls.
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
readOnce.Do(func() {
m, e := internal.ReadDoc()
if e != nil {
cachedDoc = &DocImpl{}
errCachedRead = fmt.Errorf("internal.ReadDoc: %w", e)
return
}
cachedDoc = &DocImpl{internaldoc: m}
})
return cachedDoc, errCachedRead
}

// GetCheck returns the information for check `name`.
Expand Down
22 changes: 11 additions & 11 deletions docs/checks/internal/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ checks:
Dependency-Update-Tool:
risk: High
tags: supply-chain, security, dependencies
repos: GitHub, local
repos: GitHub, Azure DevOps, local
short: Determines if the project uses a dependency update tool.
description: |
Risk: `High` (possibly vulnerable to attacks on known flaws)
Expand Down Expand Up @@ -89,7 +89,7 @@ checks:
Binary-Artifacts:
risk: High
tags: supply-chain, security, dependencies
repos: GitHub, GitLab, local
repos: GitHub, GitLab, Azure DevOps, local
short: Determines if the project has generated executable (binary) artifacts in the source repository.
description: |
Risk: `High` (non-reviewable code)
Expand Down Expand Up @@ -140,7 +140,7 @@ checks:
Branch-Protection:
risk: High
tags: supply-chain, security, source-code, code-reviews
repos: GitHub, GitLab
repos: GitHub, GitLab, Azure DevOps
short: Determines if the default and release branches are protected with GitHub's branch protection settings.
description: |
Risk: `High` (vulnerable to intentional malicious code injection)
Expand Down Expand Up @@ -226,7 +226,7 @@ checks:
CI-Tests:
risk: Low
tags: supply-chain, testing
repos: GitHub, GitLab
repos: GitHub, GitLab, Azure DevOps
short: Determines if the project runs tests before pull requests are merged.
description: |
Risk: `Low` (possible unknown vulnerabilities)
Expand Down Expand Up @@ -286,7 +286,7 @@ checks:
Code-Review:
risk: High
tags: supply-chain, security, source-code, code-reviews
repos: GitHub
repos: GitHub, Azure DevOps
short: Determines if the project requires human code review before pull requests (aka merge requests) are merged.
description: |
Risk: `High` (unintentional vulnerabilities or possible injection of malicious
Expand Down Expand Up @@ -359,7 +359,7 @@ checks:
Contributors:
risk: Low
tags: source-code
repos: GitHub
repos: GitHub, Azure DevOps
short: Determines if the project has a set of contributors from multiple organizations (e.g., companies).
description: |
Risk: `Low` (lower number of trusted code reviewers)
Expand Down Expand Up @@ -466,7 +466,7 @@ checks:
Pinned-Dependencies:
risk: Medium
tags: supply-chain, security, dependencies
repos: GitHub, local
repos: GitHub, Azure DevOps, local
short: Determines if the project has declared and pinned the dependencies of its build process.
description: |
Risk: `Medium` (possible compromised dependencies)
Expand Down Expand Up @@ -532,7 +532,7 @@ checks:
SAST:
risk: Medium
tags: supply-chain, security, testing
repos: GitHub
repos: GitHub, Azure DevOps
short: Determines if the project uses static code analysis.
description: |
Risk: `Medium` (possible unknown bugs)
Expand Down Expand Up @@ -603,7 +603,7 @@ checks:
Security-Policy:
risk: Medium
short: Determines if the project has published a security policy.
repos: GitHub
repos: GitHub, Azure DevOps
tags: supply-chain, security, policy
description: |
Risk: `Medium` (possible insecure reporting of vulnerabilities)
Expand Down Expand Up @@ -746,7 +746,7 @@ checks:
Vulnerabilities:
risk: High
tags: supply-chain, security, vulnerabilities
repos: GitHub
repos: GitHub, Azure DevOps
short: Determines if the project has open, known unfixed vulnerabilities.
description: |
Risk: `High` (known vulnerabilities)
Expand Down Expand Up @@ -810,7 +810,7 @@ checks:
License:
risk: Low
tags: license
repos: GitHub, local
repos: GitHub, Azure DevOps, local
short: Determines if the project has defined a license.
description: |
Risk: `Low` (possible impediment to security review)
Expand Down
2 changes: 1 addition & 1 deletion pkg/scorecard/scorecard.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func Run(ctx context.Context, repo clients.Repo, opts ...Option) (Result, error)
requiredRequestTypes = append(requiredRequestTypes, checker.CommitBased)
}

checksToRun, err := policy.GetEnabled(nil, c.checks, requiredRequestTypes)
checksToRun, err := policy.GetEnabled(nil, c.checks, requiredRequestTypes, repo.Type())
if err != nil {
return Result{}, fmt.Errorf("getting enabled checks: %w", err)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/scorecard/scorecard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func TestRun(t *testing.T) {
repo := mockrepo.NewMockRepo(ctrl)

repo.EXPECT().URI().Return(tt.args.uri).AnyTimes()
repo.EXPECT().Type().Return(clients.RepoTypeGitHub).AnyTimes()

mockRepoClient.EXPECT().InitRepo(repo, tt.args.commitSHA, 0).Return(nil)

Expand Down Expand Up @@ -281,6 +282,7 @@ func TestRun_WithProbes(t *testing.T) {

repo.EXPECT().URI().Return(tt.args.uri).AnyTimes()
repo.EXPECT().Host().Return("github.com").AnyTimes()
repo.EXPECT().Type().Return(clients.RepoTypeGitHub).AnyTimes()

mockRepoClient.EXPECT().InitRepo(repo, tt.args.commitSHA, 0).Return(nil)
mockRepoClient.EXPECT().URI().Return(repo.URI()).AnyTimes()
Expand Down
47 changes: 44 additions & 3 deletions policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

"github.com/ossf/scorecard/v5/checker"
"github.com/ossf/scorecard/v5/checks"
"github.com/ossf/scorecard/v5/clients"
docs "github.com/ossf/scorecard/v5/docs/checks"
sce "github.com/ossf/scorecard/v5/errors"
)

Expand Down Expand Up @@ -143,9 +145,21 @@
sp *ScorecardPolicy,
argsChecks []string,
requiredRequestTypes []checker.RequestType,
repoType clients.RepoType,
) (checker.CheckNameToFnMap, error) {
enabledChecks := checker.CheckNameToFnMap{}

// Build a case-insensitive repo-type lookup map once, only when needed.
var repoTypeLookup map[string][]string
if repoType != "" {
if d, err := docs.Read(); err == nil {
repoTypeLookup = make(map[string][]string)
for _, c := range d.GetChecks() {
repoTypeLookup[strings.ToLower(c.GetName())] = c.GetSupportedRepoTypes()
}
}
}

switch {
case len(argsChecks) != 0:
// Populate checks to run with the `--repo` CLI argument.
Expand All @@ -156,6 +170,9 @@
fmt.Sprintf("Unsupported RequestType %s by check: %s",
fmt.Sprint(requiredRequestTypes), checkName))
}
if !isSupportedRepoType(repoTypeLookup, checkName, repoType) {
continue
}
if !enableCheck(checkName, &enabledChecks) {
return enabledChecks,
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
Expand All @@ -165,9 +182,9 @@
// 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

Check warning on line 185 in policy/policy.go

View check run for this annotation

Codecov / codecov/patch

policy/policy.go#L185

Added line #L185 was not covered by tests
}
if !isSupportedRepoType(repoTypeLookup, checkName, repoType) {
continue
}

Expand All @@ -182,6 +199,9 @@
if !isSupportedCheck(checkName, requiredRequestTypes) {
continue
}
if !isSupportedRepoType(repoTypeLookup, checkName, repoType) {
continue
}
if !enableCheck(checkName, &enabledChecks) {
return enabledChecks,
sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("invalid check: %s", checkName))
Expand Down Expand Up @@ -216,6 +236,27 @@
return len(unsupported) == 0
}

// isSupportedRepoType checks if a check supports the given repo type
// using a pre-built lookup map. If the map is nil (docs unavailable or
// repoType empty) or the check is not found, it defaults to supported.
func isSupportedRepoType(repoTypeLookup map[string][]string, checkName string, repoType clients.RepoType) bool {
if repoTypeLookup == nil {
return true
}

supportedTypes, exists := repoTypeLookup[strings.ToLower(checkName)]
if !exists {
return true

Check warning on line 249 in policy/policy.go

View check run for this annotation

Codecov / codecov/patch

policy/policy.go#L249

Added line #L249 was not covered by tests
}

for _, t := range supportedTypes {
if strings.EqualFold(t, string(repoType)) {
return true
}
}
return false
}

// Enables checks by name.
func enableCheck(checkName string, enabledChecks *checker.CheckNameToFnMap) bool {
if enabledChecks != nil {
Expand Down
Loading
Loading