mirror of
https://github.com/anchore/syft.git
synced 2026-10-13 22:57:19 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c3e49957c | ||
|
|
4762ba0943 | ||
|
|
d1635971a1 | ||
|
|
9467bd66c2 | ||
|
|
59107324ce |
@@ -210,24 +210,33 @@ func collectRelationships(bom *cyclonedx.BOM, s *sbom.SBOM, idMap map[string]int
|
||||
return
|
||||
}
|
||||
for _, d := range *bom.Dependencies {
|
||||
to, fromExists := idMap[d.Ref].(artifact.Identifiable)
|
||||
if !fromExists {
|
||||
continue
|
||||
}
|
||||
|
||||
if d.Dependencies == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
toPtr, toExists := idMap[d.Ref]
|
||||
if !toExists {
|
||||
continue
|
||||
}
|
||||
to, ok := common.PtrToStruct(toPtr).(artifact.Identifiable)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, t := range *d.Dependencies {
|
||||
from, toExists := idMap[t].(artifact.Identifiable)
|
||||
if !toExists {
|
||||
fromPtr, fromExists := idMap[t]
|
||||
if !fromExists {
|
||||
continue
|
||||
}
|
||||
from, ok := common.PtrToStruct(fromPtr).(artifact.Identifiable)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
s.Relationships = append(s.Relationships, artifact.Relationship{
|
||||
From: from,
|
||||
To: to,
|
||||
Type: artifact.DependencyOfRelationship, // FIXME this information is lost
|
||||
// match assumptions in encoding, that this is the only type of relationship captured:
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import (
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
@@ -336,3 +338,101 @@ func Test_missingComponentsDecode(t *testing.T) {
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_decodeDependencies(t *testing.T) {
|
||||
c1 := cyclonedx.Component{
|
||||
Name: "c1",
|
||||
}
|
||||
|
||||
c2 := cyclonedx.Component{
|
||||
Name: "c2",
|
||||
}
|
||||
|
||||
c3 := cyclonedx.Component{
|
||||
Name: "c3",
|
||||
}
|
||||
|
||||
for _, c := range []*cyclonedx.Component{&c1, &c2, &c3} {
|
||||
c.BOMRef = c.Name
|
||||
}
|
||||
|
||||
setTypes := func(typ cyclonedx.ComponentType, components ...cyclonedx.Component) *[]cyclonedx.Component {
|
||||
var out []cyclonedx.Component
|
||||
for _, c := range components {
|
||||
c.Type = typ
|
||||
out = append(out, c)
|
||||
}
|
||||
return &out
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sbom cyclonedx.BOM
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "dependencies decoded as dependencyOf relationships",
|
||||
sbom: cyclonedx.BOM{
|
||||
Components: setTypes(cyclonedx.ComponentTypeLibrary,
|
||||
c1,
|
||||
c2,
|
||||
c3,
|
||||
),
|
||||
Dependencies: &[]cyclonedx.Dependency{
|
||||
{
|
||||
Ref: c1.BOMRef,
|
||||
Dependencies: &[]string{
|
||||
c2.BOMRef,
|
||||
c3.BOMRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{c2.Name, c3.Name},
|
||||
},
|
||||
{
|
||||
name: "dependencies skipped with unhandled components",
|
||||
sbom: cyclonedx.BOM{
|
||||
Components: setTypes("",
|
||||
c1,
|
||||
c2,
|
||||
c3,
|
||||
),
|
||||
Dependencies: &[]cyclonedx.Dependency{
|
||||
{
|
||||
Ref: c1.BOMRef,
|
||||
Dependencies: &[]string{
|
||||
c2.BOMRef,
|
||||
c3.BOMRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s, err := ToSyftModel(&test.sbom)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s)
|
||||
|
||||
var deps []string
|
||||
if s != nil {
|
||||
for _, r := range s.Relationships {
|
||||
if r.Type != artifact.DependencyOfRelationship {
|
||||
continue
|
||||
}
|
||||
if p, ok := r.To.(pkg.Package); !ok || p.Name != c1.Name {
|
||||
continue
|
||||
}
|
||||
if p, ok := r.From.(pkg.Package); ok {
|
||||
deps = append(deps, p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
require.Equal(t, test.expected, deps)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,27 +143,29 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
|
||||
for _, r := range relationships {
|
||||
exists := isExpressiblePackageRelationship(r.Type)
|
||||
if !exists {
|
||||
log.Debugf("unable to convert relationship from CycloneDX 1.4 JSON, dropping: %+v", r)
|
||||
log.Debugf("unable to convert relationship type to CycloneDX JSON, dropping: %#v", r)
|
||||
continue
|
||||
}
|
||||
|
||||
// we only capture package-to-package relationships for now
|
||||
fromPkg, ok := r.From.(*pkg.Package)
|
||||
fromPkg, ok := r.From.(pkg.Package)
|
||||
if !ok {
|
||||
log.Tracef("unable to convert relationship fromPkg to CycloneDX JSON, dropping: %#v", r)
|
||||
continue
|
||||
}
|
||||
|
||||
toPkg, ok := r.To.(*pkg.Package)
|
||||
toPkg, ok := r.To.(pkg.Package)
|
||||
if !ok {
|
||||
log.Tracef("unable to convert relationship toPkg to CycloneDX JSON, dropping: %#v", r)
|
||||
continue
|
||||
}
|
||||
|
||||
// ind dep
|
||||
|
||||
innerDeps := []string{}
|
||||
innerDeps = append(innerDeps, deriveBomRef(*fromPkg))
|
||||
innerDeps = append(innerDeps, deriveBomRef(fromPkg))
|
||||
result = append(result, cyclonedx.Dependency{
|
||||
Ref: deriveBomRef(*toPkg),
|
||||
Ref: deriveBomRef(toPkg),
|
||||
Dependencies: &innerDeps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,13 @@ package cyclonedxhelpers
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func Test_formatCPE(t *testing.T) {
|
||||
@@ -32,3 +38,98 @@ func Test_formatCPE(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_relationships(t *testing.T) {
|
||||
p1 := pkg.Package{
|
||||
Name: "p1",
|
||||
}
|
||||
p1.SetID()
|
||||
|
||||
p2 := pkg.Package{
|
||||
Name: "p2",
|
||||
}
|
||||
p2.SetID()
|
||||
|
||||
p3 := pkg.Package{
|
||||
Name: "p3",
|
||||
}
|
||||
p3.SetID()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sbom sbom.SBOM
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "package dependencyOf relationships output as dependencies",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(p1, p2, p3),
|
||||
},
|
||||
Relationships: []artifact.Relationship{
|
||||
{
|
||||
From: p2,
|
||||
To: p1,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: p3,
|
||||
To: p1,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{p2.Name, p3.Name},
|
||||
},
|
||||
{
|
||||
name: "package contains relationships not output",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(p1, p2, p3),
|
||||
},
|
||||
Relationships: []artifact.Relationship{
|
||||
{
|
||||
From: p2,
|
||||
To: p1,
|
||||
Type: artifact.ContainsRelationship,
|
||||
},
|
||||
{
|
||||
From: p3,
|
||||
To: p1,
|
||||
Type: artifact.ContainsRelationship,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cdx := ToFormatModel(test.sbom)
|
||||
got := cdx.Dependencies
|
||||
|
||||
var deps []string
|
||||
if got != nil {
|
||||
for _, r := range *got {
|
||||
for _, d := range *r.Dependencies {
|
||||
c := findComponent(cdx, d)
|
||||
require.NotNil(t, c)
|
||||
deps = append(deps, c.Name)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
require.Equal(t, test.expected, deps)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func findComponent(cdx *cyclonedx.BOM, bomRef string) *cyclonedx.Component {
|
||||
for _, c := range *cdx.Components {
|
||||
if c.BOMRef == bomRef {
|
||||
return &c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-2
@@ -92,8 +92,7 @@ func CatalogPackages(src source.Source, cfg cataloger.Config) (*pkg.Collection,
|
||||
}
|
||||
|
||||
func removeRelationshipsByID(relationships []artifact.Relationship, id artifact.ID) []artifact.Relationship {
|
||||
// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
|
||||
filtered := relationships[:0]
|
||||
var filtered []artifact.Relationship
|
||||
for _, r := range relationships {
|
||||
if r.To.ID() != id && r.From.ID() != id {
|
||||
filtered = append(filtered, r)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package syft
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_removeRelationshipsByID(t *testing.T) {
|
||||
p1 := pkg.Package{}
|
||||
p1.OverrideID("1")
|
||||
|
||||
p2 := pkg.Package{}
|
||||
p2.OverrideID("2")
|
||||
|
||||
p3 := pkg.Package{}
|
||||
p3.OverrideID("3")
|
||||
|
||||
rel := func(pkgs ...pkg.Package) (out []artifact.Relationship) {
|
||||
for _, p := range pkgs {
|
||||
out = append(out, artifact.Relationship{
|
||||
From: p,
|
||||
To: p,
|
||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
relationships := rel(p1, p2, p3)
|
||||
|
||||
for _, r := range relationships {
|
||||
if r.From.ID() == "1" || r.From.ID() == "2" {
|
||||
relationships = removeRelationshipsByID(relationships, r.From.ID())
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t, rel(p3), relationships)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
@@ -13,14 +12,6 @@ var (
|
||||
forbiddenProductGroupIDFields = strset.New("plugin", "plugins", "client")
|
||||
forbiddenVendorGroupIDFields = strset.New("plugin", "plugins")
|
||||
|
||||
domains = []string{
|
||||
"com",
|
||||
"org",
|
||||
"net",
|
||||
"io",
|
||||
"be",
|
||||
}
|
||||
|
||||
primaryJavaManifestGroupIDFields = []string{
|
||||
"Extension-Name",
|
||||
"Specification-Vendor",
|
||||
@@ -66,7 +57,7 @@ func vendorsFromJavaManifestNames(p pkg.Package) fieldCandidateSet {
|
||||
for _, name := range javaManifestNameFields {
|
||||
if metadata.Manifest.Main != nil {
|
||||
if value, exists := metadata.Manifest.Main[name]; exists {
|
||||
if !startsWithTopLevelDomain(value) {
|
||||
if !looksLikeGroupID(value) {
|
||||
vendors.add(fieldCandidate{
|
||||
value: normalizePersonName(value),
|
||||
disallowSubSelections: true,
|
||||
@@ -80,7 +71,7 @@ func vendorsFromJavaManifestNames(p pkg.Package) fieldCandidateSet {
|
||||
continue
|
||||
}
|
||||
if value, exists := section[name]; exists {
|
||||
if !startsWithTopLevelDomain(value) {
|
||||
if !looksLikeGroupID(value) {
|
||||
vendors.add(fieldCandidate{
|
||||
value: normalizePersonName(value),
|
||||
disallowSubSelections: true,
|
||||
@@ -144,7 +135,7 @@ func productsFromArtifactAndGroupIDs(artifactID string, groupIDs []string) []str
|
||||
continue
|
||||
}
|
||||
|
||||
// umbrella projects tend to have sub components that either start or end with the project name. We expect
|
||||
// umbrella projects tend to have subcomponents that either start or end with the project name. We expect
|
||||
// to identify fields that may represent the umbrella project, and not fields that indicate auxiliary
|
||||
// information about the package.
|
||||
couldBeProjectName := strings.HasPrefix(artifactID, field) || strings.HasSuffix(artifactID, field)
|
||||
@@ -168,7 +159,7 @@ func artifactIDFromJavaPackage(p pkg.Package) string {
|
||||
}
|
||||
|
||||
artifactID := strings.TrimSpace(metadata.PomProperties.ArtifactID)
|
||||
if startsWithTopLevelDomain(artifactID) && len(strings.Split(artifactID, ".")) > 1 {
|
||||
if looksLikeGroupID(artifactID) {
|
||||
// there is a strong indication that the artifact ID is really a group ID, don't use it
|
||||
return ""
|
||||
}
|
||||
@@ -181,13 +172,13 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return GroupIDsFromJavaMetadata(metadata)
|
||||
return GroupIDsFromJavaMetadata(p.Name, metadata)
|
||||
}
|
||||
|
||||
func GroupIDsFromJavaMetadata(metadata pkg.JavaMetadata) (groupIDs []string) {
|
||||
func GroupIDsFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupIDs []string) {
|
||||
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
|
||||
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
|
||||
groupIDs = append(groupIDs, groupIDsFromJavaManifest(metadata.Manifest)...)
|
||||
groupIDs = append(groupIDs, groupIDsFromJavaManifest(pkgName, metadata.Manifest)...)
|
||||
|
||||
return groupIDs
|
||||
}
|
||||
@@ -197,16 +188,10 @@ func groupIDsFromPomProperties(properties *pkg.PomProperties) (groupIDs []string
|
||||
return nil
|
||||
}
|
||||
|
||||
if startsWithTopLevelDomain(properties.GroupID) {
|
||||
if properties.GroupID != "" {
|
||||
groupIDs = append(groupIDs, cleanGroupID(properties.GroupID))
|
||||
}
|
||||
|
||||
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
|
||||
if startsWithTopLevelDomain(properties.ArtifactID) && len(strings.Split(properties.ArtifactID, ".")) > 1 {
|
||||
// there is a strong indication that the artifact ID is really a group ID
|
||||
groupIDs = append(groupIDs, cleanGroupID(properties.ArtifactID))
|
||||
}
|
||||
|
||||
return groupIDs
|
||||
}
|
||||
|
||||
@@ -215,33 +200,22 @@ func groupIDsFromPomProject(project *pkg.PomProject) (groupIDs []string) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// extract the project info...
|
||||
groupIDs = addGroupIDsFromGroupIDsAndArtifactID(project.GroupID, project.ArtifactID)
|
||||
|
||||
if project.Parent == nil {
|
||||
return groupIDs
|
||||
if project.GroupID != "" {
|
||||
groupIDs = append(groupIDs, project.GroupID)
|
||||
}
|
||||
|
||||
// extract the parent project info...
|
||||
groupIDs = append(groupIDs, addGroupIDsFromGroupIDsAndArtifactID(project.Parent.GroupID, project.Parent.ArtifactID)...)
|
||||
if project.Parent != nil && project.Parent.GroupID != "" {
|
||||
groupIDs = append(groupIDs, project.Parent.GroupID)
|
||||
}
|
||||
|
||||
return groupIDs
|
||||
}
|
||||
|
||||
func addGroupIDsFromGroupIDsAndArtifactID(groupID, artifactID string) (groupIDs []string) {
|
||||
if startsWithTopLevelDomain(groupID) {
|
||||
groupIDs = append(groupIDs, cleanGroupID(groupID))
|
||||
func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []string {
|
||||
if groupID, ok := defaultArtifactIDToGroupID[pkgName]; ok {
|
||||
return []string{groupID}
|
||||
}
|
||||
|
||||
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
|
||||
if startsWithTopLevelDomain(artifactID) && len(strings.Split(artifactID, ".")) > 1 {
|
||||
// there is a strong indication that the artifact ID is really a group ID
|
||||
groupIDs = append(groupIDs, cleanGroupID(artifactID))
|
||||
}
|
||||
return groupIDs
|
||||
}
|
||||
|
||||
func groupIDsFromJavaManifest(manifest *pkg.JavaManifest) []string {
|
||||
if manifest == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -268,13 +242,13 @@ func getManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (grou
|
||||
|
||||
for _, name := range fields {
|
||||
if value, exists := manifest.Main[name]; exists {
|
||||
if startsWithTopLevelDomain(value) {
|
||||
if looksLikeGroupID(value) {
|
||||
groupIDs = append(groupIDs, cleanGroupID(value))
|
||||
}
|
||||
}
|
||||
for _, section := range manifest.NamedSections {
|
||||
if value, exists := section[name]; exists {
|
||||
if startsWithTopLevelDomain(value) {
|
||||
if looksLikeGroupID(value) {
|
||||
groupIDs = append(groupIDs, cleanGroupID(value))
|
||||
}
|
||||
}
|
||||
@@ -295,6 +269,6 @@ func removeOSCIDirectives(groupID string) string {
|
||||
return strings.Split(groupID, ";")[0]
|
||||
}
|
||||
|
||||
func startsWithTopLevelDomain(value string) bool {
|
||||
return internal.HasAnyOfPrefixes(value, domains...)
|
||||
func looksLikeGroupID(value string) bool {
|
||||
return strings.Contains(value, ".")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package cpe
|
||||
|
||||
var defaultArtifactIDToGroupID = map[string]string{
|
||||
"ant": "org.apache.ant",
|
||||
"ant-antlr": "org.apache.ant",
|
||||
"ant-antunit": "org.apache.ant",
|
||||
"ant-apache-bcel": "org.apache.ant",
|
||||
"ant-apache-bsf": "org.apache.ant",
|
||||
"ant-apache-log4j": "org.apache.ant",
|
||||
"ant-apache-oro": "org.apache.ant",
|
||||
"ant-apache-regexp": "org.apache.ant",
|
||||
"ant-apache-resolver": "org.apache.ant",
|
||||
"ant-apache-xalan2": "org.apache.ant",
|
||||
"ant-commons-logging": "org.apache.ant",
|
||||
"ant-commons-net": "org.apache.ant",
|
||||
"ant-compress": "org.apache.ant",
|
||||
"ant-dotnet": "org.apache.ant",
|
||||
"ant-imageio": "org.apache.ant",
|
||||
"ant-jai": "org.apache.ant",
|
||||
"ant-jakartamail": "org.apache.ant",
|
||||
"ant-javamail": "org.apache.ant",
|
||||
"ant-jdepend": "org.apache.ant",
|
||||
"ant-jmf": "org.apache.ant",
|
||||
"ant-jsch": "org.apache.ant",
|
||||
"ant-junit": "org.apache.ant",
|
||||
"ant-junit4": "org.apache.ant",
|
||||
"ant-junitlauncher": "org.apache.ant",
|
||||
"ant-launcher": "org.apache.ant",
|
||||
"ant-netrexx": "org.apache.ant",
|
||||
"ant-nodeps": "org.apache.ant",
|
||||
"ant-parent": "org.apache.ant",
|
||||
"ant-starteam": "org.apache.ant",
|
||||
"ant-stylebook": "org.apache.ant",
|
||||
"ant-swing": "org.apache.ant",
|
||||
"ant-testutil": "org.apache.ant",
|
||||
"ant-trax": "org.apache.ant",
|
||||
"ant-weblogic": "org.apache.ant",
|
||||
"ant-xz": "org.apache.ant",
|
||||
"spring": "org.springframework",
|
||||
"spring-amqp": "org.springframework.amqp",
|
||||
"spring-batch-core": "org.springframework.batch",
|
||||
"spring-beans": "org.springframework",
|
||||
"spring-boot": "org.springframework.boot",
|
||||
"spring-boot-starter-web": "org.springframework.boot",
|
||||
"spring-boot-starter-webflux": "org.springframework.boot",
|
||||
"spring-cloud-function-context": "org.springframework.cloud",
|
||||
"spring-cloud-function-parent": "org.springframework.cloud",
|
||||
"spring-cloud-gateway": "org.springframework.cloud",
|
||||
"spring-cloud-openfeign-core": "org.springframework.cloud",
|
||||
"spring-cloud-task-dependencies": "org.springframework.cloud",
|
||||
"spring-core": "org.springframework",
|
||||
"spring-data-jpa": "org.springframework.data",
|
||||
"spring-data-mongodb": "org.springframework.data",
|
||||
"spring-data-rest-core": "org.springframework.data",
|
||||
"spring-expression": "org.springframework",
|
||||
"spring-integration-zip": "org.springframework.integration",
|
||||
"spring-oxm": "org.springframework",
|
||||
"spring-security-core": "org.springframework.security",
|
||||
"spring-security-config": "org.springframework.security",
|
||||
"spring-security-oauth": "org.springframework.security.oauth",
|
||||
"spring-security-oauth-parent": "org.springframework.security.oauth",
|
||||
"spring-security-oauth2-client": "org.springframework.security",
|
||||
"spring-session-core": "org.springframework.session",
|
||||
"spring-vault-core": "org.springframework.vault",
|
||||
"spring-web": "org.springframework",
|
||||
"spring-webflow": "org.springframework.webflow",
|
||||
"spring-webflux": "org.springframework",
|
||||
"spring-webmvc": "org.springframework",
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
@@ -427,3 +428,38 @@ func Test_vendorsFromJavaManifestNames(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_groupIDsFromJavaManifest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
manifest pkg.JavaManifest
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "spring-security-core",
|
||||
manifest: pkg.JavaManifest{},
|
||||
expected: []string{"org.springframework.security"},
|
||||
},
|
||||
{
|
||||
name: "spring-web",
|
||||
manifest: pkg.JavaManifest{},
|
||||
expected: []string{"org.springframework"},
|
||||
},
|
||||
{
|
||||
name: "spring-foo",
|
||||
manifest: pkg.JavaManifest{
|
||||
Main: map[string]string{
|
||||
"Implementation-Vendor": "org.foo",
|
||||
},
|
||||
},
|
||||
expected: []string{"org.foo"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := groupIDsFromJavaManifest(test.name, &test.manifest)
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,16 @@ type Config struct {
|
||||
ExcludeBinaryOverlapByOwnership bool
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Search: DefaultSearchConfig(),
|
||||
Parallelism: 1,
|
||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
ExcludeBinaryOverlapByOwnership: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (c Config) Java() java.Config {
|
||||
return java.Config{
|
||||
SearchUnindexedArchives: c.Search.IncludeUnindexedArchives,
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
// PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec)
|
||||
func packageURL(name, version string, metadata pkg.JavaMetadata) string {
|
||||
var groupID = name
|
||||
groupIDs := cpe.GroupIDsFromJavaMetadata(metadata)
|
||||
groupIDs := cpe.GroupIDsFromJavaMetadata(name, metadata)
|
||||
if len(groupIDs) > 0 {
|
||||
groupID = groupIDs[0]
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func BenchmarkImagePackageCatalogers(b *testing.B) {
|
||||
tarPath := imagetest.GetFixtureImageTarPath(b, fixtureImageName)
|
||||
|
||||
var pc *pkg.Collection
|
||||
for _, c := range cataloger.ImageCatalogers(defaultConfig()) {
|
||||
for _, c := range cataloger.ImageCatalogers(cataloger.DefaultConfig()) {
|
||||
// in case of future alteration where state is persisted, assume no dependency is safe to reuse
|
||||
userInput := "docker-archive:" + tarPath
|
||||
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
|
||||
@@ -260,7 +260,7 @@ func TestPkgCoverageCatalogerConfiguration(t *testing.T) {
|
||||
assert.Equal(t, definedLanguages, observedLanguages)
|
||||
|
||||
// Verify that rust isn't actually an image cataloger
|
||||
c := defaultConfig()
|
||||
c := cataloger.DefaultConfig()
|
||||
c.Catalogers = []string{"rust"}
|
||||
assert.Len(t, cataloger.ImageCatalogers(c), 0)
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
||||
formatOption: cyclonedxjson.ID,
|
||||
redactor: func(in []byte) []byte {
|
||||
// unstable values
|
||||
in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref)": "[^"]+",`).ReplaceAll(in, []byte{})
|
||||
|
||||
in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref|ref)":\s*"(\n|[^"])+"`).ReplaceAll(in, []byte(`"$1": "redacted"`))
|
||||
in = regexp.MustCompile(`"(dependsOn)":\s*\[(?:\s|[^]])+]`).ReplaceAll(in, []byte(`"$1": []`))
|
||||
return in
|
||||
},
|
||||
json: true,
|
||||
@@ -57,7 +57,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
||||
formatOption: cyclonedxxml.ID,
|
||||
redactor: func(in []byte) []byte {
|
||||
// unstable values
|
||||
in = regexp.MustCompile(`(serialNumber|bom-ref)="[^"]+"`).ReplaceAll(in, []byte{})
|
||||
in = regexp.MustCompile(`(serialNumber|bom-ref|ref)="[^"]+"`).ReplaceAll(in, []byte{})
|
||||
in = regexp.MustCompile(`<timestamp>[^<]+</timestamp>`).ReplaceAll(in, []byte{})
|
||||
|
||||
return in
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/python"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
@@ -26,7 +24,7 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco
|
||||
theSource.Close()
|
||||
})
|
||||
|
||||
c := defaultConfig()
|
||||
c := cataloger.DefaultConfig()
|
||||
c.Catalogers = catalogerCfg
|
||||
|
||||
c.Search.Scope = scope
|
||||
@@ -54,16 +52,6 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco
|
||||
}, theSource
|
||||
}
|
||||
|
||||
func defaultConfig() cataloger.Config {
|
||||
return cataloger.Config{
|
||||
Search: cataloger.DefaultSearchConfig(),
|
||||
Parallelism: 1,
|
||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
ExcludeBinaryOverlapByOwnership: true,
|
||||
}
|
||||
}
|
||||
|
||||
func catalogDirectory(t *testing.T, dir string) (sbom.SBOM, source.Source) {
|
||||
userInput := "dir:" + dir
|
||||
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
|
||||
@@ -75,7 +63,7 @@ func catalogDirectory(t *testing.T, dir string) (sbom.SBOM, source.Source) {
|
||||
})
|
||||
|
||||
// TODO: this would be better with functional options (after/during API refactor)
|
||||
c := defaultConfig()
|
||||
c := cataloger.DefaultConfig()
|
||||
c.Search.Scope = source.AllLayersScope
|
||||
pkgCatalog, relationships, actualDistro, err := syft.CatalogPackages(theSource, c)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user