fix(license): normalize licenses for PostAnalyzers (#9941)

This commit is contained in:
DmitriyLewen
2025-12-29 12:27:04 +06:00
committed by GitHub
parent b64d5adc6b
commit 11dd3fac38
14 changed files with 349 additions and 272 deletions

View File

@@ -18,6 +18,7 @@ import (
fos "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/licensing"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/misconf"
xio "github.com/aquasecurity/trivy/pkg/x/io"
@@ -287,6 +288,7 @@ func (r *AnalysisResult) Merge(newResult *AnalysisResult) {
}
if len(newResult.Applications) > 0 {
normalizeApplicationsLicenses(newResult.Applications)
r.Applications = append(r.Applications, newResult.Applications...)
}
@@ -335,6 +337,14 @@ func belongToGroup(groupName Group, analyzerType Type, disabledAnalyzers []Type,
return true
}
func normalizeApplicationsLicenses(applications []types.Application) {
for i, app := range applications {
for j, pkg := range app.Packages {
applications[i].Packages[j].Licenses = licensing.NormalizeLicenses(pkg.Licenses)
}
}
}
const separator = ":"
func NewAnalyzerGroup(opts AnalyzerOptions) (AnalyzerGroup, error) {

View File

@@ -276,6 +276,73 @@ func TestAnalysisResult_Merge(t *testing.T) {
},
},
},
{
name: "normalize licenses for PackageInfos and Applications",
args: args{
new: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: "gomod",
FilePath: "go.mod",
Packages: types.Packages{
{
Name: "github.com/example/package",
Version: "v1.0.0",
Licenses: []string{
"",
"BSD",
"GPL-2",
"GPL-2.0",
},
},
},
},
{
Type: "gomod",
FilePath: "empty-license/go.mod",
Packages: types.Packages{
{
Name: "github.com/empty/license",
Version: "v1.0.0",
Licenses: []string{
"",
},
},
},
},
},
},
},
want: analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: "gomod",
FilePath: "go.mod",
Packages: types.Packages{
{
Name: "github.com/example/package",
Version: "v1.0.0",
Licenses: []string{
"BSD-3-Clause",
"GPL-2.0-only",
},
},
},
},
{
Type: "gomod",
FilePath: "empty-license/go.mod",
Packages: types.Packages{
{
Name: "github.com/empty/license",
Version: "v1.0.0",
Licenses: nil,
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -9,7 +9,6 @@ import (
"github.com/aquasecurity/trivy/pkg/digest"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/licensing"
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
)
@@ -104,10 +103,6 @@ func toApplication(fileType types.LangType, filePath, libFilePath string, r xio.
pkgs[i].DependsOn = deps[pkg.ID]
pkgs[i].Digest = d
pkgs[i].Indirect = isIndirect(pkg.Relationship) // For backward compatibility
for j, license := range pkg.Licenses {
pkgs[i].Licenses[j] = licensing.Normalize(license)
}
}
return &types.Application{

View File

@@ -33,7 +33,7 @@ func Test_pomAnalyzer_Analyze(t *testing.T) {
ID: "com.example:example:1.0.0",
Name: "com.example:example",
Version: "1.0.0",
Licenses: []string{"Apache-2.0"},
Licenses: []string{"Apache 2.0"},
Relationship: types.RelationshipRoot,
DependsOn: []string{
"com.example:example-api:2.0.0",
@@ -71,7 +71,7 @@ func Test_pomAnalyzer_Analyze(t *testing.T) {
Name: "com.example:example",
Version: "1.0.0",
Relationship: types.RelationshipRoot,
Licenses: []string{"Apache-2.0"},
Licenses: []string{"Apache 2.0"},
DependsOn: []string{
"com.example:example-api:2.0.0",
},
@@ -106,7 +106,7 @@ func Test_pomAnalyzer_Analyze(t *testing.T) {
ID: "com.example:example:1.0.0",
Name: "com.example:example",
Version: "1.0.0",
Licenses: []string{"Apache-2.0"},
Licenses: []string{"Apache 2.0"},
Relationship: types.RelationshipRoot,
DependsOn: []string{
"com.example:example-api:@example.version@",
@@ -144,7 +144,7 @@ func Test_pomAnalyzer_Analyze(t *testing.T) {
ID: "com.example:example:2.0.0",
Name: "com.example:example",
Version: "2.0.0",
Licenses: []string{"Apache-2.0"},
Licenses: []string{"Apache 2.0"},
Relationship: types.RelationshipRoot,
},
{

View File

@@ -32,7 +32,7 @@ func Test_eggAnalyzer_Analyze(t *testing.T) {
Name: "kitchen",
Version: "1.2.6",
Licenses: []string{
"LGPL-2.1-only",
"GNU Library or Lesser General Public License (LGPL)",
},
FilePath: "testdata/egg-zip/kitchen-1.2.6-py2.7.egg",
},
@@ -55,7 +55,7 @@ func Test_eggAnalyzer_Analyze(t *testing.T) {
Name: "kitchen",
Version: "1.2.6",
Licenses: []string{
"LGPL-2.1-only",
"GNU Library or Lesser General Public License (LGPL)",
},
FilePath: "testdata/egg-zip/kitchen-1.2.6-py2.7.egg",
Digest: "sha1:4e13b6e379966771e896ee43cf8e240bf6083dca",

View File

@@ -32,7 +32,7 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) {
{
Name: "distlib",
Version: "0.3.1",
Licenses: []string{"Python-2.0"},
Licenses: []string{"Python license"},
FilePath: "distlib-0.3.1.egg-info/PKG-INFO",
Digest: "sha1:d9d89d8ed3b2b683767c96814c9c5d3e57ef2e1b",
},
@@ -53,7 +53,7 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) {
{
Name: "setuptools",
Version: "51.3.3",
Licenses: []string{"MIT"},
Licenses: []string{"MIT License"},
FilePath: "setuptools-51.3.3.egg-info/PKG-INFO",
},
},
@@ -73,7 +73,7 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) {
{
Name: "setuptools",
Version: "51.3.3",
Licenses: []string{"MIT"},
Licenses: []string{"MIT License"},
FilePath: "setuptools-51.3.3.dist-info/METADATA",
},
},
@@ -93,7 +93,7 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) {
{
Name: "distlib",
Version: "0.3.1",
Licenses: []string{"Python-2.0"},
Licenses: []string{"Python license"},
FilePath: "distlib-0.3.1.dist-info/METADATA",
},
},

View File

@@ -7,9 +7,9 @@ import (
"os"
"path"
"regexp"
"slices"
"strings"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
@@ -88,30 +88,19 @@ func (a *dpkgLicenseAnalyzer) parseCopyright(r xio.ReadSeekerAt) ([]types.Licens
l := strings.TrimSpace(line[8:])
l = normalizeLicense(l)
if l != "" {
for _, lic := range licensing.SplitLicenses(l) {
if lic == "" {
continue
}
lic = licensing.Normalize(lic)
if !slices.Contains(licenses, lic) {
licenses = append(licenses, lic)
}
}
}
licenses = append(licenses, licensing.SplitLicenses(l)...)
case strings.Contains(line, "/usr/share/common-licenses/"):
// Common license pattern
license := commonLicenseReferenceRegexp.FindStringSubmatch(line)
if len(license) == 2 {
l := licensing.Normalize(license[1])
if l != "" && !slices.Contains(licenses, l) {
licenses = append(licenses, l)
}
licenses = append(licenses, license[1])
}
}
}
licenses = licensing.NormalizeLicenses(licenses)
licenses = lo.Uniq(licenses)
return xslices.Map(licenses, func(license string) types.LicenseFinding {
return types.LicenseFinding{Name: license}
}), nil

View File

@@ -2,8 +2,11 @@ package licensing
import (
"regexp"
"slices"
"strings"
"github.com/samber/lo/it"
expr "github.com/aquasecurity/trivy/pkg/licensing/expression"
)
@@ -671,11 +674,23 @@ func standardizeKeyAndSuffix(name string) expr.SimpleExpr {
return expr.SimpleExpr{License: name, HasPlus: hasPlus}
}
func Normalize(name string) string {
return NormalizeLicense(expr.SimpleExpr{License: name}).String()
func NormalizeLicenses(licenses []string) []string {
seq := it.UniqMap(slices.Values(licenses), normalizeLicense)
seq = it.Compact(seq)
normalized := slices.Collect(seq)
if len(normalized) == 0 {
return nil
}
return normalized
}
func NormalizeLicense(exp expr.Expression) expr.Expression {
func normalizeLicense(name string) string {
return NormalizeLicenseExpression(expr.SimpleExpr{License: name}).String()
}
func NormalizeLicenseExpression(exp expr.Expression) expr.Expression {
switch e := exp.(type) {
case expr.SimpleExpr:
return normalizeSimpleExpr(e)
@@ -762,7 +777,7 @@ func LaxSplitLicenses(str string) []string {
afterWith = true
continue
default:
normalizedLicense := Normalize(s)
normalizedLicense := normalizeLicense(s)
if afterWith && len(licenses) > 0 {
// If we found "WITH" operator, we should not split the license
// e.g. "GPL-2 WITH Autoconf exception" => {"GPL-2 WITH Autoconf exception"}

View File

@@ -4,6 +4,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/trivy/pkg/licensing/expression"
)
// All map keys must be standardized to be matched
@@ -16,3 +18,234 @@ func TestMap(t *testing.T) {
})
}
}
func TestNormalize(t *testing.T) {
tests := []struct {
licenses []expression.Expression
want string
wantLicense expression.Expression
}{
{
licenses: []expression.Expression{
expression.SimpleExpr{License: " the apache license "},
expression.SimpleExpr{License: " the\tapache \r\nlicense \r\n "},
expression.SimpleExpr{License: " apache "},
expression.SimpleExpr{License: "ApacheLicence"},
expression.SimpleExpr{License: "ApacheLicense"},
expression.SimpleExpr{License: "al-2"},
expression.SimpleExpr{License: "al-v2"},
expression.SimpleExpr{License: "al2"},
expression.SimpleExpr{License: "alv2"},
expression.SimpleExpr{License: "apache - v 2.0"},
expression.SimpleExpr{License: "apache - v. 2.0"},
expression.SimpleExpr{License: "apache - ver 2.0"},
expression.SimpleExpr{License: "apache - version 2.0"},
expression.SimpleExpr{License: "apache 2"},
expression.SimpleExpr{License: "apache 2.0"},
expression.SimpleExpr{License: "apache license (2.0)"},
expression.SimpleExpr{License: "apache license (v. 2)"},
expression.SimpleExpr{License: "apache license (v. 2.0)"},
expression.SimpleExpr{License: "apache license (v2)"},
expression.SimpleExpr{License: "apache license (v2.0)"},
expression.SimpleExpr{License: "apache license (version 2.0)"},
expression.SimpleExpr{License: "apache license 2"},
expression.SimpleExpr{License: "apache license 2.0"},
expression.SimpleExpr{License: "apache license v2"},
expression.SimpleExpr{License: "apache license v2.0"},
expression.SimpleExpr{License: "apache license version 2"},
expression.SimpleExpr{License: "apache license version 2.0"},
expression.SimpleExpr{License: "apache license"},
expression.SimpleExpr{License: "apache license, 2.0"},
expression.SimpleExpr{License: "apache license, asl version 2.0"},
expression.SimpleExpr{License: "apache license, version 2"},
expression.SimpleExpr{License: "apache license, version 2.0 (http://www.apache.org/licenses/license-2.0)"},
expression.SimpleExpr{License: "apache license, version 2.0"},
expression.SimpleExpr{License: "apache license,version 2.0"},
expression.SimpleExpr{License: "apache license,version-2.0"},
expression.SimpleExpr{License: "apache license-2.0"},
expression.SimpleExpr{License: "apache public 2.0"},
expression.SimpleExpr{License: "apache public license 2.0"},
expression.SimpleExpr{License: "apache public license-2.0"},
expression.SimpleExpr{License: "apache public-2"},
expression.SimpleExpr{License: "apache public-2.0"},
expression.SimpleExpr{License: "apache software license (apache-2.0)"},
expression.SimpleExpr{License: "apache software license - version 2.0"},
expression.SimpleExpr{License: "apache software license 2.0"},
expression.SimpleExpr{License: "apache software license, version 2"},
expression.SimpleExpr{License: "apache software license, version 2.0"},
expression.SimpleExpr{License: "apache software-2.0"},
expression.SimpleExpr{License: "apache v 2.0"},
expression.SimpleExpr{License: "apache v. 2.0"},
expression.SimpleExpr{License: "apache v2"},
expression.SimpleExpr{License: "apache v2.0"},
expression.SimpleExpr{License: "apache ver 2.0"},
expression.SimpleExpr{License: "apache ver. 2.0"},
expression.SimpleExpr{License: "apache version 2.0"},
expression.SimpleExpr{License: "apache version 2.0, january 2004"},
expression.SimpleExpr{License: "apache version-2"},
expression.SimpleExpr{License: "apache version-2.0"},
expression.SimpleExpr{License: "apache"},
expression.SimpleExpr{License: "apache, 2"},
expression.SimpleExpr{License: "apache, v2.0"},
expression.SimpleExpr{License: "apache, version 2"},
expression.SimpleExpr{License: "apache, version 2.0"},
expression.SimpleExpr{License: "apache-2"},
expression.SimpleExpr{License: "apache-2.0"},
expression.SimpleExpr{License: "apache-licence"},
expression.SimpleExpr{License: "apache-license"},
expression.SimpleExpr{License: "apache-licensed"},
expression.SimpleExpr{License: "apache-licensed"},
expression.SimpleExpr{License: "asf 2.0"},
expression.SimpleExpr{License: "asl 2"},
expression.SimpleExpr{License: "asl, version 2"},
expression.SimpleExpr{License: "asl2.0"},
expression.SimpleExpr{License: "the apache license"},
expression.SimpleExpr{License: "the apache license"},
},
want: "Apache-2.0",
wantLicense: expression.SimpleExpr{License: "Apache-2.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "Apache+"},
},
want: "Apache-2.0+",
wantLicense: expression.SimpleExpr{License: "Apache-2.0", HasPlus: true},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) V1.1"},
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) VERSION 1.1"},
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL), VERSION 1.1"},
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE 1.1 (CDDL-1.1)"},
},
want: "CDDL-1.1",
wantLicense: expression.SimpleExpr{License: "CDDL-1.1"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE (EPL) 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE (EPL), VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE - V 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE - V1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE - VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE 1.0 (EPL-1.0)"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE V. 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE V1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE, VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC"},
},
want: "EPL-1.0",
wantLicense: expression.SimpleExpr{License: "EPL-1.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE (EUPL V.1.1)"},
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE 1.1 (EUPL 1.1)"},
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE 1.1"},
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE, VERSION 1.1"},
},
want: "EUPL-1.1",
wantLicense: expression.SimpleExpr{License: "EUPL-1.1"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPL-or-later"},
expression.SimpleExpr{License: "GPL+"},
expression.SimpleExpr{License: "GPL-2.0-only+"},
},
want: "GPL-2.0-or-later",
wantLicense: expression.SimpleExpr{License: "GPL-2.0", HasPlus: true},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPL (≥ 3)"},
expression.SimpleExpr{License: "GPL3+"},
expression.SimpleExpr{License: "GPL3-or-later"},
expression.SimpleExpr{License: "GPL3 or later licence"},
},
want: "GPL-3.0-or-later",
wantLicense: expression.SimpleExpr{License: "GPL-3.0", HasPlus: true},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GNU GENERAL PUBLIC LICENSE 3"},
expression.SimpleExpr{License: "GNU GENERAL PUBLIC LICENSE (GPL) V. 3"},
expression.SimpleExpr{License: "GNU GENERAL PUBLIC LICENSE VERSION 3 (GPL V3)"},
},
want: "GPL-3.0-only",
wantLicense: expression.SimpleExpr{License: "GPL-3.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "LGPL LICENSE-3"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE V3"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE V3.0"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE VERSION 3"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE, VERSION 3.0"},
expression.SimpleExpr{License: "GNU LIBRARY OR LESSER GENERAL PUBLIC LICENSE VERSION 3.0 (LGPLV3)"},
expression.SimpleExpr{License: "GNU GENERAL LESSER PUBLIC LICENSE (LGPL) VERSION 3.0"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE (LGPL), VERSION 3"},
},
want: "LGPL-3.0-only",
wantLicense: expression.SimpleExpr{License: "LGPL-3.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "The Unlicense"},
expression.SimpleExpr{License: "Unlicense"},
expression.SimpleExpr{License: "UNLICENSE"},
},
want: "Unlicense",
wantLicense: expression.SimpleExpr{License: "Unlicense"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "MIT License"},
expression.SimpleExpr{License: "http://json.codeplex.com/license"},
},
want: "MIT",
wantLicense: expression.SimpleExpr{License: "MIT"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: " The unmapped license "},
},
want: "The unmapped license",
wantLicense: expression.SimpleExpr{License: "The unmapped license"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "Universal Permissive License, Version 1.0"},
},
want: "UPL-1.0",
wantLicense: expression.SimpleExpr{License: "UPL-1.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPLv2 WITH EXCEPTIONS"},
expression.NewCompoundExpr( // "GPLv2 WITH EXCEPTIONS"
expression.SimpleExpr{License: "GPLv2"},
expression.TokenWith,
expression.SimpleExpr{License: "EXCEPTIONS"},
),
},
want: "GPL-2.0-with-classpath-exception",
wantLicense: expression.SimpleExpr{License: "GPL-2.0-with-classpath-exception"},
},
}
for _, tt := range tests {
t.Run(tt.want, func(t *testing.T) {
for _, ll := range tt.licenses {
got := normalizeLicense(ll.String())
gotLicense := NormalizeLicenseExpression(ll)
assert.Equal(t, tt.want, got)
assert.Equal(t, tt.wantLicense, gotLicense)
}
})
}
}

