mirror of
https://github.com/anchore/syft.git
synced 2026-10-31 22:46:36 +01:00
Add ability to scan snaps (as a source) (#3929)
This commit is contained in:
@@ -37,6 +37,7 @@ const (
|
||||
prefixImage = "Image"
|
||||
prefixDirectory = "Directory"
|
||||
prefixFile = "File"
|
||||
prefixSnap = "Snap"
|
||||
prefixUnknown = "Unknown"
|
||||
)
|
||||
|
||||
@@ -228,6 +229,18 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
Value: d.Value,
|
||||
})
|
||||
}
|
||||
|
||||
case source.SnapMetadata:
|
||||
prefix = prefixSnap
|
||||
purpose = spdxPrimaryPurposeContainer
|
||||
|
||||
for _, d := range m.Digests {
|
||||
checksums = append(checksums, spdx.Checksum{
|
||||
Algorithm: toChecksumAlgorithm(d.Algorithm),
|
||||
Value: d.Value,
|
||||
})
|
||||
}
|
||||
|
||||
default:
|
||||
prefix = prefixUnknown
|
||||
purpose = spdxPrimaryPurposeOther
|
||||
|
||||
@@ -237,6 +237,82 @@ func Test_toFormatModel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
in: sbom.SBOM{
|
||||
Source: source.Description{
|
||||
Name: "etcd",
|
||||
Version: "3.4.36",
|
||||
Metadata: source.SnapMetadata{
|
||||
Summary: "Distributed reliable key-value store",
|
||||
Base: "core18",
|
||||
Grade: "stable",
|
||||
Confinement: "strict",
|
||||
Architectures: []string{
|
||||
"amd64",
|
||||
},
|
||||
Digests: []file.Digest{
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "d34db33f",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(pkg.Package{
|
||||
Name: "pkg-1",
|
||||
Version: "version-1",
|
||||
}),
|
||||
},
|
||||
},
|
||||
expected: &spdx.Document{
|
||||
SPDXIdentifier: "DOCUMENT",
|
||||
SPDXVersion: spdx.Version,
|
||||
DataLicense: spdx.DataLicense,
|
||||
DocumentName: "etcd",
|
||||
Packages: []*spdx.Package{
|
||||
{
|
||||
PackageSPDXIdentifier: "Package-pkg-1-pkg-1",
|
||||
PackageName: "pkg-1",
|
||||
PackageVersion: "version-1",
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
{
|
||||
PackageSPDXIdentifier: "DocumentRoot-Snap-etcd",
|
||||
PackageName: "etcd",
|
||||
PackageVersion: "3.4.36",
|
||||
PrimaryPackagePurpose: "CONTAINER",
|
||||
PackageChecksums: []spdx.Checksum{{Algorithm: "SHA256", Value: "d34db33f"}},
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
},
|
||||
Relationships: []*spdx.Relationship{
|
||||
{
|
||||
RefA: spdx.DocElementID{
|
||||
ElementRefID: "DocumentRoot-Snap-etcd",
|
||||
},
|
||||
RefB: spdx.DocElementID{
|
||||
ElementRefID: "Package-pkg-1-pkg-1",
|
||||
},
|
||||
Relationship: spdx.RelationshipContains,
|
||||
},
|
||||
{
|
||||
RefA: spdx.DocElementID{
|
||||
ElementRefID: "DOCUMENT",
|
||||
},
|
||||
RefB: spdx.DocElementID{
|
||||
ElementRefID: "DocumentRoot-Snap-etcd",
|
||||
},
|
||||
Relationship: spdx.RelationshipDescribes,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -130,6 +130,11 @@ func toPath(s source.Description, p pkg.Package) string {
|
||||
return fmt.Sprintf("%s/%s", path, packagePath)
|
||||
}
|
||||
return packagePath
|
||||
case source.SnapMetadata:
|
||||
if inputPath != "" {
|
||||
return fmt.Sprintf("%s:/%s", inputPath, packagePath)
|
||||
}
|
||||
return packagePath
|
||||
}
|
||||
}
|
||||
return inputPath
|
||||
|
||||
@@ -173,6 +173,11 @@ func Test_toGithubModel(t *testing.T) {
|
||||
metadata: source.FileMetadata{Path: "./archive.tar.gz"},
|
||||
testPath: "archive.tar.gz:/etc",
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
metadata: source.SnapMetadata{},
|
||||
testPath: "name:/etc",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -180,6 +185,7 @@ func Test_toGithubModel(t *testing.T) {
|
||||
s := sbomFixture()
|
||||
|
||||
if test.metadata != nil {
|
||||
s.Source.Name = "name"
|
||||
s.Source.Metadata = test.metadata
|
||||
}
|
||||
actual := ToGithubModel(&s)
|
||||
|
||||
@@ -45,6 +45,15 @@ func Test_DocumentName(t *testing.T) {
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
srcMetadata: source.Description{
|
||||
Name: "some/name",
|
||||
// there is nothing in the snap metadata that indicates a name
|
||||
Metadata: source.SnapMetadata{},
|
||||
},
|
||||
expected: "some/name",
|
||||
},
|
||||
{
|
||||
name: "named",
|
||||
srcMetadata: source.Description{
|
||||
|
||||
@@ -16,6 +16,7 @@ const (
|
||||
InputImage = "image"
|
||||
InputDirectory = "dir"
|
||||
InputFile = "file"
|
||||
InputSnap = "snap"
|
||||
)
|
||||
|
||||
func DocumentNameAndNamespace(src source.Description, desc sbom.Descriptor) (string, string) {
|
||||
@@ -33,6 +34,8 @@ func DocumentNamespace(name string, src source.Description, desc sbom.Descriptor
|
||||
input = InputDirectory
|
||||
case source.FileMetadata:
|
||||
input = InputFile
|
||||
case source.SnapMetadata:
|
||||
input = InputSnap
|
||||
}
|
||||
|
||||
uniqueID := uuid.Must(uuid.NewRandom())
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_documentNamespace(t *testing.T) {
|
||||
func Test_DocumentNamespace(t *testing.T) {
|
||||
tracker := sourcemetadata.NewCompletionTester(t)
|
||||
|
||||
tests := []struct {
|
||||
@@ -53,6 +53,14 @@ func Test_documentNamespace(t *testing.T) {
|
||||
},
|
||||
expected: "https://anchore.com/syft/file/my-name-",
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
inputName: "my-name",
|
||||
src: source.Description{
|
||||
Metadata: source.SnapMetadata{},
|
||||
},
|
||||
expected: "https://anchore.com/syft/snap/my-name-",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
@@ -145,6 +145,51 @@ func TestSource_UnmarshalJSON(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
input: []byte(`{
|
||||
"id": "foobar",
|
||||
"name": "etcd",
|
||||
"version": "3.4.36",
|
||||
"type": "snap",
|
||||
"metadata": {
|
||||
"summary": "Distributed reliable key-value store",
|
||||
"base": "core18",
|
||||
"grade": "stable",
|
||||
"confinement": "strict",
|
||||
"architectures": [
|
||||
"amd64"
|
||||
],
|
||||
"digests": [
|
||||
{
|
||||
"algorithm": "sha256",
|
||||
"value": "6700d789d2c38b0f7513058ddcea8f9a275e2206b4621a772eb065e12069956e"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`),
|
||||
expected: &Source{
|
||||
ID: "foobar",
|
||||
Name: "etcd",
|
||||
Version: "3.4.36",
|
||||
Type: "snap",
|
||||
Metadata: source.SnapMetadata{
|
||||
Summary: "Distributed reliable key-value store",
|
||||
Base: "core18",
|
||||
Grade: "stable",
|
||||
Confinement: "strict",
|
||||
Architectures: []string{
|
||||
"amd64",
|
||||
},
|
||||
Digests: []file.Digest{
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "6700d789d2c38b0f7513058ddcea8f9a275e2206b4621a772eb065e12069956e",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown source type",
|
||||
input: []byte(`{
|
||||
|
||||
@@ -127,6 +127,36 @@ func Test_toSourceModel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.SnapMetadata{
|
||||
Summary: "some summary",
|
||||
Base: "some/base",
|
||||
Grade: "some grade",
|
||||
Confinement: "some confinement",
|
||||
Architectures: []string{"x86_64", "arm64"},
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
},
|
||||
},
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "snap",
|
||||
Metadata: source.SnapMetadata{
|
||||
Summary: "some summary",
|
||||
Base: "some/base",
|
||||
Grade: "some grade",
|
||||
Confinement: "some confinement",
|
||||
Architectures: []string{"x86_64", "arm64"},
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
// below are regression tests for when the name/version are not provided
|
||||
// historically we've hoisted up the name/version from the metadata, now it is a simple pass-through
|
||||
{
|
||||
|
||||
@@ -100,6 +100,36 @@ func Test_toSyftSourceData(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "snap",
|
||||
src: model.Source{
|
||||
ID: "the-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "snap",
|
||||
Metadata: source.SnapMetadata{
|
||||
Summary: "something!",
|
||||
Base: "base!",
|
||||
Grade: "grade!",
|
||||
Confinement: "confined!",
|
||||
Architectures: []string{"arch!"},
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest!"}},
|
||||
},
|
||||
},
|
||||
expected: &source.Description{
|
||||
ID: "the-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Metadata: source.SnapMetadata{
|
||||
Summary: "something!",
|
||||
Base: "base!",
|
||||
Grade: "grade!",
|
||||
Confinement: "confined!",
|
||||
Architectures: []string{"arch!"},
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest!"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
// below are regression tests for when the name/version are not provided
|
||||
// historically we've hoisted up the name/version from the metadata, now it is a simple pass-through
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user