mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
add cdx group as purl namespace (#3922)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
e23ca43a83
commit
ac883f52ed
@ -71,31 +71,14 @@ func Backfill(p *pkg.Package) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setJavaMetadataFromPurl(p *pkg.Package, purl packageurl.PackageURL) {
|
func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
|
||||||
if p.Type != pkg.JavaPkg {
|
if p.Type != pkg.JavaPkg {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if purl.Namespace != "" {
|
if p.Metadata == nil {
|
||||||
if p.Metadata == nil {
|
// since we don't know if the purl elements directly came from pom properties or the manifest,
|
||||||
p.Metadata = pkg.JavaArchive{}
|
// we can only go as far as to set the type to JavaArchive, but not fill in the group id and artifact id
|
||||||
}
|
p.Metadata = pkg.JavaArchive{}
|
||||||
meta, got := p.Metadata.(pkg.JavaArchive)
|
|
||||||
if got && meta.PomProperties == nil {
|
|
||||||
meta.PomProperties = &pkg.JavaPomProperties{}
|
|
||||||
p.Metadata = meta
|
|
||||||
}
|
|
||||||
if meta.PomProperties != nil {
|
|
||||||
// capture the group id from the purl if it is not already set
|
|
||||||
if meta.PomProperties.ArtifactID == "" {
|
|
||||||
meta.PomProperties.ArtifactID = purl.Name
|
|
||||||
}
|
|
||||||
if meta.PomProperties.GroupID == "" {
|
|
||||||
meta.PomProperties.GroupID = purl.Namespace
|
|
||||||
}
|
|
||||||
if meta.PomProperties.Version == "" {
|
|
||||||
meta.PomProperties.Version = purl.Version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -101,13 +101,9 @@ func Test_Backfill(t *testing.T) {
|
|||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Name: "some-thing",
|
Name: "some-thing",
|
||||||
Version: "1.2.3",
|
Version: "1.2.3",
|
||||||
Metadata: pkg.JavaArchive{
|
// we intentionally don't claim we found a pom properties file with a groupID from the purl.
|
||||||
PomProperties: &pkg.JavaPomProperties{
|
// but we do claim that we found java data with an empty type.
|
||||||
GroupID: "org.apache",
|
Metadata: pkg.JavaArchive{},
|
||||||
ArtifactID: "some-thing",
|
|
||||||
Version: "1.2.3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package helpers
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/CycloneDX/cyclonedx-go"
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
|
||||||
@ -90,15 +91,19 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
|
|||||||
Locations: decodeLocations(values),
|
Locations: decodeLocations(values),
|
||||||
Licenses: pkg.NewLicenseSet(decodeLicenses(c)...),
|
Licenses: pkg.NewLicenseSet(decodeLicenses(c)...),
|
||||||
CPEs: decodeCPEs(c),
|
CPEs: decodeCPEs(c),
|
||||||
PURL: c.PackageURL,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: this may write in syft package type information
|
||||||
DecodeInto(p, values, "syft:package", CycloneDXFields)
|
DecodeInto(p, values, "syft:package", CycloneDXFields)
|
||||||
|
|
||||||
metadataType := values["syft:package:metadataType"]
|
metadataType := values["syft:package:metadataType"]
|
||||||
|
|
||||||
p.Metadata = decodePackageMetadata(values, c, metadataType)
|
p.Metadata = decodePackageMetadata(values, c, metadataType)
|
||||||
|
|
||||||
|
// this will either use the purl from the component or generate a new one based off of any type information
|
||||||
|
// that was decoded above.
|
||||||
|
p.PURL = getPURL(c, p.Type)
|
||||||
|
|
||||||
if p.Type == "" {
|
if p.Type == "" {
|
||||||
p.Type = pkg.TypeFromPURL(p.PURL)
|
p.Type = pkg.TypeFromPURL(p.PURL)
|
||||||
}
|
}
|
||||||
@ -111,6 +116,41 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPURL(c *cyclonedx.Component, ty pkg.Type) string {
|
||||||
|
if c.PackageURL != "" {
|
||||||
|
// if there is a purl that where the namespace does not match the group information, we may
|
||||||
|
// accidentally drop group. We should consider adding group as a top-level syft package field.
|
||||||
|
return c.PackageURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(c.BOMRef, "pkg:") {
|
||||||
|
// the bomref is a purl, so try to use that as the purl
|
||||||
|
_, err := packageurl.FromString(c.BOMRef)
|
||||||
|
if err == nil {
|
||||||
|
return c.BOMRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ty == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
tyStr := ty.PackageURLType()
|
||||||
|
switch tyStr {
|
||||||
|
case "", packageurl.TypeGeneric:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
purl := packageurl.PackageURL{
|
||||||
|
Type: tyStr,
|
||||||
|
Namespace: c.Group,
|
||||||
|
Name: c.Name,
|
||||||
|
Version: c.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
return purl.ToString()
|
||||||
|
}
|
||||||
|
|
||||||
func setPackageName(p *pkg.Package, c *cyclonedx.Component) {
|
func setPackageName(p *pkg.Package, c *cyclonedx.Component) {
|
||||||
name := c.Name
|
name := c.Name
|
||||||
if c.Group != "" {
|
if c.Group != "" {
|
||||||
|
|||||||
@ -275,6 +275,7 @@ func Test_decodeComponent(t *testing.T) {
|
|||||||
component cyclonedx.Component
|
component cyclonedx.Component
|
||||||
wantLanguage pkg.Language
|
wantLanguage pkg.Language
|
||||||
wantMetadata any
|
wantMetadata any
|
||||||
|
wantPURL string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "derive language from pURL if missing",
|
name: "derive language from pURL if missing",
|
||||||
@ -286,6 +287,18 @@ func Test_decodeComponent(t *testing.T) {
|
|||||||
BOMRef: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
|
BOMRef: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
|
||||||
},
|
},
|
||||||
wantLanguage: pkg.Java,
|
wantLanguage: pkg.Java,
|
||||||
|
wantPURL: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "derive language from bomref if missing",
|
||||||
|
component: cyclonedx.Component{
|
||||||
|
Name: "ch.qos.logback/logback-classic",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Type: "library",
|
||||||
|
BOMRef: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
|
||||||
|
},
|
||||||
|
wantLanguage: pkg.Java,
|
||||||
|
wantPURL: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "handle RpmdbMetadata type without properties",
|
name: "handle RpmdbMetadata type without properties",
|
||||||
@ -303,6 +316,7 @@ func Test_decodeComponent(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantMetadata: pkg.RpmDBEntry{},
|
wantMetadata: pkg.RpmDBEntry{},
|
||||||
|
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "handle RpmdbMetadata type with properties",
|
name: "handle RpmdbMetadata type with properties",
|
||||||
@ -326,6 +340,24 @@ func Test_decodeComponent(t *testing.T) {
|
|||||||
wantMetadata: pkg.RpmDBEntry{
|
wantMetadata: pkg.RpmDBEntry{
|
||||||
Release: "some-release",
|
Release: "some-release",
|
||||||
},
|
},
|
||||||
|
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "generate a purl from package type",
|
||||||
|
component: cyclonedx.Component{
|
||||||
|
Name: "log4j",
|
||||||
|
Group: "org.apache.logging.log4j",
|
||||||
|
Version: "2.0.4",
|
||||||
|
Type: "library",
|
||||||
|
BOMRef: "log4j",
|
||||||
|
Properties: &[]cyclonedx.Property{
|
||||||
|
{
|
||||||
|
Name: "syft:package:type",
|
||||||
|
Value: "java-archive",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantPURL: "pkg:maven/org.apache.logging.log4j/log4j@2.0.4",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,9 +370,122 @@ func Test_decodeComponent(t *testing.T) {
|
|||||||
if tt.wantMetadata != nil {
|
if tt.wantMetadata != nil {
|
||||||
assert.Truef(t, reflect.DeepEqual(tt.wantMetadata, p.Metadata), "metadata should match: %+v != %+v", tt.wantMetadata, p.Metadata)
|
assert.Truef(t, reflect.DeepEqual(tt.wantMetadata, p.Metadata), "metadata should match: %+v != %+v", tt.wantMetadata, p.Metadata)
|
||||||
}
|
}
|
||||||
if tt.wantMetadata == nil && tt.wantLanguage == "" {
|
|
||||||
|
if tt.wantPURL != "" {
|
||||||
|
assert.Equal(t, tt.wantPURL, p.PURL, "purl should match")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.wantMetadata == nil && tt.wantLanguage == "" && tt.wantPURL == "" {
|
||||||
t.Fatal("this is a useless test, please remove it")
|
t.Fatal("this is a useless test, please remove it")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetPURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
component *cyclonedx.Component
|
||||||
|
pkgType pkg.Type
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "component with PackageURL",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
PackageURL: "pkg:npm/lodash@4.17.21",
|
||||||
|
Name: "lodash",
|
||||||
|
Version: "4.17.20", // different version to verify PackageURL is used
|
||||||
|
Group: "npm",
|
||||||
|
},
|
||||||
|
pkgType: pkg.NpmPkg,
|
||||||
|
expected: "pkg:npm/lodash@4.17.21",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with BOMRef as valid PURL",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
BOMRef: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
|
||||||
|
Name: "commons-lang3",
|
||||||
|
Version: "3.11.0", // different version to verify BOMRef is used
|
||||||
|
Group: "org.apache.commons",
|
||||||
|
},
|
||||||
|
pkgType: pkg.JavaPkg,
|
||||||
|
expected: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with BOMRef not a valid PURL",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
BOMRef: "not-a-purl-ref",
|
||||||
|
Name: "commons-lang3",
|
||||||
|
Version: "3.12.0",
|
||||||
|
Group: "org.apache.commons",
|
||||||
|
},
|
||||||
|
pkgType: pkg.JavaPkg,
|
||||||
|
expected: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with unknown package type",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
Name: "some-component",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Group: "org.example",
|
||||||
|
},
|
||||||
|
pkgType: pkg.UnknownPkg,
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with empty package type",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
Name: "some-component",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Group: "org.example",
|
||||||
|
},
|
||||||
|
pkgType: "",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with generic package type",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
Name: "some-component",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Group: "org.example",
|
||||||
|
},
|
||||||
|
pkgType: pkg.LinuxKernelModulePkg,
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with valid package type",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
Name: "react",
|
||||||
|
Version: "18.2.0",
|
||||||
|
Group: "facebook",
|
||||||
|
},
|
||||||
|
pkgType: pkg.NpmPkg,
|
||||||
|
expected: "pkg:npm/facebook/react@18.2.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with no group",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
Name: "express",
|
||||||
|
Version: "4.18.2",
|
||||||
|
},
|
||||||
|
pkgType: pkg.NpmPkg,
|
||||||
|
expected: "pkg:npm/express@4.18.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "component with no version",
|
||||||
|
component: &cyclonedx.Component{
|
||||||
|
Name: "express",
|
||||||
|
Group: "npm",
|
||||||
|
},
|
||||||
|
pkgType: pkg.NpmPkg,
|
||||||
|
expected: "pkg:npm/npm/express",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := getPURL(tt.component, tt.pkgType)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -165,13 +165,9 @@ func TestDecoder_Decode(t *testing.T) {
|
|||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
PURL: "pkg:maven/org.apache/some-pkg@4.11.3",
|
PURL: "pkg:maven/org.apache/some-pkg@4.11.3",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Metadata: pkg.JavaArchive{
|
// we intentionally do not claim we found a pom properties file (don't derive this from the purl).
|
||||||
PomProperties: &pkg.JavaPomProperties{
|
// but we need a metadata allocated since all Java packages have a this metadata type (a consistency point)
|
||||||
GroupID: "org.apache",
|
Metadata: pkg.JavaArchive{},
|
||||||
ArtifactID: "some-pkg",
|
|
||||||
Version: "4.11.3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user