mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 10:36:45 +01:00
fix: add support for licenses not found on list (#1540)
Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
parent
deb7052f41
commit
38a090c218
@ -12,9 +12,22 @@ import (
|
|||||||
// EX: gpl-2.0.0-only ---> GPL-2.0-only
|
// EX: gpl-2.0.0-only ---> GPL-2.0-only
|
||||||
// See the debian link for more details on the spdx license differences
|
// See the debian link for more details on the spdx license differences
|
||||||
|
|
||||||
|
const (
|
||||||
|
LicenseRefPrefix = "LicenseRef-" // prefix for non-standard licenses
|
||||||
|
)
|
||||||
|
|
||||||
//go:generate go run ./generate
|
//go:generate go run ./generate
|
||||||
|
|
||||||
func ID(id string) (string, bool) {
|
func ID(id string) (value, other string, exists bool) {
|
||||||
value, exists := licenseIDs[strings.ToLower(id)]
|
id = strings.TrimSpace(id)
|
||||||
return value, exists
|
// ignore blank strings or the joiner
|
||||||
|
if id == "" || id == "AND" {
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
// first look for a canonical license
|
||||||
|
if value, exists := licenseIDs[strings.ToLower(id)]; exists {
|
||||||
|
return value, "", exists
|
||||||
|
}
|
||||||
|
// we did not find, so treat it as a separate license
|
||||||
|
return "", id, true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,52 +9,91 @@ import (
|
|||||||
func TestIDParse(t *testing.T) {
|
func TestIDParse(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
shortName string
|
shortName string
|
||||||
spdx string
|
id string
|
||||||
|
other string
|
||||||
|
found bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"GPL-1-only",
|
"GPL-1-only",
|
||||||
"GPL-1.0-only",
|
"GPL-1.0-only",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"GPL-2",
|
"GPL-2",
|
||||||
"GPL-2.0-only",
|
"GPL-2.0-only",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"GPL-2+",
|
"GPL-2+",
|
||||||
"GPL-2.0-or-later",
|
"GPL-2.0-or-later",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"GPL-3.0.0-or-later",
|
"GPL-3.0.0-or-later",
|
||||||
"GPL-3.0-or-later",
|
"GPL-3.0-or-later",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"GPL-3-with-autoconf-exception",
|
"GPL-3-with-autoconf-exception",
|
||||||
"GPL-3.0-with-autoconf-exception",
|
"GPL-3.0-with-autoconf-exception",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CC-by-nc-3-de",
|
"CC-by-nc-3-de",
|
||||||
"CC-BY-NC-3.0-DE",
|
"CC-BY-NC-3.0-DE",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
// the below few cases are NOT expected, however, seem unavoidable given the current approach
|
// the below few cases are NOT expected, however, seem unavoidable given the current approach
|
||||||
{
|
{
|
||||||
"w3c-20150513.0.0",
|
"w3c-20150513.0.0",
|
||||||
"W3C-20150513",
|
"W3C-20150513",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spencer-86.0.0",
|
"spencer-86.0.0",
|
||||||
"Spencer-86",
|
"Spencer-86",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unicode-dfs-2015.0.0",
|
"unicode-dfs-2015.0.0",
|
||||||
"Unicode-DFS-2015",
|
"Unicode-DFS-2015",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Unknown",
|
||||||
|
"",
|
||||||
|
"Unknown",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
" ",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AND",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.shortName, func(t *testing.T) {
|
t.Run(test.shortName, func(t *testing.T) {
|
||||||
got, exists := ID(test.shortName)
|
value, other, exists := ID(test.shortName)
|
||||||
assert.True(t, exists)
|
assert.Equal(t, test.found, exists)
|
||||||
assert.Equal(t, test.spdx, got)
|
assert.Equal(t, test.id, value)
|
||||||
|
assert.Equal(t, test.other, other)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,10 +10,11 @@ import (
|
|||||||
func encodeLicenses(p pkg.Package) *cyclonedx.Licenses {
|
func encodeLicenses(p pkg.Package) *cyclonedx.Licenses {
|
||||||
lc := cyclonedx.Licenses{}
|
lc := cyclonedx.Licenses{}
|
||||||
for _, licenseName := range p.Licenses {
|
for _, licenseName := range p.Licenses {
|
||||||
if value, exists := spdxlicense.ID(licenseName); exists {
|
if value, other, exists := spdxlicense.ID(licenseName); exists {
|
||||||
lc = append(lc, cyclonedx.LicenseChoice{
|
lc = append(lc, cyclonedx.LicenseChoice{
|
||||||
License: &cyclonedx.License{
|
License: &cyclonedx.License{
|
||||||
ID: value,
|
ID: value,
|
||||||
|
Name: other,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -28,7 +29,16 @@ func decodeLicenses(c *cyclonedx.Component) (out []string) {
|
|||||||
if c.Licenses != nil {
|
if c.Licenses != nil {
|
||||||
for _, l := range *c.Licenses {
|
for _, l := range *c.Licenses {
|
||||||
if l.License != nil {
|
if l.License != nil {
|
||||||
out = append(out, l.License.ID)
|
var lic string
|
||||||
|
switch {
|
||||||
|
case l.License.ID != "":
|
||||||
|
lic = l.License.ID
|
||||||
|
case l.License.Name != "":
|
||||||
|
lic = l.License.Name
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, lic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,9 @@ func Test_encodeLicense(t *testing.T) {
|
|||||||
"made-up",
|
"made-up",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: &cyclonedx.Licenses{
|
||||||
|
{License: &cyclonedx.License{Name: "made-up"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with SPDX license",
|
name: "with SPDX license",
|
||||||
|
|||||||
@ -22,12 +22,7 @@ func License(p pkg.Package) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/
|
// take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/
|
||||||
var parsedLicenses []string
|
parsedLicenses := parseLicenses(p.Licenses)
|
||||||
for _, l := range p.Licenses {
|
|
||||||
if value, exists := spdxlicense.ID(l); exists {
|
|
||||||
parsedLicenses = append(parsedLicenses, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parsedLicenses) == 0 {
|
if len(parsedLicenses) == 0 {
|
||||||
return NOASSERTION
|
return NOASSERTION
|
||||||
@ -35,3 +30,16 @@ func License(p pkg.Package) string {
|
|||||||
|
|
||||||
return strings.Join(parsedLicenses, " AND ")
|
return strings.Join(parsedLicenses, " AND ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLicenses(raw []string) (parsedLicenses []string) {
|
||||||
|
for _, l := range raw {
|
||||||
|
if value, other, exists := spdxlicense.ID(l); exists {
|
||||||
|
parsed := value
|
||||||
|
if other != "" {
|
||||||
|
parsed = spdxlicense.LicenseRefPrefix + other
|
||||||
|
}
|
||||||
|
parsedLicenses = append(parsedLicenses, parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ func Test_License(t *testing.T) {
|
|||||||
"made-up",
|
"made-up",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: NOASSERTION,
|
expected: "LicenseRef-made-up",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with SPDX license",
|
name: "with SPDX license",
|
||||||
|
|||||||
@ -124,6 +124,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
|||||||
Packages: toPackages(s.Artifacts.PackageCatalog, s),
|
Packages: toPackages(s.Artifacts.PackageCatalog, s),
|
||||||
Files: toFiles(s),
|
Files: toFiles(s),
|
||||||
Relationships: relationships,
|
Relationships: relationships,
|
||||||
|
OtherLicenses: toOtherLicenses(s.Artifacts.PackageCatalog),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,6 +512,28 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) {
|
|||||||
return ty
|
return ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense {
|
||||||
|
licenses := map[string]bool{}
|
||||||
|
for _, pkg := range catalog.Sorted() {
|
||||||
|
for _, license := range parseLicenses(pkg.Licenses) {
|
||||||
|
if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) {
|
||||||
|
licenses[license] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var result []*spdx.OtherLicense
|
||||||
|
for license := range licenses {
|
||||||
|
// separate the actual ID from the prefix
|
||||||
|
name := strings.TrimPrefix(license, spdxlicense.LicenseRefPrefix)
|
||||||
|
result = append(result, &spdx.OtherLicense{
|
||||||
|
LicenseIdentifier: license,
|
||||||
|
LicenseName: name,
|
||||||
|
ExtractedText: NONE, // we probably should have some extracted text here, but this is good enough for now
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: handle SPDX excludes file case
|
// TODO: handle SPDX excludes file case
|
||||||
// f file is an "excludes" file, skip it /* exclude SPDX analysis file(s) */
|
// f file is an "excludes" file, skip it /* exclude SPDX analysis file(s) */
|
||||||
// see: https://spdx.github.io/spdx-spec/v2.3/package-information/#79-package-verification-code-field
|
// see: https://spdx.github.io/spdx-spec/v2.3/package-information/#79-package-verification-code-field
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user