From 269832ce8daedbf2b085df247e9b506fe4ab29af Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 8 Apr 2021 14:21:23 -0400 Subject: [PATCH 1/4] add conffile listing to dpkg metadata + normalize digests Signed-off-by: Alex Goodman --- syft/pkg/cataloger/deb/cataloger.go | 180 +++++++++++++----- syft/pkg/cataloger/deb/cataloger_test.go | 45 ++++- .../cataloger/deb/parse_dpkg_info_files.go | 51 ++++- .../deb/parse_dpkg_info_files_test.go | 71 ++++++- syft/pkg/cataloger/deb/parse_dpkg_status.go | 74 ++++--- .../cataloger/deb/parse_dpkg_status_test.go | 86 +++++++++ .../lib/dpkg/info/libpam-runtime.conffiles | 2 + .../test-fixtures/info/util-linux.conffiles | 6 + syft/pkg/dpkg_metadata.go | 7 +- 9 files changed, 425 insertions(+), 97 deletions(-) create mode 100644 syft/pkg/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.conffiles create mode 100644 syft/pkg/cataloger/deb/test-fixtures/info/util-linux.conffiles diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index 34c3df3ac23..2e54f8698fb 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -8,14 +8,17 @@ import ( "io" "path" "path/filepath" + "sort" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) const ( - md5sumsExt = ".md5sums" - docsPath = "/usr/share/doc" + md5sumsExt = ".md5sums" + conffilesExt = ".conffiles" + docsPath = "/usr/share/doc" ) type Cataloger struct{} @@ -56,93 +59,166 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, error) p.FoundBy = c.Name() p.Locations = []source.Location{dbLocation} - metadata := p.Metadata.(pkg.DpkgMetadata) + // the current entry only has what may have been listed in the status file, however, there are additional + // files that are listed in multiple other locations. We should retrieve them all and merge the file lists + // together. + mergeFileListing(resolver, dbLocation, p) - md5Reader, md5Location, err := fetchMd5Contents(resolver, dbLocation, p) - if err != nil { - return nil, fmt.Errorf("unable to find dpkg md5 contents: %w", err) - } + // fetch additional data from the copyright file to derive the license information + addLicenses(resolver, dbLocation, p) + } - if md5Reader != nil { - // attach the file list - metadata.Files = parseDpkgMD5Info(md5Reader) - - // keep a record of the file where this was discovered - if md5Location != nil { - p.Locations = append(p.Locations, *md5Location) - } - } else { - // ensure the file list is an empty collection (not nil) - metadata.Files = make([]pkg.DpkgFileRecord, 0) - } + results = append(results, pkgs...) + } + return results, nil +} - // persist alterations - p.Metadata = metadata +func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { + // get license information from the copyright file + copyrightReader, copyrightLocation := fetchCopyrightContents(resolver, dbLocation, p) - // get license information from the copyright file - copyrightReader, copyrightLocation, err := fetchCopyrightContents(resolver, dbLocation, p) - if err != nil { - return nil, fmt.Errorf("unable to find dpkg copyright contents: %w", err) - } + if copyrightReader != nil { + // attach the licenses + p.Licenses = parseLicensesFromCopyright(copyrightReader) + + // keep a record of the file where this was discovered + if copyrightLocation != nil { + p.Locations = append(p.Locations, *copyrightLocation) + } + } +} - if copyrightReader != nil { - // attach the licenses - p.Licenses = parseLicensesFromCopyright(copyrightReader) +func mergeFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { + metadata := p.Metadata.(pkg.DpkgMetadata) - // keep a record of the file where this was discovered - if copyrightLocation != nil { - p.Locations = append(p.Locations, *copyrightLocation) - } + // get file listing (package files + additional config files) + files, infoLocations := getAdditionalFileListing(resolver, dbLocation, p) +loopNewFiles: + for _, newFile := range files { + for _, existingFile := range metadata.Files { + if existingFile.Path == newFile.Path { + // skip adding this file since it already exists + continue loopNewFiles } } + metadata.Files = append(metadata.Files, newFile) + } - results = append(results, pkgs...) + // sort files by path + sort.SliceStable(metadata.Files, func(i, j int) bool { + return metadata.Files[i].Path < metadata.Files[j].Path + }) + + // persist alterations + p.Metadata = metadata + + // persist location information from each new source of information + p.Locations = append(p.Locations, infoLocations...) +} + +func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) ([]pkg.DpkgFileRecord, []source.Location) { + // ensure the file list is an empty collection (not nil) + var files = make([]pkg.DpkgFileRecord, 0) + var locations []source.Location + + md5Reader, md5Location := fetchMd5Contents(resolver, dbLocation, p) + + if md5Reader != nil { + // attach the file list + files = append(files, parseDpkgMD5Info(md5Reader)...) + + // keep a record of the file where this was discovered + if md5Location != nil { + locations = append(locations, *md5Location) + } } - return results, nil + + conffilesReader, conffilesLocation := fetchConffileContents(resolver, dbLocation, p) + + if conffilesReader != nil { + // attach the file list + files = append(files, parseDpkgConffileInfo(md5Reader)...) + + // keep a record of the file where this was discovered + if md5Location != nil { + locations = append(locations, *conffilesLocation) + } + } + + return files, locations } -func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.Reader, *source.Location, error) { +func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) { + var md5Reader io.ReadCloser + var err error + parentPath := filepath.Dir(dbLocation.RealPath) // look for /var/lib/dpkg/info/NAME:ARCH.md5sums name := md5Key(p) - md5SumLocation := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+md5sumsExt)) + location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+md5sumsExt)) - if md5SumLocation == nil { + if location == nil { // the most specific key did not work, fallback to just the name // look for /var/lib/dpkg/info/NAME.md5sums - md5SumLocation = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", p.Name+md5sumsExt)) + location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", p.Name+md5sumsExt)) } // this is unexpected, but not a show-stopper - if md5SumLocation == nil { - return nil, nil, nil + if location != nil { + md5Reader, err = resolver.FileContentsByLocation(*location) + if err != nil { + log.Warnf("failed to fetch deb md5 contents (package=%s): %+v", p.Name, err) + } } - reader, err := resolver.FileContentsByLocation(*md5SumLocation) - if err != nil { - return nil, nil, fmt.Errorf("failed to fetch deb md5 contents (%+v): %w", p, err) + return md5Reader, location +} + +func fetchConffileContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) { + var reader io.ReadCloser + var err error + + parentPath := filepath.Dir(dbLocation.RealPath) + + // look for /var/lib/dpkg/info/NAME:ARCH.conffiles + name := md5Key(p) + location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+conffilesExt)) + + if location == nil { + // the most specific key did not work, fallback to just the name + // look for /var/lib/dpkg/info/NAME.conffiles + location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", p.Name+conffilesExt)) } - return reader, md5SumLocation, 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", p.Name, err) + } + } + + return reader, location } -func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.Reader, *source.Location, error) { +func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) { // look for /usr/share/docs/NAME/copyright files name := p.Name copyrightPath := path.Join(docsPath, name, "copyright") - copyrightLocation := resolver.RelativeFileByPath(dbLocation, copyrightPath) + location := resolver.RelativeFileByPath(dbLocation, copyrightPath) // we may not have a copyright file for each package, ignore missing files - if copyrightLocation == nil { - return nil, nil, nil + if location == nil { + return nil, nil } - reader, err := resolver.FileContentsByLocation(*copyrightLocation) + reader, err := resolver.FileContentsByLocation(*location) if err != nil { - return nil, nil, fmt.Errorf("failed to fetch deb copyright contents (%+v): %w", p, err) + log.Warnf("failed to fetch deb copyright contents (package=%s): %w", p.Name, err) } - return reader, copyrightLocation, nil + return reader, location } func md5Key(p *pkg.Package) string { diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index b856a0cb964..84bef7c11fb 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -3,6 +3,8 @@ package deb import ( "testing" + "github.com/anchore/syft/syft/file" + "github.com/anchore/stereoscope/pkg/imagetest" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" @@ -19,7 +21,12 @@ func TestDpkgCataloger(t *testing.T) { { name: "go-case", sources: map[string][]string{ - "libpam-runtime": {"/var/lib/dpkg/status", "/var/lib/dpkg/info/libpam-runtime.md5sums", "/usr/share/doc/libpam-runtime/copyright"}, + "libpam-runtime": { + "/var/lib/dpkg/status", + "/var/lib/dpkg/info/libpam-runtime.md5sums", + "/var/lib/dpkg/info/libpam-runtime.conffiles", + "/usr/share/doc/libpam-runtime/copyright", + }, }, expected: []pkg.Package{ { @@ -37,10 +44,38 @@ func TestDpkgCataloger(t *testing.T) { Maintainer: "Steve Langasek ", InstalledSize: 1016, Files: []pkg.DpkgFileRecord{ - {Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", MD5: "55f905631797551d4d936a34c7e73474"}, - {Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", MD5: "cede84bda30d2380217f97753c8ccf3a"}, - {Path: "/usr/share/doc/zlib1g/changelog.gz", MD5: "f3c9dafa6da7992c47328b4464f6d122"}, - {Path: "/usr/share/doc/zlib1g/copyright", MD5: "a4fae96070439a5209a62ae5b8017ab2"}, + { + 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", + }}, }, }, }, diff --git a/syft/pkg/cataloger/deb/parse_dpkg_info_files.go b/syft/pkg/cataloger/deb/parse_dpkg_info_files.go index 0561732c925..105c47da956 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_info_files.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_info_files.go @@ -5,12 +5,11 @@ import ( "io" "strings" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" ) -func parseDpkgMD5Info(reader io.Reader) []pkg.DpkgFileRecord { - // we must preallocate to ensure the resulting struct does not have null - var findings = make([]pkg.DpkgFileRecord, 0) +func parseDpkgMD5Info(reader io.Reader) (findings []pkg.DpkgFileRecord) { scanner := bufio.NewScanner(reader) for scanner.Scan() { @@ -23,9 +22,53 @@ func parseDpkgMD5Info(reader io.Reader) []pkg.DpkgFileRecord { } findings = append(findings, pkg.DpkgFileRecord{ Path: path, - MD5: strings.TrimSpace(fields[0]), + Digest: &file.Digest{ + Algorithm: "md5", + Value: strings.TrimSpace(fields[0]), + }, }) } } return findings } + +func parseDpkgConffileInfo(reader io.Reader) (findings []pkg.DpkgFileRecord) { + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + line := strings.Trim(scanner.Text(), " \n") + fields := strings.SplitN(line, " ", 2) + + if line == "" { + continue + } + + var path string + if len(fields) >= 1 { + path = strings.TrimSpace(fields[0]) + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + } + + var digest *file.Digest + if len(fields) >= 2 { + digest = &file.Digest{ + Algorithm: "md5", + Value: strings.TrimSpace(fields[1]), + } + } + + if path != "" { + record := pkg.DpkgFileRecord{ + Path: path, + IsConfigFile: true, + } + if digest != nil { + record.Digest = digest + } + findings = append(findings, record) + } + } + return findings +} diff --git a/syft/pkg/cataloger/deb/parse_dpkg_info_files_test.go b/syft/pkg/cataloger/deb/parse_dpkg_info_files_test.go index df252ddc456..c345c714eab 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_info_files_test.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_info_files_test.go @@ -4,6 +4,8 @@ import ( "os" "testing" + "github.com/anchore/syft/syft/file" + "github.com/go-test/deep" "github.com/anchore/syft/syft/pkg" @@ -17,10 +19,22 @@ func TestMD5SumInfoParsing(t *testing.T) { { fixture: "test-fixtures/info/zlib1g.md5sums", expected: []pkg.DpkgFileRecord{ - {Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", MD5: "55f905631797551d4d936a34c7e73474"}, - {Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", MD5: "cede84bda30d2380217f97753c8ccf3a"}, - {Path: "/usr/share/doc/zlib1g/changelog.gz", MD5: "f3c9dafa6da7992c47328b4464f6d122"}, - {Path: "/usr/share/doc/zlib1g/copyright", MD5: "a4fae96070439a5209a62ae5b8017ab2"}, + {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", + }}, }, }, } @@ -55,3 +69,52 @@ func TestMD5SumInfoParsing(t *testing.T) { }) } } + +func TestConffileInfoParsing(t *testing.T) { + tests := []struct { + fixture string + expected []pkg.DpkgFileRecord + }{ + { + fixture: "test-fixtures/info/util-linux.conffiles", + expected: []pkg.DpkgFileRecord{ + {Path: "/etc/default/hwclock", IsConfigFile: true}, + {Path: "/etc/init.d/hwclock.sh", IsConfigFile: true}, + {Path: "/etc/pam.d/runuser", IsConfigFile: true}, + {Path: "/etc/pam.d/runuser-l", IsConfigFile: true}, + {Path: "/etc/pam.d/su", IsConfigFile: true}, + {Path: "/etc/pam.d/su-l", IsConfigFile: true}, + }, + }, + } + + for _, test := range tests { + t.Run(test.fixture, func(t *testing.T) { + file, err := os.Open(test.fixture) + if err != nil { + t.Fatal("Unable to read: ", err) + } + defer func() { + err := file.Close() + if err != nil { + t.Fatal("closing file failed:", err) + } + }() + + actual := parseDpkgConffileInfo(file) + + if len(actual) != len(test.expected) { + for _, a := range actual { + t.Logf(" %+v", a) + } + t.Fatalf("unexpected package count: %d!=%d", len(actual), len(test.expected)) + } + + diffs := deep.Equal(actual, test.expected) + for _, d := range diffs { + t.Errorf("diff: %+v", d) + } + + }) + } +} diff --git a/syft/pkg/cataloger/deb/parse_dpkg_status.go b/syft/pkg/cataloger/deb/parse_dpkg_status.go index 2c14ac14ada..fb61b593751 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_status.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_status.go @@ -15,7 +15,10 @@ import ( "github.com/mitchellh/mapstructure" ) -var errEndOfPackages = fmt.Errorf("no more packages to read") +var ( + errEndOfPackages = fmt.Errorf("no more packages to read") + sourceRegexp = regexp.MustCompile(`(?P\S+)( \((?P.*)\))?`) +) // parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed. func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) { @@ -48,20 +51,49 @@ func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) { } // parseDpkgStatusEntry returns an individual Dpkg entry, or returns errEndOfPackages if there are no more packages to parse from the reader. -// nolint:funlen -func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err error) { - dpkgFields := make(map[string]interface{}) +func parseDpkgStatusEntry(reader *bufio.Reader) (pkg.DpkgMetadata, error) { var retErr error + dpkgFields, err := extractAllFields(reader) + if err != nil { + if !errors.Is(err, errEndOfPackages) { + return pkg.DpkgMetadata{}, err + } + retErr = err + } + + var entry pkg.DpkgMetadata + err = mapstructure.Decode(dpkgFields, &entry) + if err != nil { + return pkg.DpkgMetadata{}, err + } + + name, version := extractSourceVersion(entry.Source) + if version != "" { + entry.SourceVersion = version + entry.Source = name + } + + // there may be an optional conffiles section that we should persist as files + if conffilesSection, exists := dpkgFields["Conffiles"]; exists && conffilesSection != nil { + if sectionStr, ok := conffilesSection.(string); ok { + entry.Files = parseDpkgConffileInfo(strings.NewReader(sectionStr)) + } + } + + return entry, retErr +} + +func extractAllFields(reader *bufio.Reader) (map[string]interface{}, error) { + dpkgFields := make(map[string]interface{}) var key string for { line, err := reader.ReadString('\n') if err != nil { if err == io.EOF { - retErr = errEndOfPackages - break + return dpkgFields, errEndOfPackages } - return pkg.DpkgMetadata{}, err + return nil, err } line = strings.TrimRight(line, "\n") @@ -79,12 +111,12 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err err case strings.HasPrefix(line, " "): // a field-body continuation if len(key) == 0 { - return pkg.DpkgMetadata{}, fmt.Errorf("no match for continuation: line: '%s'", line) + return nil, fmt.Errorf("no match for continuation: line: '%s'", line) } val, ok := dpkgFields[key] if !ok { - return pkg.DpkgMetadata{}, fmt.Errorf("no previous key exists, expecting: %s", key) + return nil, fmt.Errorf("no previous key exists, expecting: %s", key) } // concatenate onto previous value val = fmt.Sprintf("%s\n %s", val, strings.TrimSpace(line)) @@ -94,36 +126,18 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err err var val interface{} key, val, err = handleNewKeyValue(line) if err != nil { - return pkg.DpkgMetadata{}, err + return nil, err } if _, ok := dpkgFields[key]; ok { - return pkg.DpkgMetadata{}, fmt.Errorf("duplicate key discovered: %s", key) + return nil, fmt.Errorf("duplicate key discovered: %s", key) } dpkgFields[key] = val } } - - err = mapstructure.Decode(dpkgFields, &entry) - if err != nil { - return pkg.DpkgMetadata{}, err - } - - name, version := extractSourceVersion(entry.Source) - if version != "" { - entry.SourceVersion = version - entry.Source = name - } - - return entry, retErr + return dpkgFields, nil } -// match examples: -// "a-thing (1.2.3)" name="a-thing" version="1.2.3" -// "a-thing" name="a-thing" version="" -// "" name="" version="" -var sourceRegexp = regexp.MustCompile(`(?P\S+)( \((?P.*)\))?`) - // If the source entry string is of the form " ()" then parse and return the components, if // of the "" form, then return name and nil func extractSourceVersion(source string) (string, string) { diff --git a/syft/pkg/cataloger/deb/parse_dpkg_status_test.go b/syft/pkg/cataloger/deb/parse_dpkg_status_test.go index 0c4b0720204..e0b53d6ee27 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_status_test.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_status_test.go @@ -5,6 +5,8 @@ import ( "os" "testing" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" "github.com/go-test/deep" ) @@ -30,6 +32,40 @@ func TestSinglePackage(t *testing.T) { Architecture: "amd64", InstalledSize: 4064, Maintainer: "APT Development Team ", + Files: []pkg.DpkgFileRecord{ + { + Path: "/etc/apt/apt.conf.d/01autoremove", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "76120d358bc9037bb6358e737b3050b5", + }, + IsConfigFile: true, + }, + { + Path: "/etc/cron.daily/apt-compat", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "49e9b2cfa17849700d4db735d04244f3", + }, + IsConfigFile: true, + }, + { + Path: "/etc/kernel/postinst.d/apt-auto-removal", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "4ad976a68f045517cf4696cec7b8aa3a", + }, + IsConfigFile: true, + }, + { + Path: "/etc/logrotate.d/apt", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "179f2ed4f85cbaca12fa3d69c2a4a1c3", + }, + IsConfigFile: true, + }, + }, }, }, } @@ -81,6 +117,56 @@ func TestMultiplePackages(t *testing.T) { Architecture: "amd64", InstalledSize: 4327, Maintainer: "LaMont Jones ", + Files: []pkg.DpkgFileRecord{ + { + Path: "/etc/default/hwclock", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "3916544450533eca69131f894db0ca12", + }, + IsConfigFile: true, + }, + { + Path: "/etc/init.d/hwclock.sh", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "1ca5c0743fa797ffa364db95bb8d8d8e", + }, + IsConfigFile: true, + }, + { + Path: "/etc/pam.d/runuser", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "b8b44b045259525e0fae9e38fdb2aeeb", + }, + IsConfigFile: true, + }, + { + Path: "/etc/pam.d/runuser-l", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "2106ea05877e8913f34b2c77fa02be45", + }, + IsConfigFile: true, + }, + { + Path: "/etc/pam.d/su", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "ce6dcfda3b190a27a455bb38a45ff34a", + }, + IsConfigFile: true, + }, + { + Path: "/etc/pam.d/su-l", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "756fef5687fecc0d986e5951427b0c4f", + }, + IsConfigFile: true, + }, + }, }, }, }, diff --git a/syft/pkg/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.conffiles b/syft/pkg/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.conffiles new file mode 100644 index 00000000000..1fe9bc1cd97 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.conffiles @@ -0,0 +1,2 @@ +/etc/pam.conf +/etc/pam.d/other \ No newline at end of file diff --git a/syft/pkg/cataloger/deb/test-fixtures/info/util-linux.conffiles b/syft/pkg/cataloger/deb/test-fixtures/info/util-linux.conffiles new file mode 100644 index 00000000000..173ae853c60 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/info/util-linux.conffiles @@ -0,0 +1,6 @@ +/etc/default/hwclock +/etc/init.d/hwclock.sh +/etc/pam.d/runuser +/etc/pam.d/runuser-l +/etc/pam.d/su +/etc/pam.d/su-l \ No newline at end of file diff --git a/syft/pkg/dpkg_metadata.go b/syft/pkg/dpkg_metadata.go index b50ac49aa60..8f2245c5513 100644 --- a/syft/pkg/dpkg_metadata.go +++ b/syft/pkg/dpkg_metadata.go @@ -3,6 +3,8 @@ package pkg import ( "sort" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/distro" "github.com/package-url/packageurl-go" "github.com/scylladb/go-set/strset" @@ -27,8 +29,9 @@ type DpkgMetadata struct { // DpkgFileRecord represents a single file attributed to a debian package. type DpkgFileRecord struct { - Path string `json:"path"` - MD5 string `json:"md5"` + Path string `json:"path"` + Digest *file.Digest `json:"digest,omitempty"` + IsConfigFile bool `json:"isConfigFile"` } // PackageURL returns the PURL for the specific Debian package (see https://github.com/package-url/purl-spec) From ba3407a7674c8be353b2cf52d6823b0e2503b1af Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 9 Apr 2021 08:37:07 -0400 Subject: [PATCH 2/4] add dpkg conffile update to json schema + json test snapshots Signed-off-by: Alex Goodman --- internal/constants.go | 2 +- .../snapshot/TestJSONDirsPresenter.golden | 4 +- .../snapshot/TestJSONImgsPresenter.golden | 18 +- .../stereoscope-fixture-image-simple.golden | Bin 16896 -> 15360 bytes .../snapshot/TestJSONPresenter.golden | 4 +- schema/json/generate.go | 2 +- schema/json/schema-1.1.0.json | 891 ++++++++++++++++++ syft/pkg/cataloger/deb/parse_dpkg_status.go | 5 + 8 files changed, 911 insertions(+), 15 deletions(-) create mode 100644 schema/json/schema-1.1.0.json diff --git a/internal/constants.go b/internal/constants.go index 40280bcf2b9..7932c185602 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON presenter // 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 = "1.0.5" + JSONSchemaVersion = "1.1.0" ) diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirsPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirsPresenter.golden index 22592df9f47..6f2217d0f59 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirsPresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirsPresenter.golden @@ -75,7 +75,7 @@ "version": "[not provided]" }, "schema": { - "version": "1.0.5", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.5.json" + "version": "1.1.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json" } } diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestJSONImgsPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestJSONImgsPresenter.golden index 444f0bbead7..2efd1f9c89e 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestJSONImgsPresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestJSONImgsPresenter.golden @@ -9,7 +9,7 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:6c376352c0537f4483e4033e332d7a4ab9433db68c54c297a834d36719aeb6c9" + "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" } ], "licenses": [ @@ -40,7 +40,7 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:fc8218a8142ee4952bb8d9b96b3e9838322e9e6eae6477136bcad8fd768949b7" + "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" } ], "licenses": [], @@ -67,7 +67,7 @@ "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:1f9cb9dc477f7482856f88ed40c38e260db0526d7a0dad5a0be566bfedde929b", + "imageID": "sha256:5900c94a5bc1e083aa24ad1a223bf6eb9910dc8a6b01cb979ec306cb91709ea1", "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "tags": [ @@ -77,17 +77,17 @@ "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:6c376352c0537f4483e4033e332d7a4ab9433db68c54c297a834d36719aeb6c9", + "digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:fc8218a8142ee4952bb8d9b96b3e9838322e9e6eae6477136bcad8fd768949b7", + "digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoxNTg2LCJkaWdlc3QiOiJzaGEyNTY6MWY5Y2I5ZGM0NzdmNzQ4Mjg1NmY4OGVkNDBjMzhlMjYwZGIwNTI2ZDdhMGRhZDVhMGJlNTY2YmZlZGRlOTI5YiJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMDQ4LCJkaWdlc3QiOiJzaGEyNTY6NmMzNzYzNTJjMDUzN2Y0NDgzZTQwMzNlMzMyZDdhNGFiOTQzM2RiNjhjNTRjMjk3YTgzNGQzNjcxOWFlYjZjOSJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYzgyMThhODE0MmVlNDk1MmJiOGQ5Yjk2YjNlOTgzODMyMmU5ZTZlYWU2NDc3MTM2YmNhZDhmZDc2ODk0OWI3In1dfQ==", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpudWxsLCJJbWFnZSI6InNoYTI1NjoyOWQ1YjFjOTkyNjg0MzgwYjQ3NTEyMjliMmNjN2E4MzdkOTBmOWQ1OTJhYmIxZjAyZGYzZGRkMGQ3OWFjMDkxIiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCIjKG5vcCkgQUREIGZpbGU6ZGYzYjc0NGY1NGE5YjE2YjliOWFlZDQwZTNlOThkOWNhMmI0OWY1YTc3ZDlmYThhOTc2OTBkN2JhZjU4ODgyMCBpbiAvc29tZWZpbGUtMi50eHQgIl0sIkltYWdlIjoic2hhMjU2OjI5ZDViMWM5OTI2ODQzODBiNDc1MTIyOWIyY2M3YTgzN2Q5MGY5ZDU5MmFiYjFmMDJkZjNkZGQwZDc5YWMwOTEiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDQtMDFUMTI6NDg6MzIuMjYzNjAzMVoiLCJkb2NrZXJfdmVyc2lvbiI6IjIwLjEwLjIiLCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0wNC0wMVQxMjo0ODozMi4wODY3MTY2WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSBBREQgZmlsZTphYzMyZGEyM2Q1MWU4MDFmMDJmOTI0MTIzZWQzMDk5MGViM2YwZmVjMWI5ZWQ0ZjBiMDZjMjRlODhiOWMzNjk1IGluIC9zb21lZmlsZS0xLnR4dCAifSx7ImNyZWF0ZWQiOiIyMDIxLTA0LTAxVDEyOjQ4OjMyLjI2MzYwMzFaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOmRmM2I3NDRmNTRhOWIxNmI5YjlhZWQ0MGUzZTk4ZDljYTJiNDlmNWE3N2Q5ZmE4YTk3NjkwZDdiYWY1ODg4MjAgaW4gL3NvbWVmaWxlLTIudHh0ICJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OjZjMzc2MzUyYzA1MzdmNDQ4M2U0MDMzZTMzMmQ3YTRhYjk0MzNkYjY4YzU0YzI5N2E4MzRkMzY3MTlhZWI2YzkiLCJzaGEyNTY6ZmM4MjE4YTgxNDJlZTQ5NTJiYjhkOWI5NmIzZTk4MzgzMjJlOWU2ZWFlNjQ3NzEzNmJjYWQ4ZmQ3Njg5NDliNyJdfX0=", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NjUsImRpZ2VzdCI6InNoYTI1Njo1OTAwYzk0YTViYzFlMDgzYWEyNGFkMWEyMjNiZjZlYjk5MTBkYzhhNmIwMWNiOTc5ZWMzMDZjYjkxNzA5ZWExIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDQtMDZUMTk6MTM6NTIuNTI0Mzc4WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTA0LTA2VDE5OjEzOjUyLjQ1ODIwNjFaIiwiY3JlYXRlZF9ieSI6IkFERCBmaWxlLTEudHh0IC9zb21lZmlsZS0xLnR4dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifSx7ImNyZWF0ZWQiOiIyMDIxLTA0LTA2VDE5OjEzOjUyLjUyNDM3OFoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMi50eHQgL3NvbWVmaWxlLTIudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OmZiNmJlZWNiNzViMzlmNGJiODEzZGJmMTc3ZTUwMWVkZDVkZGIzZTY5YmI0NWNlZGViNzhjNjc2ZWUxYjdhNTkiLCJzaGEyNTY6MzE5YjU4OGNlNjQyNTNhODdiNTMzYzhlZDAxY2YwMDI1ZTBlYWM5OGU3YjUxNmUxMjUzMjk1N2UxMjQ0ZmRlYyJdfX0=", "repoDigests": [], "scope": "Squashed" } @@ -102,7 +102,7 @@ "version": "[not provided]" }, "schema": { - "version": "1.0.5", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.5.json" + "version": "1.1.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json" } } diff --git a/internal/presenter/packages/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/internal/presenter/packages/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index b6759cac62be2f99be64e5380b3296f714e9a102..a5985f95c4f36dfec9b67737ea614680187f8ae7 100644 GIT binary patch literal 15360 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 Date: Fri, 9 Apr 2021 08:46:59 -0400 Subject: [PATCH 3/4] fix DpkgMetadata.Files test to ensure it is never nil Signed-off-by: Alex Goodman --- syft/pkg/cataloger/deb/parse_dpkg_status_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/syft/pkg/cataloger/deb/parse_dpkg_status_test.go b/syft/pkg/cataloger/deb/parse_dpkg_status_test.go index e0b53d6ee27..f61b23058d6 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_status_test.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_status_test.go @@ -110,6 +110,7 @@ func TestMultiplePackages(t *testing.T) { Architecture: "all", InstalledSize: 3036, Maintainer: "GNU Libc Maintainers ", + Files: []pkg.DpkgFileRecord{}, }, { Package: "util-linux", From 0511972dfaef39a2c904e41d22cda67f779807e5 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 9 Apr 2021 11:41:44 -0400 Subject: [PATCH 4/4] clarify default collection value + fix appending conffiles location Signed-off-by: Alex Goodman --- syft/pkg/cataloger/deb/cataloger.go | 4 ++-- syft/pkg/cataloger/deb/parse_dpkg_status.go | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index 2e54f8698fb..905d08727df 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -117,7 +117,7 @@ loopNewFiles: } func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) ([]pkg.DpkgFileRecord, []source.Location) { - // ensure the file list is an empty collection (not nil) + // ensure the default value for a collection is never nil since this may be shown as JSON var files = make([]pkg.DpkgFileRecord, 0) var locations []source.Location @@ -140,7 +140,7 @@ func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Lo files = append(files, parseDpkgConffileInfo(md5Reader)...) // keep a record of the file where this was discovered - if md5Location != nil { + if conffilesLocation != nil { locations = append(locations, *conffilesLocation) } } diff --git a/syft/pkg/cataloger/deb/parse_dpkg_status.go b/syft/pkg/cataloger/deb/parse_dpkg_status.go index 91f9f0fc511..8992c18361e 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_status.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_status.go @@ -61,7 +61,10 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (pkg.DpkgMetadata, error) { retErr = err } - var entry pkg.DpkgMetadata + entry := pkg.DpkgMetadata{ + // ensure the default value for a collection is never nil since this may be shown as JSON + Files: make([]pkg.DpkgFileRecord, 0), + } err = mapstructure.Decode(dpkgFields, &entry) if err != nil { return pkg.DpkgMetadata{}, err @@ -80,11 +83,6 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (pkg.DpkgMetadata, error) { } } - if entry.Files == nil { - // ensure that the collection is always allocated - entry.Files = make([]pkg.DpkgFileRecord, 0) - } - return entry, retErr }