Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 5 additions & 22 deletions syft/format/internal/backfill.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
return
}
if purl.Namespace != "" {
if p.Metadata == nil {
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
}
}
if p.Metadata == nil {
// since we don't know if the purl elements directly came from pom properties or the manifest,
// 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{}
}
}

Expand Down
10 changes: 3 additions & 7 deletions syft/format/internal/backfill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,9 @@ func Test_Backfill(t *testing.T) {
Language: pkg.Java,
Name: "some-thing",
Version: "1.2.3",
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.apache",
ArtifactID: "some-thing",
Version: "1.2.3",
},
},
// we intentionally don't claim we found a pom properties file with a groupID from the purl.
// but we do claim that we found java data with an empty type.
Metadata: pkg.JavaArchive{},
},
},
}
Expand Down
42 changes: 41 additions & 1 deletion syft/format/internal/cyclonedxutil/helpers/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package helpers
import (
"fmt"
"reflect"
"strings"

"github.com/CycloneDX/cyclonedx-go"

Expand Down Expand Up @@ -90,15 +91,19 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
Locations: decodeLocations(values),
Licenses: pkg.NewLicenseSet(decodeLicenses(c)...),
CPEs: decodeCPEs(c),
PURL: c.PackageURL,
}

// note: this may write in syft package type information
DecodeInto(p, values, "syft:package", CycloneDXFields)

metadataType := values["syft:package: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 == "" {
p.Type = pkg.TypeFromPURL(p.PURL)
}
Expand All @@ -111,6 +116,41 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
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) {
name := c.Name
if c.Group != "" {
Expand Down
147 changes: 146 additions & 1 deletion syft/format/internal/cyclonedxutil/helpers/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ func Test_decodeComponent(t *testing.T) {
component cyclonedx.Component
wantLanguage pkg.Language
wantMetadata any
wantPURL string
}{
{
name: "derive language from pURL if missing",
Expand All @@ -286,6 +287,18 @@ func Test_decodeComponent(t *testing.T) {
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: "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",
Expand All @@ -303,6 +316,7 @@ func Test_decodeComponent(t *testing.T) {
},
},
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",
Expand All @@ -326,6 +340,24 @@ func Test_decodeComponent(t *testing.T) {
wantMetadata: pkg.RpmDBEntry{
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",
},
}

Expand All @@ -338,9 +370,122 @@ func Test_decodeComponent(t *testing.T) {
if tt.wantMetadata != nil {
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")
}
})
}
}

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)
})
}
}
10 changes: 3 additions & 7 deletions syft/format/purls/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,9 @@ func TestDecoder_Decode(t *testing.T) {
Type: pkg.JavaPkg,
PURL: "pkg:maven/org.apache/some-pkg@4.11.3",
Language: pkg.Java,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.apache",
ArtifactID: "some-pkg",
Version: "4.11.3",
},
},
// we intentionally do not claim we found a pom properties file (don't derive this from the purl).
// but we need a metadata allocated since all Java packages have a this metadata type (a consistency point)
Metadata: pkg.JavaArchive{},
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion syft/pkg/cataloger/redhat/parse_rpm_archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"strconv"
"time"

rpmdb "github.com/anchore/go-rpmdb/pkg"
"github.com/sassoftware/go-rpmutils"

rpmdb "github.com/anchore/go-rpmdb/pkg"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
Expand Down
1 change: 0 additions & 1 deletion syft/pkg/cataloger/redhat/parse_rpm_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"strings"

rpmdb "github.com/anchore/go-rpmdb/pkg"

"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
Expand Down
Loading