mirror of
https://github.com/anchore/syft.git
synced 2026-04-05 22:30:35 +02:00
Add annotations for evidence on package locations (#1723)
* add location annotations + deb evidence annotations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename LocationData struct and Annotation helper function Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add failing integration test for evidence coverage Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add evidence to aplm cataloger locations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * change location annotation helper to return a location copy Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add evidence to binary cataloger locations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * updated remaining catalogers with location annotations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix unit tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix linting Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * bump json schema Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * partial addressing of review comments Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename location.WithAnnotation Signed-off-by: Alex Goodman <alex.goodman@anchore.com> --------- Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
05715489c4
commit
5d156b8241
@ -6,5 +6,5 @@ const (
|
||||
|
||||
// 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.
|
||||
JSONSchemaVersion = "7.1.1"
|
||||
JSONSchemaVersion = "7.1.2"
|
||||
)
|
||||
|
||||
1668
schema/json/schema-7.1.2.json
Normal file
1668
schema/json/schema-7.1.2.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@ func Test_encodeComponentProperties(t *testing.T) {
|
||||
input: pkg.Package{
|
||||
FoundBy: "cataloger",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{Coordinates: source.Coordinates{RealPath: "test"}},
|
||||
source.NewLocationFromCoordinates(source.Coordinates{RealPath: "test"}),
|
||||
),
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "libc-utils",
|
||||
|
||||
@ -220,10 +220,8 @@ func toSyftCoordinates(f *spdx.File) source.Coordinates {
|
||||
}
|
||||
|
||||
func toSyftLocation(f *spdx.File) *source.Location {
|
||||
return &source.Location{
|
||||
Coordinates: toSyftCoordinates(f),
|
||||
VirtualPath: f.FileName,
|
||||
}
|
||||
l := source.NewVirtualLocationFromCoordinates(toSyftCoordinates(f), f.FileName)
|
||||
return &l
|
||||
}
|
||||
|
||||
func requireAndTrimPrefix(val interface{}, prefix string) string {
|
||||
|
||||
@ -36,36 +36,30 @@ func Test_toGithubModel(t *testing.T) {
|
||||
Name: "pkg-1",
|
||||
Version: "1.0.1",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(source.Coordinates{
|
||||
RealPath: "/usr/lib",
|
||||
FileSystemID: "fsid-1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "pkg-2",
|
||||
Version: "2.0.2",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(source.Coordinates{
|
||||
RealPath: "/usr/lib",
|
||||
FileSystemID: "fsid-1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "pkg-3",
|
||||
Version: "3.0.3",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(source.Coordinates{
|
||||
RealPath: "/etc",
|
||||
FileSystemID: "fsid-1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
},
|
||||
} {
|
||||
|
||||
@ -53,11 +53,9 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(source.Coordinates{
|
||||
RealPath: "/a/place/a",
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
Type: pkg.PythonPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
@ -79,11 +77,9 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(source.Coordinates{
|
||||
RealPath: "/b/place/b",
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
|
||||
@ -26,7 +26,7 @@ type PackageBasicData struct {
|
||||
Version string `json:"version"`
|
||||
Type pkg.Type `json:"type"`
|
||||
FoundBy string `json:"foundBy"`
|
||||
Locations []source.Coordinates `json:"locations"`
|
||||
Locations []source.Location `json:"locations"`
|
||||
Licenses []string `json:"licenses"`
|
||||
Language pkg.Language `json:"language"`
|
||||
CPEs []string `json:"cpes"`
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "7.1.1",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
|
||||
"version": "7.1.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.2.json"
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "7.1.1",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
|
||||
"version": "7.1.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.2.json"
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "7.1.1",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
|
||||
"version": "7.1.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.2.json"
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,12 +195,6 @@ func toPackageModel(p pkg.Package) model.Package {
|
||||
licenses = p.Licenses
|
||||
}
|
||||
|
||||
locations := p.Locations.ToSlice()
|
||||
var coordinates = make([]source.Coordinates, len(locations))
|
||||
for i, l := range locations {
|
||||
coordinates[i] = l.Coordinates
|
||||
}
|
||||
|
||||
return model.Package{
|
||||
PackageBasicData: model.PackageBasicData{
|
||||
ID: string(p.ID()),
|
||||
@ -208,7 +202,7 @@ func toPackageModel(p pkg.Package) model.Package {
|
||||
Version: p.Version,
|
||||
Type: p.Type,
|
||||
FoundBy: p.FoundBy,
|
||||
Locations: coordinates,
|
||||
Locations: p.Locations.ToSlice(),
|
||||
Licenses: licenses,
|
||||
Language: p.Language,
|
||||
CPEs: cpes,
|
||||
|
||||
@ -202,16 +202,11 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package {
|
||||
cpes = append(cpes, value)
|
||||
}
|
||||
|
||||
var locations = make([]source.Location, len(p.Locations))
|
||||
for i, c := range p.Locations {
|
||||
locations[i] = source.NewLocationFromCoordinates(c)
|
||||
}
|
||||
|
||||
out := pkg.Package{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
FoundBy: p.FoundBy,
|
||||
Locations: source.NewLocationSet(locations...),
|
||||
Locations: source.NewLocationSet(p.Locations...),
|
||||
Licenses: p.Licenses,
|
||||
Language: p.Language,
|
||||
Type: p.Type,
|
||||
|
||||
@ -327,45 +327,45 @@ func TestCatalog_MergeRecords(t *testing.T) {
|
||||
{
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:package:1:1:*:*:*:*:*:*:*")},
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/b/path",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/another/path",
|
||||
},
|
||||
"/another/path",
|
||||
),
|
||||
),
|
||||
Type: RpmPkg,
|
||||
},
|
||||
{
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:b:package:1:1:*:*:*:*:*:*:*")},
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/b/path",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
VirtualPath: "/another/path",
|
||||
},
|
||||
"/another/path",
|
||||
),
|
||||
),
|
||||
Type: RpmPkg,
|
||||
},
|
||||
},
|
||||
expectedLocations: []source.Location{
|
||||
{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/b/path",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/another/path",
|
||||
},
|
||||
{
|
||||
Coordinates: source.Coordinates{
|
||||
"/another/path",
|
||||
),
|
||||
source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/b/path",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
VirtualPath: "/another/path",
|
||||
},
|
||||
"/another/path",
|
||||
),
|
||||
},
|
||||
expectedCPECount: 2,
|
||||
},
|
||||
|
||||
@ -70,7 +70,11 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader
|
||||
}
|
||||
|
||||
return []pkg.Package{
|
||||
newPackage(*metadata, env.LinuxRelease, reader.Location),
|
||||
newPackage(
|
||||
*metadata,
|
||||
env.LinuxRelease,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -123,7 +123,7 @@ func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader s
|
||||
|
||||
pkgs := make([]pkg.Package, 0, len(apks))
|
||||
for _, apk := range apks {
|
||||
pkgs = append(pkgs, newPackage(apk, r, reader.Location))
|
||||
pkgs = append(pkgs, newPackage(apk, r, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)))
|
||||
}
|
||||
|
||||
return pkgs, discoverPackageDependencies(pkgs), nil
|
||||
|
||||
@ -829,12 +829,12 @@ func match(classifier string, paths ...string) pkg.ClassifierMatch {
|
||||
}
|
||||
return pkg.ClassifierMatch{
|
||||
Classifier: classifier,
|
||||
Location: source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
Location: source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: realPath,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
},
|
||||
virtualPath,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
@ -107,7 +106,13 @@ func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate stri
|
||||
}
|
||||
|
||||
matchMetadata := internal.MatchNamedCaptureGroups(tmplPattern, string(contents))
|
||||
return singlePackage(classifier, location, matchMetadata), nil
|
||||
|
||||
p := newPackage(classifier, location, matchMetadata)
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []pkg.Package{*p}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +125,13 @@ func fileContentsVersionMatcher(pattern string) evidenceMatcher {
|
||||
}
|
||||
|
||||
matchMetadata := internal.MatchNamedCaptureGroups(pat, string(contents))
|
||||
return singlePackage(classifier, location, matchMetadata), nil
|
||||
|
||||
p := newPackage(classifier, location, matchMetadata)
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []pkg.Package{*p}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,8 +152,8 @@ func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evide
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, libraryLication := range locations {
|
||||
pkgs, err := sharedLibraryMatcher(resolver, classifier, libraryLication)
|
||||
for _, libraryLocation := range locations {
|
||||
pkgs, err := sharedLibraryMatcher(resolver, classifier, libraryLocation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -176,58 +187,6 @@ func mustPURL(purl string) packageurl.PackageURL {
|
||||
return p
|
||||
}
|
||||
|
||||
func singlePackage(classifier classifier, location source.Location, matchMetadata map[string]string) []pkg.Package {
|
||||
version, ok := matchMetadata["version"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
update := matchMetadata["update"]
|
||||
|
||||
var cpes []cpe.CPE
|
||||
for _, c := range classifier.CPEs {
|
||||
c.Version = version
|
||||
c.Update = update
|
||||
cpes = append(cpes, c)
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: classifier.Package,
|
||||
Version: version,
|
||||
Locations: source.NewLocationSet(location),
|
||||
Type: pkg.BinaryPkg,
|
||||
CPEs: cpes,
|
||||
FoundBy: catalogerName,
|
||||
MetadataType: pkg.BinaryMetadataType,
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
{
|
||||
Classifier: classifier.Class,
|
||||
Location: location,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if classifier.Type != "" {
|
||||
p.Type = classifier.Type
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(classifier.PURL, emptyPURL) {
|
||||
purl := classifier.PURL
|
||||
purl.Version = version
|
||||
p.PURL = purl.ToString()
|
||||
}
|
||||
|
||||
if classifier.Language != "" {
|
||||
p.Language = classifier.Language
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
return []pkg.Package{p}
|
||||
}
|
||||
|
||||
func getContents(resolver source.FileResolver, location source.Location) ([]byte, error) {
|
||||
reader, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
|
||||
63
syft/pkg/cataloger/binary/package.go
Normal file
63
syft/pkg/cataloger/binary/package.go
Normal file
@ -0,0 +1,63 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func newPackage(classifier classifier, location source.Location, matchMetadata map[string]string) *pkg.Package {
|
||||
version, ok := matchMetadata["version"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
update := matchMetadata["update"]
|
||||
|
||||
var cpes []cpe.CPE
|
||||
for _, c := range classifier.CPEs {
|
||||
c.Version = version
|
||||
c.Update = update
|
||||
cpes = append(cpes, c)
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: classifier.Package,
|
||||
Version: version,
|
||||
Locations: source.NewLocationSet(
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Type: pkg.BinaryPkg,
|
||||
CPEs: cpes,
|
||||
FoundBy: catalogerName,
|
||||
MetadataType: pkg.BinaryMetadataType,
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
{
|
||||
Classifier: classifier.Class,
|
||||
Location: location,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if classifier.Type != "" {
|
||||
p.Type = classifier.Type
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(classifier.PURL, emptyPURL) {
|
||||
purl := classifier.PURL
|
||||
purl.Version = version
|
||||
p.PURL = purl.ToString()
|
||||
}
|
||||
|
||||
if classifier.Language != "" {
|
||||
p.Language = classifier.Language
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
return &p
|
||||
}
|
||||
@ -48,7 +48,10 @@ func parseConanfile(_ source.FileResolver, _ *generic.Environment, reader source
|
||||
continue
|
||||
}
|
||||
|
||||
p := newConanfilePackage(m, reader.Location)
|
||||
p := newConanfilePackage(
|
||||
m,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -44,7 +44,10 @@ func parseConanlock(_ source.FileResolver, _ *generic.Environment, reader source
|
||||
Context: node.Context,
|
||||
}
|
||||
|
||||
p := newConanlockPackage(metadata, reader.Location)
|
||||
p := newConanlockPackage(
|
||||
metadata,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
|
||||
if p != nil {
|
||||
pkgs = append(pkgs, *p)
|
||||
|
||||
@ -58,7 +58,13 @@ func parsePubspecLock(_ source.FileResolver, _ *generic.Environment, reader sour
|
||||
|
||||
for _, name := range names {
|
||||
pubPkg := p.Packages[name]
|
||||
pkgs = append(pkgs, newPubspecLockPackage(name, pubPkg, reader.Location))
|
||||
pkgs = append(pkgs,
|
||||
newPubspecLockPackage(
|
||||
name,
|
||||
pubPkg,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Package dpkg provides a concrete Cataloger implementation for Debian package DB status files.
|
||||
Package deb provides a concrete Cataloger implementation for Debian package DB status files.
|
||||
*/
|
||||
package deb
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ func newDpkgPackage(d pkg.DpkgMetadata, dbLocation source.Location, resolver sou
|
||||
p := pkg.Package{
|
||||
Name: d.Package,
|
||||
Version: d.Version,
|
||||
Locations: source.NewLocationSet(dbLocation),
|
||||
Locations: source.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(d, release),
|
||||
Type: pkg.DebPkg,
|
||||
MetadataType: pkg.DpkgMetadataType,
|
||||
@ -162,6 +162,7 @@ func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Lo
|
||||
return files, locations
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location, m pkg.DpkgMetadata) (io.ReadCloser, *source.Location) {
|
||||
var md5Reader io.ReadCloser
|
||||
var err error
|
||||
@ -182,17 +183,22 @@ func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location,
|
||||
location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", m.Package+md5sumsExt))
|
||||
}
|
||||
|
||||
if location == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// this is unexpected, but not a show-stopper
|
||||
if location != nil {
|
||||
md5Reader, err = resolver.FileContentsByLocation(*location)
|
||||
if err != nil {
|
||||
log.Warnf("failed to fetch deb md5 contents (package=%s): %+v", m.Package, err)
|
||||
}
|
||||
}
|
||||
|
||||
return md5Reader, location
|
||||
l := location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)
|
||||
|
||||
return md5Reader, &l
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func fetchConffileContents(resolver source.FileResolver, dbLocation source.Location, m pkg.DpkgMetadata) (io.ReadCloser, *source.Location) {
|
||||
var reader io.ReadCloser
|
||||
var err error
|
||||
@ -213,15 +219,19 @@ func fetchConffileContents(resolver source.FileResolver, dbLocation source.Locat
|
||||
location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", m.Package+conffilesExt))
|
||||
}
|
||||
|
||||
if location == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// this is unexpected, but not a show-stopper
|
||||
if location != nil {
|
||||
reader, err = resolver.FileContentsByLocation(*location)
|
||||
if err != nil {
|
||||
log.Warnf("failed to fetch deb conffiles contents (package=%s): %+v", m.Package, err)
|
||||
}
|
||||
}
|
||||
|
||||
return reader, location
|
||||
l := location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)
|
||||
|
||||
return reader, &l
|
||||
}
|
||||
|
||||
func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Location, m pkg.DpkgMetadata) (io.ReadCloser, *source.Location) {
|
||||
@ -243,7 +253,9 @@ func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Loca
|
||||
log.Warnf("failed to fetch deb copyright contents (package=%s): %w", m.Package, err)
|
||||
}
|
||||
|
||||
return reader, location
|
||||
l := location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)
|
||||
|
||||
return reader, &l
|
||||
}
|
||||
|
||||
func md5Key(metadata pkg.DpkgMetadata) string {
|
||||
|
||||
@ -45,7 +45,11 @@ func parseDotnetDeps(_ source.FileResolver, _ *generic.Environment, reader sourc
|
||||
|
||||
for _, nameVersion := range names {
|
||||
lib := p.Libraries[nameVersion]
|
||||
dotnetPkg := newDotnetDepsPackage(nameVersion, lib, reader.Location)
|
||||
dotnetPkg := newDotnetDepsPackage(
|
||||
nameVersion,
|
||||
lib,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
|
||||
if dotnetPkg != nil {
|
||||
pkgs = append(pkgs, *dotnetPkg)
|
||||
|
||||
@ -43,11 +43,16 @@ func parseMixLock(_ source.FileResolver, _ *generic.Environment, reader source.L
|
||||
continue
|
||||
}
|
||||
|
||||
packages = append(packages, newPackage(pkg.MixLockMetadata{
|
||||
packages = append(packages,
|
||||
newPackage(
|
||||
pkg.MixLockMetadata{
|
||||
Name: name,
|
||||
Version: version,
|
||||
PkgHash: hash,
|
||||
PkgHashExt: hashExt,
|
||||
}))
|
||||
},
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,15 +6,18 @@ import (
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func TestParseMixLock(t *testing.T) {
|
||||
locations := source.NewLocationSet(source.NewLocation("test-fixtures/mix.lock"))
|
||||
expected := []pkg.Package{
|
||||
{
|
||||
Name: "castore",
|
||||
Version: "0.1.17",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/castore@0.1.17",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -29,6 +32,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "1.1.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/connection@1.1.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -43,6 +47,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "2.9.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/cowboy@2.9.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -57,6 +62,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "0.4.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/cowboy_telemetry@0.4.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -71,6 +77,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "2.11.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/cowlib@2.11.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -85,6 +92,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "2.4.2",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/db_connection@2.4.2",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -99,6 +107,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "2.0.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/decimal@2.0.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -113,6 +122,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "1.4.25",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/earmark_parser@1.4.25",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -127,6 +137,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "3.8.1",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/ecto@3.8.1",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -141,6 +152,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "3.8.1",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/ecto_sql@3.8.1",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -155,6 +167,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "0.5.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/esbuild@0.5.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -169,6 +182,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "0.28.4",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/ex_doc@0.28.4",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -183,6 +197,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "0.19.1",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/gettext@0.19.1",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -197,6 +212,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "0.1.1",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/hpax@0.1.1",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
@ -211,6 +227,7 @@ func TestParseMixLock(t *testing.T) {
|
||||
Version: "1.3.0",
|
||||
Language: pkg.Elixir,
|
||||
Type: pkg.HexPkg,
|
||||
Locations: locations,
|
||||
PURL: "pkg:hex/jason@1.3.0",
|
||||
MetadataType: pkg.MixLockMetadataType,
|
||||
Metadata: pkg.MixLockMetadata{
|
||||
|
||||
@ -48,10 +48,13 @@ func parseRebarLock(_ source.FileResolver, _ *generic.Environment, reader source
|
||||
version = versionNode.Get(2).Get(1).String()
|
||||
}
|
||||
|
||||
p := newPackage(pkg.RebarLockMetadata{
|
||||
p := newPackage(
|
||||
pkg.RebarLockMetadata{
|
||||
Name: name,
|
||||
Version: version,
|
||||
})
|
||||
},
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
|
||||
pkgMap[name] = &p
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func TestParseRebarLock(t *testing.T) {
|
||||
@ -261,6 +262,10 @@ func TestParseRebarLock(t *testing.T) {
|
||||
// TODO: relationships are not under test
|
||||
var expectedRelationships []artifact.Relationship
|
||||
|
||||
for idx := range test.expected {
|
||||
test.expected[idx].Locations = source.NewLocationSet(source.NewLocation(test.fixture))
|
||||
}
|
||||
|
||||
pkgtest.TestFileParser(t, test.fixture, parseRebarLock, test.expected, expectedRelationships)
|
||||
})
|
||||
}
|
||||
|
||||
@ -62,7 +62,15 @@ func (c *goBinaryCataloger) parseGoBinary(resolver source.FileResolver, _ *gener
|
||||
|
||||
func (c *goBinaryCataloger) makeGoMainPackage(resolver source.FileResolver, mod *debug.BuildInfo, arch string, location source.Location) pkg.Package {
|
||||
gbs := getBuildSettings(mod.Settings)
|
||||
main := c.newGoBinaryPackage(resolver, &mod.Main, mod.Main.Path, mod.GoVersion, arch, gbs, location)
|
||||
main := c.newGoBinaryPackage(
|
||||
resolver,
|
||||
&mod.Main,
|
||||
mod.Main.Path,
|
||||
mod.GoVersion,
|
||||
arch,
|
||||
gbs,
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
if main.Version == devel {
|
||||
if version, ok := gbs["vcs.revision"]; ok {
|
||||
if timestamp, ok := gbs["vcs.time"]; ok {
|
||||
@ -204,7 +212,15 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver source.FileResolver, locatio
|
||||
if dep == nil {
|
||||
continue
|
||||
}
|
||||
p := c.newGoBinaryPackage(resolver, dep, mod.Main.Path, mod.GoVersion, arch, nil, location)
|
||||
p := c.newGoBinaryPackage(
|
||||
resolver,
|
||||
dep,
|
||||
mod.Main.Path,
|
||||
mod.GoVersion,
|
||||
arch,
|
||||
nil,
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
if pkg.IsValid(&p) {
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
@ -136,12 +136,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Version: "(devel)",
|
||||
PURL: "pkg:golang/github.com/anchore/syft@(devel)",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -183,12 +183,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{},
|
||||
@ -226,12 +226,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -262,12 +262,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -320,12 +320,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Version: "v0.0.0-20221014195457-41bc6bb41035",
|
||||
PURL: "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -375,12 +375,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -397,12 +397,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -452,12 +452,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -473,12 +473,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
@ -502,12 +502,12 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
}
|
||||
p.SetID()
|
||||
}
|
||||
location := source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
location := source.NewLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
c := goBinaryCataloger{}
|
||||
pkgs := c.buildGoPkgInfo(source.NewMockResolverForPaths(), location, test.mod, test.arch)
|
||||
|
||||
@ -51,7 +51,7 @@ func (c *goModCataloger) parseGoModFile(resolver source.FileResolver, _ *generic
|
||||
Name: m.Mod.Path,
|
||||
Version: m.Mod.Version,
|
||||
Licenses: licenses,
|
||||
Locations: source.NewLocationSet(reader.Location),
|
||||
Locations: source.NewLocationSet(reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(m.Mod.Path, m.Mod.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
@ -73,7 +73,7 @@ func (c *goModCataloger) parseGoModFile(resolver source.FileResolver, _ *generic
|
||||
Name: m.New.Path,
|
||||
Version: m.New.Version,
|
||||
Licenses: licenses,
|
||||
Locations: source.NewLocationSet(reader.Location),
|
||||
Locations: source.NewLocationSet(reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(m.New.Path, m.New.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
|
||||
@ -46,6 +46,14 @@ func parseCabalFreeze(_ source.FileResolver, _ *generic.Environment, reader sour
|
||||
fields := strings.Split(line, " ==")
|
||||
|
||||
pkgName, pkgVersion := fields[0], fields[1]
|
||||
pkgs = append(pkgs, newPackage(pkgName, pkgVersion, nil, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackage(
|
||||
pkgName,
|
||||
pkgVersion,
|
||||
nil,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,10 +62,18 @@ func parseStackLock(_ source.FileResolver, _ *generic.Environment, reader source
|
||||
|
||||
for _, pack := range lockFile.Packages {
|
||||
pkgName, pkgVersion, pkgHash := parseStackPackageEncoding(pack.Completed.Hackage)
|
||||
pkgs = append(pkgs, newPackage(pkgName, pkgVersion, &pkg.HackageMetadata{
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackage(
|
||||
pkgName,
|
||||
pkgVersion,
|
||||
&pkg.HackageMetadata{
|
||||
PkgHash: pkgHash,
|
||||
SnapshotURL: snapshotURL,
|
||||
}, reader.Location))
|
||||
},
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
@ -34,9 +34,17 @@ func parseStackYaml(_ source.FileResolver, _ *generic.Environment, reader source
|
||||
var pkgs []pkg.Package
|
||||
for _, dep := range stackFile.ExtraDeps {
|
||||
pkgName, pkgVersion, pkgHash := parseStackPackageEncoding(dep)
|
||||
pkgs = append(pkgs, newPackage(pkgName, pkgVersion, &pkg.HackageMetadata{
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackage(
|
||||
pkgName,
|
||||
pkgVersion,
|
||||
&pkg.HackageMetadata{
|
||||
PkgHash: pkgHash,
|
||||
}, reader.Location))
|
||||
},
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
@ -190,7 +190,9 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
||||
Version: selectVersion(manifest, j.fileInfo),
|
||||
Licenses: selectLicense(manifest),
|
||||
Language: pkg.Java,
|
||||
Locations: source.NewLocationSet(j.location),
|
||||
Locations: source.NewLocationSet(
|
||||
j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Type: j.fileInfo.pkgType(),
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
@ -382,7 +384,9 @@ func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.Po
|
||||
p := pkg.Package{
|
||||
Name: pomProperties.ArtifactID,
|
||||
Version: pomProperties.Version,
|
||||
Locations: source.NewLocationSet(location),
|
||||
Locations: source.NewLocationSet(
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Language: pkg.Java,
|
||||
Type: pomProperties.PkgTypeIndicated(),
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
|
||||
@ -51,7 +51,9 @@ func parseGradleLockfile(_ source.FileResolver, _ *generic.Environment, reader s
|
||||
mappedPkg := pkg.Package{
|
||||
Name: dep.Name,
|
||||
Version: dep.Version,
|
||||
Locations: source.NewLocationSet(reader.Location),
|
||||
Locations: source.NewLocationSet(
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
|
||||
@ -29,7 +29,11 @@ func parserPomXML(_ source.FileResolver, _ *generic.Environment, reader source.L
|
||||
|
||||
var pkgs []pkg.Package
|
||||
for _, dep := range pom.Dependencies {
|
||||
p := newPackageFromPom(pom, dep, reader.Location)
|
||||
p := newPackageFromPom(
|
||||
pom,
|
||||
dep,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
if p.Name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ func newPackageLockV1Package(resolver source.FileResolver, location source.Locat
|
||||
pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: source.NewLocationSet(location),
|
||||
Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(name, version),
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
@ -89,7 +89,7 @@ func newPackageLockV2Package(resolver source.FileResolver, location source.Locat
|
||||
pkg.Package{
|
||||
Name: name,
|
||||
Version: u.Version,
|
||||
Locations: source.NewLocationSet(location),
|
||||
Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(name, u.Version),
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
@ -107,7 +107,7 @@ func newPnpmPackage(resolver source.FileResolver, location source.Location, name
|
||||
pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: source.NewLocationSet(location),
|
||||
Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(name, version),
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
@ -122,7 +122,7 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location,
|
||||
pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: source.NewLocationSet(location),
|
||||
Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
PURL: packageURL(name, version),
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
|
||||
@ -68,7 +68,10 @@ func parsePackageJSON(_ source.FileResolver, _ *generic.Environment, reader sour
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPackageJSONPackage(p, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackageJSONPackage(p, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
)
|
||||
}
|
||||
|
||||
pkg.Sort(pkgs)
|
||||
|
||||
@ -113,7 +113,10 @@ func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, read
|
||||
name = pkgMeta.Name
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPackageLockV2Package(resolver, reader.Location, getNameFromPath(name), pkgMeta))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackageLockV2Package(resolver, reader.Location, getNameFromPath(name), pkgMeta),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ func (c *StoreCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, [
|
||||
continue
|
||||
}
|
||||
|
||||
p := newNixStorePackage(*storePath, location)
|
||||
p := newNixStorePackage(*storePath, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,13 @@ func parseComposerLock(_ source.FileResolver, _ *generic.Environment, reader sou
|
||||
return nil, nil, fmt.Errorf("failed to parse composer.lock file: %w", err)
|
||||
}
|
||||
for _, m := range lock.Packages {
|
||||
pkgs = append(pkgs, newComposerLockPackage(m, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newComposerLockPackage(
|
||||
m,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +53,12 @@ func parseInstalledJSON(_ source.FileResolver, _ *generic.Environment, reader so
|
||||
return nil, nil, fmt.Errorf("failed to parse installed.json file: %w", err)
|
||||
}
|
||||
for _, pkgMeta := range lock.Packages {
|
||||
pkgs = append(pkgs, newComposerLockPackage(pkgMeta, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newComposerLockPackage(pkgMeta,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,9 @@ func parsePortageContents(resolver source.FileResolver, _ *generic.Environment,
|
||||
Name: name,
|
||||
Version: version,
|
||||
PURL: packageURL(name, version),
|
||||
Locations: source.NewLocationSet(),
|
||||
Locations: source.NewLocationSet(
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Type: pkg.PortagePkg,
|
||||
MetadataType: pkg.PortageMetadataType,
|
||||
Metadata: pkg.PortageMetadata{
|
||||
@ -117,7 +119,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk
|
||||
licenses := findings.ToSlice()
|
||||
sort.Strings(licenses)
|
||||
p.Licenses = licenses
|
||||
p.Locations.Add(*location)
|
||||
p.Locations.Add(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
}
|
||||
|
||||
func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
||||
@ -150,5 +152,5 @@ func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Pa
|
||||
}
|
||||
|
||||
p.Metadata = entry
|
||||
p.Locations.Add(*location)
|
||||
p.Locations.Add(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
}
|
||||
|
||||
@ -39,7 +39,14 @@ func parsePoetryLock(_ source.FileResolver, _ *generic.Environment, reader sourc
|
||||
|
||||
var pkgs []pkg.Package
|
||||
for _, p := range metadata.Packages {
|
||||
pkgs = append(pkgs, newPackageForIndex(p.Name, p.Version, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackageForIndex(
|
||||
p.Name,
|
||||
p.Version,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
@ -61,7 +61,14 @@ func parseRequirementsTxt(_ source.FileResolver, _ *generic.Environment, reader
|
||||
log.WithFields("path", reader.RealPath).Debugf("found empty package in requirements.txt line: %q", line)
|
||||
continue
|
||||
}
|
||||
packages = append(packages, newPackageForIndex(name, version, reader.Location))
|
||||
packages = append(
|
||||
packages,
|
||||
newPackageForIndex(
|
||||
name,
|
||||
version,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
|
||||
@ -53,7 +53,14 @@ func parseSetup(_ source.FileResolver, _ *generic.Environment, reader source.Loc
|
||||
continue
|
||||
}
|
||||
|
||||
packages = append(packages, newPackageForIndex(name, version, reader.Location))
|
||||
packages = append(
|
||||
packages,
|
||||
newPackageForIndex(
|
||||
name,
|
||||
version,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ func fetchInstalledFiles(resolver source.FileResolver, metadataLocation source.L
|
||||
installedFilesRef := resolver.RelativeFileByPath(metadataLocation, installedFilesPath)
|
||||
|
||||
if installedFilesRef != nil {
|
||||
sources = append(sources, *installedFilesRef)
|
||||
sources = append(sources, installedFilesRef.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
|
||||
installedFilesContents, err := resolver.FileContentsByLocation(*installedFilesRef)
|
||||
if err != nil {
|
||||
@ -78,7 +78,7 @@ func fetchRecordFiles(resolver source.FileResolver, metadataLocation source.Loca
|
||||
recordRef := resolver.RelativeFileByPath(metadataLocation, recordPath)
|
||||
|
||||
if recordRef != nil {
|
||||
sources = append(sources, *recordRef)
|
||||
sources = append(sources, recordRef.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
|
||||
recordContents, err := resolver.FileContentsByLocation(*recordRef)
|
||||
if err != nil {
|
||||
@ -105,7 +105,7 @@ func fetchTopLevelPackages(resolver source.FileResolver, metadataLocation source
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
sources = append(sources, *topLevelLocation)
|
||||
sources = append(sources, topLevelLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
|
||||
topLevelContents, err := resolver.FileContentsByLocation(*topLevelLocation)
|
||||
if err != nil {
|
||||
@ -134,7 +134,7 @@ func fetchDirectURLData(resolver source.FileResolver, metadataLocation source.Lo
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
sources = append(sources, *directURLLocation)
|
||||
sources = append(sources, directURLLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
|
||||
directURLContents, err := resolver.FileContentsByLocation(*directURLLocation)
|
||||
if err != nil {
|
||||
@ -161,7 +161,9 @@ func fetchDirectURLData(resolver source.FileResolver, metadataLocation source.Lo
|
||||
|
||||
// assembleEggOrWheelMetadata discovers and accumulates python package metadata from multiple file sources and returns a single metadata object as well as a list of files where the metadata was derived from.
|
||||
func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation source.Location) (*pkg.PythonPackageMetadata, []source.Location, error) {
|
||||
var sources = []source.Location{metadataLocation}
|
||||
var sources = []source.Location{
|
||||
metadataLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
}
|
||||
|
||||
metadataContents, err := resolver.FileContentsByLocation(metadataLocation)
|
||||
if err != nil {
|
||||
|
||||
@ -13,12 +13,12 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func newPackage(dbLocation source.Location, metadata pkg.RpmMetadata, distro *linux.Release) pkg.Package {
|
||||
func newPackage(location source.Location, metadata pkg.RpmMetadata, distro *linux.Release) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: metadata.Name,
|
||||
Version: toELVersion(metadata),
|
||||
PURL: packageURL(metadata, distro),
|
||||
Locations: source.NewLocationSet(dbLocation),
|
||||
Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Type: pkg.RpmPkg,
|
||||
MetadataType: pkg.RpmMetadataType,
|
||||
Metadata: metadata,
|
||||
|
||||
@ -44,7 +44,7 @@ func parseGemFileLockEntries(_ source.FileResolver, _ *generic.Environment, read
|
||||
newGemfileLockPackage(
|
||||
candidate[0],
|
||||
strings.Trim(candidate[1], "()"),
|
||||
reader.Location,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -95,7 +95,10 @@ func parseGemSpecEntries(_ source.FileResolver, _ *generic.Environment, reader s
|
||||
return nil, nil, fmt.Errorf("unable to decode gem metadata: %w", err)
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newGemspecPackage(metadata, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newGemspecPackage(metadata, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
@ -31,7 +31,7 @@ func newPackagesFromAudit(location source.Location, versionInfo rustaudit.Versio
|
||||
|
||||
for _, dep := range versionInfo.Packages {
|
||||
dep := dep
|
||||
p := newPackageFromAudit(&dep, location)
|
||||
p := newPackageFromAudit(&dep, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
if pkg.IsValid(&p) && dep.Kind == rustaudit.Runtime {
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
@ -36,7 +36,13 @@ func parseCargoLock(_ source.FileResolver, _ *generic.Environment, reader source
|
||||
if p.Dependencies == nil {
|
||||
p.Dependencies = make([]string, 0)
|
||||
}
|
||||
pkgs = append(pkgs, newPackageFromCargoMetadata(p, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackageFromCargoMetadata(
|
||||
p,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
@ -47,7 +47,9 @@ func parseSBOM(_ source.FileResolver, _ *generic.Environment, reader source.Loca
|
||||
// Why not keep the original list of locations? Since the "locations" field is meant to capture
|
||||
// where there is evidence of this file, and the catalogers have not run against any file other than,
|
||||
// the SBOM, this is the only location that is relevant for this cataloger.
|
||||
p.Locations = source.NewLocationSet(reader.Location)
|
||||
p.Locations = source.NewLocationSet(
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
p.FoundBy = catalogerName
|
||||
|
||||
pkgs = append(pkgs, p)
|
||||
|
||||
@ -59,7 +59,15 @@ func parsePodfileLock(_ source.FileResolver, _ *generic.Environment, reader sour
|
||||
return nil, nil, fmt.Errorf("malformed podfile.lock: incomplete checksums")
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPackage(podName, podVersion, pkgHash, reader.Location))
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackage(
|
||||
podName,
|
||||
podVersion,
|
||||
pkgHash,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
|
||||
7
syft/pkg/evidence.go
Normal file
7
syft/pkg/evidence.go
Normal file
@ -0,0 +1,7 @@
|
||||
package pkg
|
||||
|
||||
const (
|
||||
EvidenceAnnotationKey = "evidence"
|
||||
PrimaryEvidenceAnnotation = "primary"
|
||||
SupportingEvidenceAnnotation = "supporting"
|
||||
)
|
||||
@ -7,19 +7,19 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func TestIDUniqueness(t *testing.T) {
|
||||
originalLocation := source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
originalLocation := source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "39.0742° N, 21.8243° E",
|
||||
FileSystemID: "Earth",
|
||||
},
|
||||
VirtualPath: "/Ancient-Greece",
|
||||
}
|
||||
"/Ancient-Greece",
|
||||
)
|
||||
|
||||
originalPkg := Package{
|
||||
Name: "pi",
|
||||
Version: "3.14",
|
||||
@ -238,13 +238,13 @@ func TestIDUniqueness(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPackage_Merge(t *testing.T) {
|
||||
originalLocation := source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
originalLocation := source.NewVirtualLocationFromCoordinates(
|
||||
source.Coordinates{
|
||||
RealPath: "39.0742° N, 21.8243° E",
|
||||
FileSystemID: "Earth",
|
||||
},
|
||||
VirtualPath: "/Ancient-Greece",
|
||||
}
|
||||
"/Ancient-Greece",
|
||||
)
|
||||
|
||||
similarLocation := originalLocation
|
||||
similarLocation.FileSystemID = "Mars"
|
||||
@ -423,13 +423,23 @@ func TestPackage_Merge(t *testing.T) {
|
||||
cmp.AllowUnexported(Package{}),
|
||||
cmp.Comparer(
|
||||
func(x, y source.LocationSet) bool {
|
||||
return cmp.Equal(
|
||||
x.ToSlice(), y.ToSlice(),
|
||||
cmp.AllowUnexported(source.Location{}),
|
||||
cmp.AllowUnexported(file.Reference{}),
|
||||
)
|
||||
xs := x.ToSlice()
|
||||
ys := y.ToSlice()
|
||||
|
||||
if len(xs) != len(ys) {
|
||||
return false
|
||||
}
|
||||
for i, xe := range xs {
|
||||
ye := ys[i]
|
||||
if !locationComparer(xe, ye) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
),
|
||||
cmp.Comparer(locationComparer),
|
||||
); diff != "" {
|
||||
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
|
||||
}
|
||||
@ -437,6 +447,10 @@ func TestPackage_Merge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func locationComparer(x, y source.Location) bool {
|
||||
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.VirtualPath, y.VirtualPath)
|
||||
}
|
||||
|
||||
func TestIsValid(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
||||
@ -765,30 +765,10 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
//VirtualPath: "file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
//VirtualPath: "file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "parent/file-4.txt",
|
||||
},
|
||||
//VirtualPath: "parent/file-4.txt",
|
||||
},
|
||||
NewLocation("file-1.txt"), // note: missing virtual path "file-1.txt"
|
||||
NewLocation("file-3.txt"), // note: missing virtual path "file-3.txt"
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt"
|
||||
NewLocation("parent/file-4.txt"), // note: missing virtual path "file-4.txt"
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -801,31 +781,11 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
VirtualPath: "link-1",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
},
|
||||
NewVirtualLocation("file-1.txt", "link-1"),
|
||||
NewVirtualLocation("file-2.txt", "link-2"),
|
||||
// we already have this real file path via another link, so only one is returned
|
||||
//{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "link-indirect",
|
||||
//},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
VirtualPath: "link-within",
|
||||
},
|
||||
//NewVirtualLocation("file-2.txt", "link-indirect"),
|
||||
NewVirtualLocation("file-3.txt", "link-within"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -838,12 +798,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// this has two copies in the base image, which overwrites the same location
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -855,30 +810,10 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
//VirtualPath: "file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
//VirtualPath: "file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "parent/file-4.txt",
|
||||
},
|
||||
//VirtualPath: "parent/file-4.txt",
|
||||
},
|
||||
NewLocation("file-1.txt"), // note: missing virtual path "file-1.txt"
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt"
|
||||
NewLocation("file-3.txt"), // note: missing virtual path "file-3.txt"
|
||||
NewLocation("parent/file-4.txt"), // note: missing virtual path "parent/file-4.txt"
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -890,28 +825,35 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
VirtualPath: "link-1",
|
||||
ref: file.Reference{RealPath: "file-1.txt"},
|
||||
},
|
||||
},
|
||||
{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
ref: file.Reference{RealPath: "file-2.txt"},
|
||||
},
|
||||
},
|
||||
// we already have this real file path via another link, so only one is returned
|
||||
//{
|
||||
// LocationData: LocationData{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "link-indirect",
|
||||
// ref: file.Reference{RealPath: "file-2.txt"},
|
||||
// },
|
||||
//},
|
||||
{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
@ -920,6 +862,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "by extension",
|
||||
runner: func(resolver FileResolver) []Location {
|
||||
@ -929,30 +872,10 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
//VirtualPath: "file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
//VirtualPath: "file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "parent/file-4.txt",
|
||||
},
|
||||
//VirtualPath: "parent/file-4.txt",
|
||||
},
|
||||
NewLocation("file-1.txt"), // note: missing virtual path "file-1.txt"
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt"
|
||||
NewLocation("file-3.txt"), // note: missing virtual path "file-3.txt"
|
||||
NewLocation("parent/file-4.txt"), // note: missing virtual path "parent/file-4.txt"
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -965,12 +888,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
},
|
||||
NewVirtualLocation("file-2.txt", "link-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -983,12 +901,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-indirect",
|
||||
},
|
||||
NewVirtualLocation("file-2.txt", "link-indirect"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -6,8 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
)
|
||||
|
||||
func TestExcludingResolver(t *testing.T) {
|
||||
@ -79,25 +77,25 @@ func TestExcludingResolver(t *testing.T) {
|
||||
|
||||
for _, path := range diff {
|
||||
assert.False(t, er.HasPath(path))
|
||||
c, err := er.FileContentsByLocation(makeLocation(path))
|
||||
c, err := er.FileContentsByLocation(NewLocation(path))
|
||||
assert.Nil(t, c)
|
||||
assert.Error(t, err)
|
||||
m, err := er.FileMetadataByLocation(makeLocation(path))
|
||||
m, err := er.FileMetadataByLocation(NewLocation(path))
|
||||
assert.Empty(t, m.LinkDestination)
|
||||
assert.Error(t, err)
|
||||
l := er.RelativeFileByPath(makeLocation(""), path)
|
||||
l := er.RelativeFileByPath(NewLocation(""), path)
|
||||
assert.Nil(t, l)
|
||||
}
|
||||
|
||||
for _, path := range test.expected {
|
||||
assert.True(t, er.HasPath(path))
|
||||
c, err := er.FileContentsByLocation(makeLocation(path))
|
||||
c, err := er.FileContentsByLocation(NewLocation(path))
|
||||
assert.NotNil(t, c)
|
||||
assert.Nil(t, err)
|
||||
m, err := er.FileMetadataByLocation(makeLocation(path))
|
||||
m, err := er.FileMetadataByLocation(NewLocation(path))
|
||||
assert.NotEmpty(t, m.LinkDestination)
|
||||
assert.Nil(t, err)
|
||||
l := er.RelativeFileByPath(makeLocation(""), path)
|
||||
l := er.RelativeFileByPath(NewLocation(""), path)
|
||||
assert.NotNil(t, l)
|
||||
}
|
||||
})
|
||||
@ -119,17 +117,6 @@ func difference(a, b []string) []string {
|
||||
return diff
|
||||
}
|
||||
|
||||
func makeLocation(path string) Location {
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: path,
|
||||
FileSystemID: "",
|
||||
},
|
||||
VirtualPath: "",
|
||||
ref: file.Reference{},
|
||||
}
|
||||
}
|
||||
|
||||
func locationPaths(locations []Location) []string {
|
||||
paths := []string{}
|
||||
for _, l := range locations {
|
||||
@ -145,7 +132,7 @@ type mockResolver struct {
|
||||
func (r *mockResolver) getLocations() ([]Location, error) {
|
||||
out := []Location{}
|
||||
for _, path := range r.locations {
|
||||
out = append(out, makeLocation(path))
|
||||
out = append(out, NewLocation(path))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@ -189,11 +176,8 @@ func (r *mockResolver) FilesByBasenameGlob(_ ...string) ([]Location, error) {
|
||||
}
|
||||
|
||||
func (r *mockResolver) RelativeFileByPath(_ Location, path string) *Location {
|
||||
return &Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: path,
|
||||
},
|
||||
}
|
||||
l := NewLocation(path)
|
||||
return &l
|
||||
}
|
||||
|
||||
func (r *mockResolver) AllLocations() <-chan Location {
|
||||
|
||||
@ -398,66 +398,17 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/group",
|
||||
},
|
||||
VirtualPath: "/etc/group",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/passwd",
|
||||
},
|
||||
VirtualPath: "/etc/passwd",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/shadow",
|
||||
},
|
||||
VirtualPath: "/etc/shadow",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
NewVirtualLocation("/etc/group", "/etc/group"),
|
||||
NewVirtualLocation("/etc/passwd", "/etc/passwd"),
|
||||
NewVirtualLocation("/etc/shadow", "/etc/shadow"),
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
// note: we're de-duping the redundant access to file-3.txt
|
||||
// ... (there would usually be two copies)
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // copy 1
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // copy 2
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -469,32 +420,10 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/link-within",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/link-1"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"), // copy 2
|
||||
NewVirtualLocation("/file-3.txt", "/link-within"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -506,20 +435,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -531,45 +448,12 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
// when we copy into the link path, the same file-4.txt is copied
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // when we copy into the link path, the same file-4.txt is copied
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -581,45 +465,12 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
// when we copy into the link path, the same file-4.txt is copied
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // when we copy into the link path, the same file-4.txt is copied
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -632,18 +483,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -656,18 +497,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-indirect",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-indirect",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -382,48 +382,13 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/group",
|
||||
},
|
||||
VirtualPath: "/etc/group",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/passwd",
|
||||
},
|
||||
VirtualPath: "/etc/passwd",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/shadow",
|
||||
},
|
||||
VirtualPath: "/etc/shadow",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/etc/group", "/etc/group"),
|
||||
NewVirtualLocation("/etc/passwd", "/etc/passwd"),
|
||||
NewVirtualLocation("/etc/shadow", "/etc/shadow"),
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -435,32 +400,14 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/link-1"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
|
||||
// though this is a link, and it matches to the file, the resolver de-duplicates files
|
||||
// by the real path, so it is not included in the results
|
||||
//{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "/file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "/link-indirect",
|
||||
//},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/link-within",
|
||||
},
|
||||
//NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
|
||||
NewVirtualLocation("/file-3.txt", "/link-within"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -473,12 +420,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// this has two copies in the base image, which overwrites the same location
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -490,30 +432,10 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -524,29 +446,38 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
|
||||
{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
ref: file.Reference{RealPath: "/file-1.txt"},
|
||||
},
|
||||
},
|
||||
{
|
||||
LocationData: LocationData{
|
||||
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
ref: file.Reference{RealPath: "/file-2.txt"},
|
||||
},
|
||||
},
|
||||
// we already have this real file path via another link, so only one is returned
|
||||
//{
|
||||
// LocationData: LocationData{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "/file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "/link-indirect",
|
||||
// ref: file.Reference{RealPath: "/file-2.txt"},
|
||||
// },
|
||||
//},
|
||||
{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
@ -555,6 +486,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "by extension",
|
||||
runner: func(resolver FileResolver) []Location {
|
||||
@ -564,30 +496,10 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -600,12 +512,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -618,12 +525,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-indirect",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -646,7 +548,8 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
|
||||
func compareLocations(t *testing.T, expected, actual []Location) {
|
||||
t.Helper()
|
||||
ignoreUnexported := cmpopts.IgnoreFields(Location{}, "ref")
|
||||
ignoreUnexported := cmpopts.IgnoreFields(LocationData{}, "ref")
|
||||
ignoreMetadata := cmpopts.IgnoreFields(LocationMetadata{}, "Annotations")
|
||||
ignoreFS := cmpopts.IgnoreFields(Coordinates{}, "FileSystemID")
|
||||
|
||||
sort.Sort(Locations(expected))
|
||||
@ -655,6 +558,7 @@ func compareLocations(t *testing.T, expected, actual []Location) {
|
||||
if d := cmp.Diff(expected, actual,
|
||||
ignoreUnexported,
|
||||
ignoreFS,
|
||||
ignoreMetadata,
|
||||
); d != "" {
|
||||
|
||||
t.Errorf("unexpected locations (-want +got):\n%s", d)
|
||||
|
||||
@ -3,6 +3,8 @@ package source
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
)
|
||||
@ -10,59 +12,126 @@ import (
|
||||
// Location represents a path relative to a particular filesystem resolved to a specific file.Reference. This struct is used as a key
|
||||
// in content fetching to uniquely identify a file relative to a request (the VirtualPath).
|
||||
type Location struct {
|
||||
LocationData `cyclonedx:""`
|
||||
LocationMetadata `cyclonedx:""`
|
||||
}
|
||||
|
||||
type LocationData struct {
|
||||
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)
|
||||
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
|
||||
VirtualPath string `hash:"ignore" json:"virtualPath,omitempty"` // The path to the file which may or may not have hardlinks / symlinks
|
||||
VirtualPath string `hash:"ignore" json:"-"` // 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.
|
||||
}
|
||||
|
||||
type LocationMetadata struct {
|
||||
Annotations map[string]string `json:"annotations,omitempty"` // Arbitrary key-value pairs that can be used to annotate a location
|
||||
}
|
||||
|
||||
func (m *LocationMetadata) merge(other LocationMetadata) error {
|
||||
var errs error
|
||||
for k, v := range other.Annotations {
|
||||
if otherV, ok := m.Annotations[k]; ok {
|
||||
if v != otherV {
|
||||
err := fmt.Errorf("unable to merge location metadata: conflicting values for key=%q: %q != %q", k, v, otherV)
|
||||
errs = multierror.Append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
m.Annotations[k] = v
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (l Location) WithAnnotation(key, value string) Location {
|
||||
if l.LocationMetadata.Annotations == nil {
|
||||
l.LocationMetadata.Annotations = map[string]string{}
|
||||
}
|
||||
l.LocationMetadata.Annotations[key] = value
|
||||
return l
|
||||
}
|
||||
|
||||
// NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.
|
||||
func NewLocation(realPath string) Location {
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: realPath,
|
||||
},
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewVirtualLocation creates a new location for a path accessed by a virtual path (a path with a symlink or hardlink somewhere in the path)
|
||||
func NewVirtualLocation(realPath, virtualPath string) Location {
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: realPath,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
}
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewLocationFromCoordinates creates a new location for the given Coordinates.
|
||||
func NewLocationFromCoordinates(coordinates Coordinates) Location {
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: coordinates,
|
||||
}
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewVirtualLocationFromCoordinates creates a new location for the given Coordinates via a virtual path.
|
||||
func NewVirtualLocationFromCoordinates(coordinates Coordinates, virtualPath string) Location {
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: coordinates,
|
||||
VirtualPath: virtualPath,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewLocationFromImage creates a new Location representing the given path (extracted from the ref) relative to the given image.
|
||||
func NewLocationFromImage(virtualPath string, ref file.Reference, img *image.Image) Location {
|
||||
layer := img.FileCatalog.Layer(ref)
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: string(ref.RealPath),
|
||||
FileSystemID: layer.Metadata.Digest,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
ref: ref,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewLocationFromDirectory creates a new Location representing the given path (extracted from the ref) relative to the given directory.
|
||||
func NewLocationFromDirectory(responsePath string, ref file.Reference) Location {
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: responsePath,
|
||||
},
|
||||
ref: ref,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,11 +141,16 @@ func NewVirtualLocationFromDirectory(responsePath, virtualResponsePath string, r
|
||||
return NewLocationFromDirectory(responsePath, ref)
|
||||
}
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: responsePath,
|
||||
},
|
||||
VirtualPath: virtualResponsePath,
|
||||
ref: ref,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,10 +4,12 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
type LocationSet struct {
|
||||
set map[Location]struct{}
|
||||
set map[LocationData]LocationMetadata
|
||||
}
|
||||
|
||||
func NewLocationSet(locations ...Location) (s LocationSet) {
|
||||
@ -20,10 +22,18 @@ func NewLocationSet(locations ...Location) (s LocationSet) {
|
||||
|
||||
func (s *LocationSet) Add(locations ...Location) {
|
||||
if s.set == nil {
|
||||
s.set = make(map[Location]struct{})
|
||||
s.set = make(map[LocationData]LocationMetadata)
|
||||
}
|
||||
for _, l := range locations {
|
||||
s.set[l] = struct{}{}
|
||||
if m, ok := s.set[l.LocationData]; ok {
|
||||
err := m.merge(l.LocationMetadata)
|
||||
if err != nil {
|
||||
log.Debugf("partial merge of location metadata: %+v", err)
|
||||
}
|
||||
s.set[l.LocationData] = m
|
||||
} else {
|
||||
s.set[l.LocationData] = l.LocationMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +42,7 @@ func (s LocationSet) Remove(locations ...Location) {
|
||||
return
|
||||
}
|
||||
for _, l := range locations {
|
||||
delete(s.set, l)
|
||||
delete(s.set, l.LocationData)
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +50,7 @@ func (s LocationSet) Contains(l Location) bool {
|
||||
if s.set == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := s.set[l]
|
||||
_, ok := s.set[l.LocationData]
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -50,8 +60,11 @@ func (s LocationSet) ToSlice() []Location {
|
||||
}
|
||||
locations := make([]Location, len(s.set))
|
||||
idx := 0
|
||||
for v := range s.set {
|
||||
locations[idx] = v
|
||||
for dir := range s.set {
|
||||
locations[idx] = Location{
|
||||
LocationData: dir,
|
||||
LocationMetadata: s.set[dir],
|
||||
}
|
||||
idx++
|
||||
}
|
||||
sort.Sort(Locations(locations))
|
||||
|
||||
@ -12,35 +12,43 @@ import (
|
||||
func TestLocationSet(t *testing.T) {
|
||||
|
||||
etcHostsLinkVar := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/var/etc/hosts",
|
||||
},
|
||||
}
|
||||
|
||||
etcHostsLinkHome := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/home/wagoodman/hosts",
|
||||
},
|
||||
}
|
||||
|
||||
binA := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
}
|
||||
|
||||
binB := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@ -87,41 +95,51 @@ func TestLocationSet(t *testing.T) {
|
||||
|
||||
func TestLocationSet_Hash(t *testing.T) {
|
||||
etcAlink := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/var/etc/hosts",
|
||||
},
|
||||
}
|
||||
|
||||
etcA := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
etcB := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
binA := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
}
|
||||
|
||||
binB := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@ -37,9 +37,11 @@ func TestLocation_ID(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
l := Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: test.coordinates,
|
||||
VirtualPath: test.virtualPath,
|
||||
ref: test.ref,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, l.ID(), test.coordinates.ID())
|
||||
})
|
||||
|
||||
@ -18,7 +18,7 @@ var _ FileResolver = (*MockResolver)(nil)
|
||||
// paths, which are typically paths to test fixtures.
|
||||
type MockResolver struct {
|
||||
locations []Location
|
||||
metadata map[Location]FileMetadata
|
||||
metadata map[Coordinates]FileMetadata
|
||||
mimeTypeIndex map[string][]Location
|
||||
extension map[string][]Location
|
||||
basename map[string][]Location
|
||||
@ -41,18 +41,19 @@ func NewMockResolverForPaths(paths ...string) *MockResolver {
|
||||
|
||||
return &MockResolver{
|
||||
locations: locations,
|
||||
metadata: make(map[Location]FileMetadata),
|
||||
metadata: make(map[Coordinates]FileMetadata),
|
||||
extension: extension,
|
||||
basename: basename,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMockResolverForPathsWithMetadata(metadata map[Location]FileMetadata) *MockResolver {
|
||||
func NewMockResolverForPathsWithMetadata(metadata map[Coordinates]FileMetadata) *MockResolver {
|
||||
var locations []Location
|
||||
var mimeTypeIndex = make(map[string][]Location)
|
||||
extension := make(map[string][]Location)
|
||||
basename := make(map[string][]Location)
|
||||
for l, m := range metadata {
|
||||
for c, m := range metadata {
|
||||
l := NewLocationFromCoordinates(c)
|
||||
locations = append(locations, l)
|
||||
mimeTypeIndex[m.MIMEType] = append(mimeTypeIndex[m.MIMEType], l)
|
||||
ext := path.Ext(l.RealPath)
|
||||
@ -89,7 +90,7 @@ func (r MockResolver) String() string {
|
||||
// path does not exist, an error is returned.
|
||||
func (r MockResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
||||
for _, l := range r.locations {
|
||||
if l == location {
|
||||
if l.Coordinates == location.Coordinates {
|
||||
return os.Open(location.RealPath)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -254,3 +255,63 @@ func TestPkgCoverageCatalogerConfiguration(t *testing.T) {
|
||||
c.Catalogers = []string{"rust"}
|
||||
assert.Len(t, cataloger.ImageCatalogers(c), 0)
|
||||
}
|
||||
|
||||
func TestPkgCoverageImage_HasEvidence(t *testing.T) {
|
||||
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil)
|
||||
|
||||
var cases []testCase
|
||||
cases = append(cases, commonTestCases...)
|
||||
cases = append(cases, imageOnlyTestCases...)
|
||||
|
||||
pkgTypesMissingEvidence := strset.New()
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
for a := range sbom.Artifacts.PackageCatalog.Enumerate(c.pkgType) {
|
||||
assert.NotEmpty(t, a.Locations.ToSlice(), "package %q has no locations (type=%q)", a.Name, a.Type)
|
||||
for _, l := range a.Locations.ToSlice() {
|
||||
if _, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists {
|
||||
pkgTypesMissingEvidence.Add(string(a.Type))
|
||||
t.Errorf("missing evidence annotation (pkg=%s type=%s)", a.Name, a.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
if pkgTypesMissingEvidence.Size() > 0 {
|
||||
t.Log("Package types missing evidence annotations (img resolver): ", pkgTypesMissingEvidence.List())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPkgCoverageDirectory_HasEvidence(t *testing.T) {
|
||||
sbom, _ := catalogDirectory(t, "test-fixtures/image-pkg-coverage")
|
||||
|
||||
var cases []testCase
|
||||
cases = append(cases, commonTestCases...)
|
||||
cases = append(cases, imageOnlyTestCases...)
|
||||
|
||||
pkgTypesMissingEvidence := strset.New()
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
for a := range sbom.Artifacts.PackageCatalog.Enumerate(c.pkgType) {
|
||||
assert.NotEmpty(t, a.Locations.ToSlice(), "package %q has no locations (type=%q)", a.Name, a.Type)
|
||||
for _, l := range a.Locations.ToSlice() {
|
||||
if _, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists {
|
||||
pkgTypesMissingEvidence.Add(string(a.Type))
|
||||
t.Errorf("missing evidence annotation (pkg=%s type=%s)", a.Name, a.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
if pkgTypesMissingEvidence.Size() > 0 {
|
||||
t.Log("Package types missing evidence annotations (dir resolver): ", pkgTypesMissingEvidence.List())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user