View File

@@ -6,240 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/trivy/pkg/licensing"
"github.com/aquasecurity/trivy/pkg/licensing/expression"
)
func TestNormalize(t *testing.T) {
tests := []struct {
licenses []expression.Expression
want string
wantLicense expression.Expression
}{
{
licenses: []expression.Expression{
expression.SimpleExpr{License: " the apache license "},
expression.SimpleExpr{License: " the\tapache \r\nlicense \r\n "},
expression.SimpleExpr{License: " apache "},
expression.SimpleExpr{License: "ApacheLicence"},
expression.SimpleExpr{License: "ApacheLicense"},
expression.SimpleExpr{License: "al-2"},
expression.SimpleExpr{License: "al-v2"},
expression.SimpleExpr{License: "al2"},
expression.SimpleExpr{License: "alv2"},
expression.SimpleExpr{License: "apache - v 2.0"},
expression.SimpleExpr{License: "apache - v. 2.0"},
expression.SimpleExpr{License: "apache - ver 2.0"},
expression.SimpleExpr{License: "apache - version 2.0"},
expression.SimpleExpr{License: "apache 2"},
expression.SimpleExpr{License: "apache 2.0"},
expression.SimpleExpr{License: "apache license (2.0)"},
expression.SimpleExpr{License: "apache license (v. 2)"},
expression.SimpleExpr{License: "apache license (v. 2.0)"},
expression.SimpleExpr{License: "apache license (v2)"},
expression.SimpleExpr{License: "apache license (v2.0)"},
expression.SimpleExpr{License: "apache license (version 2.0)"},
expression.SimpleExpr{License: "apache license 2"},
expression.SimpleExpr{License: "apache license 2.0"},
expression.SimpleExpr{License: "apache license v2"},
expression.SimpleExpr{License: "apache license v2.0"},
expression.SimpleExpr{License: "apache license version 2"},
expression.SimpleExpr{License: "apache license version 2.0"},
expression.SimpleExpr{License: "apache license"},
expression.SimpleExpr{License: "apache license, 2.0"},
expression.SimpleExpr{License: "apache license, asl version 2.0"},
expression.SimpleExpr{License: "apache license, version 2"},
expression.SimpleExpr{License: "apache license, version 2.0 (http://www.apache.org/licenses/license-2.0)"},
expression.SimpleExpr{License: "apache license, version 2.0"},
expression.SimpleExpr{License: "apache license,version 2.0"},
expression.SimpleExpr{License: "apache license,version-2.0"},
expression.SimpleExpr{License: "apache license-2.0"},
expression.SimpleExpr{License: "apache public 2.0"},
expression.SimpleExpr{License: "apache public license 2.0"},
expression.SimpleExpr{License: "apache public license-2.0"},
expression.SimpleExpr{License: "apache public-2"},
expression.SimpleExpr{License: "apache public-2.0"},
expression.SimpleExpr{License: "apache software license (apache-2.0)"},
expression.SimpleExpr{License: "apache software license - version 2.0"},
expression.SimpleExpr{License: "apache software license 2.0"},
expression.SimpleExpr{License: "apache software license, version 2"},
expression.SimpleExpr{License: "apache software license, version 2.0"},
expression.SimpleExpr{License: "apache software-2.0"},
expression.SimpleExpr{License: "apache v 2.0"},
expression.SimpleExpr{License: "apache v. 2.0"},
expression.SimpleExpr{License: "apache v2"},
expression.SimpleExpr{License: "apache v2.0"},
expression.SimpleExpr{License: "apache ver 2.0"},
expression.SimpleExpr{License: "apache ver. 2.0"},
expression.SimpleExpr{License: "apache version 2.0"},
expression.SimpleExpr{License: "apache version 2.0, january 2004"},
expression.SimpleExpr{License: "apache version-2"},
expression.SimpleExpr{License: "apache version-2.0"},
expression.SimpleExpr{License: "apache"},
expression.SimpleExpr{License: "apache, 2"},
expression.SimpleExpr{License: "apache, v2.0"},
expression.SimpleExpr{License: "apache, version 2"},
expression.SimpleExpr{License: "apache, version 2.0"},
expression.SimpleExpr{License: "apache-2"},
expression.SimpleExpr{License: "apache-2.0"},
expression.SimpleExpr{License: "apache-licence"},
expression.SimpleExpr{License: "apache-license"},
expression.SimpleExpr{License: "apache-licensed"},
expression.SimpleExpr{License: "apache-licensed"},
expression.SimpleExpr{License: "asf 2.0"},
expression.SimpleExpr{License: "asl 2"},
expression.SimpleExpr{License: "asl, version 2"},
expression.SimpleExpr{License: "asl2.0"},
expression.SimpleExpr{License: "the apache license"},
expression.SimpleExpr{License: "the apache license"},
},
want: "Apache-2.0",
wantLicense: expression.SimpleExpr{License: "Apache-2.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "Apache+"},
},
want: "Apache-2.0+",
wantLicense: expression.SimpleExpr{License: "Apache-2.0", HasPlus: true},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) V1.1"},
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) VERSION 1.1"},
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL), VERSION 1.1"},
expression.SimpleExpr{License: "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE 1.1 (CDDL-1.1)"},
},
want: "CDDL-1.1",
wantLicense: expression.SimpleExpr{License: "CDDL-1.1"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE (EPL) 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE (EPL), VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE - V 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE - V1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE - VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE 1.0 (EPL-1.0)"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE V. 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE V1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC LICENSE, VERSION 1.0"},
expression.SimpleExpr{License: "ECLIPSE PUBLIC"},
},
want: "EPL-1.0",
wantLicense: expression.SimpleExpr{License: "EPL-1.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE (EUPL V.1.1)"},
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE 1.1 (EUPL 1.1)"},
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE 1.1"},
expression.SimpleExpr{License: "EUROPEAN UNION PUBLIC LICENSE, VERSION 1.1"},
},
want: "EUPL-1.1",
wantLicense: expression.SimpleExpr{License: "EUPL-1.1"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPL-or-later"},
expression.SimpleExpr{License: "GPL+"},
expression.SimpleExpr{License: "GPL-2.0-only+"},
},
want: "GPL-2.0-or-later",
wantLicense: expression.SimpleExpr{License: "GPL-2.0", HasPlus: true},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPL (≥ 3)"},
expression.SimpleExpr{License: "GPL3+"},
expression.SimpleExpr{License: "GPL3-or-later"},
expression.SimpleExpr{License: "GPL3 or later licence"},
},
want: "GPL-3.0-or-later",
wantLicense: expression.SimpleExpr{License: "GPL-3.0", HasPlus: true},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GNU GENERAL PUBLIC LICENSE 3"},
expression.SimpleExpr{License: "GNU GENERAL PUBLIC LICENSE (GPL) V. 3"},
expression.SimpleExpr{License: "GNU GENERAL PUBLIC LICENSE VERSION 3 (GPL V3)"},
},
want: "GPL-3.0-only",
wantLicense: expression.SimpleExpr{License: "GPL-3.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "LGPL LICENSE-3"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE V3"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE V3.0"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE VERSION 3"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE, VERSION 3.0"},
expression.SimpleExpr{License: "GNU LIBRARY OR LESSER GENERAL PUBLIC LICENSE VERSION 3.0 (LGPLV3)"},
expression.SimpleExpr{License: "GNU GENERAL LESSER PUBLIC LICENSE (LGPL) VERSION 3.0"},
expression.SimpleExpr{License: "GNU LESSER GENERAL PUBLIC LICENSE (LGPL), VERSION 3"},
},
want: "LGPL-3.0-only",
wantLicense: expression.SimpleExpr{License: "LGPL-3.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "The Unlicense"},
expression.SimpleExpr{License: "Unlicense"},
expression.SimpleExpr{License: "UNLICENSE"},
},
want: "Unlicense",
wantLicense: expression.SimpleExpr{License: "Unlicense"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "MIT License"},
expression.SimpleExpr{License: "http://json.codeplex.com/license"},
},
want: "MIT",
wantLicense: expression.SimpleExpr{License: "MIT"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: " The unmapped license "},
},
want: "The unmapped license",
wantLicense: expression.SimpleExpr{License: "The unmapped license"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "Universal Permissive License, Version 1.0"},
},
want: "UPL-1.0",
wantLicense: expression.SimpleExpr{License: "UPL-1.0"},
},
{
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPLv2 WITH EXCEPTIONS"},
expression.NewCompoundExpr( // "GPLv2 WITH EXCEPTIONS"
expression.SimpleExpr{License: "GPLv2"},
expression.TokenWith,
expression.SimpleExpr{License: "EXCEPTIONS"},
),
},
want: "GPL-2.0-with-classpath-exception",
wantLicense: expression.SimpleExpr{License: "GPL-2.0-with-classpath-exception"},
},
}
for _, tt := range tests {
t.Run(tt.want, func(t *testing.T) {
for _, ll := range tt.licenses {
got := licensing.Normalize(ll.String())
gotLicense := licensing.NormalizeLicense(ll)
assert.Equal(t, tt.want, got)
assert.Equal(t, tt.wantLicense, gotLicense)
}
})
}
}
func TestSplitLicenses(t *testing.T) {
tests := []struct {
name string

View File

@@ -26,7 +26,7 @@ func NewScanner(categories map[types.LicenseCategory][]string) Scanner {
}
func (s *Scanner) Scan(licenseName string) (types.LicenseCategory, string) {
expr, err := expression.Normalize(licenseName, NormalizeLicense)
expr, err := expression.Normalize(licenseName, NormalizeLicenseExpression)
if err != nil {
return types.CategoryUnknown, ""
}

View File

@@ -202,7 +202,7 @@ func (c *IgnoreConfig) MatchLicense(licenseID, filePath string) *IgnoreFinding {
return expr
}
_, err := expression.Normalize(licenseID, licensing.NormalizeLicense, matchLicenses)
_, err := expression.Normalize(licenseID, licensing.NormalizeLicenseExpression, matchLicenses)
if err != nil {
log.WithPrefix("ignore").Debug("Unable to normalize license expression", log.String("license", licenseID), log.Err(err))
return nil

View File

@@ -348,7 +348,7 @@ func (m *Marshaler) normalizeLicense(license string) expression.Expression {
license = strings.ReplaceAll(license, "-with-", " WITH ")
license = strings.ReplaceAll(license, "-WITH-", " WITH ")
normalizedLicenses, err := expression.Normalize(license, licensing.NormalizeLicense, expression.NormalizeForSPDX)
normalizedLicenses, err := expression.Normalize(license, licensing.NormalizeLicenseExpression, expression.NormalizeForSPDX)
if err != nil {
// Not fail on the invalid license
m.logger.Warn("Unable to marshal SPDX licenses", log.String("license", license))

View File

@@ -463,7 +463,7 @@ func (m *Marshaler) normalizeLicenses(licenses []string) (string, []*spdx.OtherL
return expression.SimpleExpr{License: l.LicenseIdentifier}
}
normalizedLicense, err := expression.Normalize(license, licensing.NormalizeLicense, expression.NormalizeForSPDX, replaceOtherLicenses)
normalizedLicense, err := expression.Normalize(license, licensing.NormalizeLicenseExpression, expression.NormalizeForSPDX, replaceOtherLicenses)
if err != nil {
// Not fail on the invalid license
m.logger.Warn("Unable to marshal SPDX licenses", log.String("license", license))