mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 07:56:36 +01:00
Add package URL support to the CycloneDX presenter (#164)
* add package URL support to the CycloneDX presenter Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * wrap license tags with licenses Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
@@ -14,6 +14,7 @@ type Component struct {
|
||||
Version string `xml:"version"` // Required; The version of the component as defined by the project
|
||||
Description string `xml:"description,omitempty"` // A description of the component
|
||||
Licenses *[]License `xml:"licenses>license"` // A node describing zero or more license names, SPDX license IDs or expressions
|
||||
PackageURL string `xml:"purl,omitempty"` // Specifies the package-url (PackageURL). The purl, if specified, must be valid and conform to the specification defined at: https://github.com/package-url/purl-spec
|
||||
// TODO: scope, hashes, copyright, cpe, purl, swid, modified, pedigree, externalReferences
|
||||
// TODO: add user-defined parameters for syft-specific values (image layer index, cataloger, location path, etc.)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package cyclonedx
|
||||
import (
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -31,13 +33,14 @@ func NewDocument() Document {
|
||||
}
|
||||
|
||||
// NewDocumentFromCatalog returns a CycloneDX Document object populated with the catalog contents.
|
||||
func NewDocumentFromCatalog(catalog *pkg.Catalog) Document {
|
||||
func NewDocumentFromCatalog(catalog *pkg.Catalog, d distro.Distro) Document {
|
||||
bom := NewDocument()
|
||||
for p := range catalog.Enumerate() {
|
||||
component := Component{
|
||||
Type: "library", // TODO: this is not accurate
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
Type: "library", // TODO: this is not accurate
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
PackageURL: p.PackageURL(d),
|
||||
}
|
||||
var licenses []License
|
||||
for _, licenseName := range p.Licenses {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
)
|
||||
@@ -16,19 +18,21 @@ import (
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
scope scope.Scope
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
// NewPresenter creates a CycloneDX presenter from the given Catalog and Scope objects.
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter {
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
scope: s,
|
||||
distro: d,
|
||||
}
|
||||
}
|
||||
|
||||
// Present writes the CycloneDX report to the given io.Writer.
|
||||
func (pres *Presenter) Present(output io.Writer) error {
|
||||
bom := NewDocumentFromCatalog(pres.catalog)
|
||||
bom := NewDocumentFromCatalog(pres.catalog, pres.distro)
|
||||
|
||||
srcObj := pres.scope.Source()
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package cyclonedx
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
@@ -22,16 +23,21 @@ func TestCycloneDxDirsPresenter(t *testing.T) {
|
||||
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-1",
|
||||
Name: "package1",
|
||||
Version: "1.0.1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
Source: []file.Reference{
|
||||
{Path: "/some/path/pkg1"},
|
||||
},
|
||||
Metadata: pkg.DpkgMetadata{
|
||||
Package: "package1",
|
||||
Version: "1.0.1",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-2",
|
||||
Name: "package2",
|
||||
Version: "2.0.1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
@@ -42,13 +48,24 @@ func TestCycloneDxDirsPresenter(t *testing.T) {
|
||||
"MIT",
|
||||
"Apache-v2",
|
||||
},
|
||||
Metadata: pkg.DpkgMetadata{
|
||||
Package: "package2",
|
||||
Version: "1.0.2",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromDir("/some/path")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pres := NewPresenter(catalog, s)
|
||||
|
||||
d, err := distro.NewDistro(distro.Ubuntu, "20.04")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pres := NewPresenter(catalog, s, d)
|
||||
|
||||
// run presenter
|
||||
err = pres.Present(&buffer)
|
||||
@@ -84,30 +101,61 @@ func TestCycloneDxImgsPresenter(t *testing.T) {
|
||||
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-1",
|
||||
Name: "package1",
|
||||
Version: "1.0.1",
|
||||
Source: []file.Reference{
|
||||
*img.SquashedTree().File("/somefile-1.txt"),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
Type: pkg.RpmPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
Metadata: pkg.RpmMetadata{
|
||||
Name: "package1",
|
||||
Epoch: 0,
|
||||
Arch: "x86_64",
|
||||
Release: "1",
|
||||
Version: "1.0.1",
|
||||
SourceRpm: "package1-1.0.1-1.src.rpm",
|
||||
Size: 12406784,
|
||||
License: "MIT",
|
||||
Vendor: "",
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-2",
|
||||
Name: "package2",
|
||||
Version: "2.0.1",
|
||||
Source: []file.Reference{
|
||||
*img.SquashedTree().File("/somefile-2.txt"),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
Type: pkg.RpmPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
Licenses: []string{
|
||||
"MIT",
|
||||
"Apache-v2",
|
||||
},
|
||||
Metadata: pkg.RpmMetadata{
|
||||
Name: "package2",
|
||||
Epoch: 0,
|
||||
Arch: "x86_64",
|
||||
Release: "1",
|
||||
Version: "1.0.2",
|
||||
SourceRpm: "package2-1.0.2-1.src.rpm",
|
||||
Size: 12406784,
|
||||
License: "MIT",
|
||||
Vendor: "",
|
||||
},
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
pres := NewPresenter(catalog, s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d, err := distro.NewDistro(distro.RedHat, "8")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pres := NewPresenter(catalog, s, d)
|
||||
|
||||
// run presenter
|
||||
err = pres.Present(&buffer)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:0667955f-34d0-49c1-8062-996dbc636974">
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:88c9a559-fb74-45a1-9dbb-a3d8bcbcacc8">
|
||||
<components>
|
||||
<component type="library">
|
||||
<name>package-1</name>
|
||||
<name>package1</name>
|
||||
<version>1.0.1</version>
|
||||
<purl>pkg:deb/ubuntu/package1@1.0.1?arch=amd64</purl>
|
||||
</component>
|
||||
<component type="library">
|
||||
<name>package-2</name>
|
||||
<name>package2</name>
|
||||
<version>2.0.1</version>
|
||||
<licenses>
|
||||
<license>
|
||||
@@ -16,10 +17,11 @@
|
||||
<name>Apache-v2</name>
|
||||
</license>
|
||||
</licenses>
|
||||
<purl>pkg:deb/ubuntu/package2@1.0.2?arch=amd64</purl>
|
||||
</component>
|
||||
</components>
|
||||
<bd:metadata>
|
||||
<bd:timestamp>2020-08-26T15:31:39-04:00</bd:timestamp>
|
||||
<bd:timestamp>2020-08-29T20:17:49-04:00</bd:timestamp>
|
||||
<bd:tool>
|
||||
<bd:vendor>anchore</bd:vendor>
|
||||
<bd:name>syft</bd:name>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:2e7af7d0-83a4-4730-9175-66858406b285">
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" xmlns:bd="http://cyclonedx.org/schema/ext/bom-descriptor/1.0" version="1" serialNumber="urn:uuid:6da957f1-3337-4128-870c-fe271aa195d1">
|
||||
<components>
|
||||
<component type="library">
|
||||
<name>package-1</name>
|
||||
<name>package1</name>
|
||||
<version>1.0.1</version>
|
||||
<purl>pkg:rpm/redhat/package1@0:1.0.1-1?arch=x86_64</purl>
|
||||
</component>
|
||||
<component type="library">
|
||||
<name>package-2</name>
|
||||
<name>package2</name>
|
||||
<version>2.0.1</version>
|
||||
<licenses>
|
||||
<license>
|
||||
@@ -16,10 +17,11 @@
|
||||
<name>Apache-v2</name>
|
||||
</license>
|
||||
</licenses>
|
||||
<purl>pkg:rpm/redhat/package2@0:1.0.2-1?arch=x86_64</purl>
|
||||
</component>
|
||||
</components>
|
||||
<bd:metadata>
|
||||
<bd:timestamp>2020-08-26T15:31:39-04:00</bd:timestamp>
|
||||
<bd:timestamp>2020-08-29T20:17:49-04:00</bd:timestamp>
|
||||
<bd:tool>
|
||||
<bd:vendor>anchore</bd:vendor>
|
||||
<bd:name>syft</bd:name>
|
||||
|
||||
@@ -7,6 +7,8 @@ package presenter
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/presenter/cyclonedx"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -23,7 +25,7 @@ type Presenter interface {
|
||||
}
|
||||
|
||||
// GetPresenter returns a presenter for images or directories
|
||||
func GetPresenter(option Option, s scope.Scope, catalog *pkg.Catalog) Presenter {
|
||||
func GetPresenter(option Option, s scope.Scope, catalog *pkg.Catalog, d *distro.Distro) Presenter {
|
||||
switch option {
|
||||
case JSONPresenter:
|
||||
return json.NewPresenter(catalog, s)
|
||||
@@ -32,7 +34,7 @@ func GetPresenter(option Option, s scope.Scope, catalog *pkg.Catalog) Presenter
|
||||
case TablePresenter:
|
||||
return table.NewPresenter(catalog, s)
|
||||
case CycloneDxPresenter:
|
||||
return cyclonedx.NewPresenter(catalog, s)
|
||||
return cyclonedx.NewPresenter(catalog, s, *d)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user