mirror of
https://github.com/anchore/syft.git
synced 2026-11-05 08:56:41 +01:00
feat: report unknowns in sbom (#2998)
Signed-off-by: Keith Zantow <kzantow@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
co-authored by
Alex Goodman
parent
4d7ed9f749
commit
ccbee94b87
@@ -16,6 +16,7 @@ import (
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/licenses"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -133,14 +134,22 @@ func (j *archiveParser) parse(ctx context.Context) ([]pkg.Package, []artifact.Re
|
||||
}
|
||||
pkgs = append(pkgs, auxPkgs...)
|
||||
|
||||
var errs error
|
||||
if j.detectNested {
|
||||
// find nested java archive packages
|
||||
nestedPkgs, nestedRelationships, err := j.discoverPkgsFromNestedArchives(ctx, parentPkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
errs = unknown.Append(errs, j.location, err)
|
||||
}
|
||||
pkgs = append(pkgs, nestedPkgs...)
|
||||
relationships = append(relationships, nestedRelationships...)
|
||||
} else {
|
||||
// .jar and .war files are present in archives, are others? or generally just consider them top-level?
|
||||
nestedArchives := j.fileManifest.GlobMatch(true, "*.jar", "*.war")
|
||||
if len(nestedArchives) > 0 {
|
||||
slices.Sort(nestedArchives)
|
||||
errs = unknown.Appendf(errs, j.location, "nested archives not cataloged: %v", strings.Join(nestedArchives, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
// lastly, add the parent package to the list (assuming the parent exists)
|
||||
@@ -166,7 +175,11 @@ func (j *archiveParser) parse(ctx context.Context) ([]pkg.Package, []artifact.Re
|
||||
p.SetID()
|
||||
}
|
||||
|
||||
return pkgs, relationships, nil
|
||||
if len(pkgs) == 0 {
|
||||
errs = unknown.Appendf(errs, j.location, "no package identified in archive")
|
||||
}
|
||||
|
||||
return pkgs, relationships, errs
|
||||
}
|
||||
|
||||
// discoverMainPackage parses the root Java manifest used as the parent package to all discovered nested packages.
|
||||
@@ -283,14 +296,14 @@ func (j *archiveParser) findLicenseFromJavaMetadata(ctx context.Context, groupID
|
||||
if parsedPom != nil {
|
||||
pomLicenses, err = j.maven.resolveLicenses(ctx, parsedPom.project)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "mavenID", j.maven.getMavenID(ctx, parsedPom.project)).Debug("error attempting to resolve pom licenses")
|
||||
log.WithFields("error", err, "mavenID", j.maven.getMavenID(ctx, parsedPom.project)).Trace("error attempting to resolve pom licenses")
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && len(pomLicenses) == 0 {
|
||||
pomLicenses, err = j.maven.findLicenses(ctx, groupID, artifactID, version)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "mavenID", mavenID{groupID, artifactID, version}).Debug("error attempting to find licenses")
|
||||
log.WithFields("error", err, "mavenID", mavenID{groupID, artifactID, version}).Trace("error attempting to find licenses")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +313,7 @@ func (j *archiveParser) findLicenseFromJavaMetadata(ctx context.Context, groupID
|
||||
groupID = strings.Join(packages[:len(packages)-1], ".")
|
||||
pomLicenses, err = j.maven.findLicenses(ctx, groupID, artifactID, version)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "mavenID", mavenID{groupID, artifactID, version}).Debug("error attempting to find sub-group licenses")
|
||||
log.WithFields("error", err, "mavenID", mavenID{groupID, artifactID, version}).Trace("error attempting to find sub-group licenses")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +643,7 @@ func newPackageFromMavenData(ctx context.Context, r *mavenResolver, pomPropertie
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "mavenID", mavenID{pomProperties.GroupID, pomProperties.ArtifactID, pomProperties.Version}).Debug("error attempting to resolve licenses")
|
||||
log.WithFields("error", err, "mavenID", mavenID{pomProperties.GroupID, pomProperties.ArtifactID, pomProperties.Version}).Trace("error attempting to resolve licenses")
|
||||
}
|
||||
|
||||
licenses := make([]pkg.License, 0)
|
||||
|
||||
@@ -91,10 +91,12 @@ func TestParseJar(t *testing.T) {
|
||||
fixture string
|
||||
expected map[string]pkg.Package
|
||||
ignoreExtras []string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "example-jenkins-plugin",
|
||||
fixture: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi",
|
||||
wantErr: require.Error, // there are nested jars, which are not scanned and result in unknown errors
|
||||
ignoreExtras: []string{
|
||||
"Plugin-Version", // has dynamic date
|
||||
"Built-By", // podman returns the real UID
|
||||
@@ -153,6 +155,7 @@ func TestParseJar(t *testing.T) {
|
||||
{
|
||||
name: "example-java-app-gradle",
|
||||
fixture: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar",
|
||||
wantErr: require.NoError, // no nested jars
|
||||
expected: map[string]pkg.Package{
|
||||
"example-java-app-gradle": {
|
||||
Name: "example-java-app-gradle",
|
||||
@@ -226,6 +229,7 @@ func TestParseJar(t *testing.T) {
|
||||
{
|
||||
name: "example-java-app-maven",
|
||||
fixture: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
|
||||
wantErr: require.NoError, // no nested jars
|
||||
ignoreExtras: []string{
|
||||
"Build-Jdk", // can't guarantee the JDK used at build time
|
||||
"Built-By", // podman returns the real UID
|
||||
@@ -351,13 +355,15 @@ func TestParseJar(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, _, err := parser.parse(context.Background())
|
||||
require.NoError(t, err)
|
||||
if test.wantErr != nil {
|
||||
test.wantErr(t, err)
|
||||
}
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
for _, a := range actual {
|
||||
t.Log(" ", a)
|
||||
}
|
||||
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(test.expected))
|
||||
t.Fatalf("unexpected package count; expected: %d got: %d", len(test.expected), len(actual))
|
||||
}
|
||||
|
||||
var parent *pkg.Package
|
||||
@@ -1488,3 +1494,11 @@ func run(t testing.TB, cmd *exec.Cmd) {
|
||||
func ptr[T any](value T) *T {
|
||||
return &value
|
||||
}
|
||||
|
||||
func Test_corruptJarArchive(t *testing.T) {
|
||||
ap := newGenericArchiveParserAdapter(DefaultArchiveCatalogerConfig())
|
||||
pkgtest.NewCatalogTester().
|
||||
FromFile(t, "test-fixtures/corrupt/example.jar").
|
||||
WithError().
|
||||
TestParser(t, ap.parseJavaArchive)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func (r *mavenResolver) resolvePropertyValue(ctx context.Context, propertyValue
|
||||
}
|
||||
resolved, err := r.resolveExpression(ctx, resolutionContext, *propertyValue, resolvingProperties)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "propertyValue", *propertyValue).Debug("error resolving maven property")
|
||||
log.WithFields("error", err, "propertyValue", *propertyValue).Trace("error resolving maven property")
|
||||
return ""
|
||||
}
|
||||
return resolved
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -41,11 +42,13 @@ func (p pomXMLCataloger) Catalog(ctx context.Context, fileResolver file.Resolver
|
||||
|
||||
r := newMavenResolver(fileResolver, p.cfg)
|
||||
|
||||
var errs error
|
||||
var poms []*gopom.Project
|
||||
for _, pomLocation := range locations {
|
||||
pom, err := readPomFromLocation(fileResolver, pomLocation)
|
||||
if err != nil || pom == nil {
|
||||
log.WithFields("error", err, "pomLocation", pomLocation).Debug("error while reading pom")
|
||||
errs = unknown.Appendf(errs, pomLocation, "error reading pom.xml: %w", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -60,7 +63,7 @@ func (p pomXMLCataloger) Catalog(ctx context.Context, fileResolver file.Resolver
|
||||
for _, pom := range poms {
|
||||
pkgs = append(pkgs, processPomXML(ctx, r, pom, r.pomLocations[pom])...)
|
||||
}
|
||||
return pkgs, nil, nil
|
||||
return pkgs, nil, errs
|
||||
}
|
||||
|
||||
func readPomFromLocation(fileResolver file.Resolver, pomLocation file.Location) (*gopom.Project, error) {
|
||||
|
||||
@@ -705,3 +705,11 @@ func getCommonsTextExpectedPackages() []pkg.Package {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Test_corruptPomXml(t *testing.T) {
|
||||
c := NewPomCataloger(DefaultArchiveCatalogerConfig())
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/corrupt").
|
||||
WithError().
|
||||
TestCataloger(t, c)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func Test_parseTarWrappedJavaArchive(t *testing.T) {
|
||||
@@ -57,3 +58,11 @@ func Test_parseTarWrappedJavaArchive(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_corruptTarArchive(t *testing.T) {
|
||||
ap := newGenericTarWrappedJavaArchiveParser(DefaultArchiveCatalogerConfig())
|
||||
pkgtest.NewCatalogTester().
|
||||
FromFile(t, "test-fixtures/corrupt/example.tar").
|
||||
WithError().
|
||||
TestParser(t, ap.parseTarWrappedJavaArchive)
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
example archive
|
||||
@@ -0,0 +1 @@
|
||||
example archive
|
||||
@@ -0,0 +1 @@
|
||||
<this is not a valid pom xml>
|
||||
Reference in New Issue
Block a user