Skip to content

Commit

Permalink
add package dependency quality notes
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <[email protected]>
  • Loading branch information
wagoodman committed Nov 15, 2024
1 parent 70ef3f2 commit 5b7ec60
Show file tree
Hide file tree
Showing 105 changed files with 2,946 additions and 2,273 deletions.
9 changes: 9 additions & 0 deletions internal/task/package_task_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string

pkgs, relationships = applyCompliance(cfg.ComplianceConfig, pkgs, relationships)

finalizeDependencyCompleteness(pkgs)

sbom.AddPackages(pkgs...)
sbom.AddRelationships(relationships...)
t.Add(int64(len(pkgs)))
Expand All @@ -124,6 +126,13 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
return NewTask(c.Name(), fn, tags...)
}

func finalizeDependencyCompleteness(pkgs []pkg.Package) {
// ensure that all packages have a non-empty dependency list
for i := range pkgs {
pkgs[i].Dependencies = pkg.ParseDependencyCompleteness(string(pkgs[i].Dependencies))
}
}

func finalizePkgCatalogerResults(cfg CatalogingFactoryConfig, resolver file.PathResolver, catalogerName string, pkgs []pkg.Package, relationships []artifact.Relationship) ([]pkg.Package, []artifact.Relationship) {
for i, p := range pkgs {
if p.FoundBy == "" {
Expand Down
12 changes: 8 additions & 4 deletions syft/pkg/cataloger/alpine/cataloger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ func TestApkDBCataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("GPL-3.0-or-later", dbLocation),
),
Locations: file.NewLocationSet(dbLocation),
Locations: file.NewLocationSet(dbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "bash",
OriginPackage: "bash",
Expand Down Expand Up @@ -52,7 +53,8 @@ func TestApkDBCataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("GPL-2.0-only", dbLocation),
),
Locations: file.NewLocationSet(dbLocation),
Locations: file.NewLocationSet(dbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "busybox-binsh",
OriginPackage: "busybox",
Expand Down Expand Up @@ -81,7 +83,8 @@ func TestApkDBCataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("MIT", dbLocation),
),
Locations: file.NewLocationSet(dbLocation),
Locations: file.NewLocationSet(dbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "musl",
OriginPackage: "musl",
Expand All @@ -108,7 +111,8 @@ func TestApkDBCataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("GPL-2.0-or-later", dbLocation),
),
Locations: file.NewLocationSet(dbLocation),
Locations: file.NewLocationSet(dbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "readline",
OriginPackage: "readline",
Expand Down
15 changes: 8 additions & 7 deletions syft/pkg/cataloger/alpine/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ func newPackage(d parsedData, release *linux.Release, dbLocation file.Location)
}

p := pkg.Package{
Name: d.Package,
Version: d.Version,
Locations: file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation, licenseStrings...)...),
PURL: packageURL(d.ApkDBEntry, release),
Type: pkg.ApkPkg,
Metadata: d.ApkDBEntry,
Name: d.Package,
Version: d.Version,
Locations: file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation, licenseStrings...)...),
PURL: packageURL(d.ApkDBEntry, release),
Type: pkg.ApkPkg,
Dependencies: pkg.CompleteDependencies,
Metadata: d.ApkDBEntry,
}

