Skip to content

Commit

Permalink
✨ Added probe for permissive licenses (#3838)
Browse files Browse the repository at this point in the history
* Added check for permissive licenses

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Regenerated docs and added more permissive licenses to check

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Added e2e tests

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Corrected copyright dates and missing newlines

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Corrected copyright dates

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Adjustments after review

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Added file location in case a permissive license was found and adjusted tests

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* Removed code for check, adjusted probe code to be invocated independently

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>

* add remediate on outcome detail

Signed-off-by: Spencer Schrock <sschrock@google.com>

* avoid memory aliasing

Signed-off-by: Spencer Schrock <sschrock@google.com>

---------

Signed-off-by: Felix Hoeborn <f.hoeborn@gmail.com>
Signed-off-by: Felix Hoeborn <98820380+fhoeborn@users.noreply.github.com>
Signed-off-by: Spencer Schrock <sschrock@google.com>
Co-authored-by: Spencer Schrock <sschrock@google.com>
  • Loading branch information
fhoeborn and spencerschrock committed Apr 12, 2024
1 parent b77f248 commit 21d53ce
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 0 deletions.
2 changes: 2 additions & 0 deletions probes/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/ossf/scorecard/v4/probes/hasNoGitHubWorkflowPermissionUnknown"
"github.com/ossf/scorecard/v4/probes/hasOSVVulnerabilities"
"github.com/ossf/scorecard/v4/probes/hasOpenSSFBadge"
"github.com/ossf/scorecard/v4/probes/hasPermissiveLicense"
"github.com/ossf/scorecard/v4/probes/hasRecentCommits"
"github.com/ossf/scorecard/v4/probes/hasUnverifiedBinaryArtifacts"
"github.com/ossf/scorecard/v4/probes/issueActivityByProjectMember"
Expand Down Expand Up @@ -155,6 +156,7 @@ var (
// Probes which aren't included by any checks.
// These still need to be listed so they can be called with --probes.
Uncategorized = []ProbeImpl{
hasPermissiveLicense.Run,
codeReviewOneReviewers.Run,
hasBinaryArtifacts.Run,
}
Expand Down
29 changes: 29 additions & 0 deletions probes/hasPermissiveLicense/def.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2024 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: hasPermissiveLicense
short: Check that the project has an permissive license.
motivation: >
A permissive license allows users to use the analyzed component to be used in derivative works. Non-permissive licenses (as copyleft licenses) might be a legal risk for potential users.
implementation: >
The implementation checks whether a permissive license is present
outcome:
- If a license file is found and is permissive, the probe returns a single OutcomeTrue.
- If a license file is missing the probe returns a single OutcomeFalse.
- If the license is not permissive, the probe returns a single OutcomeFalse.
remediation:
onOutcome: False
effort: High
text:
- A relicensing is necessary, or the component should not be used by the user for this purpose.
107 changes: 107 additions & 0 deletions probes/hasPermissiveLicense/impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2024 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 hasPermissiveLicense

import (
"embed"
"fmt"

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

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

func init() {
probes.MustRegister(Probe, Run, []probes.CheckName{probes.License})
}

const Probe = "hasPermissiveLicense"

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

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

for i := range raw.LicenseResults.LicenseFiles {
licenseFile := raw.LicenseResults.LicenseFiles[i]
spdxID := licenseFile.LicenseInformation.SpdxID
switch spdxID {
case
"Unlicense",
"Beerware",
"Apache-2.0",
"MIT",
"0BSD",
"BSD-1-Clause",
"BSD-2-Clause",
"BSD-3-Clause",
"BSD-4-Clause",
"APSL-1.0",
"APSL-1.1",
"APSL-1.2",
"APSL-2.0",
"ECL-1.0",
"ECL-2.0",
"EFL-1.0",
"EFL-2.0",
"Fair",
"FSFAP",
"WTFPL",
"Zlib",
"CNRI-Python",
"ISC",
"Intel":
// Store the license name in the msg
msg := licenseFile.LicenseInformation.Name
loc := &finding.Location{
Type: licenseFile.File.Type,
Path: licenseFile.File.Path,
LineStart: &licenseFile.File.Offset,
LineEnd: &licenseFile.File.EndOffset,
Snippet: &licenseFile.File.Snippet,
}
f, err := finding.NewWith(fs, Probe,
msg, loc,
finding.OutcomeTrue)
if err != nil {
return nil, Probe, fmt.Errorf("create finding: %w", err)
}
return []finding.Finding{*f}, Probe, nil
}
}

f, err := finding.NewWith(fs, Probe,
"", nil,
finding.OutcomeFalse)
if err != nil {
return nil, Probe, fmt.Errorf("create finding: %w", err)
}
return []finding.Finding{*f}, Probe, nil
}
130 changes: 130 additions & 0 deletions probes/hasPermissiveLicense/impl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2024 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 hasPermissiveLicense

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

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

func Test_Run(t *testing.T) {
t.Parallel()
//nolint:govet
tests := []struct {
name string
raw *checker.RawResults
outcomes []finding.Outcome
err error
}{
{
name: "License file found and is permissive: outcome should be positive",
raw: &checker.RawResults{
LicenseResults: checker.LicenseData{
LicenseFiles: []checker.LicenseFile{
{
File: checker.File{
Path: "LICENSE.md",
},
LicenseInformation: checker.License{
SpdxID: "Apache-2.0",
},
},
},
},
},
outcomes: []finding.Outcome{
finding.OutcomeTrue,
},
},
{
name: "License file found and is not permissive: outcome should be negative",
raw: &checker.RawResults{
LicenseResults: checker.LicenseData{
LicenseFiles: []checker.LicenseFile{
{
File: checker.File{
Path: "LICENSE.md",
},
LicenseInformation: checker.License{
SpdxID: "AGPL-3.0",
},
},
},
},
},
outcomes: []finding.Outcome{
finding.OutcomeFalse,
},
},
{
name: "License file not found and outcome should be negative",
raw: &checker.RawResults{
LicenseResults: checker.LicenseData{
LicenseFiles: []checker.LicenseFile{},
},
},
outcomes: []finding.Outcome{
finding.OutcomeFalse,
},
},
{
name: "nil license files and outcome should be negative",
raw: &checker.RawResults{
LicenseResults: checker.LicenseData{
LicenseFiles: nil,
},
},
outcomes: []finding.Outcome{
finding.OutcomeFalse,
},
},
{
name: "0 license files and outcome should be negative",
raw: &checker.RawResults{
LicenseResults: checker.LicenseData{
LicenseFiles: []checker.LicenseFile{},
},
},
outcomes: []finding.Outcome{
finding.OutcomeFalse,
},
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

findings, s, err := Run(tt.raw)
if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) {
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors()))
}
if err != nil {
return
}
if diff := cmp.Diff(Probe, s); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
test.AssertOutcomes(t, findings, tt.outcomes)
})
}
}

0 comments on commit 21d53ce

Please sign in to comment.