Skip to content

Commit

Permalink
🌱 Add license probe (#3465)
Browse files Browse the repository at this point in the history
* 🌱 Add license probe

Signed-off-by: AdamKorcz <[email protected]>

* [WIP] add two remaining license checks as probes

Signed-off-by: AdamKorcz <[email protected]>

* fix nits

Signed-off-by: AdamKorcz <[email protected]>

* Use Errorf in test

Signed-off-by: AdamKorcz <[email protected]>

* use zrunner

Signed-off-by: AdamKorcz <[email protected]>

* fix wrong return value

Signed-off-by: AdamKorcz <[email protected]>

* fix linting issues and remove empty default

Signed-off-by: AdamKorcz <[email protected]>

* fix double if statement

Signed-off-by: AdamKorcz <[email protected]>

* Remove struct field from test

Signed-off-by: AdamKorcz <[email protected]>

* Add test for nil-case of license files slice

Signed-off-by: AdamKorcz <[email protected]>

* rewrite multiple def.ymls

Signed-off-by: AdamKorcz <[email protected]>

* fix nits

Signed-off-by: AdamKorcz <[email protected]>

* Add unit test with multiple unapproved license files

Signed-off-by: AdamKorcz <[email protected]>

* Add link to approved license formats

Signed-off-by: AdamKorcz <[email protected]>

* fix linting

Signed-off-by: AdamKorcz <[email protected]>

* remove comment

Signed-off-by: AdamKorcz <[email protected]>

* preserve logging from original check

Signed-off-by: AdamKorcz <[email protected]>

* fix typo

Signed-off-by: AdamKorcz <[email protected]>

* remove redundant map manipulation

Signed-off-by: AdamKorcz <[email protected]>

* rename hasApproveLicense probe

Signed-off-by: AdamKorcz <[email protected]>

* Return OutcomeNotApplicable if hasFSFOrOSIApprovedLicense probe does not find a license

Signed-off-by: AdamKorcz <[email protected]>

* Include license file locations in log

Signed-off-by: AdamKorcz <[email protected]>

* fix linting issues

Signed-off-by: AdamKorcz <[email protected]>

* replace strings filtering with OutcomeNotApplicable in hasLicenseFileAtTopDir probe

Signed-off-by: AdamKorcz <[email protected]>

* Fix linter issue

Signed-off-by: AdamKorcz <[email protected]>

* Include location of found license files

Signed-off-by: AdamKorcz <[email protected]>

---------

Signed-off-by: AdamKorcz <[email protected]>
  • Loading branch information
AdamKorcz authored Oct 24, 2023
1 parent 622f104 commit 0e3a523
Show file tree
Hide file tree
Showing 14 changed files with 980 additions and 194 deletions.
140 changes: 77 additions & 63 deletions checks/evaluation/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,78 +18,92 @@ import (
"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
"github.com/ossf/scorecard/v4/probes/hasFSFOrOSIApprovedLicense"
"github.com/ossf/scorecard/v4/probes/hasLicenseFile"
"github.com/ossf/scorecard/v4/probes/hasLicenseFileAtTopDir"
)

func scoreLicenseCriteria(f *checker.LicenseFile,
// License applies the score policy for the License check.
func License(name string,
findings []finding.Finding,
dl checker.DetailLogger,
) int {
var score int
msg := checker.LogMessage{
Path: "",
Type: finding.FileTypeNone,
Text: "",
Offset: 1,
}
msg.Path = f.File.Path
msg.Type = finding.FileTypeSource
// #1 a license file was found.
score += 6

// #2 the licence was found at the top-level or LICENSE/ folder.
switch f.LicenseInformation.Attribution {
case checker.LicenseAttributionTypeAPI, checker.LicenseAttributionTypeHeuristics:
// both repoAPI and scorecard (not using the API) follow checks.md
// for a file to be found it must have been in the correct location
// award location points.
score += 3
msg.Text = "License file found in expected location"
dl.Info(&msg)
// for repo attribution prepare warning if not an recognized license"
msg.Text = "Any licence detected not an FSF or OSI recognized license"
case checker.LicenseAttributionTypeOther:
// TODO ascertain location found
score += 0
msg.Text = "License file found in unexpected location"
dl.Warn(&msg)
// for non repo attribution not the license detection is not supported
msg.Text = "Detecting license content not supported"
default:
}

// #3 is the license either an FSF or OSI recognized/approved license
if f.LicenseInformation.Approved {
score += 1
msg.Text = "FSF or OSI recognized license"
dl.Info(&msg)
} else {
// message text for this condition set above
dl.Warn(&msg)
) checker.CheckResult {
// We have 3 unique probes, each should have a finding.
expectedProbes := []string{
hasLicenseFile.Probe,
hasFSFOrOSIApprovedLicense.Probe,
hasLicenseFileAtTopDir.Probe,
}
return score
}

// License applies the score policy for the License check.
func License(name string, dl checker.DetailLogger,
r *checker.LicenseData,
) checker.CheckResult {
var score int
if r == nil {
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
if !finding.UniqueProbesEqual(findings, expectedProbes) {
e := sce.WithMessage(sce.ErrScorecardInternal, "invalid probe results")
return checker.CreateRuntimeErrorResult(name, e)
}

// Apply the policy evaluation.
if r.LicenseFiles == nil || len(r.LicenseFiles) == 0 {
return checker.CreateMinScoreResult(name, "license file not detected")
// Compute the score.
score := 0
m := make(map[string]bool)
for i := range findings {
f := &findings[i]
switch f.Outcome {
case finding.OutcomeNotApplicable:
dl.Info(&checker.LogMessage{
Type: finding.FileTypeSource,
Offset: 1,
Text: f.Message,
})
case finding.OutcomePositive:
switch f.Probe {
case hasFSFOrOSIApprovedLicense.Probe:
dl.Info(&checker.LogMessage{
Type: finding.FileTypeSource,
Offset: 1,
Path: f.Message,
Text: "FSF or OSI recognized license",
})
score += scoreProbeOnce(f.Probe, m, 1)
case hasLicenseFileAtTopDir.Probe:
dl.Info(&checker.LogMessage{
Type: finding.FileTypeSource,
Offset: 1,
Path: f.Message,
Text: "License file found in expected location",
})
score += scoreProbeOnce(f.Probe, m, 3)
case hasLicenseFile.Probe:
score += scoreProbeOnce(f.Probe, m, 6)
default:
e := sce.WithMessage(sce.ErrScorecardInternal, "unknown probe results")
return checker.CreateRuntimeErrorResult(name, e)
}
case finding.OutcomeNegative:
switch f.Probe {
case hasLicenseFileAtTopDir.Probe:
dl.Warn(&checker.LogMessage{
Type: finding.FileTypeSource,
Offset: 1,
Path: f.Message,
Text: "License file found in unexpected location",
})
case hasFSFOrOSIApprovedLicense.Probe:
dl.Warn(&checker.LogMessage{
Type: finding.FileTypeSource,
Offset: 1,
Path: "",
Text: f.Message,
})
}
default:
continue // for linting
}
}

// TODO: although this a loop, the raw checks will only return one licence file
// when more than one license file can be aggregated into a composite
// score, that logic can be comprehended here.
score = 0
for idx := range r.LicenseFiles {
score = scoreLicenseCriteria(&r.LicenseFiles[idx], dl)
_, defined := m[hasLicenseFile.Probe]
if !defined {
if score > 0 {
e := sce.WithMessage(sce.ErrScorecardInternal, "score calculation problem")
return checker.CreateRuntimeErrorResult(name, e)
}
return checker.CreateMinScoreResult(name, "license file not detected")
}

return checker.CreateResultWithScore(name, "license file detected", score)
}
Loading

0 comments on commit 0e3a523

Please sign in to comment.