p.SetID()
Expand Down
10 changes: 6 additions & 4 deletions syft/pkg/cataloger/alpine/parse_apk_db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ func TestSinglePackageDetails(t *testing.T) {
pkg.NewLicense("BSD"),
pkg.NewLicense("GPL2+"),
),
Type: pkg.ApkPkg,
Type: pkg.ApkPkg,
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "musl-utils",
OriginPackage: "musl",
Expand Down Expand Up @@ -177,7 +178,8 @@ func TestSinglePackageDetails(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicense("GPL-2.0-only"),
),
Type: pkg.ApkPkg,
Type: pkg.ApkPkg,
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "alpine-baselayout-data",
OriginPackage: "alpine-baselayout",
Expand Down Expand Up @@ -221,8 +223,8 @@ func TestSinglePackageDetails(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicense("GPL-2.0-only"),
),
Type: pkg.ApkPkg,
PURL: "",
Type: pkg.ApkPkg,
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.ApkDBEntry{
Package: "alpine-baselayout",
OriginPackage: "alpine-baselayout",
Expand Down
19 changes: 12 additions & 7 deletions syft/pkg/cataloger/arch/cataloger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func TestAlpmCataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("MIT", treeSitterDbLocation),
),
Locations: file.NewLocationSet(treeSitterDbLocation),
Locations: file.NewLocationSet(treeSitterDbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.AlpmDBEntry{
BasePackage: "tree-sitter",
Package: "tree-sitter",
Expand All @@ -60,7 +61,8 @@ func TestAlpmCataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("GPL3", emacsDbLocation),
),
Locations: file.NewLocationSet(emacsDbLocation),
Locations: file.NewLocationSet(emacsDbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.AlpmDBEntry{
BasePackage: "emacs",
Package: "emacs",
Expand All @@ -86,6 +88,7 @@ func TestAlpmCataloger(t *testing.T) {
fuzzyDbLocation,
file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/files"),
),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.AlpmDBEntry{
Package: "fuzzy",
Version: "1.2-3",
Expand All @@ -103,11 +106,12 @@ func TestAlpmCataloger(t *testing.T) {
}

madeupPkg := pkg.Package{
Name: "madeup",
Version: "20.30-4",
Type: pkg.AlpmPkg,
FoundBy: "alpm-db-cataloger",
Locations: file.NewLocationSet(madeupDbLocation),
Name: "madeup",
Version: "20.30-4",
Type: pkg.AlpmPkg,
FoundBy: "alpm-db-cataloger",
Locations: file.NewLocationSet(madeupDbLocation),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.AlpmDBEntry{
Package: "madeup",
Version: "20.30-4",
Expand All @@ -131,6 +135,7 @@ func TestAlpmCataloger(t *testing.T) {
file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/files"),
file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/mtree"),
),
Dependencies: pkg.CompleteDependencies,
Metadata: pkg.AlpmDBEntry{
BasePackage: "gmp",
Package: "gmp",
Expand Down
15 changes: 8 additions & 7 deletions syft/pkg/cataloger/arch/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location,
locs.Add(otherLocations...)

p := pkg.Package{
Name: m.Package,
Version: m.Version,
Locations: locs,
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...),
Type: pkg.AlpmPkg,
PURL: packageURL(m, release),
Metadata: m.AlpmDBEntry,
Name: m.Package,
Version: m.Version,
Locations: locs,
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...),
Type: pkg.AlpmPkg,
PURL: packageURL(m, release),
Dependencies: pkg.CompleteDependencies,
Metadata: m.AlpmDBEntry,
}
p.SetID()

Expand Down
3 changes: 3 additions & 0 deletions syft/pkg/cataloger/binary/classifier_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func newClassifierPackage(classifier Classifier, location file.Location, matchMe
Type: pkg.BinaryPkg,
CPEs: cpes,
FoundBy: catalogerName,
// classifiers are limited to identifying package identities, but have no information about dependencies.
// Furthermore, we have no understanding about the mechanisms for dependency resolution for these packages.
Dependencies: pkg.UnknownDependencyCompleteness,
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
{
Expand Down
5 changes: 4 additions & 1 deletion syft/pkg/cataloger/binary/elf_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet) p
PURL: packageURL(metadata),
Type: pkgType(metadata.Type),
Locations: locations,
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
// though we can look for shared libs, we cannot see static dependencies nor dynamic dependencies using dlopen.
// this means that, even in cases where the dep info is actually complete, we can't programmatically determine that.
Dependencies: pkg.IncompleteDependencies,
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
}

p.SetID()
Expand Down
16 changes: 10 additions & 6 deletions syft/pkg/cataloger/binary/elf_package_cataloger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
pkg.License{Value: "MIT", SPDXExpression: "MIT", Type: "declared"},
),

Type: pkg.BinaryPkg,
Type: pkg.BinaryPkg,
Dependencies: pkg.IncompleteDependencies,
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
Type: "testfixture",
Vendor: "syft",
Expand All @@ -56,7 +57,8 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
Licenses: pkg.NewLicenseSet(
pkg.License{Value: "MIT", SPDXExpression: "MIT", Type: "declared"},
),
Type: pkg.BinaryPkg,
Type: pkg.BinaryPkg,
Dependencies: pkg.IncompleteDependencies,
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
Type: "testfixture",
Vendor: "syft",
Expand All @@ -80,8 +82,9 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
file.NewLocation("/sha256sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
file.NewLocation("/sha1sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
),
Licenses: pkg.NewLicenseSet(),
Type: pkg.RpmPkg,
Licenses: pkg.NewLicenseSet(),
Type: pkg.RpmPkg,
Dependencies: pkg.IncompleteDependencies,
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
Type: "rpm",
Architecture: "x86_64",
Expand All @@ -102,8 +105,9 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
file.NewLocation("/sha256sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
file.NewLocation("/sha1sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
),
Licenses: pkg.NewLicenseSet(),
Type: pkg.RpmPkg,
Licenses: pkg.NewLicenseSet(),
Type: pkg.RpmPkg,
Dependencies: pkg.IncompleteDependencies,
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
Type: "rpm",
Architecture: "arm",
Expand Down
9 changes: 5 additions & 4 deletions syft/pkg/cataloger/binary/elf_package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,11 @@ func Test_newELFPackage(t *testing.T) {
},

expected: pkg.Package{
Name: "syfttestfixture",
Version: "0.01",
Type: "binary",
PURL: "pkg:generic/syftsys/[email protected]",
Name: "syfttestfixture",
Version: "0.01",
Type: "binary",
PURL: "pkg:generic/syftsys/[email protected]",
Dependencies: pkg.IncompleteDependencies,
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
Type: "binary",
System: "syftsys",
Expand Down
36 changes: 22 additions & 14 deletions syft/pkg/cataloger/cpp/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,35 +67,43 @@ func splitConanRef(ref string) *conanRef {
}

func newConanfilePackage(m pkg.ConanfileEntry, locations ...file.Location) *pkg.Package {
return newConanPackage(m.Ref, m, locations...)
// though a conanfile is a listing of direct dependencies, we are not capturing these today, so are forced to answer incomplete
return newConanPackage(m.Ref, m, pkg.IncompleteDependencies, locations...)
}

func newConanlockPackage(m pkg.ConanV1LockEntry, locations ...file.Location) *pkg.Package {
return newConanPackage(m.Ref, m, locations...)
func newConanlockV1Package(m pkg.ConanV1LockEntry, locations ...file.Location) *pkg.Package {
// conan.lock is primarily used to lock the dependency graph at specific versions to ensure consistent builds,
// and the dependency tree can be inferred from the contents (distinguishing between direct and transitive dependencies)
return newConanPackage(m.Ref, m, pkg.CompleteDependencies, locations...)
}

func newConanReferencePackage(m pkg.ConanV2LockEntry, locations ...file.Location) *pkg.Package {
return newConanPackage(m.Ref, m, locations...)
func newConanLockv2Package(m pkg.ConanV2LockEntry, locations ...file.Location) *pkg.Package {
// conan.lock is primarily used to lock the dependency graph at specific versions to ensure consistent builds,
// and the dependency tree can be inferred from the contents (distinguishing between direct and transitive dependencies)
return newConanPackage(m.Ref, m, pkg.CompleteDependencies, locations...)
}

func newConaninfoPackage(m pkg.ConaninfoEntry, locations ...file.Location) *pkg.Package {
return newConanPackage(m.Ref, m, locations...)
// conaninfo.txt is generated during the build and contains detailed information about the entire dependency tree,
// and today the codebase does infer direct dependencies from this source
return newConanPackage(m.Ref, m, pkg.CompleteDependencies, locations...)
}

func newConanPackage(refStr string, metadata any, locations ...file.Location) *pkg.Package {
func newConanPackage(refStr string, metadata any, dep pkg.DependencyCompleteness, locations ...file.Location) *pkg.Package {
ref := splitConanRef(refStr)
if ref == nil {
return nil
}

p := pkg.Package{
Name: ref.Name,
Version: ref.Version,
Locations: file.NewLocationSet(locations...),
PURL: packageURL(ref),
Language: pkg.CPP,
Type: pkg.ConanPkg,
Metadata: metadata,
Name: ref.Name,
Version: ref.Version,
Locations: file.NewLocationSet(locations...),
PURL: packageURL(ref),
Language: pkg.CPP,
Type: pkg.ConanPkg,
Dependencies: dep,
Metadata: metadata,
}

p.SetID()
Expand Down
4 changes: 3 additions & 1 deletion syft/pkg/cataloger/cpp/parse_conanfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ func parseConanfile(_ context.Context, _ file.Resolver, _ *generic.Environment,
r := bufio.NewReader(reader)
inRequirements := false
var pkgs []pkg.Package
loop:
for {
line, err := r.ReadString('\n')
switch {
case errors.Is(err, io.EOF):
return pkgs, nil, unknown.IfEmptyf(pkgs, "unable to determine packages")
break loop
case err != nil:
return nil, nil, fmt.Errorf("failed to parse conanfile.txt file: %w", err)
}
Expand Down Expand Up @@ -56,4 +57,5 @@ func parseConanfile(_ context.Context, _ file.Resolver, _ *generic.Environment,

pkgs = append(pkgs, *p)
}
return pkgs, nil, unknown.IfEmptyf(pkgs, "unable to determine packages")
}
Loading

0 comments on commit 5b7ec60

Please sign in to comment.