mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
replace power-user presenter with syft-json format
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
81c956cdbd
commit
e809403e94
@ -283,7 +283,7 @@ func packagesExecWorker(userInput string) <-chan error {
|
|||||||
|
|
||||||
bus.Publish(partybus.Event{
|
bus.Publish(partybus.Event{
|
||||||
Type: event.PresenterReady,
|
Type: event.PresenterReady,
|
||||||
Value: f.Presenter(s),
|
Value: f.Presenter(s, appConfig),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
return errs
|
return errs
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/formats/syftjson"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/gookit/color"
|
"github.com/gookit/color"
|
||||||
|
|
||||||
@ -13,7 +15,6 @@ import (
|
|||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/internal/bus"
|
"github.com/anchore/syft/internal/bus"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/internal/presenter/poweruser"
|
|
||||||
"github.com/anchore/syft/internal/ui"
|
"github.com/anchore/syft/internal/ui"
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
@ -139,7 +140,7 @@ func powerUserExecWorker(userInput string) <-chan error {
|
|||||||
|
|
||||||
bus.Publish(partybus.Event{
|
bus.Publish(partybus.Event{
|
||||||
Type: event.PresenterReady,
|
Type: event.PresenterReady,
|
||||||
Value: poweruser.NewJSONPresenter(s, *appConfig),
|
Value: syftjson.Format().Presenter(s, *appConfig),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ type packageSBOMImportAPI interface {
|
|||||||
func packageSbomModel(s sbom.SBOM) (*external.ImagePackageManifest, error) {
|
func packageSbomModel(s sbom.SBOM) (*external.ImagePackageManifest, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
err := syftjson.Format().Presenter(s).Present(&buf)
|
err := syftjson.Format().Presenter(s, nil).Present(&buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to serialize results: %w", err)
|
return nil, fmt.Errorf("unable to serialize results: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,7 +105,7 @@ func TestPackageSbomToModel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
pres := syftjson.Format().Presenter(s)
|
pres := syftjson.Format().Presenter(s, nil)
|
||||||
if err := pres.Present(&buf); err != nil {
|
if err := pres.Present(&buf); err != nil {
|
||||||
t.Fatalf("unable to get expected json: %+v", err)
|
t.Fatalf("unable to get expected json: %+v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,6 +163,10 @@ func (cfg *Application) parseLogLevelOption() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Log.Level == "" {
|
||||||
|
cfg.Log.Level = cfg.Log.LevelOpt.String()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
func encoder(output io.Writer, s sbom.SBOM, _ interface{}) error {
|
||||||
enc := xml.NewEncoder(output)
|
enc := xml.NewEncoder(output)
|
||||||
enc.Indent("", " ")
|
enc.Indent("", " ")
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ var updateCycloneDx = flag.Bool("update-cyclonedx", false, "update the *.golden
|
|||||||
|
|
||||||
func TestCycloneDxDirectoryPresenter(t *testing.T) {
|
func TestCycloneDxDirectoryPresenter(t *testing.T) {
|
||||||
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
Format().Presenter(testutils.DirectoryInput(t)),
|
Format().Presenter(testutils.DirectoryInput(t), nil),
|
||||||
*updateCycloneDx,
|
*updateCycloneDx,
|
||||||
cycloneDxRedactor,
|
cycloneDxRedactor,
|
||||||
)
|
)
|
||||||
@ -21,7 +21,7 @@ func TestCycloneDxDirectoryPresenter(t *testing.T) {
|
|||||||
func TestCycloneDxImagePresenter(t *testing.T) {
|
func TestCycloneDxImagePresenter(t *testing.T) {
|
||||||
testImage := "image-simple"
|
testImage := "image-simple"
|
||||||
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
||||||
Format().Presenter(testutils.ImageInput(t, testImage)),
|
Format().Presenter(testutils.ImageInput(t, testImage), nil),
|
||||||
testImage,
|
testImage,
|
||||||
*updateCycloneDx,
|
*updateCycloneDx,
|
||||||
cycloneDxRedactor,
|
cycloneDxRedactor,
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
const anchoreNamespace = "https://anchore.com/syft"
|
const anchoreNamespace = "https://anchore.com/syft"
|
||||||
|
|
||||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
func encoder(output io.Writer, s sbom.SBOM, _ interface{}) error {
|
||||||
doc := toFormatModel(s)
|
doc := toFormatModel(s)
|
||||||
|
|
||||||
enc := json.NewEncoder(output)
|
enc := json.NewEncoder(output)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ var updateSpdxJson = flag.Bool("update-spdx-json", false, "update the *.golden f
|
|||||||
|
|
||||||
func TestSPDXJSONDirectoryPresenter(t *testing.T) {
|
func TestSPDXJSONDirectoryPresenter(t *testing.T) {
|
||||||
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
Format().Presenter(testutils.DirectoryInput(t)),
|
Format().Presenter(testutils.DirectoryInput(t), nil),
|
||||||
*updateSpdxJson,
|
*updateSpdxJson,
|
||||||
spdxJsonRedactor,
|
spdxJsonRedactor,
|
||||||
)
|
)
|
||||||
@ -21,7 +21,7 @@ func TestSPDXJSONDirectoryPresenter(t *testing.T) {
|
|||||||
func TestSPDXJSONImagePresenter(t *testing.T) {
|
func TestSPDXJSONImagePresenter(t *testing.T) {
|
||||||
testImage := "image-simple"
|
testImage := "image-simple"
|
||||||
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
||||||
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot())),
|
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot()), nil),
|
||||||
testImage,
|
testImage,
|
||||||
*updateSpdxJson,
|
*updateSpdxJson,
|
||||||
spdxJsonRedactor,
|
spdxJsonRedactor,
|
||||||
|
|||||||
@ -3,18 +3,18 @@
|
|||||||
"name": "/some/path",
|
"name": "/some/path",
|
||||||
"spdxVersion": "SPDX-2.2",
|
"spdxVersion": "SPDX-2.2",
|
||||||
"creationInfo": {
|
"creationInfo": {
|
||||||
"created": "2021-10-29T16:26:08.995826Z",
|
"created": "2021-11-17T19:35:54.834877Z",
|
||||||
"creators": [
|
"creators": [
|
||||||
"Organization: Anchore, Inc",
|
"Organization: Anchore, Inc",
|
||||||
"Tool: syft-[not provided]"
|
"Tool: syft-[not provided]"
|
||||||
],
|
],
|
||||||
"licenseListVersion": "3.14"
|
"licenseListVersion": "3.15"
|
||||||
},
|
},
|
||||||
"dataLicense": "CC0-1.0",
|
"dataLicense": "CC0-1.0",
|
||||||
"documentNamespace": "https:/anchore.com/syft/dir/some/path-5362d380-914a-458f-b059-d8d27899574c",
|
"documentNamespace": "https:/anchore.com/syft/dir/some/path-65e2226e-a61e-4ed1-81bb-56022e1ff1eb",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-Package-python-package-1-1.0.1",
|
"SPDXID": "SPDXRef-2a115ac97d018a0e",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"licenseConcluded": "MIT",
|
"licenseConcluded": "MIT",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -31,15 +31,12 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"filesAnalyzed": false,
|
"filesAnalyzed": false,
|
||||||
"hasFiles": [
|
|
||||||
"SPDXRef-File-package-1-efae7fecc76ca25da40f79d7ef5b8933510434914835832c7976f3e866aa756a"
|
|
||||||
],
|
|
||||||
"licenseDeclared": "MIT",
|
"licenseDeclared": "MIT",
|
||||||
"sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1",
|
"sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1",
|
||||||
"versionInfo": "1.0.1"
|
"versionInfo": "1.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-Package-deb-package-2-2.0.1",
|
"SPDXID": "SPDXRef-5e920b2bece2c3ae",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"licenseConcluded": "NONE",
|
"licenseConcluded": "NONE",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -60,20 +57,5 @@
|
|||||||
"sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1",
|
"sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1",
|
||||||
"versionInfo": "2.0.1"
|
"versionInfo": "2.0.1"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
{
|
|
||||||
"SPDXID": "SPDXRef-File-package-1-efae7fecc76ca25da40f79d7ef5b8933510434914835832c7976f3e866aa756a",
|
|
||||||
"name": "foo",
|
|
||||||
"licenseConcluded": "",
|
|
||||||
"fileName": "/some/path/pkg1/dependencies/foo"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"relationships": [
|
|
||||||
{
|
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-1.0.1",
|
|
||||||
"relationshipType": "CONTAINS",
|
|
||||||
"relatedSpdxElement": "SPDXRef-File-package-1-efae7fecc76ca25da40f79d7ef5b8933510434914835832c7976f3e866aa756a"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,18 +3,18 @@
|
|||||||
"name": "user-image-input",
|
"name": "user-image-input",
|
||||||
"spdxVersion": "SPDX-2.2",
|
"spdxVersion": "SPDX-2.2",
|
||||||
"creationInfo": {
|
"creationInfo": {
|
||||||
"created": "2021-10-29T16:26:09.001799Z",
|
"created": "2021-11-17T19:35:57.761372Z",
|
||||||
"creators": [
|
"creators": [
|
||||||
"Organization: Anchore, Inc",
|
"Organization: Anchore, Inc",
|
||||||
"Tool: syft-[not provided]"
|
"Tool: syft-[not provided]"
|
||||||
],
|
],
|
||||||
"licenseListVersion": "3.14"
|
"licenseListVersion": "3.15"
|
||||||
},
|
},
|
||||||
"dataLicense": "CC0-1.0",
|
"dataLicense": "CC0-1.0",
|
||||||
"documentNamespace": "https:/anchore.com/syft/image/user-image-input-3ad8571c-513f-4fce-944e-5125353c3186",
|
"documentNamespace": "https:/anchore.com/syft/image/user-image-input-5383918f-ec96-4aa9-b756-ad16e1ada31e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-Package-python-package-1-1.0.1",
|
"SPDXID": "SPDXRef-888661d4f0362f02",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"licenseConcluded": "MIT",
|
"licenseConcluded": "MIT",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
"versionInfo": "1.0.1"
|
"versionInfo": "1.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-Package-deb-package-2-2.0.1",
|
"SPDXID": "SPDXRef-4068ff5e8926b305",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"licenseConcluded": "NONE",
|
"licenseConcluded": "NONE",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spdx/tools-golang/tvsaver"
|
"github.com/spdx/tools-golang/tvsaver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
func encoder(output io.Writer, s sbom.SBOM, _ interface{}) error {
|
||||||
model := toFormatModel(s)
|
model := toFormatModel(s)
|
||||||
return tvsaver.Save2_2(&model, output)
|
return tvsaver.Save2_2(&model, output)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ var updateSpdxTagValue = flag.Bool("update-spdx-tv", false, "update the *.golden
|
|||||||
func TestSPDXTagValueDirectoryPresenter(t *testing.T) {
|
func TestSPDXTagValueDirectoryPresenter(t *testing.T) {
|
||||||
|
|
||||||
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
Format().Presenter(testutils.DirectoryInput(t)),
|
Format().Presenter(testutils.DirectoryInput(t), nil),
|
||||||
*updateSpdxTagValue,
|
*updateSpdxTagValue,
|
||||||
spdxTagValueRedactor,
|
spdxTagValueRedactor,
|
||||||
)
|
)
|
||||||
@ -22,7 +22,7 @@ func TestSPDXTagValueDirectoryPresenter(t *testing.T) {
|
|||||||
func TestSPDXTagValueImagePresenter(t *testing.T) {
|
func TestSPDXTagValueImagePresenter(t *testing.T) {
|
||||||
testImage := "image-simple"
|
testImage := "image-simple"
|
||||||
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
||||||
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot())),
|
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot()), nil),
|
||||||
testImage,
|
testImage,
|
||||||
*updateSpdxTagValue,
|
*updateSpdxTagValue,
|
||||||
spdxTagValueRedactor,
|
spdxTagValueRedactor,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ func TestEncodeDecodeCycle(t *testing.T) {
|
|||||||
originalSBOM := testutils.ImageInput(t, testImage)
|
originalSBOM := testutils.ImageInput(t, testImage)
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
assert.NoError(t, encoder(&buf, originalSBOM))
|
assert.NoError(t, encoder(&buf, originalSBOM, map[string]string{"config": "value"}))
|
||||||
|
|
||||||
actualSBOM, err := decoder(bytes.NewReader(buf.Bytes()))
|
actualSBOM, err := decoder(bytes.NewReader(buf.Bytes()))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import (
|
|||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
func encoder(output io.Writer, s sbom.SBOM, appConfig interface{}) error {
|
||||||
// TODO: application config not available yet
|
// TODO: application config not available yet
|
||||||
doc := ToFormatModel(s, nil)
|
doc := ToFormatModel(s, appConfig)
|
||||||
|
|
||||||
enc := json.NewEncoder(output)
|
enc := json.NewEncoder(output)
|
||||||
// prevent > and < from being escaped in the payload
|
// prevent > and < from being escaped in the payload
|
||||||
|
|||||||
@ -4,6 +4,14 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/distro"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/formats/common/testutils"
|
"github.com/anchore/syft/internal/formats/common/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,7 +19,7 @@ var updateJson = flag.Bool("update-json", false, "update the *.golden files for
|
|||||||
|
|
||||||
func TestDirectoryPresenter(t *testing.T) {
|
func TestDirectoryPresenter(t *testing.T) {
|
||||||
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
Format().Presenter(testutils.DirectoryInput(t)),
|
Format().Presenter(testutils.DirectoryInput(t), nil),
|
||||||
*updateJson,
|
*updateJson,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -19,8 +27,165 @@ func TestDirectoryPresenter(t *testing.T) {
|
|||||||
func TestImagePresenter(t *testing.T) {
|
func TestImagePresenter(t *testing.T) {
|
||||||
testImage := "image-simple"
|
testImage := "image-simple"
|
||||||
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
||||||
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot())),
|
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot()), nil),
|
||||||
testImage,
|
testImage,
|
||||||
*updateJson,
|
*updateJson,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFullJSONDocument(t *testing.T) {
|
||||||
|
catalog := pkg.NewCatalog()
|
||||||
|
|
||||||
|
p1 := pkg.Package{
|
||||||
|
Name: "package-1",
|
||||||
|
Version: "1.0.1",
|
||||||
|
Locations: []source.Location{
|
||||||
|
{
|
||||||
|
Coordinates: source.Coordinates{
|
||||||
|
RealPath: "/a/place/a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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",
|
||||||
|
Files: []pkg.PythonFileRecord{},
|
||||||
|
},
|
||||||
|
PURL: "a-purl-1",
|
||||||
|
CPEs: []pkg.CPE{
|
||||||
|
pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
p2 := pkg.Package{
|
||||||
|
Name: "package-2",
|
||||||
|
Version: "2.0.1",
|
||||||
|
Locations: []source.Location{
|
||||||
|
{
|
||||||
|
Coordinates: source.Coordinates{
|
||||||
|
RealPath: "/b/place/b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: pkg.DebPkg,
|
||||||
|
FoundBy: "the-cataloger-2",
|
||||||
|
MetadataType: pkg.DpkgMetadataType,
|
||||||
|
Metadata: pkg.DpkgMetadata{
|
||||||
|
Package: "package-2",
|
||||||
|
Version: "2.0.1",
|
||||||
|
Files: []pkg.DpkgFileRecord{},
|
||||||
|
},
|
||||||
|
PURL: "a-purl-2",
|
||||||
|
CPEs: []pkg.CPE{
|
||||||
|
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog.Add(p1)
|
||||||
|
catalog.Add(p2)
|
||||||
|
|
||||||
|
s := sbom.SBOM{
|
||||||
|
Artifacts: sbom.Artifacts{
|
||||||
|
PackageCatalog: catalog,
|
||||||
|
FileMetadata: map[source.Coordinates]source.FileMetadata{
|
||||||
|
source.NewLocation("/a/place").Coordinates: {
|
||||||
|
Mode: 0775,
|
||||||
|
Type: "directory",
|
||||||
|
UserID: 0,
|
||||||
|
GroupID: 0,
|
||||||
|
},
|
||||||
|
source.NewLocation("/a/place/a").Coordinates: {
|
||||||
|
Mode: 0775,
|
||||||
|
Type: "regularFile",
|
||||||
|
UserID: 0,
|
||||||
|
GroupID: 0,
|
||||||
|
},
|
||||||
|
source.NewLocation("/b").Coordinates: {
|
||||||
|
Mode: 0775,
|
||||||
|
Type: "symbolicLink",
|
||||||
|
LinkDestination: "/c",
|
||||||
|
UserID: 0,
|
||||||
|
GroupID: 0,
|
||||||
|
},
|
||||||
|
source.NewLocation("/b/place/b").Coordinates: {
|
||||||
|
Mode: 0644,
|
||||||
|
Type: "regularFile",
|
||||||
|
UserID: 1,
|
||||||
|
GroupID: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FileDigests: map[source.Coordinates][]file.Digest{
|
||||||
|
source.NewLocation("/a/place/a").Coordinates: {
|
||||||
|
{
|
||||||
|
Algorithm: "sha256",
|
||||||
|
Value: "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
source.NewLocation("/b/place/b").Coordinates: {
|
||||||
|
{
|
||||||
|
Algorithm: "sha256",
|
||||||
|
Value: "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FileContents: map[source.Coordinates]string{
|
||||||
|
source.NewLocation("/a/place/a").Coordinates: "the-contents",
|
||||||
|
},
|
||||||
|
Distro: &distro.Distro{
|
||||||
|
Type: distro.RedHat,
|
||||||
|
RawVersion: "7",
|
||||||
|
IDLike: "rhel",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationships: []artifact.Relationship{
|
||||||
|
{
|
||||||
|
From: p1,
|
||||||
|
To: p2,
|
||||||
|
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||||
|
Data: map[string]string{
|
||||||
|
"file": "path",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Source: source.Metadata{
|
||||||
|
Scheme: source.ImageScheme,
|
||||||
|
ImageMetadata: source.ImageMetadata{
|
||||||
|
UserInput: "user-image-input",
|
||||||
|
ID: "sha256:c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
||||||
|
ManifestDigest: "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
||||||
|
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
|
Tags: []string{
|
||||||
|
"stereoscope-fixture-image-simple:85066c51088bdd274f7a89e99e00490f666c49e72ffc955707cd6e18f0e22c5b",
|
||||||
|
},
|
||||||
|
Size: 38,
|
||||||
|
Layers: []source.LayerMetadata{
|
||||||
|
{
|
||||||
|
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
|
Digest: "sha256:3de16c5b8659a2e8d888b8ded8427be7a5686a3c8c4e4dd30de20f362827285b",
|
||||||
|
Size: 22,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
|
Digest: "sha256:366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
||||||
|
Size: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RawManifest: []byte("eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJh..."),
|
||||||
|
RawConfig: []byte("eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZp..."),
|
||||||
|
RepoDigests: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
|
Format().Presenter(s, map[string]string{
|
||||||
|
"app": "config",
|
||||||
|
}),
|
||||||
|
*updateJson,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@
|
|||||||
"version": "[not provided]"
|
"version": "[not provided]"
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,75 +1,4 @@
|
|||||||
{
|
{
|
||||||
"fileContents": [
|
|
||||||
{
|
|
||||||
"location": {
|
|
||||||
"path": "/a/place/a"
|
|
||||||
},
|
|
||||||
"contents": "the-contents"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fileMetadata": [
|
|
||||||
{
|
|
||||||
"location": {
|
|
||||||
"path": "/a/place"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"mode": 775,
|
|
||||||
"type": "directory",
|
|
||||||
"userID": 0,
|
|
||||||
"groupID": 0,
|
|
||||||
"mimeType": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"location": {
|
|
||||||
"path": "/a/place/a"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"mode": 775,
|
|
||||||
"type": "regularFile",
|
|
||||||
"userID": 0,
|
|
||||||
"groupID": 0,
|
|
||||||
"digests": [
|
|
||||||
{
|
|
||||||
"algorithm": "sha256",
|
|
||||||
"value": "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mimeType": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"location": {
|
|
||||||
"path": "/b"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"mode": 775,
|
|
||||||
"type": "symbolicLink",
|
|
||||||
"linkDestination": "/c",
|
|
||||||
"userID": 0,
|
|
||||||
"groupID": 0,
|
|
||||||
"mimeType": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"location": {
|
|
||||||
"path": "/b/place/b"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"mode": 644,
|
|
||||||
"type": "regularFile",
|
|
||||||
"userID": 1,
|
|
||||||
"groupID": 2,
|
|
||||||
"digests": [
|
|
||||||
{
|
|
||||||
"algorithm": "sha256",
|
|
||||||
"value": "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mimeType": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "962403cfb7be50d7",
|
"id": "962403cfb7be50d7",
|
||||||
@ -131,7 +60,84 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"artifactRelationships": [],
|
"artifactRelationships": [
|
||||||
|
{
|
||||||
|
"parent": "962403cfb7be50d7",
|
||||||
|
"child": "b11f44847bba0ed1",
|
||||||
|
"type": "ownership-by-file-overlap",
|
||||||
|
"metadata": {
|
||||||
|
"file": "path"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"id": "913b4592e2c2ebdf",
|
||||||
|
"location": {
|
||||||
|
"path": "/a/place"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"mode": 775,
|
||||||
|
"type": "directory",
|
||||||
|
"userID": 0,
|
||||||
|
"groupID": 0,
|
||||||
|
"mimeType": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e7c88bd18e11b0b",
|
||||||
|
"location": {
|
||||||
|
"path": "/a/place/a"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"mode": 775,
|
||||||
|
"type": "regularFile",
|
||||||
|
"userID": 0,
|
||||||
|
"groupID": 0,
|
||||||
|
"mimeType": ""
|
||||||
|
},
|
||||||
|
"contents": "the-contents",
|
||||||
|
"digests": [
|
||||||
|
{
|
||||||
|
"algorithm": "sha256",
|
||||||
|
"value": "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5c3dc6885f48b5a1",
|
||||||
|
"location": {
|
||||||
|
"path": "/b"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"mode": 775,
|
||||||
|
"type": "symbolicLink",
|
||||||
|
"linkDestination": "/c",
|
||||||
|
"userID": 0,
|
||||||
|
"groupID": 0,
|
||||||
|
"mimeType": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "799d2f12da0bcec4",
|
||||||
|
"location": {
|
||||||
|
"path": "/b/place/b"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"mode": 644,
|
||||||
|
"type": "regularFile",
|
||||||
|
"userID": 1,
|
||||||
|
"groupID": 2,
|
||||||
|
"mimeType": ""
|
||||||
|
},
|
||||||
|
"digests": [
|
||||||
|
{
|
||||||
|
"algorithm": "sha256",
|
||||||
|
"value": "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": {
|
"source": {
|
||||||
"type": "image",
|
"type": "image",
|
||||||
"target": {
|
"target": {
|
||||||
@ -169,75 +175,11 @@
|
|||||||
"name": "syft",
|
"name": "syft",
|
||||||
"version": "[not provided]",
|
"version": "[not provided]",
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"configPath": "",
|
"app": "config"
|
||||||
"output": "",
|
|
||||||
"file": "",
|
|
||||||
"quiet": false,
|
|
||||||
"check-for-app-update": false,
|
|
||||||
"anchore": {
|
|
||||||
"host": "",
|
|
||||||
"path": "",
|
|
||||||
"dockerfile": "",
|
|
||||||
"overwrite-existing-image": false,
|
|
||||||
"import-timeout": 0
|
|
||||||
},
|
|
||||||
"dev": {
|
|
||||||
"profile-cpu": false,
|
|
||||||
"profile-mem": false
|
|
||||||
},
|
|
||||||
"log": {
|
|
||||||
"structured": false,
|
|
||||||
"level": "",
|
|
||||||
"file-location": ""
|
|
||||||
},
|
|
||||||
"package": {
|
|
||||||
"cataloger": {
|
|
||||||
"enabled": false,
|
|
||||||
"scope": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"file-metadata": {
|
|
||||||
"cataloger": {
|
|
||||||
"enabled": false,
|
|
||||||
"scope": ""
|
|
||||||
},
|
|
||||||
"digests": [
|
|
||||||
"sha256"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"file-classification": {
|
|
||||||
"cataloger": {
|
|
||||||
"enabled": false,
|
|
||||||
"scope": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"file-contents": {
|
|
||||||
"cataloger": {
|
|
||||||
"enabled": false,
|
|
||||||
"scope": ""
|
|
||||||
},
|
|
||||||
"skip-files-above-size": 0,
|
|
||||||
"globs": null
|
|
||||||
},
|
|
||||||
"secrets": {
|
|
||||||
"cataloger": {
|
|
||||||
"enabled": false,
|
|
||||||
"scope": ""
|
|
||||||
},
|
|
||||||
"additional-patterns": null,
|
|
||||||
"exclude-pattern-names": null,
|
|
||||||
"reveal-values": false,
|
|
||||||
"skip-files-above-size": 0
|
|
||||||
},
|
|
||||||
"registry": {
|
|
||||||
"insecure-skip-tls-verify": false,
|
|
||||||
"insecure-use-http": false,
|
|
||||||
"auth": null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@
|
|||||||
"version": "[not provided]"
|
"version": "[not provided]"
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
func encoder(output io.Writer, s sbom.SBOM, _ interface{}) error {
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
|
|
||||||
columns := []string{"Name", "Version", "Type"}
|
columns := []string{"Name", "Version", "Type"}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ var updateTableGoldenFiles = flag.Bool("update-table", false, "update the *.gold
|
|||||||
|
|
||||||
func TestTablePresenter(t *testing.T) {
|
func TestTablePresenter(t *testing.T) {
|
||||||
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
Format().Presenter(testutils.DirectoryInput(t)),
|
Format().Presenter(testutils.DirectoryInput(t), nil),
|
||||||
*updateTableGoldenFiles,
|
*updateTableGoldenFiles,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
func encoder(output io.Writer, s sbom.SBOM, _ interface{}) error {
|
||||||
// init the tabular writer
|
// init the tabular writer
|
||||||
w := new(tabwriter.Writer)
|
w := new(tabwriter.Writer)
|
||||||
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight)
|
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight)
|
||||||
|
|||||||
@ -11,7 +11,7 @@ var updateTextPresenterGoldenFiles = flag.Bool("update-text", false, "update the
|
|||||||
|
|
||||||
func TestTextDirectoryPresenter(t *testing.T) {
|
func TestTextDirectoryPresenter(t *testing.T) {
|
||||||
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenSnapshot(t,
|
||||||
Format().Presenter(testutils.DirectoryInput(t)),
|
Format().Presenter(testutils.DirectoryInput(t), nil),
|
||||||
*updateTextPresenterGoldenFiles,
|
*updateTextPresenterGoldenFiles,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ func TestTextDirectoryPresenter(t *testing.T) {
|
|||||||
func TestTextImagePresenter(t *testing.T) {
|
func TestTextImagePresenter(t *testing.T) {
|
||||||
testImage := "image-simple"
|
testImage := "image-simple"
|
||||||
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
|
||||||
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot())),
|
Format().Presenter(testutils.ImageInput(t, testImage, testutils.FromSnapshot()), nil),
|
||||||
testImage,
|
testImage,
|
||||||
*updateTextPresenterGoldenFiles,
|
*updateTextPresenterGoldenFiles,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
package poweruser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/formats/syftjson"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/sbom"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSONPresenter is a JSON presentation object for the syft results
|
|
||||||
type JSONPresenter struct {
|
|
||||||
sbom sbom.SBOM
|
|
||||||
config interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewJSONPresenter creates a new JSON presenter object for the given cataloging results.
|
|
||||||
func NewJSONPresenter(s sbom.SBOM, appConfig interface{}) *JSONPresenter {
|
|
||||||
return &JSONPresenter{
|
|
||||||
sbom: s,
|
|
||||||
config: appConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present the PackageCatalog results to the given writer.
|
|
||||||
func (p *JSONPresenter) Present(output io.Writer) error {
|
|
||||||
doc := syftjson.ToFormatModel(p.sbom, p.config)
|
|
||||||
enc := json.NewEncoder(output)
|
|
||||||
// prevent > and < from being escaped in the payload
|
|
||||||
enc.SetEscapeHTML(false)
|
|
||||||
enc.SetIndent("", " ")
|
|
||||||
return enc.Encode(&doc)
|
|
||||||
}
|
|
||||||
@ -1,189 +0,0 @@
|
|||||||
package poweruser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/sbom"
|
|
||||||
|
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
|
|
||||||
"github.com/anchore/go-testutils"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/syft/distro"
|
|
||||||
"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 TestJSONPresenter(t *testing.T) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
|
|
||||||
catalog := pkg.NewCatalog()
|
|
||||||
|
|
||||||
catalog.Add(pkg.Package{
|
|
||||||
Name: "package-1",
|
|
||||||
Version: "1.0.1",
|
|
||||||
Locations: []source.Location{
|
|
||||||
{
|
|
||||||
Coordinates: source.Coordinates{
|
|
||||||
RealPath: "/a/place/a",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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",
|
|
||||||
Files: []pkg.PythonFileRecord{},
|
|
||||||
},
|
|
||||||
PURL: "a-purl-1",
|
|
||||||
CPEs: []pkg.CPE{
|
|
||||||
must(pkg.NewCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*")),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
catalog.Add(pkg.Package{
|
|
||||||
Name: "package-2",
|
|
||||||
Version: "2.0.1",
|
|
||||||
Locations: []source.Location{
|
|
||||||
{
|
|
||||||
Coordinates: source.Coordinates{
|
|
||||||
RealPath: "/b/place/b",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Type: pkg.DebPkg,
|
|
||||||
FoundBy: "the-cataloger-2",
|
|
||||||
MetadataType: pkg.DpkgMetadataType,
|
|
||||||
Metadata: pkg.DpkgMetadata{
|
|
||||||
Package: "package-2",
|
|
||||||
Version: "2.0.1",
|
|
||||||
Files: []pkg.DpkgFileRecord{},
|
|
||||||
},
|
|
||||||
PURL: "a-purl-2",
|
|
||||||
CPEs: []pkg.CPE{
|
|
||||||
must(pkg.NewCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*")),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
appConfig := config.Application{
|
|
||||||
FileMetadata: config.FileMetadata{
|
|
||||||
Digests: []string{"sha256"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := sbom.SBOM{
|
|
||||||
Artifacts: sbom.Artifacts{
|
|
||||||
PackageCatalog: catalog,
|
|
||||||
FileMetadata: map[source.Coordinates]source.FileMetadata{
|
|
||||||
source.NewLocation("/a/place").Coordinates: {
|
|
||||||
Mode: 0775,
|
|
||||||
Type: "directory",
|
|
||||||
UserID: 0,
|
|
||||||
GroupID: 0,
|
|
||||||
},
|
|
||||||
source.NewLocation("/a/place/a").Coordinates: {
|
|
||||||
Mode: 0775,
|
|
||||||
Type: "regularFile",
|
|
||||||
UserID: 0,
|
|
||||||
GroupID: 0,
|
|
||||||
},
|
|
||||||
source.NewLocation("/b").Coordinates: {
|
|
||||||
Mode: 0775,
|
|
||||||
Type: "symbolicLink",
|
|
||||||
LinkDestination: "/c",
|
|
||||||
UserID: 0,
|
|
||||||
GroupID: 0,
|
|
||||||
},
|
|
||||||
source.NewLocation("/b/place/b").Coordinates: {
|
|
||||||
Mode: 0644,
|
|
||||||
Type: "regularFile",
|
|
||||||
UserID: 1,
|
|
||||||
GroupID: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
FileDigests: map[source.Coordinates][]file.Digest{
|
|
||||||
source.NewLocation("/a/place/a").Coordinates: {
|
|
||||||
{
|
|
||||||
Algorithm: "sha256",
|
|
||||||
Value: "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
source.NewLocation("/b/place/b").Coordinates: {
|
|
||||||
{
|
|
||||||
Algorithm: "sha256",
|
|
||||||
Value: "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
FileContents: map[source.Coordinates]string{
|
|
||||||
source.NewLocation("/a/place/a").Coordinates: "the-contents",
|
|
||||||
},
|
|
||||||
Distro: &distro.Distro{
|
|
||||||
Type: distro.RedHat,
|
|
||||||
RawVersion: "7",
|
|
||||||
IDLike: "rhel",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Source: source.Metadata{
|
|
||||||
Scheme: source.ImageScheme,
|
|
||||||
ImageMetadata: source.ImageMetadata{
|
|
||||||
UserInput: "user-image-input",
|
|
||||||
ID: "sha256:c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
|
||||||
ManifestDigest: "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
|
||||||
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
|
|
||||||
Tags: []string{
|
|
||||||
"stereoscope-fixture-image-simple:85066c51088bdd274f7a89e99e00490f666c49e72ffc955707cd6e18f0e22c5b",
|
|
||||||
},
|
|
||||||
Size: 38,
|
|
||||||
Layers: []source.LayerMetadata{
|
|
||||||
{
|
|
||||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
||||||
Digest: "sha256:3de16c5b8659a2e8d888b8ded8427be7a5686a3c8c4e4dd30de20f362827285b",
|
|
||||||
Size: 22,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
||||||
Digest: "sha256:366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
|
||||||
Size: 16,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RawManifest: []byte("eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJh..."),
|
|
||||||
RawConfig: []byte("eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZp..."),
|
|
||||||
RepoDigests: []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := NewJSONPresenter(cfg, appConfig).Present(&buffer); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
actual := buffer.Bytes()
|
|
||||||
|
|
||||||
if *updateJSONGoldenFiles {
|
|
||||||
testutils.UpdateGoldenFileContents(t, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := testutils.GetGoldenFileContents(t)
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,14 +12,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Encode takes all SBOM elements and a format option and encodes an SBOM document.
|
// Encode takes all SBOM elements and a format option and encodes an SBOM document.
|
||||||
func Encode(s sbom.SBOM, option format.Option) ([]byte, error) {
|
func Encode(s sbom.SBOM, appConfig interface{}, option format.Option) ([]byte, error) {
|
||||||
f := formats.ByOption(option)
|
f := formats.ByOption(option)
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return nil, fmt.Errorf("unsupported format: %+v", option)
|
return nil, fmt.Errorf("unsupported format: %+v", option)
|
||||||
}
|
}
|
||||||
buff := bytes.Buffer{}
|
buff := bytes.Buffer{}
|
||||||
|
|
||||||
if err := f.Encode(&buff, s); err != nil {
|
if err := f.Encode(&buff, s, appConfig); err != nil {
|
||||||
return nil, fmt.Errorf("unable to encode sbom: %w", err)
|
return nil, fmt.Errorf("unable to encode sbom: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,4 +7,4 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Encoder is a function that can transform Syft native objects into an SBOM document of a specific format written to the given writer.
|
// Encoder is a function that can transform Syft native objects into an SBOM document of a specific format written to the given writer.
|
||||||
type Encoder func(io.Writer, sbom.SBOM) error
|
type Encoder func(io.Writer, sbom.SBOM, interface{}) error
|
||||||
|
|||||||
@ -29,11 +29,11 @@ func NewFormat(option Option, encoder Encoder, decoder Decoder, validator Valida
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Format) Encode(output io.Writer, s sbom.SBOM) error {
|
func (f Format) Encode(output io.Writer, s sbom.SBOM, appConfig interface{}) error {
|
||||||
if f.encoder == nil {
|
if f.encoder == nil {
|
||||||
return ErrEncodingNotSupported
|
return ErrEncodingNotSupported
|
||||||
}
|
}
|
||||||
return f.encoder(output, s)
|
return f.encoder(output, s, appConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Format) Decode(reader io.Reader) (*sbom.SBOM, error) {
|
func (f Format) Decode(reader io.Reader) (*sbom.SBOM, error) {
|
||||||
@ -51,9 +51,9 @@ func (f Format) Validate(reader io.Reader) error {
|
|||||||
return f.validator(reader)
|
return f.validator(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Format) Presenter(s sbom.SBOM) *Presenter {
|
func (f Format) Presenter(s sbom.SBOM, appConfig interface{}) *Presenter {
|
||||||
if f.encoder == nil {
|
if f.encoder == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return NewPresenter(f.encoder, s)
|
return NewPresenter(f.encoder, s, appConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,16 +8,18 @@ import (
|
|||||||
|
|
||||||
type Presenter struct {
|
type Presenter struct {
|
||||||
sbom sbom.SBOM
|
sbom sbom.SBOM
|
||||||
|
appConfig interface{}
|
||||||
encoder Encoder
|
encoder Encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPresenter(encoder Encoder, s sbom.SBOM) *Presenter {
|
func NewPresenter(encoder Encoder, s sbom.SBOM, appConfig interface{}) *Presenter {
|
||||||
return &Presenter{
|
return &Presenter{
|
||||||
sbom: s,
|
sbom: s,
|
||||||
|
appConfig: appConfig,
|
||||||
encoder: encoder,
|
encoder: encoder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pres *Presenter) Present(output io.Writer) error {
|
func (pres *Presenter) Present(output io.Writer) error {
|
||||||
return pres.encoder(output, pres.sbom)
|
return pres.encoder(output, pres.sbom, pres.appConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package cataloger
|
package cataloger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
"github.com/anchore/syft/internal/bus"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
@ -43,6 +45,7 @@ func Catalog(resolver source.FileResolver, theDistro *distro.Distro, catalogers
|
|||||||
catalog := pkg.NewCatalog()
|
catalog := pkg.NewCatalog()
|
||||||
var allRelationships []artifact.Relationship
|
var allRelationships []artifact.Relationship
|
||||||
|
|
||||||
|
// TODO: update to show relationships
|
||||||
filesProcessed, packagesDiscovered := newMonitor()
|
filesProcessed, packagesDiscovered := newMonitor()
|
||||||
|
|
||||||
// perform analysis, accumulating errors for each failed analysis
|
// perform analysis, accumulating errors for each failed analysis
|
||||||
@ -57,6 +60,7 @@ func Catalog(resolver source.FileResolver, theDistro *distro.Distro, catalogers
|
|||||||
|
|
||||||
catalogedPackages := len(packages)
|
catalogedPackages := len(packages)
|
||||||
|
|
||||||
|
// TODO: update to show relationships and files
|
||||||
log.Debugf("package cataloger %q discovered %d packages", theCataloger.Name(), catalogedPackages)
|
log.Debugf("package cataloger %q discovered %d packages", theCataloger.Name(), catalogedPackages)
|
||||||
packagesDiscovered.N += int64(catalogedPackages)
|
packagesDiscovered.N += int64(catalogedPackages)
|
||||||
|
|
||||||
@ -67,6 +71,15 @@ func Catalog(resolver source.FileResolver, theDistro *distro.Distro, catalogers
|
|||||||
// generate PURL
|
// generate PURL
|
||||||
p.PURL = generatePackageURL(p, theDistro)
|
p.PURL = generatePackageURL(p, theDistro)
|
||||||
|
|
||||||
|
// TODO: break out into another function (refactor this function)
|
||||||
|
// create file-to-package relationships for files owned by the package
|
||||||
|
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to create any package-file relationships for package name=%q: %w", p.Name, err)
|
||||||
|
} else {
|
||||||
|
allRelationships = append(allRelationships, owningRelationships...)
|
||||||
|
}
|
||||||
|
|
||||||
// add to catalog
|
// add to catalog
|
||||||
catalog.Add(p)
|
catalog.Add(p)
|
||||||
}
|
}
|
||||||
@ -85,3 +98,33 @@ func Catalog(resolver source.FileResolver, theDistro *distro.Distro, catalogers
|
|||||||
|
|
||||||
return catalog, allRelationships, nil
|
return catalog, allRelationships, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func packageFileOwnershipRelationships(p pkg.Package, resolver source.FilePathResolver) ([]artifact.Relationship, error) {
|
||||||
|
fileOwner, ok := p.Metadata.(pkg.FileOwner)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var relationships []artifact.Relationship
|
||||||
|
|
||||||
|
for _, path := range fileOwner.OwnedFiles() {
|
||||||
|
locations, err := resolver.FilesByPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to find path for path=%q: %w", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//if len(locations) == 0 {
|
||||||
|
// // TODO: this is notable, we should at least log it(?)... however, ideally there is something in the SBOM about this
|
||||||
|
//}
|
||||||
|
|
||||||
|
for _, l := range locations {
|
||||||
|
relationships = append(relationships, artifact.Relationship{
|
||||||
|
From: l.Coordinates,
|
||||||
|
To: p,
|
||||||
|
Type: artifact.PackageOfRelationship,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return relationships, nil
|
||||||
|
}
|
||||||
|
|||||||
51
syft/source/coordinates_test.go
Normal file
51
syft/source/coordinates_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCoordinateSet(t *testing.T) {
|
||||||
|
|
||||||
|
binA := Coordinates{
|
||||||
|
RealPath: "/bin",
|
||||||
|
FileSystemID: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
binB := Coordinates{
|
||||||
|
RealPath: "/bin",
|
||||||
|
FileSystemID: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []Coordinates
|
||||||
|
expected []Coordinates
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "de-dup same location",
|
||||||
|
input: []Coordinates{
|
||||||
|
binA, binA, binA,
|
||||||
|
},
|
||||||
|
expected: []Coordinates{
|
||||||
|
binA,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dont de-dup different filesystem",
|
||||||
|
input: []Coordinates{
|
||||||
|
binB, binA,
|
||||||
|
},
|
||||||
|
expected: []Coordinates{
|
||||||
|
binA, binB,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, NewCoordinateSet(test.input...).ToSlice())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,14 +31,18 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
|||||||
|
|
||||||
originalSBOM, _ := catalogFixtureImage(t, "image-pkg-coverage")
|
originalSBOM, _ := catalogFixtureImage(t, "image-pkg-coverage")
|
||||||
|
|
||||||
by1, err := syft.Encode(originalSBOM, test.format)
|
appConfig := map[string]string{
|
||||||
|
"config": "value",
|
||||||
|
}
|
||||||
|
|
||||||
|
by1, err := syft.Encode(originalSBOM, appConfig, test.format)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
newSBOM, newFormat, err := syft.Decode(bytes.NewReader(by1))
|
newSBOM, newFormat, err := syft.Decode(bytes.NewReader(by1))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, test.format, newFormat)
|
assert.Equal(t, test.format, newFormat)
|
||||||
|
|
||||||
by2, err := syft.Encode(*newSBOM, test.format)
|
by2, err := syft.Encode(*newSBOM, appConfig, test.format)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
if !assert.True(t, bytes.Equal(by1, by2)) {
|
if !assert.True(t, bytes.Equal(by1, by2)) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user