add dpkg evidence support

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-09-12 17:36:53 -04:00
parent 1b863268df
commit cd68818281
8 changed files with 81 additions and 30 deletions

View file

@ -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,
},
})
}
}

View file

@ -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
@ -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

View file

@ -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{
@ -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{

View file

@ -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,
@ -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
}
@ -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
}
@ -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)
@ -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)
@ -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)

View file

@ -6,6 +6,8 @@ import (
"errors"
"fmt"
"io"
"path"
"path/filepath"
"regexp"
"strings"
@ -34,12 +36,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 := filepath.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)

View file

@ -0,0 +1 @@
# some shell script...

View file

@ -3,11 +3,12 @@ package ocaml
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/stretchr/testify/assert"
)
func TestParseOpamPackage(t *testing.T) {