refactor(license): improve license expression normalization (#8257)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
This commit is contained in:
Teppei Fukuda
2025-01-21 16:26:24 +09:00
committed by GitHub
parent c00232720a
commit 2d30dd7241
7 changed files with 270 additions and 194 deletions

View File

@@ -11,7 +11,7 @@ var (
ErrInvalidExpression = xerrors.New("invalid expression error")
)
type NormalizeFunc func(license string) SimpleExpr
type NormalizeFunc func(license Expression) Expression
func parse(license string) (Expression, error) {
l := NewLexer(strings.NewReader(license))
@@ -24,42 +24,48 @@ func parse(license string) (Expression, error) {
return l.result, nil
}
func Normalize(license string, fn ...NormalizeFunc) (string, error) {
func Normalize(license string, funcs ...NormalizeFunc) (string, error) {
expr, err := parse(license)
if err != nil {
return "", xerrors.Errorf("license (%s) parse error: %w", license, err)
}
expr = normalize(expr, fn...)
for _, fn := range funcs {
expr = normalize(expr, fn)
}
return expr.String(), nil
}
func normalize(expr Expression, fn ...NormalizeFunc) Expression {
switch e := expr.(type) {
func normalize(expr Expression, fn NormalizeFunc) Expression {
// Apply normalization function first
normalized := fn(expr)
switch e := normalized.(type) {
case SimpleExpr:
for _, f := range fn {
normalized := f(e.License)
e.License = normalized.License
e.HasPlus = e.HasPlus || normalized.HasPlus
}
return e
// No further normalization for SimpleExpr
case CompoundExpr:
e.left = normalize(e.left, fn...)
e.right = normalize(e.right, fn...)
// Only recursively process if the result is a CompoundExpr
e.left = normalize(e.left, fn)
e.right = normalize(e.right, fn)
e.conjunction.literal = strings.ToUpper(e.conjunction.literal) // e.g. "and" => "AND"
return e
}
return expr
return normalized
}
// NormalizeForSPDX replaces ' ' to '-' in license-id.
// SPDX license MUST NOT have white space between a license-id.
// There MUST be white space on either side of the operator "WITH".
// ref: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions
func NormalizeForSPDX(s string) SimpleExpr {
func NormalizeForSPDX(expr Expression) Expression {
e, ok := expr.(SimpleExpr)
if !ok {
return expr // do not normalize compound expressions
}
var b strings.Builder
for _, c := range s {
for _, c := range e.License {
switch {
// spec: idstring = 1*(ALPHA / DIGIT / "-" / "." )
case isAlphabet(c) || unicode.IsNumber(c) || c == '-' || c == '.':
@@ -72,7 +78,7 @@ func NormalizeForSPDX(s string) SimpleExpr {
_, _ = b.WriteRune('-')
}
}
return SimpleExpr{License: b.String(), HasPlus: false}
return SimpleExpr{License: b.String(), HasPlus: e.HasPlus}
}
func isAlphabet(r rune) bool {

View File

@@ -37,7 +37,7 @@ func TestNormalize(t *testing.T) {
{
name: "upper",
license: "LGPL-2.1-only OR MIT",
fn: func(license string) SimpleExpr { return SimpleExpr{strings.ToUpper(license), false} },
fn: func(license Expression) Expression { return SimpleExpr{strings.ToUpper(license.String()), false} },
want: "LGPL-2.1-ONLY OR MIT",
},
}

View File

@@ -5,6 +5,13 @@ import (
"slices"
)
var (
TokenIdent = Token{token: IDENT, literal: "IDENT"}
TokenAnd = Token{token: AND, literal: "AND"}
TokenOR = Token{token: OR, literal: "OR"}
TokenWith = Token{token: WITH, literal: "WITH"}
)
type Expression interface {
String() string
}
@@ -41,6 +48,14 @@ type CompoundExpr struct {
right Expression
}
func NewCompoundExpr(left Expression, conjunction Token, right Expression) CompoundExpr {
return CompoundExpr{left: left, conjunction: conjunction, right: right}
}
func (c CompoundExpr) Conjunction() Token {
return c.conjunction
}
func (c CompoundExpr) String() string {
left := c.left.String()
if l, ok := c.left.(CompoundExpr); ok {

View File

@@ -674,17 +674,38 @@ func standardizeKeyAndSuffix(name string) expr.SimpleExpr {
}
func Normalize(name string) string {
return NormalizeLicense(name).String()
return NormalizeLicense(expr.SimpleExpr{License: name}).String()
}
func NormalizeLicense(name string) expr.SimpleExpr {
func NormalizeLicense(exp expr.Expression) expr.Expression {
switch e := exp.(type) {
case expr.SimpleExpr:
return normalizeSimpleExpr(e)
case expr.CompoundExpr:
return normalizeCompoundExpr(e)
}
return exp
}
func normalizeSimpleExpr(e expr.SimpleExpr) expr.Expression {
// Always trim leading and trailing spaces, even if we don't find this license in `mapping`.
name = strings.TrimSpace(name)
name := strings.TrimSpace(e.License)
normalized := standardizeKeyAndSuffix(name)
if found, ok := mapping[normalized.License]; ok {
return expr.SimpleExpr{License: found.License, HasPlus: e.HasPlus || found.HasPlus || normalized.HasPlus}
}
return expr.SimpleExpr{License: name, HasPlus: e.HasPlus}
}
func normalizeCompoundExpr(e expr.CompoundExpr) expr.Expression {
if e.Conjunction() != expr.TokenWith {
return e // Do not normalize compound expressions other than "WITH"
}
normalized := standardizeKeyAndSuffix(e.String())
if found, ok := mapping[normalized.License]; ok {
return expr.SimpleExpr{License: found.License, HasPlus: found.HasPlus || normalized.HasPlus}
}
return expr.SimpleExpr{License: name, HasPlus: false}
return e // Do not normalize compound expressions that are not found in `mapping`
}
func SplitLicenses(str string) []string {

View File

@@ -6,224 +6,237 @@ 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 []string
normalized string
normalizedKey string
licenses []expression.Expression
want string
wantLicense expression.Expression
}{
{
licenses: []string{
" the apache license ",
" the\tapache \r\nlicense \r\n ",
" apache ",
"ApacheLicence",
"ApacheLicense",
"al-2",
"al-v2",
"al2",
"alv2",
"apache - v 2.0",
"apache - v. 2.0",
"apache - ver 2.0",
"apache - version 2.0",
"apache 2",
"apache 2.0",
"apache license (2.0)",
"apache license (v. 2)",
"apache license (v. 2.0)",
"apache license (v2)",
"apache license (v2.0)",
"apache license (version 2.0)",
"apache license 2",
"apache license 2.0",
"apache license v2",
"apache license v2.0",
"apache license version 2",
"apache license version 2.0",
"apache license",
"apache license, 2.0",
"apache license, asl version 2.0",
"apache license, version 2",
"apache license, version 2.0 (http://www.apache.org/licenses/license-2.0)",
"apache license, version 2.0",
"apache license,version 2.0",
"apache license,version-2.0",
"apache license-2.0",
"apache public 2.0",
"apache public license 2.0",
"apache public license-2.0",
"apache public-2",
"apache public-2.0",
"apache software license (apache-2.0)",
"apache software license - version 2.0",
"apache software license 2.0",
"apache software license, version 2",
"apache software license, version 2.0",
"apache software-2.0",
"apache v 2.0",
"apache v. 2.0",
"apache v2",
"apache v2.0",
"apache ver 2.0",
"apache ver. 2.0",
"apache version 2.0",
"apache version 2.0, january 2004",
"apache version-2",
"apache version-2.0",
"apache",
"apache, 2",
"apache, v2.0",
"apache, version 2",
"apache, version 2.0",
"apache-2",
"apache-2.0",
"apache-licence",
"apache-license",
"apache-licensed",
"apache-licensed",
"asf 2.0",
"asl 2",
"asl, version 2",
"asl2.0",
"the apache license",
"the apache license",
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"},
},
normalized: "Apache-2.0",
normalizedKey: "Apache-2.0",
want: "Apache-2.0",
wantLicense: expression.SimpleExpr{License: "Apache-2.0"},
},
{
licenses: []string{
"Apache+",
licenses: []expression.Expression{
expression.SimpleExpr{License: "Apache+"},
},
normalized: "Apache-2.0+",
normalizedKey: "Apache-2.0",
want: "Apache-2.0+",
wantLicense: expression.SimpleExpr{License: "Apache-2.0", HasPlus: true},
},
{
licenses: []string{
"COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) V1.1",
"COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) VERSION 1.1",
"COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL), VERSION 1.1",
"COMMON DEVELOPMENT AND DISTRIBUTION LICENSE 1.1 (CDDL-1.1)",
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)"},
},
normalized: "CDDL-1.1",
normalizedKey: "CDDL-1.1",
want: "CDDL-1.1",
wantLicense: expression.SimpleExpr{License: "CDDL-1.1"},
},
{
licenses: []string{
"ECLIPSE PUBLIC LICENSE (EPL) 1.0",
"ECLIPSE PUBLIC LICENSE (EPL), VERSION 1.0",
"ECLIPSE PUBLIC LICENSE - V 1.0",
"ECLIPSE PUBLIC LICENSE - V1.0",
"ECLIPSE PUBLIC LICENSE - VERSION 1.0",
"ECLIPSE PUBLIC LICENSE 1.0 (EPL-1.0)",
"ECLIPSE PUBLIC LICENSE 1.0",
"ECLIPSE PUBLIC LICENSE V. 1.0",
"ECLIPSE PUBLIC LICENSE V1.0",
"ECLIPSE PUBLIC LICENSE VERSION 1.0",
"ECLIPSE PUBLIC LICENSE, VERSION 1.0",
"ECLIPSE PUBLIC",
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"},
},
normalized: "EPL-1.0",
normalizedKey: "EPL-1.0",
want: "EPL-1.0",
wantLicense: expression.SimpleExpr{License: "EPL-1.0"},
},
{
licenses: []string{
"EUROPEAN UNION PUBLIC LICENSE (EUPL V.1.1)",
"EUROPEAN UNION PUBLIC LICENSE 1.1 (EUPL 1.1)",
"EUROPEAN UNION PUBLIC LICENSE 1.1",
"EUROPEAN UNION PUBLIC LICENSE, VERSION 1.1",
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"},
},
normalized: "EUPL-1.1",
normalizedKey: "EUPL-1.1",
want: "EUPL-1.1",
wantLicense: expression.SimpleExpr{License: "EUPL-1.1"},
},
{
licenses: []string{
"GPL-or-later",
"GPL+",
"GPL-2.0-only+",
licenses: []expression.Expression{
expression.SimpleExpr{License: "GPL-or-later"},
expression.SimpleExpr{License: "GPL+"},
expression.SimpleExpr{License: "GPL-2.0-only+"},
},
normalized: "GPL-2.0-or-later",
normalizedKey: "GPL-2.0",
want: "GPL-2.0-or-later",
wantLicense: expression.SimpleExpr{License: "GPL-2.0", HasPlus: true},
},
{
licenses: []string{
"GPL (≥ 3)",
"GPL3+",
"GPL3-or-later",
"GPL3 or later licence",
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"},
},
normalized: "GPL-3.0-or-later",
normalizedKey: "GPL-3.0",
want: "GPL-3.0-or-later",
wantLicense: expression.SimpleExpr{License: "GPL-3.0", HasPlus: true},
},
{
licenses: []string{
"GNU GENERAL PUBLIC LICENSE 3",
"GNU GENERAL PUBLIC LICENSE (GPL) V. 3",
"GNU GENERAL PUBLIC LICENSE VERSION 3 (GPL V3)",
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)"},
},
normalized: "GPL-3.0-only",
normalizedKey: "GPL-3.0",
want: "GPL-3.0-only",
wantLicense: expression.SimpleExpr{License: "GPL-3.0"},
},
{
licenses: []string{
"LGPL LICENSE-3",
"GNU LESSER GENERAL PUBLIC LICENSE V3",
"GNU LESSER GENERAL PUBLIC LICENSE V3.0",
"GNU LESSER GENERAL PUBLIC LICENSE VERSION 3",
"GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0",
"GNU LESSER GENERAL PUBLIC LICENSE, VERSION 3.0",
"GNU LIBRARY OR LESSER GENERAL PUBLIC LICENSE VERSION 3.0 (LGPLV3)",
"GNU GENERAL LESSER PUBLIC LICENSE (LGPL) VERSION 3.0",
"GNU LESSER GENERAL PUBLIC LICENSE (LGPL), VERSION 3",
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"},
},
normalized: "LGPL-3.0-only",
normalizedKey: "LGPL-3.0",
want: "LGPL-3.0-only",
wantLicense: expression.SimpleExpr{License: "LGPL-3.0"},
},
{
licenses: []string{
"The Unlicense",
"Unlicense",
"Unlicensed",
"UNLICENSE",
"UNLICENSED",
licenses: []expression.Expression{
expression.SimpleExpr{License: "The Unlicense"},
expression.SimpleExpr{License: "Unlicense"},
expression.SimpleExpr{License: "Unlicensed"},
expression.SimpleExpr{License: "UNLICENSE"},
expression.SimpleExpr{License: "UNLICENSED"},
},
normalized: "Unlicense",
normalizedKey: "Unlicense",
want: "Unlicense",
wantLicense: expression.SimpleExpr{License: "Unlicense"},
},
{
licenses: []string{
"MIT License",
"http://json.codeplex.com/license",
licenses: []expression.Expression{
expression.SimpleExpr{License: "MIT License"},
expression.SimpleExpr{License: "http://json.codeplex.com/license"},
},
normalized: "MIT",
normalizedKey: "MIT",
want: "MIT",
wantLicense: expression.SimpleExpr{License: "MIT"},
},
{
licenses: []string{
" The unmapped license ",
licenses: []expression.Expression{
expression.SimpleExpr{License: " The unmapped license "},
},
normalized: "The unmapped license",
normalizedKey: "The unmapped license",
want: "The unmapped license",
wantLicense: expression.SimpleExpr{License: "The unmapped license"},
},
{
licenses: []string{
"Universal Permissive License, Version 1.0",
licenses: []expression.Expression{
expression.SimpleExpr{License: "Universal Permissive License, Version 1.0"},
},
normalized: "UPL-1.0",
normalizedKey: "UPL-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.normalized, func(t *testing.T) {
t.Run(tt.want, func(t *testing.T) {
for _, ll := range tt.licenses {
normalized := licensing.Normalize(ll)
normalizedKey := licensing.NormalizeLicense(ll).License
assert.Equal(t, tt.normalized, normalized)
assert.Equal(t, tt.normalizedKey, normalizedKey)
got := licensing.Normalize(ll.String())
gotLicense := licensing.NormalizeLicense(ll)
assert.Equal(t, tt.want, got)
assert.Equal(t, tt.wantLicense, gotLicense)
}
})
}

View File

@@ -5,6 +5,7 @@ import (
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/licensing/expression"
)
type ScannerOption struct {
@@ -21,9 +22,17 @@ func NewScanner(categories map[types.LicenseCategory][]string) Scanner {
}
func (s *Scanner) Scan(licenseName string) (types.LicenseCategory, string) {
license := NormalizeLicense(licenseName)
normalized := NormalizeLicense(expression.SimpleExpr{License: licenseName})
var normalizedName string
switch normalized := normalized.(type) {
case expression.SimpleExpr:
normalizedName = normalized.License
case expression.CompoundExpr:
normalizedName = normalized.String()
}
for category, names := range s.categories {
if slices.Contains(names, license.License) {
if slices.Contains(names, normalizedName) {
return category, categoryToSeverity(category).String()
}
}

View File

@@ -30,6 +30,18 @@ func TestScanner_Scan(t *testing.T) {
wantCategory: types.CategoryForbidden,
wantSeverity: "CRITICAL",
},
{
name: "has plus",
categories: map[types.LicenseCategory][]string{
types.CategoryForbidden: {
expression.BSD3Clause,
expression.Apache20,
},
},
licenseName: "Apache-2.0+",
wantCategory: types.CategoryForbidden,
wantSeverity: "CRITICAL",
},
{
name: "restricted",
categories: map[types.LicenseCategory][]string{