diff --git a/internal/presenter/packages/model/spdx22/document.go b/internal/presenter/packages/model/spdx22/document.go index 995ae8f7d..1c9ab1f2a 100644 --- a/internal/presenter/packages/model/spdx22/document.go +++ b/internal/presenter/packages/model/spdx22/document.go @@ -40,4 +40,6 @@ type Document struct { Files []File `json:"files,omitempty"` // Snippets referenced in the SPDX document Snippets []Snippet `json:"snippets,omitempty"` + // Relationships referenced in the SPDX document + Relationships []Relationship `json:"relationships,omitempty"` } diff --git a/internal/presenter/packages/model/spdx22/file.go b/internal/presenter/packages/model/spdx22/file.go index 913642493..eefe05472 100644 --- a/internal/presenter/packages/model/spdx22/file.go +++ b/internal/presenter/packages/model/spdx22/file.go @@ -20,22 +20,22 @@ type File struct { Item // (At least one is required.) The checksum property provides a mechanism that can be used to verify that the // contents of a File or Package have not changed. - Checksums []Checksum `json:"checksums"` + Checksums []Checksum `json:"checksums,omitempty"` // This field provides a place for the SPDX file creator to record file contributors. Contributors could include // names of copyright holders and/or authors who may not be copyright holders yet contributed to the file content. - FileContributors []string `json:"fileContributors"` + FileContributors []string `json:"fileContributors,omitempty"` // Each element is a SPDX ID for a File. - FileDependencies []string `json:"fileDependencies"` + FileDependencies []string `json:"fileDependencies,omitempty"` // The name of the file relative to the root of the package. FileName string `json:"fileName"` // The type of the file - FileTypes []string `json:"fileTypes"` + FileTypes []string `json:"fileTypes,omitempty"` // This field provides a place for the SPDX file creator to record potential legal notices found in the file. // This may or may not include copyright statements. NoticeText string `json:"noticeText,omitempty"` // Indicates the project in which the SpdxElement originated. Tools must preserve doap:homepage and doap:name // properties and the URI (if one is known) of doap:Project resources that are values of this property. All other // properties of doap:Projects are not directly supported by SPDX and may be dropped when translating to or - // from some SPDX formats. - ArtifactOf []string `json:"artifactOf"` + // from some SPDX formats(deprecated). + ArtifactOf []string `json:"artifactOf,omitempty"` } diff --git a/internal/presenter/packages/model/spdx22/relationship.go b/internal/presenter/packages/model/spdx22/relationship.go index c1f52204f..ca99a879e 100644 --- a/internal/presenter/packages/model/spdx22/relationship.go +++ b/internal/presenter/packages/model/spdx22/relationship.go @@ -1,11 +1,13 @@ package spdx22 type Relationship struct { - // SPDX ID for SpdxElement. A related SpdxElement. - RelatedSpdxElement string `json:"relatedSpdxElement"` + // Id to which the SPDX element is related + SpdxElementID string `json:"spdxElementId"` // Describes the type of relationship between two SPDX elements. RelationshipType RelationshipType `json:"relationshipType"` - Comment string `json:"comment,omitempty"` + // SPDX ID for SpdxElement. A related SpdxElement. + RelatedSpdxElement string `json:"relatedSpdxElement"` + Comment string `json:"comment,omitempty"` } // source: https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/ diff --git a/internal/presenter/packages/spdx_helpers.go b/internal/presenter/packages/spdx_helpers.go index a847a2430..c175e0da9 100644 --- a/internal/presenter/packages/spdx_helpers.go +++ b/internal/presenter/packages/spdx_helpers.go @@ -1,7 +1,9 @@ package packages import ( + "crypto/sha256" "fmt" + "path/filepath" "strings" "github.com/anchore/syft/internal/presenter/packages/model/spdx22" @@ -29,6 +31,43 @@ func getSPDXExternalRefs(p *pkg.Package) (externalRefs []spdx22.ExternalRef) { return externalRefs } +func getSPDXFiles(packageSpdxID string, p *pkg.Package) (files []spdx22.File, fileIDs []string, relationships []spdx22.Relationship) { + files = make([]spdx22.File, 0) + fileIDs = make([]string, 0) + relationships = make([]spdx22.Relationship, 0) + + pkgFileOwner, ok := p.Metadata.(pkg.FileOwner) + if !ok { + return files, fileIDs, relationships + } + + for _, ownedFilePath := range pkgFileOwner.OwnedFiles() { + baseFileName := filepath.Base(ownedFilePath) + pathHash := sha256.Sum256([]byte(ownedFilePath)) + fileSpdxID := spdx22.ElementID(fmt.Sprintf("File-%s-%x", p.Name, pathHash)).String() + + fileIDs = append(fileIDs, fileSpdxID) + + files = append(files, spdx22.File{ + FileName: ownedFilePath, + Item: spdx22.Item{ + Element: spdx22.Element{ + SPDXID: fileSpdxID, + Name: baseFileName, + }, + }, + }) + + relationships = append(relationships, spdx22.Relationship{ + SpdxElementID: packageSpdxID, + RelationshipType: spdx22.ContainsRelationship, + RelatedSpdxElement: fileSpdxID, + }) + } + + return files, fileIDs, relationships +} + func getSPDXLicense(p *pkg.Package) string { // source: https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license // The options to populate this field are limited to: diff --git a/internal/presenter/packages/spdx_json_presenter.go b/internal/presenter/packages/spdx_json_presenter.go index da90c3626..0e34f4d9c 100644 --- a/internal/presenter/packages/spdx_json_presenter.go +++ b/internal/presenter/packages/spdx_json_presenter.go @@ -39,6 +39,7 @@ func (pres *SPDXJsonPresenter) Present(output io.Writer) error { return enc.Encode(&doc) } +// newSPDXJsonDocument creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results. func newSPDXJsonDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) spdx22.Document { var name string switch srcMetadata.Scheme { @@ -48,6 +49,8 @@ func newSPDXJsonDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) spdx name = srcMetadata.Path } + packages, files, relationships := newSPDXJsonElements(catalog) + return spdx22.Document{ Element: spdx22.Element{ SPDXID: spdx22.ElementID("DOCUMENT").String(), @@ -65,22 +68,34 @@ func newSPDXJsonDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) spdx }, DataLicense: "CC0-1.0", DocumentNamespace: fmt.Sprintf("https://anchore.com/syft/image/%s", srcMetadata.ImageMetadata.UserInput), - Packages: newSPDXJsonPackages(catalog), + Packages: packages, + Files: files, + Relationships: relationships, } } -func newSPDXJsonPackages(catalog *pkg.Catalog) []spdx22.Package { - results := make([]spdx22.Package, 0) +func newSPDXJsonElements(catalog *pkg.Catalog) ([]spdx22.Package, []spdx22.File, []spdx22.Relationship) { + packages := make([]spdx22.Package, 0) + relationships := make([]spdx22.Relationship, 0) + files := make([]spdx22.File, 0) + for _, p := range catalog.Sorted() { license := getSPDXLicense(p) + packageSpdxID := spdx22.ElementID(fmt.Sprintf("Package-%+v-%s-%s", p.Type, p.Name, p.Version)).String() + + packageFiles, fileIDs, packageFileRelationships := getSPDXFiles(packageSpdxID, p) + files = append(files, packageFiles...) + + relationships = append(relationships, packageFileRelationships...) // note: the license concluded and declared should be the same since we are collecting license information // from the project data itself (the installed package files). - results = append(results, spdx22.Package{ + packages = append(packages, spdx22.Package{ Description: getSPDXDescription(p), DownloadLocation: getSPDXDownloadLocation(p), ExternalRefs: getSPDXExternalRefs(p), FilesAnalyzed: false, + HasFiles: fileIDs, Homepage: getSPDXHomepage(p), LicenseDeclared: license, // The Declared License is what the authors of a project believe govern the package Originator: getSPDXOriginator(p), @@ -89,11 +104,12 @@ func newSPDXJsonPackages(catalog *pkg.Catalog) []spdx22.Package { Item: spdx22.Item{ LicenseConcluded: license, // The Concluded License field is the license the SPDX file creator believes governs the package Element: spdx22.Element{ - SPDXID: spdx22.ElementID(fmt.Sprintf("Package-%+v-%s-%s", p.Type, p.Name, p.Version)).String(), + SPDXID: packageSpdxID, Name: p.Name, }, }, }) } - return results + + return packages, files, relationships } diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirectoryPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirectoryPresenter.golden index 5a58549c9..2abdef222 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirectoryPresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestJSONDirectoryPresenter.golden @@ -27,6 +27,11 @@ "author": "", "authorEmail": "", "platform": "", + "files": [ + { + "path": "/some/path/pkg1/depedencies/foo" + } + ], "sitePackagesRootPath": "" } }, diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestJSONImagePresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestJSONImagePresenter.golden index 092d50b3e..abcda45b5 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestJSONImagePresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestJSONImagePresenter.golden @@ -9,7 +9,7 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" + "layerID": "sha256:ffb5e9eaa453a002110719d12c294960117ca2903953d1faa40f01dc3f77045c" } ], "licenses": [ @@ -40,7 +40,7 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" + "layerID": "sha256:8463854829fc53d47b9dcdf7ee79fe7eb4ca7933c910f67f8521412f7a2f5c21" } ], "licenses": [], @@ -67,7 +67,7 @@ "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:5900c94a5bc1e083aa24ad1a223bf6eb9910dc8a6b01cb979ec306cb91709ea1", + "imageID": "sha256:112851310e48e604f7379e2a3acddab50e91ce926edacb598a532e60ff6b776a", "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:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59", + "digest": "sha256:ffb5e9eaa453a002110719d12c294960117ca2903953d1faa40f01dc3f77045c", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec", + "digest": "sha256:8463854829fc53d47b9dcdf7ee79fe7eb4ca7933c910f67f8521412f7a2f5c21", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NjUsImRpZ2VzdCI6InNoYTI1Njo1OTAwYzk0YTViYzFlMDgzYWEyNGFkMWEyMjNiZjZlYjk5MTBkYzhhNmIwMWNiOTc5ZWMzMDZjYjkxNzA5ZWExIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDQtMDZUMTk6MTM6NTIuNTI0Mzc4WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTA0LTA2VDE5OjEzOjUyLjQ1ODIwNjFaIiwiY3JlYXRlZF9ieSI6IkFERCBmaWxlLTEudHh0IC9zb21lZmlsZS0xLnR4dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifSx7ImNyZWF0ZWQiOiIyMDIxLTA0LTA2VDE5OjEzOjUyLjUyNDM3OFoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMi50eHQgL3NvbWVmaWxlLTIudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OmZiNmJlZWNiNzViMzlmNGJiODEzZGJmMTc3ZTUwMWVkZDVkZGIzZTY5YmI0NWNlZGViNzhjNjc2ZWUxYjdhNTkiLCJzaGEyNTY6MzE5YjU4OGNlNjQyNTNhODdiNTMzYzhlZDAxY2YwMDI1ZTBlYWM5OGU3YjUxNmUxMjUzMjk1N2UxMjQ0ZmRlYyJdfX0=", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjoxMTI4NTEzMTBlNDhlNjA0ZjczNzllMmEzYWNkZGFiNTBlOTFjZTkyNmVkYWNiNTk4YTUzMmU2MGZmNmI3NzZhIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmZmI1ZTllYWE0NTNhMDAyMTEwNzE5ZDEyYzI5NDk2MDExN2NhMjkwMzk1M2QxZmFhNDBmMDFkYzNmNzcwNDVjIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2Ojg0NjM4NTQ4MjlmYzUzZDQ3YjlkY2RmN2VlNzlmZTdlYjRjYTc5MzNjOTEwZjY3Zjg1MjE0MTJmN2EyZjVjMjEifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDktMDhUMTc6MjE6NTguODk2NTI5MTkyWiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTA5LTA4VDE3OjIxOjU4Ljg3OTY5MDgyNFoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjEtMDktMDhUMTc6MjE6NTguODk2NTI5MTkyWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZmZiNWU5ZWFhNDUzYTAwMjExMDcxOWQxMmMyOTQ5NjAxMTdjYTI5MDM5NTNkMWZhYTQwZjAxZGMzZjc3MDQ1YyIsInNoYTI1Njo4NDYzODU0ODI5ZmM1M2Q0N2I5ZGNkZjdlZTc5ZmU3ZWI0Y2E3OTMzYzkxMGY2N2Y4NTIxNDEyZjdhMmY1YzIxIl19fQ==", "repoDigests": [], "scope": "Squashed" } diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONDirectoryPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONDirectoryPresenter.golden index 169f9bd8c..249517449 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONDirectoryPresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONDirectoryPresenter.golden @@ -3,12 +3,12 @@ "name": "/some/path", "spdxVersion": "SPDX-2.2", "creationInfo": { - "created": "2021-06-23T17:48:32.734847Z", + "created": "2021-09-16T20:44:35.198887Z", "creators": [ "Organization: Anchore, Inc", "Tool: syft-[not provided]" ], - "licenseListVersion": "3.13" + "licenseListVersion": "3.14" }, "dataLicense": "CC0-1.0", "documentNamespace": "https://anchore.com/syft/image/", @@ -31,6 +31,9 @@ } ], "filesAnalyzed": false, + "hasFiles": [ + "SPDXRef-File-package-1-04cd22424378dcd6c77fce08beb52493b5494a60ea5e1f9bdf9b16dc0cacffe9" + ], "licenseDeclared": "MIT", "sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1", "versionInfo": "1.0.1" @@ -57,5 +60,20 @@ "sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1", "versionInfo": "2.0.1" } + ], + "files": [ + { + "SPDXID": "SPDXRef-File-package-1-04cd22424378dcd6c77fce08beb52493b5494a60ea5e1f9bdf9b16dc0cacffe9", + "name": "foo", + "licenseConcluded": "", + "fileName": "/some/path/pkg1/depedencies/foo" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-Package-python-package-1-1.0.1", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-File-package-1-04cd22424378dcd6c77fce08beb52493b5494a60ea5e1f9bdf9b16dc0cacffe9" + } ] } diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONImagePresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONImagePresenter.golden index f9dc50d19..8906ef161 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONImagePresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestSPDXJSONImagePresenter.golden @@ -3,12 +3,12 @@ "name": "user-image-input", "spdxVersion": "SPDX-2.2", "creationInfo": { - "created": "2021-06-23T17:48:32.7379Z", + "created": "2021-09-16T20:44:35.203911Z", "creators": [ "Organization: Anchore, Inc", "Tool: syft-[not provided]" ], - "licenseListVersion": "3.13" + "licenseListVersion": "3.14" }, "dataLicense": "CC0-1.0", "documentNamespace": "https://anchore.com/syft/image/user-image-input", diff --git a/internal/presenter/packages/test-fixtures/snapshot/TestTextImagePresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestTextImagePresenter.golden index 4ab3a446e..eb23e8bca 100644 --- a/internal/presenter/packages/test-fixtures/snapshot/TestTextImagePresenter.golden +++ b/internal/presenter/packages/test-fixtures/snapshot/TestTextImagePresenter.golden @@ -1,11 +1,11 @@ [Image] Layer: 0 - Digest: sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59 + Digest: sha256:ffb5e9eaa453a002110719d12c294960117ca2903953d1faa40f01dc3f77045c Size: 22 MediaType: application/vnd.docker.image.rootfs.diff.tar.gzip Layer: 1 - Digest: sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec + Digest: sha256:8463854829fc53d47b9dcdf7ee79fe7eb4ca7933c910f67f8521412f7a2f5c21 Size: 16 MediaType: application/vnd.docker.image.rootfs.diff.tar.gzip 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 a5985f95c..249e2544c 100644 Binary files a/internal/presenter/packages/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/internal/presenter/packages/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/internal/presenter/packages/utils_test.go b/internal/presenter/packages/utils_test.go index c23d4caf8..9c6a88242 100644 --- a/internal/presenter/packages/utils_test.go +++ b/internal/presenter/packages/utils_test.go @@ -158,6 +158,11 @@ func presenterDirectoryInput(t testing.TB) (*pkg.Catalog, source.Metadata, *dist Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", + Files: []pkg.PythonFileRecord{ + { + Path: "/some/path/pkg1/depedencies/foo", + }, + }, }, PURL: "a-purl-2", CPEs: []pkg.CPE{ diff --git a/syft/pkg/apk_metadata.go b/syft/pkg/apk_metadata.go index 84b148645..0578f3061 100644 --- a/syft/pkg/apk_metadata.go +++ b/syft/pkg/apk_metadata.go @@ -11,7 +11,7 @@ import ( const ApkDbGlob = "**/lib/apk/db/installed" -var _ fileOwner = (*ApkMetadata)(nil) +var _ FileOwner = (*ApkMetadata)(nil) // ApkMetadata represents all captured data for a Alpine DB package entry. // See the following sources for more information: @@ -63,7 +63,7 @@ func (m ApkMetadata) PackageURL() string { return pURL.ToString() } -func (m ApkMetadata) ownedFiles() (result []string) { +func (m ApkMetadata) OwnedFiles() (result []string) { s := strset.New() for _, f := range m.Files { if f.Path != "" { diff --git a/syft/pkg/apk_metadata_test.go b/syft/pkg/apk_metadata_test.go index ef58401a7..791d28cfd 100644 --- a/syft/pkg/apk_metadata_test.go +++ b/syft/pkg/apk_metadata_test.go @@ -73,7 +73,7 @@ func TestApkMetadata_pURL(t *testing.T) { } } -func TestApkMetadata_fileOwner(t *testing.T) { +func TestApkMetadata_FileOwner(t *testing.T) { tests := []struct { metadata ApkMetadata expected []string @@ -107,7 +107,7 @@ func TestApkMetadata_fileOwner(t *testing.T) { t.Run(strings.Join(test.expected, ","), func(t *testing.T) { var i interface{} i = test.metadata - actual := i.(fileOwner).ownedFiles() + actual := i.(FileOwner).OwnedFiles() for _, d := range deep.Equal(test.expected, actual) { t.Errorf("diff: %+v", d) } diff --git a/syft/pkg/dpkg_metadata.go b/syft/pkg/dpkg_metadata.go index 8f2245c55..b17bc385a 100644 --- a/syft/pkg/dpkg_metadata.go +++ b/syft/pkg/dpkg_metadata.go @@ -12,7 +12,7 @@ import ( const DpkgDbGlob = "**/var/lib/dpkg/{status,status.d/**}" -var _ fileOwner = (*DpkgMetadata)(nil) +var _ FileOwner = (*DpkgMetadata)(nil) // 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. @@ -55,7 +55,7 @@ func (m DpkgMetadata) PackageURL(d *distro.Distro) string { return pURL.ToString() } -func (m DpkgMetadata) ownedFiles() (result []string) { +func (m DpkgMetadata) OwnedFiles() (result []string) { s := strset.New() for _, f := range m.Files { if f.Path != "" { diff --git a/syft/pkg/dpkg_metadata_test.go b/syft/pkg/dpkg_metadata_test.go index 81a0cb665..36fa1a256 100644 --- a/syft/pkg/dpkg_metadata_test.go +++ b/syft/pkg/dpkg_metadata_test.go @@ -54,7 +54,7 @@ func TestDpkgMetadata_pURL(t *testing.T) { } } -func TestDpkgMetadata_fileOwner(t *testing.T) { +func TestDpkgMetadata_FileOwner(t *testing.T) { tests := []struct { metadata DpkgMetadata expected []string @@ -88,7 +88,7 @@ func TestDpkgMetadata_fileOwner(t *testing.T) { t.Run(strings.Join(test.expected, ","), func(t *testing.T) { var i interface{} i = test.metadata - actual := i.(fileOwner).ownedFiles() + actual := i.(FileOwner).OwnedFiles() for _, d := range deep.Equal(test.expected, actual) { t.Errorf("diff: %+v", d) } diff --git a/syft/pkg/file_owner.go b/syft/pkg/file_owner.go index 24520c304..1e13cf614 100644 --- a/syft/pkg/file_owner.go +++ b/syft/pkg/file_owner.go @@ -1,5 +1,9 @@ package pkg -type fileOwner interface { - ownedFiles() []string +// FileOwner is the interface that wraps OwnedFiles method. +// +// OwnedFiles returns a list of files that a piece of +// package Metadata indicates are owned by the package. +type FileOwner interface { + OwnedFiles() []string } diff --git a/syft/pkg/ownership_by_files_relationship.go b/syft/pkg/ownership_by_files_relationship.go index f235557a4..4e2b4d314 100644 --- a/syft/pkg/ownership_by_files_relationship.go +++ b/syft/pkg/ownership_by_files_relationship.go @@ -55,11 +55,11 @@ func findOwnershipByFilesRelationships(catalog *Catalog) map[ID]map[ID]*strset.S } // check to see if this is a file owner - pkgFileOwner, ok := candidateOwnerPkg.Metadata.(fileOwner) + pkgFileOwner, ok := candidateOwnerPkg.Metadata.(FileOwner) if !ok { continue } - for _, ownedFilePath := range pkgFileOwner.ownedFiles() { + for _, ownedFilePath := range pkgFileOwner.OwnedFiles() { if matchesAny(ownedFilePath, globsForbiddenFromBeingOwned) { // we skip over known exceptions to file ownership, such as the RPM package owning // the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended diff --git a/syft/pkg/python_package_metadata.go b/syft/pkg/python_package_metadata.go index c1e752d21..f059dfac3 100644 --- a/syft/pkg/python_package_metadata.go +++ b/syft/pkg/python_package_metadata.go @@ -6,7 +6,7 @@ import ( "github.com/scylladb/go-set/strset" ) -var _ fileOwner = (*PythonPackageMetadata)(nil) +var _ FileOwner = (*PythonPackageMetadata)(nil) // PythonFileDigest represents the file metadata for a single file attributed to a python package. type PythonFileDigest struct { @@ -34,7 +34,7 @@ type PythonPackageMetadata struct { TopLevelPackages []string `json:"topLevelPackages,omitempty"` } -func (m PythonPackageMetadata) ownedFiles() (result []string) { +func (m PythonPackageMetadata) OwnedFiles() (result []string) { s := strset.New() for _, f := range m.Files { if f.Path != "" { diff --git a/syft/pkg/python_package_metadata_test.go b/syft/pkg/python_package_metadata_test.go index c2cd37817..5364d3329 100644 --- a/syft/pkg/python_package_metadata_test.go +++ b/syft/pkg/python_package_metadata_test.go @@ -7,7 +7,7 @@ import ( "github.com/go-test/deep" ) -func TestPythonMetadata_fileOwner(t *testing.T) { +func TestPythonMetadata_FileOwner(t *testing.T) { tests := []struct { metadata PythonPackageMetadata expected []string @@ -41,7 +41,7 @@ func TestPythonMetadata_fileOwner(t *testing.T) { t.Run(strings.Join(test.expected, ","), func(t *testing.T) { var i interface{} i = test.metadata - actual := i.(fileOwner).ownedFiles() + actual := i.(FileOwner).OwnedFiles() for _, d := range deep.Equal(test.expected, actual) { t.Errorf("diff: %+v", d) } diff --git a/syft/pkg/rpmdb_metadata.go b/syft/pkg/rpmdb_metadata.go index b6460c824..e48eb723c 100644 --- a/syft/pkg/rpmdb_metadata.go +++ b/syft/pkg/rpmdb_metadata.go @@ -15,7 +15,7 @@ import ( const RpmDbGlob = "**/var/lib/rpm/Packages" -var _ fileOwner = (*RpmdbMetadata)(nil) +var _ FileOwner = (*RpmdbMetadata)(nil) // RpmdbMetadata represents all captured data for a RPM DB package entry. type RpmdbMetadata struct { @@ -79,7 +79,7 @@ func (m RpmdbMetadata) PackageURL(d *distro.Distro) string { return pURL.ToString() } -func (m RpmdbMetadata) ownedFiles() (result []string) { +func (m RpmdbMetadata) OwnedFiles() (result []string) { s := strset.New() for _, f := range m.Files { if f.Path != "" { diff --git a/syft/pkg/rpmdb_metadata_test.go b/syft/pkg/rpmdb_metadata_test.go index 7eded2046..6e4fa7cce 100644 --- a/syft/pkg/rpmdb_metadata_test.go +++ b/syft/pkg/rpmdb_metadata_test.go @@ -56,7 +56,7 @@ func TestRpmMetadata_pURL(t *testing.T) { } } -func TestRpmMetadata_fileOwner(t *testing.T) { +func TestRpmMetadata_FileOwner(t *testing.T) { tests := []struct { metadata RpmdbMetadata expected []string @@ -90,7 +90,7 @@ func TestRpmMetadata_fileOwner(t *testing.T) { t.Run(strings.Join(test.expected, ","), func(t *testing.T) { var i interface{} i = test.metadata - actual := i.(fileOwner).ownedFiles() + actual := i.(FileOwner).OwnedFiles() for _, d := range deep.Equal(test.expected, actual) { t.Errorf("diff: %+v", d) }