Introduce new format pattern + port json processing (#550)

* add new format pattern

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add syftjson format

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add internal formats helper

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add SBOM encode/decode to lib API

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* remove json presenter + update presenter tests to use common utils

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* remove presenter format enum type + add formats shim in presenter helper

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add MustCPE helper for tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update usage of format enum

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add test fixtures for encode/decode tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix integration test

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* migrate format detection to use reader

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* address review comments

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman
2021-10-20 21:36:34 +00:00
committed by GitHub
parent 5e315c0f17
commit 560b05c2c9
57 changed files with 3139 additions and 421 deletions
@@ -4,13 +4,15 @@ import (
"flag"
"regexp"
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
)
var updateCycloneDx = flag.Bool("update-cyclonedx", false, "update the *.golden files for cyclone-dx presenters")
func TestCycloneDxDirectoryPresenter(t *testing.T) {
catalog, metadata, _ := presenterDirectoryInput(t)
assertPresenterAgainstGoldenSnapshot(t,
catalog, metadata, _ := testutils.DirectoryInput(t)
testutils.AssertPresenterAgainstGoldenSnapshot(t,
NewCycloneDxPresenter(catalog, metadata),
*updateCycloneDx,
cycloneDxRedactor,
@@ -19,8 +21,8 @@ func TestCycloneDxDirectoryPresenter(t *testing.T) {
func TestCycloneDxImagePresenter(t *testing.T) {
testImage := "image-simple"
catalog, metadata, _ := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
catalog, metadata, _ := testutils.ImageInput(t, testImage)
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
NewCycloneDxPresenter(catalog, metadata),
testImage,
*updateCycloneDx,
@@ -1,23 +0,0 @@
package packages
import "github.com/anchore/syft/syft/distro"
// 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
}
// NewJSONDistribution creates a struct with the Linux distribution to be represented in JSON.
func NewJSONDistribution(d *distro.Distro) JSONDistribution {
if d == nil {
return JSONDistribution{}
}
return JSONDistribution{
Name: d.Name(),
Version: d.FullVersion(),
IDLike: d.IDLike,
}
}
@@ -1,62 +0,0 @@
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"`
}
@@ -1,71 +0,0 @@
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)
if catalog == nil {
return artifacts, nil
}
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
}
@@ -1,43 +0,0 @@
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)
}
@@ -1,37 +0,0 @@
package packages
import (
"flag"
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
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 {
panic(e)
}
return c
}
func TestJSONDirectoryPresenter(t *testing.T) {
catalog, metadata, dist := presenterDirectoryInput(t)
assertPresenterAgainstGoldenSnapshot(t,
NewJSONPresenter(catalog, metadata, dist, source.SquashedScope),
*updateJSONGoldenFiles,
)
}
func TestJSONImagePresenter(t *testing.T) {
testImage := "image-simple"
catalog, metadata, dist := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
NewJSONPresenter(catalog, metadata, dist, source.SquashedScope),
testImage,
*updateJSONGoldenFiles,
)
}
@@ -1,23 +0,0 @@
package packages
import "github.com/anchore/syft/syft/pkg"
type JSONRelationship struct {
Parent string `json:"parent"`
Child string `json:"child"`
Type string `json:"type"`
Metadata interface{} `json:"metadata"`
}
func newJSONRelationships(relationships []pkg.Relationship) []JSONRelationship {
result := make([]JSONRelationship, len(relationships))
for i, r := range relationships {
result[i] = JSONRelationship{
Parent: string(r.Parent),
Child: string(r.Child),
Type: string(r.Type),
Metadata: r.Metadata,
}
}
return result
}
@@ -1,39 +0,0 @@
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)
}
}
@@ -12,7 +12,7 @@ import (
)
func Test_getSPDXExternalRefs(t *testing.T) {
testCPE := must(pkg.NewCPE("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*"))
testCPE := pkg.MustCPE("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*")
tests := []struct {
name string
input pkg.Package
@@ -4,13 +4,15 @@ import (
"flag"
"regexp"
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
)
var updateSpdxJson = flag.Bool("update-spdx-json", false, "update the *.golden files for spdx-json presenters")
func TestSPDXJSONDirectoryPresenter(t *testing.T) {
catalog, metadata, _ := presenterDirectoryInput(t)
assertPresenterAgainstGoldenSnapshot(t,
catalog, metadata, _ := testutils.DirectoryInput(t)
testutils.AssertPresenterAgainstGoldenSnapshot(t,
NewSPDXJSONPresenter(catalog, metadata),
*updateSpdxJson,
spdxJsonRedactor,
@@ -19,8 +21,8 @@ func TestSPDXJSONDirectoryPresenter(t *testing.T) {
func TestSPDXJSONImagePresenter(t *testing.T) {
testImage := "image-simple"
catalog, metadata, _ := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
catalog, metadata, _ := testutils.ImageInput(t, testImage)
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
NewSPDXJSONPresenter(catalog, metadata),
testImage,
*updateSpdxJson,
@@ -4,13 +4,15 @@ import (
"flag"
"regexp"
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
)
var updateSpdxTagValue = flag.Bool("update-spdx-tv", false, "update the *.golden files for spdx-tv presenters")
func TestSPDXTagValueDirectoryPresenter(t *testing.T) {
catalog, metadata, _ := presenterDirectoryInput(t)
assertPresenterAgainstGoldenSnapshot(t,
catalog, metadata, _ := testutils.DirectoryInput(t)
testutils.AssertPresenterAgainstGoldenSnapshot(t,
NewSPDXTagValuePresenter(catalog, metadata),
*updateSpdxTagValue,
spdxTagValueRedactor,
@@ -19,8 +21,8 @@ func TestSPDXTagValueDirectoryPresenter(t *testing.T) {
func TestSPDXTagValueImagePresenter(t *testing.T) {
testImage := "image-simple"
catalog, metadata, _ := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
catalog, metadata, _ := testutils.ImageInput(t, testImage)
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
NewSPDXTagValuePresenter(catalog, metadata),
testImage,
*updateSpdxTagValue,
@@ -4,6 +4,8 @@ import (
"flag"
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
"github.com/go-test/deep"
)
@@ -11,8 +13,8 @@ var updateTablePresenterGoldenFiles = flag.Bool("update-table", false, "update t
func TestTablePresenter(t *testing.T) {
testImage := "image-simple"
catalog, _, _ := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
catalog, _, _ := testutils.ImageInput(t, testImage)
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
NewTablePresenter(catalog),
testImage,
*updateTablePresenterGoldenFiles,
@@ -3,7 +3,7 @@
"name": "/some/path",
"spdxVersion": "SPDX-2.2",
"creationInfo": {
"created": "2021-09-16T20:44:35.198887Z",
"created": "2021-10-12T18:40:22.948394Z",
"creators": [
"Organization: Anchore, Inc",
"Tool: syft-[not provided]"
@@ -11,7 +11,7 @@
"licenseListVersion": "3.14"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/image/",
"documentNamespace": "https:/anchore.com/syft/dir/some/path-98ae71fb-f276-4c5c-acf7-25770bf7bca2",
"packages": [
{
"SPDXID": "SPDXRef-Package-python-package-1-1.0.1",
@@ -32,7 +32,7 @@
],
"filesAnalyzed": false,
"hasFiles": [
"SPDXRef-File-package-1-04cd22424378dcd6c77fce08beb52493b5494a60ea5e1f9bdf9b16dc0cacffe9"
"SPDXRef-File-package-1-efae7fecc76ca25da40f79d7ef5b8933510434914835832c7976f3e866aa756a"
],
"licenseDeclared": "MIT",
"sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1",
@@ -63,17 +63,17 @@
],
"files": [
{
"SPDXID": "SPDXRef-File-package-1-04cd22424378dcd6c77fce08beb52493b5494a60ea5e1f9bdf9b16dc0cacffe9",
"SPDXID": "SPDXRef-File-package-1-efae7fecc76ca25da40f79d7ef5b8933510434914835832c7976f3e866aa756a",
"name": "foo",
"licenseConcluded": "",
"fileName": "/some/path/pkg1/depedencies/foo"
"fileName": "/some/path/pkg1/dependencies/foo"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Package-python-package-1-1.0.1",
"relationshipType": "CONTAINS",
"relatedSpdxElement": "SPDXRef-File-package-1-04cd22424378dcd6c77fce08beb52493b5494a60ea5e1f9bdf9b16dc0cacffe9"
"relatedSpdxElement": "SPDXRef-File-package-1-efae7fecc76ca25da40f79d7ef5b8933510434914835832c7976f3e866aa756a"
}
]
}
@@ -3,7 +3,7 @@
"name": "user-image-input",
"spdxVersion": "SPDX-2.2",
"creationInfo": {
"created": "2021-09-16T20:44:35.203911Z",
"created": "2021-10-12T18:40:22.953633Z",
"creators": [
"Organization: Anchore, Inc",
"Tool: syft-[not provided]"
@@ -11,7 +11,7 @@
"licenseListVersion": "3.14"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/image/user-image-input",
"documentNamespace": "https:/anchore.com/syft/image/user-image-input-149edbad-3c01-4ee0-b3a0-75232312bf51",
"packages": [
{
"SPDXID": "SPDXRef-Package-python-package-1-1.0.1",
@@ -1,11 +1,11 @@
[Image]
Layer: 0
Digest: sha256:ffb5e9eaa453a002110719d12c294960117ca2903953d1faa40f01dc3f77045c
Digest: sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59
Size: 22
MediaType: application/vnd.docker.image.rootfs.diff.tar.gzip
Layer: 1
Digest: sha256:8463854829fc53d47b9dcdf7ee79fe7eb4ca7933c910f67f8521412f7a2f5c21
Digest: sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec
Size: 16
MediaType: application/vnd.docker.image.rootfs.diff.tar.gzip
@@ -3,13 +3,15 @@ package packages
import (
"flag"
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
)
var updateTextPresenterGoldenFiles = flag.Bool("update-text", false, "update the *.golden files for text presenters")
func TestTextDirectoryPresenter(t *testing.T) {
catalog, metadata, _ := presenterDirectoryInput(t)
assertPresenterAgainstGoldenSnapshot(t,
catalog, metadata, _ := testutils.DirectoryInput(t)
testutils.AssertPresenterAgainstGoldenSnapshot(t,
NewTextPresenter(catalog, metadata),
*updateTextPresenterGoldenFiles,
)
@@ -17,8 +19,8 @@ func TestTextDirectoryPresenter(t *testing.T) {
func TestTextImagePresenter(t *testing.T) {
testImage := "image-simple"
catalog, metadata, _ := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
catalog, metadata, _ := testutils.ImageInput(t, testImage)
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
NewTextPresenter(catalog, metadata),
testImage,
*updateTextPresenterGoldenFiles,
-199
View File
@@ -1,199 +0,0 @@
package packages
import (
"bytes"
"testing"
"github.com/anchore/go-testutils"
"github.com/anchore/stereoscope/pkg/filetree"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/presenter"
"github.com/anchore/syft/syft/source"
"github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
)
type redactor func(s []byte) []byte
func assertPresenterAgainstGoldenImageSnapshot(t *testing.T, pres presenter.Presenter, testImage string, updateSnapshot bool, redactors ...redactor) {
var buffer bytes.Buffer
// grab the latest image contents and persist
if updateSnapshot {
imagetest.UpdateGoldenFixtureImage(t, testImage)
}
err := pres.Present(&buffer)
assert.NoError(t, err)
actual := buffer.Bytes()
// replace the expected snapshot contents with the current presenter contents
if updateSnapshot {
testutils.UpdateGoldenFileContents(t, actual)
}
var expected = testutils.GetGoldenFileContents(t)
// remove dynamic values, which should be tested independently
for _, r := range redactors {
actual = r(actual)
expected = r(expected)
}
// assert that the golden file snapshot matches the actual contents
if !bytes.Equal(expected, actual) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(expected), string(actual), true)
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
}
}
func assertPresenterAgainstGoldenSnapshot(t *testing.T, pres presenter.Presenter, updateSnapshot bool, redactors ...redactor) {
var buffer bytes.Buffer
err := pres.Present(&buffer)
assert.NoError(t, err)
actual := buffer.Bytes()
// replace the expected snapshot contents with the current presenter contents
if updateSnapshot {
testutils.UpdateGoldenFileContents(t, actual)
}
var expected = testutils.GetGoldenFileContents(t)
// remove dynamic values, which should be tested independently
for _, r := range redactors {
actual = r(actual)
expected = r(expected)
}
if !bytes.Equal(expected, actual) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(expected), string(actual), true)
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
}
}
func presenterImageInput(t testing.TB, testImage string) (*pkg.Catalog, source.Metadata, *distro.Distro) {
t.Helper()
catalog := pkg.NewCatalog()
img := imagetest.GetGoldenFixtureImage(t, testImage)
_, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks)
_, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks)
// populate catalog with test data
catalog.Add(pkg.Package{
ID: "package-1-id",
Name: "package-1",
Version: "1.0.1",
Locations: []source.Location{
source.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
},
Type: pkg.PythonPkg,
FoundBy: "the-cataloger-1",
Language: pkg.Python,
MetadataType: pkg.PythonPackageMetadataType,
Licenses: []string{"MIT"},
Metadata: pkg.PythonPackageMetadata{
Name: "package-1",
Version: "1.0.1",
},
PURL: "a-purl-1",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*")),
},
})
catalog.Add(pkg.Package{
ID: "package-2-id",
Name: "package-2",
Version: "2.0.1",
Locations: []source.Location{
source.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
},
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
MetadataType: pkg.DpkgMetadataType,
Metadata: pkg.DpkgMetadata{
Package: "package-2",
Version: "2.0.1",
},
PURL: "a-purl-2",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*")),
},
})
// this is a hard coded value that is not given by the fixture helper and must be provided manually
img.Metadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
src, err := source.NewFromImage(img, "user-image-input")
assert.NoError(t, err)
dist, err := distro.NewDistro(distro.Debian, "1.2.3", "like!")
assert.NoError(t, err)
return catalog, src.Metadata, &dist
}
func presenterDirectoryInput(t testing.TB) (*pkg.Catalog, source.Metadata, *distro.Distro) {
catalog := pkg.NewCatalog()
// populate catalog with test data
catalog.Add(pkg.Package{
ID: "package-1-id",
Name: "package-1",
Version: "1.0.1",
Type: pkg.PythonPkg,
FoundBy: "the-cataloger-1",
Locations: []source.Location{
{RealPath: "/some/path/pkg1"},
},
Language: pkg.Python,
MetadataType: pkg.PythonPackageMetadataType,
Licenses: []string{"MIT"},
Metadata: pkg.PythonPackageMetadata{
Name: "package-1",
Version: "1.0.1",
Files: []pkg.PythonFileRecord{
{
Path: "/some/path/pkg1/depedencies/foo",
},
},
},
PURL: "a-purl-2",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*")),
},
})
catalog.Add(pkg.Package{
ID: "package-2-id",
Name: "package-2",
Version: "2.0.1",
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
Locations: []source.Location{
{RealPath: "/some/path/pkg1"},
},
MetadataType: pkg.DpkgMetadataType,
Metadata: pkg.DpkgMetadata{
Package: "package-2",
Version: "2.0.1",
},
PURL: "a-purl-2",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*")),
},
})
dist, err := distro.NewDistro(distro.Debian, "1.2.3", "like!")
assert.NoError(t, err)
src, err := source.NewFromDirectory("/some/path")
assert.NoError(t, err)
return catalog, src.Metadata, &dist
}
@@ -1,7 +1,8 @@
package poweruser
import (
"github.com/anchore/syft/internal/presenter/packages"
"github.com/anchore/syft/internal/formats/syftjson"
"github.com/anchore/syft/internal/formats/syftjson/model"
)
type JSONDocument struct {
@@ -13,16 +14,11 @@ type JSONDocument struct {
FileContents []JSONFileContents `json:"fileContents,omitempty"` // note: must have omitempty
FileMetadata []JSONFileMetadata `json:"fileMetadata,omitempty"` // note: must have omitempty
Secrets []JSONSecrets `json:"secrets,omitempty"` // note: must have omitempty
packages.JSONDocument
model.Document
}
// 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.Package.Cataloger.ScopeOpt, config.ApplicationConfig)
if err != nil {
return JSONDocument{}, err
}
fileMetadata, err := NewJSONFileMetadata(config.FileMetadata, config.FileDigests)
if err != nil {
return JSONDocument{}, err
@@ -33,6 +29,6 @@ func NewJSONDocument(config JSONDocumentConfig) (JSONDocument, error) {
FileContents: NewJSONFileContents(config.FileContents),
FileMetadata: fileMetadata,
Secrets: NewJSONSecrets(config.Secrets),
JSONDocument: pkgsDoc,
Document: syftjson.ToFormatModel(config.PackageCatalog, &config.SourceMetadata, config.Distro, config.ApplicationConfig.Package.Cataloger.ScopeOpt, config.ApplicationConfig),
}, nil
}