mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
278 lines
7.1 KiB
Go
278 lines
7.1 KiB
Go
package syftjson
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
|
"github.com/anchore/syft/internal"
|
|
"github.com/anchore/syft/syft/cpe"
|
|
"github.com/anchore/syft/syft/file"
|
|
"github.com/anchore/syft/syft/format/internal/testutil"
|
|
"github.com/anchore/syft/syft/linux"
|
|
"github.com/anchore/syft/syft/pkg"
|
|
"github.com/anchore/syft/syft/sbom"
|
|
"github.com/anchore/syft/syft/source"
|
|
)
|
|
|
|
func Test_EncodeDecodeCycle(t *testing.T) {
|
|
|
|
table := []struct {
|
|
name string
|
|
fixtureImage string
|
|
cfg EncoderConfig
|
|
}{
|
|
{
|
|
name: "go case",
|
|
fixtureImage: "image-simple",
|
|
cfg: DefaultEncoderConfig(),
|
|
},
|
|
{
|
|
name: "default alpine",
|
|
fixtureImage: "image-alpine",
|
|
cfg: DefaultEncoderConfig(),
|
|
},
|
|
{
|
|
name: "legacy alpine",
|
|
fixtureImage: "image-alpine",
|
|
cfg: EncoderConfig{
|
|
Legacy: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range table {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
originalSBOM := testutil.ImageInput(t, tt.fixtureImage)
|
|
|
|
enc, err := NewFormatEncoderWithConfig(tt.cfg)
|
|
require.NoError(t, err)
|
|
dec := NewFormatDecoder()
|
|
|
|
var buf bytes.Buffer
|
|
assert.NoError(t, enc.Encode(&buf, originalSBOM))
|
|
|
|
actualSBOM, decodedID, decodedVersion, err := dec.Decode(bytes.NewReader(buf.Bytes()))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, ID, decodedID)
|
|
assert.Equal(t, internal.JSONSchemaVersion, decodedVersion)
|
|
|
|
for _, d := range deep.Equal(originalSBOM.Source, actualSBOM.Source) {
|
|
if strings.HasSuffix(d, "<nil slice> != []") {
|
|
// semantically the same
|
|
continue
|
|
}
|
|
t.Errorf("metadata difference: %+v", d)
|
|
}
|
|
|
|
actualPackages := actualSBOM.Artifacts.Packages.Sorted()
|
|
for idx, p := range originalSBOM.Artifacts.Packages.Sorted() {
|
|
if !assert.Equal(t, p.Name, actualPackages[idx].Name) {
|
|
t.Errorf("different package at idx=%d: %s vs %s", idx, p.Name, actualPackages[idx].Name)
|
|
continue
|
|
}
|
|
|
|
for _, d := range deep.Equal(p, actualPackages[idx]) {
|
|
if strings.Contains(d, ".AccessPath: ") {
|
|
// location.Virtual path is not exposed in the json output
|
|
continue
|
|
}
|
|
if strings.HasSuffix(d, "<nil slice> != []") {
|
|
// semantically the same
|
|
continue
|
|
}
|
|
t.Errorf("%q package difference (%s): %+v", tt.fixtureImage, p.Name, d)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOutOfDateParser(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
documentVersion string
|
|
parserVersion string
|
|
want error
|
|
}{{
|
|
name: "no warning when doc version is older",
|
|
documentVersion: "1.0.9",
|
|
parserVersion: "3.1.0",
|
|
}, {
|
|
name: "warning when parser is older",
|
|
documentVersion: "4.3.2",
|
|
parserVersion: "3.1.0",
|
|
want: fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", "4.3.2", "3.1.0"),
|
|
}, {
|
|
name: "warning when document version is unparseable",
|
|
documentVersion: "some-nonsense",
|
|
parserVersion: "3.1.0",
|
|
want: fmt.Errorf("error comparing document schema version with parser schema version: %w", errors.New("Invalid Semantic Version")),
|
|
}, {
|
|
name: "warning when parser version is unparseable",
|
|
documentVersion: "7.1.0",
|
|
parserVersion: "some-nonsense",
|
|
want: fmt.Errorf("error comparing document schema version with parser schema version: %w", errors.New("Invalid Semantic Version")),
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := checkSupportedSchema(tt.documentVersion, tt.parserVersion)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_encodeDecodeFileMetadata(t *testing.T) {
|
|
p := pkg.Package{
|
|
Name: "pkg",
|
|
Version: "version",
|
|
FoundBy: "something",
|
|
Locations: file.NewLocationSet(file.Location{
|
|
LocationData: file.LocationData{
|
|
Coordinates: file.Coordinates{
|
|
RealPath: "/somewhere",
|
|
FileSystemID: "id",
|
|
},
|
|
},
|
|
LocationMetadata: file.LocationMetadata{
|
|
Annotations: map[string]string{
|
|
"key": "value",
|
|
},
|
|
},
|
|
}),
|
|
Licenses: pkg.NewLicenseSet(pkg.License{
|
|
Value: "MIT",
|
|
SPDXExpression: "MIT",
|
|
Type: "MIT",
|
|
URLs: []string{"https://example.org/license"},
|
|
Locations: file.LocationSet{},
|
|
}),
|
|
Language: "language",
|
|
Type: "type",
|
|
CPEs: []cpe.CPE{
|
|
{
|
|
Part: "a",
|
|
Vendor: "vendor",
|
|
Product: "product",
|
|
Version: "version",
|
|
Update: "update",
|
|
},
|
|
},
|
|
PURL: "pkg:generic/pkg@version",
|
|
Metadata: nil,
|
|
}
|
|
p.SetID()
|
|
|
|
c := file.Coordinates{
|
|
RealPath: "some-file",
|
|
FileSystemID: "some-fs-id",
|
|
}
|
|
|
|
s := sbom.SBOM{
|
|
Artifacts: sbom.Artifacts{
|
|
Packages: pkg.NewCollection(p),
|
|
FileMetadata: map[file.Coordinates]file.Metadata{
|
|
c: {
|
|
FileInfo: stereoscopeFile.ManualInfo{
|
|
NameValue: c.RealPath,
|
|
ModeValue: 0644,
|
|
SizeValue: 7,
|
|
},
|
|
Path: c.RealPath,
|
|
Type: stereoscopeFile.TypeRegular,
|
|
UserID: 1,
|
|
GroupID: 2,
|
|
MIMEType: "text/plain",
|
|
},
|
|
},
|
|
FileDigests: map[file.Coordinates][]file.Digest{
|
|
c: {
|
|
{
|
|
Algorithm: "sha1",
|
|
Value: "d34db33f",
|
|
},
|
|
},
|
|
},
|
|
FileContents: map[file.Coordinates]string{
|
|
c: "some contents",
|
|
},
|
|
FileLicenses: map[file.Coordinates][]file.License{
|
|
c: {
|
|
{
|
|
Value: "MIT",
|
|
SPDXExpression: "MIT",
|
|
Type: "MIT",
|
|
LicenseEvidence: &file.LicenseEvidence{
|
|
Confidence: 1,
|
|
Offset: 2,
|
|
Extent: 3,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
LinuxDistribution: &linux.Release{
|
|
PrettyName: "some os",
|
|
Name: "os",
|
|
ID: "os-id",
|
|
IDLike: []string{"os"},
|
|
Version: "version",
|
|
VersionID: "version",
|
|
VersionCodename: "codename",
|
|
BuildID: "build-id",
|
|
ImageID: "image-id",
|
|
ImageVersion: "image-version",
|
|
Variant: "variant",
|
|
VariantID: "variant-id",
|
|
HomeURL: "https://example.org/os",
|
|
SupportURL: "https://example.org/os/support",
|
|
BugReportURL: "https://example.org/os/bugs",
|
|
PrivacyPolicyURL: "https://example.org/os/privacy",
|
|
CPEName: "os-cpe",
|
|
SupportEnd: "now",
|
|
},
|
|
},
|
|
Relationships: nil,
|
|
Source: source.Description{
|
|
ID: "some-id",
|
|
Name: "some-name",
|
|
Version: "some-version",
|
|
Metadata: source.FileSourceMetadata{
|
|
Path: "/some-file-source-path",
|
|
Digests: []file.Digest{
|
|
{
|
|
Algorithm: "sha1",
|
|
Value: "d34db33f",
|
|
},
|
|
},
|
|
MIMEType: "file/zip",
|
|
},
|
|
},
|
|
Descriptor: sbom.Descriptor{
|
|
Name: "syft",
|
|
Version: "this-version",
|
|
},
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
enc := NewFormatEncoder()
|
|
err := enc.Encode(buf, s)
|
|
require.NoError(t, err)
|
|
|
|
dec := NewFormatDecoder()
|
|
|
|
got, decodedID, decodedVersion, err := dec.Decode(bytes.NewReader(buf.Bytes()))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, ID, decodedID)
|
|
assert.Equal(t, internal.JSONSchemaVersion, decodedVersion)
|
|
|
|
require.Equal(t, s, *got)
|
|
}
|