From 4b45c42f5a3650bfd4f32e8315fe3b7ac6f123df Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 16 Nov 2020 13:59:15 -0500 Subject: [PATCH] make cyclonedx presenter generally reusable (for grype) Signed-off-by: Alex Goodman --- syft/presenter/cyclonedx/bom-extension.go | 32 +++++++++++++++----- syft/presenter/cyclonedx/document.go | 34 +++++++++++----------- syft/presenter/cyclonedx/presenter.go | 29 ++---------------- syft/presenter/cyclonedx/presenter_test.go | 2 +- syft/presenter/json/distribution.go | 15 ++++++++++ syft/presenter/json/document.go | 11 +------ 6 files changed, 62 insertions(+), 61 deletions(-) diff --git a/syft/presenter/cyclonedx/bom-extension.go b/syft/presenter/cyclonedx/bom-extension.go index f25713734..7f99de637 100644 --- a/syft/presenter/cyclonedx/bom-extension.go +++ b/syft/presenter/cyclonedx/bom-extension.go @@ -4,8 +4,7 @@ import ( "encoding/xml" "time" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/version" + "github.com/anchore/syft/syft/source" ) // Source: https://cyclonedx.org/ext/bom-descriptor/ @@ -35,15 +34,34 @@ type BdComponent struct { } // NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details. -func NewBomDescriptor() *BomDescriptor { - versionInfo := version.FromBuild() - return &BomDescriptor{ +func NewBomDescriptor(name, version string, srcMetadata source.Metadata) *BomDescriptor { + descriptor := BomDescriptor{ XMLName: xml.Name{}, Timestamp: time.Now().Format(time.RFC3339), Tool: &BdTool{ Vendor: "anchore", - Name: internal.ApplicationName, - Version: versionInfo.Version, + Name: name, + Version: version, }, } + + switch srcMetadata.Scheme { + case source.ImageScheme: + descriptor.Component = &BdComponent{ + Component: Component{ + Type: "container", + Name: srcMetadata.ImageMetadata.UserInput, + Version: srcMetadata.ImageMetadata.Digest, + }, + } + case source.DirectoryScheme: + descriptor.Component = &BdComponent{ + Component: Component{ + Type: "file", + Name: srcMetadata.Path, + }, + } + } + + return &descriptor } diff --git a/syft/presenter/cyclonedx/document.go b/syft/presenter/cyclonedx/document.go index 4539ab73e..78752480e 100644 --- a/syft/presenter/cyclonedx/document.go +++ b/syft/presenter/cyclonedx/document.go @@ -3,9 +3,11 @@ package cyclonedx import ( "encoding/xml" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/version" "github.com/anchore/syft/syft/distro" - "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" "github.com/google/uuid" ) @@ -22,19 +24,19 @@ type Document struct { BomDescriptor *BomDescriptor `xml:"bd:metadata"` // The BOM descriptor extension } -// NewDocument returns an empty CycloneDX Document object. -func NewDocument() Document { - return Document{ - XMLNs: "http://cyclonedx.org/schema/bom/1.2", - XMLNsBd: "http://cyclonedx.org/schema/ext/bom-descriptor/1.0", - Version: 1, - SerialNumber: uuid.New().URN(), - } -} - // NewDocumentFromCatalog returns a CycloneDX Document object populated with the catalog contents. -func NewDocumentFromCatalog(catalog *pkg.Catalog, d distro.Distro) Document { - bom := NewDocument() +func NewDocument(catalog *pkg.Catalog, d distro.Distro, srcMetadata source.Metadata) Document { + versionInfo := version.FromBuild() + + doc := Document{ + XMLNs: "http://cyclonedx.org/schema/bom/1.2", + XMLNsBd: "http://cyclonedx.org/schema/ext/bom-descriptor/1.0", + Version: 1, + SerialNumber: uuid.New().URN(), + BomDescriptor: NewBomDescriptor(internal.ApplicationName, versionInfo.Version, srcMetadata), + } + + // attach components for p := range catalog.Enumerate() { component := Component{ Type: "library", // TODO: this is not accurate @@ -51,10 +53,8 @@ func NewDocumentFromCatalog(catalog *pkg.Catalog, d distro.Distro) Document { if len(licenses) > 0 { component.Licenses = &licenses } - bom.Components = append(bom.Components, component) + doc.Components = append(doc.Components, component) } - bom.BomDescriptor = NewBomDescriptor() - - return bom + return doc } diff --git a/syft/presenter/cyclonedx/presenter.go b/syft/presenter/cyclonedx/presenter.go index 2a28b8db7..ad86ac947 100644 --- a/syft/presenter/cyclonedx/presenter.go +++ b/syft/presenter/cyclonedx/presenter.go @@ -5,7 +5,6 @@ package cyclonedx import ( "encoding/xml" - "fmt" "io" "github.com/anchore/syft/syft/distro" @@ -22,39 +21,17 @@ type Presenter struct { } // NewPresenter creates a CycloneDX presenter from the given Catalog and Locations objects. -func NewPresenter(catalog *pkg.Catalog, s source.Metadata, d distro.Distro) *Presenter { +func NewPresenter(catalog *pkg.Catalog, srcMetadata source.Metadata, d distro.Distro) *Presenter { return &Presenter{ catalog: catalog, - srcMetadata: s, + srcMetadata: srcMetadata, distro: d, } } // Present writes the CycloneDX report to the given io.Writer. func (pres *Presenter) Present(output io.Writer) error { - bom := NewDocumentFromCatalog(pres.catalog, pres.distro) - - switch pres.srcMetadata.Scheme { - case source.DirectoryScheme: - bom.BomDescriptor.Component = &BdComponent{ - Component: Component{ - Type: "file", - Name: pres.srcMetadata.Path, - Version: "", - }, - } - case source.ImageScheme: - // TODO: can we use the tags a bit better? - bom.BomDescriptor.Component = &BdComponent{ - Component: Component{ - Type: "container", - Name: pres.srcMetadata.ImageMetadata.UserInput, - Version: pres.srcMetadata.ImageMetadata.Digest, - }, - } - default: - return fmt.Errorf("unsupported source: %T", pres.srcMetadata.Scheme) - } + bom := NewDocument(pres.catalog, pres.distro, pres.srcMetadata) encoder := xml.NewEncoder(output) encoder.Indent("", " ") diff --git a/syft/presenter/cyclonedx/presenter_test.go b/syft/presenter/cyclonedx/presenter_test.go index ee29ca62c..a4ba735fd 100644 --- a/syft/presenter/cyclonedx/presenter_test.go +++ b/syft/presenter/cyclonedx/presenter_test.go @@ -177,7 +177,7 @@ func TestCycloneDxImgsPresenter(t *testing.T) { if !bytes.Equal(expected, actual) { dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(string(expected), string(actual), true) + diffs := dmp.DiffMain(string(actual), string(expected), true) t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs)) } } diff --git a/syft/presenter/json/distribution.go b/syft/presenter/json/distribution.go index 567aa50f5..b19c4ca68 100644 --- a/syft/presenter/json/distribution.go +++ b/syft/presenter/json/distribution.go @@ -1,8 +1,23 @@ package json +import "github.com/anchore/syft/syft/distro" + // Distribution provides information about a detected Linux Distribution type Distribution struct { Name string `json:"name"` Version string `json:"version"` IDLike string `json:"idLike"` } + +func NewDistribution(d distro.Distro) Distribution { + distroName := d.Name() + if distroName == "UnknownDistroType" { + distroName = "" + } + + return Distribution{ + Name: distroName, + Version: d.FullVersion(), + IDLike: d.IDLike, + } +} diff --git a/syft/presenter/json/document.go b/syft/presenter/json/document.go index 230c9ad8c..19b89c9d2 100644 --- a/syft/presenter/json/document.go +++ b/syft/presenter/json/document.go @@ -21,19 +21,10 @@ func NewDocument(catalog *pkg.Catalog, srcMetadata source.Metadata, d distro.Dis return Document{}, nil } - distroName := d.Name() - if distroName == "UnknownDistroType" { - distroName = "" - } - doc := Document{ Artifacts: make([]Artifact, 0), Source: src, - Distro: Distribution{ - Name: distroName, - Version: d.FullVersion(), - IDLike: d.IDLike, - }, + Distro: NewDistribution(d), Descriptor: Descriptor{ Name: internal.ApplicationName, Version: version.FromBuild().Version,