From 2a329002b85799d720543d27bf41ce6a0455910e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 9 Nov 2020 15:33:24 -0500 Subject: [PATCH] enhance dpkg support by parsing md5sum and copyright file sources Signed-off-by: Alex Goodman --- schema/json/schema.json | 95 +++-- syft/cataloger/deb/cataloger.go | 189 ++++++++- syft/cataloger/deb/cataloger_test.go | 99 +++++ syft/cataloger/deb/parse_copyright.go | 38 ++ syft/cataloger/deb/parse_copyright_test.go | 58 +++ syft/cataloger/deb/parse_dpkg_info_files.go | 30 ++ .../deb/parse_dpkg_info_files_test.go | 57 +++ syft/cataloger/deb/parse_dpkg_status.go | 87 ++-- syft/cataloger/deb/parse_dpkg_status_test.go | 34 +- .../test-fixtures/copyright/libaudit-common | 43 ++ .../deb/test-fixtures/copyright/liblzma5 | 383 ++++++++++++++++++ .../deb/test-fixtures/copyright/trilicense | 13 + .../deb/test-fixtures/image-dpkg/Dockerfile | 2 + .../usr/share/doc/libpam-runtime/copyright | 43 ++ .../var/lib/dpkg/info/libpam-runtime.md5sums | 4 + .../image-dpkg/var/lib/dpkg/status | 21 + .../deb/test-fixtures/info/zlib1g.md5sums | 4 + .../deb/test-fixtures/{ => status}/multiple | 0 .../deb/test-fixtures/{ => status}/single | 0 syft/pkg/dpkg_metadata.go | 17 +- syft/presenter/json/artifact.go | 2 + .../snapshot/TestJsonDirsPresenter.golden | 6 +- .../snapshot/TestJsonImgsPresenter.golden | 6 +- 23 files changed, 1136 insertions(+), 95 deletions(-) create mode 100644 syft/cataloger/deb/cataloger_test.go create mode 100644 syft/cataloger/deb/parse_copyright.go create mode 100644 syft/cataloger/deb/parse_copyright_test.go create mode 100644 syft/cataloger/deb/parse_dpkg_info_files.go create mode 100644 syft/cataloger/deb/parse_dpkg_info_files_test.go create mode 100644 syft/cataloger/deb/test-fixtures/copyright/libaudit-common create mode 100644 syft/cataloger/deb/test-fixtures/copyright/liblzma5 create mode 100644 syft/cataloger/deb/test-fixtures/copyright/trilicense create mode 100644 syft/cataloger/deb/test-fixtures/image-dpkg/Dockerfile create mode 100644 syft/cataloger/deb/test-fixtures/image-dpkg/usr/share/doc/libpam-runtime/copyright create mode 100644 syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.md5sums create mode 100644 syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/status create mode 100644 syft/cataloger/deb/test-fixtures/info/zlib1g.md5sums rename syft/cataloger/deb/test-fixtures/{ => status}/multiple (100%) rename syft/cataloger/deb/test-fixtures/{ => status}/single (100%) diff --git a/schema/json/schema.json b/schema/json/schema.json index a908a335e..954c58d92 100644 --- a/schema/json/schema.json +++ b/schema/json/schema.json @@ -10,6 +10,19 @@ }, "type": "array" }, + "licenses": { + "anyOf": [ + { + "type": "null" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ] + }, "locations": { "items": { "anyOf": [ @@ -53,55 +66,62 @@ "type": "integer" }, "files": { - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "properties": { - "checksum": { + "anyOf": [ + { + "type": "null" + }, + { + "items": { + "anyOf": [ + { "type": "string" }, - "digest": { + { "properties": { - "algorithm": { + "checksum": { "type": "string" }, - "value": { + "digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "algorithm", + "value" + ], + "type": "object" + }, + "ownerGid": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "path": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "size": { "type": "string" } }, "required": [ - "algorithm", - "value" + "path" ], "type": "object" - }, - "ownerGid": { - "type": "string" - }, - "ownerUid": { - "type": "string" - }, - "path": { - "type": "string" - }, - "permissions": { - "type": "string" - }, - "size": { - "type": "string" } - }, - "required": [ - "path" - ], - "type": "object" - } - ] - }, - "type": "array" + ] + }, + "type": "array" + } + ] }, "gitCommitOfApkPort": { "type": "string" @@ -307,6 +327,7 @@ }, "required": [ "foundBy", + "licenses", "locations", "name", "type", diff --git a/syft/cataloger/deb/cataloger.go b/syft/cataloger/deb/cataloger.go index b1332c572..c10f0dd71 100644 --- a/syft/cataloger/deb/cataloger.go +++ b/syft/cataloger/deb/cataloger.go @@ -4,14 +4,193 @@ Package dpkg provides a concrete Cataloger implementation for Debian package DB package deb import ( - "github.com/anchore/syft/syft/cataloger/common" + "fmt" + "io" + "path" + "strings" + + "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/scope" ) +const ( + dpkgStatusGlob = "**/var/lib/dpkg/status" + md5sumsExt = ".md5sums" + docsPath = "/usr/share/doc" +) + +type Cataloger struct{} + // NewDpkgdbCataloger returns a new Deb package cataloger object. -func NewDpkgdbCataloger() *common.GenericCataloger { - globParsers := map[string]common.ParserFn{ - "**/var/lib/dpkg/status": parseDpkgStatus, +func NewDpkgdbCataloger() *Cataloger { + return &Cataloger{} +} + +// Name returns a string that uniquely describes a cataloger +func (c *Cataloger) Name() string { + return "dpkgdb-cataloger" +} + +// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing dpkg support files. +func (c *Cataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, error) { + dbFileMatches, err := resolver.FilesByGlob(dpkgStatusGlob) + if err != nil { + return nil, fmt.Errorf("failed to find dpkg status files's by glob: %w", err) } - return common.NewGenericCataloger(nil, globParsers, "dpkgdb-cataloger") + var pkgs []pkg.Package + for _, dbRef := range dbFileMatches { + dbContents, err := resolver.FileContentsByRef(dbRef) + if err != nil { + return nil, err + } + + pkgs, err = parseDpkgStatus(strings.NewReader(dbContents)) + if err != nil { + return nil, fmt.Errorf("unable to catalog dpkg package=%+v: %w", dbRef.Path, err) + } + + md5ContentsByName, md5RefsByName, err := fetchMd5Contents(resolver, dbRef, pkgs) + if err != nil { + return nil, fmt.Errorf("unable to find dpkg md5 contents: %w", err) + } + + copyrightContentsByName, copyrightRefsByName, err := fetchCopyrightContents(resolver, dbRef, pkgs) + if err != nil { + return nil, fmt.Errorf("unable to find dpkg copyright contents: %w", err) + } + + for i := range pkgs { + p := &pkgs[i] + p.FoundBy = c.Name() + p.Source = []file.Reference{dbRef} + + if md5Reader, ok := md5ContentsByName[md5Key(*p)]; ok { + // attach the file list + metadata := p.Metadata.(pkg.DpkgMetadata) + metadata.Files = parseDpkgMD5Info(md5Reader) + p.Metadata = metadata + + // keep a record of the file where this was discovered + if ref, ok := md5RefsByName[md5Key(*p)]; ok { + p.Source = append(p.Source, ref) + } + } + + copyrightReader, ok := copyrightContentsByName[p.Name] + if ok { + // attach the licenses + p.Licenses = parseLicensesFromCopyright(copyrightReader) + + // keep a record of the file where this was discovered + if ref, ok := copyrightRefsByName[p.Name]; ok { + p.Source = append(p.Source, ref) + } + } + } + } + return pkgs, nil +} + +func fetchMd5Contents(resolver scope.Resolver, dbRef file.Reference, pkgs []pkg.Package) (map[string]io.Reader, map[string]file.Reference, error) { + // fetch all MD5 file contents. This approach is more efficient than fetching each MD5 file one at a time + + var md5FileMatches []file.Reference + var nameByRef = make(map[file.Reference]string) + parentPath, err := dbRef.Path.ParentPath() + if err != nil { + return nil, nil, fmt.Errorf("unable to find parent of path=%+v: %w", dbRef.Path, err) + } + for _, p := range pkgs { + // look for /var/lib/dpkg/info/NAME:ARCH.md5sums + name := md5Key(p) + md5sumPath := path.Join(string(parentPath), "info", name+md5sumsExt) + md5SumRef, err := resolver.RelativeFileByPath(dbRef, md5sumPath) + if err != nil { + return nil, nil, fmt.Errorf("unable to find relative md5sum from path=%+v: %w", dbRef.Path, err) + } + + if md5SumRef == nil { + // the most specific key did not work, fallback to just the name + // look for /var/lib/dpkg/info/NAME.md5sums + name := p.Name + md5sumPath := path.Join(string(parentPath), "info", name+md5sumsExt) + md5SumRef, err = resolver.RelativeFileByPath(dbRef, md5sumPath) + if err != nil { + return nil, nil, fmt.Errorf("unable to find relative md5sum from path=%+v: %w", dbRef.Path, err) + } + } + // we should have at least one reference + if md5SumRef != nil { + md5FileMatches = append(md5FileMatches, *md5SumRef) + nameByRef[*md5SumRef] = name + } + } + + // fetch the md5 contents + md5ContentsByRef, err := resolver.MultipleFileContentsByRef(md5FileMatches...) + if err != nil { + return nil, nil, err + } + + // organize content results and refs by a combination of name and architecture + var contentsByName = make(map[string]io.Reader) + var refsByName = make(map[string]file.Reference) + for ref, contents := range md5ContentsByRef { + name := nameByRef[ref] + contentsByName[name] = strings.NewReader(contents) + refsByName[name] = ref + } + + return contentsByName, refsByName, nil +} + +func fetchCopyrightContents(resolver scope.Resolver, dbRef file.Reference, pkgs []pkg.Package) (map[string]io.Reader, map[string]file.Reference, error) { + // fetch all copyright file contents. This approach is more efficient than fetching each copyright file one at a time + + var copyrightFileMatches []file.Reference + var nameByRef = make(map[file.Reference]string) + for _, p := range pkgs { + // look for /usr/share/docs/NAME/copyright files + name := p.Name + copyrightPath := path.Join(docsPath, name, "copyright") + copyrightRef, err := resolver.RelativeFileByPath(dbRef, copyrightPath) + if err != nil { + return nil, nil, fmt.Errorf("unable to find relative copyright from path=%+v: %w", dbRef.Path, err) + } + + // we may not have a copyright file for each package, ignore missing files + if copyrightRef != nil { + copyrightFileMatches = append(copyrightFileMatches, *copyrightRef) + nameByRef[*copyrightRef] = name + } + } + + // fetch the copyright contents + copyrightContentsByRef, err := resolver.MultipleFileContentsByRef(copyrightFileMatches...) + if err != nil { + return nil, nil, err + } + + // organize content results and refs by package name + var contentsByName = make(map[string]io.Reader) + var refsByName = make(map[string]file.Reference) + for ref, contents := range copyrightContentsByRef { + name := nameByRef[ref] + contentsByName[name] = strings.NewReader(contents) + refsByName[name] = ref + } + + return contentsByName, refsByName, nil +} + +func md5Key(p pkg.Package) string { + metadata := p.Metadata.(pkg.DpkgMetadata) + + contentKey := p.Name + if metadata.Architecture != "" && metadata.Architecture != "all" { + contentKey = contentKey + ":" + metadata.Architecture + } + return contentKey } diff --git a/syft/cataloger/deb/cataloger_test.go b/syft/cataloger/deb/cataloger_test.go new file mode 100644 index 000000000..fb36c59e5 --- /dev/null +++ b/syft/cataloger/deb/cataloger_test.go @@ -0,0 +1,99 @@ +package deb + +import ( + "testing" + + "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/scope" + "github.com/go-test/deep" +) + +func TestDpkgCataloger(t *testing.T) { + + tests := []struct { + name string + sources map[string][]string + expected []pkg.Package + }{ + { + 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"}, + }, + expected: []pkg.Package{ + { + Name: "libpam-runtime", + Version: "1.1.8-3.6", + FoundBy: "dpkgdb-cataloger", + Licenses: []string{"GPL-2", "LGPL-2.1"}, + 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, + 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"}, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + + img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-dpkg") + defer cleanup() + + s, err := scope.NewScopeFromImage(img, scope.AllLayersScope) + if err != nil { + t.Fatal(err) + } + + c := NewDpkgdbCataloger() + + actual, err := c.Catalog(s.Resolver) + if err != nil { + t.Fatalf("failed to catalog: %+v", err) + } + + 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)) + } + + // test sources... + for idx := range actual { + a := &actual[idx] + // we will test the sources separately + var sourcesList = make([]string, len(a.Source)) + for i, s := range a.Source { + sourcesList[i] = string(s.Path) + } + a.Source = nil + + for _, d := range deep.Equal(sourcesList, test.sources[a.Name]) { + t.Errorf("diff: %+v", d) + } + } + + // test remaining fields... + for _, d := range deep.Equal(actual, test.expected) { + t.Errorf("diff: %+v", d) + } + + }) + } + +} diff --git a/syft/cataloger/deb/parse_copyright.go b/syft/cataloger/deb/parse_copyright.go new file mode 100644 index 000000000..462e7ac33 --- /dev/null +++ b/syft/cataloger/deb/parse_copyright.go @@ -0,0 +1,38 @@ +package deb + +import ( + "bufio" + "io" + "sort" + "strings" + + "github.com/anchore/syft/internal" +) + +// For more information see: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#license-syntax + +func parseLicensesFromCopyright(reader io.Reader) []string { + findings := internal.NewStringSet() + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "License:") { + candidate := strings.Replace(line, "License:", "", 1) + candidate = strings.TrimSpace(candidate) + if strings.Contains(candidate, " or ") || strings.Contains(candidate, " and ") { + // this is a multi-license summary, ignore this as other recurrent license lines should cover this + continue + } + if candidate != "" && strings.ToLower(candidate) != "none" { + findings.Add(candidate) + } + } + } + + results := findings.ToSlice() + + sort.Strings(results) + + return results +} diff --git a/syft/cataloger/deb/parse_copyright_test.go b/syft/cataloger/deb/parse_copyright_test.go new file mode 100644 index 000000000..71bc2ff72 --- /dev/null +++ b/syft/cataloger/deb/parse_copyright_test.go @@ -0,0 +1,58 @@ +package deb + +import ( + "os" + "testing" + + "github.com/go-test/deep" +) + +func TestParseLicensesFromCopyright(t *testing.T) { + tests := []struct { + fixture string + expected []string + }{ + { + fixture: "test-fixtures/copyright/trilicense", + expected: []string{"GPL-2", "LGPL-2.1", "MPL-1.1"}, + }, + { + fixture: "test-fixtures/copyright/liblzma5", + expected: []string{"Autoconf", "GPL-2", "GPL-2+", "LGPL-2.1+", "PD", "PD-debian", "config-h", "noderivs", "permissive-fsf", "permissive-nowarranty", "probably-PD"}, + }, + { + fixture: "test-fixtures/copyright/libaudit-common", + expected: []string{"GPL-2", "LGPL-2.1"}, + }, + } + + 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 := parseLicensesFromCopyright(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/cataloger/deb/parse_dpkg_info_files.go b/syft/cataloger/deb/parse_dpkg_info_files.go new file mode 100644 index 000000000..74715495e --- /dev/null +++ b/syft/cataloger/deb/parse_dpkg_info_files.go @@ -0,0 +1,30 @@ +package deb + +import ( + "bufio" + "io" + "strings" + + "github.com/anchore/syft/syft/pkg" +) + +func parseDpkgMD5Info(reader io.Reader) []pkg.DpkgFileRecord { + var findings []pkg.DpkgFileRecord + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + line := scanner.Text() + fields := strings.SplitN(line, " ", 2) + if len(fields) == 2 { + path := strings.TrimSpace(fields[1]) + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + findings = append(findings, pkg.DpkgFileRecord{ + Path: path, + MD5: strings.TrimSpace(fields[0]), + }) + } + } + return findings +} diff --git a/syft/cataloger/deb/parse_dpkg_info_files_test.go b/syft/cataloger/deb/parse_dpkg_info_files_test.go new file mode 100644 index 000000000..df252ddc4 --- /dev/null +++ b/syft/cataloger/deb/parse_dpkg_info_files_test.go @@ -0,0 +1,57 @@ +package deb + +import ( + "os" + "testing" + + "github.com/go-test/deep" + + "github.com/anchore/syft/syft/pkg" +) + +func TestMD5SumInfoParsing(t *testing.T) { + tests := []struct { + fixture string + expected []pkg.DpkgFileRecord + }{ + { + 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"}, + }, + }, + } + + 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 := parseDpkgMD5Info(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/cataloger/deb/parse_dpkg_status.go b/syft/cataloger/deb/parse_dpkg_status.go index cd5b3c132..d33cb5e14 100644 --- a/syft/cataloger/deb/parse_dpkg_status.go +++ b/syft/cataloger/deb/parse_dpkg_status.go @@ -2,40 +2,43 @@ package deb import ( "bufio" + "errors" "fmt" "io" + "strconv" "strings" - "github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/pkg" "github.com/mitchellh/mapstructure" ) -// integrity check -var _ common.ParserFn = parseDpkgStatus - var errEndOfPackages = fmt.Errorf("no more packages to read") // parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed. -func parseDpkgStatus(_ string, reader io.Reader) ([]pkg.Package, error) { +func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) { buffedReader := bufio.NewReader(reader) var packages = make([]pkg.Package, 0) - for { + continueProcessing := true + for continueProcessing { entry, err := parseDpkgStatusEntry(buffedReader) if err != nil { - if err == errEndOfPackages { - break + if errors.Is(err, errEndOfPackages) { + continueProcessing = false + } else { + return nil, err } - return nil, err } - packages = append(packages, pkg.Package{ - Name: entry.Package, - Version: entry.Version, - Type: pkg.DebPkg, - MetadataType: pkg.DpkgMetadataType, - Metadata: entry, - }) + + if entry.Package != "" { + packages = append(packages, pkg.Package{ + Name: entry.Package, + Version: entry.Version, + Type: pkg.DebPkg, + MetadataType: pkg.DpkgMetadataType, + Metadata: entry, + }) + } } return packages, nil @@ -43,14 +46,16 @@ func parseDpkgStatus(_ string, 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. func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err error) { - dpkgFields := make(map[string]string) + dpkgFields := make(map[string]interface{}) + var retErr error var key string for { line, err := reader.ReadString('\n') if err != nil { if err == io.EOF { - return pkg.DpkgMetadata{}, errEndOfPackages + retErr = errEndOfPackages + break } return pkg.DpkgMetadata{}, err } @@ -82,18 +87,16 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err err dpkgFields[key] = val default: // parse a new key - if i := strings.Index(line, ":"); i > 0 { - key = strings.TrimSpace(line[0:i]) - val := strings.TrimSpace(line[i+1:]) - - if _, ok := dpkgFields[key]; ok { - return pkg.DpkgMetadata{}, fmt.Errorf("duplicate key discovered: %s", key) - } - - dpkgFields[key] = val - } else { - return pkg.DpkgMetadata{}, fmt.Errorf("cannot parse field from line: '%s'", line) + var val interface{} + key, val, err = handleNewKeyValue(line) + if err != nil { + return pkg.DpkgMetadata{}, err } + + if _, ok := dpkgFields[key]; ok { + return pkg.DpkgMetadata{}, fmt.Errorf("duplicate key discovered: %s", key) + } + dpkgFields[key] = val } } @@ -102,5 +105,29 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err err return pkg.DpkgMetadata{}, err } - return entry, nil + return entry, retErr +} + +// handleNewKeyValue parse a new key-value pair from the given unprocessed line +func handleNewKeyValue(line string) (string, interface{}, error) { + if i := strings.Index(line, ":"); i > 0 { + var key = strings.TrimSpace(line[0:i]) + // mapstruct cant handle "-" + key = strings.ReplaceAll(key, "-", "") + val := strings.TrimSpace(line[i+1:]) + + // further processing of values based on the key that was discovered + switch key { + case "InstalledSize": + numVal, err := strconv.Atoi(val) + if err != nil { + return "", nil, fmt.Errorf("bad installed-size value=%q: %w", val, err) + } + return key, numVal, nil + default: + return key, val, nil + } + } + + return "", nil, fmt.Errorf("cannot parse field from line: '%s'", line) } diff --git a/syft/cataloger/deb/parse_dpkg_status_test.go b/syft/cataloger/deb/parse_dpkg_status_test.go index f08e69374..d7e311e39 100644 --- a/syft/cataloger/deb/parse_dpkg_status_test.go +++ b/syft/cataloger/deb/parse_dpkg_status_test.go @@ -24,17 +24,19 @@ func TestSinglePackage(t *testing.T) { { name: "Test Single Package", expected: pkg.DpkgMetadata{ - Package: "apt", - Source: "apt-dev", - Version: "1.8.2", - Architecture: "amd64", + Package: "apt", + Source: "apt-dev", + Version: "1.8.2", + Architecture: "amd64", + InstalledSize: 4064, + Maintainer: "APT Development Team ", }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - file, err := os.Open("test-fixtures/single") + file, err := os.Open("test-fixtures/status/single") if err != nil { t.Fatal("Unable to read test_fixtures/single: ", err) } @@ -66,15 +68,19 @@ func TestMultiplePackages(t *testing.T) { name: "Test Multiple Package", expected: []pkg.DpkgMetadata{ { - Package: "tzdata", - Version: "2020a-0+deb10u1", - Source: "tzdata-dev", - Architecture: "all", + Package: "tzdata", + Version: "2020a-0+deb10u1", + Source: "tzdata-dev", + Architecture: "all", + InstalledSize: 3036, + Maintainer: "GNU Libc Maintainers ", }, { - Package: "util-linux", - Version: "2.33.1-0.1", - Architecture: "amd64", + Package: "util-linux", + Version: "2.33.1-0.1", + Architecture: "amd64", + InstalledSize: 4327, + Maintainer: "LaMont Jones ", }, }, }, @@ -82,7 +88,7 @@ func TestMultiplePackages(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - file, err := os.Open("test-fixtures/multiple") + file, err := os.Open("test-fixtures/status/multiple") if err != nil { t.Fatal("Unable to read: ", err) } @@ -93,7 +99,7 @@ func TestMultiplePackages(t *testing.T) { } }() - pkgs, err := parseDpkgStatus(file.Name(), file) + pkgs, err := parseDpkgStatus(file) if err != nil { t.Fatal("Unable to read file contents: ", err) } diff --git a/syft/cataloger/deb/test-fixtures/copyright/libaudit-common b/syft/cataloger/deb/test-fixtures/copyright/libaudit-common new file mode 100644 index 000000000..363b056eb --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/copyright/libaudit-common @@ -0,0 +1,43 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: auditd +Source: https://people.redhat.com/sgrubb/audit/ + +Files: * +Copyright: 2012-2016 Steve Grubb + 2006-2012 Rik Faith +License: GPL-2 + +Files: src/libev/* +Copyright: 2007-2009 Marc Alexamder Lehmann +License: GPL-2 + +Files: lib/* +Copyright: 2005-2008 Steve Grubb +License: LGPL-2.1 + The audit daemon's library libaudit.* is released under LGPL + so that it may be linked with 3rd party software. + . + On Debian systems, refer to /usr/share/common-licenses/LGPL-2.1 + for the complete text of the GNU Lesser General Public License. + +Files: debian/* +Copyright: 2007-2011 Philipp Matthias Hahn + 2012-2016 Laurent Bigonville +License: GPL-2 + +License: GPL-2 + This package 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 package 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 complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-1'. diff --git a/syft/cataloger/deb/test-fixtures/copyright/liblzma5 b/syft/cataloger/deb/test-fixtures/copyright/liblzma5 new file mode 100644 index 000000000..332790115 --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/copyright/liblzma5 @@ -0,0 +1,383 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: XZ Utils +Upstream-Contact: + Lasse Collin + http://tukaani.org/xz/lists.html +Source: + http://tukaani.org/xz + http://git.tukaani.org/xz.git +Comment: + XZ Utils is developed and maintained upstream by Lasse Collin. Major + portions are based on code by other authors; see AUTHORS for details. + Most of the source has been put into the public domain, but some files + have not (details below). + . + This file describes the source package. The binary packages contain + some files derived from other works: for example, images in the API + documentation come from Doxygen. +License: + Different licenses apply to different files in this package. Here + is a rough summary of which licenses apply to which parts of this + package (but check the individual files to be sure!): + . + - liblzma is in the public domain. + . + - xz, xzdec, and lzmadec command line tools are in the public + domain unless GNU getopt_long had to be compiled and linked + in from the lib directory. The getopt_long code is under + GNU LGPLv2.1+. + . + - The scripts to grep, diff, and view compressed files have been + adapted from gzip. These scripts and their documentation are + under GNU GPLv2+. + . + - All the documentation in the doc directory and most of the + XZ Utils specific documentation files in other directories + are in the public domain. + . + - Translated messages are in the public domain. + . + - The build system contains public domain files, and files that + are under GNU GPLv2+ or GNU GPLv3+. None of these files end up + in the binaries being built. + . + - Test files and test code in the tests directory, and debugging + utilities in the debug directory are in the public domain. + . + - The extra directory may contain public domain files, and files + that are under various free software licenses. + . + You can do whatever you want with the files that have been put into + the public domain. If you find public domain legally problematic, + take the previous sentence as a license grant. If you still find + the lack of copyright legally problematic, you have too many + lawyers. + . + As usual, this software is provided "as is", without any warranty. + . + If you copy significant amounts of public domain code from XZ Utils + into your project, acknowledging this somewhere in your software is + polite (especially if it is proprietary, non-free software), but + naturally it is not legally required. Here is an example of a good + notice to put into "about box" or into documentation: + . + This software includes code from XZ Utils . + . + The following license texts are included in the following files: + - COPYING.LGPLv2.1: GNU Lesser General Public License version 2.1 + - COPYING.GPLv2: GNU General Public License version 2 + - COPYING.GPLv3: GNU General Public License version 3 + . + Note that the toolchain (compiler, linker etc.) may add some code + pieces that are copyrighted. Thus, it is possible that e.g. liblzma + binary wouldn't actually be in the public domain in its entirety + even though it contains no copyrighted code from the XZ Utils source + package. + . + If you have questions, don't hesitate to ask the author(s) for more + information. + +Files: * +Copyright: 2006-2012, Lasse Collin + 1999-2008, Igor Pavlov + 2006, Ville Koskinen + 1998, Steve Reid + 2000, Wei Dai + 2003, Kevin Springle + 2009, Jonathan Nieder + 2010, Anders F Björklund +License: PD + This file has been put in the public domain. + You can do whatever you want with this file. +Comment: + From: Lasse Collin + To: Jonathan Nieder + Subject: Re: XZ utils for Debian + Date: Sun, 19 Jul 2009 13:28:23 +0300 + Message-Id: <200907191328.23816.lasse.collin@tukaani.org> + . + [...] + . + > AUTHORS, ChangeLog, COPYING, README, THANKS, TODO, + > dos/README, windows/README + . + COPYING says that most docs are in the public domain. Maybe that's not + clear enough, but on the other hand it looks a bit stupid to put + copyright information in tiny and relatively small docs like README. + . + I don't dare to say that _all_ XZ Utils specific docs are in the public + domain unless otherwise mentioned in the file. I'm including PDF files + generated by groff + ps2pdf, and some day I might include Doxygen- + generated HTML docs too. Those don't include any copyright notices, but + it seems likely that groff + ps2pdf or at least Doxygen put some + copyrighted content into the generated files. + +Files: INSTALL NEWS PACKAGERS + windows/README-Windows.txt + windows/INSTALL-Windows.txt +Copyright: 2009-2010, Lasse Collin +License: probably-PD + See the note on AUTHORS, README, and so on above. + +Files: src/scripts/* lib/* extra/scanlzma/scanlzma.c +Copyright: © 1993, Jean-loup Gailly + © 1989-1994, 1996-1999, 2001-2007, Free Software Foundation, Inc. + © 2006 Timo Lindfors + 2005, Charles Levert + 2005, 2009, Lasse Collin + 2009, Andrew Dudman +Other-Authors: Paul Eggert, Ulrich Drepper +License: GPL-2+ + +Files: src/scripts/Makefile.am src/scripts/xzless.1 +Copyright: 2009, Andrew Dudman + 2009, Lasse Collin +License: PD + This file has been put in the public domain. + You can do whatever you want with this file. + +Files: doc/examples/xz_pipe_comp.c doc/examples/xz_pipe_decomp.c +Copyright: 2010, Daniel Mealha Cabrita +License: PD + Not copyrighted -- provided to the public domain. + +Files: lib/getopt.c lib/getopt1.c lib/getopt.in.h +Copyright: © 1987-2007 Free Software Foundation, Inc. +Other-Authors: Ulrich Drepper +License: LGPL-2.1+ + +Files: m4/getopt.m4 m4/posix-shell.m4 +Copyright: © 2002-2006, 2008 Free Software Foundation, Inc. + © 2007-2008 Free Software Foundation, Inc. +Other-Authors: Bruno Haible, Paul Eggert +License: permissive-fsf + +Files: m4/acx_pthread.m4 +Copyright: © 2008, Steven G. Johnson +License: Autoconf + +Files: Doxyfile.in +Copyright: © 1997-2007 by Dimitri van Heesch +Origin: Doxygen 1.4.7 +License: GPL-2 + +Files: src/liblzma/check/crc32_table_?e.h + src/liblzma/check/crc64_table_?e.h + src/liblzma/lzma/fastpos_table.c + src/liblzma/rangecoder/price_table.c +Copyright: none, automatically generated data +Generated-With: + src/liblzma/check/crc32_tablegen.c + src/liblzma/check/crc64_tablegen.c + src/liblzma/lzma/fastpos_tablegen.c + src/liblzma/rangecoder/price_tablegen.c +License: none + No copyright to license. + +Files: .gitignore m4/.gitignore po/.gitignore po/LINGUAS po/POTFILES.in +Copyright: none; these are just short lists. +License: none + No copyright to license. + +Files: tests/compress_prepared_bcj_* +Copyright: 2008-2009, Lasse Collin +Source-Code: tests/bcj_test.c +License: PD + This file has been put into the public domain. + You can do whatever you want with this file. +Comment: + changelog.gz (commit 975d8fd) explains: + . + Recreated the BCJ test files for x86 and SPARC. The old files + were linked with crt*.o, which are copyrighted, and thus the + old test files were not in the public domain as a whole. They + are freely distributable though, but it is better to be careful + and avoid including any copyrighted pieces in the test files. + The new files are just compiled and assembled object files, + and thus don't contain any copyrighted code. + +Files: po/cs.po po/de.po po/fr.po +Copyright: 2010, Marek Černocký + 2010, Andre Noll + 2011, Adrien Nader +License: PD + This file is put in the public domain. + +Files: po/it.po po/pl.po +Copyright: 2009, 2010, Gruppo traduzione italiano di Ubuntu-it + 2010, Lorenzo De Liso + 2009, 2010, 2011, Milo Casagrande + 2011, Jakub Bogusz +License: PD + This file is in the public domain + +Files: INSTALL.generic +Copyright: © 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, + 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +License: permissive-nowarranty + +Files: dos/config.h +Copyright: © 1992, 1993, 1994, 1999, 2000, 2001, 2002, 2005 + Free Software Foundation, Inc. + 2007-2010, Lasse Collin +Other-Authors: Roland McGrath, Akim Demaille, Paul Eggert, + David Mackenzie, Bruno Haible, and many others. +Origin: configure.ac from XZ Utils, + visibility.m4 serial 1 (gettext-0.15), + Autoconf 2.52g +License: config-h + configure.ac: + . + # Author: Lasse Collin + # + # This file has been put into the public domain. + # You can do whatever you want with this file. + . + visibility.m4: + . + dnl Copyright (C) 2005 Free Software Foundation, Inc. + dnl This file is free software; the Free Software Foundation + dnl gives unlimited permission to copy and/or distribute it, + dnl with or without modifications, as long as this notice is preserved. + . + dnl From Bruno Haible. + . + comments from Autoconf 2.52g: + . + # Copyright 1992, 1993, 1994, 1999, 2000, 2001, 2002 + # Free Software Foundation, Inc. + . + [...] + . + # As a special exception, the Free Software Foundation gives unlimited + # permission to copy, distribute and modify the configure scripts that + # are the output of Autoconf. You need not follow the terms of the GNU + # General Public License when using or distributing such scripts, even + # though portions of the text of Autoconf appear in them. The GNU + # General Public License (GPL) does govern all other use of the material + # that constitutes the Autoconf program. + . + On Debian systems, the complete text of the GNU General Public + License version 2 can be found in ‘/usr/share/common-licenses/GPL-2’. + dos/config.h was generated with autoheader, which tells Autoconf to + output a script to generate a config.h file and then runs it. + +Files: po/Makevars +Origin: gettext-runtime/po/Makevars (gettext-0.12) +Copyright: © 2003 Free Software Foundation, Inc. +Authors: Bruno Haible +License: LGPL-2.1+ + The gettext-runtime package is under the LGPL, see files intl/COPYING.LIB-2.0 + and intl/COPYING.LIB-2.1. + . + On Debian systems, the complete text of intl/COPYING.LIB-2.0 from + gettext-runtime 0.12 can be found in ‘/usr/share/common-licenses/LGPL-2’ + and the text of intl/COPYING.LIB-2.1 can be found in + ‘/usr/share/common-licenses/LGPL-2.1’. + . + po/Makevars consists mostly of helpful comments and does not contain a + copyright and license notice. + +Files: COPYING.GPLv2 COPYING.GPLv3 COPYING.LGPLv2.1 +Copyright: © 1989, 1991, 1999, 2007 Free Software Foundation, Inc. +License: noderivs + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Files: debian/* +Copyright: 2009-2012, Jonathan Nieder +License: PD-debian + The Debian packaging files are in the public domain. + You may freely use, modify, distribute, and relicense them. + +License: LGPL-2.1+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + . + 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 Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + . + On Debian systems, the complete text of the GNU Lesser General Public + License version 2.1 can be found in ‘/usr/share/common-licenses/LGPL-2.1’. + +License: GPL-2 + Permission to use, copy, modify, and distribute this software and its + documentation under the terms of the GNU General Public License is + hereby granted. No representations are made about the suitability of + this software for any purpose. It is provided "as is" without express + or implied warranty. See the GNU General Public License for more + details. + . + Documents produced by doxygen are derivative works derived from the + input used in their production; they are not affected by this license. + . + On Debian systems, the complete text of the version of the GNU General + Public License distributed with Doxygen can be found in + ‘/usr/share/common-licenses/GPL-2’. + +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 as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + . + 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 program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + . + On Debian systems, the complete text of the GNU General Public License + version 2 can be found in ‘/usr/share/common-licenses/GPL-2’. + +License: Autoconf + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + . + 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 program. If not, see . + . + As a special exception, the respective Autoconf Macro's copyright owner + gives unlimited permission to copy, distribute and modify the configure + scripts that are the output of Autoconf when processing the Macro. You + need not follow the terms of the GNU General Public License when using + or distributing such scripts, even though portions of the text of the + Macro appear in them. The GNU General Public License (GPL) does govern + all other use of the material that constitutes the Autoconf Macro. + . + This special exception to the GPL applies to versions of the Autoconf + Macro released by the Autoconf Archive. When you make and distribute a + modified version of the Autoconf Macro, you may extend this special + exception to the GPL to apply to your modified version as well. + . + On Debian systems, the complete text of the GNU General Public + License version 3 can be found in ‘/usr/share/common-licenses/GPL-3’. + +License: permissive-fsf + This file is free software; the Free Software Foundation + gives unlimited permission to copy and/or distribute it, + with or without modifications, as long as this notice is preserved. + +License: permissive-nowarranty + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. This file is offered as-is, + without warranty of any kind. diff --git a/syft/cataloger/deb/test-fixtures/copyright/trilicense b/syft/cataloger/deb/test-fixtures/copyright/trilicense new file mode 100644 index 000000000..33d768c58 --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/copyright/trilicense @@ -0,0 +1,13 @@ +Files: src/js/editline/* +Copyright: 1993, John Doe + 1993, Joe Average +License: MPL-1.1 or GPL-2 or LGPL-2.1 + +License: MPL-1.1 + [LICENSE TEXT] + +License: GPL-2 + [LICENSE TEXT] + +License: LGPL-2.1 + [LICENSE TEXT] \ No newline at end of file diff --git a/syft/cataloger/deb/test-fixtures/image-dpkg/Dockerfile b/syft/cataloger/deb/test-fixtures/image-dpkg/Dockerfile new file mode 100644 index 000000000..770e60b56 --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/image-dpkg/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +COPY . . \ No newline at end of file diff --git a/syft/cataloger/deb/test-fixtures/image-dpkg/usr/share/doc/libpam-runtime/copyright b/syft/cataloger/deb/test-fixtures/image-dpkg/usr/share/doc/libpam-runtime/copyright new file mode 100644 index 000000000..363b056eb --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/image-dpkg/usr/share/doc/libpam-runtime/copyright @@ -0,0 +1,43 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: auditd +Source: https://people.redhat.com/sgrubb/audit/ + +Files: * +Copyright: 2012-2016 Steve Grubb + 2006-2012 Rik Faith +License: GPL-2 + +Files: src/libev/* +Copyright: 2007-2009 Marc Alexamder Lehmann +License: GPL-2 + +Files: lib/* +Copyright: 2005-2008 Steve Grubb +License: LGPL-2.1 + The audit daemon's library libaudit.* is released under LGPL + so that it may be linked with 3rd party software. + . + On Debian systems, refer to /usr/share/common-licenses/LGPL-2.1 + for the complete text of the GNU Lesser General Public License. + +Files: debian/* +Copyright: 2007-2011 Philipp Matthias Hahn + 2012-2016 Laurent Bigonville +License: GPL-2 + +License: GPL-2 + This package 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 package 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 complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-1'. diff --git a/syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.md5sums b/syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.md5sums new file mode 100644 index 000000000..70a9410cf --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/info/libpam-runtime.md5sums @@ -0,0 +1,4 @@ +55f905631797551d4d936a34c7e73474 lib/x86_64-linux-gnu/libz.so.1.2.11 +cede84bda30d2380217f97753c8ccf3a usr/share/doc/zlib1g/changelog.Debian.gz +f3c9dafa6da7992c47328b4464f6d122 usr/share/doc/zlib1g/changelog.gz +a4fae96070439a5209a62ae5b8017ab2 usr/share/doc/zlib1g/copyright \ No newline at end of file diff --git a/syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/status b/syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/status new file mode 100644 index 000000000..692a28706 --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/image-dpkg/var/lib/dpkg/status @@ -0,0 +1,21 @@ +Package: libpam-runtime +Status: install ok installed +Priority: required +Section: admin +Installed-Size: 1016 +Maintainer: Steve Langasek +Architecture: all +Multi-Arch: foreign +Source: pam +Version: 1.1.8-3.6 +Replaces: libpam0g-dev, libpam0g-util +Depends: debconf (>= 0.5) | debconf-2.0, debconf (>= 1.5.19) | cdebconf, libpam-modules (>= 1.0.1-6) +Conflicts: libpam0g-util +Conffiles: + /etc/pam.conf 87fc76f18e98ee7d3848f6b81b3391e5 + /etc/pam.d/other 31aa7f2181889ffb00b87df4126d1701 +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. +Homepage: http://www.linux-pam.org/ \ No newline at end of file diff --git a/syft/cataloger/deb/test-fixtures/info/zlib1g.md5sums b/syft/cataloger/deb/test-fixtures/info/zlib1g.md5sums new file mode 100644 index 000000000..70a9410cf --- /dev/null +++ b/syft/cataloger/deb/test-fixtures/info/zlib1g.md5sums @@ -0,0 +1,4 @@ +55f905631797551d4d936a34c7e73474 lib/x86_64-linux-gnu/libz.so.1.2.11 +cede84bda30d2380217f97753c8ccf3a usr/share/doc/zlib1g/changelog.Debian.gz +f3c9dafa6da7992c47328b4464f6d122 usr/share/doc/zlib1g/changelog.gz +a4fae96070439a5209a62ae5b8017ab2 usr/share/doc/zlib1g/copyright \ No newline at end of file diff --git a/syft/cataloger/deb/test-fixtures/multiple b/syft/cataloger/deb/test-fixtures/status/multiple similarity index 100% rename from syft/cataloger/deb/test-fixtures/multiple rename to syft/cataloger/deb/test-fixtures/status/multiple diff --git a/syft/cataloger/deb/test-fixtures/single b/syft/cataloger/deb/test-fixtures/status/single similarity index 100% rename from syft/cataloger/deb/test-fixtures/single rename to syft/cataloger/deb/test-fixtures/status/single diff --git a/syft/pkg/dpkg_metadata.go b/syft/pkg/dpkg_metadata.go index 63db0f7e4..f1e80802e 100644 --- a/syft/pkg/dpkg_metadata.go +++ b/syft/pkg/dpkg_metadata.go @@ -8,11 +8,18 @@ import ( // DpkgMetadata represents all captured data for a Debian package DB entry; available fields are described // at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section. type DpkgMetadata struct { - Package string `mapstructure:"Package" json:"package"` - Source string `mapstructure:"Source" json:"source"` - Version string `mapstructure:"Version" json:"version"` - Architecture string `mapstructure:"Architecture" json:"architecture"` - // TODO: consider keeping the remaining values as an embedded map + Package string `mapstructure:"Package" json:"package"` + Source string `mapstructure:"Source" json:"source"` + Version string `mapstructure:"Version" json:"version"` + Architecture string `mapstructure:"Architecture" json:"architecture"` + Maintainer string `mapstructure:"Maintainer" json:"maintainer"` + InstalledSize int `mapstructure:"InstalledSize" json:"installedSize"` + Files []DpkgFileRecord `json:"files"` +} + +type DpkgFileRecord struct { + Path string `json:"path"` + MD5 string `json:"md5"` } func (m DpkgMetadata) PackageURL(d distro.Distro) string { diff --git a/syft/presenter/json/artifact.go b/syft/presenter/json/artifact.go index f6157015e..3c7957cef 100644 --- a/syft/presenter/json/artifact.go +++ b/syft/presenter/json/artifact.go @@ -11,6 +11,7 @@ type Artifact struct { Type string `json:"type"` FoundBy []string `json:"foundBy"` Locations Locations `json:"locations,omitempty"` + Licenses []string `json:"licenses"` Metadata interface{} `json:"metadata,omitempty"` } @@ -26,6 +27,7 @@ func NewArtifact(p *pkg.Package, s scope.Scope) (Artifact, error) { Type: string(p.Type), FoundBy: []string{p.FoundBy}, Locations: locations, + Licenses: p.Licenses, Metadata: p.Metadata, }, nil } diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index e73fecc86..c5fefb539 100644 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -9,7 +9,8 @@ ], "locations": [ "/some/path/pkg1" - ] + ], + "licenses": null }, { "name": "package-2", @@ -20,7 +21,8 @@ ], "locations": [ "/some/path/pkg1" - ] + ], + "licenses": null } ], "source": { diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index 62d379db2..525c74cf0 100644 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -12,7 +12,8 @@ "path": "/somefile-1.txt", "layerIndex": 0 } - ] + ], + "licenses": null }, { "name": "package-2", @@ -26,7 +27,8 @@ "path": "/somefile-2.txt", "layerIndex": 1 } - ] + ], + "licenses": null } ], "source": {