Skip to content

Commit

Permalink
CLI flag for directory base
Browse files Browse the repository at this point in the history
Signed-off-by: Avi Deitcher <avi@deitcher.net>
  • Loading branch information
deitch committed Jun 6, 2023
1 parent 1764e1c commit e25965a
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 31 deletions.
7 changes: 6 additions & 1 deletion cmd/syft/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ func Run(_ context.Context, app *config.Application, args []string) error {
// could be an image or a directory, with or without a scheme
// TODO: validate that source is image
userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
si, err := source.ParseInput(userInput,
source.WithPlatform(app.Platform),
source.WithName(app.SourceName),
source.WithVersion(app.SourceVersion),
source.WithDefaultImageSource(app.DefaultImagePullSource),
)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/syft/cli/options/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type PackagesOptions struct {
Catalogers []string
SourceName string
SourceVersion string
BasePath string
}

var _ Interface = (*PackagesOptions)(nil)
Expand Down Expand Up @@ -59,6 +60,9 @@ func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
cmd.Flags().StringVarP(&o.SourceVersion, "source-version", "", "",
"set the name of the target being analyzed")

cmd.Flags().StringVarP(&o.BasePath, "base-path", "", "",
"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")

return bindPackageConfigOptions(cmd.Flags(), v)
}

Expand Down Expand Up @@ -106,5 +110,9 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
return err
}

if err := v.BindPFlag("base-path", flags.Lookup("base-path")); err != nil {
return err
}

return nil
}
8 changes: 7 additions & 1 deletion cmd/syft/cli/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ func Run(_ context.Context, app *config.Application, args []string) error {

// could be an image or a directory, with or without a scheme
userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
si, err := source.ParseInput(userInput,
source.WithPlatform(app.Platform),
source.WithName(app.SourceName),
source.WithVersion(app.SourceVersion),
source.WithDefaultImageSource(app.DefaultImagePullSource),
source.WithBasePath(app.BasePath),
)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}
Expand Down
7 changes: 6 additions & 1 deletion cmd/syft/cli/poweruser/poweruser.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ func Run(_ context.Context, app *config.Application, args []string) error {
}()

userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
si, err := source.ParseInput(userInput,
source.WithPlatform(app.Platform),
source.WithName(app.SourceName),
source.WithVersion(app.SourceVersion),
source.WithDefaultImageSource(app.DefaultImagePullSource),
)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions internal/config/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type Application struct {
SourceVersion string `yaml:"source-version" json:"source-version" mapstructure:"source-version"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
}

func (cfg Application) ToCatalogerConfig() cataloger.Config {
Expand Down
45 changes: 45 additions & 0 deletions syft/source/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package source

type sourceOpt struct {
name string
version string
platform string
defaultImageSource string
base string
}
type Option func(*sourceOpt) error

func WithName(name string) Option {
return func(s *sourceOpt) error {
s.name = name
return nil
}
}

func WithPlatform(platform string) Option {
return func(s *sourceOpt) error {
s.platform = platform
return nil
}
}

func WithVersion(version string) Option {
return func(s *sourceOpt) error {
s.version = version
return nil
}
}

func WithDefaultImageSource(defaultImageSource string) Option {
return func(s *sourceOpt) error {
s.defaultImageSource = defaultImageSource
return nil
}
}

func WithBasePath(base string) Option {
return func(s *sourceOpt) error {
s.base = base
return nil
}
}
46 changes: 24 additions & 22 deletions syft/source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,18 @@ type Input struct {
Platform string
Name string
Version string
BasePath string
}

// ParseInput generates a source Input that can be used as an argument to generate a new source
// from specific providers including a registry.
func ParseInput(userInput string, platform string) (*Input, error) {
return ParseInputWithName(userInput, platform, "", "")
}

// 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, name, defaultImageSource string) (*Input, error) {
return ParseInputWithNameVersion(userInput, platform, name, "", defaultImageSource)
}

// ParseInputWithNameVersion 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 and version.
func ParseInputWithNameVersion(userInput, platform, name, version, defaultImageSource string) (*Input, error) {
func ParseInput(userInput string, opts ...Option) (*Input, error) {
opt := &sourceOpt{}
for _, o := range opts {
if err := o(opt); err != nil {
return nil, err
}
}
fs := afero.NewOsFs()
scheme, source, location, err := DetectScheme(fs, image.DetectSource, userInput)
if err != nil {
Expand All @@ -79,8 +74,8 @@ func ParseInputWithNameVersion(userInput, platform, name, version, defaultImageS
case ImageScheme, UnknownScheme:
scheme = ImageScheme
location = userInput
if defaultImageSource != "" {
source = parseDefaultImageSource(defaultImageSource)
if opt.defaultImageSource != "" {
source = parseDefaultImageSource(opt.defaultImageSource)
} else {
imagePullSource := image.DetermineDefaultImagePullSource(userInput)
source = imagePullSource
Expand All @@ -92,20 +87,22 @@ func ParseInputWithNameVersion(userInput, platform, name, version, defaultImageS
}
}

if scheme != ImageScheme && platform != "" {
if scheme != ImageScheme && opt.platform != "" {
return nil, fmt.Errorf("cannot specify a platform for a non-image source")
}

// collect user input for downstream consumption
return &Input{
in := &Input{
UserInput: userInput,
Scheme: scheme,
ImageSource: source,
Location: location,
Platform: platform,
Name: name,
Version: version,
}, nil
Platform: opt.platform,
Name: opt.name,
Version: opt.version,
BasePath: opt.base,
}
return in, nil
}

func parseDefaultImageSource(defaultImageSource string) image.Source {
Expand Down Expand Up @@ -259,7 +256,12 @@ func generateDirectorySource(fs afero.Fs, in Input) (*Source, func(), error) {
return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err)
}

s, err := NewFromDirectoryWithNameVersion(in.Location, in.Name, in.Version)
var s Source
if in.BasePath != "" {
s, err = NewFromDirectoryRootWithNameVersion(in.Location, in.Name, in.Version)
} else {
s, err = NewFromDirectoryWithNameVersion(in.Location, in.Name, in.Version)
}
if err != nil {
return nil, func() {}, fmt.Errorf("could not populate source from path=%q: %w", in.Location, err)
}
Expand Down
6 changes: 3 additions & 3 deletions syft/source/source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestParseInput(t *testing.T) {
if test.errFn == nil {
test.errFn = require.NoError
}
sourceInput, err := ParseInput(test.input, test.platform)
sourceInput, err := ParseInput(test.input, WithPlatform(test.platform))
test.errFn(t, err)
if test.expected != "" {
require.NotNil(t, sourceInput)
Expand Down Expand Up @@ -596,7 +596,7 @@ func TestDirectoryExclusions(t *testing.T) {
registryOpts := &image.RegistryOptions{}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
sourceInput, err := ParseInput("dir:"+test.input, "")
sourceInput, err := ParseInput("dir:" + test.input)
require.NoError(t, err)
src, fn, err := New(*sourceInput, registryOpts, test.exclusions)
defer fn()
Expand Down Expand Up @@ -696,7 +696,7 @@ func TestImageExclusions(t *testing.T) {
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
archiveLocation := imagetest.PrepareFixtureImage(t, "docker-archive", test.input)
sourceInput, err := ParseInput(archiveLocation, "")
sourceInput, err := ParseInput(archiveLocation)
require.NoError(t, err)
src, fn, err := New(*sourceInput, registryOpts, test.exclusions)
defer fn()
Expand Down
2 changes: 1 addition & 1 deletion test/integration/catalog_packages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func BenchmarkImagePackageCatalogers(b *testing.B) {
for _, c := range cataloger.ImageCatalogers(cataloger.DefaultConfig()) {
// in case of future alteration where state is persisted, assume no dependency is safe to reuse
userInput := "docker-archive:" + tarPath
sourceInput, err := source.ParseInput(userInput, "")
sourceInput, err := source.ParseInput(userInput)
require.NoError(b, err)
theSource, cleanupSource, err := source.New(*sourceInput, nil, nil)
b.Cleanup(cleanupSource)
Expand Down
4 changes: 2 additions & 2 deletions test/integration/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco
imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName)
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
userInput := "docker-archive:" + tarPath
sourceInput, err := source.ParseInput(userInput, "")
sourceInput, err := source.ParseInput(userInput)
require.NoError(t, err)
theSource, cleanupSource, err := source.New(*sourceInput, nil, nil)
t.Cleanup(cleanupSource)
Expand Down Expand Up @@ -52,7 +52,7 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco

func catalogDirectory(t *testing.T, dir string) (sbom.SBOM, *source.Source) {
userInput := "dir:" + dir
sourceInput, err := source.ParseInput(userInput, "")
sourceInput, err := source.ParseInput(userInput)
require.NoError(t, err)
theSource, cleanupSource, err := source.New(*sourceInput, nil, nil)
t.Cleanup(cleanupSource)
Expand Down

0 comments on commit e25965a

Please sign in to comment.