feat: Add --name option to override name in output (#1269)

This commit is contained in:
Justin Chadwell 2022-11-10 19:03:23 +00:00 committed by GitHub
parent 949cff158d
commit 10f43d75e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 68 additions and 20 deletions

View File

@ -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)
} }

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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,
} }
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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),
}, },

View File

@ -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"),
}, },
} }