feat: move file patterns to a global level to be able to use it on any analyzer (#2539)

This commit is contained in:
jerbob92
2022-09-01 10:01:57 +02:00
committed by GitHub
parent 2580ea1583
commit 5f0bf1445a
35 changed files with 269 additions and 298 deletions

View File

@@ -2,21 +2,3 @@
!!! hint
See also [Others](../../vulnerability/examples/others.md) in Vulnerability section.
## File patterns
When a directory is given as an input, Trivy will recursively look for and test all files based on file patterns.
The default file patterns are [here](../custom/index.md).
In addition to the default file patterns, the `--file-patterns` option takes regexp patterns to look for your files.
For example, it may be useful when your file name of Dockerfile doesn't match the default patterns.
This can be repeated for specifying multiple file patterns.
Allowed values are here:
- dockerfile
- yaml
- json
- toml
- hcl
For more details, see [an example](https://github.com/aquasecurity/trivy/tree/{{ git.commit }}/examples/misconf/file-patterns)

View File

@@ -82,6 +82,11 @@ Available in client/server mode
```yaml
scan:
# Same as '--file-patterns'
# Default is empty
file-patterns:
-
# Same as '--skip-dirs'
# Default is empty
skip-dirs:
@@ -195,11 +200,6 @@ Available with misconfiguration scanning
```yaml
misconfiguration:
# Same as '--file-patterns'
# Default is empty
file-patterns:
-
# Same as '--include-non-failures'
# Default is false
include-non-failures: false

View File

@@ -16,6 +16,22 @@ If your image contains lock files which are not maintained by you, you can skip
$ trivy image --skip-dirs /var/lib/gems/2.5.0/gems/fluent-plugin-detect-exceptions-0.0.13 --skip-dirs "/var/lib/gems/2.5.0/gems/http_parser.rb-0.6.0" quay.io/fluentd_elasticsearch/fluentd:v2.9.0
```
## File patterns
When a directory is given as an input, Trivy will recursively look for and test all files based on file patterns.
The default file patterns are [here](../custom/index.md).
In addition to the default file patterns, the `--file-patterns` option takes regexp patterns to look for your files.
For example, it may be useful when your file name of Dockerfile doesn't match the default patterns.
This can be repeated for specifying multiple file patterns.
A file pattern contains the analyzer it is used for, and the pattern itself, joined by a semicolon. For example:
```
--file-patterns "dockerfile:.*.docker" --file-patterns "yaml:deployment" --file-patterns "pip:requirements-.*\.txt"
```
For more details, see [an example](https://github.com/aquasecurity/trivy/tree/{{ git.commit }}/examples/misconf/file-patterns)
## Exit Code
By default, `Trivy` exits with code 0 even when vulnerabilities are detected.
Use the `--exit-code` option if you want to exit with a non-zero exit code.

View File

@@ -448,6 +448,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
ScanRemovedPackages: opts.ScanRemovedPkgs, // this is valid only for 'image' subcommand
ListAllPackages: opts.ListAllPkgs,
LicenseCategories: opts.LicenseCategories,
FilePatterns: opts.FilePatterns,
}
if slices.Contains(opts.SecurityChecks, types.SecurityCheckVulnerability) {
@@ -464,7 +465,6 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
Namespaces: append(opts.PolicyNamespaces, defaultPolicyNamespaces...),
PolicyPaths: opts.PolicyPaths,
DataPaths: opts.DataPaths,
FilePatterns: opts.FilePatterns,
HelmValues: opts.HelmValues,
HelmValueFiles: opts.HelmValueFiles,
HelmFileValues: opts.HelmFileValues,
@@ -504,6 +504,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
DisabledAnalyzers: disabledAnalyzers(opts),
SkipFiles: opts.SkipFiles,
SkipDirs: opts.SkipDirs,
FilePatterns: opts.FilePatterns,
InsecureSkipTLS: opts.Insecure,
Offline: opts.OfflineScan,
NoProgress: opts.NoProgress || opts.Quiet,

View File

@@ -3,6 +3,7 @@ package all
import (
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/buildinfo"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/command/apk"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dotnet/deps"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dotnet/nuget"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/binary"

View File

@@ -5,6 +5,7 @@ import (
"errors"
"io/fs"
"os"
"regexp"
"sort"
"strings"
"sync"
@@ -91,6 +92,7 @@ type Opener func() (dio.ReadSeekCloserAt, error)
type AnalyzerGroup struct {
analyzers []analyzer
configAnalyzers []configAnalyzer
filePatterns map[Type][]*regexp.Regexp
}
type AnalysisResult struct {
@@ -266,12 +268,36 @@ func belongToGroup(groupName Group, analyzerType Type, disabledAnalyzers []Type,
return true
}
func NewAnalyzerGroup(groupName Group, disabledAnalyzers []Type) AnalyzerGroup {
const separator = ":"
func NewAnalyzerGroup(groupName Group, disabledAnalyzers []Type, filePatterns []string) (AnalyzerGroup, error) {
if groupName == "" {
groupName = GroupBuiltin
}
var group AnalyzerGroup
group := AnalyzerGroup{
filePatterns: map[Type][]*regexp.Regexp{},
}
for _, p := range filePatterns {
// e.g. "dockerfile:my_dockerfile_*"
s := strings.SplitN(p, separator, 2)
if len(s) != 2 {
return group, xerrors.Errorf("invalid file pattern (%s)", p)
}
fileType, pattern := s[0], s[1]
r, err := regexp.Compile(pattern)
if err != nil {
return group, xerrors.Errorf("invalid file regexp (%s): %w", p, err)
}
if _, ok := group.filePatterns[Type(fileType)]; !ok {
group.filePatterns[Type(fileType)] = []*regexp.Regexp{}
}
group.filePatterns[Type(fileType)] = append(group.filePatterns[Type(fileType)], r)
}
for analyzerType, a := range analyzers {
if !belongToGroup(groupName, analyzerType, disabledAnalyzers, a) {
continue
@@ -286,7 +312,7 @@ func NewAnalyzerGroup(groupName Group, disabledAnalyzers []Type) AnalyzerGroup {
group.configAnalyzers = append(group.configAnalyzers, a)
}
return group
return group, nil
}
// AnalyzerVersions returns analyzer version identifier used for cache keys.
@@ -313,14 +339,16 @@ func (ag AnalyzerGroup) AnalyzeFile(ctx context.Context, wg *sync.WaitGroup, lim
return nil
}
// filepath extracted from tar file doesn't have the prefix "/"
cleanPath := strings.TrimLeft(filePath, "/")
for _, a := range ag.analyzers {
// Skip disabled analyzers
if slices.Contains(disabled, a.Type()) {
continue
}
// filepath extracted from tar file doesn't have the prefix "/"
if !a.Required(strings.TrimLeft(filePath, "/"), info) {
if !ag.filePatternMatch(a.Type(), cleanPath) && !a.Required(cleanPath, info) {
continue
}
rc, err := opener()
@@ -375,3 +403,12 @@ func (ag AnalyzerGroup) AnalyzeImageConfig(targetOS types.OS, configBlob []byte)
}
return nil
}
func (ag AnalyzerGroup) filePatternMatch(analyzerType Type, filePath string) bool {
for _, pattern := range ag.filePatterns[analyzerType] {
if pattern.MatchString(filePath) {
return true
}
}
return false
}

View File

@@ -281,6 +281,7 @@ func TestAnalyzeFile(t *testing.T) {
filePath string
testFilePath string
disabledAnalyzers []analyzer.Type
filePatterns []string
}
tests := []struct {
name string
@@ -377,6 +378,28 @@ func TestAnalyzeFile(t *testing.T) {
},
want: &analyzer.AnalysisResult{},
},
{
name: "happy path with library analyzer file pattern regex",
args: args{
filePath: "/app/Gemfile-dev.lock",
testFilePath: "testdata/app/Gemfile.lock",
filePatterns: []string{"bundler:Gemfile(-.*)?\\.lock"},
},
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: "bundler",
FilePath: "/app/Gemfile-dev.lock",
Libraries: []types.Package{
{
Name: "actioncable",
Version: "5.2.3",
},
},
},
},
},
},
{
name: "ignore permission error",
args: args{
@@ -393,6 +416,24 @@ func TestAnalyzeFile(t *testing.T) {
},
wantErr: "unable to open /lib/apk/db/installed",
},
{
name: "sad path with broken file pattern regex",
args: args{
filePath: "/app/Gemfile-dev.lock",
testFilePath: "testdata/app/Gemfile.lock",
filePatterns: []string{"bundler:Gemfile(-.*?\\.lock"},
},
wantErr: "error parsing regexp",
},
{
name: "sad path with broken file pattern",
args: args{
filePath: "/app/Gemfile-dev.lock",
testFilePath: "testdata/app/Gemfile.lock",
filePatterns: []string{"Gemfile(-.*)?\\.lock"},
},
wantErr: "invalid file pattern",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -400,7 +441,13 @@ func TestAnalyzeFile(t *testing.T) {
limit := semaphore.NewWeighted(3)
got := new(analyzer.AnalysisResult)
a := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.args.disabledAnalyzers)
a, err := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.args.disabledAnalyzers, tt.args.filePatterns)
if err != nil && tt.wantErr != "" {
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
require.NoError(t, err)
info, err := os.Stat(tt.args.testFilePath)
require.NoError(t, err)
@@ -440,6 +487,7 @@ func TestAnalyzeConfig(t *testing.T) {
targetOS types.OS
configBlob []byte
disabledAnalyzers []analyzer.Type
filePatterns []string
}
tests := []struct {
name string
@@ -482,7 +530,8 @@ func TestAnalyzeConfig(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.args.disabledAnalyzers)
a, err := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.args.disabledAnalyzers, tt.args.filePatterns)
require.NoError(t, err)
got := a.AnalyzeImageConfig(tt.args.targetOS, tt.args.configBlob)
assert.Equal(t, tt.want, got)
})
@@ -517,7 +566,8 @@ func TestAnalyzer_AnalyzerVersions(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.disabled)
a, err := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.disabled, nil)
require.NoError(t, err)
got := a.AnalyzerVersions()
fmt.Printf("%v\n", got)
assert.Equal(t, tt.want, got)
@@ -549,7 +599,8 @@ func TestAnalyzer_ImageConfigAnalyzerVersions(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.disabled)
a, err := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, tt.disabled, nil)
require.NoError(t, err)
got := a.ImageConfigAnalyzerVersions()
assert.Equal(t, tt.want, got)
})

View File

@@ -0,0 +1,9 @@
package all
import (
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/dockerfile"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/helm"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/json"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraform"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/yaml"
)

View File

@@ -1,29 +1,13 @@
package config
import (
"regexp"
"sort"
"strings"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/helm"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/dockerfile"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/json"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraform"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/yaml"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
const separator = ":"
type ScannerOption struct {
Trace bool
RegoOnly bool
Namespaces []string
FilePatterns []string
PolicyPaths []string
DataPaths []string
DisableEmbeddedPolicies bool
@@ -37,44 +21,6 @@ type ScannerOption struct {
func (o *ScannerOption) Sort() {
sort.Strings(o.Namespaces)
sort.Strings(o.FilePatterns)
sort.Strings(o.PolicyPaths)
sort.Strings(o.DataPaths)
}
func RegisterConfigAnalyzers(filePatterns []string) error {
var dockerRegexp, jsonRegexp, yamlRegexp, helmRegexp *regexp.Regexp
for _, p := range filePatterns {
// e.g. "dockerfile:my_dockerfile_*"
s := strings.SplitN(p, separator, 2)
if len(s) != 2 {
return xerrors.Errorf("invalid file pattern (%s)", p)
}
fileType, pattern := s[0], s[1]
r, err := regexp.Compile(pattern)
if err != nil {
return xerrors.Errorf("invalid file regexp (%s): %w", p, err)
}
switch fileType {
case types.Dockerfile:
dockerRegexp = r
case types.JSON:
jsonRegexp = r
case types.YAML:
yamlRegexp = r
case types.Helm:
helmRegexp = r
default:
return xerrors.Errorf("unknown file type: %s, pattern: %s", fileType, pattern)
}
}
analyzer.RegisterAnalyzer(dockerfile.NewConfigAnalyzer(dockerRegexp))
analyzer.RegisterAnalyzer(terraform.NewConfigAnalyzer())
analyzer.RegisterAnalyzer(json.NewConfigAnalyzer(jsonRegexp))
analyzer.RegisterAnalyzer(yaml.NewConfigAnalyzer(yamlRegexp))
analyzer.RegisterAnalyzer(helm.NewConfigAnalyzer(helmRegexp))
return nil
}

View File

@@ -10,10 +10,9 @@ import (
func TestScannerOption_Sort(t *testing.T) {
type fields struct {
Namespaces []string
FilePatterns []string
PolicyPaths []string
DataPaths []string
Namespaces []string
PolicyPaths []string
DataPaths []string
}
tests := []struct {
name string
@@ -23,25 +22,22 @@ func TestScannerOption_Sort(t *testing.T) {
{
name: "happy path",
fields: fields{
Namespaces: []string{"main", "custom", "default"},
FilePatterns: []string{"dockerfile:foo*", "yaml:yml_*"},
PolicyPaths: []string{"policy"},
DataPaths: []string{"data/b", "data/c", "data/a"},
Namespaces: []string{"main", "custom", "default"},
PolicyPaths: []string{"policy"},
DataPaths: []string{"data/b", "data/c", "data/a"},
},
want: config.ScannerOption{
Namespaces: []string{"custom", "default", "main"},
FilePatterns: []string{"dockerfile:foo*", "yaml:yml_*"},
PolicyPaths: []string{"policy"},
DataPaths: []string{"data/a", "data/b", "data/c"},
Namespaces: []string{"custom", "default", "main"},
PolicyPaths: []string{"policy"},
DataPaths: []string{"data/a", "data/b", "data/c"},
},
},
{
name: "missing some fields",
fields: fields{
Namespaces: []string{"main"},
FilePatterns: nil,
PolicyPaths: nil,
DataPaths: nil,
Namespaces: []string{"main"},
PolicyPaths: nil,
DataPaths: nil,
},
want: config.ScannerOption{
Namespaces: []string{"main"},
@@ -51,10 +47,9 @@ func TestScannerOption_Sort(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := config.ScannerOption{
Namespaces: tt.fields.Namespaces,
FilePatterns: tt.fields.FilePatterns,
PolicyPaths: tt.fields.PolicyPaths,
DataPaths: tt.fields.DataPaths,
Namespaces: tt.fields.Namespaces,
PolicyPaths: tt.fields.PolicyPaths,
DataPaths: tt.fields.DataPaths,
}
o.Sort()

View File

@@ -5,7 +5,6 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"strings"
"golang.org/x/xerrors"
@@ -14,21 +13,17 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
func init() {
analyzer.RegisterAnalyzer(&dockerConfigAnalyzer{})
}
const version = 1
var requiredFiles = []string{"Dockerfile", "Containerfile"}
type ConfigAnalyzer struct {
filePattern *regexp.Regexp
}
type dockerConfigAnalyzer struct{}
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
return ConfigAnalyzer{
filePattern: filePattern,
}
}
func (s ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
func (s dockerConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
b, err := io.ReadAll(input.Content)
if err != nil {
return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err)
@@ -50,11 +45,7 @@ func (s ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
// Required does a case-insensitive check for filePath and returns true if
// filePath equals/startsWith/hasExtension requiredFiles
func (s ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
if s.filePattern != nil && s.filePattern.MatchString(filePath) {
return true
}
func (s dockerConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
base := filepath.Base(filePath)
ext := filepath.Ext(base)
for _, file := range requiredFiles {
@@ -69,10 +60,10 @@ func (s ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return false
}
func (s ConfigAnalyzer) Type() analyzer.Type {
func (s dockerConfigAnalyzer) Type() analyzer.Type {
return analyzer.TypeDockerfile
}
func (s ConfigAnalyzer) Version() int {
func (s dockerConfigAnalyzer) Version() int {
return version
}

View File

@@ -1,16 +1,14 @@
package dockerfile_test
package dockerfile
import (
"context"
"os"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/dockerfile"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
@@ -68,7 +66,7 @@ COPY --from=build /bar /bar
require.NoError(t, err)
defer f.Close()
a := dockerfile.NewConfigAnalyzer(nil)
a := dockerConfigAnalyzer{}
ctx := context.Background()
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
FilePath: tt.inputFile,
@@ -88,10 +86,9 @@ COPY --from=build /bar /bar
func Test_dockerConfigAnalyzer_Required(t *testing.T) {
tests := []struct {
name string
filePattern *regexp.Regexp
filePath string
want bool
name string
filePath string
want bool
}{
{
name: "dockerfile",
@@ -143,16 +140,10 @@ func Test_dockerConfigAnalyzer_Required(t *testing.T) {
filePath: "deployment.json",
want: false,
},
{
name: "file pattern",
filePattern: regexp.MustCompile(`foo*`),
filePath: "foo_file",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := dockerfile.NewConfigAnalyzer(tt.filePattern)
s := dockerConfigAnalyzer{}
got := s.Required(tt.filePath, nil)
assert.Equal(t, tt.want, got)
})
@@ -160,7 +151,7 @@ func Test_dockerConfigAnalyzer_Required(t *testing.T) {
}
func Test_dockerConfigAnalyzer_Type(t *testing.T) {
s := dockerfile.NewConfigAnalyzer(nil)
s := dockerConfigAnalyzer{}
want := analyzer.TypeDockerfile
got := s.Type()
assert.Equal(t, want, got)

View File

@@ -8,7 +8,6 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"strings"
"golang.org/x/xerrors"
@@ -18,21 +17,17 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
func init() {
analyzer.RegisterAnalyzer(&helmConfigAnalyzer{})
}
const version = 1
const maxTarSize = 209_715_200 // 200MB
type ConfigAnalyzer struct {
filePattern *regexp.Regexp
}
type helmConfigAnalyzer struct{}
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
return ConfigAnalyzer{
filePattern: filePattern,
}
}
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
func (a helmConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
if isArchive(input.FilePath) {
if !isHelmChart(input.FilePath, input.Content) {
return nil, nil
@@ -62,11 +57,7 @@ func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
}, nil
}
func (a ConfigAnalyzer) Required(filePath string, info os.FileInfo) bool {
if a.filePattern != nil && a.filePattern.MatchString(filePath) {
return true
}
func (a helmConfigAnalyzer) Required(filePath string, info os.FileInfo) bool {
if info.Size() > maxTarSize {
// tarball is too big to be Helm chart - move on
return false
@@ -88,11 +79,11 @@ func (a ConfigAnalyzer) Required(filePath string, info os.FileInfo) bool {
return false
}
func (ConfigAnalyzer) Type() analyzer.Type {
func (helmConfigAnalyzer) Type() analyzer.Type {
return analyzer.TypeHelm
}
func (ConfigAnalyzer) Version() int {
func (helmConfigAnalyzer) Version() int {
return version
}

View File

@@ -3,7 +3,6 @@ package helm
import (
"context"
"os"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
@@ -340,7 +339,7 @@ affinity: {}
info, err := os.Stat(tt.inputFile)
require.NoError(t, err)
a := NewConfigAnalyzer(nil)
a := helmConfigAnalyzer{}
ctx := context.Background()
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
FilePath: tt.inputFile,
@@ -361,10 +360,9 @@ affinity: {}
func Test_helmConfigAnalyzer_Required(t *testing.T) {
tests := []struct {
name string
filePattern *regexp.Regexp
filePath string
want bool
name string
filePath string
want bool
}{
{
name: "yaml",
@@ -406,17 +404,10 @@ func Test_helmConfigAnalyzer_Required(t *testing.T) {
filePath: "testdata/nope.tgz",
want: true, // its a tarball after all
},
{
name: "file pattern",
filePattern: regexp.MustCompile(`foo*`),
filePath: "foo_file",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewConfigAnalyzer(tt.filePattern)
s := helmConfigAnalyzer{}
info, _ := os.Stat(tt.filePath)
@@ -427,7 +418,7 @@ func Test_helmConfigAnalyzer_Required(t *testing.T) {
}
func Test_helmConfigAnalyzer_Type(t *testing.T) {
s := NewConfigAnalyzer(nil)
s := helmConfigAnalyzer{}
want := analyzer.TypeHelm
got := s.Type()

View File

@@ -5,7 +5,6 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"golang.org/x/xerrors"
@@ -13,6 +12,10 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
func init() {
analyzer.RegisterAnalyzer(&jsonConfigAnalyzer{})
}
const version = 1
var (
@@ -20,17 +23,9 @@ var (
excludedFiles = []string{types.NpmPkgLock, types.NuGetPkgsLock, types.NuGetPkgsConfig}
)
type ConfigAnalyzer struct {
filePattern *regexp.Regexp
}
type jsonConfigAnalyzer struct{}
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
return ConfigAnalyzer{
filePattern: filePattern,
}
}
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
func (a jsonConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
b, err := io.ReadAll(input.Content)
if err != nil {
return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err)
@@ -50,11 +45,7 @@ func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
}, nil
}
func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
if a.filePattern != nil && a.filePattern.MatchString(filePath) {
return true
}
func (a jsonConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
filename := filepath.Base(filePath)
for _, excludedFile := range excludedFiles {
if filename == excludedFile {
@@ -65,10 +56,10 @@ func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return filepath.Ext(filePath) == requiredExt
}
func (ConfigAnalyzer) Type() analyzer.Type {
func (jsonConfigAnalyzer) Type() analyzer.Type {
return analyzer.TypeJSON
}
func (ConfigAnalyzer) Version() int {
func (jsonConfigAnalyzer) Version() int {
return version
}

View File

@@ -1,16 +1,14 @@
package json_test
package json
import (
"context"
"os"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/json"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
@@ -133,7 +131,7 @@ func Test_jsonConfigAnalyzer_Analyze(t *testing.T) {
require.NoError(t, err)
defer f.Close()
s := json.NewConfigAnalyzer(nil)
s := jsonConfigAnalyzer{}
ctx := context.Background()
got, err := s.Analyze(ctx, analyzer.AnalysisInput{
@@ -154,10 +152,9 @@ func Test_jsonConfigAnalyzer_Analyze(t *testing.T) {
func Test_jsonConfigAnalyzer_Required(t *testing.T) {
tests := []struct {
name string
filePattern *regexp.Regexp
filePath string
want bool
name string
filePath string
want bool
}{
{
name: "json",
@@ -174,16 +171,10 @@ func Test_jsonConfigAnalyzer_Required(t *testing.T) {
filePath: "package-lock.json",
want: false,
},
{
name: "file pattern",
filePattern: regexp.MustCompile(`foo*`),
filePath: "foo_file",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := json.NewConfigAnalyzer(tt.filePattern)
s := jsonConfigAnalyzer{}
got := s.Required(tt.filePath, nil)
assert.Equal(t, tt.want, got)
@@ -192,7 +183,7 @@ func Test_jsonConfigAnalyzer_Required(t *testing.T) {
}
func Test_jsonConfigAnalyzer_Type(t *testing.T) {
s := json.NewConfigAnalyzer(nil)
s := jsonConfigAnalyzer{}
want := analyzer.TypeJSON
got := s.Type()

View File

@@ -13,19 +13,18 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
func init() {
analyzer.RegisterAnalyzer(&terraformConfigAnalyzer{})
}
const version = 1
var requiredExts = []string{".tf", ".tf.json"}
type ConfigAnalyzer struct {
}
func NewConfigAnalyzer() ConfigAnalyzer {
return ConfigAnalyzer{}
}
type terraformConfigAnalyzer struct{}
// Analyze returns a name of Terraform file
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
func (a terraformConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
b, err := io.ReadAll(input.Content)
if err != nil {
return nil, xerrors.Errorf("read error (%s): %w", input.FilePath, err)
@@ -44,14 +43,14 @@ func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
}, nil
}
func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
func (a terraformConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return slices.Contains(requiredExts, filepath.Ext(filePath))
}
func (ConfigAnalyzer) Type() analyzer.Type {
func (terraformConfigAnalyzer) Type() analyzer.Type {
return analyzer.TypeTerraform
}
func (ConfigAnalyzer) Version() int {
func (terraformConfigAnalyzer) Version() int {
return version
}

View File

@@ -1,4 +1,4 @@
package terraform_test
package terraform
import (
"bytes"
@@ -9,7 +9,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraform"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
@@ -41,7 +40,7 @@ func TestConfigAnalyzer_Analyze(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := terraform.ConfigAnalyzer{}
a := terraformConfigAnalyzer{}
ctx := context.Background()
got, err := a.Analyze(ctx, tt.input)
@@ -75,7 +74,7 @@ func TestConfigAnalyzer_Required(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := terraform.ConfigAnalyzer{}
a := terraformConfigAnalyzer{}
got := a.Required(tt.filePath, nil)
assert.Equal(t, tt.want, got)
})

View File

@@ -5,7 +5,6 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"golang.org/x/xerrors"
@@ -13,21 +12,17 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
func init() {
analyzer.RegisterAnalyzer(&yamlConfigAnalyzer{})
}
const version = 1
var requiredExts = []string{".yaml", ".yml"}
type ConfigAnalyzer struct {
filePattern *regexp.Regexp
}
type yamlConfigAnalyzer struct{}
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
return ConfigAnalyzer{
filePattern: filePattern,
}
}
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
func (a yamlConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
b, err := io.ReadAll(input.Content)
if err != nil {
return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err)
@@ -47,11 +42,7 @@ func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
}, nil
}
func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
if a.filePattern != nil && a.filePattern.MatchString(filePath) {
return true
}
func (a yamlConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
ext := filepath.Ext(filePath)
for _, required := range requiredExts {
if ext == required {
@@ -61,10 +52,10 @@ func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return false
}
func (ConfigAnalyzer) Type() analyzer.Type {
func (yamlConfigAnalyzer) Type() analyzer.Type {
return analyzer.TypeYaml
}
func (ConfigAnalyzer) Version() int {
func (yamlConfigAnalyzer) Version() int {
return version
}

View File

@@ -1,16 +1,14 @@
package yaml_test
package yaml
import (
"context"
"os"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/yaml"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
@@ -163,7 +161,7 @@ spec:
require.NoError(t, err)
defer f.Close()
a := yaml.NewConfigAnalyzer(nil)
a := yamlConfigAnalyzer{}
ctx := context.Background()
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
FilePath: tt.inputFile,
@@ -183,10 +181,9 @@ spec:
func Test_yamlConfigAnalyzer_Required(t *testing.T) {
tests := []struct {
name string
filePattern *regexp.Regexp
filePath string
want bool
name string
filePath string
want bool
}{
{
name: "yaml",
@@ -203,16 +200,10 @@ func Test_yamlConfigAnalyzer_Required(t *testing.T) {
filePath: "deployment.json",
want: false,
},
{
name: "file pattern",
filePattern: regexp.MustCompile(`foo*`),
filePath: "foo_file",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := yaml.NewConfigAnalyzer(tt.filePattern)
s := yamlConfigAnalyzer{}
got := s.Required(tt.filePath, nil)
assert.Equal(t, tt.want, got)
@@ -221,7 +212,7 @@ func Test_yamlConfigAnalyzer_Required(t *testing.T) {
}
func Test_yamlConfigAnalyzer_Type(t *testing.T) {
s := yaml.NewConfigAnalyzer(nil)
s := yamlConfigAnalyzer{}
want := analyzer.TypeYaml
got := s.Type()

View File

@@ -16,6 +16,7 @@ type Option struct {
DisabledHandlers []types.HandlerType
SkipFiles []string
SkipDirs []string
FilePatterns []string
NoProgress bool
Offline bool
InsecureSkipTLS bool
@@ -34,6 +35,7 @@ func (o *Option) Sort() {
})
sort.Strings(o.SkipFiles)
sort.Strings(o.SkipDirs)
sort.Strings(o.FilePatterns)
}
type Artifact interface {

View File

@@ -15,7 +15,6 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
@@ -40,12 +39,6 @@ type Artifact struct {
}
func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (artifact.Artifact, error) {
misconf := opt.MisconfScannerOption
// Register config analyzers
if err := config.RegisterConfigAnalyzers(misconf.FilePatterns); err != nil {
return nil, xerrors.Errorf("config scanner error: %w", err)
}
// Initialize handlers
handlerManager, err := handler.NewManager(opt)
if err != nil {
@@ -57,11 +50,16 @@ func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (a
return nil, xerrors.Errorf("secret scanner error: %w", err)
}
a, err := analyzer.NewAnalyzerGroup(opt.AnalyzerGroup, opt.DisabledAnalyzers, opt.FilePatterns)
if err != nil {
return nil, xerrors.Errorf("analyzer group error: %w", err)
}
return Artifact{
image: img,
cache: c,
walker: walker.NewLayerTar(opt.SkipFiles, opt.SkipDirs),
analyzer: analyzer.NewAnalyzerGroup(opt.AnalyzerGroup, opt.DisabledAnalyzers),
analyzer: a,
handlerManager: handlerManager,
artifactOption: opt,

View File

@@ -19,6 +19,7 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/command/apk"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/php/composer"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/bundler"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"

View File

@@ -14,7 +14,6 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
@@ -38,11 +37,6 @@ type Artifact struct {
}
func NewArtifact(rootPath string, c cache.ArtifactCache, opt artifact.Option) (artifact.Artifact, error) {
// Register config analyzers
if err := config.RegisterConfigAnalyzers(opt.MisconfScannerOption.FilePatterns); err != nil {
return nil, xerrors.Errorf("config analyzer error: %w", err)
}
handlerManager, err := handler.NewManager(opt)
if err != nil {
return nil, xerrors.Errorf("handler initialize error: %w", err)
@@ -53,11 +47,16 @@ func NewArtifact(rootPath string, c cache.ArtifactCache, opt artifact.Option) (a
return nil, xerrors.Errorf("secret scanner error: %w", err)
}
a, err := analyzer.NewAnalyzerGroup(opt.AnalyzerGroup, opt.DisabledAnalyzers, opt.FilePatterns)
if err != nil {
return nil, xerrors.Errorf("analyzer group error: %w", err)
}
return Artifact{
rootPath: filepath.Clean(rootPath),
cache: c,
walker: walker.NewFS(buildAbsPaths(rootPath, opt.SkipFiles), buildAbsPaths(rootPath, opt.SkipDirs)),
analyzer: analyzer.NewAnalyzerGroup(opt.AnalyzerGroup, opt.DisabledAnalyzers),
analyzer: a,
handlerManager: handlerManager,
artifactOption: opt,

View File

@@ -15,6 +15,7 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/python/pip"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk"

View File

@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"

View File

@@ -18,14 +18,15 @@ func CalcKey(id string, analyzerVersions, hookVersions map[string]int, artifactO
h := sha256.New()
// Write ID, analyzer/handler versions, and skipped files/dirs
// Write ID, analyzer/handler versions, skipped files/dirs and file patterns
keyBase := struct {
ID string
AnalyzerVersions map[string]int
HookVersions map[string]int
SkipFiles []string
SkipDirs []string
}{id, analyzerVersions, hookVersions, artifactOpt.SkipFiles, artifactOpt.SkipDirs}
FilePatterns []string `json:",omitempty"`
}{id, analyzerVersions, hookVersions, artifactOpt.SkipFiles, artifactOpt.SkipDirs, artifactOpt.FilePatterns}
if err := json.NewEncoder(h).Encode(keyBase); err != nil {
return "", xerrors.Errorf("json encode error: %w", err)

View File

@@ -78,7 +78,7 @@ func TestCalcKey(t *testing.T) {
},
patterns: []string{""},
},
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
want: "sha256:9b81e0bf3aa7809a0f41bc696f353fca5645bcb63b975ab30e23d81886df2e61",
},
{
name: "with single non empty string in file patterns",
@@ -90,7 +90,7 @@ func TestCalcKey(t *testing.T) {
},
patterns: []string{"test"},
},
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
want: "sha256:7d91b2623ae4b5641a1f36efa59c774231efe8c28c27a03869894fd49b047fe8",
},
{
name: "with non empty followed by empty string in file patterns",
@@ -102,7 +102,7 @@ func TestCalcKey(t *testing.T) {
},
patterns: []string{"test", ""},
},
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
want: "sha256:5c7f1555e95fc60cdaa7e92e99aee15ee7be356fad9e83f1c24a3be06713a5a8",
},
{
name: "with non empty preceded by empty string in file patterns",
@@ -114,7 +114,7 @@ func TestCalcKey(t *testing.T) {
},
patterns: []string{"", "test"},
},
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
want: "sha256:5c7f1555e95fc60cdaa7e92e99aee15ee7be356fad9e83f1c24a3be06713a5a8",
},
{
name: "with policy",
@@ -158,13 +158,13 @@ func TestCalcKey(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
artifactOpt := artifact.Option{
SkipFiles: tt.args.skipFiles,
SkipDirs: tt.args.skipDirs,
SkipFiles: tt.args.skipFiles,
SkipDirs: tt.args.skipDirs,
FilePatterns: tt.args.patterns,
MisconfScannerOption: config.ScannerOption{
FilePatterns: tt.args.patterns,
PolicyPaths: tt.args.policy,
DataPaths: tt.args.data,
PolicyPaths: tt.args.policy,
DataPaths: tt.args.data,
},
}
got, err := CalcKey(tt.args.key, tt.args.analyzerVersions, tt.args.hookVersions, artifactOpt)

View File

@@ -11,6 +11,8 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/artifact/local"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
)
type ConfigScanner struct {

View File

@@ -183,7 +183,7 @@ func newMisconfPostHandler(artifactOpt artifact.Option) (handler.PostHandler, er
tfOpts := addTFOpts(opts, artifactOpt.MisconfScannerOption)
return misconfPostHandler{
filePatterns: artifactOpt.MisconfScannerOption.FilePatterns,
filePatterns: artifactOpt.FilePatterns,
scanners: map[string]scanners.FSScanner{
types.Terraform: tfscanner.New(tfOpts...),
types.CloudFormation: cfscanner.New(opts...),

View File

@@ -6,7 +6,6 @@ import (
"testing"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
misconf "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/stretchr/testify/assert"
@@ -56,7 +55,7 @@ func Test_Handle(t *testing.T) {
result := &analyzer.AnalysisResult{
Files: tt.files,
}
misconfHandler, err := newMisconfPostHandler(artifact.Option{MisconfScannerOption: misconf.ScannerOption{FilePatterns: tt.filePatterns}})
misconfHandler, err := newMisconfPostHandler(artifact.Option{FilePatterns: tt.filePatterns})
assert.NoError(t, err)
blobInfo := &types.BlobInfo{}

View File

@@ -10,12 +10,6 @@ import (
// config-policy: "custom-policy/policy"
// policy-namespaces: "user"
var (
FilePatternsFlag = Flag{
Name: "file-patterns",
ConfigName: "misconfiguration.file-patterns",
Value: []string{},
Usage: "specify config file patterns, available with '--security-checks config'",
}
IncludeNonFailuresFlag = Flag{
Name: "include-non-failures",
ConfigName: "misconfiguration.include-non-failures",
@@ -87,7 +81,6 @@ var (
// MisconfFlagGroup composes common printer flag structs used for commands providing misconfinguration scanning.
type MisconfFlagGroup struct {
FilePatterns *Flag
IncludeNonFailures *Flag
SkipPolicyUpdate *Flag // deprecated
Trace *Flag
@@ -106,7 +99,6 @@ type MisconfFlagGroup struct {
}
type MisconfOptions struct {
FilePatterns []string
IncludeNonFailures bool
SkipPolicyUpdate bool // deprecated
Trace bool
@@ -126,7 +118,6 @@ type MisconfOptions struct {
func NewMisconfFlagGroup() *MisconfFlagGroup {
return &MisconfFlagGroup{
FilePatterns: &FilePatternsFlag,
IncludeNonFailures: &IncludeNonFailuresFlag,
SkipPolicyUpdate: &SkipPolicyUpdateFlag,
Trace: &TraceFlag,
@@ -147,7 +138,6 @@ func (f *MisconfFlagGroup) Name() string {
func (f *MisconfFlagGroup) Flags() []*Flag {
return []*Flag{
f.FilePatterns,
f.IncludeNonFailures,
f.SkipPolicyUpdate,
f.Trace,
@@ -168,7 +158,6 @@ func (f *MisconfFlagGroup) ToOptions() (MisconfOptions, error) {
log.Logger.Warn("'--skip-policy-update' is no longer necessary as the built-in policies are embedded into the binary")
}
return MisconfOptions{
FilePatterns: getStringSlice(f.FilePatterns),
IncludeNonFailures: getBool(f.IncludeNonFailures),
Trace: getBool(f.Trace),

View File

@@ -35,6 +35,12 @@ var (
Value: fmt.Sprintf("%s,%s", types.SecurityCheckVulnerability, types.SecurityCheckSecret),
Usage: "comma-separated list of what security issues to detect (vuln,config,secret)",
}
FilePatternsFlag = Flag{
Name: "file-patterns",
ConfigName: "scan.file-patterns",
Value: []string{},
Usage: "specify config file patterns",
}
)
type ScanFlagGroup struct {
@@ -42,6 +48,7 @@ type ScanFlagGroup struct {
SkipFiles *Flag
OfflineScan *Flag
SecurityChecks *Flag
FilePatterns *Flag
}
type ScanOptions struct {
@@ -50,6 +57,7 @@ type ScanOptions struct {
SkipFiles []string
OfflineScan bool
SecurityChecks []string
FilePatterns []string
}
func NewScanFlagGroup() *ScanFlagGroup {
@@ -58,6 +66,7 @@ func NewScanFlagGroup() *ScanFlagGroup {
SkipFiles: &SkipFilesFlag,
OfflineScan: &OfflineScanFlag,
SecurityChecks: &SecurityChecksFlag,
FilePatterns: &FilePatternsFlag,
}
}
@@ -66,7 +75,7 @@ func (f *ScanFlagGroup) Name() string {
}
func (f *ScanFlagGroup) Flags() []*Flag {
return []*Flag{f.SkipDirs, f.SkipFiles, f.OfflineScan, f.SecurityChecks}
return []*Flag{f.SkipDirs, f.SkipFiles, f.OfflineScan, f.SecurityChecks, f.FilePatterns}
}
func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
@@ -85,6 +94,7 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
SkipFiles: getStringSlice(f.SkipFiles),
OfflineScan: getBool(f.OfflineScan),
SecurityChecks: securityChecks,
FilePatterns: getStringSlice(f.FilePatterns),
}, nil
}

View File

@@ -92,7 +92,10 @@ func TestManager_Register(t *testing.T) {
}()
// Confirm the analyzer is registered
got := analyzer.NewAnalyzerGroup("", nil).AnalyzerVersions()
a, err := analyzer.NewAnalyzerGroup("", nil, nil)
require.NoError(t, err)
got := a.AnalyzerVersions()
assert.Equal(t, tt.wantAnalyzerVersions, got)
// Confirm the post scanner is registered

View File

@@ -11,4 +11,5 @@ type ScanOptions struct {
ScanRemovedPackages bool
ListAllPackages bool
LicenseCategories map[types.LicenseCategory][]string
FilePatterns []string
}