Skip to content

Commit

Permalink
[WIP] add two remaining license checks as probes
Browse files Browse the repository at this point in the history
Signed-off-by: AdamKorcz <[email protected]>
  • Loading branch information
AdamKorcz committed Sep 14, 2023
1 parent b03bec8 commit c980789
Show file tree
Hide file tree
Showing 11 changed files with 674 additions and 10 deletions.
33 changes: 28 additions & 5 deletions checks/evaluation/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ 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/hasApprovedLicenseFile"
"github.com/ossf/scorecard/v4/probes/hasLicenseFile"
"github.com/ossf/scorecard/v4/probes/hasLicenseFileAtTopDir"
)

// License applies the score policy for the Fuzzing check.
Expand All @@ -29,21 +31,42 @@ func License(name string,
// We have 7 unique probes, each should have a finding.
expectedProbes := []string{
hasLicenseFile.Probe,
hasApprovedLicenseFile.Probe,
hasLicenseFileAtTopDir.Probe,
}

if !finding.UniqueProbesEqual(findings, expectedProbes) {
e := sce.WithMessage(sce.ErrScorecardInternal, "invalid probe results")
return checker.CreateRuntimeErrorResult(name, e)
}

// Compute the score. This is currently configured for a single probe
// that returns positive or negative (whether the project has a license
// file at all).
// Compute the score.
score := 0
m := make(map[string]bool)
for i := range findings {
f := &findings[i]
if f.Outcome == finding.OutcomePositive {
return checker.CreateMaxScoreResult(name, "project has license file")
switch f.Probe {
case hasApprovedLicenseFile.Probe:
score += scoreProbeOnce(f.Probe, m, 1)
case hasLicenseFileAtTopDir.Probe:
score += scoreProbeOnce(f.Probe, m, 3)
case hasLicenseFile.Probe:
score += scoreProbeOnce(f.Probe, m, 6)
m[f.Probe] = true
default:
e := sce.WithMessage(sce.ErrScorecardInternal, "unknown probe results")
return checker.CreateRuntimeErrorResult(name, e)
}
}
}
return checker.CreateMinScoreResult(name, "project does not have a license file")
_, 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)
}
91 changes: 90 additions & 1 deletion checks/evaluation/license_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"testing"

