mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 10:36:45 +01:00
feat: Add --name option to override name in output (#1269)
This commit is contained in:
parent
949cff158d
commit
10f43d75e0
@ -124,7 +124,7 @@ func parseAttestationOutput(outputs []string) (format string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseImageSource(userInput string, app *config.Application) (s *source.Input, err error) {
|
func parseImageSource(userInput string, app *config.Application) (s *source.Input, err error) {
|
||||||
si, err := source.ParseInput(userInput, app.Platform, false)
|
si, err := source.ParseInputWithName(userInput, app.Platform, false, app.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not generate source input for attest command: %w", err)
|
return nil, fmt.Errorf("could not generate source input for attest command: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ type PackagesOptions struct {
|
|||||||
Platform string
|
Platform string
|
||||||
Exclude []string
|
Exclude []string
|
||||||
Catalogers []string
|
Catalogers []string
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Interface = (*PackagesOptions)(nil)
|
var _ Interface = (*PackagesOptions)(nil)
|
||||||
@ -47,6 +48,9 @@ func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
|||||||
cmd.Flags().StringArrayVarP(&o.Catalogers, "catalogers", "", nil,
|
cmd.Flags().StringArrayVarP(&o.Catalogers, "catalogers", "", nil,
|
||||||
"enable one or more package catalogers")
|
"enable one or more package catalogers")
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&o.Name, "name", "", "",
|
||||||
|
"set the name of the target being analyzed")
|
||||||
|
|
||||||
return bindPackageConfigOptions(cmd.Flags(), v)
|
return bindPackageConfigOptions(cmd.Flags(), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +73,10 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := v.BindPFlag("name", flags.Lookup("name")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := v.BindPFlag("output", flags.Lookup("output")); err != nil {
|
if err := v.BindPFlag("output", flags.Lookup("output")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
|
|||||||
|
|
||||||
// could be an image or a directory, with or without a scheme
|
// could be an image or a directory, with or without a scheme
|
||||||
userInput := args[0]
|
userInput := args[0]
|
||||||
si, err := source.ParseInput(userInput, app.Platform, true)
|
si, err := source.ParseInputWithName(userInput, app.Platform, true, app.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not generate source input for packages command: %w", err)
|
return fmt.Errorf("could not generate source input for packages command: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
userInput := args[0]
|
userInput := args[0]
|
||||||
si, err := source.ParseInput(userInput, app.Platform, true)
|
si, err := source.ParseInputWithName(userInput, app.Platform, true, app.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not generate source input for packages command: %w", err)
|
return fmt.Errorf("could not generate source input for packages command: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,6 +55,7 @@ type Application struct {
|
|||||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||||
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
|
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
|
||||||
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
||||||
|
Name string `yaml:"name" json:"name" mapstructure:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg Application) ToCatalogerConfig() cataloger.Config {
|
func (cfg Application) ToCatalogerConfig() cataloger.Config {
|
||||||
|
|||||||
@ -159,8 +159,12 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
||||||
|
name := srcMetadata.Name
|
||||||
switch srcMetadata.Scheme {
|
switch srcMetadata.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageScheme:
|
||||||
|
if name == "" {
|
||||||
|
name = srcMetadata.ImageMetadata.UserInput
|
||||||
|
}
|
||||||
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)
|
||||||
@ -168,10 +172,13 @@ func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component
|
|||||||
return &cyclonedx.Component{
|
return &cyclonedx.Component{
|
||||||
BOMRef: string(bomRef),
|
BOMRef: string(bomRef),
|
||||||
Type: cyclonedx.ComponentTypeContainer,
|
Type: cyclonedx.ComponentTypeContainer,
|
||||||
Name: srcMetadata.ImageMetadata.UserInput,
|
Name: name,
|
||||||
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
||||||
}
|
}
|
||||||
case source.DirectoryScheme, source.FileScheme:
|
case source.DirectoryScheme, source.FileScheme:
|
||||||
|
if name == "" {
|
||||||
|
name = srcMetadata.Path
|
||||||
|
}
|
||||||
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)
|
||||||
@ -179,7 +186,7 @@ func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component
|
|||||||
return &cyclonedx.Component{
|
return &cyclonedx.Component{
|
||||||
BOMRef: string(bomRef),
|
BOMRef: string(bomRef),
|
||||||
Type: cyclonedx.ComponentTypeFile,
|
Type: cyclonedx.ComponentTypeFile,
|
||||||
Name: srcMetadata.Path,
|
Name: name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func DocumentName(srcMetadata source.Metadata) string {
|
func DocumentName(srcMetadata source.Metadata) string {
|
||||||
|
if srcMetadata.Name != "" {
|
||||||
|
return cleanName(srcMetadata.Name)
|
||||||
|
}
|
||||||
|
|
||||||
switch srcMetadata.Scheme {
|
switch srcMetadata.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageScheme:
|
||||||
return cleanName(srcMetadata.ImageMetadata.UserInput)
|
return cleanName(srcMetadata.ImageMetadata.UserInput)
|
||||||
|
|||||||
@ -6,4 +6,5 @@ type Metadata struct {
|
|||||||
Scheme Scheme // the source data scheme type (directory or image)
|
Scheme Scheme // 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)
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,12 +44,19 @@ type Input struct {
|
|||||||
ImageSource image.Source
|
ImageSource image.Source
|
||||||
Location string
|
Location string
|
||||||
Platform string
|
Platform string
|
||||||
|
Name string
|
||||||
autoDetectAvailableImageSources bool
|
autoDetectAvailableImageSources bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseInput generates a source Input that can be used as an argument to generate a new source
|
// ParseInput generates a source Input that can be used as an argument to generate a new source
|
||||||
// from specific providers including a registry.
|
// from specific providers including a registry.
|
||||||
func ParseInput(userInput string, platform string, detectAvailableImageSources bool) (*Input, error) {
|
func ParseInput(userInput string, platform string, detectAvailableImageSources bool) (*Input, error) {
|
||||||
|
return ParseInputWithName(userInput, platform, detectAvailableImageSources, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseInputWithName generates a source Input that can be used as an argument to generate a new source
|
||||||
|
// from specific providers including a registry, with an explicit name.
|
||||||
|
func ParseInputWithName(userInput string, platform string, detectAvailableImageSources bool, name string) (*Input, error) {
|
||||||
fs := afero.NewOsFs()
|
fs := afero.NewOsFs()
|
||||||
scheme, source, location, err := DetectScheme(fs, image.DetectSource, userInput)
|
scheme, source, location, err := DetectScheme(fs, image.DetectSource, userInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -86,6 +93,7 @@ func ParseInput(userInput string, platform string, detectAvailableImageSources b
|
|||||||
ImageSource: source,
|
ImageSource: source,
|
||||||
Location: location,
|
Location: location,
|
||||||
Platform: platform,
|
Platform: platform,
|
||||||
|
Name: name,
|
||||||
autoDetectAvailableImageSources: detectAvailableImageSources,
|
autoDetectAvailableImageSources: detectAvailableImageSources,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -109,9 +117,9 @@ func New(in Input, registryOptions *image.RegistryOptions, exclusions []string)
|
|||||||
|
|
||||||
switch in.Scheme {
|
switch in.Scheme {
|
||||||
case FileScheme:
|
case FileScheme:
|
||||||
source, cleanupFn, err = generateFileSource(fs, in.Location)
|
source, cleanupFn, err = generateFileSource(fs, in)
|
||||||
case DirectoryScheme:
|
case DirectoryScheme:
|
||||||
source, cleanupFn, err = generateDirectorySource(fs, in.Location)
|
source, cleanupFn, err = generateDirectorySource(fs, in)
|
||||||
case ImageScheme:
|
case ImageScheme:
|
||||||
source, cleanupFn, err = generateImageSource(in, registryOptions)
|
source, cleanupFn, err = generateImageSource(in, registryOptions)
|
||||||
default:
|
default:
|
||||||
@ -131,7 +139,7 @@ func generateImageSource(in Input, registryOptions *image.RegistryOptions) (*Sou
|
|||||||
return nil, cleanup, fmt.Errorf("could not fetch image %q: %w", in.Location, err)
|
return nil, cleanup, fmt.Errorf("could not fetch image %q: %w", in.Location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := NewFromImage(img, in.Location)
|
s, err := NewFromImageWithName(img, in.Location, in.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cleanup, fmt.Errorf("could not populate source with image: %w", err)
|
return nil, cleanup, fmt.Errorf("could not populate source with image: %w", err)
|
||||||
}
|
}
|
||||||
@ -206,44 +214,50 @@ func getImageWithRetryStrategy(in Input, registryOptions *image.RegistryOptions)
|
|||||||
return img, cleanup, err
|
return img, cleanup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateDirectorySource(fs afero.Fs, location string) (*Source, func(), error) {
|
func generateDirectorySource(fs afero.Fs, in Input) (*Source, func(), error) {
|
||||||
fileMeta, err := fs.Stat(location)
|
fileMeta, err := fs.Stat(in.Location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err)
|
return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", in.Location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fileMeta.IsDir() {
|
if !fileMeta.IsDir() {
|
||||||
return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", location, err)
|
return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := NewFromDirectory(location)
|
s, err := NewFromDirectoryWithName(in.Location, in.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, func() {}, fmt.Errorf("could not populate source from path=%q: %w", location, err)
|
return nil, func() {}, fmt.Errorf("could not populate source from path=%q: %w", in.Location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &s, func() {}, nil
|
return &s, func() {}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFileSource(fs afero.Fs, location string) (*Source, func(), error) {
|
func generateFileSource(fs afero.Fs, in Input) (*Source, func(), error) {
|
||||||
fileMeta, err := fs.Stat(location)
|
fileMeta, err := fs.Stat(in.Location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err)
|
return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", in.Location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileMeta.IsDir() {
|
if fileMeta.IsDir() {
|
||||||
return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", location, err)
|
return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, cleanupFn := NewFromFile(location)
|
s, cleanupFn := NewFromFileWithName(in.Location, in.Name)
|
||||||
|
|
||||||
return &s, cleanupFn, nil
|
return &s, cleanupFn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively.
|
// NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively.
|
||||||
func NewFromDirectory(path string) (Source, error) {
|
func NewFromDirectory(path string) (Source, error) {
|
||||||
|
return NewFromDirectoryWithName(path, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromDirectoryWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name.
|
||||||
|
func NewFromDirectoryWithName(path string, name string) (Source, error) {
|
||||||
s := Source{
|
s := Source{
|
||||||
mutex: &sync.Mutex{},
|
mutex: &sync.Mutex{},
|
||||||
Metadata: Metadata{
|
Metadata: Metadata{
|
||||||
|
Name: name,
|
||||||
Scheme: DirectoryScheme,
|
Scheme: DirectoryScheme,
|
||||||
Path: path,
|
Path: path,
|
||||||
},
|
},
|
||||||
@ -255,11 +269,17 @@ func NewFromDirectory(path string) (Source, error) {
|
|||||||
|
|
||||||
// NewFromFile creates a new source object tailored to catalog a file.
|
// NewFromFile creates a new source object tailored to catalog a file.
|
||||||
func NewFromFile(path string) (Source, func()) {
|
func NewFromFile(path string) (Source, func()) {
|
||||||
|
return NewFromFileWithName(path, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromFileWithName creates a new source object tailored to catalog a file, with an explicitly provided name.
|
||||||
|
func NewFromFileWithName(path string, name string) (Source, func()) {
|
||||||
analysisPath, cleanupFn := fileAnalysisPath(path)
|
analysisPath, cleanupFn := fileAnalysisPath(path)
|
||||||
|
|
||||||
s := Source{
|
s := Source{
|
||||||
mutex: &sync.Mutex{},
|
mutex: &sync.Mutex{},
|
||||||
Metadata: Metadata{
|
Metadata: Metadata{
|
||||||
|
Name: name,
|
||||||
Scheme: FileScheme,
|
Scheme: FileScheme,
|
||||||
Path: path,
|
Path: path,
|
||||||
},
|
},
|
||||||
@ -299,6 +319,12 @@ func fileAnalysisPath(path string) (string, func()) {
|
|||||||
// NewFromImage creates a new source object tailored to catalog a given container image, relative to the
|
// NewFromImage creates a new source object tailored to catalog a given container image, relative to the
|
||||||
// option given (e.g. all-layers, squashed, etc)
|
// option given (e.g. all-layers, squashed, etc)
|
||||||
func NewFromImage(img *image.Image, userImageStr string) (Source, error) {
|
func NewFromImage(img *image.Image, userImageStr string) (Source, error) {
|
||||||
|
return NewFromImageWithName(img, userImageStr, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromImageWithName creates a new source object tailored to catalog a given container image, relative to the
|
||||||
|
// option given (e.g. all-layers, squashed, etc), with an explicit name.
|
||||||
|
func NewFromImageWithName(img *image.Image, userImageStr string, name string) (Source, error) {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
return Source{}, fmt.Errorf("no image given")
|
return Source{}, fmt.Errorf("no image given")
|
||||||
}
|
}
|
||||||
@ -306,6 +332,7 @@ func NewFromImage(img *image.Image, userImageStr string) (Source, error) {
|
|||||||
s := Source{
|
s := Source{
|
||||||
Image: img,
|
Image: img,
|
||||||
Metadata: Metadata{
|
Metadata: Metadata{
|
||||||
|
Name: name,
|
||||||
Scheme: ImageScheme,
|
Scheme: ImageScheme,
|
||||||
ImageMetadata: NewImageMetadata(img, userImageStr),
|
ImageMetadata: NewImageMetadata(img, userImageStr),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -120,7 +120,7 @@ func TestSetID(t *testing.T) {
|
|||||||
Path: "test-fixtures/image-simple",
|
Path: "test-fixtures/image-simple",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: artifact.ID("26e8f4daad203793"),
|
expected: artifact.ID("14b60020c4f9955"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user