Skip to content

Commit

Permalink
Track supporting DPKG evidence (#3228)
Browse files Browse the repository at this point in the history
* add dpkg evidence support

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* use path over filepath

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
  • Loading branch information
wagoodman authored Oct 4, 2024
1 parent 770fdc5 commit 13c6876
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 29 deletions.
11 changes: 7 additions & 4 deletions internal/relationship/evident_by.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ func EvidentBy(catalog *pkg.Collection) []artifact.Relationship {
var edges []artifact.Relationship
for _, p := range catalog.Sorted() {
for _, l := range p.Locations.ToSlice() {
if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists || v != pkg.PrimaryEvidenceAnnotation {
// skip non-primary evidence from being expressed as a relationship.
// note: this may be configurable in the future.
continue
kind := pkg.SupportingEvidenceAnnotation
if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; exists {
kind = v
}

edges = append(edges, artifact.Relationship{
From: p,
To: l.Coordinates,
Type: artifact.EvidentByRelationship,
Data: map[string]string{
"kind": kind,
},
})
}
}
Expand Down
16 changes: 12 additions & 4 deletions internal/task/file_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
}

if selection == file.FilesOwnedByPackageSelection {
var coordinates []file.Coordinates
var coordinates file.CoordinateSet

accessor.ReadFromSBOM(func(sbom *sbom.SBOM) {
// get any file coordinates that are owned by a package
for _, r := range sbom.Relationships {
if r.Type != artifact.ContainsRelationship {
continue
Expand All @@ -145,16 +146,23 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
continue
}
if c, ok := r.To.(file.Coordinates); ok {
coordinates = append(coordinates, c)
coordinates.Add(c)
}
}

// get any file coordinates referenced by a package directly
for p := range sbom.Artifacts.Packages.Enumerate() {
coordinates.Add(p.Locations.CoordinateSet().ToSlice()...)
}
})

if len(coordinates) == 0 {
coords := coordinates.ToSlice()

if len(coords) == 0 {
return nil, false
}

return coordinates, true
return coords, true
}

return nil, false
Expand Down
28 changes: 15 additions & 13 deletions syft/pkg/cataloger/debian/cataloger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ func TestDpkgCataloger(t *testing.T) {
Version: "1.1.8-3.6",
FoundBy: "dpkg-db-cataloger",
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("GPL-1", file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright")),
pkg.NewLicenseFromLocations("GPL-2", file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright")),
pkg.NewLicenseFromLocations("LGPL-2.1", file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright")),
pkg.NewLicenseFromLocations("GPL-1", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
pkg.NewLicenseFromLocations("GPL-2", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
pkg.NewLicenseFromLocations("LGPL-2.1", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
),
Locations: file.NewLocationSet(
file.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"),
file.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.md5sums", "/var/lib/dpkg/info/libpam-runtime.md5sums"),
file.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.conffiles", "/var/lib/dpkg/info/libpam-runtime.conffiles"),
file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright"),
file.NewLocation("/var/lib/dpkg/status"),
file.NewLocation("/var/lib/dpkg/info/libpam-runtime.preinst"),
file.NewLocation("/var/lib/dpkg/info/libpam-runtime.md5sums"),
file.NewLocation("/var/lib/dpkg/info/libpam-runtime.conffiles"),
file.NewLocation("/usr/share/doc/libpam-runtime/copyright"),
),
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Expand Down Expand Up @@ -98,14 +99,15 @@ func TestDpkgCataloger(t *testing.T) {
Version: "3.34.1-3",
FoundBy: "dpkg-db-cataloger",
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("public-domain", file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright")),
pkg.NewLicenseFromLocations("GPL-2+", file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright")),
pkg.NewLicenseFromLocations("GPL-2", file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright")),
pkg.NewLicenseFromLocations("public-domain", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
pkg.NewLicenseFromLocations("GPL-2+", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
pkg.NewLicenseFromLocations("GPL-2", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
),
Locations: file.NewLocationSet(
file.NewVirtualLocation("/var/lib/dpkg/status.d/libsqlite3-0", "/var/lib/dpkg/status.d/libsqlite3-0"),
file.NewVirtualLocation("/var/lib/dpkg/status.d/libsqlite3-0.md5sums", "/var/lib/dpkg/status.d/libsqlite3-0.md5sums"),
file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright"),
file.NewLocation("/var/lib/dpkg/status.d/libsqlite3-0"),
file.NewLocation("/var/lib/dpkg/status.d/libsqlite3-0.md5sums"),
file.NewLocation("/var/lib/dpkg/status.d/libsqlite3-0.preinst"),
file.NewLocation("/usr/share/doc/libsqlite3-0/copyright"),
),
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Expand Down
18 changes: 11 additions & 7 deletions syft/pkg/cataloger/debian/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ const (
docsPath = "/usr/share/doc"
)

func newDpkgPackage(d pkg.DpkgDBEntry, dbLocation file.Location, resolver file.Resolver, release *linux.Release) pkg.Package {
func newDpkgPackage(d pkg.DpkgDBEntry, dbLocation file.Location, resolver file.Resolver, release *linux.Release, evidence ...file.Location) pkg.Package {
// TODO: separate pr to license refactor, but explore extracting dpkg-specific license parsing into a separate function
licenses := make([]pkg.License, 0)

locations := file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
locations.Add(evidence...)

p := pkg.Package{
Name: d.Package,
Version: d.Version,
Licenses: pkg.NewLicenseSet(licenses...),
Locations: file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Locations: locations,
PURL: packageURL(d, release),
Type: pkg.DebPkg,
Metadata: d,
Expand Down Expand Up @@ -88,7 +92,7 @@ func packageURL(m pkg.DpkgDBEntry, distro *linux.Release) string {
func addLicenses(resolver file.Resolver, dbLocation file.Location, p *pkg.Package) {
metadata, ok := p.Metadata.(pkg.DpkgDBEntry)
if !ok {
log.WithFields("package", p).Warn("unable to extract DPKG metadata to add licenses")
log.WithFields("package", p).Trace("unable to extract DPKG metadata to add licenses")
return
}

Expand All @@ -110,7 +114,7 @@ func addLicenses(resolver file.Resolver, dbLocation file.Location, p *pkg.Packag
func mergeFileListing(resolver file.Resolver, dbLocation file.Location, p *pkg.Package) {
metadata, ok := p.Metadata.(pkg.DpkgDBEntry)
if !ok {
log.WithFields("package", p).Warn("unable to extract DPKG metadata to file listing")
log.WithFields("package", p).Trace("unable to extract DPKG metadata to file listing")
return
}

Expand Down Expand Up @@ -204,7 +208,7 @@ func fetchMd5Contents(resolver file.Resolver, dbLocation file.Location, m pkg.Dp
// this is unexpected, but not a show-stopper
md5Reader, err = resolver.FileContentsByLocation(*location)
if err != nil {
log.Warnf("failed to fetch deb md5 contents (package=%s): %+v", m.Package, err)
log.Tracef("failed to fetch deb md5 contents (package=%s): %+v", m.Package, err)
}

l := location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)
Expand Down Expand Up @@ -239,7 +243,7 @@ func fetchConffileContents(resolver file.Resolver, dbLocation file.Location, m p
// this is unexpected, but not a show-stopper
reader, err = resolver.FileContentsByLocation(*location)
if err != nil {
log.Warnf("failed to fetch deb conffiles contents (package=%s): %+v", m.Package, err)
log.Tracef("failed to fetch deb conffiles contents (package=%s): %+v", m.Package, err)
}

l := location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)
Expand All @@ -263,7 +267,7 @@ func fetchCopyrightContents(resolver file.Resolver, dbLocation file.Location, m

reader, err := resolver.FileContentsByLocation(*location)
if err != nil {
log.Warnf("failed to fetch deb copyright contents (package=%s): %s", m.Package, err)
log.Tracef("failed to fetch deb copyright contents (package=%s): %s", m.Package, err)
}
defer internal.CloseAndLogError(reader, location.RealPath)

Expand Down
32 changes: 31 additions & 1 deletion syft/pkg/cataloger/debian/parse_dpkg_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"path"
"regexp"
"strings"

Expand Down Expand Up @@ -34,12 +35,41 @@ func parseDpkgDB(_ context.Context, resolver file.Resolver, env *generic.Environ

var pkgs []pkg.Package
for _, m := range metadata {
pkgs = append(pkgs, newDpkgPackage(m, reader.Location, resolver, env.LinuxRelease))
p := newDpkgPackage(m, reader.Location, resolver, env.LinuxRelease, findDpkgInfoFiles(m.Package, resolver, reader.Location)...)
pkgs = append(pkgs, p)
}

return pkgs, nil, nil
}

func findDpkgInfoFiles(name string, resolver file.Resolver, dbLocation file.Location) []file.Location {
if resolver == nil {
return nil
}
if strings.TrimSpace(name) == "" {
return nil
}

// for typical debian-base distributions, the installed package info is at /var/lib/dpkg/status
// and the md5sum information is under /var/lib/dpkg/info/; however, for distroless the installed
// package info is across multiple files under /var/lib/dpkg/status.d/ and the md5sums are contained in
// the same directory
searchPath := path.Dir(dbLocation.RealPath)

if !strings.HasSuffix(searchPath, "status.d") {
searchPath = path.Join(searchPath, "info")
}

// look for /var/lib/dpkg/info/NAME.*
locations, err := resolver.FilesByGlob(path.Join(searchPath, name+".*"))
if err != nil {
log.WithFields("error", err, "pkg", name).Trace("failed to fetch related dpkg info files")
return nil
}

return locations
}

// parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed.
func parseDpkgStatus(reader io.Reader) ([]pkg.DpkgDBEntry, error) {
buffedReader := bufio.NewReader(reader)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# some shell script...
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# some shell script...

0 comments on commit 13c6876

Please sign in to comment.