mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 14:02:26 +01:00
* [wip] add containerd UI handlers Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * Add containerd support - Add UI handlers (done by @wagoodman) - Add containerd types and wrappers (done by @wagoodman) - Add flag for specifying containerd address Closes #201 Signed-off-by: Shane Dell <shanedell100@gmail.com> * Fix lint Signed-off-by: Shane Dell <shanedell100@gmail.com> * add containerd ui handler Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * add containerd scheme to readme Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * add test for scheme detection Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: Shane Dell <shanedell100@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <alex.goodman@anchore.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
208 lines
5.5 KiB
Go
208 lines
5.5 KiB
Go
package source
|
|
|
|
import (
|
|
"crypto"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/mitchellh/go-homedir"
|
|
"github.com/spf13/afero"
|
|
|
|
"github.com/anchore/stereoscope/pkg/image"
|
|
)
|
|
|
|
type detectedType string
|
|
|
|
const (
|
|
// unknownType is the default scheme
|
|
unknownType detectedType = "unknown-type"
|
|
|
|
// directoryType indicates the source being cataloged is a directory on the root filesystem
|
|
directoryType detectedType = "directory-type"
|
|
|
|
// containerImageType indicates the source being cataloged is a container image
|
|
containerImageType detectedType = "container-image-type"
|
|
|
|
// fileType indicates the source being cataloged is a single file
|
|
fileType detectedType = "file-type"
|
|
)
|
|
|
|
type sourceResolver func(string) (image.Source, string, error)
|
|
|
|
// Detection is an object that captures the detected user input regarding source location, scheme, and provider type.
|
|
// It acts as a struct input for some source constructors.
|
|
type Detection struct {
|
|
detectedType detectedType
|
|
imageSource image.Source
|
|
location string
|
|
}
|
|
|
|
func (d Detection) IsContainerImage() bool {
|
|
return d.detectedType == containerImageType
|
|
}
|
|
|
|
type DetectConfig struct {
|
|
DefaultImageSource string
|
|
}
|
|
|
|
func DefaultDetectConfig() DetectConfig {
|
|
return DetectConfig{}
|
|
}
|
|
|
|
// Detect generates a source Detection that can be used as an argument to generate a new source
|
|
// from specific providers including a registry, with an explicit name.
|
|
func Detect(userInput string, cfg DetectConfig) (*Detection, error) {
|
|
fs := afero.NewOsFs()
|
|
ty, src, location, err := detect(fs, image.DetectSource, userInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if src == image.UnknownSource {
|
|
// only run for these two schemes
|
|
// only check on packages command, attest we automatically try to pull from userInput
|
|
switch ty {
|
|
case containerImageType, unknownType:
|
|
ty = containerImageType
|
|
location = userInput
|
|
if cfg.DefaultImageSource != "" {
|
|
src = parseDefaultImageSource(cfg.DefaultImageSource)
|
|
} else {
|
|
src = image.DetermineDefaultImagePullSource(userInput)
|
|
}
|
|
}
|
|
}
|
|
|
|
// collect user input for downstream consumption
|
|
return &Detection{
|
|
detectedType: ty,
|
|
imageSource: src,
|
|
location: location,
|
|
}, nil
|
|
}
|
|
|
|
type DetectionSourceConfig struct {
|
|
Alias Alias
|
|
RegistryOptions *image.RegistryOptions
|
|
Platform *image.Platform
|
|
Exclude ExcludeConfig
|
|
DigestAlgorithms []crypto.Hash
|
|
BasePath string
|
|
}
|
|
|
|
func DefaultDetectionSourceConfig() DetectionSourceConfig {
|
|
return DetectionSourceConfig{
|
|
DigestAlgorithms: []crypto.Hash{
|
|
crypto.SHA256,
|
|
},
|
|
}
|
|
}
|
|
|
|
// NewSource produces a Source based on userInput like dir: or image:tag
|
|
func (d Detection) NewSource(cfg DetectionSourceConfig) (Source, error) {
|
|
var err error
|
|
var src Source
|
|
|
|
if d.detectedType != containerImageType && cfg.Platform != nil {
|
|
return nil, fmt.Errorf("cannot specify a platform for a non-image source")
|
|
}
|
|
|
|
switch d.detectedType {
|
|
case fileType:
|
|
src, err = NewFromFile(
|
|
FileConfig{
|
|
Path: d.location,
|
|
Exclude: cfg.Exclude,
|
|
DigestAlgorithms: cfg.DigestAlgorithms,
|
|
Alias: cfg.Alias,
|
|
},
|
|
)
|
|
case directoryType:
|
|
base := cfg.BasePath
|
|
if base == "" {
|
|
base = d.location
|
|
}
|
|
src, err = NewFromDirectory(
|
|
DirectoryConfig{
|
|
Path: d.location,
|
|
Base: base,
|
|
Exclude: cfg.Exclude,
|
|
Alias: cfg.Alias,
|
|
},
|
|
)
|
|
case containerImageType:
|
|
src, err = NewFromStereoscopeImage(
|
|
StereoscopeImageConfig{
|
|
Reference: d.location,
|
|
From: d.imageSource,
|
|
Platform: cfg.Platform,
|
|
RegistryOptions: cfg.RegistryOptions,
|
|
Exclude: cfg.Exclude,
|
|
Alias: cfg.Alias,
|
|
},
|
|
)
|
|
default:
|
|
err = fmt.Errorf("unable to process input for scanning")
|
|
}
|
|
|
|
return src, err
|
|
}
|
|
|
|
func detect(fs afero.Fs, imageSourceResolver sourceResolver, userInput string) (detectedType, image.Source, string, error) {
|
|
switch {
|
|
case strings.HasPrefix(userInput, "dir:"):
|
|
dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:"))
|
|
if err != nil {
|
|
return unknownType, image.UnknownSource, "", fmt.Errorf("unable to expand directory path: %w", err)
|
|
}
|
|
return directoryType, image.UnknownSource, dirLocation, nil
|
|
|
|
case strings.HasPrefix(userInput, "file:"):
|
|
fileLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "file:"))
|
|
if err != nil {
|
|
return unknownType, image.UnknownSource, "", fmt.Errorf("unable to expand directory path: %w", err)
|
|
}
|
|
return fileType, 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
|
|
src, imageSpec, err := imageSourceResolver(userInput)
|
|
if err == nil && src != image.UnknownSource {
|
|
return containerImageType, src, imageSpec, nil
|
|
}
|
|
|
|
// next: let's try more generic sources (dir, file, etc.)
|
|
location, err := homedir.Expand(userInput)
|
|
if err != nil {
|
|
return unknownType, image.UnknownSource, "", fmt.Errorf("unable to expand potential directory path: %w", err)
|
|
}
|
|
|
|
fileMeta, err := fs.Stat(location)
|
|
if err != nil {
|
|
return unknownType, src, "", nil
|
|
}
|
|
|
|
if fileMeta.IsDir() {
|
|
return directoryType, src, location, nil
|
|
}
|
|
|
|
return fileType, src, location, nil
|
|
}
|
|
|
|
func parseDefaultImageSource(defaultImageSource string) image.Source {
|
|
switch defaultImageSource {
|
|
case "registry":
|
|
return image.OciRegistrySource
|
|
case "docker":
|
|
return image.DockerDaemonSource
|
|
case "podman":
|
|
return image.PodmanDaemonSource
|
|
case "containerd":
|
|
return image.ContainerdDaemonSource
|
|
default:
|
|
return image.UnknownSource
|
|
}
|
|
}
|