mirror of
https://github.com/anchore/syft.git
synced 2026-04-05 22:30:35 +02:00
migrate location and file metadata to the file package
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
a49c7e3c53
commit
f999604a29
@ -136,12 +136,12 @@ func attestExec(ctx context.Context, _ *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("could not generate source input for attest command: %w", err)
|
return fmt.Errorf("could not generate source input for attest command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch si.Scheme {
|
switch si.Type {
|
||||||
case source.ImageScheme, source.UnknownScheme:
|
case source.ImageType, source.UnknownType:
|
||||||
// at this point we know that it cannot be dir: or file: schemes, so we will assume that the unknown scheme could represent an image
|
// at this point we know that it cannot be dir: or file: schemes, so we will assume that the unknown scheme could represent an image
|
||||||
si.Scheme = source.ImageScheme
|
si.Type = source.ImageType
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("attest command can only be used with image sources but discovered %q when given %q", si.Scheme, userInput)
|
return fmt.Errorf("attest command can only be used with image sources but discovered %q when given %q", si.Type, userInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the original detection was from a local daemon we want to short circuit
|
// if the original detection was from a local daemon we want to short circuit
|
||||||
|
|||||||
@ -312,7 +312,7 @@ func packagesExecWorker(si source.Input, writer sbom.Writer) <-chan error {
|
|||||||
func runPackageSbomUpload(src *source.Source, s sbom.SBOM) error {
|
func runPackageSbomUpload(src *source.Source, s sbom.SBOM) error {
|
||||||
log.Infof("uploading results to %s", appConfig.Anchore.Host)
|
log.Infof("uploading results to %s", appConfig.Anchore.Host)
|
||||||
|
|
||||||
if src.Metadata.Scheme != source.ImageScheme {
|
if src.Metadata.Scheme != source.ImageType {
|
||||||
return fmt.Errorf("unable to upload results: only images are supported")
|
return fmt.Errorf("unable to upload results: only images are supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
"github.com/anchore/syft/internal/bus"
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
"github.com/wagoodman/go-progress"
|
"github.com/wagoodman/go-progress"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/anchore/client-go/pkg/external"
|
"github.com/anchore/client-go/pkg/external"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -56,9 +57,9 @@ func sbomFixture() sbom.SBOM {
|
|||||||
Name: "name",
|
Name: "name",
|
||||||
Version: "version",
|
Version: "version",
|
||||||
FoundBy: "foundBy",
|
FoundBy: "foundBy",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "path",
|
RealPath: "path",
|
||||||
FileSystemID: "layerID",
|
FileSystemID: "layerID",
|
||||||
},
|
},
|
||||||
@ -102,13 +103,13 @@ func sbomFixture() sbom.SBOM {
|
|||||||
},
|
},
|
||||||
Relationships: []artifact.Relationship{
|
Relationships: []artifact.Relationship{
|
||||||
{
|
{
|
||||||
From: source.NewLocation("/place1"),
|
From: file.NewLocation("/place1"),
|
||||||
To: source.NewLocation("/place2"),
|
To: file.NewLocation("/place2"),
|
||||||
Type: artifact.ContainsRelationship,
|
Type: artifact.ContainsRelationship,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Source: source.Metadata{
|
Source: source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: "user-in",
|
UserInput: "user-in",
|
||||||
Layers: nil,
|
Layers: nil,
|
||||||
|
|||||||
@ -4,12 +4,13 @@ import (
|
|||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,15 @@ package config
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/internal/version"
|
|
||||||
"github.com/anchore/syft/syft"
|
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/version"
|
||||||
|
"github.com/anchore/syft/syft"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/file/cataloger/fileclassifier"
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
@ -76,7 +78,7 @@ func (cfg Application) ToCatalogingConfig() (*syft.CatalogingConfig, error) {
|
|||||||
SecretsConfig: *secretsConfig,
|
SecretsConfig: *secretsConfig,
|
||||||
SecretsScope: cfg.Secrets.Cataloger.ScopeOpt,
|
SecretsScope: cfg.Secrets.Cataloger.ScopeOpt,
|
||||||
ClassifyFiles: cfg.FileClassification.Cataloger.Enabled,
|
ClassifyFiles: cfg.FileClassification.Cataloger.Enabled,
|
||||||
FileClassifiers: file.DefaultClassifiers(),
|
FileClassifiers: fileclassifier.DefaultClassifiers(),
|
||||||
ContentsConfig: cfg.FileContents.ToConfig(),
|
ContentsConfig: cfg.FileContents.ToConfig(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/file/cataloger/secrets"
|
"github.com/anchore/syft/syft/file/cataloger/secrets"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
|
|||||||
@ -3,11 +3,12 @@ package cyclonedxhelpers
|
|||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/CycloneDX/cyclonedx-go"
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/formats/common"
|
"github.com/anchore/syft/internal/formats/common"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func encodeComponent(p pkg.Package) cyclonedx.Component {
|
func encodeComponent(p pkg.Package) cyclonedx.Component {
|
||||||
@ -71,9 +72,9 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeLocations(vals map[string]string) []source.Location {
|
func decodeLocations(vals map[string]string) []file.Location {
|
||||||
v := common.Decode(reflect.TypeOf([]source.Location{}), vals, "syft:location", CycloneDXFields)
|
v := common.Decode(reflect.TypeOf([]file.Location{}), vals, "syft:location", CycloneDXFields)
|
||||||
out, _ := v.([]source.Location)
|
out, _ := v.([]file.Location)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
package cyclonedxhelpers
|
package cyclonedxhelpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/CycloneDX/cyclonedx-go"
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_encodeComponentProperties(t *testing.T) {
|
func Test_encodeComponentProperties(t *testing.T) {
|
||||||
@ -26,8 +26,8 @@ func Test_encodeComponentProperties(t *testing.T) {
|
|||||||
name: "from apk",
|
name: "from apk",
|
||||||
input: pkg.Package{
|
input: pkg.Package{
|
||||||
FoundBy: "cataloger",
|
FoundBy: "cataloger",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{Coordinates: source.Coordinates{RealPath: "test"}},
|
{Coordinates: file.Coordinates{RealPath: "test"}},
|
||||||
},
|
},
|
||||||
Metadata: pkg.ApkMetadata{
|
Metadata: pkg.ApkMetadata{
|
||||||
Package: "libc-utils",
|
Package: "libc-utils",
|
||||||
|
|||||||
@ -199,7 +199,7 @@ func decodeMetadata(component *cyclonedx.Component) source.Metadata {
|
|||||||
switch component.Type {
|
switch component.Type {
|
||||||
case cyclonedx.ComponentTypeContainer:
|
case cyclonedx.ComponentTypeContainer:
|
||||||
return source.Metadata{
|
return source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: component.Name,
|
UserInput: component.Name,
|
||||||
ID: component.BOMRef,
|
ID: component.BOMRef,
|
||||||
@ -208,7 +208,7 @@ func decodeMetadata(component *cyclonedx.Component) source.Metadata {
|
|||||||
}
|
}
|
||||||
case cyclonedx.ComponentTypeFile:
|
case cyclonedx.ComponentTypeFile:
|
||||||
return source.Metadata{
|
return source.Metadata{
|
||||||
Scheme: source.FileScheme, // or source.DirectoryScheme
|
Scheme: source.FileType, // or source.DirectoryType
|
||||||
Path: component.Name,
|
Path: component.Name,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: component.Name,
|
UserInput: component.Name,
|
||||||
|
|||||||
@ -155,7 +155,7 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
|
|||||||
|
|
||||||
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
||||||
switch srcMetadata.Scheme {
|
switch srcMetadata.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageType:
|
||||||
bomRef, err := artifact.IDByHash(srcMetadata.ImageMetadata.ID)
|
bomRef, err := artifact.IDByHash(srcMetadata.ImageMetadata.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("unable to get fingerprint of image metadata=%s: %+v", srcMetadata.ImageMetadata.ID, err)
|
log.Warnf("unable to get fingerprint of image metadata=%s: %+v", srcMetadata.ImageMetadata.ID, err)
|
||||||
@ -166,7 +166,7 @@ func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component
|
|||||||
Name: srcMetadata.ImageMetadata.UserInput,
|
Name: srcMetadata.ImageMetadata.UserInput,
|
||||||
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
||||||
}
|
}
|
||||||
case source.DirectoryScheme, source.FileScheme:
|
case source.DirectoryType, source.FileType:
|
||||||
bomRef, err := artifact.IDByHash(srcMetadata.Path)
|
bomRef, err := artifact.IDByHash(srcMetadata.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("unable to get fingerprint of source metadata path=%s: %+v", srcMetadata.Path, err)
|
log.Warnf("unable to get fingerprint of source metadata path=%s: %+v", srcMetadata.Path, err)
|
||||||
|
|||||||
@ -10,9 +10,9 @@ import (
|
|||||||
|
|
||||||
func DocumentName(srcMetadata source.Metadata) (string, error) {
|
func DocumentName(srcMetadata source.Metadata) (string, error) {
|
||||||
switch srcMetadata.Scheme {
|
switch srcMetadata.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageType:
|
||||||
return cleanName(srcMetadata.ImageMetadata.UserInput), nil
|
return cleanName(srcMetadata.ImageMetadata.UserInput), nil
|
||||||
case source.DirectoryScheme, source.FileScheme:
|
case source.DirectoryType, source.FileType:
|
||||||
return cleanName(srcMetadata.Path), nil
|
return cleanName(srcMetadata.Path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
func Test_DocumentName(t *testing.T) {
|
func Test_DocumentName(t *testing.T) {
|
||||||
allSchemes := strset.New()
|
allSchemes := strset.New()
|
||||||
for _, s := range source.AllSchemes {
|
for _, s := range source.AllTypes {
|
||||||
allSchemes.Add(string(s))
|
allSchemes.Add(string(s))
|
||||||
}
|
}
|
||||||
testedSchemes := strset.New()
|
testedSchemes := strset.New()
|
||||||
@ -29,7 +29,7 @@ func Test_DocumentName(t *testing.T) {
|
|||||||
name: "image",
|
name: "image",
|
||||||
inputName: "my-name",
|
inputName: "my-name",
|
||||||
srcMetadata: source.Metadata{
|
srcMetadata: source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: "image-repo/name:tag",
|
UserInput: "image-repo/name:tag",
|
||||||
ID: "id",
|
ID: "id",
|
||||||
@ -42,7 +42,7 @@ func Test_DocumentName(t *testing.T) {
|
|||||||
name: "directory",
|
name: "directory",
|
||||||
inputName: "my-name",
|
inputName: "my-name",
|
||||||
srcMetadata: source.Metadata{
|
srcMetadata: source.Metadata{
|
||||||
Scheme: source.DirectoryScheme,
|
Scheme: source.DirectoryType,
|
||||||
Path: "some/path/to/place",
|
Path: "some/path/to/place",
|
||||||
},
|
},
|
||||||
expected: "some/path/to/place",
|
expected: "some/path/to/place",
|
||||||
@ -51,7 +51,7 @@ func Test_DocumentName(t *testing.T) {
|
|||||||
name: "file",
|
name: "file",
|
||||||
inputName: "my-name",
|
inputName: "my-name",
|
||||||
srcMetadata: source.Metadata{
|
srcMetadata: source.Metadata{
|
||||||
Scheme: source.FileScheme,
|
Scheme: source.FileType,
|
||||||
Path: "some/path/to/place",
|
Path: "some/path/to/place",
|
||||||
},
|
},
|
||||||
expected: "some/path/to/place",
|
expected: "some/path/to/place",
|
||||||
|
|||||||
@ -21,11 +21,11 @@ func DocumentNameAndNamespace(srcMetadata source.Metadata) (string, string, erro
|
|||||||
func DocumentNamespace(name string, srcMetadata source.Metadata) string {
|
func DocumentNamespace(name string, srcMetadata source.Metadata) string {
|
||||||
input := "unknown-source-type"
|
input := "unknown-source-type"
|
||||||
switch srcMetadata.Scheme {
|
switch srcMetadata.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageType:
|
||||||
input = "image"
|
input = "image"
|
||||||
case source.DirectoryScheme:
|
case source.DirectoryType:
|
||||||
input = "dir"
|
input = "dir"
|
||||||
case source.FileScheme:
|
case source.FileType:
|
||||||
input = "file"
|
input = "file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func Test_documentNamespace(t *testing.T) {
|
func Test_documentNamespace(t *testing.T) {
|
||||||
allSchemes := strset.New()
|
allSchemes := strset.New()
|
||||||
for _, s := range source.AllSchemes {
|
for _, s := range source.AllTypes {
|
||||||
allSchemes.Add(string(s))
|
allSchemes.Add(string(s))
|
||||||
}
|
}
|
||||||
testedSchemes := strset.New()
|
testedSchemes := strset.New()
|
||||||
@ -27,7 +27,7 @@ func Test_documentNamespace(t *testing.T) {
|
|||||||
name: "image",
|
name: "image",
|
||||||
inputName: "my-name",
|
inputName: "my-name",
|
||||||
srcMetadata: source.Metadata{
|
srcMetadata: source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: "image-repo/name:tag",
|
UserInput: "image-repo/name:tag",
|
||||||
ID: "id",
|
ID: "id",
|
||||||
@ -40,7 +40,7 @@ func Test_documentNamespace(t *testing.T) {
|
|||||||
name: "directory",
|
name: "directory",
|
||||||
inputName: "my-name",
|
inputName: "my-name",
|
||||||
srcMetadata: source.Metadata{
|
srcMetadata: source.Metadata{
|
||||||
Scheme: source.DirectoryScheme,
|
Scheme: source.DirectoryType,
|
||||||
Path: "some/path/to/place",
|
Path: "some/path/to/place",
|
||||||
},
|
},
|
||||||
expected: "https://anchore.com/syft/dir/my-name-",
|
expected: "https://anchore.com/syft/dir/my-name-",
|
||||||
@ -49,7 +49,7 @@ func Test_documentNamespace(t *testing.T) {
|
|||||||
name: "file",
|
name: "file",
|
||||||
inputName: "my-name",
|
inputName: "my-name",
|
||||||
srcMetadata: source.Metadata{
|
srcMetadata: source.Metadata{
|
||||||
Scheme: source.FileScheme,
|
Scheme: source.FileType,
|
||||||
Path: "some/path/to/place",
|
Path: "some/path/to/place",
|
||||||
},
|
},
|
||||||
expected: "https://anchore.com/syft/file/my-name-",
|
expected: "https://anchore.com/syft/file/my-name-",
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
package spdxhelpers
|
package spdxhelpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,9 +18,9 @@ func Test_SourceInfo(t *testing.T) {
|
|||||||
name: "locations are captured",
|
name: "locations are captured",
|
||||||
input: pkg.Package{
|
input: pkg.Package{
|
||||||
// note: no type given
|
// note: no type given
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/a-place", "/b-place"),
|
file.NewVirtualLocation("/a-place", "/b-place"),
|
||||||
source.NewVirtualLocation("/c-place", "/d-place"),
|
file.NewVirtualLocation("/c-place", "/d-place"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/anchore/syft/syft/linux"
|
"github.com/anchore/syft/syft/linux"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
|
func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
|
||||||
@ -22,8 +21,8 @@ func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
|
|||||||
s := &sbom.SBOM{
|
s := &sbom.SBOM{
|
||||||
Artifacts: sbom.Artifacts{
|
Artifacts: sbom.Artifacts{
|
||||||
PackageCatalog: pkg.NewCatalog(),
|
PackageCatalog: pkg.NewCatalog(),
|
||||||
FileMetadata: map[source.Coordinates]source.FileMetadata{},
|
FileMetadata: map[file.Coordinates]file.Metadata{},
|
||||||
FileDigests: map[source.Coordinates][]file.Digest{},
|
FileDigests: map[file.Coordinates][]file.Digest{},
|
||||||
LinuxDistribution: findLinuxReleaseByPURL(doc),
|
LinuxDistribution: findLinuxReleaseByPURL(doc),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -98,7 +97,7 @@ func toFileDigests(f *spdx.File2_2) (digests []file.Digest) {
|
|||||||
return digests
|
return digests
|
||||||
}
|
}
|
||||||
|
|
||||||
func toFileMetadata(f *spdx.File2_2) (meta source.FileMetadata) {
|
func toFileMetadata(f *spdx.File2_2) (meta file.Metadata) {
|
||||||
// FIXME Syft is currently lossy due to the SPDX 2.2.1 spec not supporting arbitrary mimetypes
|
// FIXME Syft is currently lossy due to the SPDX 2.2.1 spec not supporting arbitrary mimetypes
|
||||||
for _, typ := range f.FileType {
|
for _, typ := range f.FileType {
|
||||||
switch FileType(typ) {
|
switch FileType(typ) {
|
||||||
@ -132,7 +131,7 @@ func toSyftRelationships(spdxIDMap map[string]interface{}, doc *spdx.Document2_2
|
|||||||
b := spdxIDMap[string(r.RefB.ElementRefID)]
|
b := spdxIDMap[string(r.RefB.ElementRefID)]
|
||||||
from, fromOk := a.(*pkg.Package)
|
from, fromOk := a.(*pkg.Package)
|
||||||
toPackage, toPackageOk := b.(*pkg.Package)
|
toPackage, toPackageOk := b.(*pkg.Package)
|
||||||
toLocation, toLocationOk := b.(*source.Location)
|
toLocation, toLocationOk := b.(*file.Location)
|
||||||
if !fromOk || !(toPackageOk || toLocationOk) {
|
if !fromOk || !(toPackageOk || toLocationOk) {
|
||||||
log.Debugf("unable to find valid relationship mapping from SPDX 2.2 JSON, ignoring: (from: %+v) (to: %+v)", a, b)
|
log.Debugf("unable to find valid relationship mapping from SPDX 2.2 JSON, ignoring: (from: %+v) (to: %+v)", a, b)
|
||||||
continue
|
continue
|
||||||
@ -174,7 +173,7 @@ func toSyftRelationships(spdxIDMap map[string]interface{}, doc *spdx.Document2_2
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSyftCoordinates(f *spdx.File2_2) source.Coordinates {
|
func toSyftCoordinates(f *spdx.File2_2) file.Coordinates {
|
||||||
const layerIDPrefix = "layerID: "
|
const layerIDPrefix = "layerID: "
|
||||||
var fileSystemID string
|
var fileSystemID string
|
||||||
if strings.Index(f.FileComment, layerIDPrefix) == 0 {
|
if strings.Index(f.FileComment, layerIDPrefix) == 0 {
|
||||||
@ -183,14 +182,14 @@ func toSyftCoordinates(f *spdx.File2_2) source.Coordinates {
|
|||||||
if strings.Index(string(f.FileSPDXIdentifier), layerIDPrefix) == 0 {
|
if strings.Index(string(f.FileSPDXIdentifier), layerIDPrefix) == 0 {
|
||||||
fileSystemID = strings.TrimPrefix(string(f.FileSPDXIdentifier), layerIDPrefix)
|
fileSystemID = strings.TrimPrefix(string(f.FileSPDXIdentifier), layerIDPrefix)
|
||||||
}
|
}
|
||||||
return source.Coordinates{
|
return file.Coordinates{
|
||||||
RealPath: f.FileName,
|
RealPath: f.FileName,
|
||||||
FileSystemID: fileSystemID,
|
FileSystemID: fileSystemID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSyftLocation(f *spdx.File2_2) *source.Location {
|
func toSyftLocation(f *spdx.File2_2) *file.Location {
|
||||||
return &source.Location{
|
return &file.Location{
|
||||||
Coordinates: toSyftCoordinates(f),
|
Coordinates: toSyftCoordinates(f),
|
||||||
VirtualPath: f.FileName,
|
VirtualPath: f.FileName,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/go-testutils"
|
"github.com/anchore/go-testutils"
|
||||||
"github.com/anchore/stereoscope/pkg/filetree"
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
@ -157,8 +159,8 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
|||||||
catalog.Add(pkg.Package{
|
catalog.Add(pkg.Package{
|
||||||
Name: "package-1",
|
Name: "package-1",
|
||||||
Version: "1.0.1",
|
Version: "1.0.1",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
|
file.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
|
||||||
},
|
},
|
||||||
Type: pkg.PythonPkg,
|
Type: pkg.PythonPkg,
|
||||||
FoundBy: "the-cataloger-1",
|
FoundBy: "the-cataloger-1",
|
||||||
@ -177,8 +179,8 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
|||||||
catalog.Add(pkg.Package{
|
catalog.Add(pkg.Package{
|
||||||
Name: "package-2",
|
Name: "package-2",
|
||||||
Version: "2.0.1",
|
Version: "2.0.1",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
|
file.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
|
||||||
},
|
},
|
||||||
Type: pkg.DebPkg,
|
Type: pkg.DebPkg,
|
||||||
FoundBy: "the-cataloger-2",
|
FoundBy: "the-cataloger-2",
|
||||||
@ -234,8 +236,8 @@ func newDirectoryCatalog() *pkg.Catalog {
|
|||||||
Version: "1.0.1",
|
Version: "1.0.1",
|
||||||
Type: pkg.PythonPkg,
|
Type: pkg.PythonPkg,
|
||||||
FoundBy: "the-cataloger-1",
|
FoundBy: "the-cataloger-1",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewLocation("/some/path/pkg1"),
|
file.NewLocation("/some/path/pkg1"),
|
||||||
},
|
},
|
||||||
Language: pkg.Python,
|
Language: pkg.Python,
|
||||||
MetadataType: pkg.PythonPackageMetadataType,
|
MetadataType: pkg.PythonPackageMetadataType,
|
||||||
@ -259,8 +261,8 @@ func newDirectoryCatalog() *pkg.Catalog {
|
|||||||
Version: "2.0.1",
|
Version: "2.0.1",
|
||||||
Type: pkg.DebPkg,
|
Type: pkg.DebPkg,
|
||||||
FoundBy: "the-cataloger-2",
|
FoundBy: "the-cataloger-2",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewLocation("/some/path/pkg1"),
|
file.NewLocation("/some/path/pkg1"),
|
||||||
},
|
},
|
||||||
MetadataType: pkg.DpkgMetadataType,
|
MetadataType: pkg.DpkgMetadataType,
|
||||||
Metadata: pkg.DpkgMetadata{
|
Metadata: pkg.DpkgMetadata{
|
||||||
|
|||||||
@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
|
// toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
|
||||||
@ -94,7 +93,7 @@ func fileIDsForPackage(packageSpdxID string, relationships []artifact.Relationsh
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := relationship.To.(source.Coordinates); !ok {
|
if _, ok := relationship.To.(file.Coordinates); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +109,7 @@ func toFiles(s sbom.SBOM) []model.File {
|
|||||||
artifacts := s.Artifacts
|
artifacts := s.Artifacts
|
||||||
|
|
||||||
for _, coordinates := range sbom.AllCoordinates(s) {
|
for _, coordinates := range sbom.AllCoordinates(s) {
|
||||||
var metadata *source.FileMetadata
|
var metadata *file.Metadata
|
||||||
if metadataForLocation, exists := artifacts.FileMetadata[coordinates]; exists {
|
if metadataForLocation, exists := artifacts.FileMetadata[coordinates]; exists {
|
||||||
metadata = &metadataForLocation
|
metadata = &metadataForLocation
|
||||||
}
|
}
|
||||||
@ -165,7 +164,7 @@ func toChecksumAlgorithm(algorithm string) string {
|
|||||||
return strings.ToUpper(algorithm)
|
return strings.ToUpper(algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toFileTypes(metadata *source.FileMetadata) (ty []string) {
|
func toFileTypes(metadata *file.Metadata) (ty []string) {
|
||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||||
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,12 +18,12 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
metadata source.FileMetadata
|
metadata file.Metadata
|
||||||
expected []string
|
expected []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "application",
|
name: "application",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "application/vnd.unknown",
|
MIMEType: "application/vnd.unknown",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -33,7 +32,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "archive",
|
name: "archive",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "application/zip",
|
MIMEType: "application/zip",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -43,7 +42,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "audio",
|
name: "audio",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "audio/ogg",
|
MIMEType: "audio/ogg",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -52,7 +51,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "video",
|
name: "video",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "video/3gpp",
|
MIMEType: "video/3gpp",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -61,7 +60,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "text",
|
name: "text",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "text/html",
|
MIMEType: "text/html",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -70,7 +69,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "image",
|
name: "image",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "image/png",
|
MIMEType: "image/png",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -79,7 +78,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "binary",
|
name: "binary",
|
||||||
metadata: source.FileMetadata{
|
metadata: file.Metadata{
|
||||||
MIMEType: "application/x-sharedlib",
|
MIMEType: "application/x-sharedlib",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
@ -175,7 +174,7 @@ func Test_fileIDsForPackage(t *testing.T) {
|
|||||||
Name: "bogus",
|
Name: "bogus",
|
||||||
}
|
}
|
||||||
|
|
||||||
c := source.Coordinates{
|
c := file.Coordinates{
|
||||||
RealPath: "/path",
|
RealPath: "/path",
|
||||||
FileSystemID: "nowhere",
|
FileSystemID: "nowhere",
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,9 +42,9 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
|||||||
p1 := pkg.Package{
|
p1 := pkg.Package{
|
||||||
Name: "package-1",
|
Name: "package-1",
|
||||||
Version: "1.0.1",
|
Version: "1.0.1",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a/place/a",
|
RealPath: "/a/place/a",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -68,9 +68,9 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
|||||||
p2 := pkg.Package{
|
p2 := pkg.Package{
|
||||||
Name: "package-2",
|
Name: "package-2",
|
||||||
Version: "2.0.1",
|
Version: "2.0.1",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/b/place/b",
|
RealPath: "/b/place/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -95,49 +95,49 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
|||||||
s := sbom.SBOM{
|
s := sbom.SBOM{
|
||||||
Artifacts: sbom.Artifacts{
|
Artifacts: sbom.Artifacts{
|
||||||
PackageCatalog: catalog,
|
PackageCatalog: catalog,
|
||||||
FileMetadata: map[source.Coordinates]source.FileMetadata{
|
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||||
source.NewLocation("/a/place").Coordinates: {
|
file.NewLocation("/a/place").Coordinates: {
|
||||||
Mode: 0775,
|
Mode: 0775,
|
||||||
Type: "directory",
|
Type: "directory",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
GroupID: 0,
|
GroupID: 0,
|
||||||
},
|
},
|
||||||
source.NewLocation("/a/place/a").Coordinates: {
|
file.NewLocation("/a/place/a").Coordinates: {
|
||||||
Mode: 0775,
|
Mode: 0775,
|
||||||
Type: "regularFile",
|
Type: "regularFile",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
GroupID: 0,
|
GroupID: 0,
|
||||||
},
|
},
|
||||||
source.NewLocation("/b").Coordinates: {
|
file.NewLocation("/b").Coordinates: {
|
||||||
Mode: 0775,
|
Mode: 0775,
|
||||||
Type: "symbolicLink",
|
Type: "symbolicLink",
|
||||||
LinkDestination: "/c",
|
LinkDestination: "/c",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
GroupID: 0,
|
GroupID: 0,
|
||||||
},
|
},
|
||||||
source.NewLocation("/b/place/b").Coordinates: {
|
file.NewLocation("/b/place/b").Coordinates: {
|
||||||
Mode: 0644,
|
Mode: 0644,
|
||||||
Type: "regularFile",
|
Type: "regularFile",
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
GroupID: 2,
|
GroupID: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FileDigests: map[source.Coordinates][]file.Digest{
|
FileDigests: map[file.Coordinates][]file.Digest{
|
||||||
source.NewLocation("/a/place/a").Coordinates: {
|
file.NewLocation("/a/place/a").Coordinates: {
|
||||||
{
|
{
|
||||||
Algorithm: "sha256",
|
Algorithm: "sha256",
|
||||||
Value: "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
Value: "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
source.NewLocation("/b/place/b").Coordinates: {
|
file.NewLocation("/b/place/b").Coordinates: {
|
||||||
{
|
{
|
||||||
Algorithm: "sha256",
|
Algorithm: "sha256",
|
||||||
Value: "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c",
|
Value: "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FileContents: map[source.Coordinates]string{
|
FileContents: map[file.Coordinates]string{
|
||||||
source.NewLocation("/a/place/a").Coordinates: "the-contents",
|
file.NewLocation("/a/place/a").Coordinates: "the-contents",
|
||||||
},
|
},
|
||||||
LinuxDistribution: &linux.Release{
|
LinuxDistribution: &linux.Release{
|
||||||
ID: "redhat",
|
ID: "redhat",
|
||||||
@ -159,7 +159,7 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Source: source.Metadata{
|
Source: source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: "user-image-input",
|
UserInput: "user-image-input",
|
||||||
ID: "sha256:c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
ID: "sha256:c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
||||||
|
|||||||
@ -2,13 +2,11 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Location source.Coordinates `json:"location"`
|
Location file.Coordinates `json:"location"`
|
||||||
Metadata *FileMetadataEntry `json:"metadata,omitempty"`
|
Metadata *FileMetadataEntry `json:"metadata,omitempty"`
|
||||||
Contents string `json:"contents,omitempty"`
|
Contents string `json:"contents,omitempty"`
|
||||||
Digests []file.Digest `json:"digests,omitempty"`
|
Digests []file.Digest `json:"digests,omitempty"`
|
||||||
@ -16,10 +14,10 @@ type File struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FileMetadataEntry struct {
|
type FileMetadataEntry struct {
|
||||||
Mode int `json:"mode"`
|
Mode int `json:"mode"`
|
||||||
Type source.FileType `json:"type"`
|
Type file.Type `json:"type"`
|
||||||
LinkDestination string `json:"linkDestination,omitempty"`
|
LinkDestination string `json:"linkDestination,omitempty"`
|
||||||
UserID int `json:"userID"`
|
UserID int `json:"userID"`
|
||||||
GroupID int `json:"groupID"`
|
GroupID int `json:"groupID"`
|
||||||
MIMEType string `json:"mimeType"`
|
MIMEType string `json:"mimeType"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
@ -18,16 +18,16 @@ type Package struct {
|
|||||||
|
|
||||||
// PackageBasicData contains non-ambiguous values (type-wise) from pkg.Package.
|
// PackageBasicData contains non-ambiguous values (type-wise) from pkg.Package.
|
||||||
type PackageBasicData struct {
|
type PackageBasicData struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Type pkg.Type `json:"type"`
|
Type pkg.Type `json:"type"`
|
||||||
FoundBy string `json:"foundBy"`
|
FoundBy string `json:"foundBy"`
|
||||||
Locations []source.Coordinates `json:"locations"`
|
Locations []file.Coordinates `json:"locations"`
|
||||||
Licenses []string `json:"licenses"`
|
Licenses []string `json:"licenses"`
|
||||||
Language pkg.Language `json:"language"`
|
Language pkg.Language `json:"language"`
|
||||||
CPEs []string `json:"cpes"`
|
CPEs []string `json:"cpes"`
|
||||||
PURL string `json:"purl"`
|
PURL string `json:"purl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageCustomData contains ambiguous values (type-wise) from pkg.Package.
|
// PackageCustomData contains ambiguous values (type-wise) from pkg.Package.
|
||||||
|
|||||||
@ -2,10 +2,9 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Secrets struct {
|
type Secrets struct {
|
||||||
Location source.Coordinates `json:"location"`
|
Location file.Coordinates `json:"location"`
|
||||||
Secrets []file.SearchResult `json:"secrets"`
|
Secrets []file.SearchResult `json:"secrets"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,7 +73,7 @@ func toDescriptor(d sbom.Descriptor) model.Descriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSecrets(data map[source.Coordinates][]file.SearchResult) []model.Secrets {
|
func toSecrets(data map[file.Coordinates][]file.SearchResult) []model.Secrets {
|
||||||
results := make([]model.Secrets, 0)
|
results := make([]model.Secrets, 0)
|
||||||
for coordinates, secrets := range data {
|
for coordinates, secrets := range data {
|
||||||
results = append(results, model.Secrets{
|
results = append(results, model.Secrets{
|
||||||
@ -94,7 +94,7 @@ func toFile(s sbom.SBOM) []model.File {
|
|||||||
artifacts := s.Artifacts
|
artifacts := s.Artifacts
|
||||||
|
|
||||||
for _, coordinates := range sbom.AllCoordinates(s) {
|
for _, coordinates := range sbom.AllCoordinates(s) {
|
||||||
var metadata *source.FileMetadata
|
var metadata *file.Metadata
|
||||||
if metadataForLocation, exists := artifacts.FileMetadata[coordinates]; exists {
|
if metadataForLocation, exists := artifacts.FileMetadata[coordinates]; exists {
|
||||||
metadata = &metadataForLocation
|
metadata = &metadataForLocation
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ func toFile(s sbom.SBOM) []model.File {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func toFileMetadataEntry(coordinates source.Coordinates, metadata *source.FileMetadata) *model.FileMetadataEntry {
|
func toFileMetadataEntry(coordinates file.Coordinates, metadata *file.Metadata) *model.FileMetadataEntry {
|
||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ func toPackageModel(p pkg.Package) model.Package {
|
|||||||
licenses = p.Licenses
|
licenses = p.Licenses
|
||||||
}
|
}
|
||||||
|
|
||||||
var coordinates = make([]source.Coordinates, len(p.Locations))
|
var coordinates = make([]file.Coordinates, len(p.Locations))
|
||||||
for i, l := range p.Locations {
|
for i, l := range p.Locations {
|
||||||
coordinates[i] = l.Coordinates
|
coordinates[i] = l.Coordinates
|
||||||
}
|
}
|
||||||
@ -216,7 +216,7 @@ func toRelationshipModel(relationships []artifact.Relationship) []model.Relation
|
|||||||
// toSourceModel creates a new source object to be represented into JSON.
|
// toSourceModel creates a new source object to be represented into JSON.
|
||||||
func toSourceModel(src source.Metadata) (model.Source, error) {
|
func toSourceModel(src source.Metadata) (model.Source, error) {
|
||||||
switch src.Scheme {
|
switch src.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageType:
|
||||||
metadata := src.ImageMetadata
|
metadata := src.ImageMetadata
|
||||||
// ensure that empty collections are not shown as null
|
// ensure that empty collections are not shown as null
|
||||||
if metadata.RepoDigests == nil {
|
if metadata.RepoDigests == nil {
|
||||||
@ -229,12 +229,12 @@ func toSourceModel(src source.Metadata) (model.Source, error) {
|
|||||||
Type: "image",
|
Type: "image",
|
||||||
Target: metadata,
|
Target: metadata,
|
||||||
}, nil
|
}, nil
|
||||||
case source.DirectoryScheme:
|
case source.DirectoryType:
|
||||||
return model.Source{
|
return model.Source{
|
||||||
Type: "directory",
|
Type: "directory",
|
||||||
Target: src.Path,
|
Target: src.Path,
|
||||||
}, nil
|
}, nil
|
||||||
case source.FileScheme:
|
case source.FileType:
|
||||||
return model.Source{
|
return model.Source{
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Target: src.Path,
|
Target: src.Path,
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
func Test_toSourceModel(t *testing.T) {
|
func Test_toSourceModel(t *testing.T) {
|
||||||
allSchemes := strset.New()
|
allSchemes := strset.New()
|
||||||
for _, s := range source.AllSchemes {
|
for _, s := range source.AllTypes {
|
||||||
allSchemes.Add(string(s))
|
allSchemes.Add(string(s))
|
||||||
}
|
}
|
||||||
testedSchemes := strset.New()
|
testedSchemes := strset.New()
|
||||||
@ -26,7 +26,7 @@ func Test_toSourceModel(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "directory",
|
name: "directory",
|
||||||
src: source.Metadata{
|
src: source.Metadata{
|
||||||
Scheme: source.DirectoryScheme,
|
Scheme: source.DirectoryType,
|
||||||
Path: "some/path",
|
Path: "some/path",
|
||||||
},
|
},
|
||||||
expected: model.Source{
|
expected: model.Source{
|
||||||
@ -37,7 +37,7 @@ func Test_toSourceModel(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "file",
|
name: "file",
|
||||||
src: source.Metadata{
|
src: source.Metadata{
|
||||||
Scheme: source.FileScheme,
|
Scheme: source.FileType,
|
||||||
Path: "some/path",
|
Path: "some/path",
|
||||||
},
|
},
|
||||||
expected: model.Source{
|
expected: model.Source{
|
||||||
@ -48,7 +48,7 @@ func Test_toSourceModel(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "image",
|
name: "image",
|
||||||
src: source.Metadata{
|
src: source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: "user-input",
|
UserInput: "user-input",
|
||||||
ID: "id...",
|
ID: "id...",
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/anchore/syft/internal/formats/syftjson/model"
|
"github.com/anchore/syft/internal/formats/syftjson/model"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/linux"
|
"github.com/anchore/syft/syft/linux"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
@ -111,17 +112,17 @@ func toSyftSourceData(s model.Source) *source.Metadata {
|
|||||||
switch s.Type {
|
switch s.Type {
|
||||||
case "directory":
|
case "directory":
|
||||||
return &source.Metadata{
|
return &source.Metadata{
|
||||||
Scheme: source.DirectoryScheme,
|
Scheme: source.DirectoryType,
|
||||||
Path: s.Target.(string),
|
Path: s.Target.(string),
|
||||||
}
|
}
|
||||||
case "file":
|
case "file":
|
||||||
return &source.Metadata{
|
return &source.Metadata{
|
||||||
Scheme: source.FileScheme,
|
Scheme: source.FileType,
|
||||||
Path: s.Target.(string),
|
Path: s.Target.(string),
|
||||||
}
|
}
|
||||||
case "image":
|
case "image":
|
||||||
return &source.Metadata{
|
return &source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: s.Target.(source.ImageMetadata),
|
ImageMetadata: s.Target.(source.ImageMetadata),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,9 +149,9 @@ func toSyftPackage(p model.Package) pkg.Package {
|
|||||||
cpes = append(cpes, value)
|
cpes = append(cpes, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var locations = make([]source.Location, len(p.Locations))
|
var locations = make([]file.Location, len(p.Locations))
|
||||||
for i, c := range p.Locations {
|
for i, c := range p.Locations {
|
||||||
locations[i] = source.NewLocationFromCoordinates(c)
|
locations[i] = file.NewLocationFromCoordinates(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkg.Package{
|
return pkg.Package{
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
func Test_toSyftSourceData(t *testing.T) {
|
func Test_toSyftSourceData(t *testing.T) {
|
||||||
allSchemes := strset.New()
|
allSchemes := strset.New()
|
||||||
for _, s := range source.AllSchemes {
|
for _, s := range source.AllTypes {
|
||||||
allSchemes.Add(string(s))
|
allSchemes.Add(string(s))
|
||||||
}
|
}
|
||||||
testedSchemes := strset.New()
|
testedSchemes := strset.New()
|
||||||
@ -24,7 +24,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "directory",
|
name: "directory",
|
||||||
expected: source.Metadata{
|
expected: source.Metadata{
|
||||||
Scheme: source.DirectoryScheme,
|
Scheme: source.DirectoryType,
|
||||||
Path: "some/path",
|
Path: "some/path",
|
||||||
},
|
},
|
||||||
src: model.Source{
|
src: model.Source{
|
||||||
@ -35,7 +35,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "file",
|
name: "file",
|
||||||
expected: source.Metadata{
|
expected: source.Metadata{
|
||||||
Scheme: source.FileScheme,
|
Scheme: source.FileType,
|
||||||
Path: "some/path",
|
Path: "some/path",
|
||||||
},
|
},
|
||||||
src: model.Source{
|
src: model.Source{
|
||||||
@ -46,7 +46,7 @@ func Test_toSyftSourceData(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "image",
|
name: "image",
|
||||||
expected: source.Metadata{
|
expected: source.Metadata{
|
||||||
Scheme: source.ImageScheme,
|
Scheme: source.ImageType,
|
||||||
ImageMetadata: source.ImageMetadata{
|
ImageMetadata: source.ImageMetadata{
|
||||||
UserInput: "user-input",
|
UserInput: "user-input",
|
||||||
ID: "id...",
|
ID: "id...",
|
||||||
|
|||||||
@ -16,9 +16,9 @@ func encoder(output io.Writer, s sbom.SBOM) error {
|
|||||||
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight)
|
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight)
|
||||||
|
|
||||||
switch s.Source.Scheme {
|
switch s.Source.Scheme {
|
||||||
case source.DirectoryScheme, source.FileScheme:
|
case source.DirectoryType, source.FileType:
|
||||||
fmt.Fprintf(w, "[Path: %s]\n", s.Source.Path)
|
fmt.Fprintf(w, "[Path: %s]\n", s.Source.Path)
|
||||||
case source.ImageScheme:
|
case source.ImageType:
|
||||||
fmt.Fprintln(w, "[Image]")
|
fmt.Fprintln(w, "[Image]")
|
||||||
|
|
||||||
for idx, l := range s.Source.ImageMetadata.Layers {
|
for idx, l := range s.Source.ImageMetadata.Layers {
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Guess() string {
|
func Guess() string {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package syft
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
|
|||||||
@ -2,12 +2,13 @@ package syft
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file/cataloger/fileclassifier"
|
||||||
"github.com/anchore/syft/syft/file/cataloger/filecontents"
|
"github.com/anchore/syft/syft/file/cataloger/filecontents"
|
||||||
"github.com/anchore/syft/syft/file/cataloger/secrets"
|
"github.com/anchore/syft/syft/file/cataloger/secrets"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/internal/version"
|
"github.com/anchore/syft/internal/version"
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
@ -31,7 +32,7 @@ type CatalogingConfig struct {
|
|||||||
SecretsScope source.Scope
|
SecretsScope source.Scope
|
||||||
// file classification
|
// file classification
|
||||||
ClassifyFiles bool
|
ClassifyFiles bool
|
||||||
FileClassifiers []file.Classifier
|
FileClassifiers []fileclassifier.Classifier
|
||||||
// file contents
|
// file contents
|
||||||
ContentsConfig filecontents.CatalogerConfig
|
ContentsConfig filecontents.CatalogerConfig
|
||||||
}
|
}
|
||||||
@ -43,7 +44,7 @@ func DefaultCatalogingConfig() CatalogingConfig {
|
|||||||
ToolVersion: version.Guess(),
|
ToolVersion: version.Guess(),
|
||||||
SecretsScope: source.AllLayersScope,
|
SecretsScope: source.AllLayersScope,
|
||||||
SecretsConfig: secrets.DefaultCatalogerConfig(),
|
SecretsConfig: secrets.DefaultCatalogerConfig(),
|
||||||
FileClassifiers: file.DefaultClassifiers(),
|
FileClassifiers: fileclassifier.DefaultClassifiers(),
|
||||||
ContentsConfig: filecontents.DefaultCatalogerConfig(),
|
ContentsConfig: filecontents.DefaultCatalogerConfig(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@ package syft
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
|
"github.com/anchore/syft/syft/file/cataloger/fileclassifier"
|
||||||
"github.com/anchore/syft/syft/file/cataloger/secrets"
|
"github.com/anchore/syft/syft/file/cataloger/secrets"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/packages"
|
"github.com/anchore/syft/syft/pkg/cataloger/packages"
|
||||||
@ -100,7 +101,7 @@ func WithFileClassification() CatalogingOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithFileClassifiers(classifiers ...file.Classifier) CatalogingOption {
|
func WithFileClassifiers(classifiers ...fileclassifier.Classifier) CatalogingOption {
|
||||||
return func(_ *source.Source, config *CatalogingConfig) error {
|
return func(_ *source.Source, config *CatalogingConfig) error {
|
||||||
config.ClassifyFiles = !(len(classifiers) > 0)
|
config.ClassifyFiles = !(len(classifiers) > 0)
|
||||||
config.FileClassifiers = classifiers
|
config.FileClassifiers = classifiers
|
||||||
|
|||||||
@ -5,6 +5,7 @@ package parsers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/event/monitor"
|
"github.com/anchore/syft/syft/event/monitor"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
|
|||||||
@ -7,17 +7,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Cataloger struct {
|
type Cataloger struct {
|
||||||
classifiers []file.Classifier
|
classifiers []Classifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCataloger(classifiers []file.Classifier) (*Cataloger, error) {
|
func NewCataloger(classifiers []Classifier) (*Cataloger, error) {
|
||||||
return &Cataloger{
|
return &Cataloger{
|
||||||
classifiers: classifiers,
|
classifiers: classifiers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates][]file.Classification, error) {
|
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[file.Coordinates][]file.Classification, error) {
|
||||||
results := make(map[source.Coordinates][]file.Classification)
|
results := make(map[file.Coordinates][]file.Classification)
|
||||||
|
|
||||||
numResults := 0
|
numResults := 0
|
||||||
for _, location := range source.AllRegularFiles(resolver) {
|
for _, location := range source.AllRegularFiles(resolver) {
|
||||||
|
|||||||
@ -106,7 +106,7 @@ func TestClassifierCataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
|
||||||
c, err := NewCataloger(file.DefaultClassifiers())
|
c, err := NewCataloger(DefaultClassifiers())
|
||||||
test.expectedErr(t, err)
|
test.expectedErr(t, err)
|
||||||
|
|
||||||
src, err := source.NewFromDirectory(test.fixtureDir)
|
src, err := source.NewFromDirectory(test.fixtureDir)
|
||||||
@ -161,7 +161,7 @@ func TestClassifierCataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
|
||||||
c, err := NewCataloger(file.DefaultClassifiers())
|
c, err := NewCataloger(DefaultClassifiers())
|
||||||
test.expectedErr(t, err)
|
test.expectedErr(t, err)
|
||||||
|
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage)
|
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage)
|
||||||
@ -192,7 +192,7 @@ func TestClassifierCataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T
|
|||||||
|
|
||||||
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
||||||
|
|
||||||
c, err := NewCataloger(file.DefaultClassifiers())
|
c, err := NewCataloger(DefaultClassifiers())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
src, err := source.NewFromDirectory("test-fixtures/classifiers/negative")
|
src, err := source.NewFromDirectory("test-fixtures/classifiers/negative")
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package file
|
package fileclassifier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -7,6 +7,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
@ -68,7 +70,7 @@ func DefaultClassifiers() []Classifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Classifier) Classify(resolver source.FileResolver, location source.Location) (*Classification, error) {
|
func (c Classifier) Classify(resolver source.FileResolver, location file.Location) (*file.Classification, error) {
|
||||||
doesFilepathMatch, filepathNamedGroupValues := filepathMatches(c.FilepathPatterns, location)
|
doesFilepathMatch, filepathNamedGroupValues := filepathMatches(c.FilepathPatterns, location)
|
||||||
if !doesFilepathMatch {
|
if !doesFilepathMatch {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -86,7 +88,7 @@ func (c Classifier) Classify(resolver source.FileResolver, location source.Locat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var result *Classification
|
var result *file.Classification
|
||||||
for _, patternTemplate := range c.EvidencePatternTemplates {
|
for _, patternTemplate := range c.EvidencePatternTemplates {
|
||||||
tmpl, err := template.New("").Parse(patternTemplate)
|
tmpl, err := template.New("").Parse(patternTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -110,7 +112,7 @@ func (c Classifier) Classify(resolver source.FileResolver, location source.Locat
|
|||||||
|
|
||||||
matchMetadata := internal.MatchNamedCaptureGroups(pattern, string(contents))
|
matchMetadata := internal.MatchNamedCaptureGroups(pattern, string(contents))
|
||||||
if result == nil {
|
if result == nil {
|
||||||
result = &Classification{
|
result = &file.Classification{
|
||||||
Class: c.Class,
|
Class: c.Class,
|
||||||
Metadata: matchMetadata,
|
Metadata: matchMetadata,
|
||||||
}
|
}
|
||||||
@ -123,7 +125,7 @@ func (c Classifier) Classify(resolver source.FileResolver, location source.Locat
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func filepathMatches(patterns []*regexp.Regexp, location source.Location) (bool, map[string]string) {
|
func filepathMatches(patterns []*regexp.Regexp, location file.Location) (bool, map[string]string) {
|
||||||
for _, path := range []string{location.RealPath, location.VirtualPath} {
|
for _, path := range []string{location.RealPath, location.VirtualPath} {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
continue
|
continue
|
||||||
@ -1,25 +1,25 @@
|
|||||||
package file
|
package fileclassifier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFilepathMatches(t *testing.T) {
|
func TestFilepathMatches(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
location source.Location
|
location file.Location
|
||||||
patterns []string
|
patterns []string
|
||||||
expectedMatches bool
|
expectedMatches bool
|
||||||
expectedNamedGroups map[string]string
|
expectedNamedGroups map[string]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "simple-filename-match",
|
name: "simple-filename-match",
|
||||||
location: source.Location{
|
location: file.Location{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "python2.7",
|
RealPath: "python2.7",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -30,8 +30,8 @@ func TestFilepathMatches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "filepath-match",
|
name: "filepath-match",
|
||||||
location: source.Location{
|
location: file.Location{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/usr/bin/python2.7",
|
RealPath: "/usr/bin/python2.7",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -42,7 +42,7 @@ func TestFilepathMatches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "virtual-filepath-match",
|
name: "virtual-filepath-match",
|
||||||
location: source.Location{
|
location: file.Location{
|
||||||
VirtualPath: "/usr/bin/python2.7",
|
VirtualPath: "/usr/bin/python2.7",
|
||||||
},
|
},
|
||||||
patterns: []string{
|
patterns: []string{
|
||||||
@ -52,7 +52,7 @@ func TestFilepathMatches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "full-filepath-match",
|
name: "full-filepath-match",
|
||||||
location: source.Location{
|
location: file.Location{
|
||||||
VirtualPath: "/usr/bin/python2.7",
|
VirtualPath: "/usr/bin/python2.7",
|
||||||
},
|
},
|
||||||
patterns: []string{
|
patterns: []string{
|
||||||
@ -62,8 +62,8 @@ func TestFilepathMatches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "anchored-filename-match-FAILS",
|
name: "anchored-filename-match-FAILS",
|
||||||
location: source.Location{
|
location: file.Location{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/usr/bin/python2.7",
|
RealPath: "/usr/bin/python2.7",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -74,7 +74,7 @@ func TestFilepathMatches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty-filename-match-FAILS",
|
name: "empty-filename-match-FAILS",
|
||||||
location: source.Location{},
|
location: file.Location{},
|
||||||
patterns: []string{
|
patterns: []string{
|
||||||
`^python([0-9]+\.[0-9]+)$`,
|
`^python([0-9]+\.[0-9]+)$`,
|
||||||
},
|
},
|
||||||
@ -34,9 +34,9 @@ func NewCataloger(config CatalogerConfig) (*Cataloger, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates]string, error) {
|
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[file.Coordinates]string, error) {
|
||||||
results := make(map[source.Coordinates]string)
|
results := make(map[file.Coordinates]string)
|
||||||
var locations []source.Location
|
var locations []file.Location
|
||||||
|
|
||||||
locations, err := resolver.FilesByGlob(i.config.Globs...)
|
locations, err := resolver.FilesByGlob(i.config.Globs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -67,7 +67,7 @@ func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinate
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) catalogLocation(resolver source.FileResolver, location source.Location) (string, error) {
|
func (i *Cataloger) catalogLocation(resolver source.FileResolver, location file.Location) (string, error) {
|
||||||
contentReader, err := resolver.FileContentsByLocation(location)
|
contentReader, err := resolver.FileContentsByLocation(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package filecontents
|
package filecontents
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
@ -15,41 +16,41 @@ func TestContentsCataloger(t *testing.T) {
|
|||||||
globs []string
|
globs []string
|
||||||
maxSize int64
|
maxSize int64
|
||||||
files []string
|
files []string
|
||||||
expected map[source.Coordinates]string
|
expected map[file.Coordinates]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "multi-pattern",
|
name: "multi-pattern",
|
||||||
globs: []string{"test-fixtures/last/*.txt", "test-fixtures/*.txt"},
|
globs: []string{"test-fixtures/last/*.txt", "test-fixtures/*.txt"},
|
||||||
files: allFiles,
|
files: allFiles,
|
||||||
expected: map[source.Coordinates]string{
|
expected: map[file.Coordinates]string{
|
||||||
source.NewLocation("test-fixtures/last/path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9sYXN0L3BhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/last/path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9sYXN0L3BhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
source.NewLocation("test-fixtures/another-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hbm90aGVyLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/another-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hbm90aGVyLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
source.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-patterns",
|
name: "no-patterns",
|
||||||
globs: []string{},
|
globs: []string{},
|
||||||
files: []string{"test-fixtures/last/path.txt", "test-fixtures/another-path.txt", "test-fixtures/a-path.txt"},
|
files: []string{"test-fixtures/last/path.txt", "test-fixtures/another-path.txt", "test-fixtures/a-path.txt"},
|
||||||
expected: map[source.Coordinates]string{},
|
expected: map[file.Coordinates]string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all-txt",
|
name: "all-txt",
|
||||||
globs: []string{"**/*.txt"},
|
globs: []string{"**/*.txt"},
|
||||||
files: allFiles,
|
files: allFiles,
|
||||||
expected: map[source.Coordinates]string{
|
expected: map[file.Coordinates]string{
|
||||||
source.NewLocation("test-fixtures/last/path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9sYXN0L3BhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/last/path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9sYXN0L3BhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
source.NewLocation("test-fixtures/another-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hbm90aGVyLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/another-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hbm90aGVyLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
source.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subpath",
|
name: "subpath",
|
||||||
globs: []string{"test-fixtures/*.txt"},
|
globs: []string{"test-fixtures/*.txt"},
|
||||||
files: allFiles,
|
files: allFiles,
|
||||||
expected: map[source.Coordinates]string{
|
expected: map[file.Coordinates]string{
|
||||||
source.NewLocation("test-fixtures/another-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hbm90aGVyLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/another-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hbm90aGVyLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
source.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -57,9 +58,9 @@ func TestContentsCataloger(t *testing.T) {
|
|||||||
maxSize: 42,
|
maxSize: 42,
|
||||||
globs: []string{"**/*.txt"},
|
globs: []string{"**/*.txt"},
|
||||||
files: allFiles,
|
files: allFiles,
|
||||||
expected: map[source.Coordinates]string{
|
expected: map[file.Coordinates]string{
|
||||||
source.NewLocation("test-fixtures/last/path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9sYXN0L3BhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/last/path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9sYXN0L3BhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
source.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
file.NewLocation("test-fixtures/a-path.txt").Coordinates: "dGVzdC1maXh0dXJlcy9hLXBhdGgudHh0IGZpbGUgY29udGVudHMh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,8 +29,8 @@ func NewCataloger(hashes []crypto.Hash) (*Cataloger, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates][]file.Digest, error) {
|
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[file.Coordinates][]file.Digest, error) {
|
||||||
results := make(map[source.Coordinates][]file.Digest)
|
results := make(map[file.Coordinates][]file.Digest)
|
||||||
locations := source.AllRegularFiles(resolver)
|
locations := source.AllRegularFiles(resolver)
|
||||||
stage, prog := digestsCatalogingProgress(int64(len(locations)))
|
stage, prog := digestsCatalogingProgress(int64(len(locations)))
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
@ -57,14 +57,14 @@ func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinate
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) catalogLocation(resolver source.FileResolver, location source.Location) ([]file.Digest, error) {
|
func (i *Cataloger) catalogLocation(resolver source.FileResolver, location file.Location) ([]file.Digest, error) {
|
||||||
meta, err := resolver.FileMetadataByLocation(location)
|
meta, err := resolver.FileMetadataByLocation(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should only attempt to report digests for files that are regular files (don't attempt to resolve links)
|
// we should only attempt to report digests for files that are regular files (don't attempt to resolve links)
|
||||||
if meta.Type != source.RegularFile {
|
if meta.Type != file.RegularFile {
|
||||||
return nil, errUndigestableFile
|
return nil, errUndigestableFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,8 +16,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testDigests(t testing.TB, root string, files []string, hashes ...crypto.Hash) map[source.Coordinates][]file.Digest {
|
func testDigests(t testing.TB, root string, files []string, hashes ...crypto.Hash) map[file.Coordinates][]file.Digest {
|
||||||
digests := make(map[source.Coordinates][]file.Digest)
|
digests := make(map[file.Coordinates][]file.Digest)
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
fh, err := os.Open(filepath.Join(root, f))
|
fh, err := os.Open(filepath.Join(root, f))
|
||||||
@ -31,14 +31,14 @@ func testDigests(t testing.TB, root string, files []string, hashes ...crypto.Has
|
|||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
// we don't keep digests for empty files
|
// we don't keep digests for empty files
|
||||||
digests[source.NewLocation(f).Coordinates] = []file.Digest{}
|
digests[file.NewLocation(f).Coordinates] = []file.Digest{}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hash := range hashes {
|
for _, hash := range hashes {
|
||||||
h := hash.New()
|
h := hash.New()
|
||||||
h.Write(b)
|
h.Write(b)
|
||||||
digests[source.NewLocation(f).Coordinates] = append(digests[source.NewLocation(f).Coordinates], file.Digest{
|
digests[file.NewLocation(f).Coordinates] = append(digests[file.NewLocation(f).Coordinates], file.Digest{
|
||||||
Algorithm: file.CleanDigestAlgorithmName(hash.String()),
|
Algorithm: file.CleanDigestAlgorithmName(hash.String()),
|
||||||
Value: fmt.Sprintf("%x", h.Sum(nil)),
|
Value: fmt.Sprintf("%x", h.Sum(nil)),
|
||||||
})
|
})
|
||||||
@ -54,7 +54,7 @@ func TestDigestsCataloger(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
digests []crypto.Hash
|
digests []crypto.Hash
|
||||||
files []string
|
files []string
|
||||||
expected map[source.Coordinates][]file.Digest
|
expected map[file.Coordinates][]file.Digest
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "md5",
|
name: "md5",
|
||||||
@ -145,7 +145,7 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get file=%q : %+v", test.path, err)
|
t.Fatalf("unable to get file=%q : %+v", test.path, err)
|
||||||
}
|
}
|
||||||
l := source.NewLocationFromImage(test.path, *ref, img)
|
l := file.NewLocationFromImage(test.path, *ref, img)
|
||||||
|
|
||||||
if len(actual[l.Coordinates]) == 0 {
|
if len(actual[l.Coordinates]) == 0 {
|
||||||
if test.expected != "" {
|
if test.expected != "" {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"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/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
"github.com/wagoodman/go-progress"
|
"github.com/wagoodman/go-progress"
|
||||||
@ -16,9 +17,9 @@ func NewCataloger() *Cataloger {
|
|||||||
return &Cataloger{}
|
return &Cataloger{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates]source.FileMetadata, error) {
|
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[file.Coordinates]file.Metadata, error) {
|
||||||
results := make(map[source.Coordinates]source.FileMetadata)
|
results := make(map[file.Coordinates]file.Metadata)
|
||||||
var locations []source.Location
|
var locations []file.Location
|
||||||
for location := range resolver.AllLocations() {
|
for location := range resolver.AllLocations() {
|
||||||
locations = append(locations, location)
|
locations = append(locations, location)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -33,13 +34,13 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
path string
|
path string
|
||||||
exists bool
|
exists bool
|
||||||
expected source.FileMetadata
|
expected file.Metadata
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
path: "/file-1.txt",
|
path: "/file-1.txt",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0644,
|
Mode: 0644,
|
||||||
Type: "RegularFile",
|
Type: "RegularFile",
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
@ -51,7 +52,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
path: "/hardlink-1",
|
path: "/hardlink-1",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0644,
|
Mode: 0644,
|
||||||
Type: "HardLink",
|
Type: "HardLink",
|
||||||
LinkDestination: "file-1.txt",
|
LinkDestination: "file-1.txt",
|
||||||
@ -63,7 +64,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
path: "/symlink-1",
|
path: "/symlink-1",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0777 | os.ModeSymlink,
|
Mode: 0777 | os.ModeSymlink,
|
||||||
Type: "SymbolicLink",
|
Type: "SymbolicLink",
|
||||||
LinkDestination: "file-1.txt",
|
LinkDestination: "file-1.txt",
|
||||||
@ -75,7 +76,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
path: "/char-device-1",
|
path: "/char-device-1",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0644 | os.ModeDevice | os.ModeCharDevice,
|
Mode: 0644 | os.ModeDevice | os.ModeCharDevice,
|
||||||
Type: "CharacterDevice",
|
Type: "CharacterDevice",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
@ -86,7 +87,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
path: "/block-device-1",
|
path: "/block-device-1",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0644 | os.ModeDevice,
|
Mode: 0644 | os.ModeDevice,
|
||||||
Type: "BlockDevice",
|
Type: "BlockDevice",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
@ -97,7 +98,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
path: "/fifo-1",
|
path: "/fifo-1",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0644 | os.ModeNamedPipe,
|
Mode: 0644 | os.ModeNamedPipe,
|
||||||
Type: "FIFONode",
|
Type: "FIFONode",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
@ -108,7 +109,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
path: "/bin",
|
path: "/bin",
|
||||||
exists: true,
|
exists: true,
|
||||||
expected: source.FileMetadata{
|
expected: file.Metadata{
|
||||||
Mode: 0755 | os.ModeDir,
|
Mode: 0755 | os.ModeDir,
|
||||||
Type: "Directory",
|
Type: "Directory",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
@ -120,12 +121,12 @@ func TestFileMetadataCataloger(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.path, func(t *testing.T) {
|
t.Run(test.path, func(t *testing.T) {
|
||||||
_, ref, err := img.SquashedTree().File(file.Path(test.path))
|
_, ref, err := img.SquashedTree().File(stereoscopeFile.Path(test.path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get file: %+v", err)
|
t.Fatalf("unable to get file: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l := source.NewLocationFromImage(test.path, *ref, img)
|
l := file.NewLocationFromImage(test.path, *ref, img)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual[l.Coordinates], "mismatched metadata")
|
assert.Equal(t, test.expected, actual[l.Coordinates], "mismatched metadata")
|
||||||
|
|
||||||
|
|||||||
@ -56,8 +56,8 @@ func NewCataloger(config CatalogerConfig) (*Cataloger, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates][]file.SearchResult, error) {
|
func (i *Cataloger) Catalog(resolver source.FileResolver) (map[file.Coordinates][]file.SearchResult, error) {
|
||||||
results := make(map[source.Coordinates][]file.SearchResult)
|
results := make(map[file.Coordinates][]file.SearchResult)
|
||||||
locations := source.AllRegularFiles(resolver)
|
locations := source.AllRegularFiles(resolver)
|
||||||
stage, prog, secretsDiscovered := newSecretsCatalogerMonitor(int64(len(locations)))
|
stage, prog, secretsDiscovered := newSecretsCatalogerMonitor(int64(len(locations)))
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
@ -82,7 +82,7 @@ func (i *Cataloger) Catalog(resolver source.FileResolver) (map[source.Coordinate
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Cataloger) catalogLocation(resolver source.FileResolver, location source.Location) ([]file.SearchResult, error) {
|
func (i *Cataloger) catalogLocation(resolver source.FileResolver, location file.Location) ([]file.SearchResult, error) {
|
||||||
metadata, err := resolver.FileMetadataByLocation(location)
|
metadata, err := resolver.FileMetadataByLocation(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -120,7 +120,7 @@ func (i *Cataloger) catalogLocation(resolver source.FileResolver, location sourc
|
|||||||
return secrets, nil
|
return secrets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractValue(resolver source.FileResolver, location source.Location, start, length int64) (string, error) {
|
func extractValue(resolver source.FileResolver, location file.Location, start, length int64) (string, error) {
|
||||||
readCloser, err := resolver.FileContentsByLocation(location)
|
readCloser, err := resolver.FileContentsByLocation(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to fetch reader for location=%q : %w", location, err)
|
return "", fmt.Errorf("unable to fetch reader for location=%q : %w", location, err)
|
||||||
|
|||||||
@ -199,7 +199,7 @@ func TestSecretsCataloger(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loc := source.NewLocation(test.fixture)
|
loc := file.NewLocation(test.fixture)
|
||||||
if _, exists := actualResults[loc.Coordinates]; !exists {
|
if _, exists := actualResults[loc.Coordinates]; !exists {
|
||||||
t.Fatalf("could not find location=%q in results", loc)
|
t.Fatalf("could not find location=%q in results", loc)
|
||||||
}
|
}
|
||||||
@ -437,7 +437,7 @@ j4f668YfhUbKdRF6S6734856
|
|||||||
t.Fatalf("could not catalog: %+v", err)
|
t.Fatalf("could not catalog: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
loc := source.NewLocation(test.fixture)
|
loc := file.NewLocation(test.fixture)
|
||||||
if _, exists := actualResults[loc.Coordinates]; !exists && test.expected != nil {
|
if _, exists := actualResults[loc.Coordinates]; !exists && test.expected != nil {
|
||||||
t.Fatalf("could not find location=%q in results", loc)
|
t.Fatalf("could not find location=%q in results", loc)
|
||||||
} else if !exists && test.expected == nil {
|
} else if !exists && test.expected == nil {
|
||||||
|
|||||||
@ -4,17 +4,18 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func catalogLocationByLine(resolver source.FileResolver, location source.Location, patterns map[string]*regexp.Regexp) ([]file.SearchResult, error) {
|
func catalogLocationByLine(resolver source.FileResolver, location file.Location, patterns map[string]*regexp.Regexp) ([]file.SearchResult, error) {
|
||||||
readCloser, err := resolver.FileContentsByLocation(location)
|
readCloser, err := resolver.FileContentsByLocation(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to fetch reader for location=%q : %w", location, err)
|
return nil, fmt.Errorf("unable to fetch reader for location=%q : %w", location, err)
|
||||||
@ -46,7 +47,7 @@ func catalogLocationByLine(resolver source.FileResolver, location source.Locatio
|
|||||||
return allSecrets, nil
|
return allSecrets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchForSecretsWithinLine(resolver source.FileResolver, location source.Location, patterns map[string]*regexp.Regexp, line []byte, lineNo int64, position int64) ([]file.SearchResult, error) {
|
func searchForSecretsWithinLine(resolver source.FileResolver, location file.Location, patterns map[string]*regexp.Regexp, line []byte, lineNo int64, position int64) ([]file.SearchResult, error) {
|
||||||
var secrets []file.SearchResult
|
var secrets []file.SearchResult
|
||||||
for name, pattern := range patterns {
|
for name, pattern := range patterns {
|
||||||
matches := pattern.FindAllIndex(line, -1)
|
matches := pattern.FindAllIndex(line, -1)
|
||||||
@ -75,7 +76,7 @@ func searchForSecretsWithinLine(resolver source.FileResolver, location source.Lo
|
|||||||
return secrets, nil
|
return secrets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readerAtPosition(resolver source.FileResolver, location source.Location, seekPosition int64) (io.ReadCloser, error) {
|
func readerAtPosition(resolver source.FileResolver, location file.Location, seekPosition int64) (io.ReadCloser, error) {
|
||||||
readCloser, err := resolver.FileContentsByLocation(location)
|
readCloser, err := resolver.FileContentsByLocation(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to fetch reader for location=%q : %w", location, err)
|
return nil, fmt.Errorf("unable to fetch reader for location=%q : %w", location, err)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,7 +1,7 @@
|
|||||||
//go:build linux || darwin
|
//go:build linux || darwin
|
||||||
// +build linux darwin
|
// +build linux darwin
|
||||||
|
|
||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@ -1,7 +1,7 @@
|
|||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -109,3 +109,7 @@ func (l Location) String() string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("Location<%s>", str)
|
return fmt.Sprintf("Location<%s>", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l Location) Ref() file.Reference {
|
||||||
|
return l.ref
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,18 +1,16 @@
|
|||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileMetadata struct {
|
type Metadata struct {
|
||||||
Mode os.FileMode
|
Mode os.FileMode
|
||||||
Type FileType
|
Type Type
|
||||||
UserID int
|
UserID int
|
||||||
GroupID int
|
GroupID int
|
||||||
LinkDestination string
|
LinkDestination string
|
||||||
@ -20,15 +18,15 @@ type FileMetadata struct {
|
|||||||
MIMEType string
|
MIMEType string
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileMetadataByLocation(img *image.Image, location Location) (FileMetadata, error) {
|
func MetadataByLocation(img *image.Image, location Location) (Metadata, error) {
|
||||||
entry, err := img.FileCatalog.Get(location.ref)
|
entry, err := img.FileCatalog.Get(location.ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FileMetadata{}, err
|
return Metadata{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileMetadata{
|
return Metadata{
|
||||||
Mode: entry.Metadata.Mode,
|
Mode: entry.Metadata.Mode,
|
||||||
Type: newFileTypeFromTarHeaderTypeFlag(entry.Metadata.TypeFlag),
|
Type: NewFileTypeFromTarHeaderTypeFlag(entry.Metadata.TypeFlag),
|
||||||
UserID: entry.Metadata.UserID,
|
UserID: entry.Metadata.UserID,
|
||||||
GroupID: entry.Metadata.GroupID,
|
GroupID: entry.Metadata.GroupID,
|
||||||
LinkDestination: entry.Metadata.Linkname,
|
LinkDestination: entry.Metadata.Linkname,
|
||||||
@ -37,7 +35,7 @@ func fileMetadataByLocation(img *image.Image, location Location) (FileMetadata,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileMetadataFromPath(path string, info os.FileInfo, withMIMEType bool) FileMetadata {
|
func MetadataFromPath(path string, info os.FileInfo, withMIMEType bool) Metadata {
|
||||||
var mimeType string
|
var mimeType string
|
||||||
uid, gid := GetXid(info)
|
uid, gid := GetXid(info)
|
||||||
|
|
||||||
@ -57,9 +55,9 @@ func fileMetadataFromPath(path string, info os.FileInfo, withMIMEType bool) File
|
|||||||
mimeType = file.MIMEType(f)
|
mimeType = file.MIMEType(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileMetadata{
|
return Metadata{
|
||||||
Mode: info.Mode(),
|
Mode: info.Mode(),
|
||||||
Type: newFileTypeFromMode(info.Mode()),
|
Type: NewFileTypeFromMode(info.Mode()),
|
||||||
// unsupported across platforms
|
// unsupported across platforms
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
GroupID: gid,
|
GroupID: gid,
|
||||||
@ -1,7 +1,7 @@
|
|||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@ -49,7 +49,7 @@ func Test_fileMetadataFromPath(t *testing.T) {
|
|||||||
info, err := os.Lstat(test.path)
|
info, err := os.Lstat(test.path)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
actual := fileMetadataFromPath(test.path, info, test.withMIMEType)
|
actual := MetadataFromPath(test.path, info, test.withMIMEType)
|
||||||
assert.Equal(t, test.expectedMIMEType, actual.MIMEType)
|
assert.Equal(t, test.expectedMIMEType, actual.MIMEType)
|
||||||
assert.Equal(t, test.expectedType, string(actual.Type))
|
assert.Equal(t, test.expectedType, string(actual.Type))
|
||||||
})
|
})
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package source
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
@ -6,23 +6,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RegularFile FileType = "RegularFile"
|
RegularFile Type = "RegularFile"
|
||||||
// IrregularFile is how syft defines files that are neither regular, symbolic or directory.
|
// IrregularFile is how syft defines files that are neither regular, symbolic or directory.
|
||||||
// For ref: the seven standard Unix file types are regular, directory, symbolic link,
|
// For ref: the seven standard Unix file types are regular, directory, symbolic link,
|
||||||
// FIFO special, block special, character special, and socket as defined by POSIX.
|
// FIFO special, block special, character special, and socket as defined by POSIX.
|
||||||
IrregularFile FileType = "IrregularFile"
|
IrregularFile Type = "IrregularFile"
|
||||||
HardLink FileType = "HardLink"
|
HardLink Type = "HardLink"
|
||||||
SymbolicLink FileType = "SymbolicLink"
|
SymbolicLink Type = "SymbolicLink"
|
||||||
CharacterDevice FileType = "CharacterDevice"
|
CharacterDevice Type = "CharacterDevice"
|
||||||
BlockDevice FileType = "BlockDevice"
|
BlockDevice Type = "BlockDevice"
|
||||||
Directory FileType = "Directory"
|
Directory Type = "Directory"
|
||||||
FIFONode FileType = "FIFONode"
|
FIFONode Type = "FIFONode"
|
||||||
Socket FileType = "Socket"
|
Socket Type = "Socket"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileType string
|
type Type string
|
||||||
|
|
||||||
func newFileTypeFromTarHeaderTypeFlag(flag byte) FileType {
|
func NewFileTypeFromTarHeaderTypeFlag(flag byte) Type {
|
||||||
switch flag {
|
switch flag {
|
||||||
case tar.TypeReg, tar.TypeRegA:
|
case tar.TypeReg, tar.TypeRegA:
|
||||||
return RegularFile
|
return RegularFile
|
||||||
@ -42,7 +42,7 @@ func newFileTypeFromTarHeaderTypeFlag(flag byte) FileType {
|
|||||||
return IrregularFile
|
return IrregularFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileTypeFromMode(mode os.FileMode) FileType {
|
func NewFileTypeFromMode(mode os.FileMode) Type {
|
||||||
switch {
|
switch {
|
||||||
case isSet(mode, os.ModeSymlink):
|
case isSet(mode, os.ModeSymlink):
|
||||||
return SymbolicLink
|
return SymbolicLink
|
||||||
@ -1,13 +1,12 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type expectedIndexes struct {
|
type expectedIndexes struct {
|
||||||
@ -19,16 +18,16 @@ func TestCatalogAddPopulatesIndex(t *testing.T) {
|
|||||||
|
|
||||||
var pkgs = []Package{
|
var pkgs = []Package{
|
||||||
{
|
{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/a/path", "/another/path"),
|
file.NewVirtualLocation("/a/path", "/another/path"),
|
||||||
source.NewVirtualLocation("/b/path", "/bee/path"),
|
file.NewVirtualLocation("/b/path", "/bee/path"),
|
||||||
},
|
},
|
||||||
Type: RpmPkg,
|
Type: RpmPkg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/c/path", "/another/path"),
|
file.NewVirtualLocation("/c/path", "/another/path"),
|
||||||
source.NewVirtualLocation("/d/path", "/another/path"),
|
file.NewVirtualLocation("/d/path", "/another/path"),
|
||||||
},
|
},
|
||||||
Type: NpmPkg,
|
Type: NpmPkg,
|
||||||
},
|
},
|
||||||
@ -106,17 +105,17 @@ func assertIndexes(t *testing.T, c *Catalog, expectedIndexes expectedIndexes) {
|
|||||||
|
|
||||||
func TestCatalog_PathIndexDeduplicatesRealVsVirtualPaths(t *testing.T) {
|
func TestCatalog_PathIndexDeduplicatesRealVsVirtualPaths(t *testing.T) {
|
||||||
p1 := Package{
|
p1 := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/b/path", "/another/path"),
|
file.NewVirtualLocation("/b/path", "/another/path"),
|
||||||
source.NewVirtualLocation("/b/path", "/b/path"),
|
file.NewVirtualLocation("/b/path", "/b/path"),
|
||||||
},
|
},
|
||||||
Type: RpmPkg,
|
Type: RpmPkg,
|
||||||
Name: "Package-1",
|
Name: "Package-1",
|
||||||
}
|
}
|
||||||
|
|
||||||
p2 := Package{
|
p2 := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/b/path", "/b/path"),
|
file.NewVirtualLocation("/b/path", "/b/path"),
|
||||||
},
|
},
|
||||||
Type: RpmPkg,
|
Type: RpmPkg,
|
||||||
Name: "Package-2",
|
Name: "Package-2",
|
||||||
|
|||||||
@ -3,12 +3,13 @@ package apkdb
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
@ -60,7 +62,7 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []arti
|
|||||||
for i := range pkgs {
|
for i := range pkgs {
|
||||||
p := &pkgs[i]
|
p := &pkgs[i]
|
||||||
p.FoundBy = c.Name()
|
p.FoundBy = c.Name()
|
||||||
p.Locations = []source.Location{dbLocation}
|
p.Locations = []file.Location{dbLocation}
|
||||||
|
|
||||||
// the current entry only has what may have been listed in the status file, however, there are additional
|
// the current entry only has what may have been listed in the status file, however, there are additional
|
||||||
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
|
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
|
||||||
@ -78,7 +80,7 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []arti
|
|||||||
return allPackages, nil, nil
|
return allPackages, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
func addLicenses(resolver source.FileResolver, dbLocation file.Location, p *pkg.Package) {
|
||||||
// get license information from the copyright file
|
// get license information from the copyright file
|
||||||
copyrightReader, copyrightLocation := fetchCopyrightContents(resolver, dbLocation, p)
|
copyrightReader, copyrightLocation := fetchCopyrightContents(resolver, dbLocation, p)
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
func mergeFileListing(resolver source.FileResolver, dbLocation file.Location, p *pkg.Package) {
|
||||||
metadata := p.Metadata.(pkg.DpkgMetadata)
|
metadata := p.Metadata.(pkg.DpkgMetadata)
|
||||||
|
|
||||||
// get file listing (package files + additional config files)
|
// get file listing (package files + additional config files)
|
||||||
@ -120,10 +122,10 @@ loopNewFiles:
|
|||||||
p.Locations = append(p.Locations, infoLocations...)
|
p.Locations = append(p.Locations, infoLocations...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) ([]pkg.DpkgFileRecord, []source.Location) {
|
func getAdditionalFileListing(resolver source.FileResolver, dbLocation file.Location, p *pkg.Package) ([]pkg.DpkgFileRecord, []file.Location) {
|
||||||
// ensure the default value for a collection is never nil since this may be shown as JSON
|
// ensure the default value for a collection is never nil since this may be shown as JSON
|
||||||
var files = make([]pkg.DpkgFileRecord, 0)
|
var files = make([]pkg.DpkgFileRecord, 0)
|
||||||
var locations []source.Location
|
var locations []file.Location
|
||||||
|
|
||||||
md5Reader, md5Location := fetchMd5Contents(resolver, dbLocation, p)
|
md5Reader, md5Location := fetchMd5Contents(resolver, dbLocation, p)
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Lo
|
|||||||
return files, locations
|
return files, locations
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) {
|
func fetchMd5Contents(resolver source.FileResolver, dbLocation file.Location, p *pkg.Package) (io.ReadCloser, *file.Location) {
|
||||||
var md5Reader io.ReadCloser
|
var md5Reader io.ReadCloser
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -177,7 +179,7 @@ func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location,
|
|||||||
return md5Reader, location
|
return md5Reader, location
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchConffileContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) {
|
func fetchConffileContents(resolver source.FileResolver, dbLocation file.Location, p *pkg.Package) (io.ReadCloser, *file.Location) {
|
||||||
var reader io.ReadCloser
|
var reader io.ReadCloser
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -204,7 +206,7 @@ func fetchConffileContents(resolver source.FileResolver, dbLocation source.Locat
|
|||||||
return reader, location
|
return reader, location
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) {
|
func fetchCopyrightContents(resolver source.FileResolver, dbLocation file.Location, p *pkg.Package) (io.ReadCloser, *file.Location) {
|
||||||
// look for /usr/share/docs/NAME/copyright files
|
// look for /usr/share/docs/NAME/copyright files
|
||||||
name := p.Name
|
name := p.Name
|
||||||
copyrightPath := path.Join(docsPath, name, "copyright")
|
copyrightPath := path.Join(docsPath, name, "copyright")
|
||||||
|
|||||||
@ -6,6 +6,8 @@ package generic
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
@ -70,8 +72,8 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []arti
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SelectFiles takes a set of file trees and resolves and file references of interest for future cataloging
|
// SelectFiles takes a set of file trees and resolves and file references of interest for future cataloging
|
||||||
func (c *Cataloger) selectFiles(resolver source.FilePathResolver) map[source.Location]Parser {
|
func (c *Cataloger) selectFiles(resolver source.FilePathResolver) map[file.Location]Parser {
|
||||||
var parserByLocation = make(map[source.Location]Parser)
|
var parserByLocation = make(map[file.Location]Parser)
|
||||||
|
|
||||||
// select by exact path
|
// select by exact path
|
||||||
for path, parser := range c.pathParsers {
|
for path, parser := range c.pathParsers {
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
package generic
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -17,13 +18,13 @@ const (
|
|||||||
|
|
||||||
type exeOpener func(file io.ReadCloser) ([]exe, error)
|
type exeOpener func(file io.ReadCloser) ([]exe, error)
|
||||||
|
|
||||||
func newGoBinaryPackage(name, version, h1Digest, goVersion, architecture string, location source.Location) pkg.Package {
|
func newGoBinaryPackage(name, version, h1Digest, goVersion, architecture string, location file.Location) pkg.Package {
|
||||||
p := pkg.Package{
|
p := pkg.Package{
|
||||||
Name: name,
|
Name: name,
|
||||||
Version: version,
|
Version: version,
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
location,
|
location,
|
||||||
},
|
},
|
||||||
MetadataType: pkg.GolangBinMetadataType,
|
MetadataType: pkg.GolangBinMetadataType,
|
||||||
@ -39,7 +40,7 @@ func newGoBinaryPackage(name, version, h1Digest, goVersion, architecture string,
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGoBin(location source.Location, reader io.ReadCloser, opener exeOpener) (pkgs []pkg.Package, err error) {
|
func parseGoBin(location file.Location, reader io.ReadCloser, opener exeOpener) (pkgs []pkg.Package, err error) {
|
||||||
var exes []exe
|
var exes []exe
|
||||||
// it has been found that there are stdlib paths within openExe that can panic. We want to prevent this behavior
|
// it has been found that there are stdlib paths within openExe that can panic. We want to prevent this behavior
|
||||||
// bubbling up and halting execution. For this reason we try to recover from any panic and return an error.
|
// bubbling up and halting execution. For this reason we try to recover from any panic and return an error.
|
||||||
@ -62,7 +63,7 @@ func parseGoBin(location source.Location, reader io.ReadCloser, opener exeOpener
|
|||||||
return pkgs, err
|
return pkgs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildGoPkgInfo(location source.Location, mod, goVersion, arch string) []pkg.Package {
|
func buildGoPkgInfo(location file.Location, mod, goVersion, arch string) []pkg.Package {
|
||||||
pkgsSlice := make([]pkg.Package, 0)
|
pkgsSlice := make([]pkg.Package, 0)
|
||||||
scanner := bufio.NewScanner(strings.NewReader(mod))
|
scanner := bufio.NewScanner(strings.NewReader(mod))
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
package golang
|
package golang
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,9 +35,9 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
Version: "v0.2.1",
|
Version: "v0.2.1",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a-path",
|
RealPath: "/a-path",
|
||||||
FileSystemID: "layer-id",
|
FileSystemID: "layer-id",
|
||||||
},
|
},
|
||||||
@ -55,9 +55,9 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a-path",
|
RealPath: "/a-path",
|
||||||
FileSystemID: "layer-id",
|
FileSystemID: "layer-id",
|
||||||
},
|
},
|
||||||
@ -86,9 +86,9 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
Version: "v0.0.0-20211006190231-62292e806868",
|
Version: "v0.0.0-20211006190231-62292e806868",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a-path",
|
RealPath: "/a-path",
|
||||||
FileSystemID: "layer-id",
|
FileSystemID: "layer-id",
|
||||||
},
|
},
|
||||||
@ -106,9 +106,9 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a-path",
|
RealPath: "/a-path",
|
||||||
FileSystemID: "layer-id",
|
FileSystemID: "layer-id",
|
||||||
},
|
},
|
||||||
@ -126,9 +126,9 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a-path",
|
RealPath: "/a-path",
|
||||||
FileSystemID: "layer-id",
|
FileSystemID: "layer-id",
|
||||||
},
|
},
|
||||||
@ -151,8 +151,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
p := &test.expected[i]
|
p := &test.expected[i]
|
||||||
p.SetID()
|
p.SetID()
|
||||||
}
|
}
|
||||||
location := source.Location{
|
location := file.Location{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/a-path",
|
RealPath: "/a-path",
|
||||||
FileSystemID: "layer-id",
|
FileSystemID: "layer-id",
|
||||||
},
|
},
|
||||||
@ -178,7 +178,7 @@ func Test_parseGoBin_recoversFromPanic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
pkgs, err := parseGoBin(source.NewLocation("some/path"), nil, freakOut)
|
pkgs, err := parseGoBin(file.NewLocation("some/path"), nil, freakOut)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, pkgs)
|
assert.Nil(t, pkgs)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,11 +2,12 @@ package java
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/internal/archive"
|
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/archive"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package java
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/archive"
|
"github.com/anchore/syft/internal/archive"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package java
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/archive"
|
"github.com/anchore/syft/internal/archive"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
|||||||
@ -4,10 +4,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
|
|||||||
@ -3,9 +3,10 @@ package javascript
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,10 +3,11 @@ package javascript
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package packages
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
|
|||||||
@ -69,13 +69,13 @@ func IndexCatalogers(cfg SearchConfig) []pkg.Cataloger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CatalogersBySourceScheme(scheme source.Scheme, cfg SearchConfig) []pkg.Cataloger {
|
func CatalogersBySourceScheme(scheme source.Type, cfg SearchConfig) []pkg.Cataloger {
|
||||||
switch scheme {
|
switch scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageType:
|
||||||
return InstalledCatalogers(cfg)
|
return InstalledCatalogers(cfg)
|
||||||
case source.FileScheme:
|
case source.FileType:
|
||||||
return AllCatalogers(cfg)
|
return AllCatalogers(cfg)
|
||||||
case source.DirectoryScheme:
|
case source.DirectoryType:
|
||||||
return IndexCatalogers(cfg)
|
return IndexCatalogers(cfg)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -3,9 +3,10 @@ package php
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
@ -35,7 +37,7 @@ func (c *PackageCataloger) Name() string {
|
|||||||
|
|
||||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing python egg and wheel installations.
|
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing python egg and wheel installations.
|
||||||
func (c *PackageCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
func (c *PackageCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
var fileMatches []source.Location
|
var fileMatches []file.Location
|
||||||
|
|
||||||
for _, glob := range []string{eggMetadataGlob, wheelMetadataGlob, eggFileMetadataGlob} {
|
for _, glob := range []string{eggMetadataGlob, wheelMetadataGlob, eggFileMetadataGlob} {
|
||||||
matches, err := resolver.FilesByGlob(glob)
|
matches, err := resolver.FilesByGlob(glob)
|
||||||
@ -59,7 +61,7 @@ func (c *PackageCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// catalogEggOrWheel takes the primary metadata file reference and returns the python package it represents.
|
// catalogEggOrWheel takes the primary metadata file reference and returns the python package it represents.
|
||||||
func (c *PackageCataloger) catalogEggOrWheel(resolver source.FileResolver, metadataLocation source.Location) (*pkg.Package, error) {
|
func (c *PackageCataloger) catalogEggOrWheel(resolver source.FileResolver, metadataLocation file.Location) (*pkg.Package, error) {
|
||||||
metadata, sources, err := c.assembleEggOrWheelMetadata(resolver, metadataLocation)
|
metadata, sources, err := c.assembleEggOrWheelMetadata(resolver, metadataLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -94,7 +96,7 @@ func (c *PackageCataloger) catalogEggOrWheel(resolver source.FileResolver, metad
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetchRecordFiles finds a corresponding RECORD file for the given python package metadata file and returns the set of file records contained.
|
// fetchRecordFiles finds a corresponding RECORD file for the given python package metadata file and returns the set of file records contained.
|
||||||
func (c *PackageCataloger) fetchRecordFiles(resolver source.FileResolver, metadataLocation source.Location) (files []pkg.PythonFileRecord, sources []source.Location, err error) {
|
func (c *PackageCataloger) fetchRecordFiles(resolver source.FileResolver, metadataLocation file.Location) (files []pkg.PythonFileRecord, sources []file.Location, err error) {
|
||||||
// we've been given a file reference to a specific wheel METADATA file. note: this may be for a directory
|
// we've been given a file reference to a specific wheel METADATA file. note: this may be for a directory
|
||||||
// or for an image... for an image the METADATA file may be present within multiple layers, so it is important
|
// or for an image... for an image the METADATA file may be present within multiple layers, so it is important
|
||||||
// to reconcile the RECORD path to the same layer (or the next adjacent lower layer).
|
// to reconcile the RECORD path to the same layer (or the next adjacent lower layer).
|
||||||
@ -124,7 +126,7 @@ func (c *PackageCataloger) fetchRecordFiles(resolver source.FileResolver, metada
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetchTopLevelPackages finds a corresponding top_level.txt file for the given python package metadata file and returns the set of package names contained.
|
// fetchTopLevelPackages finds a corresponding top_level.txt file for the given python package metadata file and returns the set of package names contained.
|
||||||
func (c *PackageCataloger) fetchTopLevelPackages(resolver source.FileResolver, metadataLocation source.Location) (pkgs []string, sources []source.Location, err error) {
|
func (c *PackageCataloger) fetchTopLevelPackages(resolver source.FileResolver, metadataLocation file.Location) (pkgs []string, sources []file.Location, err error) {
|
||||||
// a top_level.txt file specifies the python top-level packages (provided by this python package) installed into site-packages
|
// a top_level.txt file specifies the python top-level packages (provided by this python package) installed into site-packages
|
||||||
parentDir := filepath.Dir(metadataLocation.RealPath)
|
parentDir := filepath.Dir(metadataLocation.RealPath)
|
||||||
topLevelPath := filepath.Join(parentDir, "top_level.txt")
|
topLevelPath := filepath.Join(parentDir, "top_level.txt")
|
||||||
@ -154,7 +156,7 @@ func (c *PackageCataloger) fetchTopLevelPackages(resolver source.FileResolver, m
|
|||||||
return pkgs, sources, nil
|
return pkgs, sources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PackageCataloger) fetchDirectURLData(resolver source.FileResolver, metadataLocation source.Location) (d *pkg.PythonDirectURLOriginInfo, sources []source.Location, err error) {
|
func (c *PackageCataloger) fetchDirectURLData(resolver source.FileResolver, metadataLocation file.Location) (d *pkg.PythonDirectURLOriginInfo, sources []file.Location, err error) {
|
||||||
parentDir := filepath.Dir(metadataLocation.RealPath)
|
parentDir := filepath.Dir(metadataLocation.RealPath)
|
||||||
directURLPath := filepath.Join(parentDir, "direct_url.json")
|
directURLPath := filepath.Join(parentDir, "direct_url.json")
|
||||||
directURLLocation := resolver.RelativeFileByPath(metadataLocation, directURLPath)
|
directURLLocation := resolver.RelativeFileByPath(metadataLocation, directURLPath)
|
||||||
@ -189,8 +191,8 @@ func (c *PackageCataloger) fetchDirectURLData(resolver source.FileResolver, meta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assembleEggOrWheelMetadata discovers and accumulates python package metadata from multiple file sources and returns a single metadata object as well as a list of files where the metadata was derived from.
|
// assembleEggOrWheelMetadata discovers and accumulates python package metadata from multiple file sources and returns a single metadata object as well as a list of files where the metadata was derived from.
|
||||||
func (c *PackageCataloger) assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation source.Location) (*pkg.PythonPackageMetadata, []source.Location, error) {
|
func (c *PackageCataloger) assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation file.Location) (*pkg.PythonPackageMetadata, []file.Location, error) {
|
||||||
var sources = []source.Location{metadataLocation}
|
var sources = []file.Location{metadataLocation}
|
||||||
|
|
||||||
metadataContents, err := resolver.FileContentsByLocation(metadataLocation)
|
metadataContents, err := resolver.FileContentsByLocation(metadataLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -3,11 +3,12 @@ package python
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package python
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/pelletier/go-toml"
|
"github.com/pelletier/go-toml"
|
||||||
|
|||||||
@ -3,10 +3,11 @@ package python
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,11 +2,12 @@ package python
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,11 +3,12 @@ package python
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// parseApkDb parses an "Packages" RPM DB and returns the Packages listed within it.
|
// parseApkDb parses an "Packages" RPM DB and returns the Packages listed within it.
|
||||||
func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, reader io.Reader) ([]pkg.Package, error) {
|
func parseRpmDB(resolver source.FilePathResolver, dbLocation file.Location, reader io.Reader) ([]pkg.Package, error) {
|
||||||
f, err := ioutil.TempFile("", internal.ApplicationName+"-rpmdb")
|
f, err := ioutil.TempFile("", internal.ApplicationName+"-rpmdb")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create temp rpmdb file: %w", err)
|
return nil, fmt.Errorf("failed to create temp rpmdb file: %w", err)
|
||||||
@ -63,7 +63,7 @@ func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, re
|
|||||||
p := pkg.Package{
|
p := pkg.Package{
|
||||||
Name: entry.Name,
|
Name: entry.Name,
|
||||||
Version: toELVersion(metadata),
|
Version: toELVersion(metadata),
|
||||||
Locations: []source.Location{dbLocation},
|
Locations: []file.Location{dbLocation},
|
||||||
FoundBy: catalogerName,
|
FoundBy: catalogerName,
|
||||||
Type: pkg.RpmPkg,
|
Type: pkg.RpmPkg,
|
||||||
MetadataType: pkg.RpmdbMetadataType,
|
MetadataType: pkg.RpmdbMetadataType,
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
@ -29,34 +27,34 @@ func (r rpmdbTestFileResolverMock) HasPath(path string) bool {
|
|||||||
return !r.ignorePaths
|
return !r.ignorePaths
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpmdbTestFileResolverMock) FilesByPath(paths ...string) ([]source.Location, error) {
|
func (r *rpmdbTestFileResolverMock) FilesByPath(paths ...string) ([]file.Location, error) {
|
||||||
if r.ignorePaths {
|
if r.ignorePaths {
|
||||||
// act as if no paths exist
|
// act as if no paths exist
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
// act as if all files exist
|
// act as if all files exist
|
||||||
var locations = make([]source.Location, len(paths))
|
var locations = make([]file.Location, len(paths))
|
||||||
for i, p := range paths {
|
for i, p := range paths {
|
||||||
locations[i] = source.NewLocation(p)
|
locations[i] = file.NewLocation(p)
|
||||||
}
|
}
|
||||||
return locations, nil
|
return locations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpmdbTestFileResolverMock) FilesByGlob(...string) ([]source.Location, error) {
|
func (r *rpmdbTestFileResolverMock) FilesByGlob(...string) ([]file.Location, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpmdbTestFileResolverMock) RelativeFileByPath(source.Location, string) *source.Location {
|
func (r *rpmdbTestFileResolverMock) RelativeFileByPath(file.Location, string) *file.Location {
|
||||||
panic(fmt.Errorf("not implemented"))
|
panic(fmt.Errorf("not implemented"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpmdbTestFileResolverMock) FilesByMIMEType(...string) ([]source.Location, error) {
|
func (r *rpmdbTestFileResolverMock) FilesByMIMEType(...string) ([]file.Location, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRpmDB(t *testing.T) {
|
func TestParseRpmDB(t *testing.T) {
|
||||||
dbLocation := source.NewLocation("test-path")
|
dbLocation := file.NewLocation("test-path")
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
fixture string
|
fixture string
|
||||||
@ -71,7 +69,7 @@ func TestParseRpmDB(t *testing.T) {
|
|||||||
"dive": {
|
"dive": {
|
||||||
Name: "dive",
|
Name: "dive",
|
||||||
Version: "0.9.2-1",
|
Version: "0.9.2-1",
|
||||||
Locations: []source.Location{dbLocation},
|
Locations: []file.Location{dbLocation},
|
||||||
FoundBy: catalogerName,
|
FoundBy: catalogerName,
|
||||||
Type: pkg.RpmPkg,
|
Type: pkg.RpmPkg,
|
||||||
MetadataType: pkg.RpmdbMetadataType,
|
MetadataType: pkg.RpmdbMetadataType,
|
||||||
@ -98,7 +96,7 @@ func TestParseRpmDB(t *testing.T) {
|
|||||||
"dive": {
|
"dive": {
|
||||||
Name: "dive",
|
Name: "dive",
|
||||||
Version: "0.9.2-1",
|
Version: "0.9.2-1",
|
||||||
Locations: []source.Location{dbLocation},
|
Locations: []file.Location{dbLocation},
|
||||||
FoundBy: catalogerName,
|
FoundBy: catalogerName,
|
||||||
Type: pkg.RpmPkg,
|
Type: pkg.RpmPkg,
|
||||||
MetadataType: pkg.RpmdbMetadataType,
|
MetadataType: pkg.RpmdbMetadataType,
|
||||||
|
|||||||
@ -2,10 +2,11 @@ package ruby
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package rust
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/pelletier/go-toml"
|
"github.com/pelletier/go-toml"
|
||||||
|
|||||||
@ -6,26 +6,27 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Package represents an application or library that has been bundled into a distributable format.
|
// Package represents an application or library that has been bundled into a distributable format.
|
||||||
// TODO: if we ignore FoundBy for ID generation should we merge the field to show it was found in two places?
|
// TODO: if we ignore FoundBy for ID generation should we merge the field to show it was found in two places?
|
||||||
type Package struct {
|
type Package struct {
|
||||||
id artifact.ID `hash:"ignore"`
|
id artifact.ID `hash:"ignore"`
|
||||||
Name string // the package name
|
Name string // the package name
|
||||||
Version string // the version of the package
|
Version string // the version of the package
|
||||||
FoundBy string `cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
FoundBy string `cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
||||||
Locations []source.Location // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
Locations []file.Location // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
||||||
Licenses []string // licenses discovered with the package metadata
|
Licenses []string // licenses discovered with the package metadata
|
||||||
Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
||||||
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
||||||
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
|
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
|
||||||
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec) (note: this is NOT included in the definition of the ID since all fields on a pURL are derived from other fields)
|
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec) (note: this is NOT included in the definition of the ID since all fields on a pURL are derived from other fields)
|
||||||
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
|
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
|
||||||
Metadata interface{} // additional data found while parsing the package source
|
Metadata interface{} // additional data found while parsing the package source
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Package) SetID() {
|
func (p *Package) SetID() {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,9 +12,9 @@ func TestFingerprint(t *testing.T) {
|
|||||||
Name: "pi",
|
Name: "pi",
|
||||||
Version: "3.14",
|
Version: "3.14",
|
||||||
FoundBy: "Archimedes",
|
FoundBy: "Archimedes",
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "39.0742° N, 21.8243° E",
|
RealPath: "39.0742° N, 21.8243° E",
|
||||||
FileSystemID: "Earth",
|
FileSystemID: "Earth",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,9 +18,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
|||||||
name: "owns-by-real-path",
|
name: "owns-by-real-path",
|
||||||
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
|
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
|
||||||
parent := Package{
|
parent := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/a/path", "/another/path"),
|
file.NewVirtualLocation("/a/path", "/another/path"),
|
||||||
source.NewVirtualLocation("/b/path", "/bee/path"),
|
file.NewVirtualLocation("/b/path", "/bee/path"),
|
||||||
},
|
},
|
||||||
Type: RpmPkg,
|
Type: RpmPkg,
|
||||||
MetadataType: RpmdbMetadataType,
|
MetadataType: RpmdbMetadataType,
|
||||||
@ -35,9 +35,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
|||||||
parent.SetID()
|
parent.SetID()
|
||||||
|
|
||||||
child := Package{
|
child := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/c/path", "/another/path"),
|
file.NewVirtualLocation("/c/path", "/another/path"),
|
||||||
source.NewVirtualLocation("/d/path", "/another/path"),
|
file.NewVirtualLocation("/d/path", "/another/path"),
|
||||||
},
|
},
|
||||||
Type: NpmPkg,
|
Type: NpmPkg,
|
||||||
}
|
}
|
||||||
@ -61,9 +61,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
|||||||
name: "owns-by-virtual-path",
|
name: "owns-by-virtual-path",
|
||||||
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
|
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
|
||||||
parent := Package{
|
parent := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/a/path", "/some/other/path"),
|
file.NewVirtualLocation("/a/path", "/some/other/path"),
|
||||||
source.NewVirtualLocation("/b/path", "/bee/path"),
|
file.NewVirtualLocation("/b/path", "/bee/path"),
|
||||||
},
|
},
|
||||||
Type: RpmPkg,
|
Type: RpmPkg,
|
||||||
MetadataType: RpmdbMetadataType,
|
MetadataType: RpmdbMetadataType,
|
||||||
@ -78,9 +78,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
|||||||
parent.SetID()
|
parent.SetID()
|
||||||
|
|
||||||
child := Package{
|
child := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/c/path", "/another/path"),
|
file.NewVirtualLocation("/c/path", "/another/path"),
|
||||||
source.NewLocation("/d/path"),
|
file.NewLocation("/d/path"),
|
||||||
},
|
},
|
||||||
Type: NpmPkg,
|
Type: NpmPkg,
|
||||||
}
|
}
|
||||||
@ -103,9 +103,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
|||||||
name: "ignore-empty-path",
|
name: "ignore-empty-path",
|
||||||
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
|
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
|
||||||
parent := Package{
|
parent := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/a/path", "/some/other/path"),
|
file.NewVirtualLocation("/a/path", "/some/other/path"),
|
||||||
source.NewVirtualLocation("/b/path", "/bee/path"),
|
file.NewVirtualLocation("/b/path", "/bee/path"),
|
||||||
},
|
},
|
||||||
Type: RpmPkg,
|
Type: RpmPkg,
|
||||||
MetadataType: RpmdbMetadataType,
|
MetadataType: RpmdbMetadataType,
|
||||||
@ -121,9 +121,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
|||||||
parent.SetID()
|
parent.SetID()
|
||||||
|
|
||||||
child := Package{
|
child := Package{
|
||||||
Locations: []source.Location{
|
Locations: []file.Location{
|
||||||
source.NewVirtualLocation("/c/path", "/another/path"),
|
file.NewVirtualLocation("/c/path", "/another/path"),
|
||||||
source.NewLocation("/d/path"),
|
file.NewLocation("/d/path"),
|
||||||
},
|
},
|
||||||
Type: NpmPkg,
|
Type: NpmPkg,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,11 +17,11 @@ type SBOM struct {
|
|||||||
|
|
||||||
type Artifacts struct {
|
type Artifacts struct {
|
||||||
PackageCatalog *pkg.Catalog
|
PackageCatalog *pkg.Catalog
|
||||||
FileMetadata map[source.Coordinates]source.FileMetadata
|
FileMetadata map[file.Coordinates]file.Metadata
|
||||||
FileDigests map[source.Coordinates][]file.Digest
|
FileDigests map[file.Coordinates][]file.Digest
|
||||||
FileClassifications map[source.Coordinates][]file.Classification
|
FileClassifications map[file.Coordinates][]file.Classification
|
||||||
FileContents map[source.Coordinates]string
|
FileContents map[file.Coordinates]string
|
||||||
Secrets map[source.Coordinates][]file.SearchResult
|
Secrets map[file.Coordinates][]file.SearchResult
|
||||||
LinuxDistribution *linux.Release
|
LinuxDistribution *linux.Release
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +31,8 @@ type Descriptor struct {
|
|||||||
Configuration interface{}
|
Configuration interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AllCoordinates(sbom SBOM) []source.Coordinates {
|
func AllCoordinates(sbom SBOM) []file.Coordinates {
|
||||||
set := source.NewCoordinateSet()
|
set := file.NewCoordinateSet()
|
||||||
for coordinates := range sbom.Artifacts.FileMetadata {
|
for coordinates := range sbom.Artifacts.FileMetadata {
|
||||||
set.Add(coordinates)
|
set.Add(coordinates)
|
||||||
}
|
}
|
||||||
@ -53,12 +53,12 @@ func AllCoordinates(sbom SBOM) []source.Coordinates {
|
|||||||
return set.ToSlice()
|
return set.ToSlice()
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractCoordinates(relationship artifact.Relationship) (results []source.Coordinates) {
|
func extractCoordinates(relationship artifact.Relationship) (results []file.Coordinates) {
|
||||||
if coordinates, exists := relationship.From.(source.Coordinates); exists {
|
if coordinates, exists := relationship.From.(file.Coordinates); exists {
|
||||||
results = append(results, coordinates)
|
results = append(results, coordinates)
|
||||||
}
|
}
|
||||||
|
|
||||||
if coordinates, exists := relationship.To.(source.Coordinates); exists {
|
if coordinates, exists := relationship.To.(file.Coordinates); exists {
|
||||||
results = append(results, coordinates)
|
results = append(results, coordinates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,10 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/filetree"
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"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/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ FileResolver = (*allLayersResolver)(nil)
|
var _ FileResolver = (*allLayersResolver)(nil)
|
||||||
@ -37,7 +38,7 @@ func newAllLayersResolver(img *image.Image) (*allLayersResolver, error) {
|
|||||||
|
|
||||||
// HasPath indicates if the given path exists in the underlying source.
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
func (r *allLayersResolver) HasPath(path string) bool {
|
func (r *allLayersResolver) HasPath(path string) bool {
|
||||||
p := file.Path(path)
|
p := stereoscopeFile.Path(path)
|
||||||
for _, layerIdx := range r.layers {
|
for _, layerIdx := range r.layers {
|
||||||
tree := r.img.Layers[layerIdx].Tree
|
tree := r.img.Layers[layerIdx].Tree
|
||||||
if tree.HasPath(p) {
|
if tree.HasPath(p) {
|
||||||
@ -47,8 +48,8 @@ func (r *allLayersResolver) HasPath(path string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *allLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.ReferenceSet, layerIdx int) ([]file.Reference, error) {
|
func (r *allLayersResolver) fileByRef(ref stereoscopeFile.Reference, uniqueFileIDs stereoscopeFile.ReferenceSet, layerIdx int) ([]stereoscopeFile.Reference, error) {
|
||||||
uniqueFiles := make([]file.Reference, 0)
|
uniqueFiles := make([]stereoscopeFile.Reference, 0)
|
||||||
|
|
||||||
// since there is potentially considerable work for each symlink/hardlink that needs to be resolved, let's check to see if this is a symlink/hardlink first
|
// since there is potentially considerable work for each symlink/hardlink that needs to be resolved, let's check to see if this is a symlink/hardlink first
|
||||||
entry, err := r.img.FileCatalog.Get(ref)
|
entry, err := r.img.FileCatalog.Get(ref)
|
||||||
@ -77,15 +78,15 @@ func (r *allLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.Ref
|
|||||||
return uniqueFiles, nil
|
return uniqueFiles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByPath returns all file.References that match the given paths from any layer in the image.
|
// FilesByPath returns all stereoscopeFile.References that match the given paths from any layer in the image.
|
||||||
func (r *allLayersResolver) FilesByPath(paths ...string) ([]Location, error) {
|
func (r *allLayersResolver) FilesByPath(paths ...string) ([]file.Location, error) {
|
||||||
uniqueFileIDs := file.NewFileReferenceSet()
|
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
|
||||||
uniqueLocations := make([]Location, 0)
|
uniqueLocations := make([]file.Location, 0)
|
||||||
|
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
for idx, layerIdx := range r.layers {
|
for idx, layerIdx := range r.layers {
|
||||||
tree := r.img.Layers[layerIdx].Tree
|
tree := r.img.Layers[layerIdx].Tree
|
||||||
_, ref, err := tree.File(file.Path(path), filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks)
|
_, ref, err := tree.File(stereoscopeFile.Path(path), filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -112,17 +113,17 @@ func (r *allLayersResolver) FilesByPath(paths ...string) ([]Location, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
uniqueLocations = append(uniqueLocations, NewLocationFromImage(path, result, r.img))
|
uniqueLocations = append(uniqueLocations, file.NewLocationFromImage(path, result, r.img))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uniqueLocations, nil
|
return uniqueLocations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
// FilesByGlob returns all stereoscopeFile.References that match the given path glob pattern from any layer in the image.
|
||||||
func (r *allLayersResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
func (r *allLayersResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||||
uniqueFileIDs := file.NewFileReferenceSet()
|
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
|
||||||
uniqueLocations := make([]Location, 0)
|
uniqueLocations := make([]file.Location, 0)
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
for idx, layerIdx := range r.layers {
|
for idx, layerIdx := range r.layers {
|
||||||
@ -150,7 +151,7 @@ func (r *allLayersResolver) FilesByGlob(patterns ...string) ([]Location, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, refResult := range refResults {
|
for _, refResult := range refResults {
|
||||||
uniqueLocations = append(uniqueLocations, NewLocationFromImage(string(result.MatchPath), refResult, r.img))
|
uniqueLocations = append(uniqueLocations, file.NewLocationFromImage(string(result.MatchPath), refResult, r.img))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,14 +161,14 @@ func (r *allLayersResolver) FilesByGlob(patterns ...string) ([]Location, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
||||||
// This is helpful when attempting to find a file that is in the same layer or lower as another file.
|
// This is helpful when attempting to find a file that is in the same layer or lower as another stereoscopeFile.
|
||||||
func (r *allLayersResolver) RelativeFileByPath(location Location, path string) *Location {
|
func (r *allLayersResolver) RelativeFileByPath(location file.Location, path string) *file.Location {
|
||||||
entry, err := r.img.FileCatalog.Get(location.ref)
|
entry, err := r.img.FileCatalog.Get(location.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, relativeRef, err := entry.Layer.SquashedTree.File(file.Path(path), filetree.FollowBasenameLinks)
|
exists, relativeRef, err := entry.Layer.SquashedTree.File(stereoscopeFile.Path(path), filetree.FollowBasenameLinks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to find path=%q in squash: %+w", path, err)
|
log.Errorf("failed to find path=%q in squash: %+w", path, err)
|
||||||
return nil
|
return nil
|
||||||
@ -176,15 +177,15 @@ func (r *allLayersResolver) RelativeFileByPath(location Location, path string) *
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
relativeLocation := NewLocationFromImage(path, *relativeRef, r.img)
|
relativeLocation := file.NewLocationFromImage(path, *relativeRef, r.img)
|
||||||
|
|
||||||
return &relativeLocation
|
return &relativeLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileContentsByLocation fetches file contents for a single file reference, irregardless of the source layer.
|
// FileContentsByLocation fetches file contents for a single file reference, irregardless of the source layer.
|
||||||
// If the path does not exist an error is returned.
|
// If the path does not exist an error is returned.
|
||||||
func (r *allLayersResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
func (r *allLayersResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
|
||||||
entry, err := r.img.FileCatalog.Get(location.ref)
|
entry, err := r.img.FileCatalog.Get(location.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
|
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
|
||||||
}
|
}
|
||||||
@ -200,11 +201,11 @@ func (r *allLayersResolver) FileContentsByLocation(location Location) (io.ReadCl
|
|||||||
location = *newLocation
|
location = *newLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.img.FileContentsByRef(location.ref)
|
return r.img.FileContentsByRef(location.Ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *allLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
func (r *allLayersResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
|
||||||
var locations []Location
|
var locations []file.Location
|
||||||
for _, layerIdx := range r.layers {
|
for _, layerIdx := range r.layers {
|
||||||
layer := r.img.Layers[layerIdx]
|
layer := r.img.Layers[layerIdx]
|
||||||
|
|
||||||
@ -214,27 +215,27 @@ func (r *allLayersResolver) FilesByMIMEType(types ...string) ([]Location, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, ref := range refs {
|
for _, ref := range refs {
|
||||||
locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img))
|
locations = append(locations, file.NewLocationFromImage(string(ref.RealPath), ref, r.img))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return locations, nil
|
return locations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *allLayersResolver) AllLocations() <-chan Location {
|
func (r *allLayersResolver) AllLocations() <-chan file.Location {
|
||||||
results := make(chan Location)
|
results := make(chan file.Location)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(results)
|
defer close(results)
|
||||||
for _, layerIdx := range r.layers {
|
for _, layerIdx := range r.layers {
|
||||||
tree := r.img.Layers[layerIdx].Tree
|
tree := r.img.Layers[layerIdx].Tree
|
||||||
for _, ref := range tree.AllFiles(file.AllTypes...) {
|
for _, ref := range tree.AllFiles(stereoscopeFile.AllTypes...) {
|
||||||
results <- NewLocationFromImage(string(ref.RealPath), ref, r.img)
|
results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *allLayersResolver) FileMetadataByLocation(location Location) (FileMetadata, error) {
|
func (r *allLayersResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
|
||||||
return fileMetadataByLocation(r.img, location)
|
return file.MetadataByLocation(r.img, location)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package source
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
@ -116,15 +117,15 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
|||||||
for idx, actual := range refs {
|
for idx, actual := range refs {
|
||||||
expected := c.resolutions[idx]
|
expected := c.resolutions[idx]
|
||||||
|
|
||||||
if string(actual.ref.RealPath) != expected.path {
|
if string(actual.Ref().RealPath) != expected.path {
|
||||||
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.ref.RealPath), expected.path)
|
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.Ref().RealPath), expected.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected.path != "" && string(actual.ref.RealPath) != actual.RealPath {
|
if expected.path != "" && string(actual.Ref().RealPath) != actual.RealPath {
|
||||||
t.Errorf("we should always prefer real paths over ones with links")
|
t.Errorf("we should always prefer real paths over ones with links")
|
||||||
}
|
}
|
||||||
|
|
||||||
entry, err := img.FileCatalog.Get(actual.ref)
|
entry, err := img.FileCatalog.Get(actual.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get metadata: %+v", err)
|
t.Fatalf("failed to get metadata: %+v", err)
|
||||||
}
|
}
|
||||||
@ -223,15 +224,15 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) {
|
|||||||
for idx, actual := range refs {
|
for idx, actual := range refs {
|
||||||
expected := c.resolutions[idx]
|
expected := c.resolutions[idx]
|
||||||
|
|
||||||
if string(actual.ref.RealPath) != expected.path {
|
if string(actual.Ref().RealPath) != expected.path {
|
||||||
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.ref.RealPath), expected.path)
|
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.Ref().RealPath), expected.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected.path != "" && string(actual.ref.RealPath) != actual.RealPath {
|
if expected.path != "" && string(actual.Ref().RealPath) != actual.RealPath {
|
||||||
t.Errorf("we should always prefer real paths over ones with links")
|
t.Errorf("we should always prefer real paths over ones with links")
|
||||||
}
|
}
|
||||||
|
|
||||||
entry, err := img.FileCatalog.Get(actual.ref)
|
entry, err := img.FileCatalog.Get(actual.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get metadata: %+v", err)
|
t.Fatalf("failed to get metadata: %+v", err)
|
||||||
}
|
}
|
||||||
@ -364,72 +365,72 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) {
|
|||||||
func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
runner func(FileResolver) []Location
|
runner func(FileResolver) []file.Location
|
||||||
expected []Location
|
expected []file.Location
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "by mimetype",
|
name: "by mimetype",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// links should not show up when searching mimetype
|
// links should not show up when searching mimetype
|
||||||
actualLocations, err := resolver.FilesByMIMEType("text/plain")
|
actualLocations, err := resolver.FilesByMIMEType("text/plain")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/etc/group",
|
RealPath: "/etc/group",
|
||||||
},
|
},
|
||||||
VirtualPath: "/etc/group",
|
VirtualPath: "/etc/group",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/etc/passwd",
|
RealPath: "/etc/passwd",
|
||||||
},
|
},
|
||||||
VirtualPath: "/etc/passwd",
|
VirtualPath: "/etc/passwd",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/etc/shadow",
|
RealPath: "/etc/shadow",
|
||||||
},
|
},
|
||||||
VirtualPath: "/etc/shadow",
|
VirtualPath: "/etc/shadow",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-1.txt",
|
RealPath: "/file-1.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-1.txt",
|
VirtualPath: "/file-1.txt",
|
||||||
},
|
},
|
||||||
// copy 1
|
// copy 1
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-2.txt",
|
VirtualPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-3.txt",
|
RealPath: "/file-3.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-3.txt",
|
VirtualPath: "/file-3.txt",
|
||||||
},
|
},
|
||||||
// copy 2
|
// copy 2
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-2.txt",
|
VirtualPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
// copy 1
|
// copy 1
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/parent/file-4.txt",
|
RealPath: "/parent/file-4.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/parent/file-4.txt",
|
VirtualPath: "/parent/file-4.txt",
|
||||||
},
|
},
|
||||||
// copy 2
|
// copy 2
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/parent/file-4.txt",
|
RealPath: "/parent/file-4.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/parent/file-4.txt",
|
VirtualPath: "/parent/file-4.txt",
|
||||||
@ -438,35 +439,35 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "by glob",
|
name: "by glob",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// links are searched, but resolve to the real files
|
// links are searched, but resolve to the real files
|
||||||
actualLocations, err := resolver.FilesByGlob("*ink-*")
|
actualLocations, err := resolver.FilesByGlob("*ink-*")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-1.txt",
|
RealPath: "/file-1.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-1",
|
VirtualPath: "/link-1",
|
||||||
},
|
},
|
||||||
// copy 1
|
// copy 1
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-2",
|
VirtualPath: "/link-2",
|
||||||
},
|
},
|
||||||
// copy 2
|
// copy 2
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-2",
|
VirtualPath: "/link-2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-3.txt",
|
RealPath: "/file-3.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-within",
|
VirtualPath: "/link-within",
|
||||||
@ -475,22 +476,22 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "by path to degree 1 link",
|
name: "by path to degree 1 link",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// links resolve to the final file
|
// links resolve to the final file
|
||||||
actualLocations, err := resolver.FilesByPath("/link-2")
|
actualLocations, err := resolver.FilesByPath("/link-2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
// we have multiple copies across layers
|
// we have multiple copies across layers
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-2",
|
VirtualPath: "/link-2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-2",
|
VirtualPath: "/link-2",
|
||||||
@ -499,22 +500,22 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "by path to degree 2 link",
|
name: "by path to degree 2 link",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// multiple links resolves to the final file
|
// multiple links resolves to the final file
|
||||||
actualLocations, err := resolver.FilesByPath("/link-indirect")
|
actualLocations, err := resolver.FilesByPath("/link-indirect")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
// we have multiple copies across layers
|
// we have multiple copies across layers
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-indirect",
|
VirtualPath: "/link-indirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-indirect",
|
VirtualPath: "/link-indirect",
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package source
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AllRegularFiles(resolver FileResolver) (locations []Location) {
|
func AllRegularFiles(resolver FileResolver) (locations []file.Location) {
|
||||||
for location := range resolver.AllLocations() {
|
for location := range resolver.AllLocations() {
|
||||||
resolvedLocations, err := resolver.FilesByPath(location.RealPath)
|
resolvedLocations, err := resolver.FilesByPath(location.RealPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -19,7 +20,7 @@ func AllRegularFiles(resolver FileResolver) (locations []Location) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata.Type != RegularFile {
|
if metadata.Type != file.RegularFile {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
locations = append(locations, resolvedLocation)
|
locations = append(locations, resolvedLocation)
|
||||||
|
|||||||
@ -11,12 +11,13 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/filetree"
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"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/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
"github.com/wagoodman/go-progress"
|
"github.com/wagoodman/go-progress"
|
||||||
)
|
)
|
||||||
@ -39,10 +40,10 @@ type directoryResolver struct {
|
|||||||
currentWdRelativeToRoot string
|
currentWdRelativeToRoot string
|
||||||
currentWd string
|
currentWd string
|
||||||
fileTree *filetree.FileTree
|
fileTree *filetree.FileTree
|
||||||
metadata map[file.ID]FileMetadata
|
metadata map[stereoscopeFile.ID]file.Metadata
|
||||||
// TODO: wire up to report these paths in the json report
|
// TODO: wire up to report these paths in the json report
|
||||||
pathFilterFns []pathFilterFn
|
pathFilterFns []pathFilterFn
|
||||||
refsByMIMEType map[string][]file.Reference
|
refsByMIMEType map[string][]stereoscopeFile.Reference
|
||||||
errPaths map[string]error
|
errPaths map[string]error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,9 +79,9 @@ func newDirectoryResolver(root string, pathFilters ...pathFilterFn) (*directoryR
|
|||||||
currentWd: cleanCWD,
|
currentWd: cleanCWD,
|
||||||
currentWdRelativeToRoot: currentWdRelRoot,
|
currentWdRelativeToRoot: currentWdRelRoot,
|
||||||
fileTree: filetree.NewFileTree(),
|
fileTree: filetree.NewFileTree(),
|
||||||
metadata: make(map[file.ID]FileMetadata),
|
metadata: make(map[stereoscopeFile.ID]file.Metadata),
|
||||||
pathFilterFns: append([]pathFilterFn{isUnallowableFileType, isUnixSystemRuntimePath}, pathFilters...),
|
pathFilterFns: append([]pathFilterFn{isUnallowableFileType, isUnixSystemRuntimePath}, pathFilters...),
|
||||||
refsByMIMEType: make(map[string][]file.Reference),
|
refsByMIMEType: make(map[string][]stereoscopeFile.Reference),
|
||||||
errPaths: make(map[string]error),
|
errPaths: make(map[string]error),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,12 +181,12 @@ func (r *directoryResolver) isFileAccessErr(path string, err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, error) {
|
func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, error) {
|
||||||
switch t := newFileTypeFromMode(info.Mode()); t {
|
switch t := file.NewFileTypeFromMode(info.Mode()); t {
|
||||||
case SymbolicLink:
|
case file.SymbolicLink:
|
||||||
return r.addSymlinkToIndex(p, info)
|
return r.addSymlinkToIndex(p, info)
|
||||||
case Directory:
|
case file.Directory:
|
||||||
return "", r.addDirectoryToIndex(p, info)
|
return "", r.addDirectoryToIndex(p, info)
|
||||||
case RegularFile:
|
case file.RegularFile:
|
||||||
return "", r.addFileToIndex(p, info)
|
return "", r.addFileToIndex(p, info)
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("unsupported file type: %s", t)
|
return "", fmt.Errorf("unsupported file type: %s", t)
|
||||||
@ -193,7 +194,7 @@ func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r directoryResolver) hasBeenIndexed(p string) bool {
|
func (r directoryResolver) hasBeenIndexed(p string) bool {
|
||||||
filePath := file.Path(p)
|
filePath := stereoscopeFile.Path(p)
|
||||||
if !r.fileTree.HasPath(filePath) {
|
if !r.fileTree.HasPath(filePath) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -210,26 +211,26 @@ func (r directoryResolver) hasBeenIndexed(p string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error {
|
func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error {
|
||||||
ref, err := r.fileTree.AddDir(file.Path(p))
|
ref, err := r.fileTree.AddDir(stereoscopeFile.Path(p))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
location := NewLocationFromDirectory(p, *ref)
|
location := file.NewLocationFromDirectory(p, *ref)
|
||||||
metadata := fileMetadataFromPath(p, info, r.isInIndex(location))
|
metadata := file.MetadataFromPath(p, info, r.isInIndex(location))
|
||||||
r.addFileMetadataToIndex(ref, metadata)
|
r.addFileMetadataToIndex(ref, metadata)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r directoryResolver) addFileToIndex(p string, info os.FileInfo) error {
|
func (r directoryResolver) addFileToIndex(p string, info os.FileInfo) error {
|
||||||
ref, err := r.fileTree.AddFile(file.Path(p))
|
ref, err := r.fileTree.AddFile(stereoscopeFile.Path(p))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
location := NewLocationFromDirectory(p, *ref)
|
location := file.NewLocationFromDirectory(p, *ref)
|
||||||
metadata := fileMetadataFromPath(p, info, r.isInIndex(location))
|
metadata := file.MetadataFromPath(p, info, r.isInIndex(location))
|
||||||
r.addFileMetadataToIndex(ref, metadata)
|
r.addFileMetadataToIndex(ref, metadata)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -249,7 +250,7 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string
|
|||||||
linkTarget = filepath.Join(filepath.Dir(p), linkTarget)
|
linkTarget = filepath.Join(filepath.Dir(p), linkTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
ref, err := r.fileTree.AddSymLink(file.Path(p), file.Path(linkTarget))
|
ref, err := r.fileTree.AddSymLink(stereoscopeFile.Path(p), stereoscopeFile.Path(linkTarget))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -259,16 +260,16 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string
|
|||||||
targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget))
|
targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget))
|
||||||
}
|
}
|
||||||
|
|
||||||
location := NewLocationFromDirectory(p, *ref)
|
location := file.NewLocationFromDirectory(p, *ref)
|
||||||
location.VirtualPath = p
|
location.VirtualPath = p
|
||||||
metadata := fileMetadataFromPath(p, usedInfo, r.isInIndex(location))
|
metadata := file.MetadataFromPath(p, usedInfo, r.isInIndex(location))
|
||||||
metadata.LinkDestination = linkTarget
|
metadata.LinkDestination = linkTarget
|
||||||
r.addFileMetadataToIndex(ref, metadata)
|
r.addFileMetadataToIndex(ref, metadata)
|
||||||
|
|
||||||
return targetAbsPath, nil
|
return targetAbsPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r directoryResolver) addFileMetadataToIndex(ref *file.Reference, metadata FileMetadata) {
|
func (r directoryResolver) addFileMetadataToIndex(ref *stereoscopeFile.Reference, metadata file.Metadata) {
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
if metadata.MIMEType != "" {
|
if metadata.MIMEType != "" {
|
||||||
r.refsByMIMEType[metadata.MIMEType] = append(r.refsByMIMEType[metadata.MIMEType], *ref)
|
r.refsByMIMEType[metadata.MIMEType] = append(r.refsByMIMEType[metadata.MIMEType], *ref)
|
||||||
@ -315,7 +316,7 @@ func (r *directoryResolver) HasPath(userPath string) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return r.fileTree.HasPath(file.Path(requestPath))
|
return r.fileTree.HasPath(stereoscopeFile.Path(requestPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stringer to represent a directory path data source
|
// Stringer to represent a directory path data source
|
||||||
@ -323,9 +324,9 @@ func (r directoryResolver) String() string {
|
|||||||
return fmt.Sprintf("dir:%s", r.path)
|
return fmt.Sprintf("dir:%s", r.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByPath returns all file.References that match the given paths from the directory.
|
// FilesByPath returns all stereoscopeFile.References that match the given paths from the directory.
|
||||||
func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error) {
|
func (r directoryResolver) FilesByPath(userPaths ...string) ([]file.Location, error) {
|
||||||
var references = make([]Location, 0)
|
var references = make([]file.Location, 0)
|
||||||
|
|
||||||
for _, userPath := range userPaths {
|
for _, userPath := range userPaths {
|
||||||
userStrPath, err := r.requestPath(userPath)
|
userStrPath, err := r.requestPath(userPath)
|
||||||
@ -367,9 +368,9 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
|
|||||||
userStrPath = windowsToPosix(userStrPath)
|
userStrPath = windowsToPosix(userStrPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, ref, err := r.fileTree.File(file.Path(userStrPath), filetree.FollowBasenameLinks)
|
exists, ref, err := r.fileTree.File(stereoscopeFile.Path(userStrPath), filetree.FollowBasenameLinks)
|
||||||
if err == nil && exists {
|
if err == nil && exists {
|
||||||
loc := NewVirtualLocationFromDirectory(
|
loc := file.NewVirtualLocationFromDirectory(
|
||||||
r.responsePath(string(ref.RealPath)), // the actual path relative to the resolver root
|
r.responsePath(string(ref.RealPath)), // the actual path relative to the resolver root
|
||||||
r.responsePath(userStrPath), // the path used to access this file, relative to the resolver root
|
r.responsePath(userStrPath), // the path used to access this file, relative to the resolver root
|
||||||
*ref,
|
*ref,
|
||||||
@ -381,9 +382,9 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
|
|||||||
return references, nil
|
return references, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
// FilesByGlob returns all stereoscopeFile.References that match the given path glob pattern from any layer in the image.
|
||||||
func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
func (r directoryResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||||
result := make([]Location, 0)
|
result := make([]file.Location, 0)
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
globResults, err := r.fileTree.FilesByGlob(pattern, filetree.FollowBasenameLinks)
|
globResults, err := r.fileTree.FilesByGlob(pattern, filetree.FollowBasenameLinks)
|
||||||
@ -391,7 +392,7 @@ func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, globResult := range globResults {
|
for _, globResult := range globResults {
|
||||||
loc := NewVirtualLocationFromDirectory(
|
loc := file.NewVirtualLocationFromDirectory(
|
||||||
r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root
|
r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root
|
||||||
r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root
|
r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root
|
||||||
globResult.Reference,
|
globResult.Reference,
|
||||||
@ -404,9 +405,9 @@ func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
||||||
// This is helpful when attempting to find a file that is in the same layer or lower as another file. For the
|
// This is helpful when attempting to find a file that is in the same layer or lower as another stereoscopeFile. For the
|
||||||
// directoryResolver, this is a simple path lookup.
|
// directoryResolver, this is a simple path lookup.
|
||||||
func (r *directoryResolver) RelativeFileByPath(_ Location, path string) *Location {
|
func (r *directoryResolver) RelativeFileByPath(_ file.Location, path string) *file.Location {
|
||||||
paths, err := r.FilesByPath(path)
|
paths, err := r.FilesByPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -420,59 +421,60 @@ func (r *directoryResolver) RelativeFileByPath(_ Location, path string) *Locatio
|
|||||||
|
|
||||||
// FileContentsByLocation fetches file contents for a single file reference relative to a directory.
|
// FileContentsByLocation fetches file contents for a single file reference relative to a directory.
|
||||||
// If the path does not exist an error is returned.
|
// If the path does not exist an error is returned.
|
||||||
func (r directoryResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
func (r directoryResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
|
||||||
if location.ref.RealPath == "" {
|
if location.Ref().RealPath == "" {
|
||||||
return nil, errors.New("empty path given")
|
return nil, errors.New("empty path given")
|
||||||
}
|
}
|
||||||
if !r.isInIndex(location) {
|
if !r.isInIndex(location) {
|
||||||
// this is in cases where paths have been explicitly excluded from the tree index. In which case
|
// this is in cases where paths have been explicitly excluded from the tree index. In which case
|
||||||
// we should DENY all content requests. Why? These paths have been indicated to be inaccessible (either
|
// we should DENY all content requests. Why? These paths have been indicated to be inaccessible (either
|
||||||
// by preference or these files are not readable by the current user).
|
// by preference or these files are not readable by the current user).
|
||||||
return nil, fmt.Errorf("file content is inaccessible path=%q", location.ref.RealPath)
|
return nil, fmt.Errorf("file content is inaccessible path=%q", location.Ref().RealPath)
|
||||||
}
|
}
|
||||||
// RealPath is posix so for windows directory resolver we need to translate
|
// RealPath is posix so for windows directory resolver we need to translate
|
||||||
// to its true on disk path.
|
// to its true on disk path.
|
||||||
filePath := string(location.ref.RealPath)
|
filePath := string(location.Ref().RealPath)
|
||||||
if runtime.GOOS == WindowsOS {
|
if runtime.GOOS == WindowsOS {
|
||||||
filePath = posixToWindows(filePath)
|
filePath = posixToWindows(filePath)
|
||||||
}
|
}
|
||||||
return file.NewLazyReadCloser(filePath), nil
|
return stereoscopeFile.NewLazyReadCloser(filePath), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r directoryResolver) isInIndex(location Location) bool {
|
func (r directoryResolver) isInIndex(location file.Location) bool {
|
||||||
if location.ref.RealPath == "" {
|
if location.Ref().RealPath == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return r.fileTree.HasPath(location.ref.RealPath, filetree.FollowBasenameLinks)
|
return r.fileTree.HasPath(location.Ref().RealPath, filetree.FollowBasenameLinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *directoryResolver) AllLocations() <-chan Location {
|
func (r *directoryResolver) AllLocations() <-chan file.Location {
|
||||||
results := make(chan Location)
|
results := make(chan file.Location)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(results)
|
defer close(results)
|
||||||
// this should be all non-directory types
|
// this should be all non-directory types
|
||||||
for _, ref := range r.fileTree.AllFiles(file.TypeReg, file.TypeSymlink, file.TypeHardLink, file.TypeBlockDevice, file.TypeCharacterDevice, file.TypeFifo) {
|
for _, ref := range r.fileTree.AllFiles(stereoscopeFile.TypeReg, stereoscopeFile.TypeSymlink, stereoscopeFile.TypeHardLink, stereoscopeFile.TypeBlockDevice, stereoscopeFile.TypeCharacterDevice, stereoscopeFile.TypeFifo) {
|
||||||
results <- NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref)
|
results <- file.NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *directoryResolver) FileMetadataByLocation(location Location) (FileMetadata, error) {
|
func (r *directoryResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
|
||||||
metadata, exists := r.metadata[location.ref.ID()]
|
ref := location.Ref()
|
||||||
|
metadata, exists := r.metadata[ref.ID()]
|
||||||
if !exists {
|
if !exists {
|
||||||
return FileMetadata{}, fmt.Errorf("location: %+v : %w", location, os.ErrNotExist)
|
return file.Metadata{}, fmt.Errorf("location: %+v : %w", location, os.ErrNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata, nil
|
return metadata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *directoryResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
func (r *directoryResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
|
||||||
var locations []Location
|
var locations []file.Location
|
||||||
for _, ty := range types {
|
for _, ty := range types {
|
||||||
if refs, ok := r.refsByMIMEType[ty]; ok {
|
if refs, ok := r.refsByMIMEType[ty]; ok {
|
||||||
for _, ref := range refs {
|
for _, ref := range refs {
|
||||||
locations = append(locations, NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref))
|
locations = append(locations, file.NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,8 +517,8 @@ func isUnallowableFileType(_ string, info os.FileInfo) bool {
|
|||||||
// we can't filter out by filetype for non-existent files
|
// we can't filter out by filetype for non-existent files
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch newFileTypeFromMode(info.Mode()) {
|
switch file.NewFileTypeFromMode(info.Mode()) {
|
||||||
case CharacterDevice, Socket, BlockDevice, FIFONode, IrregularFile:
|
case file.CharacterDevice, file.Socket, file.BlockDevice, file.FIFONode, file.IrregularFile:
|
||||||
return true
|
return true
|
||||||
// note: symlinks that point to these files may still get by.
|
// note: symlinks that point to these files may still get by.
|
||||||
// We handle this later in processing to help prevent against infinite links traversal.
|
// We handle this later in processing to help prevent against infinite links traversal.
|
||||||
|
|||||||
@ -15,12 +15,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/wagoodman/go-progress"
|
"github.com/wagoodman/go-progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -466,7 +465,7 @@ func Test_directoryResolver_index(t *testing.T) {
|
|||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
p := file.Path(path.Join(cwd, test.path))
|
p := stereoscopeFile.Path(path.Join(cwd, test.path))
|
||||||
assert.Equal(t, true, r.fileTree.HasPath(p))
|
assert.Equal(t, true, r.fileTree.HasPath(p))
|
||||||
exists, ref, err := r.fileTree.File(p)
|
exists, ref, err := r.fileTree.File(p)
|
||||||
assert.Equal(t, true, exists)
|
assert.Equal(t, true, exists)
|
||||||
@ -735,20 +734,20 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
location Location
|
location file.Location
|
||||||
expects string
|
expects string
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "use file reference for content requests",
|
name: "use file reference for content requests",
|
||||||
location: NewLocationFromDirectory("some/place", file.Reference{
|
location: file.NewLocationFromDirectory("some/place", stereoscopeFile.Reference{
|
||||||
RealPath: file.Path(filepath.Join(cwd, "test-fixtures/image-simple/file-1.txt")),
|
RealPath: stereoscopeFile.Path(filepath.Join(cwd, "test-fixtures/image-simple/file-1.txt")),
|
||||||
}),
|
}),
|
||||||
expects: "this file has contents",
|
expects: "this file has contents",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error on empty file reference",
|
name: "error on empty file reference",
|
||||||
location: NewLocationFromDirectory("doesn't matter", file.Reference{}),
|
location: file.NewLocationFromDirectory("doesn't matter", stereoscopeFile.Reference{}),
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -823,12 +822,12 @@ func Test_SymlinkLoopWithGlobsShouldResolve(t *testing.T) {
|
|||||||
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-loop")
|
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-loop")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
locations, err := resolver.FilesByGlob("**/file.target")
|
locations, err := resolver.FilesByGlob("**/stereoscopeFile.target")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// Note: I'm not certain that this behavior is correct, but it is not an infinite loop (which is the point of the test)
|
// Note: I'm not certain that this behavior is correct, but it is not an infinite loop (which is the point of the test)
|
||||||
// - block/loop0/file.target
|
// - block/loop0/stereoscopeFile.target
|
||||||
// - devices/loop0/file.target
|
// - devices/loop0/stereoscopeFile.target
|
||||||
// - devices/loop0/subsystem/loop0/file.target
|
// - devices/loop0/subsystem/loop0/stereoscopeFile.target
|
||||||
assert.Len(t, locations, 3)
|
assert.Len(t, locations, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -857,7 +856,7 @@ func Test_IncludeRootPathInIndex(t *testing.T) {
|
|||||||
resolver, err := newDirectoryResolver("/", filterFn)
|
resolver, err := newDirectoryResolver("/", filterFn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
exists, ref, err := resolver.fileTree.File(file.Path("/"))
|
exists, ref, err := resolver.fileTree.File(stereoscopeFile.Path("/"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, ref)
|
require.NotNil(t, ref)
|
||||||
assert.True(t, exists)
|
assert.True(t, exists)
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package source
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
type excludeFn func(string) bool
|
type excludeFn func(string) bool
|
||||||
@ -23,16 +25,16 @@ func NewExcludingResolver(delegate FileResolver, excludeFn excludeFn) FileResolv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
func (r *excludingResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
|
||||||
if locationMatches(&location, r.excludeFn) {
|
if locationMatches(&location, r.excludeFn) {
|
||||||
return nil, fmt.Errorf("no such location: %+v", location.RealPath)
|
return nil, fmt.Errorf("no such location: %+v", location.RealPath)
|
||||||
}
|
}
|
||||||
return r.delegate.FileContentsByLocation(location)
|
return r.delegate.FileContentsByLocation(location)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) FileMetadataByLocation(location Location) (FileMetadata, error) {
|
func (r *excludingResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
|
||||||
if locationMatches(&location, r.excludeFn) {
|
if locationMatches(&location, r.excludeFn) {
|
||||||
return FileMetadata{}, fmt.Errorf("no such location: %+v", location.RealPath)
|
return file.Metadata{}, fmt.Errorf("no such location: %+v", location.RealPath)
|
||||||
}
|
}
|
||||||
return r.delegate.FileMetadataByLocation(location)
|
return r.delegate.FileMetadataByLocation(location)
|
||||||
}
|
}
|
||||||
@ -44,22 +46,22 @@ func (r *excludingResolver) HasPath(path string) bool {
|
|||||||
return r.delegate.HasPath(path)
|
return r.delegate.HasPath(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) FilesByPath(paths ...string) ([]Location, error) {
|
func (r *excludingResolver) FilesByPath(paths ...string) ([]file.Location, error) {
|
||||||
locations, err := r.delegate.FilesByPath(paths...)
|
locations, err := r.delegate.FilesByPath(paths...)
|
||||||
return filterLocations(locations, err, r.excludeFn)
|
return filterLocations(locations, err, r.excludeFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
func (r *excludingResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||||
locations, err := r.delegate.FilesByGlob(patterns...)
|
locations, err := r.delegate.FilesByGlob(patterns...)
|
||||||
return filterLocations(locations, err, r.excludeFn)
|
return filterLocations(locations, err, r.excludeFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
func (r *excludingResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
|
||||||
locations, err := r.delegate.FilesByMIMEType(types...)
|
locations, err := r.delegate.FilesByMIMEType(types...)
|
||||||
return filterLocations(locations, err, r.excludeFn)
|
return filterLocations(locations, err, r.excludeFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) RelativeFileByPath(location Location, path string) *Location {
|
func (r *excludingResolver) RelativeFileByPath(location file.Location, path string) *file.Location {
|
||||||
l := r.delegate.RelativeFileByPath(location, path)
|
l := r.delegate.RelativeFileByPath(location, path)
|
||||||
if l != nil && locationMatches(l, r.excludeFn) {
|
if l != nil && locationMatches(l, r.excludeFn) {
|
||||||
return nil
|
return nil
|
||||||
@ -67,8 +69,8 @@ func (r *excludingResolver) RelativeFileByPath(location Location, path string) *
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *excludingResolver) AllLocations() <-chan Location {
|
func (r *excludingResolver) AllLocations() <-chan file.Location {
|
||||||
c := make(chan Location)
|
c := make(chan file.Location)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(c)
|
defer close(c)
|
||||||
for location := range r.delegate.AllLocations() {
|
for location := range r.delegate.AllLocations() {
|
||||||
@ -80,11 +82,11 @@ func (r *excludingResolver) AllLocations() <-chan Location {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationMatches(location *Location, exclusionFn excludeFn) bool {
|
func locationMatches(location *file.Location, exclusionFn excludeFn) bool {
|
||||||
return exclusionFn(location.RealPath) || exclusionFn(location.VirtualPath)
|
return exclusionFn(location.RealPath) || exclusionFn(location.VirtualPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterLocations(locations []Location, err error, exclusionFn excludeFn) ([]Location, error) {
|
func filterLocations(locations []file.Location, err error, exclusionFn excludeFn) ([]file.Location, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ func TestExcludingResolver(t *testing.T) {
|
|||||||
locations, _ = excludingResolver.FilesByMIMEType()
|
locations, _ = excludingResolver.FilesByMIMEType()
|
||||||
assert.ElementsMatch(t, locationPaths(locations), test.expected)
|
assert.ElementsMatch(t, locationPaths(locations), test.expected)
|
||||||
|
|
||||||
locations = []Location{}
|
locations = []file.Location{}
|
||||||
|
|
||||||
channel := excludingResolver.AllLocations()
|
channel := excludingResolver.AllLocations()
|
||||||
for location := range channel {
|
for location := range channel {
|
||||||
@ -119,18 +118,17 @@ func difference(a, b []string) []string {
|
|||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeLocation(path string) Location {
|
func makeLocation(path string) file.Location {
|
||||||
return Location{
|
return file.Location{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: path,
|
RealPath: path,
|
||||||
FileSystemID: "",
|
FileSystemID: "",
|
||||||
},
|
},
|
||||||
VirtualPath: "",
|
VirtualPath: "",
|
||||||
ref: file.Reference{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func locationPaths(locations []Location) []string {
|
func locationPaths(locations []file.Location) []string {
|
||||||
paths := []string{}
|
paths := []string{}
|
||||||
for _, l := range locations {
|
for _, l := range locations {
|
||||||
paths = append(paths, l.RealPath)
|
paths = append(paths, l.RealPath)
|
||||||
@ -142,20 +140,20 @@ type mockResolver struct {
|
|||||||
locations []string
|
locations []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) getLocations() ([]Location, error) {
|
func (r *mockResolver) getLocations() ([]file.Location, error) {
|
||||||
out := []Location{}
|
out := []file.Location{}
|
||||||
for _, path := range r.locations {
|
for _, path := range r.locations {
|
||||||
out = append(out, makeLocation(path))
|
out = append(out, makeLocation(path))
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) FileContentsByLocation(_ Location) (io.ReadCloser, error) {
|
func (r *mockResolver) FileContentsByLocation(_ file.Location) (io.ReadCloser, error) {
|
||||||
return io.NopCloser(strings.NewReader("Hello, world!")), nil
|
return io.NopCloser(strings.NewReader("Hello, world!")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) FileMetadataByLocation(_ Location) (FileMetadata, error) {
|
func (r *mockResolver) FileMetadataByLocation(_ file.Location) (file.Metadata, error) {
|
||||||
return FileMetadata{
|
return file.Metadata{
|
||||||
LinkDestination: "MOCK",
|
LinkDestination: "MOCK",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -164,28 +162,28 @@ func (r *mockResolver) HasPath(_ string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) FilesByPath(_ ...string) ([]Location, error) {
|
func (r *mockResolver) FilesByPath(_ ...string) ([]file.Location, error) {
|
||||||
return r.getLocations()
|
return r.getLocations()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) FilesByGlob(_ ...string) ([]Location, error) {
|
func (r *mockResolver) FilesByGlob(_ ...string) ([]file.Location, error) {
|
||||||
return r.getLocations()
|
return r.getLocations()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) FilesByMIMEType(_ ...string) ([]Location, error) {
|
func (r *mockResolver) FilesByMIMEType(_ ...string) ([]file.Location, error) {
|
||||||
return r.getLocations()
|
return r.getLocations()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) RelativeFileByPath(_ Location, path string) *Location {
|
func (r *mockResolver) RelativeFileByPath(_ file.Location, path string) *file.Location {
|
||||||
return &Location{
|
return &file.Location{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: path,
|
RealPath: path,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mockResolver) AllLocations() <-chan Location {
|
func (r *mockResolver) AllLocations() <-chan file.Location {
|
||||||
c := make(chan Location)
|
c := make(chan file.Location)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(c)
|
defer close(c)
|
||||||
locations, _ := r.getLocations()
|
locations, _ := r.getLocations()
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package source
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileResolver is an interface that encompasses how to get specific file references and file contents for a generic data source.
|
// FileResolver is an interface that encompasses how to get specific file references and file contents for a generic data source.
|
||||||
@ -14,11 +16,11 @@ type FileResolver interface {
|
|||||||
|
|
||||||
// FileContentResolver knows how to get file content for a given Location
|
// FileContentResolver knows how to get file content for a given Location
|
||||||
type FileContentResolver interface {
|
type FileContentResolver interface {
|
||||||
FileContentsByLocation(Location) (io.ReadCloser, error)
|
FileContentsByLocation(file.Location) (io.ReadCloser, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileMetadataResolver interface {
|
type FileMetadataResolver interface {
|
||||||
FileMetadataByLocation(Location) (FileMetadata, error)
|
FileMetadataByLocation(file.Location) (file.Metadata, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilePathResolver knows how to get a Location for given string paths and globs
|
// FilePathResolver knows how to get a Location for given string paths and globs
|
||||||
@ -26,16 +28,16 @@ type FilePathResolver interface {
|
|||||||
// HasPath indicates if the given path exists in the underlying source.
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
HasPath(string) bool
|
HasPath(string) bool
|
||||||
// FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches)
|
// FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches)
|
||||||
FilesByPath(paths ...string) ([]Location, error)
|
FilesByPath(paths ...string) ([]file.Location, error)
|
||||||
// FilesByGlob fetches a set of file references which the given glob matches
|
// FilesByGlob fetches a set of file references which the given glob matches
|
||||||
FilesByGlob(patterns ...string) ([]Location, error)
|
FilesByGlob(patterns ...string) ([]file.Location, error)
|
||||||
// FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types
|
// FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types
|
||||||
FilesByMIMEType(types ...string) ([]Location, error)
|
FilesByMIMEType(types ...string) ([]file.Location, error)
|
||||||
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
||||||
// This is helpful when attempting to find a file that is in the same layer or lower as another file.
|
// This is helpful when attempting to find a file that is in the same layer or lower as another file.
|
||||||
RelativeFileByPath(_ Location, path string) *Location
|
RelativeFileByPath(_ file.Location, path string) *file.Location
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileLocationResolver interface {
|
type FileLocationResolver interface {
|
||||||
AllLocations() <-chan Location
|
AllLocations() <-chan file.Location
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/filetree"
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ FileResolver = (*imageSquashResolver)(nil)
|
var _ FileResolver = (*imageSquashResolver)(nil)
|
||||||
@ -30,17 +31,17 @@ func newImageSquashResolver(img *image.Image) (*imageSquashResolver, error) {
|
|||||||
|
|
||||||
// HasPath indicates if the given path exists in the underlying source.
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
func (r *imageSquashResolver) HasPath(path string) bool {
|
func (r *imageSquashResolver) HasPath(path string) bool {
|
||||||
return r.img.SquashedTree().HasPath(file.Path(path))
|
return r.img.SquashedTree().HasPath(stereoscopeFile.Path(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByPath returns all file.References that match the given paths within the squashed representation of the image.
|
// FilesByPath returns all stereoscopeFile.References that match the given paths within the squashed representation of the image.
|
||||||
func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) {
|
func (r *imageSquashResolver) FilesByPath(paths ...string) ([]file.Location, error) {
|
||||||
uniqueFileIDs := file.NewFileReferenceSet()
|
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
|
||||||
uniqueLocations := make([]Location, 0)
|
uniqueLocations := make([]file.Location, 0)
|
||||||
|
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
tree := r.img.SquashedTree()
|
tree := r.img.SquashedTree()
|
||||||
_, ref, err := tree.File(file.Path(path), filetree.FollowBasenameLinks)
|
_, ref, err := tree.File(stereoscopeFile.Path(path), filetree.FollowBasenameLinks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -70,17 +71,17 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) {
|
|||||||
|
|
||||||
if resolvedRef != nil && !uniqueFileIDs.Contains(*resolvedRef) {
|
if resolvedRef != nil && !uniqueFileIDs.Contains(*resolvedRef) {
|
||||||
uniqueFileIDs.Add(*resolvedRef)
|
uniqueFileIDs.Add(*resolvedRef)
|
||||||
uniqueLocations = append(uniqueLocations, NewLocationFromImage(path, *resolvedRef, r.img))
|
uniqueLocations = append(uniqueLocations, file.NewLocationFromImage(path, *resolvedRef, r.img))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uniqueLocations, nil
|
return uniqueLocations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByGlob returns all file.References that match the given path glob pattern within the squashed representation of the image.
|
// FilesByGlob returns all stereoscopeFile.References that match the given path glob pattern within the squashed representation of the image.
|
||||||
func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||||
uniqueFileIDs := file.NewFileReferenceSet()
|
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
|
||||||
uniqueLocations := make([]Location, 0)
|
uniqueLocations := make([]file.Location, 0)
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
results, err := r.img.SquashedTree().FilesByGlob(pattern, filetree.FollowBasenameLinks)
|
results, err := r.img.SquashedTree().FilesByGlob(pattern, filetree.FollowBasenameLinks)
|
||||||
@ -109,8 +110,8 @@ func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error
|
|||||||
return nil, fmt.Errorf("failed to find files by path (result=%+v): %w", result, err)
|
return nil, fmt.Errorf("failed to find files by path (result=%+v): %w", result, err)
|
||||||
}
|
}
|
||||||
for _, resolvedLocation := range resolvedLocations {
|
for _, resolvedLocation := range resolvedLocations {
|
||||||
if !uniqueFileIDs.Contains(resolvedLocation.ref) {
|
if !uniqueFileIDs.Contains(resolvedLocation.Ref()) {
|
||||||
uniqueFileIDs.Add(resolvedLocation.ref)
|
uniqueFileIDs.Add(resolvedLocation.Ref())
|
||||||
uniqueLocations = append(uniqueLocations, resolvedLocation)
|
uniqueLocations = append(uniqueLocations, resolvedLocation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,9 +122,9 @@ func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
||||||
// This is helpful when attempting to find a file that is in the same layer or lower as another file. For the
|
// This is helpful when attempting to find a file that is in the same layer or lower as another stereoscopeFile. For the
|
||||||
// imageSquashResolver, this is a simple path lookup.
|
// imageSquashResolver, this is a simple path lookup.
|
||||||
func (r *imageSquashResolver) RelativeFileByPath(_ Location, path string) *Location {
|
func (r *imageSquashResolver) RelativeFileByPath(_ file.Location, path string) *file.Location {
|
||||||
paths, err := r.FilesByPath(path)
|
paths, err := r.FilesByPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -137,8 +138,8 @@ func (r *imageSquashResolver) RelativeFileByPath(_ Location, path string) *Locat
|
|||||||
|
|
||||||
// FileContentsByLocation fetches file contents for a single file reference, irregardless of the source layer.
|
// FileContentsByLocation fetches file contents for a single file reference, irregardless of the source layer.
|
||||||
// If the path does not exist an error is returned.
|
// If the path does not exist an error is returned.
|
||||||
func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
func (r *imageSquashResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
|
||||||
entry, err := r.img.FileCatalog.Get(location.ref)
|
entry, err := r.img.FileCatalog.Get(location.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
|
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
|
||||||
}
|
}
|
||||||
@ -161,34 +162,34 @@ func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.Read
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.img.FileContentsByRef(location.ref)
|
return r.img.FileContentsByRef(location.Ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageSquashResolver) AllLocations() <-chan Location {
|
func (r *imageSquashResolver) AllLocations() <-chan file.Location {
|
||||||
results := make(chan Location)
|
results := make(chan file.Location)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(results)
|
defer close(results)
|
||||||
for _, ref := range r.img.SquashedTree().AllFiles(file.AllTypes...) {
|
for _, ref := range r.img.SquashedTree().AllFiles(stereoscopeFile.AllTypes...) {
|
||||||
results <- NewLocationFromImage(string(ref.RealPath), ref, r.img)
|
results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
|
||||||
refs, err := r.img.FilesByMIMETypeFromSquash(types...)
|
refs, err := r.img.FilesByMIMETypeFromSquash(types...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var locations []Location
|
var locations []file.Location
|
||||||
for _, ref := range refs {
|
for _, ref := range refs {
|
||||||
locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img))
|
locations = append(locations, file.NewLocationFromImage(string(ref.RealPath), ref, r.img))
|
||||||
}
|
}
|
||||||
|
|
||||||
return locations, nil
|
return locations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageSquashResolver) FileMetadataByLocation(location Location) (FileMetadata, error) {
|
func (r *imageSquashResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
|
||||||
return fileMetadataByLocation(r.img, location)
|
return file.MetadataByLocation(r.img, location)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package source
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
@ -106,15 +107,15 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
|||||||
|
|
||||||
actual := refs[0]
|
actual := refs[0]
|
||||||
|
|
||||||
if string(actual.ref.RealPath) != c.resolvePath {
|
if string(actual.Ref().RealPath) != c.resolvePath {
|
||||||
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.ref.RealPath), c.resolvePath)
|
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.Ref().RealPath), c.resolvePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.resolvePath != "" && string(actual.ref.RealPath) != actual.RealPath {
|
if c.resolvePath != "" && string(actual.Ref().RealPath) != actual.RealPath {
|
||||||
t.Errorf("we should always prefer real paths over ones with links")
|
t.Errorf("we should always prefer real paths over ones with links")
|
||||||
}
|
}
|
||||||
|
|
||||||
entry, err := img.FileCatalog.Get(actual.ref)
|
entry, err := img.FileCatalog.Get(actual.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get metadata: %+v", err)
|
t.Fatalf("failed to get metadata: %+v", err)
|
||||||
}
|
}
|
||||||
@ -211,15 +212,15 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) {
|
|||||||
|
|
||||||
actual := refs[0]
|
actual := refs[0]
|
||||||
|
|
||||||
if string(actual.ref.RealPath) != c.resolvePath {
|
if string(actual.Ref().RealPath) != c.resolvePath {
|
||||||
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.ref.RealPath), c.resolvePath)
|
t.Errorf("bad resolve path: '%s'!='%s'", string(actual.Ref().RealPath), c.resolvePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.resolvePath != "" && string(actual.ref.RealPath) != actual.RealPath {
|
if c.resolvePath != "" && string(actual.Ref().RealPath) != actual.RealPath {
|
||||||
t.Errorf("we should always prefer real paths over ones with links")
|
t.Errorf("we should always prefer real paths over ones with links")
|
||||||
}
|
}
|
||||||
|
|
||||||
entry, err := img.FileCatalog.Get(actual.ref)
|
entry, err := img.FileCatalog.Get(actual.Ref())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get metadata: %+v", err)
|
t.Fatalf("failed to get metadata: %+v", err)
|
||||||
}
|
}
|
||||||
@ -347,56 +348,56 @@ func TestSquashImageResolver_FilesContents(t *testing.T) {
|
|||||||
func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
runner func(FileResolver) []Location
|
runner func(FileResolver) []file.Location
|
||||||
expected []Location
|
expected []file.Location
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "by mimetype",
|
name: "by mimetype",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// links should not show up when searching mimetype
|
// links should not show up when searching mimetype
|
||||||
actualLocations, err := resolver.FilesByMIMEType("text/plain")
|
actualLocations, err := resolver.FilesByMIMEType("text/plain")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/etc/group",
|
RealPath: "/etc/group",
|
||||||
},
|
},
|
||||||
VirtualPath: "/etc/group",
|
VirtualPath: "/etc/group",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/etc/passwd",
|
RealPath: "/etc/passwd",
|
||||||
},
|
},
|
||||||
VirtualPath: "/etc/passwd",
|
VirtualPath: "/etc/passwd",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/etc/shadow",
|
RealPath: "/etc/shadow",
|
||||||
},
|
},
|
||||||
VirtualPath: "/etc/shadow",
|
VirtualPath: "/etc/shadow",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-1.txt",
|
RealPath: "/file-1.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-1.txt",
|
VirtualPath: "/file-1.txt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-3.txt",
|
RealPath: "/file-3.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-3.txt",
|
VirtualPath: "/file-3.txt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/file-2.txt",
|
VirtualPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/parent/file-4.txt",
|
RealPath: "/parent/file-4.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/parent/file-4.txt",
|
VirtualPath: "/parent/file-4.txt",
|
||||||
@ -405,27 +406,27 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "by glob",
|
name: "by glob",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// links are searched, but resolve to the real files
|
// links are searched, but resolve to the real files
|
||||||
actualLocations, err := resolver.FilesByGlob("*ink-*")
|
actualLocations, err := resolver.FilesByGlob("*ink-*")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-3.txt",
|
RealPath: "/file-3.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-within",
|
VirtualPath: "/link-within",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-2",
|
VirtualPath: "/link-2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-1.txt",
|
RealPath: "/file-1.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-1",
|
VirtualPath: "/link-1",
|
||||||
@ -434,16 +435,16 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "by path to degree 1 link",
|
name: "by path to degree 1 link",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// links resolve to the final file
|
// links resolve to the final file
|
||||||
actualLocations, err := resolver.FilesByPath("/link-2")
|
actualLocations, err := resolver.FilesByPath("/link-2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
// we have multiple copies across layers
|
// we have multiple copies across layers
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-2",
|
VirtualPath: "/link-2",
|
||||||
@ -452,16 +453,16 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "by path to degree 2 link",
|
name: "by path to degree 2 link",
|
||||||
runner: func(resolver FileResolver) []Location {
|
runner: func(resolver FileResolver) []file.Location {
|
||||||
// multiple links resolves to the final file
|
// multiple links resolves to the final file
|
||||||
actualLocations, err := resolver.FilesByPath("/link-indirect")
|
actualLocations, err := resolver.FilesByPath("/link-indirect")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return actualLocations
|
return actualLocations
|
||||||
},
|
},
|
||||||
expected: []Location{
|
expected: []file.Location{
|
||||||
// we have multiple copies across layers
|
// we have multiple copies across layers
|
||||||
{
|
{
|
||||||
Coordinates: Coordinates{
|
Coordinates: file.Coordinates{
|
||||||
RealPath: "/file-2.txt",
|
RealPath: "/file-2.txt",
|
||||||
},
|
},
|
||||||
VirtualPath: "/link-indirect",
|
VirtualPath: "/link-indirect",
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package source
|
|||||||
|
|
||||||
// Metadata represents any static source data that helps describe "what" was cataloged.
|
// Metadata represents any static source data that helps describe "what" was cataloged.
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
Scheme Scheme // the source data scheme type (directory or image)
|
Scheme Type // the source data scheme type (directory or image)
|
||||||
ImageMetadata ImageMetadata // all image info (image only)
|
ImageMetadata ImageMetadata // all image info (image only)
|
||||||
Path string // the root path to be cataloged (directory only)
|
Path string // the root path to be cataloged (directory only)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/bmatcuk/doublestar/v4"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,28 +15,28 @@ var _ FileResolver = (*MockResolver)(nil)
|
|||||||
// It provides an implementation that can resolve local filesystem paths using only a provided discrete list of file
|
// It provides an implementation that can resolve local filesystem paths using only a provided discrete list of file
|
||||||
// paths, which are typically paths to test fixtures.
|
// paths, which are typically paths to test fixtures.
|
||||||
type MockResolver struct {
|
type MockResolver struct {
|
||||||
locations []Location
|
locations []file.Location
|
||||||
metadata map[Location]FileMetadata
|
metadata map[file.Location]file.Metadata
|
||||||
mimeTypeIndex map[string][]Location
|
mimeTypeIndex map[string][]file.Location
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockResolverForPaths creates a new MockResolver, where the only resolvable
|
// NewMockResolverForPaths creates a new MockResolver, where the only resolvable
|
||||||
// files are those specified by the supplied paths.
|
// files are those specified by the supplied paths.
|
||||||
func NewMockResolverForPaths(paths ...string) *MockResolver {
|
func NewMockResolverForPaths(paths ...string) *MockResolver {
|
||||||
var locations []Location
|
var locations []file.Location
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
locations = append(locations, NewLocation(p))
|
locations = append(locations, file.NewLocation(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MockResolver{
|
return &MockResolver{
|
||||||
locations: locations,
|
locations: locations,
|
||||||
metadata: make(map[Location]FileMetadata),
|
metadata: make(map[file.Location]file.Metadata),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockResolverForPathsWithMetadata(metadata map[Location]FileMetadata) *MockResolver {
|
func NewMockResolverForPathsWithMetadata(metadata map[file.Location]file.Metadata) *MockResolver {
|
||||||
var locations []Location
|
var locations []file.Location
|
||||||
var mimeTypeIndex = make(map[string][]Location)
|
var mimeTypeIndex = make(map[string][]file.Location)
|
||||||
for l, m := range metadata {
|
for l, m := range metadata {
|
||||||
locations = append(locations, l)
|
locations = append(locations, l)
|
||||||
mimeTypeIndex[m.MIMEType] = append(mimeTypeIndex[m.MIMEType], l)
|
mimeTypeIndex[m.MIMEType] = append(mimeTypeIndex[m.MIMEType], l)
|
||||||
@ -65,7 +66,7 @@ func (r MockResolver) String() string {
|
|||||||
|
|
||||||
// FileContentsByLocation fetches file contents for a single location. If the
|
// FileContentsByLocation fetches file contents for a single location. If the
|
||||||
// path does not exist, an error is returned.
|
// path does not exist, an error is returned.
|
||||||
func (r MockResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
func (r MockResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
|
||||||
for _, l := range r.locations {
|
for _, l := range r.locations {
|
||||||
if l == location {
|
if l == location {
|
||||||
return os.Open(location.RealPath)
|
return os.Open(location.RealPath)
|
||||||
@ -76,12 +77,12 @@ func (r MockResolver) FileContentsByLocation(location Location) (io.ReadCloser,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilesByPath returns all Locations that match the given paths.
|
// FilesByPath returns all Locations that match the given paths.
|
||||||
func (r MockResolver) FilesByPath(paths ...string) ([]Location, error) {
|
func (r MockResolver) FilesByPath(paths ...string) ([]file.Location, error) {
|
||||||
var results []Location
|
var results []file.Location
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
for _, location := range r.locations {
|
for _, location := range r.locations {
|
||||||
if p == location.RealPath {
|
if p == location.RealPath {
|
||||||
results = append(results, NewLocation(p))
|
results = append(results, file.NewLocation(p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,8 +91,8 @@ func (r MockResolver) FilesByPath(paths ...string) ([]Location, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilesByGlob returns all Locations that match the given path glob pattern.
|
// FilesByGlob returns all Locations that match the given path glob pattern.
|
||||||
func (r MockResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
func (r MockResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||||
var results []Location
|
var results []file.Location
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
for _, location := range r.locations {
|
for _, location := range r.locations {
|
||||||
matches, err := doublestar.Match(pattern, location.RealPath)
|
matches, err := doublestar.Match(pattern, location.RealPath)
|
||||||
@ -108,7 +109,7 @@ func (r MockResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RelativeFileByPath returns a single Location for the given path.
|
// RelativeFileByPath returns a single Location for the given path.
|
||||||
func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
|
func (r MockResolver) RelativeFileByPath(_ file.Location, path string) *file.Location {
|
||||||
paths, err := r.FilesByPath(path)
|
paths, err := r.FilesByPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -121,8 +122,8 @@ func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
|
|||||||
return &paths[0]
|
return &paths[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r MockResolver) AllLocations() <-chan Location {
|
func (r MockResolver) AllLocations() <-chan file.Location {
|
||||||
results := make(chan Location)
|
results := make(chan file.Location)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(results)
|
defer close(results)
|
||||||
for _, l := range r.locations {
|
for _, l := range r.locations {
|
||||||
@ -132,19 +133,19 @@ func (r MockResolver) AllLocations() <-chan Location {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r MockResolver) FileMetadataByLocation(l Location) (FileMetadata, error) {
|
func (r MockResolver) FileMetadataByLocation(l file.Location) (file.Metadata, error) {
|
||||||
info, err := os.Stat(l.RealPath)
|
info, err := os.Stat(l.RealPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FileMetadata{}, err
|
return file.Metadata{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// other types not supported
|
// other types not supported
|
||||||
ty := RegularFile
|
ty := file.RegularFile
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
ty = Directory
|
ty = file.Directory
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileMetadata{
|
return file.Metadata{
|
||||||
Mode: info.Mode(),
|
Mode: info.Mode(),
|
||||||
Type: ty,
|
Type: ty,
|
||||||
UserID: 0, // not supported
|
UserID: 0, // not supported
|
||||||
@ -153,8 +154,8 @@ func (r MockResolver) FileMetadataByLocation(l Location) (FileMetadata, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r MockResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
func (r MockResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
|
||||||
var locations []Location
|
var locations []file.Location
|
||||||
for _, ty := range types {
|
for _, ty := range types {
|
||||||
locations = append(r.mimeTypeIndex[ty], locations...)
|
locations = append(r.mimeTypeIndex[ty], locations...)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,73 +0,0 @@
|
|||||||
package source
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scheme represents the optional prefixed string at the beginning of a user request (e.g. "docker:").
|
|
||||||
type Scheme string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// UnknownScheme is the default scheme
|
|
||||||
UnknownScheme Scheme = "UnknownScheme"
|
|
||||||
// DirectoryScheme indicates the source being cataloged is a directory on the root filesystem
|
|
||||||
DirectoryScheme Scheme = "DirectoryScheme"
|
|
||||||
// ImageScheme indicates the source being cataloged is a container image
|
|
||||||
ImageScheme Scheme = "ImageScheme"
|
|
||||||
// FileScheme indicates the source being cataloged is a single file
|
|
||||||
FileScheme Scheme = "FileScheme"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AllSchemes = []Scheme{
|
|
||||||
DirectoryScheme,
|
|
||||||
ImageScheme,
|
|
||||||
FileScheme,
|
|
||||||
}
|
|
||||||
|
|
||||||
func DetectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (Scheme, image.Source, string, error) {
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(userInput, "dir:"):
|
|
||||||
dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:"))
|
|
||||||
if err != nil {
|
|
||||||
return UnknownScheme, image.UnknownSource, "", fmt.Errorf("unable to expand directory path: %w", err)
|
|
||||||
}
|
|
||||||
return DirectoryScheme, image.UnknownSource, dirLocation, nil
|
|
||||||
|
|
||||||
case strings.HasPrefix(userInput, "file:"):
|
|
||||||
fileLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "file:"))
|
|
||||||
if err != nil {
|
|
||||||
return UnknownScheme, image.UnknownSource, "", fmt.Errorf("unable to expand directory path: %w", err)
|
|
||||||
}
|
|
||||||
return FileScheme, image.UnknownSource, fileLocation, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// try the most specific sources first and move out towards more generic sources.
|
|
||||||
|
|
||||||
// first: let's try the image detector, which has more scheme parsing internal to stereoscope
|
|
||||||
source, imageSpec, err := imageDetector(userInput)
|
|
||||||
if err == nil && source != image.UnknownSource {
|
|
||||||
return ImageScheme, source, imageSpec, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// next: let's try more generic sources (dir, file, etc.)
|
|
||||||
location, err := homedir.Expand(userInput)
|
|
||||||
if err != nil {
|
|
||||||
return UnknownScheme, image.UnknownSource, "", fmt.Errorf("unable to expand potential directory path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileMeta, err := fs.Stat(location)
|
|
||||||
if err != nil {
|
|
||||||
return UnknownScheme, source, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileMeta.IsDir() {
|
|
||||||
return DirectoryScheme, source, location, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return FileScheme, source, location, nil
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user