mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 23:26:39 -08:00
feat: move file patterns to a global level to be able to use it on any analyzer (#2539)
This commit is contained in:
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
9
pkg/fanal/analyzer/config/all/import.go
Normal file
9
pkg/fanal/analyzer/config/all/import.go
Normal 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"
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
5
pkg/fanal/cache/key.go
vendored
5
pkg/fanal/cache/key.go
vendored
@@ -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)
|
||||
|
||||
18
pkg/fanal/cache/key_test.go
vendored
18
pkg/fanal/cache/key_test.go
vendored
@@ -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)
|
||||
|
||||
2
pkg/fanal/external/config_scan.go
vendored
2
pkg/fanal/external/config_scan.go
vendored
@@ -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 {
|
||||
|
||||
@@ -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...),
|
||||
|
||||
@@ -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{}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,4 +11,5 @@ type ScanOptions struct {
|
||||
ScanRemovedPackages bool
|
||||
ListAllPackages bool
|
||||
LicenseCategories map[types.LicenseCategory][]string
|
||||
FilePatterns []string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user