mirror of
https://github.com/anchore/syft.git
synced 2026-10-31 22:46:36 +01:00
Introduce relationships as first-class objects (#607)
* migrate pkg.ID and pkg.Relationship to artifact package Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * return relationships from tasks Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix more tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add artifact.Identifiable by Identity() method Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix linting Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * remove catalog ID assignment Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * adjust spdx helpers to use copy of packages Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * stabilize package ID relative to encode-decode format cycles Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename Identity() to ID() Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * use zero value for nils in ID generation Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * enable source.Location to be identifiable Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * hoist up package relationship discovery to analysis stage Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update ownership-by-file-overlap relationship description Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add test reminders to put new relationships under test Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * adjust PHP composer.lock parser function to return relationships Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
@@ -2,7 +2,7 @@ package spdxhelpers
|
||||
|
||||
import "github.com/anchore/syft/syft/pkg"
|
||||
|
||||
func Description(p *pkg.Package) string {
|
||||
func Description(p pkg.Package) string {
|
||||
if hasMetadata(p) {
|
||||
switch metadata := p.Metadata.(type) {
|
||||
case pkg.ApkMetadata:
|
||||
@@ -14,10 +14,6 @@ func Description(p *pkg.Package) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func packageExists(p *pkg.Package) bool {
|
||||
return p != nil
|
||||
}
|
||||
|
||||
func hasMetadata(p *pkg.Package) bool {
|
||||
return packageExists(p) && p.Metadata != nil
|
||||
func hasMetadata(p pkg.Package) bool {
|
||||
return p.Metadata != nil
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func Test_Description(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, Description(&test.input))
|
||||
assert.Equal(t, test.expected, Description(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package spdxhelpers
|
||||
|
||||
import "github.com/anchore/syft/syft/pkg"
|
||||
|
||||
func DownloadLocation(p *pkg.Package) string {
|
||||
func DownloadLocation(p pkg.Package) string {
|
||||
// 3.7: Package Download Location
|
||||
// Cardinality: mandatory, one
|
||||
// NONE if there is no download location whatsoever.
|
||||
|
||||
@@ -48,7 +48,7 @@ func Test_DownloadLocation(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, DownloadLocation(&test.input))
|
||||
assert.Equal(t, test.expected, DownloadLocation(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,9 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func ExternalRefs(p *pkg.Package) (externalRefs []model.ExternalRef) {
|
||||
func ExternalRefs(p pkg.Package) (externalRefs []model.ExternalRef) {
|
||||
externalRefs = make([]model.ExternalRef, 0)
|
||||
|
||||
if !packageExists(p) {
|
||||
return externalRefs
|
||||
}
|
||||
|
||||
for _, c := range p.CPEs {
|
||||
externalRefs = append(externalRefs, model.ExternalRef{
|
||||
ReferenceCategory: model.SecurityReferenceCategory,
|
||||
|
||||
@@ -39,7 +39,7 @@ func Test_ExternalRefs(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.expected, ExternalRefs(&test.input))
|
||||
assert.ElementsMatch(t, test.expected, ExternalRefs(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Files(packageSpdxID string, p *pkg.Package) (files []model.File, fileIDs []string, relationships []model.Relationship) {
|
||||
func Files(packageSpdxID string, p pkg.Package) (files []model.File, fileIDs []string, relationships []model.Relationship) {
|
||||
files = make([]model.File, 0)
|
||||
fileIDs = make([]string, 0)
|
||||
relationships = make([]model.Relationship, 0)
|
||||
|
||||
@@ -2,7 +2,7 @@ package spdxhelpers
|
||||
|
||||
import "github.com/anchore/syft/syft/pkg"
|
||||
|
||||
func Homepage(p *pkg.Package) string {
|
||||
func Homepage(p pkg.Package) string {
|
||||
if hasMetadata(p) {
|
||||
switch metadata := p.Metadata.(type) {
|
||||
case pkg.GemMetadata:
|
||||
|
||||
@@ -50,7 +50,7 @@ func Test_Homepage(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, Homepage(&test.input))
|
||||
assert.Equal(t, test.expected, Homepage(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func License(p *pkg.Package) string {
|
||||
func License(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:
|
||||
// A valid SPDX License Expression as defined in Appendix IV;
|
||||
@@ -17,7 +17,7 @@ func License(p *pkg.Package) string {
|
||||
// (ii) the SPDX file creator has made no attempt to determine this field; or
|
||||
// (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so).
|
||||
|
||||
if !packageExists(p) || len(p.Licenses) == 0 {
|
||||
if len(p.Licenses) == 0 {
|
||||
return "NONE"
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ func Test_License(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, License(&test.input))
|
||||
assert.Equal(t, test.expected, License(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func Test_Originator(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, Originator(&test.input))
|
||||
assert.Equal(t, test.expected, Originator(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Originator(p *pkg.Package) string {
|
||||
func Originator(p pkg.Package) string {
|
||||
if hasMetadata(p) {
|
||||
switch metadata := p.Metadata.(type) {
|
||||
case pkg.ApkMetadata:
|
||||
|
||||
@@ -6,11 +6,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func SourceInfo(p *pkg.Package) string {
|
||||
if !packageExists(p) {
|
||||
return ""
|
||||
}
|
||||
|
||||
func SourceInfo(p pkg.Package) string {
|
||||
answer := ""
|
||||
switch p.Type {
|
||||
case pkg.RpmPkg:
|
||||
|
||||
@@ -139,7 +139,7 @@ func Test_SourceInfo(t *testing.T) {
|
||||
if test.input.Type != "" {
|
||||
pkgTypes = append(pkgTypes, test.input.Type)
|
||||
}
|
||||
actual := SourceInfo(&test.input)
|
||||
actual := SourceInfo(test.input)
|
||||
for _, expected := range test.expected {
|
||||
assert.Contains(t, actual, expected)
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
||||
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
ID: "package-1-id",
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Locations: []source.Location{
|
||||
@@ -154,7 +153,6 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
ID: "package-2-id",
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Locations: []source.Location{
|
||||
@@ -197,7 +195,6 @@ func newDirectoryCatalog() *pkg.Catalog {
|
||||
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
ID: "package-1-id",
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Type: pkg.PythonPkg,
|
||||
@@ -223,7 +220,6 @@ func newDirectoryCatalog() *pkg.Catalog {
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
ID: "package-2-id",
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Type: pkg.DebPkg,
|
||||
|
||||
@@ -33,7 +33,7 @@ func toFormatModel(s sbom.SBOM) model.Document {
|
||||
return doc
|
||||
}
|
||||
|
||||
func toComponent(p *pkg.Package) model.Component {
|
||||
func toComponent(p pkg.Package) model.Component {
|
||||
return model.Component{
|
||||
Type: "library", // TODO: this is not accurate
|
||||
Name: p.Name,
|
||||
|
||||
@@ -256,7 +256,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
|
||||
return results
|
||||
}
|
||||
|
||||
func formatSPDXExternalRefs(p *pkg.Package) (refs []*spdx.PackageExternalReference2_2) {
|
||||
func formatSPDXExternalRefs(p pkg.Package) (refs []*spdx.PackageExternalReference2_2) {
|
||||
for _, ref := range spdxhelpers.ExternalRefs(p) {
|
||||
refs = append(refs, &spdx.PackageExternalReference2_2{
|
||||
Category: string(ref.ReferenceCategory),
|
||||
|
||||
@@ -31,11 +31,7 @@ func TestEncodeDecodeCycle(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
// ids will never be equal
|
||||
p.ID = ""
|
||||
actualPackages[idx].ID = ""
|
||||
|
||||
for _, d := range deep.Equal(*p, *actualPackages[idx]) {
|
||||
for _, d := range deep.Equal(p, actualPackages[idx]) {
|
||||
if strings.Contains(d, ".VirtualPath: ") {
|
||||
// location.Virtual path is not exposed in the json output
|
||||
continue
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "package-1-id",
|
||||
"id": "cbf4f3077fc7deee",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "python",
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "package-2-id",
|
||||
"id": "1a39aadd9705c2b9",
|
||||
"name": "package-2",
|
||||
"version": "2.0.1",
|
||||
"type": "deb",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "package-1-id",
|
||||
"id": "d1d433485a31ed07",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "python",
|
||||
@@ -32,7 +32,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "package-2-id",
|
||||
"id": "2db629ca48fa6786",
|
||||
"name": "package-2",
|
||||
"version": "2.0.1",
|
||||
"type": "deb",
|
||||
|
||||
@@ -3,6 +3,8 @@ package syftjson
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
@@ -23,7 +25,7 @@ func ToFormatModel(s sbom.SBOM, applicationConfig interface{}) model.Document {
|
||||
|
||||
return model.Document{
|
||||
Artifacts: toPackageModels(s.Artifacts.PackageCatalog),
|
||||
ArtifactRelationships: toRelationshipModel(pkg.NewRelationships(s.Artifacts.PackageCatalog)),
|
||||
ArtifactRelationships: toRelationshipModel(s.Relationships),
|
||||
Source: src,
|
||||
Distro: toDistroModel(s.Artifacts.Distro),
|
||||
Descriptor: model.Descriptor{
|
||||
@@ -50,7 +52,7 @@ func toPackageModels(catalog *pkg.Catalog) []model.Package {
|
||||
}
|
||||
|
||||
// toPackageModel crates a new Package from the given pkg.Package.
|
||||
func toPackageModel(p *pkg.Package) model.Package {
|
||||
func toPackageModel(p pkg.Package) model.Package {
|
||||
var cpes = make([]string, len(p.CPEs))
|
||||
for i, c := range p.CPEs {
|
||||
cpes[i] = c.BindToFmtString()
|
||||
@@ -69,7 +71,7 @@ func toPackageModel(p *pkg.Package) model.Package {
|
||||
|
||||
return model.Package{
|
||||
PackageBasicData: model.PackageBasicData{
|
||||
ID: string(p.ID),
|
||||
ID: string(p.ID()),
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
Type: p.Type,
|
||||
@@ -87,14 +89,14 @@ func toPackageModel(p *pkg.Package) model.Package {
|
||||
}
|
||||
}
|
||||
|
||||
func toRelationshipModel(relationships []pkg.Relationship) []model.Relationship {
|
||||
func toRelationshipModel(relationships []artifact.Relationship) []model.Relationship {
|
||||
result := make([]model.Relationship, len(relationships))
|
||||
for i, r := range relationships {
|
||||
result[i] = model.Relationship{
|
||||
Parent: string(r.Parent),
|
||||
Child: string(r.Child),
|
||||
Parent: string(r.From.ID()),
|
||||
Child: string(r.To.ID()),
|
||||
Type: string(r.Type),
|
||||
Metadata: r.Metadata,
|
||||
Metadata: r.Data,
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -61,7 +61,6 @@ func toSyftPackage(p model.Package) pkg.Package {
|
||||
}
|
||||
|
||||
return pkg.Package{
|
||||
ID: pkg.ID(p.ID),
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
FoundBy: p.FoundBy,
|
||||
|
||||
@@ -33,7 +33,6 @@ func TestJSONPresenter(t *testing.T) {
|
||||
catalog := pkg.NewCatalog()
|
||||
|
||||
catalog.Add(pkg.Package{
|
||||
ID: "package-1-id",
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Locations: []source.Location{
|
||||
@@ -57,7 +56,6 @@ func TestJSONPresenter(t *testing.T) {
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
ID: "package-2-id",
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Locations: []source.Location{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
],
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "package-1-id",
|
||||
"id": "b84dfe0eb2c5670f",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "python",
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "package-2-id",
|
||||
"id": "6619226d6979963f",
|
||||
"name": "package-2",
|
||||
"version": "2.0.1",
|
||||
"type": "deb",
|
||||
|
||||
Reference in New Issue
Block a user