diff --git a/internal/formats/syftjson/model/package.go b/internal/formats/syftjson/model/package.go index 15f05de0a..618e703cb 100644 --- a/internal/formats/syftjson/model/package.go +++ b/internal/formats/syftjson/model/package.go @@ -129,6 +129,7 @@ func (p *Package) UnmarshalJSON(b []byte) error { if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil { return err } + p.Metadata = payload default: log.Warnf("unknown package metadata type=%q for packageID=%q", p.MetadataType, p.ID) } diff --git a/internal/formats/syftjson/model/package_test.go b/internal/formats/syftjson/model/package_test.go index 305892efe..a31639c25 100644 --- a/internal/formats/syftjson/model/package_test.go +++ b/internal/formats/syftjson/model/package_test.go @@ -1,18 +1,18 @@ package model -import "testing" +import ( + "testing" -func TestUnmarshalPackage(t *testing.T) { + "github.com/anchore/syft/syft/pkg" + "github.com/stretchr/testify/assert" +) + +func TestUnmarshalPackageGolang(t *testing.T) { tests := []struct { name string p *Package packageData []byte }{ - { - name: "Package.UnmarshalJSON will unmarshal blank json", - p: &Package{}, - packageData: []byte(`{}`), - }, { name: "Package.UnmarshalJSON unmarshals PackageBasicData", p: &Package{}, @@ -47,6 +47,11 @@ func TestUnmarshalPackage(t *testing.T) { if err != nil { t.Fatalf("could not unmarshal packageData: %v", err) } + + assert.NotNil(t, test.p.Metadata) + golangMetadata := test.p.Metadata.(pkg.GolangBinMetadata) + assert.NotEmpty(t, golangMetadata) + assert.Equal(t, "go1.18", golangMetadata.GoCompiledVersion) }) } } diff --git a/internal/formats/syftjson/to_syft_model.go b/internal/formats/syftjson/to_syft_model.go index 9b6f0aa64..c8a40eb93 100644 --- a/internal/formats/syftjson/to_syft_model.go +++ b/internal/formats/syftjson/to_syft_model.go @@ -12,7 +12,9 @@ import ( ) func toSyftModel(doc model.Document) (*sbom.SBOM, error) { - catalog := toSyftCatalog(doc.Artifacts) + idAliases := make(map[string]string) + + catalog := toSyftCatalog(doc.Artifacts, idAliases) return &sbom.SBOM{ Artifacts: sbom.Artifacts{ @@ -21,7 +23,7 @@ func toSyftModel(doc model.Document) (*sbom.SBOM, error) { }, Source: *toSyftSourceData(doc.Source), Descriptor: toSyftDescriptor(doc.Descriptor), - Relationships: toSyftRelationships(&doc, catalog, doc.ArtifactRelationships), + Relationships: toSyftRelationships(&doc, catalog, doc.ArtifactRelationships, idAliases), }, nil } @@ -46,7 +48,7 @@ func toSyftLinuxRelease(d model.LinuxRelease) *linux.Release { } } -func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationships []model.Relationship) []artifact.Relationship { +func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationships []model.Relationship, idAliases map[string]string) []artifact.Relationship { idMap := make(map[string]interface{}) for _, p := range catalog.Sorted() { @@ -62,7 +64,7 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationship var out []artifact.Relationship for _, r := range relationships { - syftRelationship := toSyftRelationship(idMap, r) + syftRelationship := toSyftRelationship(idMap, r, idAliases) if syftRelationship != nil { out = append(out, *syftRelationship) } @@ -70,13 +72,20 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationship return out } -func toSyftRelationship(idMap map[string]interface{}, relationship model.Relationship) *artifact.Relationship { - from, ok := idMap[relationship.Parent].(artifact.Identifiable) +func toSyftRelationship(idMap map[string]interface{}, relationship model.Relationship, idAliases map[string]string) *artifact.Relationship { + id := func(id string) string { + aliased, ok := idAliases[id] + if ok { + return aliased + } + return id + } + from, ok := idMap[id(relationship.Parent)].(artifact.Identifiable) if !ok { log.Warnf("relationship mapping from key %s is not a valid artifact.Identifiable type: %+v", relationship.Parent, idMap[relationship.Parent]) return nil } - to, ok := idMap[relationship.Child].(artifact.Identifiable) + to, ok := idMap[id(relationship.Child)].(artifact.Identifiable) if !ok { log.Warnf("relationship mapping to key %s is not a valid artifact.Identifiable type: %+v", relationship.Child, idMap[relationship.Child]) return nil @@ -128,15 +137,15 @@ func toSyftSourceData(s model.Source) *source.Metadata { return nil } -func toSyftCatalog(pkgs []model.Package) *pkg.Catalog { +func toSyftCatalog(pkgs []model.Package, idAliases map[string]string) *pkg.Catalog { catalog := pkg.NewCatalog() for _, p := range pkgs { - catalog.Add(toSyftPackage(p)) + catalog.Add(toSyftPackage(p, idAliases)) } return catalog } -func toSyftPackage(p model.Package) pkg.Package { +func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package { var cpes []pkg.CPE for _, c := range p.CPEs { value, err := pkg.NewCPE(c) @@ -153,7 +162,7 @@ func toSyftPackage(p model.Package) pkg.Package { locations[i] = source.NewLocationFromCoordinates(c) } - return pkg.Package{ + out := pkg.Package{ Name: p.Name, Version: p.Version, FoundBy: p.FoundBy, @@ -166,4 +175,13 @@ func toSyftPackage(p model.Package) pkg.Package { MetadataType: p.MetadataType, Metadata: p.Metadata, } + + out.SetID() + + id := string(out.ID()) + if id != p.ID { + idAliases[p.ID] = id + } + + return out } diff --git a/internal/formats/syftjson/to_syft_model_test.go b/internal/formats/syftjson/to_syft_model_test.go index 7c852189d..277106b8e 100644 --- a/internal/formats/syftjson/to_syft_model_test.go +++ b/internal/formats/syftjson/to_syft_model_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/anchore/syft/internal/formats/syftjson/model" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/source" "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" @@ -79,3 +80,46 @@ func Test_toSyftSourceData(t *testing.T) { // assert all possible schemes were under test assert.ElementsMatch(t, allSchemes.List(), testedSchemes.List(), "not all source.Schemes are under test") } + +func Test_idsHaveChanged(t *testing.T) { + s, err := toSyftModel(model.Document{ + Source: model.Source{ + Type: "file", + Target: "some/path", + }, + Artifacts: []model.Package{ + { + PackageBasicData: model.PackageBasicData{ + ID: "1", + Name: "pkg-1", + }, + }, + { + PackageBasicData: model.PackageBasicData{ + ID: "2", + Name: "pkg-2", + }, + }, + }, + ArtifactRelationships: []model.Relationship{ + { + Parent: "1", + Child: "2", + Type: string(artifact.ContainsRelationship), + }, + }, + }) + + assert.NoError(t, err) + assert.Len(t, s.Relationships, 1) + + r := s.Relationships[0] + + from := s.Artifacts.PackageCatalog.Package(r.From.ID()) + assert.NotNil(t, from) + assert.Equal(t, "pkg-1", from.Name) + + to := s.Artifacts.PackageCatalog.Package(r.To.ID()) + assert.NotNil(t, to) + assert.Equal(t, "pkg-2", to.Name) +}