make cyclonedx presenter generally reusable (for grype)

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-11-16 13:59:15 -05:00
parent f46de19c6b
commit 4b45c42f5a
No known key found for this signature in database
GPG Key ID: 5CB45AE22BAB7EA7
6 changed files with 62 additions and 61 deletions

View File

@ -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
}

View File

@ -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{
// NewDocumentFromCatalog returns a CycloneDX Document object populated with the catalog contents.
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),
}
}
// NewDocumentFromCatalog returns a CycloneDX Document object populated with the catalog contents.
func NewDocumentFromCatalog(catalog *pkg.Catalog, d distro.Distro) Document {
bom := NewDocument()
// 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
}

View File

@ -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("", " ")

View File

@ -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))
}
}

View File

@ -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,
}
}

View File

@ -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,