fix: separating multiple licenses from one line in dpkg copyright files (#2508)

Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
DmitriyLewen
2022-07-15 17:24:25 +06:00
committed by GitHub
parent 469028dca5
commit 45dae7c2cf
9 changed files with 56 additions and 17 deletions

View File

@@ -34,6 +34,7 @@ jobs:
vuln vuln
misconf misconf
secret secret
license
image image
fs fs

View File

@@ -42,6 +42,7 @@ checks:
- vuln - vuln
- misconf - misconf
- secret - secret
- license
mode: mode:

View File

@@ -1,6 +1,8 @@
package language package language
import ( import (
"strings"
"golang.org/x/xerrors" "golang.org/x/xerrors"
dio "github.com/aquasecurity/go-dep-parser/pkg/io" dio "github.com/aquasecurity/go-dep-parser/pkg/io"
@@ -35,7 +37,10 @@ func ToAnalysisResult(fileType, filePath, libFilePath string, libs []godeptypes.
for _, lib := range libs { for _, lib := range libs {
var licenses []string var licenses []string
if lib.License != "" { if lib.License != "" {
licenses = []string{licensing.Normalize(lib.License)} licenses = strings.Split(lib.License, ",")
for i, license := range licenses {
licenses[i] = licensing.Normalize(strings.TrimSpace(license))
}
} }
pkgs = append(pkgs, types.Package{ pkgs = append(pkgs, types.Package{
ID: lib.ID, ID: lib.ID,

View File

@@ -31,7 +31,7 @@ func Test_gemspecLibraryAnalyzer_Analyze(t *testing.T) {
{ {
Name: "test-unit", Name: "test-unit",
Version: "3.3.7", Version: "3.3.7",
Licenses: []string{"Ruby, BSDL, PSFL"}, Licenses: []string{"Ruby", "BSDL", "PSFL"},
FilePath: "testdata/multiple_licenses.gemspec", FilePath: "testdata/multiple_licenses.gemspec",
}, },
}, },

View File

@@ -37,6 +37,7 @@ var (
licenseClassifier *classifier.Classifier licenseClassifier *classifier.Classifier
commonLicenseReferenceRegexp = regexp.MustCompile(`/?usr/share/common-licenses/([0-9A-Za-z_.+-]+[0-9A-Za-z+])`) commonLicenseReferenceRegexp = regexp.MustCompile(`/?usr/share/common-licenses/([0-9A-Za-z_.+-]+[0-9A-Za-z+])`)
licenseSplitRegexp = regexp.MustCompile("(,?[_ ]+or[_ ]+)|(,?[_ ]+and[_ ])|(,[ ]*)")
) )
// dpkgLicenseAnalyzer parses copyright files and detect licenses // dpkgLicenseAnalyzer parses copyright files and detect licenses
@@ -52,7 +53,7 @@ func (a dpkgLicenseAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisI
} }
findings := lo.Map(licenses, func(license string, _ int) types.LicenseFinding { findings := lo.Map(licenses, func(license string, _ int) types.LicenseFinding {
return types.LicenseFinding{Name: licensing.Normalize(license)} return types.LicenseFinding{Name: license}
}) })
// e.g. "usr/share/doc/zlib1g/copyright" => "zlib1g" // e.g. "usr/share/doc/zlib1g/copyright" => "zlib1g"
@@ -82,14 +83,29 @@ func (a dpkgLicenseAnalyzer) parseCopyright(r dio.ReadSeekerAt) ([]string, error
// Machine-readable format // Machine-readable format
// cf. https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#:~:text=The%20debian%2Fcopyright%20file%20must,in%20the%20Debian%20Policy%20Manual. // cf. https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#:~:text=The%20debian%2Fcopyright%20file%20must,in%20the%20Debian%20Policy%20Manual.
l := strings.TrimSpace(line[8:]) l := strings.TrimSpace(line[8:])
if len(l) > 0 && !slices.Contains(licenses, l) { if len(l) > 0 {
licenses = append(licenses, l) // Split licenses without considering "and"/"or"
// examples:
// 'GPL-1+,GPL-2' => {"GPL-1", "GPL-2"}
// 'GPL-1+ or Artistic or Artistic-dist' => {"GPL-1", "Artistic", "Artistic-dist"}
// 'LGPLv3+_or_GPLv2+' => {"LGPLv3", "GPLv2"}
// 'BSD-3-CLAUSE and GPL-2' => {"BSD-3-CLAUSE", "GPL-2"}
// 'GPL-1+ or Artistic, and BSD-4-clause-POWERDOG' => {"GPL-1+", "Artistic", "BSD-4-clause-POWERDOG"}
for _, lic := range licenseSplitRegexp.Split(l, -1) {
lic = licensing.Normalize(lic)
if !slices.Contains(licenses, lic) {
licenses = append(licenses, lic)
}
}
} }
case strings.Contains(line, "/usr/share/common-licenses/"): case strings.Contains(line, "/usr/share/common-licenses/"):
// Common license pattern // Common license pattern
license := commonLicenseReferenceRegexp.FindStringSubmatch(line) license := commonLicenseReferenceRegexp.FindStringSubmatch(line)
if len(license) == 2 && !slices.Contains(licenses, license[1]) { if len(license) == 2 {
licenses = append(licenses, license[1]) l := licensing.Normalize(license[1])
if !slices.Contains(licenses, l) {
licenses = append(licenses, l)
}
} }
} }
} }

View File

@@ -29,6 +29,9 @@ func Test_dpkgLicenseAnalyzer_Analyze(t *testing.T) {
Type: types.LicenseTypeDpkg, Type: types.LicenseTypeDpkg,
FilePath: "usr/share/doc/zlib1g/copyright", FilePath: "usr/share/doc/zlib1g/copyright",
Findings: []types.LicenseFinding{ Findings: []types.LicenseFinding{
{Name: "GPL-1.0"},
{Name: "Artistic"},
{Name: "BSD-4-clause-POWERDOG"},
{Name: "Zlib"}, {Name: "Zlib"},
}, },
PkgName: "zlib1g", PkgName: "zlib1g",
@@ -64,7 +67,6 @@ func Test_dpkgLicenseAnalyzer_Analyze(t *testing.T) {
FilePath: "usr/share/doc/apt/copyright", FilePath: "usr/share/doc/apt/copyright",
Findings: []types.LicenseFinding{ Findings: []types.LicenseFinding{
{Name: "GPL-2.0"}, {Name: "GPL-2.0"},
{Name: "GPL-2.0"},
}, },
PkgName: "apt", PkgName: "apt",
}, },

View File

@@ -37,7 +37,7 @@ Files-Excluded:
Files: * Files: *
Copyright: 1995-2013 Jean-loup Gailly and Mark Adler Copyright: 1995-2013 Jean-loup Gailly and Mark Adler
License: Zlib License: GPL-1+ or Artistic, and BSD-4-clause-POWERDOG
Files: amiga/Makefile.pup Files: amiga/Makefile.pup
Copyright: 1998 by Andreas R. Kleinert Copyright: 1998 by Andreas R. Kleinert

View File

@@ -316,7 +316,6 @@ func TestArtifact_Inspect(t *testing.T) {
Type: types.LicenseTypeDpkg, Type: types.LicenseTypeDpkg,
FilePath: "usr/share/doc/ca-certificates/copyright", FilePath: "usr/share/doc/ca-certificates/copyright",
Findings: []types.LicenseFinding{ Findings: []types.LicenseFinding{
{Name: "GPL-2.0"},
{Name: "GPL-2.0"}, {Name: "GPL-2.0"},
{Name: "MPL-2.0"}, {Name: "MPL-2.0"},
}, },

View File

@@ -6,7 +6,11 @@ var mapping = map[string]string{
// GPL // GPL
"GPL-1": GPL10, "GPL-1": GPL10,
"GPL-1+": GPL10, "GPL-1+": GPL10,
"GPL 1.0": GPL10,
"GPL 1": GPL10,
"GPL2": GPL20, "GPL2": GPL20,
"GPL 2.0": GPL20,
"GPL 2": GPL20,
"GPL-2": GPL20, "GPL-2": GPL20,
"GPL-2.0-ONLY": GPL20, "GPL-2.0-ONLY": GPL20,
"GPL2+": GPL20, "GPL2+": GPL20,
@@ -16,6 +20,8 @@ var mapping = map[string]string{
"GPL-2.0-OR-LATER": GPL20, "GPL-2.0-OR-LATER": GPL20,
"GPL-2+ WITH AUTOCONF EXCEPTION": GPL20withautoconfexception, "GPL-2+ WITH AUTOCONF EXCEPTION": GPL20withautoconfexception,
"GPL3": GPL30, "GPL3": GPL30,
"GPL 3.0": GPL30,
"GPL 3": GPL30,
"GPLV3+": GPL30, "GPLV3+": GPL30,
"GPL-3": GPL30, "GPL-3": GPL30,
"GPL-3.0-ONLY": GPL30, "GPL-3.0-ONLY": GPL30,
@@ -27,22 +33,30 @@ var mapping = map[string]string{
// LGPL // LGPL
"LGPL2": LGPL20, "LGPL2": LGPL20,
"LGPL 2": LGPL20,
"LGPL 2.0": LGPL20,
"LGPL-2": LGPL20, "LGPL-2": LGPL20,
"LGPL2+": LGPL20, "LGPL2+": LGPL20,
"LGPL-2+": LGPL20, "LGPL-2+": LGPL20,
"LGPL-2.0+": LGPL20, "LGPL-2.0+": LGPL20,
"LGPL-2.1": LGPL21, "LGPL-2.1": LGPL21,
"LGPL 2.1": LGPL21,
"LGPL-2.1+": LGPL21, "LGPL-2.1+": LGPL21,
"LGPLV2.1+": LGPL21, "LGPLV2.1+": LGPL21,
"LGPL-3": LGPL30, "LGPL-3": LGPL30,
"LGPL 3": LGPL30,
"LGPL-3+": LGPL30, "LGPL-3+": LGPL30,
"LGPL": LGPL30, // 2? 3? "LGPL": LGPL30, // 2? 3?
// MPL // MPL
"MPL1.0": MPL10, "MPL1.0": MPL10,
"MPL1": MPL10, "MPL1": MPL10,
"MPL 1.0": MPL10,
"MPL 1": MPL10,
"MPL2.0": MPL20, "MPL2.0": MPL20,
"MPL 2.0": MPL20,
"MPL2": MPL20, "MPL2": MPL20,
"MPL 2": MPL20,
// BSD // BSD
"BSD": BSD3Clause, // 2? 3? "BSD": BSD3Clause, // 2? 3?
@@ -51,8 +65,9 @@ var mapping = map[string]string{
"BSD-4-CLAUSE": BSD4Clause, "BSD-4-CLAUSE": BSD4Clause,
"APACHE": Apache20, // 1? 2? "APACHE": Apache20, // 1? 2?
"ZLIB": Zlib, "APACHE 2.0": Apache20,
"RUBY": Ruby, "RUBY": Ruby,
"ZLIB": Zlib,
} }
func Normalize(name string) string { func Normalize(name string) string {