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:
Keith Zantow
2024-10-07 16:11:37 -04:00
committed by GitHub
co-authored by Alex Goodman
parent 4d7ed9f749
commit ccbee94b87
145 changed files with 4420 additions and 233 deletions
+19 -6
View File
@@ -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)
+16 -2
View File
@@ -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)
}
+1 -1
View File
@@ -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
+4 -1
View File
@@ -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>