mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 17:03:17 +01:00
feat: add identifiable field to source object (#1243)
This commit is contained in:
parent
40d294a89e
commit
7d2fe9d95e
2
go.mod
2
go.mod
@ -60,6 +60,7 @@ require (
|
|||||||
github.com/google/go-containerregistry v0.11.0
|
github.com/google/go-containerregistry v0.11.0
|
||||||
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add
|
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add
|
||||||
github.com/knqyf263/go-rpmdb v0.0.0-20220629110411-9a3bd2ebb923
|
github.com/knqyf263/go-rpmdb v0.0.0-20220629110411-9a3bd2ebb923
|
||||||
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/sassoftware/go-rpmutils v0.2.0
|
github.com/sassoftware/go-rpmutils v0.2.0
|
||||||
github.com/sigstore/cosign v1.12.1
|
github.com/sigstore/cosign v1.12.1
|
||||||
github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2
|
github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2
|
||||||
@ -218,7 +219,6 @@ require (
|
|||||||
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
|
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
|
||||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
|
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||||
|
|||||||
@ -15,16 +15,19 @@ import (
|
|||||||
|
|
||||||
"github.com/bmatcuk/doublestar/v4"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"github.com/mholt/archiver/v3"
|
"github.com/mholt/archiver/v3"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope"
|
"github.com/anchore/stereoscope"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source is an object that captures the data source to be cataloged, configuration, and a specific resolver used
|
// Source is an object that captures the data source to be cataloged, configuration, and a specific resolver used
|
||||||
// in cataloging (based on the data source and configuration)
|
// in cataloging (based on the data source and configuration)
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
id artifact.ID
|
||||||
Image *image.Image // the image object to be cataloged (image only)
|
Image *image.Image // the image object to be cataloged (image only)
|
||||||
Metadata Metadata
|
Metadata Metadata
|
||||||
directoryResolver *directoryResolver
|
directoryResolver *directoryResolver
|
||||||
@ -304,6 +307,76 @@ func NewFromImage(img *image.Image, userImageStr string) (Source, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Source) ID() artifact.ID {
|
||||||
|
if s.id == "" {
|
||||||
|
s.SetID()
|
||||||
|
}
|
||||||
|
return s.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Source) SetID() {
|
||||||
|
var d string
|
||||||
|
switch s.Metadata.Scheme {
|
||||||
|
case DirectoryScheme:
|
||||||
|
d = digest.FromString(s.Metadata.Path).String()
|
||||||
|
case FileScheme:
|
||||||
|
// attempt to use the digest of the contents of the file as the ID
|
||||||
|
file, err := os.Open(s.Metadata.Path)
|
||||||
|
if err != nil {
|
||||||
|
d = digest.FromString(s.Metadata.Path).String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
di, err := digest.FromReader(file)
|
||||||
|
if err != nil {
|
||||||
|
d = digest.FromString(s.Metadata.Path).String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d = di.String()
|
||||||
|
case ImageScheme:
|
||||||
|
manifestDigest := digest.FromBytes(s.Image.Metadata.RawManifest).String()
|
||||||
|
if manifestDigest != "" {
|
||||||
|
d = manifestDigest
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcuate chain ID for image sources where manifestDigest is not available
|
||||||
|
// https://github.com/opencontainers/image-spec/blob/main/config.md#layer-chainid
|
||||||
|
d = calculateChainID(s.Image)
|
||||||
|
if d == "" {
|
||||||
|
// TODO what happens here if image has no layers?
|
||||||
|
// Is this case possible
|
||||||
|
d = digest.FromString(s.Metadata.ImageMetadata.UserInput).String()
|
||||||
|
}
|
||||||
|
default: // for UnknownScheme we hash the struct
|
||||||
|
id, _ := artifact.IDByHash(s)
|
||||||
|
d = string(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.id = artifact.ID(strings.TrimPrefix(d, "sha256:"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateChainID(img *image.Image) string {
|
||||||
|
if len(img.Layers) < 1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffID(L0) = digest of layer 0
|
||||||
|
// https://github.com/anchore/stereoscope/blob/1b1b744a919964f38d14e1416fb3f25221b761ce/pkg/image/layer_metadata.go#L19-L32
|
||||||
|
chainID := img.Layers[0].Metadata.Digest
|
||||||
|
id := chain(chainID, img.Layers[1:])
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func chain(chainID string, layers []*image.Layer) string {
|
||||||
|
if len(layers) < 1 {
|
||||||
|
return chainID
|
||||||
|
}
|
||||||
|
|
||||||
|
chainID = digest.FromString(layers[0].Metadata.Digest + " " + chainID).String()
|
||||||
|
return chain(chainID, layers[1:])
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Source) FileResolver(scope Scope) (FileResolver, error) {
|
func (s *Source) FileResolver(scope Scope) (FileResolver, error) {
|
||||||
switch s.Metadata.Scheme {
|
switch s.Metadata.Scheme {
|
||||||
case DirectoryScheme, FileScheme:
|
case DirectoryScheme, FileScheme:
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseInput(t *testing.T) {
|
func TestParseInput(t *testing.T) {
|
||||||
@ -65,6 +66,72 @@ func TestNewFromImageFails(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetID(t *testing.T) {
|
||||||
|
layer := image.NewLayer(nil)
|
||||||
|
layer.Metadata = image.LayerMetadata{
|
||||||
|
Digest: "sha256:6f4fb385d4e698647bf2a450749dfbb7bc2831ec9a730ef4046c78c08d468e89",
|
||||||
|
}
|
||||||
|
img := image.Image{
|
||||||
|
Layers: []*image.Layer{layer},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input *Source
|
||||||
|
expected artifact.ID
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "source.SetID sets the ID for FileScheme",
|
||||||
|
input: &Source{
|
||||||
|
Metadata: Metadata{
|
||||||
|
Scheme: FileScheme,
|
||||||
|
Path: "test-fixtures/image-simple/file-1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: artifact.ID("55096713247489add592ce977637be868497132b36d1e294a3831925ec64319a"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source.SetID sets the ID for ImageScheme",
|
||||||
|
input: &Source{
|
||||||
|
Image: &img,
|
||||||
|
Metadata: Metadata{
|
||||||
|
Scheme: ImageScheme,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: artifact.ID("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source.SetID sets the ID for DirectoryScheme",
|
||||||
|
input: &Source{
|
||||||
|
Image: &img,
|
||||||
|
Metadata: Metadata{
|
||||||
|
Scheme: DirectoryScheme,
|
||||||
|
Path: "test-fixtures/image-simple",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: artifact.ID("91db61e5e0ae097ef764796ce85e442a93f2a03e5313d4c7307e9b413f62e8c4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source.SetID sets the ID for UnknownScheme",
|
||||||
|
input: &Source{
|
||||||
|
Image: &img,
|
||||||
|
Metadata: Metadata{
|
||||||
|
Scheme: UnknownScheme,
|
||||||
|
Path: "test-fixtures/image-simple",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: artifact.ID("febd2d6148dc327d"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
test.input.SetID()
|
||||||
|
assert.Equal(t, test.expected, test.input.ID())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewFromImage(t *testing.T) {
|
func TestNewFromImage(t *testing.T) {
|
||||||
layer := image.NewLayer(nil)
|
layer := image.NewLayer(nil)
|
||||||
img := image.Image{
|
img := image.Image{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user