From 8219f8d55b4e2553fcf180014eb436f446338df9 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Wed, 28 Jun 2023 13:28:20 +0100 Subject: [PATCH] fix: discover deb file relationships in distroless images (#1901) Signed-off-by: Weston Steimel --- syft/pkg/cataloger/deb/cataloger_test.go | 194 ++++++++++++------ syft/pkg/cataloger/deb/package.go | 15 +- .../image-distroless-deb/Dockerfile | 2 + .../usr/share/doc/libsqlite3-0/copyright | 30 +++ .../var/lib/dpkg/status.d/libsqlite3-0 | 16 ++ .../lib/dpkg/status.d/libsqlite3-0.md5sums | 6 + 6 files changed, 198 insertions(+), 65 deletions(-) create mode 100644 syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/Dockerfile create mode 100644 syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/usr/share/doc/libsqlite3-0/copyright create mode 100644 syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0 create mode 100644 syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0.md5sums diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 64a3c5f8768..34871889618 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -9,81 +9,151 @@ import ( ) func TestDpkgCataloger(t *testing.T) { - licenseLocation := file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright") - expected := []pkg.Package{ + tests := []struct { + name string + expected []pkg.Package + }{ { - Name: "libpam-runtime", - Version: "1.1.8-3.6", - FoundBy: "dpkgdb-cataloger", - Licenses: pkg.NewLicenseSet( - pkg.NewLicenseFromLocations("GPL-1", licenseLocation), - pkg.NewLicenseFromLocations("GPL-2", licenseLocation), - pkg.NewLicenseFromLocations("LGPL-2.1", licenseLocation), - ), - 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"), - ), - Type: pkg.DebPkg, - MetadataType: pkg.DpkgMetadataType, - Metadata: pkg.DpkgMetadata{ - Package: "libpam-runtime", - Source: "pam", - Version: "1.1.8-3.6", - Architecture: "all", - Maintainer: "Steve Langasek ", - InstalledSize: 1016, - Description: `Runtime support for the PAM library + name: "image-dpkg", + expected: []pkg.Package{ + { + Name: "libpam-runtime", + Version: "1.1.8-3.6", + FoundBy: "dpkgdb-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")), + ), + 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"), + ), + Type: pkg.DebPkg, + MetadataType: pkg.DpkgMetadataType, + Metadata: pkg.DpkgMetadata{ + Package: "libpam-runtime", + Source: "pam", + Version: "1.1.8-3.6", + Architecture: "all", + Maintainer: "Steve Langasek ", + InstalledSize: 1016, + Description: `Runtime support for the PAM library Contains configuration files and directories required for authentication to work on Debian systems. This package is required on almost all installations.`, - Files: []pkg.DpkgFileRecord{ - { - Path: "/etc/pam.conf", - Digest: &file.Digest{ - Algorithm: "md5", - Value: "87fc76f18e98ee7d3848f6b81b3391e5", + Files: []pkg.DpkgFileRecord{ + { + Path: "/etc/pam.conf", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "87fc76f18e98ee7d3848f6b81b3391e5", + }, + IsConfigFile: true, + }, + { + Path: "/etc/pam.d/other", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "31aa7f2181889ffb00b87df4126d1701", + }, + IsConfigFile: true, + }, + {Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", Digest: &file.Digest{ + Algorithm: "md5", + Value: "55f905631797551d4d936a34c7e73474", + }}, + {Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", Digest: &file.Digest{ + Algorithm: "md5", + Value: "cede84bda30d2380217f97753c8ccf3a", + }}, + {Path: "/usr/share/doc/zlib1g/changelog.gz", Digest: &file.Digest{ + Algorithm: "md5", + Value: "f3c9dafa6da7992c47328b4464f6d122", + }}, + {Path: "/usr/share/doc/zlib1g/copyright", Digest: &file.Digest{ + Algorithm: "md5", + Value: "a4fae96070439a5209a62ae5b8017ab2", + }}, }, - IsConfigFile: true, }, - { - Path: "/etc/pam.d/other", - Digest: &file.Digest{ - Algorithm: "md5", - Value: "31aa7f2181889ffb00b87df4126d1701", + }, + }, + }, + { + name: "image-distroless-deb", + expected: []pkg.Package{ + { + Name: "libsqlite3-0", + Version: "3.34.1-3", + FoundBy: "dpkgdb-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")), + ), + 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"), + ), + Type: pkg.DebPkg, + MetadataType: pkg.DpkgMetadataType, + Metadata: pkg.DpkgMetadata{ + Package: "libsqlite3-0", + Source: "sqlite3", + Version: "3.34.1-3", + Architecture: "arm64", + Maintainer: "Laszlo Boszormenyi (GCS) ", + InstalledSize: 1490, + Description: `SQLite 3 shared library + SQLite is a C library that implements an SQL database engine. + Programs that link with the SQLite library can have SQL database + access without running a separate RDBMS process.`, + Files: []pkg.DpkgFileRecord{ + {Path: "/usr/lib/aarch64-linux-gnu/libsqlite3.so.0.8.6", Digest: &file.Digest{ + Algorithm: "md5", + Value: "e11d70c96979a1328ae4e7e50542782b", + }}, + {Path: "/usr/share/doc/libsqlite3-0/README.Debian", Digest: &file.Digest{ + Algorithm: "md5", + Value: "9d8facc2fa9d2df52f1c7cb4e5fa4741", + }}, + {Path: "/usr/share/doc/libsqlite3-0/changelog.Debian.gz", Digest: &file.Digest{ + Algorithm: "md5", + Value: "a58942e742f5056be0595e6ba69a323f", + }}, + {Path: "/usr/share/doc/libsqlite3-0/changelog.gz", Digest: &file.Digest{ + Algorithm: "md5", + Value: "52317be84c3ca44b7888c6921131e37d", + }}, + {Path: "/usr/share/doc/libsqlite3-0/changelog.html.gz", Digest: &file.Digest{ + Algorithm: "md5", + Value: "a856310354e6c8768e85b39ae838dd0a", + }}, + {Path: "/usr/share/doc/libsqlite3-0/copyright", Digest: &file.Digest{ + Algorithm: "md5", + Value: "be64db3e095486e5e105652c51199358", + }}, }, - IsConfigFile: true, }, - {Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", Digest: &file.Digest{ - Algorithm: "md5", - Value: "55f905631797551d4d936a34c7e73474", - }}, - {Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", Digest: &file.Digest{ - Algorithm: "md5", - Value: "cede84bda30d2380217f97753c8ccf3a", - }}, - {Path: "/usr/share/doc/zlib1g/changelog.gz", Digest: &file.Digest{ - Algorithm: "md5", - Value: "f3c9dafa6da7992c47328b4464f6d122", - }}, - {Path: "/usr/share/doc/zlib1g/copyright", Digest: &file.Digest{ - Algorithm: "md5", - Value: "a4fae96070439a5209a62ae5b8017ab2", - }}, }, }, }, } - c := NewDpkgdbCataloger() - - pkgtest.NewCatalogTester(). - WithImageResolver(t, "image-dpkg"). - IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change - Expects(expected, nil). - TestCataloger(t, c) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := NewDpkgdbCataloger() + pkgtest.NewCatalogTester(). + WithImageResolver(t, tt.name). + IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change + Expects(tt.expected, nil). + TestCataloger(t, c) + }) + } } func TestCataloger_Globs(t *testing.T) { diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index b37d8f46b7c..ebd72a77af2 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -6,6 +6,7 @@ import ( "path" "path/filepath" "sort" + "strings" "github.com/anchore/packageurl-go" "github.com/anchore/syft/internal" @@ -176,16 +177,24 @@ func fetchMd5Contents(resolver file.Resolver, dbLocation file.Location, m pkg.Dp return nil, nil } - parentPath := filepath.Dir(dbLocation.RealPath) + // 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:ARCH.md5sums name := md5Key(m) - location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+md5sumsExt)) + location := resolver.RelativeFileByPath(dbLocation, path.Join(searchPath, name+md5sumsExt)) if location == nil { // the most specific key did not work, fallback to just the name // look for /var/lib/dpkg/info/NAME.md5sums - location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", m.Package+md5sumsExt)) + location = resolver.RelativeFileByPath(dbLocation, path.Join(searchPath, m.Package+md5sumsExt)) } if location == nil { diff --git a/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/Dockerfile b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/Dockerfile new file mode 100644 index 00000000000..770e60b5604 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +COPY . . \ No newline at end of file diff --git a/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/usr/share/doc/libsqlite3-0/copyright b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/usr/share/doc/libsqlite3-0/copyright new file mode 100644 index 00000000000..84df12a7fe4 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/usr/share/doc/libsqlite3-0/copyright @@ -0,0 +1,30 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: sqlite3 +Source: https://www.sqlite.org/cgi/src/dir?ci=trunk + +Files: * +Copyright: D. Richard Hipp +License: public-domain + The files listed have been put on the public domain by the sqlite3 + contributors. + +Files: debian/* +Copyright: 2006- Laszlo Boszormenyi (GCS) , + 2005-2006 Tomas Fasth , + 2001-2005 Andreas Rottmann +License: GPL-2+ + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License version 2 as published + by the Free Software Foundation. + . + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + . + You should have received a copy of the GNU General Public License along + with this package; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + On Debian systems, the full text of the GNU General Public License + version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. diff --git a/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0 b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0 new file mode 100644 index 00000000000..1b97b6ca828 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0 @@ -0,0 +1,16 @@ +Package: libsqlite3-0 +Source: sqlite3 +Version: 3.34.1-3 +Architecture: arm64 +Maintainer: Laszlo Boszormenyi (GCS) +Installed-Size: 1490 +Depends: libc6 (>= 2.29) +Breaks: python-migrate (<< 0.11.0-4~), python3-migrate (<< 0.11.0-4~) +Section: libs +Priority: optional +Multi-Arch: same +Homepage: https://www.sqlite.org/ +Description: SQLite 3 shared library + SQLite is a C library that implements an SQL database engine. + Programs that link with the SQLite library can have SQL database + access without running a separate RDBMS process. diff --git a/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0.md5sums b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0.md5sums new file mode 100644 index 00000000000..f07b5486050 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/image-distroless-deb/var/lib/dpkg/status.d/libsqlite3-0.md5sums @@ -0,0 +1,6 @@ +e11d70c96979a1328ae4e7e50542782b usr/lib/aarch64-linux-gnu/libsqlite3.so.0.8.6 +9d8facc2fa9d2df52f1c7cb4e5fa4741 usr/share/doc/libsqlite3-0/README.Debian +a58942e742f5056be0595e6ba69a323f usr/share/doc/libsqlite3-0/changelog.Debian.gz +52317be84c3ca44b7888c6921131e37d usr/share/doc/libsqlite3-0/changelog.gz +a856310354e6c8768e85b39ae838dd0a usr/share/doc/libsqlite3-0/changelog.html.gz +be64db3e095486e5e105652c51199358 usr/share/doc/libsqlite3-0/copyright \ No newline at end of file