mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
fix: merging of binary packages (#1583)
This commit is contained in:
parent
8f6a317fef
commit
f5e20521e0
@ -6,5 +6,5 @@ const (
|
|||||||
|
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||||
JSONSchemaVersion = "6.2.0"
|
JSONSchemaVersion = "7.0.0"
|
||||||
)
|
)
|
||||||
|
|||||||
1642
schema/json/schema-7.0.0.json
Normal file
1642
schema/json/schema-7.0.0.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -213,5 +213,6 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
|||||||
s,
|
s,
|
||||||
*updateJson,
|
*updateJson,
|
||||||
true,
|
true,
|
||||||
|
schemaVersionRedactor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package syftjson
|
package syftjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8,7 +9,7 @@ const ID sbom.FormatID = "syft-json"
|
|||||||
|
|
||||||
func Format() sbom.Format {
|
func Format() sbom.Format {
|
||||||
return sbom.NewFormat(
|
return sbom.NewFormat(
|
||||||
"6",
|
internal.JSONSchemaVersion,
|
||||||
encoder,
|
encoder,
|
||||||
decoder,
|
decoder,
|
||||||
validator,
|
validator,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package syftjson
|
package syftjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
@ -9,16 +8,10 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/syft/formats/syftjson/model"
|
"github.com/anchore/syft/syft/formats/syftjson/model"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_SyftJsonID_Compatibility(t *testing.T) {
|
|
||||||
jsonMajorVersion := strings.Split(internal.JSONSchemaVersion, ".")[0]
|
|
||||||
assert.Equal(t, jsonMajorVersion, string(Format().Version()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_toSourceModel(t *testing.T) {
|
func Test_toSourceModel(t *testing.T) {
|
||||||
allSchemes := strset.New()
|
allSchemes := strset.New()
|
||||||
for _, s := range source.AllSchemes {
|
for _, s := range source.AllSchemes {
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/syft/source"
|
||||||
|
|
||||||
type BinaryMetadata struct {
|
type BinaryMetadata struct {
|
||||||
Classifier string `mapstructure:"Classifier" json:"classifier"`
|
Matches []ClassifierMatch `mapstructure:"Matches" json:"matches"`
|
||||||
RealPath string `mapstructure:"RealPath" json:"realPath"`
|
}
|
||||||
VirtualPath string `mapstructure:"VirtualPath" json:"virtualPath"`
|
|
||||||
|
type ClassifierMatch struct {
|
||||||
|
Classifier string `mapstructure:"Classifier" json:"classifier"`
|
||||||
|
Location source.Location `mapstructure:"Location" json:"location"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,19 +33,42 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif
|
|||||||
|
|
||||||
for _, cls := range defaultClassifiers {
|
for _, cls := range defaultClassifiers {
|
||||||
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
|
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
|
||||||
pkgs, err := catalog(resolver, cls)
|
newPkgs, err := catalog(resolver, cls)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err)
|
log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
packages = append(packages, pkgs...)
|
newPackages:
|
||||||
|
for i := range newPkgs {
|
||||||
|
newPkg := &newPkgs[i]
|
||||||
|
for j := range packages {
|
||||||
|
p := &packages[j]
|
||||||
|
// consolidate identical packages found in different locations or by different classifiers
|
||||||
|
if packagesMatch(p, newPkg) {
|
||||||
|
mergePackages(p, newPkg)
|
||||||
|
continue newPackages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packages = append(packages, *newPkg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return packages, relationships, nil
|
return packages, relationships, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error) {
|
// mergePackages merges information from the extra package into the target package
|
||||||
var pkgs []pkg.Package
|
func mergePackages(target *pkg.Package, extra *pkg.Package) {
|
||||||
|
// add the locations
|
||||||
|
target.Locations.Add(extra.Locations.ToSlice()...)
|
||||||
|
// update the metadata to indicate which classifiers were used
|
||||||
|
meta, _ := target.Metadata.(pkg.BinaryMetadata)
|
||||||
|
if m, ok := extra.Metadata.(pkg.BinaryMetadata); ok {
|
||||||
|
meta.Matches = append(meta.Matches, m.Matches...)
|
||||||
|
}
|
||||||
|
target.Metadata = meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func catalog(resolver source.FileResolver, cls classifier) (packages []pkg.Package, err error) {
|
||||||
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -56,26 +79,13 @@ func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
locationReader := source.NewLocationReadCloser(location, reader)
|
locationReader := source.NewLocationReadCloser(location, reader)
|
||||||
newPkgs, err := cls.EvidenceMatcher(cls, locationReader)
|
pkgs, err := cls.EvidenceMatcher(cls, locationReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newPackages:
|
packages = append(packages, pkgs...)
|
||||||
for i := range newPkgs {
|
|
||||||
newPkg := &newPkgs[i]
|
|
||||||
for j := range pkgs {
|
|
||||||
p := &pkgs[j]
|
|
||||||
// consolidate identical packages found in different locations,
|
|
||||||
// but continue to track each location
|
|
||||||
if packagesMatch(p, newPkg) {
|
|
||||||
p.Locations.Add(newPkg.Locations.ToSlice()...)
|
|
||||||
continue newPackages
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pkgs = append(pkgs, *newPkg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return pkgs, nil
|
return packages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// packagesMatch returns true if the binary packages "match" based on basic criteria
|
// packagesMatch returns true if the binary packages "match" based on basic criteria
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package binary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -27,10 +28,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "15beta4",
|
Version: "15beta4",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/postgresql@15beta4",
|
PURL: "pkg:generic/postgresql@15beta4",
|
||||||
Locations: singleLocation("postgres"),
|
Locations: locations("postgres"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("postgresql-binary"),
|
||||||
Classifier: "postgresql-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -41,10 +40,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "15.1",
|
Version: "15.1",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/postgresql@15.1",
|
PURL: "pkg:generic/postgresql@15.1",
|
||||||
Locations: singleLocation("postgres"),
|
Locations: locations("postgres"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("postgresql-binary"),
|
||||||
Classifier: "postgresql-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -55,10 +52,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "9.6.24",
|
Version: "9.6.24",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/postgresql@9.6.24",
|
PURL: "pkg:generic/postgresql@9.6.24",
|
||||||
Locations: singleLocation("postgres"),
|
Locations: locations("postgres"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("postgresql-binary"),
|
||||||
Classifier: "postgresql-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,9 +64,26 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "9.5alpha1",
|
Version: "9.5alpha1",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/postgresql@9.5alpha1",
|
PURL: "pkg:generic/postgresql@9.5alpha1",
|
||||||
Locations: singleLocation("postgres"),
|
Locations: locations("postgres"),
|
||||||
|
Metadata: metadata("postgresql-binary"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "positive-python-duplicates",
|
||||||
|
fixtureDir: "test-fixtures/classifiers/positive/python-duplicates",
|
||||||
|
expected: pkg.Package{
|
||||||
|
Name: "python",
|
||||||
|
Version: "3.8.16",
|
||||||
|
Type: "binary",
|
||||||
|
PURL: "pkg:generic/python@3.8.16",
|
||||||
|
Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so", "patchlevel.h"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: pkg.BinaryMetadata{
|
||||||
Classifier: "postgresql-binary",
|
Matches: []pkg.ClassifierMatch{
|
||||||
|
match("python-binary", "dir/python3.8"),
|
||||||
|
match("python-binary", "python3.8"),
|
||||||
|
match("python-binary-lib", "libpython3.8.so"),
|
||||||
|
match("cpython-source", "patchlevel.h"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -83,10 +95,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "2.9.6",
|
Version: "2.9.6",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/traefik@2.9.6",
|
PURL: "pkg:generic/traefik@2.9.6",
|
||||||
Locations: singleLocation("traefik"),
|
Locations: locations("traefik"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("traefik-binary"),
|
||||||
Classifier: "traefik-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -97,10 +107,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "1.7.34",
|
Version: "1.7.34",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/traefik@1.7.34",
|
PURL: "pkg:generic/traefik@1.7.34",
|
||||||
Locations: singleLocation("traefik"),
|
Locations: locations("traefik"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("traefik-binary"),
|
||||||
Classifier: "traefik-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -111,10 +119,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "1.6.18",
|
Version: "1.6.18",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/memcached@1.6.18",
|
PURL: "pkg:generic/memcached@1.6.18",
|
||||||
Locations: singleLocation("memcached"),
|
Locations: locations("memcached"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("memcached-binary"),
|
||||||
Classifier: "memcached-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -125,10 +131,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "2.4.54",
|
Version: "2.4.54",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/httpd@2.4.54",
|
PURL: "pkg:generic/httpd@2.4.54",
|
||||||
Locations: singleLocation("httpd"),
|
Locations: locations("httpd"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("httpd-binary"),
|
||||||
Classifier: "httpd-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -139,10 +143,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "8.2.1",
|
Version: "8.2.1",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/php-cli@8.2.1",
|
PURL: "pkg:generic/php-cli@8.2.1",
|
||||||
Locations: singleLocation("php"),
|
Locations: locations("php"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("php-cli-binary"),
|
||||||
Classifier: "php-cli-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -153,10 +155,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "8.2.1",
|
Version: "8.2.1",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/php-fpm@8.2.1",
|
PURL: "pkg:generic/php-fpm@8.2.1",
|
||||||
Locations: singleLocation("php-fpm"),
|
Locations: locations("php-fpm"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("php-fpm-binary"),
|
||||||
Classifier: "php-fpm-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -167,10 +167,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "8.2.1",
|
Version: "8.2.1",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/php@8.2.1",
|
PURL: "pkg:generic/php@8.2.1",
|
||||||
Locations: singleLocation("libphp.so"),
|
Locations: locations("libphp.so"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("php-apache-binary"),
|
||||||
Classifier: "php-apache-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -223,10 +221,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "2.8.23",
|
Version: "2.8.23",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/redis@2.8.23",
|
PURL: "pkg:generic/redis@2.8.23",
|
||||||
Locations: singleLocation("redis-server"),
|
Locations: locations("redis-server"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("redis-binary"),
|
||||||
Classifier: "redis-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -237,10 +233,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "4.0.11",
|
Version: "4.0.11",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/redis@4.0.11",
|
PURL: "pkg:generic/redis@4.0.11",
|
||||||
Locations: singleLocation("redis-server"),
|
Locations: locations("redis-server"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("redis-binary"),
|
||||||
Classifier: "redis-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -251,10 +245,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "5.0.0",
|
Version: "5.0.0",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/redis@5.0.0",
|
PURL: "pkg:generic/redis@5.0.0",
|
||||||
Locations: singleLocation("redis-server"),
|
Locations: locations("redis-server"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("redis-binary"),
|
||||||
Classifier: "redis-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -265,10 +257,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "6.0.16",
|
Version: "6.0.16",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/redis@6.0.16",
|
PURL: "pkg:generic/redis@6.0.16",
|
||||||
Locations: singleLocation("redis-server"),
|
Locations: locations("redis-server"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("redis-binary"),
|
||||||
Classifier: "redis-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -279,101 +269,84 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "7.0.0",
|
Version: "7.0.0",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/redis@7.0.0",
|
PURL: "pkg:generic/redis@7.0.0",
|
||||||
Locations: singleLocation("redis-server"),
|
Locations: locations("redis-server"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("redis-binary"),
|
||||||
Classifier: "redis-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-libpython3.7.so",
|
name: "positive-libpython3.7.so",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/python-binary-lib-3.7",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "python",
|
Name: "python",
|
||||||
Version: "3.7.4a-vZ9",
|
Version: "3.7.4a-vZ9",
|
||||||
PURL: "pkg:generic/python@3.7.4a-vZ9",
|
PURL: "pkg:generic/python@3.7.4a-vZ9",
|
||||||
Locations: singleLocation("libpython3.7.so"),
|
Locations: locations("libpython3.7.so"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("python-binary-lib"),
|
||||||
Classifier: "python-binary-lib",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-python3.6",
|
name: "positive-python3.6",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/python-binary-3.6",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "python",
|
Name: "python",
|
||||||
Version: "3.6.3a-vZ9",
|
Version: "3.6.3a-vZ9",
|
||||||
PURL: "pkg:generic/python@3.6.3a-vZ9",
|
PURL: "pkg:generic/python@3.6.3a-vZ9",
|
||||||
Locations: singleLocation("python3.6"),
|
Locations: locations("python3.6"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("python-binary"),
|
||||||
Classifier: "python-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-patchlevel.h",
|
name: "positive-patchlevel.h",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/python-source-3.9",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "python",
|
Name: "python",
|
||||||
Version: "3.9-aZ5",
|
Version: "3.9-aZ5",
|
||||||
PURL: "pkg:generic/python@3.9-aZ5",
|
PURL: "pkg:generic/python@3.9-aZ5",
|
||||||
Locations: singleLocation("patchlevel.h"),
|
Locations: locations("patchlevel.h"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("cpython-source"),
|
||||||
Classifier: "cpython-source",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-go",
|
name: "positive-go",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/go-1.14",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "go",
|
Name: "go",
|
||||||
Version: "1.14",
|
Version: "1.14",
|
||||||
PURL: "pkg:generic/go@1.14",
|
PURL: "pkg:generic/go@1.14",
|
||||||
Locations: singleLocation("go"),
|
Locations: locations("go"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("go-binary"),
|
||||||
Classifier: "go-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-node",
|
name: "positive-node",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/node-19.2.1",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "node",
|
Name: "node",
|
||||||
Version: "19.2.1",
|
Version: "19.2.1",
|
||||||
PURL: "pkg:generic/node@19.2.1",
|
PURL: "pkg:generic/node@19.2.1",
|
||||||
Locations: singleLocation("node"),
|
Locations: locations("node"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("nodejs-binary"),
|
||||||
Classifier: "nodejs-binary",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-go-hint",
|
name: "positive-go-hint",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/go-hint-1.15",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "go",
|
Name: "go",
|
||||||
Version: "1.15",
|
Version: "1.15",
|
||||||
PURL: "pkg:generic/go@1.15",
|
PURL: "pkg:generic/go@1.15",
|
||||||
Locations: singleLocation("VERSION"),
|
Locations: locations("VERSION"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("go-binary-hint"),
|
||||||
Classifier: "go-binary-hint",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive-busybox",
|
name: "positive-busybox",
|
||||||
fixtureDir: "test-fixtures/classifiers/positive",
|
fixtureDir: "test-fixtures/classifiers/positive/busybox-3.33.3",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "busybox",
|
Name: "busybox",
|
||||||
Version: "3.33.3",
|
Version: "3.33.3",
|
||||||
Locations: singleLocation("["), // note: busybox is a link to [
|
Locations: locations("["), // note: busybox is a link to [
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("busybox-binary", "[", "busybox"),
|
||||||
Classifier: "busybox-binary",
|
|
||||||
VirtualPath: "busybox",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -384,11 +357,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "1.8.0_352-b08",
|
Version: "1.8.0_352-b08",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/java@1.8.0_352-b08",
|
PURL: "pkg:generic/java@1.8.0_352-b08",
|
||||||
Locations: singleLocation("java"),
|
Locations: locations("java"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("java-binary-openjdk", "java"),
|
||||||
Classifier: "java-binary-openjdk",
|
|
||||||
VirtualPath: "java",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -399,11 +369,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "11.0.17+8-LTS",
|
Version: "11.0.17+8-LTS",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/java@11.0.17+8-LTS",
|
PURL: "pkg:generic/java@11.0.17+8-LTS",
|
||||||
Locations: singleLocation("java"),
|
Locations: locations("java"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("java-binary-openjdk", "java"),
|
||||||
Classifier: "java-binary-openjdk",
|
|
||||||
VirtualPath: "java",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -414,11 +381,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "19.0.1+10-21",
|
Version: "19.0.1+10-21",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/java@19.0.1+10-21",
|
PURL: "pkg:generic/java@19.0.1+10-21",
|
||||||
Locations: singleLocation("java"),
|
Locations: locations("java"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("java-binary-oracle", "java"),
|
||||||
Classifier: "java-binary-oracle",
|
|
||||||
VirtualPath: "java",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -429,11 +393,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "19.0.1+10-21",
|
Version: "19.0.1+10-21",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/java@19.0.1+10-21",
|
PURL: "pkg:generic/java@19.0.1+10-21",
|
||||||
Locations: singleLocation("java"),
|
Locations: locations("java"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("java-binary-oracle", "java"),
|
||||||
Classifier: "java-binary-oracle",
|
|
||||||
VirtualPath: "java",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -444,11 +405,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
Version: "1.8.0-foreman_2022_09_22_15_30-b00",
|
Version: "1.8.0-foreman_2022_09_22_15_30-b00",
|
||||||
Type: "binary",
|
Type: "binary",
|
||||||
PURL: "pkg:generic/java@1.8.0-foreman_2022_09_22_15_30-b00",
|
PURL: "pkg:generic/java@1.8.0-foreman_2022_09_22_15_30-b00",
|
||||||
Locations: singleLocation("java"),
|
Locations: locations("java"),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("java-binary-ibm", "java"),
|
||||||
Classifier: "java-binary-ibm",
|
|
||||||
VirtualPath: "java",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -466,18 +424,20 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
packages, _, err := c.Catalog(resolver)
|
packages, _, err := c.Catalog(resolver)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ok := false
|
|
||||||
for _, p := range packages {
|
for _, p := range packages {
|
||||||
if test.expected.Locations.ToSlice()[0].RealPath == p.Locations.ToSlice()[0].RealPath {
|
expectedLocations := test.expected.Locations.ToSlice()
|
||||||
ok = true
|
gotLocations := p.Locations.ToSlice()
|
||||||
assertPackagesAreEqual(t, test.expected, p)
|
require.Len(t, gotLocations, len(expectedLocations))
|
||||||
|
|
||||||
|
for i, expectedLocation := range expectedLocations {
|
||||||
|
gotLocation := gotLocations[i]
|
||||||
|
if expectedLocation.RealPath != gotLocation.RealPath {
|
||||||
|
t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
assertPackagesAreEqual(t, test.expected, p)
|
||||||
t.Fatalf("could not find test location=%q", test.expected.Locations.ToSlice()[0].RealPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -494,11 +454,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
|||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "busybox",
|
Name: "busybox",
|
||||||
Version: "1.35.0",
|
Version: "1.35.0",
|
||||||
Locations: singleLocation("/bin/["),
|
Locations: locations("/bin/["),
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: metadata("busybox-binary", "/bin/[", "/bin/busybox"),
|
||||||
Classifier: "busybox-binary",
|
|
||||||
VirtualPath: "/bin/busybox",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -517,18 +474,20 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
|||||||
packages, _, err := c.Catalog(resolver)
|
packages, _, err := c.Catalog(resolver)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ok := false
|
|
||||||
for _, p := range packages {
|
for _, p := range packages {
|
||||||
if test.expected.Locations.ToSlice()[0].RealPath == p.Locations.ToSlice()[0].RealPath {
|
expectedLocations := test.expected.Locations.ToSlice()
|
||||||
ok = true
|
gotLocations := p.Locations.ToSlice()
|
||||||
assertPackagesAreEqual(t, test.expected, p)
|
require.Len(t, gotLocations, len(expectedLocations))
|
||||||
|
|
||||||
|
for i, expectedLocation := range expectedLocations {
|
||||||
|
gotLocation := gotLocations[i]
|
||||||
|
if expectedLocation.RealPath != gotLocation.RealPath {
|
||||||
|
t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
assertPackagesAreEqual(t, test.expected, p)
|
||||||
t.Fatalf("could not find test location=%q", test.expected.Locations.ToSlice()[0].RealPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,21 +506,80 @@ func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
|||||||
assert.Equal(t, 0, len(actualResults))
|
assert.Equal(t, 0, len(actualResults))
|
||||||
}
|
}
|
||||||
|
|
||||||
func singleLocation(s string) source.LocationSet {
|
func locations(locations ...string) source.LocationSet {
|
||||||
return source.NewLocationSet(source.NewLocation(s))
|
var locs []source.Location
|
||||||
|
for _, s := range locations {
|
||||||
|
locs = append(locs, source.NewLocation(s))
|
||||||
|
}
|
||||||
|
return source.NewLocationSet(locs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// metadata paths are: realPath, virtualPath
|
||||||
|
func metadata(classifier string, paths ...string) pkg.BinaryMetadata {
|
||||||
|
return pkg.BinaryMetadata{
|
||||||
|
Matches: []pkg.ClassifierMatch{
|
||||||
|
match(classifier, paths...),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// match paths are: realPath, virtualPath
|
||||||
|
func match(classifier string, paths ...string) pkg.ClassifierMatch {
|
||||||
|
realPath := ""
|
||||||
|
if len(paths) > 0 {
|
||||||
|
realPath = paths[0]
|
||||||
|
}
|
||||||
|
virtualPath := ""
|
||||||
|
if len(paths) > 1 {
|
||||||
|
virtualPath = paths[1]
|
||||||
|
}
|
||||||
|
return pkg.ClassifierMatch{
|
||||||
|
Classifier: classifier,
|
||||||
|
Location: source.Location{
|
||||||
|
Coordinates: source.Coordinates{
|
||||||
|
RealPath: realPath,
|
||||||
|
},
|
||||||
|
VirtualPath: virtualPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) {
|
func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) {
|
||||||
meta1 := expected.Metadata.(pkg.BinaryMetadata)
|
m1 := expected.Metadata.(pkg.BinaryMetadata).Matches
|
||||||
meta2 := p.Metadata.(pkg.BinaryMetadata)
|
m2 := p.Metadata.(pkg.BinaryMetadata).Matches
|
||||||
|
matches := true
|
||||||
|
if len(m1) == len(m2) {
|
||||||
|
for i, m1 := range m1 {
|
||||||
|
m2 := m2[i]
|
||||||
|
if m1.Classifier != m2.Classifier {
|
||||||
|
matches = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if m1.Location.RealPath != "" && m1.Location.RealPath != m2.Location.RealPath {
|
||||||
|
matches = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if m1.Location.VirtualPath != "" && m1.Location.VirtualPath != m2.Location.VirtualPath {
|
||||||
|
matches = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matches = false
|
||||||
|
}
|
||||||
if expected.Name != p.Name ||
|
if expected.Name != p.Name ||
|
||||||
expected.Version != p.Version ||
|
expected.Version != p.Version ||
|
||||||
expected.PURL != p.PURL ||
|
expected.PURL != p.PURL ||
|
||||||
meta1.Classifier != meta2.Classifier {
|
!matches {
|
||||||
assert.Failf(t, "packages not equal", "%v != %v", expected, p)
|
assert.Failf(t, "packages not equal", "%v != %v", stringifyPkg(expected), stringifyPkg(p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringifyPkg(p pkg.Package) string {
|
||||||
|
matches := p.Metadata.(pkg.BinaryMetadata).Matches
|
||||||
|
return fmt.Sprintf("(name=%s, version=%s, purl=%s, matches=%+v)", p.Name, p.Version, p.PURL, matches)
|
||||||
|
}
|
||||||
|
|
||||||
type panicyResolver struct {
|
type panicyResolver struct {
|
||||||
searchCalled bool
|
searchCalled bool
|
||||||
}
|
}
|
||||||
@ -586,7 +604,7 @@ func (p *panicyResolver) FileContentsByLocation(_ source.Location) (io.ReadClose
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *panicyResolver) HasPath(s string) bool {
|
func (p *panicyResolver) HasPath(_ string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -131,9 +131,12 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc
|
|||||||
FoundBy: catalogerName,
|
FoundBy: catalogerName,
|
||||||
MetadataType: pkg.BinaryMetadataType,
|
MetadataType: pkg.BinaryMetadataType,
|
||||||
Metadata: pkg.BinaryMetadata{
|
Metadata: pkg.BinaryMetadata{
|
||||||
Classifier: classifier.Class,
|
Matches: []pkg.ClassifierMatch{
|
||||||
RealPath: reader.RealPath,
|
{
|
||||||
VirtualPath: reader.VirtualPath,
|
Classifier: classifier.Class,
|
||||||
|
Location: reader.Location,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
!libpython3.7.so
|
!*.so
|
||||||
!libphp.so
|
!VERSION
|
||||||
@ -0,0 +1 @@
|
|||||||
|
3.8.16
|
||||||
@ -0,0 +1 @@
|
|||||||
|
3.8.16
|
||||||
@ -0,0 +1 @@
|
|||||||
|
#define PY_VERSION 3.8.16
|
||||||
@ -0,0 +1 @@
|
|||||||
|
3.8.16
|
||||||
@ -13,8 +13,8 @@ type Location struct {
|
|||||||
Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
|
Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
|
||||||
// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
|
// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
|
||||||
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
|
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
|
||||||
VirtualPath string `hash:"ignore"` // The path to the file which may or may not have hardlinks / symlinks
|
VirtualPath string `hash:"ignore" json:"virtualPath,omitempty"` // The path to the file which may or may not have hardlinks / symlinks
|
||||||
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.
|
// NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user