"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
scut "github.com/ossf/scorecard/v4/utests"
)
Expand All @@ -35,23 +36,111 @@ func TestLicense(t *testing.T) {
Probe: "hasLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasApprovedLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasLicenseFileAtTopDir",
Outcome: finding.OutcomePositive,
},
},
result: scut.TestReturn{
Score: checker.MaxResultScore,
NumberOfInfo: 0,
},
}, {
name: "Negative outcome = Min score",
findings: []finding.Finding{
{
Probe: "hasLicenseFile",
Outcome: finding.OutcomeNegative,
},
{
Probe: "hasApprovedLicenseFile",
Outcome: finding.OutcomeNegative,
},
{
Probe: "hasLicenseFileAtTopDir",
Outcome: finding.OutcomeNegative,
},
},
result: scut.TestReturn{
Score: checker.MinResultScore,
NumberOfInfo: 0,
},
}, {
findings: []finding.Finding{
{
Probe: "hasLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasApprovedLicenseFile",
Outcome: finding.OutcomeNegative,
},
{
Probe: "hasLicenseFileAtTopDir",
Outcome: finding.OutcomeNegative,
},
},
result: scut.TestReturn{
Score: 6,
NumberOfInfo: 0,
},
}, {
findings: []finding.Finding{
{
Probe: "hasLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasApprovedLicenseFile",
Outcome: finding.OutcomeNegative,
},
},
result: scut.TestReturn{
Score: -1,
NumberOfInfo: 0,
Error: sce.ErrScorecardInternal,
},
}, {
findings: []finding.Finding{
{
Probe: "hasLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasApprovedLicenseFile",
Outcome: finding.OutcomeNegative,
},
{
Probe: "hasLicenseFileAtTopDir",
Outcome: finding.OutcomePositive,
},
},
result: scut.TestReturn{
Score: 9,
NumberOfInfo: 0,
},
}, {
findings: []finding.Finding{
{
Probe: "hasLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasApprovedLicenseFile",
Outcome: finding.OutcomePositive,
},
{
Probe: "hasLicenseFileAtTopDir",
Outcome: finding.OutcomeNegative,
},
},
result: scut.TestReturn{
Score: 7,
NumberOfInfo: 0,
},
},
}
for _, tt := range tests {
Expand Down
8 changes: 4 additions & 4 deletions checks/license_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ func TestLicenseFileSubdirectory(t *testing.T) {
inputFolder: "testdata/licensedir/withlicense",
expected: scut.TestReturn{
Error: nil,
Score: checker.MaxResultScore,
NumberOfInfo: 1,
NumberOfWarn: 0,
Score: 9, // Does not have approved format
NumberOfInfo: 2,
NumberOfWarn: 1,
},
err: nil,
},
Expand All @@ -54,7 +54,7 @@ func TestLicenseFileSubdirectory(t *testing.T) {
expected: scut.TestReturn{
Error: nil,
Score: checker.MinResultScore,
NumberOfWarn: 1,
NumberOfWarn: 3,
},
err: nil,
},
Expand Down
4 changes: 4 additions & 0 deletions probes/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import (
"github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedHaskell"
"github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedJavascript"
"github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedTypescript"
"github.com/ossf/scorecard/v4/probes/hasApprovedLicenseFile"
"github.com/ossf/scorecard/v4/probes/hasLicenseFile"
"github.com/ossf/scorecard/v4/probes/hasLicenseFileAtTopDir"
"github.com/ossf/scorecard/v4/probes/securityPolicyContainsLinks"
"github.com/ossf/scorecard/v4/probes/securityPolicyContainsText"
"github.com/ossf/scorecard/v4/probes/securityPolicyContainsVulnerabilityDisclosure"
Expand Down Expand Up @@ -68,6 +70,8 @@ var (
}
License = []ProbeImpl{
hasLicenseFile.Run,
hasApprovedLicenseFile.Run,
hasLicenseFileAtTopDir.Run,
}
)

Expand Down
26 changes: 26 additions & 0 deletions probes/hasApprovedLicenseFile/def.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2023 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.

id: hasApprovedLicenseFile
short: Check that the project has a license file of approved format
motivation: >
A license can give users information about how the source code may or may not be used. The lack of a license will impede any kind of security review or audit and creates a legal risk for potential users.
implementation: >
The implementation checks whether a license file is present and is of an approved format
outcome:
- If a license file is found, the outcome is positive.
remediation:
effort: Low
text:
- Update the license file format in the Github repository.
70 changes: 70 additions & 0 deletions probes/hasApprovedLicenseFile/impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2023 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.

// nolint:stylecheck
package hasApprovedLicenseFile

import (
"embed"
"fmt"

"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/finding"
"github.com/ossf/scorecard/v4/probes/internal/utils/uerror"
)

//go:embed *.yml
var fs embed.FS

const Probe = "hasApprovedLicenseFile"

func Run(raw *checker.RawResults) ([]finding.Finding, string, error) {
if raw == nil {
return nil, "", fmt.Errorf("%w: raw", uerror.ErrNil)
}

if raw.LicenseResults.LicenseFiles == nil || len(raw.LicenseResults.LicenseFiles) == 0 {
f, err := finding.NewWith(fs, Probe,
"project doe not have a license file", nil,
finding.OutcomeNegative)
if err != nil {
return nil, Probe, fmt.Errorf("create finding: %w", err)
}
return []finding.Finding{*f}, Probe, nil
}

for _, licenseFile := range raw.LicenseResults.LicenseFiles {
if licenseFile.LicenseInformation.Approved {
if licenseFile.LicenseInformation.Approved {
msg := "License file is of approved type"

f, err := finding.NewWith(fs, Probe,
msg, nil,
finding.OutcomePositive)
if err != nil {
return nil, Probe, fmt.Errorf("create finding: %w", err)
}
return []finding.Finding{*f}, Probe, nil
}
}
}

f, err := finding.NewWith(fs, Probe,
"project license files has no approved information", nil,
finding.OutcomeNegative)
if err != nil {
return nil, Probe, fmt.Errorf("create finding: %w", err)
}
return []finding.Finding{*f}, Probe, nil
}
Loading

0 comments on commit c980789

Please sign in to comment.