mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 16:06:36 +01:00
Refactor source API (#1846)
* refactor source API and syft json source block Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update source detection and format test utils Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * generate list of all source metadata types Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * extract base and root normalization into helper functions Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * preserve syftjson model package name import ref Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * alias should not be a pointer Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -4,16 +4,18 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func DocumentName(srcMetadata source.Metadata) string {
|
||||
func DocumentName(srcMetadata source.Description) string {
|
||||
if srcMetadata.Name != "" {
|
||||
return srcMetadata.Name
|
||||
}
|
||||
|
||||
switch srcMetadata.Scheme {
|
||||
case source.ImageScheme:
|
||||
return srcMetadata.ImageMetadata.UserInput
|
||||
case source.DirectoryScheme, source.FileScheme:
|
||||
return srcMetadata.Path
|
||||
switch metadata := srcMetadata.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
return metadata.UserInput
|
||||
case source.DirectorySourceMetadata:
|
||||
return metadata.Path
|
||||
case source.FileSourceMetadata:
|
||||
return metadata.Path
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -5,31 +5,27 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_DocumentName(t *testing.T) {
|
||||
allSchemes := strset.New()
|
||||
for _, s := range source.AllSchemes {
|
||||
allSchemes.Add(string(s))
|
||||
}
|
||||
testedSchemes := strset.New()
|
||||
|
||||
tracker := sourcemetadata.NewCompletionTester(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputName string
|
||||
srcMetadata source.Metadata
|
||||
srcMetadata source.Description
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "image",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
UserInput: "image-repo/name:tag",
|
||||
ID: "id",
|
||||
ManifestDigest: "digest",
|
||||
@@ -40,18 +36,16 @@ func Test_DocumentName(t *testing.T) {
|
||||
{
|
||||
name: "directory",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.DirectoryScheme,
|
||||
Path: "some/path/to/place",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{Path: "some/path/to/place"},
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.FileScheme,
|
||||
Path: "some/path/to/place",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.FileSourceMetadata{Path: "some/path/to/place"},
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
},
|
||||
@@ -62,10 +56,7 @@ func Test_DocumentName(t *testing.T) {
|
||||
assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("actual name %q", actual))
|
||||
|
||||
// track each scheme tested (passed or not)
|
||||
testedSchemes.Add(string(test.srcMetadata.Scheme))
|
||||
tracker.Tested(t, test.srcMetadata.Metadata)
|
||||
})
|
||||
}
|
||||
|
||||
// assert all possible schemes were under test
|
||||
assert.ElementsMatch(t, allSchemes.List(), testedSchemes.List(), "not all source.Schemes are under test")
|
||||
}
|
||||
|
||||
@@ -18,20 +18,20 @@ const (
|
||||
inputFile = "file"
|
||||
)
|
||||
|
||||
func DocumentNameAndNamespace(srcMetadata source.Metadata) (string, string) {
|
||||
name := DocumentName(srcMetadata)
|
||||
return name, DocumentNamespace(name, srcMetadata)
|
||||
func DocumentNameAndNamespace(src source.Description) (string, string) {
|
||||
name := DocumentName(src)
|
||||
return name, DocumentNamespace(name, src)
|
||||
}
|
||||
|
||||
func DocumentNamespace(name string, srcMetadata source.Metadata) string {
|
||||
func DocumentNamespace(name string, src source.Description) string {
|
||||
name = cleanName(name)
|
||||
input := "unknown-source-type"
|
||||
switch srcMetadata.Scheme {
|
||||
case source.ImageScheme:
|
||||
switch src.Metadata.(type) {
|
||||
case source.StereoscopeImageSourceMetadata:
|
||||
input = inputImage
|
||||
case source.DirectoryScheme:
|
||||
case source.DirectorySourceMetadata:
|
||||
input = inputDirectory
|
||||
case source.FileScheme:
|
||||
case source.FileSourceMetadata:
|
||||
input = inputFile
|
||||
}
|
||||
|
||||
|
||||
@@ -5,31 +5,26 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_documentNamespace(t *testing.T) {
|
||||
allSchemes := strset.New()
|
||||
for _, s := range source.AllSchemes {
|
||||
allSchemes.Add(string(s))
|
||||
}
|
||||
testedSchemes := strset.New()
|
||||
tracker := sourcemetadata.NewCompletionTester(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputName string
|
||||
srcMetadata source.Metadata
|
||||
expected string
|
||||
name string
|
||||
inputName string
|
||||
src source.Description
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "image",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
src: source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
UserInput: "image-repo/name:tag",
|
||||
ID: "id",
|
||||
ManifestDigest: "digest",
|
||||
@@ -40,33 +35,32 @@ func Test_documentNamespace(t *testing.T) {
|
||||
{
|
||||
name: "directory",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.DirectoryScheme,
|
||||
Path: "some/path/to/place",
|
||||
src: source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
},
|
||||
expected: "https://anchore.com/syft/dir/my-name-",
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.FileScheme,
|
||||
Path: "some/path/to/place",
|
||||
src: source.Description{
|
||||
Metadata: source.FileSourceMetadata{
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
},
|
||||
expected: "https://anchore.com/syft/file/my-name-",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := DocumentNamespace(test.inputName, test.srcMetadata)
|
||||
actual := DocumentNamespace(test.inputName, test.src)
|
||||
// note: since the namespace ends with a UUID we check the prefix
|
||||
assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("actual namespace %q", actual))
|
||||
|
||||
// track each scheme tested (passed or not)
|
||||
testedSchemes.Add(string(test.srcMetadata.Scheme))
|
||||
tracker.Tested(t, test.src.Metadata)
|
||||
})
|
||||
}
|
||||
|
||||
// assert all possible schemes were under test
|
||||
assert.ElementsMatch(t, allSchemes.List(), testedSchemes.List(), "not all source.Schemes are under test")
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ func ToSyftModel(doc *spdx.Document) (*sbom.SBOM, error) {
|
||||
|
||||
spdxIDMap := make(map[string]interface{})
|
||||
|
||||
src := source.Metadata{Scheme: source.UnknownScheme}
|
||||
src.Scheme = extractSchemeFromNamespace(doc.DocumentNamespace)
|
||||
src := extractSourceFromNamespace(doc.DocumentNamespace)
|
||||
|
||||
s := &sbom.SBOM{
|
||||
Source: src,
|
||||
@@ -54,24 +53,32 @@ func ToSyftModel(doc *spdx.Document) (*sbom.SBOM, error) {
|
||||
// image, directory, for example. This is our best effort to determine
|
||||
// the scheme. Syft-generated SBOMs have in the namespace
|
||||
// field a type encoded, which we try to identify here.
|
||||
func extractSchemeFromNamespace(ns string) source.Scheme {
|
||||
func extractSourceFromNamespace(ns string) source.Description {
|
||||
u, err := url.Parse(ns)
|
||||
if err != nil {
|
||||
return source.UnknownScheme
|
||||
return source.Description{
|
||||
Metadata: nil,
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(u.Path, "/")
|
||||
for _, p := range parts {
|
||||
switch p {
|
||||
case inputFile:
|
||||
return source.FileScheme
|
||||
return source.Description{
|
||||
Metadata: source.FileSourceMetadata{},
|
||||
}
|
||||
case inputImage:
|
||||
return source.ImageScheme
|
||||
return source.Description{
|
||||
Metadata: source.StereoscopeImageSourceMetadata{},
|
||||
}
|
||||
case inputDirectory:
|
||||
return source.DirectoryScheme
|
||||
return source.Description{
|
||||
Metadata: source.DirectorySourceMetadata{},
|
||||
}
|
||||
}
|
||||
}
|
||||
return source.UnknownScheme
|
||||
return source.Description{}
|
||||
}
|
||||
|
||||
func findLinuxReleaseByPURL(doc *spdx.Document) *linux.Release {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/spdx/tools-golang/spdx"
|
||||
@@ -197,36 +198,46 @@ func Test_extractMetadata(t *testing.T) {
|
||||
func TestExtractSourceFromNamespaces(t *testing.T) {
|
||||
tests := []struct {
|
||||
namespace string
|
||||
expected source.Scheme
|
||||
expected any
|
||||
}{
|
||||
{
|
||||
namespace: "https://anchore.com/syft/file/d42b01d0-7325-409b-b03f-74082935c4d3",
|
||||
expected: source.FileScheme,
|
||||
expected: source.FileSourceMetadata{},
|
||||
},
|
||||
{
|
||||
namespace: "https://anchore.com/syft/image/d42b01d0-7325-409b-b03f-74082935c4d3",
|
||||
expected: source.ImageScheme,
|
||||
expected: source.StereoscopeImageSourceMetadata{},
|
||||
},
|
||||
{
|
||||
namespace: "https://anchore.com/syft/dir/d42b01d0-7325-409b-b03f-74082935c4d3",
|
||||
expected: source.DirectoryScheme,
|
||||
expected: source.DirectorySourceMetadata{},
|
||||
},
|
||||
{
|
||||
namespace: "https://another-host/blob/123",
|
||||
expected: source.UnknownScheme,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
namespace: "bla bla",
|
||||
expected: source.UnknownScheme,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
namespace: "",
|
||||
expected: source.UnknownScheme,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
require.Equal(t, tt.expected, extractSchemeFromNamespace(tt.namespace))
|
||||
desc := extractSourceFromNamespace(tt.namespace)
|
||||
if tt.expected == nil && desc.Metadata == nil {
|
||||
return
|
||||
}
|
||||
if tt.expected != nil && desc.Metadata == nil {
|
||||
t.Fatal("expected metadata but got nil")
|
||||
}
|
||||
if tt.expected == nil && desc.Metadata != nil {
|
||||
t.Fatal("expected nil metadata but got something")
|
||||
}
|
||||
require.Equal(t, reflect.TypeOf(tt.expected), reflect.TypeOf(desc.Metadata))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user