diff --git a/internal/presenter/packages/cyclonedx_bom_descriptor.go b/internal/presenter/packages/cyclonedx_bom_descriptor.go new file mode 100644 index 000000000..eeaac61f7 --- /dev/null +++ b/internal/presenter/packages/cyclonedx_bom_descriptor.go @@ -0,0 +1,69 @@ +package packages + +import ( + "encoding/xml" + "time" + + "github.com/anchore/syft/syft/source" +) + +// Source: https://cyclonedx.org/ext/bom-descriptor/ + +// CycloneDxBomDescriptor represents all metadata surrounding the BOM report (such as when the BOM was made, with which tool, and the item being cataloged). +type CycloneDxBomDescriptor struct { + XMLName xml.Name `xml:"metadata"` + Timestamp string `xml:"timestamp,omitempty"` // The date and time (timestamp) when the document was created + Tools []CycloneDxBdTool `xml:"tools>tool"` // The tool used to create the BOM. + Component *CycloneDxBdComponent `xml:"component"` // The component that the BOM describes. +} + +// CycloneDxBdTool represents the tool that created the BOM report. +type CycloneDxBdTool struct { + XMLName xml.Name `xml:"tool"` + Vendor string `xml:"vendor,omitempty"` // The vendor of the tool used to create the BOM. + Name string `xml:"name,omitempty"` // The name of the tool used to create the BOM. + Version string `xml:"version,omitempty"` // The version of the tool used to create the BOM. + // TODO: hashes, author, manufacture, supplier + // TODO: add user-defined fields for the remaining build/version parameters +} + +// CycloneDxBdComponent represents the software/package being cataloged. +type CycloneDxBdComponent struct { + XMLName xml.Name `xml:"component"` + CycloneDxComponent +} + +// NewCycloneDxBomDescriptor returns a new CycloneDxBomDescriptor tailored for the current time and "syft" tool details. +func NewCycloneDxBomDescriptor(name, version string, srcMetadata source.Metadata) *CycloneDxBomDescriptor { + descriptor := CycloneDxBomDescriptor{ + XMLName: xml.Name{}, + Timestamp: time.Now().Format(time.RFC3339), + Tools: []CycloneDxBdTool{ + { + Vendor: "anchore", + Name: name, + Version: version, + }, + }, + } + + switch srcMetadata.Scheme { + case source.ImageScheme: + descriptor.Component = &CycloneDxBdComponent{ + CycloneDxComponent: CycloneDxComponent{ + Type: "container", + Name: srcMetadata.ImageMetadata.UserInput, + Version: srcMetadata.ImageMetadata.ManifestDigest, + }, + } + case source.DirectoryScheme: + descriptor.Component = &CycloneDxBdComponent{ + CycloneDxComponent: CycloneDxComponent{ + Type: "file", + Name: srcMetadata.Path, + }, + } + } + + return &descriptor +} diff --git a/internal/presenter/packages/cyclonedx_component.go b/internal/presenter/packages/cyclonedx_component.go new file mode 100644 index 000000000..9ff1877dd --- /dev/null +++ b/internal/presenter/packages/cyclonedx_component.go @@ -0,0 +1,27 @@ +package packages + +import "encoding/xml" + +// CycloneDxComponent represents a single element in the CycloneDX BOM +type CycloneDxComponent struct { + XMLName xml.Name `xml:"component"` + Type string `xml:"type,attr"` // Required; Describes if the component is a library, framework, application, container, operating system, firmware, hardware device, or file + Supplier string `xml:"supplier,omitempty"` // The organization that supplied the component. The supplier may often be the manufacture, but may also be a distributor or repackager. + Author string `xml:"author,omitempty"` // The person(s) or organization(s) that authored the component + Publisher string `xml:"publisher,omitempty"` // The person(s) or organization(s) that published the component + Group string `xml:"group,omitempty"` // The high-level classification that a project self-describes as. This will often be a shortened, single name of the company or project that produced the component, or the source package or domain name. + Name string `xml:"name"` // Required; The name of the component as defined by the project + 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 *[]CycloneDxLicense `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: source, hashes, copyright, cpe, purl, swid, modified, pedigree, externalReferences + // TODO: add user-defined parameters for syft-specific values (image layer index, cataloger, location path, etc.) +} + +// CycloneDxLicense represents a single software license for a CycloneDxComponent +type CycloneDxLicense struct { + XMLName xml.Name `xml:"license"` + ID string `xml:"id,omitempty"` // A valid SPDX license ID + Name string `xml:"name,omitempty"` // If SPDX does not define the license used, this field may be used to provide the license name +} diff --git a/internal/presenter/packages/cyclonedx_document.go b/internal/presenter/packages/cyclonedx_document.go new file mode 100644 index 000000000..9278dc6d1 --- /dev/null +++ b/internal/presenter/packages/cyclonedx_document.go @@ -0,0 +1,57 @@ +package packages + +import ( + "encoding/xml" + + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/version" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" + "github.com/google/uuid" +) + +// Source: https://github.com/CycloneDX/specification + +// CycloneDxDocument represents a CycloneDX BOM CycloneDxDocument. +type CycloneDxDocument struct { + XMLName xml.Name `xml:"bom"` + XMLNs string `xml:"xmlns,attr"` + Version int `xml:"version,attr"` + SerialNumber string `xml:"serialNumber,attr"` + BomDescriptor *CycloneDxBomDescriptor `xml:"metadata"` // The BOM descriptor extension + Components []CycloneDxComponent `xml:"components>component"` // The BOM contents +} + +// NewCycloneDxDocument returns a CycloneDX CycloneDxDocument object populated with the catalog contents. +func NewCycloneDxDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) CycloneDxDocument { + versionInfo := version.FromBuild() + + doc := CycloneDxDocument{ + XMLNs: "http://cyclonedx.org/schema/bom/1.2", + Version: 1, + SerialNumber: uuid.New().URN(), + BomDescriptor: NewCycloneDxBomDescriptor(internal.ApplicationName, versionInfo.Version, srcMetadata), + } + + // attach components + for p := range catalog.Enumerate() { + component := CycloneDxComponent{ + Type: "library", // TODO: this is not accurate + Name: p.Name, + Version: p.Version, + PackageURL: p.PURL, + } + var licenses []CycloneDxLicense + for _, licenseName := range p.Licenses { + licenses = append(licenses, CycloneDxLicense{ + Name: licenseName, + }) + } + if len(licenses) > 0 { + component.Licenses = &licenses + } + doc.Components = append(doc.Components, component) + } + + return doc +} diff --git a/syft/presenter/cyclonedx/presenter.go b/internal/presenter/packages/cyclonedx_presenter.go similarity index 57% rename from syft/presenter/cyclonedx/presenter.go rename to internal/presenter/packages/cyclonedx_presenter.go index 1c044c56c..f844fbf02 100644 --- a/syft/presenter/cyclonedx/presenter.go +++ b/internal/presenter/packages/cyclonedx_presenter.go @@ -1,7 +1,7 @@ /* Package cyclonedx is responsible for generating a CycloneDX XML report for the given container image or file system. */ -package cyclonedx +package packages import ( "encoding/xml" @@ -11,23 +11,23 @@ import ( "github.com/anchore/syft/syft/source" ) -// Presenter writes a CycloneDX report from the given Catalog and Locations contents -type Presenter struct { +// CycloneDxPresenter writes a CycloneDX report from the given Catalog and Locations contents +type CycloneDxPresenter struct { catalog *pkg.Catalog srcMetadata source.Metadata } -// NewPresenter creates a CycloneDX presenter from the given Catalog and Locations objects. -func NewPresenter(catalog *pkg.Catalog, srcMetadata source.Metadata) *Presenter { - return &Presenter{ +// NewCycloneDxPresenter creates a CycloneDX presenter from the given Catalog and Locations objects. +func NewCycloneDxPresenter(catalog *pkg.Catalog, srcMetadata source.Metadata) *CycloneDxPresenter { + return &CycloneDxPresenter{ catalog: catalog, srcMetadata: srcMetadata, } } // Present writes the CycloneDX report to the given io.Writer. -func (pres *Presenter) Present(output io.Writer) error { - bom := NewDocument(pres.catalog, pres.srcMetadata) +func (pres *CycloneDxPresenter) Present(output io.Writer) error { + bom := NewCycloneDxDocument(pres.catalog, pres.srcMetadata) encoder := xml.NewEncoder(output) encoder.Indent("", " ") diff --git a/syft/presenter/cyclonedx/presenter_test.go b/internal/presenter/packages/cyclonedx_presenter_test.go similarity index 93% rename from syft/presenter/cyclonedx/presenter_test.go rename to internal/presenter/packages/cyclonedx_presenter_test.go index 9ef9cbb24..46d3364f4 100644 --- a/syft/presenter/cyclonedx/presenter_test.go +++ b/internal/presenter/packages/cyclonedx_presenter_test.go @@ -1,4 +1,4 @@ -package cyclonedx +package packages import ( "bytes" @@ -62,7 +62,7 @@ func TestCycloneDxDirsPresenter(t *testing.T) { t.Fatal(err) } - pres := NewPresenter(catalog, s.Metadata) + pres := NewCycloneDxPresenter(catalog, s.Metadata) // run presenter err = pres.Present(&buffer) @@ -93,8 +93,7 @@ func TestCycloneDxImgsPresenter(t *testing.T) { var buffer bytes.Buffer catalog := pkg.NewCatalog() - img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-simple") - defer cleanup() + img := imagetest.GetFixtureImage(t, "docker-archive", "image-simple") _, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks) _, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks) @@ -125,7 +124,7 @@ func TestCycloneDxImgsPresenter(t *testing.T) { PURL: "the-purl-2", }) - s, err := source.NewFromImage(img, source.SquashedScope, "user-image-input") + s, err := source.NewFromImage(img, "user-image-input") if err != nil { t.Fatal(err) } @@ -138,7 +137,7 @@ func TestCycloneDxImgsPresenter(t *testing.T) { // This value is sourced from the "version" node in "./test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden" s.Metadata.ImageMetadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" - pres := NewPresenter(catalog, s.Metadata) + pres := NewCycloneDxPresenter(catalog, s.Metadata) // run presenter err = pres.Present(&buffer) diff --git a/syft/presenter/json/distribution.go b/internal/presenter/packages/json_distribution.go similarity index 54% rename from syft/presenter/json/distribution.go rename to internal/presenter/packages/json_distribution.go index 07eb5ba23..5ba939c03 100644 --- a/syft/presenter/json/distribution.go +++ b/internal/presenter/packages/json_distribution.go @@ -1,21 +1,21 @@ -package json +package packages import "github.com/anchore/syft/syft/distro" -// Distribution provides information about a detected Linux Distribution. -type Distribution struct { +// JSONDistribution provides information about a detected Linux JSONDistribution. +type JSONDistribution struct { Name string `json:"name"` // Name of the Linux distribution Version string `json:"version"` // Version of the Linux distribution (major or major.minor version) IDLike string `json:"idLike"` // the ID_LIKE field found within the /etc/os-release file } -// NewDistribution creates a struct with the Linux distribution to be represented in JSON. -func NewDistribution(d *distro.Distro) Distribution { +// NewJSONDistribution creates a struct with the Linux distribution to be represented in JSON. +func NewJSONDistribution(d *distro.Distro) JSONDistribution { if d == nil { - return Distribution{} + return JSONDistribution{} } - return Distribution{ + return JSONDistribution{ Name: d.Name(), Version: d.FullVersion(), IDLike: d.IDLike, diff --git a/internal/presenter/packages/json_document.go b/internal/presenter/packages/json_document.go new file mode 100644 index 000000000..399d037ae --- /dev/null +++ b/internal/presenter/packages/json_document.go @@ -0,0 +1,62 @@ +package packages + +import ( + "fmt" + + "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" +) + +// JSONDocument represents the syft cataloging findings as a JSON document +type JSONDocument struct { + Artifacts []JSONPackage `json:"artifacts"` // Artifacts is the list of packages discovered and placed into the catalog + ArtifactRelationships []JSONRelationship `json:"artifactRelationships"` + Source JSONSource `json:"source"` // Source represents the original object that was cataloged + Distro JSONDistribution `json:"distro"` // Distro represents the Linux distribution that was detected from the source + Descriptor JSONDescriptor `json:"descriptor"` // Descriptor is a block containing self-describing information about syft + Schema JSONSchema `json:"schema"` // Schema is a block reserved for defining the version for the shape of this JSON document and where to find the schema document to validate the shape +} + +// NewJSONDocument creates and populates a new JSON document struct from the given cataloging results. +func NewJSONDocument(catalog *pkg.Catalog, srcMetadata source.Metadata, d *distro.Distro, scope source.Scope, configuration interface{}) (JSONDocument, error) { + src, err := NewJSONSource(srcMetadata, scope) + if err != nil { + return JSONDocument{}, err + } + + artifacts, err := NewJSONPackages(catalog) + if err != nil { + return JSONDocument{}, err + } + + return JSONDocument{ + Artifacts: artifacts, + ArtifactRelationships: newJSONRelationships(pkg.NewRelationships(catalog)), + Source: src, + Distro: NewJSONDistribution(d), + Descriptor: JSONDescriptor{ + Name: internal.ApplicationName, + Version: version.FromBuild().Version, + Configuration: configuration, + }, + Schema: JSONSchema{ + Version: internal.JSONSchemaVersion, + URL: fmt.Sprintf("https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-%s.json", internal.JSONSchemaVersion), + }, + }, nil +} + +// JSONDescriptor describes what created the document as well as surrounding metadata +type JSONDescriptor struct { + Name string `json:"name"` + Version string `json:"version"` + Configuration interface{} `json:"configuration,omitempty"` +} + +type JSONSchema struct { + Version string `json:"version"` + URL string `json:"url"` +} diff --git a/internal/presenter/packages/json_package.go b/internal/presenter/packages/json_package.go new file mode 100644 index 000000000..c3be8ff1f --- /dev/null +++ b/internal/presenter/packages/json_package.go @@ -0,0 +1,68 @@ +package packages + +import ( + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +// JSONPackage represents a pkg.Package object specialized for JSON marshaling and unmarshaling. +type JSONPackage struct { + ID string `json:"id"` + Name string `json:"name"` + Version string `json:"version"` + Type string `json:"type"` + FoundBy string `json:"foundBy"` + Locations []source.Location `json:"locations"` + Licenses []string `json:"licenses"` + Language string `json:"language"` + CPEs []string `json:"cpes"` + PURL string `json:"purl"` + MetadataType string `json:"metadataType"` + Metadata interface{} `json:"metadata"` +} + +func NewJSONPackages(catalog *pkg.Catalog) ([]JSONPackage, error) { + artifacts := make([]JSONPackage, 0) + for _, p := range catalog.Sorted() { + art, err := NewJSONPackage(p) + if err != nil { + return nil, err + } + artifacts = append(artifacts, art) + } + return artifacts, nil +} + +// NewJSONPackage crates a new JSONPackage from the given pkg.Package. +func NewJSONPackage(p *pkg.Package) (JSONPackage, error) { + var cpes = make([]string, len(p.CPEs)) + for i, c := range p.CPEs { + cpes[i] = c.BindToFmtString() + } + + // ensure collections are never nil for presentation reasons + var locations = make([]source.Location, 0) + if p.Locations != nil { + locations = p.Locations + } + + var licenses = make([]string, 0) + if p.Licenses != nil { + licenses = p.Licenses + } + + return JSONPackage{ + ID: string(p.ID), + Name: p.Name, + Version: p.Version, + Type: string(p.Type), + FoundBy: p.FoundBy, + Locations: locations, + Licenses: licenses, + Language: string(p.Language), + CPEs: cpes, + PURL: p.PURL, + MetadataType: string(p.MetadataType), + Metadata: p.Metadata, + }, nil +} diff --git a/internal/presenter/packages/json_presenter.go b/internal/presenter/packages/json_presenter.go new file mode 100644 index 000000000..cbdc4d107 --- /dev/null +++ b/internal/presenter/packages/json_presenter.go @@ -0,0 +1,43 @@ +package packages + +import ( + "encoding/json" + "io" + + "github.com/anchore/syft/syft/distro" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +// JSONPresenter is a JSON presentation object for the syft results +type JSONPresenter struct { + catalog *pkg.Catalog + srcMetadata source.Metadata + distro *distro.Distro + scope source.Scope +} + +// NewJSONPresenter creates a new JSON presenter object for the given cataloging results. +func NewJSONPresenter(catalog *pkg.Catalog, s source.Metadata, d *distro.Distro, scope source.Scope) *JSONPresenter { + return &JSONPresenter{ + catalog: catalog, + srcMetadata: s, + distro: d, + scope: scope, + } +} + +// Present the catalog results to the given writer. +func (pres *JSONPresenter) Present(output io.Writer) error { + // we do not pass in configuration for backwards compatibility + doc, err := NewJSONDocument(pres.catalog, pres.srcMetadata, pres.distro, pres.scope, nil) + if err != nil { + return err + } + + enc := json.NewEncoder(output) + // prevent > and < from being escaped in the payload + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + return enc.Encode(&doc) +} diff --git a/syft/presenter/json/presenter_test.go b/internal/presenter/packages/json_presenter_test.go similarity index 89% rename from syft/presenter/json/presenter_test.go rename to internal/presenter/packages/json_presenter_test.go index f76215c93..582efdbe0 100644 --- a/syft/presenter/json/presenter_test.go +++ b/internal/presenter/packages/json_presenter_test.go @@ -1,4 +1,4 @@ -package json +package packages import ( "bytes" @@ -15,7 +15,7 @@ import ( "github.com/sergi/go-diff/diffmatchpatch" ) -var update = flag.Bool("update", false, "update the *.golden files for json presenters") +var updateJSONGoldenFiles = flag.Bool("update-json", false, "update the *.golden files for json presenters") func must(c pkg.CPE, e error) pkg.CPE { if e != nil { @@ -24,7 +24,7 @@ func must(c pkg.CPE, e error) pkg.CPE { return c } -func TestJsonDirsPresenter(t *testing.T) { +func TestJSONDirsPresenter(t *testing.T) { var buffer bytes.Buffer catalog := pkg.NewCatalog() @@ -75,7 +75,7 @@ func TestJsonDirsPresenter(t *testing.T) { if err != nil { t.Fatal(err) } - pres := NewPresenter(catalog, s.Metadata, d) + pres := NewJSONPresenter(catalog, s.Metadata, d, source.SquashedScope) // run presenter err = pres.Present(&buffer) @@ -84,7 +84,7 @@ func TestJsonDirsPresenter(t *testing.T) { } actual := buffer.Bytes() - if *update { + if *updateJSONGoldenFiles { testutils.UpdateGoldenFileContents(t, actual) } @@ -98,12 +98,12 @@ func TestJsonDirsPresenter(t *testing.T) { } -func TestJsonImgsPresenter(t *testing.T) { +func TestJSONImgsPresenter(t *testing.T) { var buffer bytes.Buffer testImage := "image-simple" - if *update { + if *updateJSONGoldenFiles { imagetest.UpdateGoldenFixtureImage(t, testImage) } @@ -158,9 +158,9 @@ func TestJsonImgsPresenter(t *testing.T) { // this is a hard coded value that is not given by the fixture helper and must be provided manually img.Metadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" - s, err := source.NewFromImage(img, source.SquashedScope, "user-image-input") + s, err := source.NewFromImage(img, "user-image-input") var d *distro.Distro - pres := NewPresenter(catalog, s.Metadata, d) + pres := NewJSONPresenter(catalog, s.Metadata, d, source.SquashedScope) // run presenter err = pres.Present(&buffer) @@ -169,7 +169,7 @@ func TestJsonImgsPresenter(t *testing.T) { } actual := buffer.Bytes() - if *update { + if *updateJSONGoldenFiles { testutils.UpdateGoldenFileContents(t, actual) } diff --git a/syft/presenter/json/relationship.go b/internal/presenter/packages/json_relationship.go similarity index 63% rename from syft/presenter/json/relationship.go rename to internal/presenter/packages/json_relationship.go index 25eee2a3c..326e4a4a5 100644 --- a/syft/presenter/json/relationship.go +++ b/internal/presenter/packages/json_relationship.go @@ -1,18 +1,18 @@ -package json +package packages import "github.com/anchore/syft/syft/pkg" -type Relationship struct { +type JSONRelationship struct { Parent string `json:"parent"` Child string `json:"child"` Type string `json:"type"` Metadata interface{} `json:"metadata"` } -func newRelationships(relationships []pkg.Relationship) []Relationship { - result := make([]Relationship, len(relationships)) +func newJSONRelationships(relationships []pkg.Relationship) []JSONRelationship { + result := make([]JSONRelationship, len(relationships)) for i, r := range relationships { - result[i] = Relationship{ + result[i] = JSONRelationship{ Parent: string(r.Parent), Child: string(r.Child), Type: string(r.Type), diff --git a/internal/presenter/packages/json_source.go b/internal/presenter/packages/json_source.go new file mode 100644 index 000000000..d0c2a477f --- /dev/null +++ b/internal/presenter/packages/json_source.go @@ -0,0 +1,39 @@ +package packages + +import ( + "fmt" + + "github.com/anchore/syft/syft/source" +) + +// JSONSource object represents the thing that was cataloged +type JSONSource struct { + Type string `json:"type"` + Target interface{} `json:"target"` +} + +type JSONImageSource struct { + source.ImageMetadata + Scope source.Scope `json:"scope"` +} + +// NewJSONSource creates a new source object to be represented into JSON. +func NewJSONSource(src source.Metadata, scope source.Scope) (JSONSource, error) { + switch src.Scheme { + case source.ImageScheme: + return JSONSource{ + Type: "image", + Target: JSONImageSource{ + Scope: scope, + ImageMetadata: src.ImageMetadata, + }, + }, nil + case source.DirectoryScheme: + return JSONSource{ + Type: "directory", + Target: src.Path, + }, nil + default: + return JSONSource{}, fmt.Errorf("unsupported source: %q", src.Scheme) + } +} diff --git a/internal/presenter/packages/presenter.go b/internal/presenter/packages/presenter.go new file mode 100644 index 000000000..c73763b5c --- /dev/null +++ b/internal/presenter/packages/presenter.go @@ -0,0 +1,25 @@ +/* +Defines a Presenter interface for displaying catalog results to an io.Writer as well as a helper utility to obtain +a specific Presenter implementation given user configuration. +*/ +package packages + +import ( + "github.com/anchore/syft/internal/presenter" +) + +// Presenter returns a presenter for images or directories +func Presenter(option PresenterOption, config PresenterConfig) presenter.Presenter { + switch option { + case JSONPresenterOption: + return NewJSONPresenter(config.Catalog, config.SourceMetadata, config.Distro, config.Scope) + case TextPresenterOption: + return NewTextPresenter(config.Catalog, config.SourceMetadata) + case TablePresenterOption: + return NewTablePresenter(config.Catalog) + case CycloneDxPresenterOption: + return NewCycloneDxPresenter(config.Catalog, config.SourceMetadata) + default: + return nil + } +} diff --git a/internal/presenter/packages/presenter_config.go b/internal/presenter/packages/presenter_config.go new file mode 100644 index 000000000..d318e4288 --- /dev/null +++ b/internal/presenter/packages/presenter_config.go @@ -0,0 +1,14 @@ +package packages + +import ( + "github.com/anchore/syft/syft/distro" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +type PresenterConfig struct { + SourceMetadata source.Metadata + Catalog *pkg.Catalog + Distro *distro.Distro + Scope source.Scope +} diff --git a/internal/presenter/packages/presenter_option.go b/internal/presenter/packages/presenter_option.go new file mode 100644 index 000000000..9bfe1f6f2 --- /dev/null +++ b/internal/presenter/packages/presenter_option.go @@ -0,0 +1,35 @@ +package packages + +import "strings" + +const ( + UnknownPresenterOption PresenterOption = "UnknownPresenterOption" + JSONPresenterOption PresenterOption = "json" + TextPresenterOption PresenterOption = "text" + TablePresenterOption PresenterOption = "table" + CycloneDxPresenterOption PresenterOption = "cyclonedx" +) + +var AllPresenters = []PresenterOption{ + JSONPresenterOption, + TextPresenterOption, + TablePresenterOption, + CycloneDxPresenterOption, +} + +type PresenterOption string + +func ParsePresenterOption(userStr string) PresenterOption { + switch strings.ToLower(userStr) { + case string(JSONPresenterOption): + return JSONPresenterOption + case string(TextPresenterOption): + return TextPresenterOption + case string(TablePresenterOption): + return TablePresenterOption + case string(CycloneDxPresenterOption), "cyclone", "cyclone-dx": + return CycloneDxPresenterOption + default: + return UnknownPresenterOption + } +} diff --git a/syft/presenter/table/presenter.go b/internal/presenter/packages/table_presenter.go similarity index 86% rename from syft/presenter/table/presenter.go rename to internal/presenter/packages/table_presenter.go index c7303e8a5..c3313996e 100644 --- a/syft/presenter/table/presenter.go +++ b/internal/presenter/packages/table_presenter.go @@ -1,4 +1,4 @@ -package table +package packages import ( "fmt" @@ -11,17 +11,17 @@ import ( "github.com/anchore/syft/syft/pkg" ) -type Presenter struct { +type TablePresenter struct { catalog *pkg.Catalog } -func NewPresenter(catalog *pkg.Catalog) *Presenter { - return &Presenter{ +func NewTablePresenter(catalog *pkg.Catalog) *TablePresenter { + return &TablePresenter{ catalog: catalog, } } -func (pres *Presenter) Present(output io.Writer) error { +func (pres *TablePresenter) Present(output io.Writer) error { rows := make([][]string, 0) columns := []string{"Name", "Version", "Type"} @@ -42,7 +42,7 @@ func (pres *Presenter) Present(output io.Writer) error { // sort by name, version, then type sort.SliceStable(rows, func(i, j int) bool { for col := 0; col < len(columns); col++ { - if rows[i][0] != rows[j][0] { + if rows[i][col] != rows[j][col] { return rows[i][col] < rows[j][col] } } diff --git a/syft/presenter/table/presenter_test.go b/internal/presenter/packages/table_presenter_test.go similarity index 87% rename from syft/presenter/table/presenter_test.go rename to internal/presenter/packages/table_presenter_test.go index fb120494b..54e1db38e 100644 --- a/syft/presenter/table/presenter_test.go +++ b/internal/presenter/packages/table_presenter_test.go @@ -1,4 +1,4 @@ -package table +package packages import ( "bytes" @@ -16,7 +16,7 @@ import ( "github.com/sergi/go-diff/diffmatchpatch" ) -var update = flag.Bool("update", false, "update the *.golden files for table presenters") +var updateTablePresenterGoldenFiles = flag.Bool("update-table", false, "update the *.golden files for table presenters") func TestTablePresenter(t *testing.T) { var buffer bytes.Buffer @@ -24,8 +24,7 @@ func TestTablePresenter(t *testing.T) { testImage := "image-simple" catalog := pkg.NewCatalog() - img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", testImage) - defer cleanup() + img := imagetest.GetFixtureImage(t, "docker-archive", testImage) _, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks) _, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks) @@ -48,7 +47,7 @@ func TestTablePresenter(t *testing.T) { Type: pkg.DebPkg, }) - pres := NewPresenter(catalog) + pres := NewTablePresenter(catalog) // run presenter err := pres.Present(&buffer) @@ -57,7 +56,7 @@ func TestTablePresenter(t *testing.T) { } actual := buffer.Bytes() - if *update { + if *updateTablePresenterGoldenFiles { testutils.UpdateGoldenFileContents(t, actual) } diff --git a/syft/presenter/text/test-fixtures/image-simple/Dockerfile b/internal/presenter/packages/test-fixtures/image-simple/Dockerfile similarity index 100% rename from syft/presenter/text/test-fixtures/image-simple/Dockerfile rename to internal/presenter/packages/test-fixtures/image-simple/Dockerfile diff --git a/syft/presenter/cyclonedx/test-fixtures/image-simple/file-1.txt b/internal/presenter/packages/test-fixtures/image-simple/file-1.txt similarity index 100% rename from syft/presenter/cyclonedx/test-fixtures/image-simple/file-1.txt rename to internal/presenter/packages/test-fixtures/image-simple/file-1.txt diff --git a/syft/presenter/cyclonedx/test-fixtures/image-simple/file-2.txt b/internal/presenter/packages/test-fixtures/image-simple/file-2.txt similarity index 100% rename from syft/presenter/cyclonedx/test-fixtures/image-simple/file-2.txt rename to internal/presenter/packages/test-fixtures/image-simple/file-2.txt diff --git a/syft/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxDirsPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestCycloneDxDirsPresenter.golden similarity index 100% rename from syft/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxDirsPresenter.golden rename to internal/presenter/packages/test-fixtures/snapshot/TestCycloneDxDirsPresenter.golden diff --git a/syft/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden similarity index 100% rename from syft/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden rename to internal/presenter/packages/test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden diff --git a/syft/presenter/table/test-fixtures/snapshot/TestTablePresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestTablePresenter.golden similarity index 100% rename from syft/presenter/table/test-fixtures/snapshot/TestTablePresenter.golden rename to internal/presenter/packages/test-fixtures/snapshot/TestTablePresenter.golden diff --git a/syft/presenter/text/test-fixtures/snapshot/TestTextDirPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestTextDirPresenter.golden similarity index 100% rename from syft/presenter/text/test-fixtures/snapshot/TestTextDirPresenter.golden rename to internal/presenter/packages/test-fixtures/snapshot/TestTextDirPresenter.golden diff --git a/syft/presenter/text/test-fixtures/snapshot/TestTextImgPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestTextImgPresenter.golden similarity index 100% rename from syft/presenter/text/test-fixtures/snapshot/TestTextImgPresenter.golden rename to internal/presenter/packages/test-fixtures/snapshot/TestTextImgPresenter.golden diff --git a/syft/presenter/text/test-fixtures/snapshot/TestTextPresenter.golden b/internal/presenter/packages/test-fixtures/snapshot/TestTextPresenter.golden similarity index 100% rename from syft/presenter/text/test-fixtures/snapshot/TestTextPresenter.golden rename to internal/presenter/packages/test-fixtures/snapshot/TestTextPresenter.golden diff --git a/syft/presenter/text/presenter.go b/internal/presenter/packages/text_presenter.go similarity index 77% rename from syft/presenter/text/presenter.go rename to internal/presenter/packages/text_presenter.go index 8e86fea47..44127da64 100644 --- a/syft/presenter/text/presenter.go +++ b/internal/presenter/packages/text_presenter.go @@ -1,4 +1,4 @@ -package text +package packages import ( "fmt" @@ -10,22 +10,22 @@ import ( "github.com/anchore/syft/syft/source" ) -// Presenter is a human-friendly text presenter to represent package and source data. -type Presenter struct { +// TextPresenter is a human-friendly text presenter to represent package and source data. +type TextPresenter struct { catalog *pkg.Catalog srcMetadata source.Metadata } -// NewPresenter creates a new presenter for the given set of catalog and image data. -func NewPresenter(catalog *pkg.Catalog, srcMetadata source.Metadata) *Presenter { - return &Presenter{ +// NewTextPresenter creates a new presenter for the given set of catalog and image data. +func NewTextPresenter(catalog *pkg.Catalog, srcMetadata source.Metadata) *TextPresenter { + return &TextPresenter{ catalog: catalog, srcMetadata: srcMetadata, } } // Present is a method that is in charge of writing to an output buffer -func (pres *Presenter) Present(output io.Writer) error { +func (pres *TextPresenter) Present(output io.Writer) error { // init the tabular writer w := new(tabwriter.Writer) w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight) diff --git a/syft/presenter/text/presenter_test.go b/internal/presenter/packages/text_presenter_test.go similarity index 86% rename from syft/presenter/text/presenter_test.go rename to internal/presenter/packages/text_presenter_test.go index ccac4b3d9..3633149cb 100644 --- a/syft/presenter/text/presenter_test.go +++ b/internal/presenter/packages/text_presenter_test.go @@ -1,4 +1,4 @@ -package text +package packages import ( "bytes" @@ -14,7 +14,7 @@ import ( "github.com/sergi/go-diff/diffmatchpatch" ) -var update = flag.Bool("update", false, "update the *.golden files for text presenters") +var updateTextPresenterGoldenFiles = flag.Bool("update-text", false, "update the *.golden files for text presenters") func TestTextDirPresenter(t *testing.T) { var buffer bytes.Buffer @@ -37,7 +37,7 @@ func TestTextDirPresenter(t *testing.T) { if err != nil { t.Fatalf("unable to create source: %+v", err) } - pres := NewPresenter(catalog, s.Metadata) + pres := NewTextPresenter(catalog, s.Metadata) // run presenter err = pres.Present(&buffer) @@ -46,7 +46,7 @@ func TestTextDirPresenter(t *testing.T) { } actual := buffer.Bytes() - if *update { + if *updateTextPresenterGoldenFiles { testutils.UpdateGoldenFileContents(t, actual) } @@ -69,8 +69,7 @@ func TestTextImgPresenter(t *testing.T) { var buffer bytes.Buffer catalog := pkg.NewCatalog() - img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-simple") - defer cleanup() + img := imagetest.GetFixtureImage(t, "docker-archive", "image-simple") _, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks) _, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks) @@ -102,18 +101,18 @@ func TestTextImgPresenter(t *testing.T) { l.Metadata.Digest = "sha256:ad8ecdc058976c07e7e347cb89fa9ad86a294b5ceaae6d09713fb035f84115abf3c4a2388a4af3aa60f13b94f4c6846930bdf53" } - s, err := source.NewFromImage(img, source.SquashedScope, "user-image-input") + s, err := source.NewFromImage(img, "user-image-input") if err != nil { t.Fatal(err) } - pres := NewPresenter(catalog, s.Metadata) + pres := NewTextPresenter(catalog, s.Metadata) // run presenter err = pres.Present(&buffer) if err != nil { t.Fatal(err) } actual := buffer.Bytes() - if *update { + if *updateTextPresenterGoldenFiles { testutils.UpdateGoldenFileContents(t, actual) } diff --git a/internal/presenter/poweruser/json_document.go b/internal/presenter/poweruser/json_document.go new file mode 100644 index 000000000..d59b38c93 --- /dev/null +++ b/internal/presenter/poweruser/json_document.go @@ -0,0 +1,32 @@ +package poweruser + +import ( + "github.com/anchore/syft/internal/presenter/packages" +) + +type JSONDocument struct { + // note: poweruser.JSONDocument is meant to always be a superset of packages.JSONDocument, any additional fields + // here should be optional by supplying "omitempty" on these fields hint to the jsonschema generator to not + // require these fields. As an accepted rule in this repo all collections should still be initialized in the + // context of being used in a JSON document. + FileMetadata []JSONFileMetadata `json:"fileMetadata,omitempty"` + packages.JSONDocument +} + +// NewJSONDocument creates and populates a new JSON document struct from the given cataloging results. +func NewJSONDocument(config JSONDocumentConfig) (JSONDocument, error) { + pkgsDoc, err := packages.NewJSONDocument(config.PackageCatalog, config.SourceMetadata, config.Distro, config.ApplicationConfig.Packages.ScopeOpt, config.ApplicationConfig) + if err != nil { + return JSONDocument{}, err + } + + fileMetadata, err := NewJSONFileMetadata(config.FileMetadata, config.FileDigests) + if err != nil { + return JSONDocument{}, err + } + + return JSONDocument{ + FileMetadata: fileMetadata, + JSONDocument: pkgsDoc, + }, nil +} diff --git a/internal/presenter/poweruser/json_document_config.go b/internal/presenter/poweruser/json_document_config.go new file mode 100644 index 000000000..fc2f053da --- /dev/null +++ b/internal/presenter/poweruser/json_document_config.go @@ -0,0 +1,18 @@ +package poweruser + +import ( + "github.com/anchore/syft/internal/config" + "github.com/anchore/syft/syft/distro" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +type JSONDocumentConfig struct { + ApplicationConfig config.Application + PackageCatalog *pkg.Catalog + FileMetadata map[source.Location]source.FileMetadata + FileDigests map[source.Location][]file.Digest + Distro *distro.Distro + SourceMetadata source.Metadata +} diff --git a/internal/presenter/poweruser/json_file_metadata.go b/internal/presenter/poweruser/json_file_metadata.go new file mode 100644 index 000000000..368525a91 --- /dev/null +++ b/internal/presenter/poweruser/json_file_metadata.go @@ -0,0 +1,50 @@ +package poweruser + +import ( + "fmt" + "strconv" + + "github.com/anchore/syft/syft/file" + + "github.com/anchore/syft/syft/source" +) + +type JSONFileMetadata struct { + Location source.Location `json:"location"` + Metadata JSONFileMetadataEntry `json:"metadata"` +} + +type JSONFileMetadataEntry struct { + Mode int `json:"mode"` + Type source.FileType `json:"type"` + UserID int `json:"userID"` + GroupID int `json:"groupID"` + Digests []file.Digest `json:"digests"` +} + +func NewJSONFileMetadata(data map[source.Location]source.FileMetadata, digests map[source.Location][]file.Digest) ([]JSONFileMetadata, error) { + results := make([]JSONFileMetadata, 0) + for location, metadata := range data { + mode, err := strconv.Atoi(fmt.Sprintf("%o", metadata.Mode)) + if err != nil { + return nil, fmt.Errorf("invalid mode found in file catalog @ location=%+v mode=%q: %w", location, metadata.Mode, err) + } + + digestResults := make([]file.Digest, 0) + if digestsForLocation, exists := digests[location]; exists { + digestResults = digestsForLocation + } + + results = append(results, JSONFileMetadata{ + Location: location, + Metadata: JSONFileMetadataEntry{ + Mode: mode, + Type: metadata.Type, + UserID: metadata.UserID, + GroupID: metadata.GroupID, + Digests: digestResults, + }, + }) + } + return results, nil +} diff --git a/internal/presenter/poweruser/json_presenter.go b/internal/presenter/poweruser/json_presenter.go new file mode 100644 index 000000000..9432693f3 --- /dev/null +++ b/internal/presenter/poweruser/json_presenter.go @@ -0,0 +1,32 @@ +package poweruser + +import ( + "encoding/json" + "io" +) + +// JSONPresenter is a JSON presentation object for the syft results +type JSONPresenter struct { + config JSONDocumentConfig +} + +// NewJSONPresenter creates a new JSON presenter object for the given cataloging results. +func NewJSONPresenter(config JSONDocumentConfig) *JSONPresenter { + return &JSONPresenter{ + config: config, + } +} + +// Present the PackageCatalog results to the given writer. +func (p *JSONPresenter) Present(output io.Writer) error { + doc, err := NewJSONDocument(p.config) + if err != nil { + return err + } + + enc := json.NewEncoder(output) + // prevent > and < from being escaped in the payload + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + return enc.Encode(&doc) +} diff --git a/internal/presenter/presenter.go b/internal/presenter/presenter.go new file mode 100644 index 000000000..823a2e38d --- /dev/null +++ b/internal/presenter/presenter.go @@ -0,0 +1,9 @@ +package presenter + +import "io" + +// Presenter defines the expected behavior for an object responsible for displaying arbitrary input and processed data +// to a given io.Writer. +type Presenter interface { + Present(io.Writer) error +} diff --git a/syft/presenter/cyclonedx/bom-descriptor.go b/syft/presenter/cyclonedx/bom-descriptor.go deleted file mode 100644 index a9a3301dd..000000000 --- a/syft/presenter/cyclonedx/bom-descriptor.go +++ /dev/null @@ -1,69 +0,0 @@ -package cyclonedx - -import ( - "encoding/xml" - "time" - - "github.com/anchore/syft/syft/source" -) - -// Source: https://cyclonedx.org/ext/bom-descriptor/ - -// BomDescriptor represents all metadata surrounding the BOM report (such as when the BOM was made, with which tool, and the item being cataloged). -type BomDescriptor struct { - XMLName xml.Name `xml:"metadata"` - Timestamp string `xml:"timestamp,omitempty"` // The date and time (timestamp) when the document was created - Tools []BdTool `xml:"tools>tool"` // The tool used to create the BOM. - Component *BdComponent `xml:"component"` // The component that the BOM describes. -} - -// BdTool represents the tool that created the BOM report. -type BdTool struct { - XMLName xml.Name `xml:"tool"` - Vendor string `xml:"vendor,omitempty"` // The vendor of the tool used to create the BOM. - Name string `xml:"name,omitempty"` // The name of the tool used to create the BOM. - Version string `xml:"version,omitempty"` // The version of the tool used to create the BOM. - // TODO: hashes, author, manufacture, supplier - // TODO: add user-defined fields for the remaining build/version parameters -} - -// BdComponent represents the software/package being cataloged. -type BdComponent struct { - XMLName xml.Name `xml:"component"` - Component -} - -// NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details. -func NewBomDescriptor(name, version string, srcMetadata source.Metadata) *BomDescriptor { - descriptor := BomDescriptor{ - XMLName: xml.Name{}, - Timestamp: time.Now().Format(time.RFC3339), - Tools: []BdTool{ - { - Vendor: "anchore", - Name: name, - Version: version, - }, - }, - } - - switch srcMetadata.Scheme { - case source.ImageScheme: - descriptor.Component = &BdComponent{ - Component: Component{ - Type: "container", - Name: srcMetadata.ImageMetadata.UserInput, - Version: srcMetadata.ImageMetadata.ManifestDigest, - }, - } - case source.DirectoryScheme: - descriptor.Component = &BdComponent{ - Component: Component{ - Type: "file", - Name: srcMetadata.Path, - }, - } - } - - return &descriptor -} diff --git a/syft/presenter/cyclonedx/component.go b/syft/presenter/cyclonedx/component.go deleted file mode 100644 index e540ef9ea..000000000 --- a/syft/presenter/cyclonedx/component.go +++ /dev/null @@ -1,27 +0,0 @@ -package cyclonedx - -import "encoding/xml" - -// Component represents a single element in the CycloneDX BOM -type Component struct { - XMLName xml.Name `xml:"component"` - Type string `xml:"type,attr"` // Required; Describes if the component is a library, framework, application, container, operating system, firmware, hardware device, or file - Supplier string `xml:"supplier,omitempty"` // The organization that supplied the component. The supplier may often be the manufacture, but may also be a distributor or repackager. - Author string `xml:"author,omitempty"` // The person(s) or organization(s) that authored the component - Publisher string `xml:"publisher,omitempty"` // The person(s) or organization(s) that published the component - Group string `xml:"group,omitempty"` // The high-level classification that a project self-describes as. This will often be a shortened, single name of the company or project that produced the component, or the source package or domain name. - Name string `xml:"name"` // Required; The name of the component as defined by the project - 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: source, hashes, copyright, cpe, purl, swid, modified, pedigree, externalReferences - // TODO: add user-defined parameters for syft-specific values (image layer index, cataloger, location path, etc.) -} - -// License represents a single software license for a Component -type License struct { - XMLName xml.Name `xml:"license"` - ID string `xml:"id,omitempty"` // A valid SPDX license ID - Name string `xml:"name,omitempty"` // If SPDX does not define the license used, this field may be used to provide the license name -} diff --git a/syft/presenter/cyclonedx/document.go b/syft/presenter/cyclonedx/document.go deleted file mode 100644 index 8791d3f9c..000000000 --- a/syft/presenter/cyclonedx/document.go +++ /dev/null @@ -1,57 +0,0 @@ -package cyclonedx - -import ( - "encoding/xml" - - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/version" - "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" - "github.com/google/uuid" -) - -// Source: https://github.com/CycloneDX/specification - -// Document represents a CycloneDX BOM Document. -type Document struct { - XMLName xml.Name `xml:"bom"` - XMLNs string `xml:"xmlns,attr"` - Version int `xml:"version,attr"` - SerialNumber string `xml:"serialNumber,attr"` - BomDescriptor *BomDescriptor `xml:"metadata"` // The BOM descriptor extension - Components []Component `xml:"components>component"` // The BOM contents -} - -// NewDocumentFromCatalog returns a CycloneDX Document object populated with the catalog contents. -func NewDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) Document { - versionInfo := version.FromBuild() - - doc := Document{ - XMLNs: "http://cyclonedx.org/schema/bom/1.2", - 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 - Name: p.Name, - Version: p.Version, - PackageURL: p.PURL, - } - var licenses []License - for _, licenseName := range p.Licenses { - licenses = append(licenses, License{ - Name: licenseName, - }) - } - if len(licenses) > 0 { - component.Licenses = &licenses - } - doc.Components = append(doc.Components, component) - } - - return doc -} diff --git a/syft/presenter/cyclonedx/test-fixtures/image-simple/Dockerfile b/syft/presenter/cyclonedx/test-fixtures/image-simple/Dockerfile deleted file mode 100644 index 62fb151e4..000000000 --- a/syft/presenter/cyclonedx/test-fixtures/image-simple/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -# Note: changes to this file will result in updating several test values. Consider making a new image fixture instead of editing this one. -FROM scratch -ADD file-1.txt /somefile-1.txt -ADD file-2.txt /somefile-2.txt -# note: adding a directory will behave differently on docker engine v18 vs v19 -ADD target / diff --git a/syft/presenter/cyclonedx/test-fixtures/image-simple/target/really/nested/file-3.txt b/syft/presenter/cyclonedx/test-fixtures/image-simple/target/really/nested/file-3.txt deleted file mode 100644 index f85472c93..000000000 --- a/syft/presenter/cyclonedx/test-fixtures/image-simple/target/really/nested/file-3.txt +++ /dev/null @@ -1,2 +0,0 @@ -another file! -with lines... \ No newline at end of file diff --git a/syft/presenter/json/descriptor.go b/syft/presenter/json/descriptor.go deleted file mode 100644 index de07721fc..000000000 --- a/syft/presenter/json/descriptor.go +++ /dev/null @@ -1,7 +0,0 @@ -package json - -// Descriptor describes what created the document as well as surrounding metadata -type Descriptor struct { - Name string `json:"name"` - Version string `json:"version"` -} diff --git a/syft/presenter/json/document.go b/syft/presenter/json/document.go deleted file mode 100644 index 5f04cdfcf..000000000 --- a/syft/presenter/json/document.go +++ /dev/null @@ -1,54 +0,0 @@ -package json - -import ( - "fmt" - - "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" -) - -// Document represents the syft cataloging findings as a JSON document -type Document struct { - Artifacts []Package `json:"artifacts"` // Artifacts is the list of packages discovered and placed into the catalog - Source Source `json:"source"` // Source represents the original object that was cataloged - Distro Distribution `json:"distro"` // Distro represents the Linux distribution that was detected from the source - Descriptor Descriptor `json:"descriptor"` // Descriptor is a block containing self-describing information about syft - Schema Schema `json:"schema"` // Schema is a block reserved for defining the version for the shape of this JSON document and where to find the schema document to validate the shape - ArtifactRelationships []Relationship `json:"artifactRelationships"` -} - -// NewDocument creates and populates a new JSON document struct from the given cataloging results. -func NewDocument(catalog *pkg.Catalog, srcMetadata source.Metadata, d *distro.Distro) (Document, error) { - src, err := NewSource(srcMetadata) - if err != nil { - return Document{}, nil - } - - doc := Document{ - Artifacts: make([]Package, 0), - Source: src, - Distro: NewDistribution(d), - Descriptor: Descriptor{ - Name: internal.ApplicationName, - Version: version.FromBuild().Version, - }, - Schema: Schema{ - Version: internal.JSONSchemaVersion, - URL: fmt.Sprintf("https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-%s.json", internal.JSONSchemaVersion), - }, - ArtifactRelationships: newRelationships(pkg.NewRelationships(catalog)), - } - - for _, p := range catalog.Sorted() { - art, err := NewPackage(p) - if err != nil { - return Document{}, err - } - doc.Artifacts = append(doc.Artifacts, art) - } - - return doc, nil -} diff --git a/syft/presenter/json/presenter.go b/syft/presenter/json/presenter.go deleted file mode 100644 index 845ecd1bf..000000000 --- a/syft/presenter/json/presenter.go +++ /dev/null @@ -1,40 +0,0 @@ -package json - -import ( - "encoding/json" - "io" - - "github.com/anchore/syft/syft/distro" - "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" -) - -// Presenter is a JSON presentation object for the syft results -type Presenter struct { - catalog *pkg.Catalog - srcMetadata source.Metadata - distro *distro.Distro -} - -// NewPresenter creates a new JSON presenter object for the given cataloging results. -func NewPresenter(catalog *pkg.Catalog, s source.Metadata, d *distro.Distro) *Presenter { - return &Presenter{ - catalog: catalog, - srcMetadata: s, - distro: d, - } -} - -// Present the catalog results to the given writer. -func (pres *Presenter) Present(output io.Writer) error { - doc, err := NewDocument(pres.catalog, pres.srcMetadata, pres.distro) - if err != nil { - return err - } - - enc := json.NewEncoder(output) - // prevent > and < from being escaped in the payload - enc.SetEscapeHTML(false) - enc.SetIndent("", " ") - return enc.Encode(&doc) -} diff --git a/syft/presenter/json/schema.go b/syft/presenter/json/schema.go deleted file mode 100644 index d7f8d27ec..000000000 --- a/syft/presenter/json/schema.go +++ /dev/null @@ -1,6 +0,0 @@ -package json - -type Schema struct { - Version string `json:"version"` - URL string `json:"url"` -} diff --git a/syft/presenter/json/source.go b/syft/presenter/json/source.go deleted file mode 100644 index 7a5ad6ad2..000000000 --- a/syft/presenter/json/source.go +++ /dev/null @@ -1,75 +0,0 @@ -package json - -import ( - "encoding/json" - "fmt" - - "github.com/anchore/syft/syft/source" -) - -// Source object represents the thing that was cataloged -type Source struct { - Type string `json:"type"` - Target interface{} `json:"target"` -} - -// sourceUnpacker is used to unmarshal Source objects -type sourceUnpacker struct { - Type string `json:"type"` - Target json.RawMessage `json:"target"` -} - -// NewSource creates a new source object to be represented into JSON. -func NewSource(src source.Metadata) (Source, error) { - switch src.Scheme { - case source.ImageScheme: - return Source{ - Type: "image", - Target: src.ImageMetadata, - }, nil - case source.DirectoryScheme: - return Source{ - Type: "directory", - Target: src.Path, - }, nil - default: - return Source{}, fmt.Errorf("unsupported source: %T", src) - } -} - -// UnmarshalJSON populates a source object from JSON bytes. -func (s *Source) UnmarshalJSON(b []byte) error { - var unpacker sourceUnpacker - if err := json.Unmarshal(b, &unpacker); err != nil { - return err - } - - s.Type = unpacker.Type - - switch s.Type { - case "image": - var payload source.ImageMetadata - if err := json.Unmarshal(unpacker.Target, &payload); err != nil { - return err - } - s.Target = payload - default: - return fmt.Errorf("unsupported package metadata type: %+v", s.Type) - } - - return nil -} - -// ToSourceMetadata takes a source object represented from JSON and creates a source.Metadata object. -func (s *Source) ToSourceMetadata() source.Metadata { - var metadata source.Metadata - switch s.Type { - case "directory": - metadata.Scheme = source.DirectoryScheme - metadata.Path = s.Target.(string) - case "image": - metadata.Scheme = source.ImageScheme - metadata.ImageMetadata = s.Target.(source.ImageMetadata) - } - return metadata -} diff --git a/syft/presenter/json/test-fixtures/image-simple/Dockerfile b/syft/presenter/json/test-fixtures/image-simple/Dockerfile deleted file mode 100644 index 62fb151e4..000000000 --- a/syft/presenter/json/test-fixtures/image-simple/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -# Note: changes to this file will result in updating several test values. Consider making a new image fixture instead of editing this one. -FROM scratch -ADD file-1.txt /somefile-1.txt -ADD file-2.txt /somefile-2.txt -# note: adding a directory will behave differently on docker engine v18 vs v19 -ADD target / diff --git a/syft/presenter/json/test-fixtures/image-simple/file-1.txt b/syft/presenter/json/test-fixtures/image-simple/file-1.txt deleted file mode 100644 index 985d3408e..000000000 --- a/syft/presenter/json/test-fixtures/image-simple/file-1.txt +++ /dev/null @@ -1 +0,0 @@ -this file has contents \ No newline at end of file diff --git a/syft/presenter/json/test-fixtures/image-simple/file-2.txt b/syft/presenter/json/test-fixtures/image-simple/file-2.txt deleted file mode 100644 index 396d08bbc..000000000 --- a/syft/presenter/json/test-fixtures/image-simple/file-2.txt +++ /dev/null @@ -1 +0,0 @@ -file-2 contents! \ No newline at end of file diff --git a/syft/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt b/syft/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt deleted file mode 100644 index f85472c93..000000000 --- a/syft/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt +++ /dev/null @@ -1,2 +0,0 @@ -another file! -with lines... \ No newline at end of file diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden deleted file mode 100644 index 721fe5bac..000000000 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ /dev/null @@ -1,81 +0,0 @@ -{ - "artifacts": [ - { - "id": "package-1-id", - "name": "package-1", - "version": "1.0.1", - "type": "python", - "foundBy": "the-cataloger-1", - "locations": [ - { - "path": "/some/path/pkg1" - } - ], - "licenses": [ - "MIT" - ], - "language": "python", - "cpes": [ - "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" - ], - "purl": "a-purl-2", - "metadataType": "PythonPackageMetadata", - "metadata": { - "name": "package-1", - "version": "1.0.1", - "license": "", - "author": "", - "authorEmail": "", - "platform": "", - "sitePackagesRootPath": "" - } - }, - { - "id": "package-2-id", - "name": "package-2", - "version": "2.0.1", - "type": "deb", - "foundBy": "the-cataloger-2", - "locations": [ - { - "path": "/some/path/pkg1" - } - ], - "licenses": [], - "language": "", - "cpes": [ - "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" - ], - "purl": "a-purl-2", - "metadataType": "DpkgMetadata", - "metadata": { - "package": "package-2", - "source": "", - "version": "2.0.1", - "sourceVersion": "", - "architecture": "", - "maintainer": "", - "installedSize": 0, - "files": null - } - } - ], - "source": { - "type": "directory", - "target": "/some/path" - }, - "distro": { - "name": "", - "version": "", - "idLike": "" - }, - "descriptor": { - "name": "syft", - "version": "[not provided]" - }, - "schema": { - "version": "1.0.3", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.3.json" - }, - "artifactRelationships": [] -} diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden deleted file mode 100644 index 7d9e4889d..000000000 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ /dev/null @@ -1,112 +0,0 @@ -{ - "artifacts": [ - { - "id": "package-1-id", - "name": "package-1", - "version": "1.0.1", - "type": "python", - "foundBy": "the-cataloger-1", - "locations": [ - { - "path": "/somefile-1.txt", - "layerID": "sha256:2abd6dc7d143b384177da9a30f5311eb1f6c4e920eb6e218ec32b58c1a8806b1" - } - ], - "licenses": [ - "MIT" - ], - "language": "python", - "cpes": [ - "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" - ], - "purl": "a-purl-1", - "metadataType": "PythonPackageMetadata", - "metadata": { - "name": "package-1", - "version": "1.0.1", - "license": "", - "author": "", - "authorEmail": "", - "platform": "", - "sitePackagesRootPath": "" - } - }, - { - "id": "package-2-id", - "name": "package-2", - "version": "2.0.1", - "type": "deb", - "foundBy": "the-cataloger-2", - "locations": [ - { - "path": "/somefile-2.txt", - "layerID": "sha256:63574b0cbd9fcfc799d82a449c0975ee3f3560747b0957f042938bdec902c4cc" - } - ], - "licenses": [], - "language": "", - "cpes": [ - "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" - ], - "purl": "a-purl-2", - "metadataType": "DpkgMetadata", - "metadata": { - "package": "package-2", - "source": "", - "version": "2.0.1", - "sourceVersion": "", - "architecture": "", - "maintainer": "", - "installedSize": 0, - "files": null - } - } - ], - "source": { - "type": "image", - "target": { - "userInput": "user-image-input", - "imageID": "sha256:0a7326bbb1c3c467969c7a73deb6366b1e34eb034616b97f8614ad662cad1ce1", - "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "tags": [ - "stereoscope-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7" - ], - "imageSize": 65, - "scope": "Squashed", - "layers": [ - { - "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:2abd6dc7d143b384177da9a30f5311eb1f6c4e920eb6e218ec32b58c1a8806b1", - "size": 22 - }, - { - "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:63574b0cbd9fcfc799d82a449c0975ee3f3560747b0957f042938bdec902c4cc", - "size": 16 - }, - { - "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:b102400548006e8dfa3abf6e2b2e6f1c440e29936d91a36e145736e2fd8cf0a1", - "size": 27 - } - ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoxODA4LCJkaWdlc3QiOiJzaGEyNTY6MGE3MzI2YmJiMWMzYzQ2Nzk2OWM3YTczZGViNjM2NmIxZTM0ZWIwMzQ2MTZiOTdmODYxNGFkNjYyY2FkMWNlMSJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMDQ4LCJkaWdlc3QiOiJzaGEyNTY6MmFiZDZkYzdkMTQzYjM4NDE3N2RhOWEzMGY1MzExZWIxZjZjNGU5MjBlYjZlMjE4ZWMzMmI1OGMxYTg4MDZiMSJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo2MzU3NGIwY2JkOWZjZmM3OTlkODJhNDQ5YzA5NzVlZTNmMzU2MDc0N2IwOTU3ZjA0MjkzOGJkZWM5MDJjNGNjIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MzU4NCwiZGlnZXN0Ijoic2hhMjU2OmIxMDI0MDA1NDgwMDZlOGRmYTNhYmY2ZTJiMmU2ZjFjNDQwZTI5OTM2ZDkxYTM2ZTE0NTczNmUyZmQ4Y2YwYTEifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpudWxsLCJJbWFnZSI6InNoYTI1Njo4YjlhNTM1MmM3YzU3M2RmNTkxYTYwMzA0NzIxMmU3ZmYzOGI3YjRiYzZiMzk1NmI3Zjk2Nzk0MWU3NGMyMjhkIiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCIjKG5vcCkgQUREIGRpcjpjOTM3YzZhYTUwODkwN2UyODUwOWI2NDRhMTJmOGQ2YzY3ZDM0ZTk2OWY4M2IxNGRlZTkzZWExN2Q3NjkwMjhhIGluIC8gIl0sIkltYWdlIjoic2hhMjU2OjhiOWE1MzUyYzdjNTczZGY1OTFhNjAzMDQ3MjEyZTdmZjM4YjdiNGJjNmIzOTU2YjdmOTY3OTQxZTc0YzIyOGQiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDMtMjJUMTM6MDg6MzUuNDkyOTI1MzAzWiIsImRvY2tlcl92ZXJzaW9uIjoiMjAuMTAuMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTAzLTIyVDEzOjA4OjM1LjA5NDMxMjQ3NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YWMzMmRhMjNkNTFlODAxZjAyZjkyNDEyM2VkMzA5OTBlYjNmMGZlYzFiOWVkNGYwYjA2YzI0ZTg4YjljMzY5NSBpbiAvc29tZWZpbGUtMS50eHQgIn0seyJjcmVhdGVkIjoiMjAyMS0wMy0yMlQxMzowODozNS4yODk5MjIwNTFaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOmRmM2I3NDRmNTRhOWIxNmI5YjlhZWQ0MGUzZTk4ZDljYTJiNDlmNWE3N2Q5ZmE4YTk3NjkwZDdiYWY1ODg4MjAgaW4gL3NvbWVmaWxlLTIudHh0ICJ9LHsiY3JlYXRlZCI6IjIwMjEtMDMtMjJUMTM6MDg6MzUuNDkyOTI1MzAzWiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSBBREQgZGlyOmM5MzdjNmFhNTA4OTA3ZTI4NTA5YjY0NGExMmY4ZDZjNjdkMzRlOTY5ZjgzYjE0ZGVlOTNlYTE3ZDc2OTAyOGEgaW4gLyAifV0sIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1NjoyYWJkNmRjN2QxNDNiMzg0MTc3ZGE5YTMwZjUzMTFlYjFmNmM0ZTkyMGViNmUyMThlYzMyYjU4YzFhODgwNmIxIiwic2hhMjU2OjYzNTc0YjBjYmQ5ZmNmYzc5OWQ4MmE0NDljMDk3NWVlM2YzNTYwNzQ3YjA5NTdmMDQyOTM4YmRlYzkwMmM0Y2MiLCJzaGEyNTY6YjEwMjQwMDU0ODAwNmU4ZGZhM2FiZjZlMmIyZTZmMWM0NDBlMjk5MzZkOTFhMzZlMTQ1NzM2ZTJmZDhjZjBhMSJdfX0=" - } - }, - "distro": { - "name": "", - "version": "", - "idLike": "" - }, - "descriptor": { - "name": "syft", - "version": "[not provided]" - }, - "schema": { - "version": "1.0.3", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.3.json" - }, - "artifactRelationships": [] -} diff --git a/syft/presenter/json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/presenter/json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden deleted file mode 100644 index 6c23e82fd..000000000 Binary files a/syft/presenter/json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and /dev/null differ diff --git a/syft/presenter/option.go b/syft/presenter/option.go deleted file mode 100644 index fe9b125d9..000000000 --- a/syft/presenter/option.go +++ /dev/null @@ -1,35 +0,0 @@ -package presenter - -import "strings" - -const ( - UnknownPresenter Option = "UnknownPresenter" - JSONPresenter Option = "json" - TextPresenter Option = "text" - TablePresenter Option = "table" - CycloneDxPresenter Option = "cyclonedx" -) - -var Options = []Option{ - JSONPresenter, - TextPresenter, - TablePresenter, - CycloneDxPresenter, -} - -type Option string - -func ParseOption(userStr string) Option { - switch strings.ToLower(userStr) { - case string(JSONPresenter): - return JSONPresenter - case string(TextPresenter): - return TextPresenter - case string(TablePresenter): - return TablePresenter - case string(CycloneDxPresenter), "cyclone", "cyclone-dx": - return CycloneDxPresenter - default: - return UnknownPresenter - } -} diff --git a/syft/presenter/presenter.go b/syft/presenter/presenter.go deleted file mode 100644 index 8bf3e46fb..000000000 --- a/syft/presenter/presenter.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Defines a Presenter interface for displaying catalog results to an io.Writer as well as a helper utility to obtain -a specific Presenter implementation given user configuration. -*/ -package presenter - -import ( - "io" - - "github.com/anchore/syft/syft/distro" - - "github.com/anchore/syft/syft/presenter/cyclonedx" - - "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/presenter/json" - "github.com/anchore/syft/syft/presenter/table" - "github.com/anchore/syft/syft/presenter/text" - "github.com/anchore/syft/syft/source" -) - -// Presenter defines the expected behavior for an object responsible for displaying arbitrary input and processed data -// to a given io.Writer. -type Presenter interface { - Present(io.Writer) error -} - -// GetPresenter returns a presenter for images or directories -func GetPresenter(option Option, srcMetadata source.Metadata, catalog *pkg.Catalog, d *distro.Distro) Presenter { - switch option { - case JSONPresenter: - return json.NewPresenter(catalog, srcMetadata, d) - case TextPresenter: - return text.NewPresenter(catalog, srcMetadata) - case TablePresenter: - return table.NewPresenter(catalog) - case CycloneDxPresenter: - return cyclonedx.NewPresenter(catalog, srcMetadata) - default: - return nil - } -} diff --git a/syft/presenter/table/test-fixtures/image-simple/Dockerfile b/syft/presenter/table/test-fixtures/image-simple/Dockerfile deleted file mode 100644 index 62fb151e4..000000000 --- a/syft/presenter/table/test-fixtures/image-simple/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -# Note: changes to this file will result in updating several test values. Consider making a new image fixture instead of editing this one. -FROM scratch -ADD file-1.txt /somefile-1.txt -ADD file-2.txt /somefile-2.txt -# note: adding a directory will behave differently on docker engine v18 vs v19 -ADD target / diff --git a/syft/presenter/table/test-fixtures/image-simple/file-1.txt b/syft/presenter/table/test-fixtures/image-simple/file-1.txt deleted file mode 100644 index 985d3408e..000000000 --- a/syft/presenter/table/test-fixtures/image-simple/file-1.txt +++ /dev/null @@ -1 +0,0 @@ -this file has contents \ No newline at end of file diff --git a/syft/presenter/table/test-fixtures/image-simple/file-2.txt b/syft/presenter/table/test-fixtures/image-simple/file-2.txt deleted file mode 100644 index 396d08bbc..000000000 --- a/syft/presenter/table/test-fixtures/image-simple/file-2.txt +++ /dev/null @@ -1 +0,0 @@ -file-2 contents! \ No newline at end of file diff --git a/syft/presenter/table/test-fixtures/image-simple/target/really/nested/file-3.txt b/syft/presenter/table/test-fixtures/image-simple/target/really/nested/file-3.txt deleted file mode 100644 index f85472c93..000000000 --- a/syft/presenter/table/test-fixtures/image-simple/target/really/nested/file-3.txt +++ /dev/null @@ -1,2 +0,0 @@ -another file! -with lines... \ No newline at end of file diff --git a/syft/presenter/text/test-fixtures/image-simple/file-1.txt b/syft/presenter/text/test-fixtures/image-simple/file-1.txt deleted file mode 100644 index 985d3408e..000000000 --- a/syft/presenter/text/test-fixtures/image-simple/file-1.txt +++ /dev/null @@ -1 +0,0 @@ -this file has contents \ No newline at end of file diff --git a/syft/presenter/text/test-fixtures/image-simple/file-2.txt b/syft/presenter/text/test-fixtures/image-simple/file-2.txt deleted file mode 100644 index 396d08bbc..000000000 --- a/syft/presenter/text/test-fixtures/image-simple/file-2.txt +++ /dev/null @@ -1 +0,0 @@ -file-2 contents! \ No newline at end of file diff --git a/syft/presenter/text/test-fixtures/snapshot/TestJsonPresenter.golden b/syft/presenter/text/test-fixtures/snapshot/TestJsonPresenter.golden deleted file mode 100644 index ab2fd8317..000000000 --- a/syft/presenter/text/test-fixtures/snapshot/TestJsonPresenter.golden +++ /dev/null @@ -1 +0,0 @@ -{"artifacts":[{"name":"package-1","version":"1.0.1","type":"deb","cataloger":"","sources":[],"metadata":null},{"name":"package-2","version":"2.0.1","type":"deb","cataloger":"","sources":[],"metadata":null}],"Source":"/some/path"} \ No newline at end of file