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
|
!!! hint
|
||||||
See also [Others](../../vulnerability/examples/others.md) in Vulnerability section.
|
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
|
```yaml
|
||||||
scan:
|
scan:
|
||||||
|
# Same as '--file-patterns'
|
||||||
|
# Default is empty
|
||||||
|
file-patterns:
|
||||||
|
-
|
||||||
|
|
||||||
# Same as '--skip-dirs'
|
# Same as '--skip-dirs'
|
||||||
# Default is empty
|
# Default is empty
|
||||||
skip-dirs:
|
skip-dirs:
|
||||||
@@ -195,11 +200,6 @@ Available with misconfiguration scanning
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
misconfiguration:
|
misconfiguration:
|
||||||
# Same as '--file-patterns'
|
|
||||||
# Default is empty
|
|
||||||
file-patterns:
|
|
||||||
-
|
|
||||||
|
|
||||||
# Same as '--include-non-failures'
|
# Same as '--include-non-failures'
|
||||||
# Default is false
|
# Default is false
|
||||||
include-non-failures: 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
|
$ 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
|
## Exit Code
|
||||||
By default, `Trivy` exits with code 0 even when vulnerabilities are detected.
|
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.
|
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
|
ScanRemovedPackages: opts.ScanRemovedPkgs, // this is valid only for 'image' subcommand
|
||||||
ListAllPackages: opts.ListAllPkgs,
|
ListAllPackages: opts.ListAllPkgs,
|
||||||
LicenseCategories: opts.LicenseCategories,
|
LicenseCategories: opts.LicenseCategories,
|
||||||
|
FilePatterns: opts.FilePatterns,
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.Contains(opts.SecurityChecks, types.SecurityCheckVulnerability) {
|
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...),
|
Namespaces: append(opts.PolicyNamespaces, defaultPolicyNamespaces...),
|
||||||
PolicyPaths: opts.PolicyPaths,
|
PolicyPaths: opts.PolicyPaths,
|
||||||
DataPaths: opts.DataPaths,
|
DataPaths: opts.DataPaths,
|
||||||
FilePatterns: opts.FilePatterns,
|
|
||||||
HelmValues: opts.HelmValues,
|
HelmValues: opts.HelmValues,
|
||||||
HelmValueFiles: opts.HelmValueFiles,
|
HelmValueFiles: opts.HelmValueFiles,
|
||||||
HelmFileValues: opts.HelmFileValues,
|
HelmFileValues: opts.HelmFileValues,
|
||||||
@@ -504,6 +504,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
|
|||||||
DisabledAnalyzers: disabledAnalyzers(opts),
|
DisabledAnalyzers: disabledAnalyzers(opts),
|
||||||
SkipFiles: opts.SkipFiles,
|
SkipFiles: opts.SkipFiles,
|
||||||
SkipDirs: opts.SkipDirs,
|
SkipDirs: opts.SkipDirs,
|
||||||
|
FilePatterns: opts.FilePatterns,
|
||||||
InsecureSkipTLS: opts.Insecure,
|
InsecureSkipTLS: opts.Insecure,
|
||||||
Offline: opts.OfflineScan,
|
Offline: opts.OfflineScan,
|
||||||
NoProgress: opts.NoProgress || opts.Quiet,
|
NoProgress: opts.NoProgress || opts.Quiet,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package all
|
|||||||
import (
|
import (
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/buildinfo"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/buildinfo"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/command/apk"
|
_ "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/deps"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dotnet/nuget"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dotnet/nuget"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/binary"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/binary"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -91,6 +92,7 @@ type Opener func() (dio.ReadSeekCloserAt, error)
|
|||||||
type AnalyzerGroup struct {
|
type AnalyzerGroup struct {
|
||||||
analyzers []analyzer
|
analyzers []analyzer
|
||||||
configAnalyzers []configAnalyzer
|
configAnalyzers []configAnalyzer
|
||||||
|
filePatterns map[Type][]*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnalysisResult struct {
|
type AnalysisResult struct {
|
||||||
@@ -266,12 +268,36 @@ func belongToGroup(groupName Group, analyzerType Type, disabledAnalyzers []Type,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAnalyzerGroup(groupName Group, disabledAnalyzers []Type) AnalyzerGroup {
|
const separator = ":"
|
||||||
|
|
||||||
|
func NewAnalyzerGroup(groupName Group, disabledAnalyzers []Type, filePatterns []string) (AnalyzerGroup, error) {
|
||||||
if groupName == "" {
|
if groupName == "" {
|
||||||
groupName = GroupBuiltin
|
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 {
|
for analyzerType, a := range analyzers {
|
||||||
if !belongToGroup(groupName, analyzerType, disabledAnalyzers, a) {
|
if !belongToGroup(groupName, analyzerType, disabledAnalyzers, a) {
|
||||||
continue
|
continue
|
||||||
@@ -286,7 +312,7 @@ func NewAnalyzerGroup(groupName Group, disabledAnalyzers []Type) AnalyzerGroup {
|
|||||||
group.configAnalyzers = append(group.configAnalyzers, a)
|
group.configAnalyzers = append(group.configAnalyzers, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
return group
|
return group, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnalyzerVersions returns analyzer version identifier used for cache keys.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filepath extracted from tar file doesn't have the prefix "/"
|
||||||
|
cleanPath := strings.TrimLeft(filePath, "/")
|
||||||
|
|
||||||
for _, a := range ag.analyzers {
|
for _, a := range ag.analyzers {
|
||||||
// Skip disabled analyzers
|
// Skip disabled analyzers
|
||||||
if slices.Contains(disabled, a.Type()) {
|
if slices.Contains(disabled, a.Type()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// filepath extracted from tar file doesn't have the prefix "/"
|
if !ag.filePatternMatch(a.Type(), cleanPath) && !a.Required(cleanPath, info) {
|
||||||
if !a.Required(strings.TrimLeft(filePath, "/"), info) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rc, err := opener()
|
rc, err := opener()
|
||||||
@@ -375,3 +403,12 @@ func (ag AnalyzerGroup) AnalyzeImageConfig(targetOS types.OS, configBlob []byte)
|
|||||||
}
|
}
|
||||||
return nil
|
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
|
filePath string
|
||||||
testFilePath string
|
testFilePath string
|
||||||
disabledAnalyzers []analyzer.Type
|
disabledAnalyzers []analyzer.Type
|
||||||
|
filePatterns []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -377,6 +378,28 @@ func TestAnalyzeFile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: &analyzer.AnalysisResult{},
|
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",
|
name: "ignore permission error",
|
||||||
args: args{
|
args: args{
|
||||||
@@ -393,6 +416,24 @@ func TestAnalyzeFile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: "unable to open /lib/apk/db/installed",
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -400,7 +441,13 @@ func TestAnalyzeFile(t *testing.T) {
|
|||||||
limit := semaphore.NewWeighted(3)
|
limit := semaphore.NewWeighted(3)
|
||||||
|
|
||||||
got := new(analyzer.AnalysisResult)
|
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)
|
info, err := os.Stat(tt.args.testFilePath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -440,6 +487,7 @@ func TestAnalyzeConfig(t *testing.T) {
|
|||||||
targetOS types.OS
|
targetOS types.OS
|
||||||
configBlob []byte
|
configBlob []byte
|
||||||
disabledAnalyzers []analyzer.Type
|
disabledAnalyzers []analyzer.Type
|
||||||
|
filePatterns []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -482,7 +530,8 @@ func TestAnalyzeConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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)
|
got := a.AnalyzeImageConfig(tt.args.targetOS, tt.args.configBlob)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
@@ -517,7 +566,8 @@ func TestAnalyzer_AnalyzerVersions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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()
|
got := a.AnalyzerVersions()
|
||||||
fmt.Printf("%v\n", got)
|
fmt.Printf("%v\n", got)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
@@ -549,7 +599,8 @@ func TestAnalyzer_ImageConfigAnalyzerVersions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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()
|
got := a.ImageConfigAnalyzerVersions()
|
||||||
assert.Equal(t, tt.want, got)
|
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
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"sort"
|
"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 {
|
type ScannerOption struct {
|
||||||
Trace bool
|
Trace bool
|
||||||
RegoOnly bool
|
RegoOnly bool
|
||||||
Namespaces []string
|
Namespaces []string
|
||||||
FilePatterns []string
|
|
||||||
PolicyPaths []string
|
PolicyPaths []string
|
||||||
DataPaths []string
|
DataPaths []string
|
||||||
DisableEmbeddedPolicies bool
|
DisableEmbeddedPolicies bool
|
||||||
@@ -37,44 +21,6 @@ type ScannerOption struct {
|
|||||||
|
|
||||||
func (o *ScannerOption) Sort() {
|
func (o *ScannerOption) Sort() {
|
||||||
sort.Strings(o.Namespaces)
|
sort.Strings(o.Namespaces)
|
||||||
sort.Strings(o.FilePatterns)
|
|
||||||
sort.Strings(o.PolicyPaths)
|
sort.Strings(o.PolicyPaths)
|
||||||
sort.Strings(o.DataPaths)
|
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) {
|
func TestScannerOption_Sort(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Namespaces []string
|
Namespaces []string
|
||||||
FilePatterns []string
|
PolicyPaths []string
|
||||||
PolicyPaths []string
|
DataPaths []string
|
||||||
DataPaths []string
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -23,25 +22,22 @@ func TestScannerOption_Sort(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path",
|
name: "happy path",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
Namespaces: []string{"main", "custom", "default"},
|
Namespaces: []string{"main", "custom", "default"},
|
||||||
FilePatterns: []string{"dockerfile:foo*", "yaml:yml_*"},
|
PolicyPaths: []string{"policy"},
|
||||||
PolicyPaths: []string{"policy"},
|
DataPaths: []string{"data/b", "data/c", "data/a"},
|
||||||
DataPaths: []string{"data/b", "data/c", "data/a"},
|
|
||||||
},
|
},
|
||||||
want: config.ScannerOption{
|
want: config.ScannerOption{
|
||||||
Namespaces: []string{"custom", "default", "main"},
|
Namespaces: []string{"custom", "default", "main"},
|
||||||
FilePatterns: []string{"dockerfile:foo*", "yaml:yml_*"},
|
PolicyPaths: []string{"policy"},
|
||||||
PolicyPaths: []string{"policy"},
|
DataPaths: []string{"data/a", "data/b", "data/c"},
|
||||||
DataPaths: []string{"data/a", "data/b", "data/c"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing some fields",
|
name: "missing some fields",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
Namespaces: []string{"main"},
|
Namespaces: []string{"main"},
|
||||||
FilePatterns: nil,
|
PolicyPaths: nil,
|
||||||
PolicyPaths: nil,
|
DataPaths: nil,
|
||||||
DataPaths: nil,
|
|
||||||
},
|
},
|
||||||
want: config.ScannerOption{
|
want: config.ScannerOption{
|
||||||
Namespaces: []string{"main"},
|
Namespaces: []string{"main"},
|
||||||
@@ -51,10 +47,9 @@ func TestScannerOption_Sort(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
o := config.ScannerOption{
|
o := config.ScannerOption{
|
||||||
Namespaces: tt.fields.Namespaces,
|
Namespaces: tt.fields.Namespaces,
|
||||||
FilePatterns: tt.fields.FilePatterns,
|
PolicyPaths: tt.fields.PolicyPaths,
|
||||||
PolicyPaths: tt.fields.PolicyPaths,
|
DataPaths: tt.fields.DataPaths,
|
||||||
DataPaths: tt.fields.DataPaths,
|
|
||||||
}
|
}
|
||||||
o.Sort()
|
o.Sort()
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
@@ -14,21 +13,17 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
analyzer.RegisterAnalyzer(&dockerConfigAnalyzer{})
|
||||||
|
}
|
||||||
|
|
||||||
const version = 1
|
const version = 1
|
||||||
|
|
||||||
var requiredFiles = []string{"Dockerfile", "Containerfile"}
|
var requiredFiles = []string{"Dockerfile", "Containerfile"}
|
||||||
|
|
||||||
type ConfigAnalyzer struct {
|
type dockerConfigAnalyzer struct{}
|
||||||
filePattern *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
|
func (s dockerConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
||||||
return ConfigAnalyzer{
|
|
||||||
filePattern: filePattern,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
|
||||||
b, err := io.ReadAll(input.Content)
|
b, err := io.ReadAll(input.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err)
|
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
|
// Required does a case-insensitive check for filePath and returns true if
|
||||||
// filePath equals/startsWith/hasExtension requiredFiles
|
// filePath equals/startsWith/hasExtension requiredFiles
|
||||||
func (s ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
func (s dockerConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
||||||
if s.filePattern != nil && s.filePattern.MatchString(filePath) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
base := filepath.Base(filePath)
|
base := filepath.Base(filePath)
|
||||||
ext := filepath.Ext(base)
|
ext := filepath.Ext(base)
|
||||||
for _, file := range requiredFiles {
|
for _, file := range requiredFiles {
|
||||||
@@ -69,10 +60,10 @@ func (s ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s ConfigAnalyzer) Type() analyzer.Type {
|
func (s dockerConfigAnalyzer) Type() analyzer.Type {
|
||||||
return analyzer.TypeDockerfile
|
return analyzer.TypeDockerfile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s ConfigAnalyzer) Version() int {
|
func (s dockerConfigAnalyzer) Version() int {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
package dockerfile_test
|
package dockerfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/dockerfile"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ COPY --from=build /bar /bar
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
a := dockerfile.NewConfigAnalyzer(nil)
|
a := dockerConfigAnalyzer{}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
|
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
|
||||||
FilePath: tt.inputFile,
|
FilePath: tt.inputFile,
|
||||||
@@ -88,10 +86,9 @@ COPY --from=build /bar /bar
|
|||||||
|
|
||||||
func Test_dockerConfigAnalyzer_Required(t *testing.T) {
|
func Test_dockerConfigAnalyzer_Required(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
filePattern *regexp.Regexp
|
filePath string
|
||||||
filePath string
|
want bool
|
||||||
want bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "dockerfile",
|
name: "dockerfile",
|
||||||
@@ -143,16 +140,10 @@ func Test_dockerConfigAnalyzer_Required(t *testing.T) {
|
|||||||
filePath: "deployment.json",
|
filePath: "deployment.json",
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "file pattern",
|
|
||||||
filePattern: regexp.MustCompile(`foo*`),
|
|
||||||
filePath: "foo_file",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := dockerfile.NewConfigAnalyzer(tt.filePattern)
|
s := dockerConfigAnalyzer{}
|
||||||
got := s.Required(tt.filePath, nil)
|
got := s.Required(tt.filePath, nil)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
@@ -160,7 +151,7 @@ func Test_dockerConfigAnalyzer_Required(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_dockerConfigAnalyzer_Type(t *testing.T) {
|
func Test_dockerConfigAnalyzer_Type(t *testing.T) {
|
||||||
s := dockerfile.NewConfigAnalyzer(nil)
|
s := dockerConfigAnalyzer{}
|
||||||
want := analyzer.TypeDockerfile
|
want := analyzer.TypeDockerfile
|
||||||
got := s.Type()
|
got := s.Type()
|
||||||
assert.Equal(t, want, got)
|
assert.Equal(t, want, got)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
@@ -18,21 +17,17 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
analyzer.RegisterAnalyzer(&helmConfigAnalyzer{})
|
||||||
|
}
|
||||||
|
|
||||||
const version = 1
|
const version = 1
|
||||||
|
|
||||||
const maxTarSize = 209_715_200 // 200MB
|
const maxTarSize = 209_715_200 // 200MB
|
||||||
|
|
||||||
type ConfigAnalyzer struct {
|
type helmConfigAnalyzer struct{}
|
||||||
filePattern *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
|
func (a helmConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
||||||
return ConfigAnalyzer{
|
|
||||||
filePattern: filePattern,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
|
||||||
if isArchive(input.FilePath) {
|
if isArchive(input.FilePath) {
|
||||||
if !isHelmChart(input.FilePath, input.Content) {
|
if !isHelmChart(input.FilePath, input.Content) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -62,11 +57,7 @@ func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ConfigAnalyzer) Required(filePath string, info os.FileInfo) bool {
|
func (a helmConfigAnalyzer) Required(filePath string, info os.FileInfo) bool {
|
||||||
if a.filePattern != nil && a.filePattern.MatchString(filePath) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Size() > maxTarSize {
|
if info.Size() > maxTarSize {
|
||||||
// tarball is too big to be Helm chart - move on
|
// tarball is too big to be Helm chart - move on
|
||||||
return false
|
return false
|
||||||
@@ -88,11 +79,11 @@ func (a ConfigAnalyzer) Required(filePath string, info os.FileInfo) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Type() analyzer.Type {
|
func (helmConfigAnalyzer) Type() analyzer.Type {
|
||||||
return analyzer.TypeHelm
|
return analyzer.TypeHelm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Version() int {
|
func (helmConfigAnalyzer) Version() int {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package helm
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -340,7 +339,7 @@ affinity: {}
|
|||||||
info, err := os.Stat(tt.inputFile)
|
info, err := os.Stat(tt.inputFile)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
a := NewConfigAnalyzer(nil)
|
a := helmConfigAnalyzer{}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
|
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
|
||||||
FilePath: tt.inputFile,
|
FilePath: tt.inputFile,
|
||||||
@@ -361,10 +360,9 @@ affinity: {}
|
|||||||
|
|
||||||
func Test_helmConfigAnalyzer_Required(t *testing.T) {
|
func Test_helmConfigAnalyzer_Required(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
filePattern *regexp.Regexp
|
filePath string
|
||||||
filePath string
|
want bool
|
||||||
want bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "yaml",
|
name: "yaml",
|
||||||
@@ -406,17 +404,10 @@ func Test_helmConfigAnalyzer_Required(t *testing.T) {
|
|||||||
filePath: "testdata/nope.tgz",
|
filePath: "testdata/nope.tgz",
|
||||||
want: true, // its a tarball after all
|
want: true, // its a tarball after all
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
name: "file pattern",
|
|
||||||
filePattern: regexp.MustCompile(`foo*`),
|
|
||||||
filePath: "foo_file",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := NewConfigAnalyzer(tt.filePattern)
|
s := helmConfigAnalyzer{}
|
||||||
|
|
||||||
info, _ := os.Stat(tt.filePath)
|
info, _ := os.Stat(tt.filePath)
|
||||||
|
|
||||||
@@ -427,7 +418,7 @@ func Test_helmConfigAnalyzer_Required(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_helmConfigAnalyzer_Type(t *testing.T) {
|
func Test_helmConfigAnalyzer_Type(t *testing.T) {
|
||||||
s := NewConfigAnalyzer(nil)
|
s := helmConfigAnalyzer{}
|
||||||
|
|
||||||
want := analyzer.TypeHelm
|
want := analyzer.TypeHelm
|
||||||
got := s.Type()
|
got := s.Type()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
@@ -13,6 +12,10 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
analyzer.RegisterAnalyzer(&jsonConfigAnalyzer{})
|
||||||
|
}
|
||||||
|
|
||||||
const version = 1
|
const version = 1
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -20,17 +23,9 @@ var (
|
|||||||
excludedFiles = []string{types.NpmPkgLock, types.NuGetPkgsLock, types.NuGetPkgsConfig}
|
excludedFiles = []string{types.NpmPkgLock, types.NuGetPkgsLock, types.NuGetPkgsConfig}
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigAnalyzer struct {
|
type jsonConfigAnalyzer struct{}
|
||||||
filePattern *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
|
func (a jsonConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
||||||
return ConfigAnalyzer{
|
|
||||||
filePattern: filePattern,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
|
||||||
b, err := io.ReadAll(input.Content)
|
b, err := io.ReadAll(input.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err)
|
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
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
func (a jsonConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
||||||
if a.filePattern != nil && a.filePattern.MatchString(filePath) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.Base(filePath)
|
filename := filepath.Base(filePath)
|
||||||
for _, excludedFile := range excludedFiles {
|
for _, excludedFile := range excludedFiles {
|
||||||
if filename == excludedFile {
|
if filename == excludedFile {
|
||||||
@@ -65,10 +56,10 @@ func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
|||||||
return filepath.Ext(filePath) == requiredExt
|
return filepath.Ext(filePath) == requiredExt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Type() analyzer.Type {
|
func (jsonConfigAnalyzer) Type() analyzer.Type {
|
||||||
return analyzer.TypeJSON
|
return analyzer.TypeJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Version() int {
|
func (jsonConfigAnalyzer) Version() int {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
package json_test
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/json"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -133,7 +131,7 @@ func Test_jsonConfigAnalyzer_Analyze(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
s := json.NewConfigAnalyzer(nil)
|
s := jsonConfigAnalyzer{}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
got, err := s.Analyze(ctx, analyzer.AnalysisInput{
|
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) {
|
func Test_jsonConfigAnalyzer_Required(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
filePattern *regexp.Regexp
|
filePath string
|
||||||
filePath string
|
want bool
|
||||||
want bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "json",
|
name: "json",
|
||||||
@@ -174,16 +171,10 @@ func Test_jsonConfigAnalyzer_Required(t *testing.T) {
|
|||||||
filePath: "package-lock.json",
|
filePath: "package-lock.json",
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "file pattern",
|
|
||||||
filePattern: regexp.MustCompile(`foo*`),
|
|
||||||
filePath: "foo_file",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := json.NewConfigAnalyzer(tt.filePattern)
|
s := jsonConfigAnalyzer{}
|
||||||
|
|
||||||
got := s.Required(tt.filePath, nil)
|
got := s.Required(tt.filePath, nil)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
@@ -192,7 +183,7 @@ func Test_jsonConfigAnalyzer_Required(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_jsonConfigAnalyzer_Type(t *testing.T) {
|
func Test_jsonConfigAnalyzer_Type(t *testing.T) {
|
||||||
s := json.NewConfigAnalyzer(nil)
|
s := jsonConfigAnalyzer{}
|
||||||
|
|
||||||
want := analyzer.TypeJSON
|
want := analyzer.TypeJSON
|
||||||
got := s.Type()
|
got := s.Type()
|
||||||
|
|||||||
@@ -13,19 +13,18 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
analyzer.RegisterAnalyzer(&terraformConfigAnalyzer{})
|
||||||
|
}
|
||||||
|
|
||||||
const version = 1
|
const version = 1
|
||||||
|
|
||||||
var requiredExts = []string{".tf", ".tf.json"}
|
var requiredExts = []string{".tf", ".tf.json"}
|
||||||
|
|
||||||
type ConfigAnalyzer struct {
|
type terraformConfigAnalyzer struct{}
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfigAnalyzer() ConfigAnalyzer {
|
|
||||||
return ConfigAnalyzer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze returns a name of Terraform file
|
// 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)
|
b, err := io.ReadAll(input.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("read error (%s): %w", input.FilePath, err)
|
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
|
}, 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))
|
return slices.Contains(requiredExts, filepath.Ext(filePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Type() analyzer.Type {
|
func (terraformConfigAnalyzer) Type() analyzer.Type {
|
||||||
return analyzer.TypeTerraform
|
return analyzer.TypeTerraform
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Version() int {
|
func (terraformConfigAnalyzer) Version() int {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package terraform_test
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraform"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ func TestConfigAnalyzer_Analyze(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
a := terraform.ConfigAnalyzer{}
|
a := terraformConfigAnalyzer{}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
got, err := a.Analyze(ctx, tt.input)
|
got, err := a.Analyze(ctx, tt.input)
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ func TestConfigAnalyzer_Required(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
a := terraform.ConfigAnalyzer{}
|
a := terraformConfigAnalyzer{}
|
||||||
got := a.Required(tt.filePath, nil)
|
got := a.Required(tt.filePath, nil)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
@@ -13,21 +12,17 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
analyzer.RegisterAnalyzer(&yamlConfigAnalyzer{})
|
||||||
|
}
|
||||||
|
|
||||||
const version = 1
|
const version = 1
|
||||||
|
|
||||||
var requiredExts = []string{".yaml", ".yml"}
|
var requiredExts = []string{".yaml", ".yml"}
|
||||||
|
|
||||||
type ConfigAnalyzer struct {
|
type yamlConfigAnalyzer struct{}
|
||||||
filePattern *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfigAnalyzer(filePattern *regexp.Regexp) ConfigAnalyzer {
|
func (a yamlConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
||||||
return ConfigAnalyzer{
|
|
||||||
filePattern: filePattern,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a ConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
|
||||||
b, err := io.ReadAll(input.Content)
|
b, err := io.ReadAll(input.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err)
|
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
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
func (a yamlConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
||||||
if a.filePattern != nil && a.filePattern.MatchString(filePath) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ext := filepath.Ext(filePath)
|
ext := filepath.Ext(filePath)
|
||||||
for _, required := range requiredExts {
|
for _, required := range requiredExts {
|
||||||
if ext == required {
|
if ext == required {
|
||||||
@@ -61,10 +52,10 @@ func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Type() analyzer.Type {
|
func (yamlConfigAnalyzer) Type() analyzer.Type {
|
||||||
return analyzer.TypeYaml
|
return analyzer.TypeYaml
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConfigAnalyzer) Version() int {
|
func (yamlConfigAnalyzer) Version() int {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
package yaml_test
|
package yaml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/yaml"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -163,7 +161,7 @@ spec:
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
a := yaml.NewConfigAnalyzer(nil)
|
a := yamlConfigAnalyzer{}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
|
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
|
||||||
FilePath: tt.inputFile,
|
FilePath: tt.inputFile,
|
||||||
@@ -183,10 +181,9 @@ spec:
|
|||||||
|
|
||||||
func Test_yamlConfigAnalyzer_Required(t *testing.T) {
|
func Test_yamlConfigAnalyzer_Required(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
filePattern *regexp.Regexp
|
filePath string
|
||||||
filePath string
|
want bool
|
||||||
want bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "yaml",
|
name: "yaml",
|
||||||
@@ -203,16 +200,10 @@ func Test_yamlConfigAnalyzer_Required(t *testing.T) {
|
|||||||
filePath: "deployment.json",
|
filePath: "deployment.json",
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "file pattern",
|
|
||||||
filePattern: regexp.MustCompile(`foo*`),
|
|
||||||
filePath: "foo_file",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := yaml.NewConfigAnalyzer(tt.filePattern)
|
s := yamlConfigAnalyzer{}
|
||||||
|
|
||||||
got := s.Required(tt.filePath, nil)
|
got := s.Required(tt.filePath, nil)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
@@ -221,7 +212,7 @@ func Test_yamlConfigAnalyzer_Required(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_yamlConfigAnalyzer_Type(t *testing.T) {
|
func Test_yamlConfigAnalyzer_Type(t *testing.T) {
|
||||||
s := yaml.NewConfigAnalyzer(nil)
|
s := yamlConfigAnalyzer{}
|
||||||
|
|
||||||
want := analyzer.TypeYaml
|
want := analyzer.TypeYaml
|
||||||
got := s.Type()
|
got := s.Type()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type Option struct {
|
|||||||
DisabledHandlers []types.HandlerType
|
DisabledHandlers []types.HandlerType
|
||||||
SkipFiles []string
|
SkipFiles []string
|
||||||
SkipDirs []string
|
SkipDirs []string
|
||||||
|
FilePatterns []string
|
||||||
NoProgress bool
|
NoProgress bool
|
||||||
Offline bool
|
Offline bool
|
||||||
InsecureSkipTLS bool
|
InsecureSkipTLS bool
|
||||||
@@ -34,6 +35,7 @@ func (o *Option) Sort() {
|
|||||||
})
|
})
|
||||||
sort.Strings(o.SkipFiles)
|
sort.Strings(o.SkipFiles)
|
||||||
sort.Strings(o.SkipDirs)
|
sort.Strings(o.SkipDirs)
|
||||||
|
sort.Strings(o.FilePatterns)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Artifact interface {
|
type Artifact interface {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"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/analyzer/secret"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
"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) {
|
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
|
// Initialize handlers
|
||||||
handlerManager, err := handler.NewManager(opt)
|
handlerManager, err := handler.NewManager(opt)
|
||||||
if err != nil {
|
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)
|
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{
|
return Artifact{
|
||||||
image: img,
|
image: img,
|
||||||
cache: c,
|
cache: c,
|
||||||
walker: walker.NewLayerTar(opt.SkipFiles, opt.SkipDirs),
|
walker: walker.NewLayerTar(opt.SkipFiles, opt.SkipDirs),
|
||||||
analyzer: analyzer.NewAnalyzerGroup(opt.AnalyzerGroup, opt.DisabledAnalyzers),
|
analyzer: a,
|
||||||
handlerManager: handlerManager,
|
handlerManager: handlerManager,
|
||||||
|
|
||||||
artifactOption: opt,
|
artifactOption: opt,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
|
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/command/apk"
|
_ "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/php/composer"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/bundler"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/bundler"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"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/analyzer/secret"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
"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) {
|
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)
|
handlerManager, err := handler.NewManager(opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("handler initialize error: %w", err)
|
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)
|
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{
|
return Artifact{
|
||||||
rootPath: filepath.Clean(rootPath),
|
rootPath: filepath.Clean(rootPath),
|
||||||
cache: c,
|
cache: c,
|
||||||
walker: walker.NewFS(buildAbsPaths(rootPath, opt.SkipFiles), buildAbsPaths(rootPath, opt.SkipDirs)),
|
walker: walker.NewFS(buildAbsPaths(rootPath, opt.SkipFiles), buildAbsPaths(rootPath, opt.SkipDirs)),
|
||||||
analyzer: analyzer.NewAnalyzerGroup(opt.AnalyzerGroup, opt.DisabledAnalyzers),
|
analyzer: a,
|
||||||
handlerManager: handlerManager,
|
handlerManager: handlerManager,
|
||||||
|
|
||||||
artifactOption: opt,
|
artifactOption: opt,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"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/language/python/pip"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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/artifact"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"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()
|
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 {
|
keyBase := struct {
|
||||||
ID string
|
ID string
|
||||||
AnalyzerVersions map[string]int
|
AnalyzerVersions map[string]int
|
||||||
HookVersions map[string]int
|
HookVersions map[string]int
|
||||||
SkipFiles []string
|
SkipFiles []string
|
||||||
SkipDirs []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 {
|
if err := json.NewEncoder(h).Encode(keyBase); err != nil {
|
||||||
return "", xerrors.Errorf("json encode error: %w", err)
|
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{""},
|
patterns: []string{""},
|
||||||
},
|
},
|
||||||
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
|
want: "sha256:9b81e0bf3aa7809a0f41bc696f353fca5645bcb63b975ab30e23d81886df2e61",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with single non empty string in file patterns",
|
name: "with single non empty string in file patterns",
|
||||||
@@ -90,7 +90,7 @@ func TestCalcKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
patterns: []string{"test"},
|
patterns: []string{"test"},
|
||||||
},
|
},
|
||||||
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
|
want: "sha256:7d91b2623ae4b5641a1f36efa59c774231efe8c28c27a03869894fd49b047fe8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with non empty followed by empty string in file patterns",
|
name: "with non empty followed by empty string in file patterns",
|
||||||
@@ -102,7 +102,7 @@ func TestCalcKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
patterns: []string{"test", ""},
|
patterns: []string{"test", ""},
|
||||||
},
|
},
|
||||||
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
|
want: "sha256:5c7f1555e95fc60cdaa7e92e99aee15ee7be356fad9e83f1c24a3be06713a5a8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with non empty preceded by empty string in file patterns",
|
name: "with non empty preceded by empty string in file patterns",
|
||||||
@@ -114,7 +114,7 @@ func TestCalcKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
patterns: []string{"", "test"},
|
patterns: []string{"", "test"},
|
||||||
},
|
},
|
||||||
want: "sha256:d69f13df33f4c159b4ea54c1967384782fcefb5e2a19af35f4cd6d2896e9285e",
|
want: "sha256:5c7f1555e95fc60cdaa7e92e99aee15ee7be356fad9e83f1c24a3be06713a5a8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with policy",
|
name: "with policy",
|
||||||
@@ -158,13 +158,13 @@ func TestCalcKey(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
artifactOpt := artifact.Option{
|
artifactOpt := artifact.Option{
|
||||||
SkipFiles: tt.args.skipFiles,
|
SkipFiles: tt.args.skipFiles,
|
||||||
SkipDirs: tt.args.skipDirs,
|
SkipDirs: tt.args.skipDirs,
|
||||||
|
FilePatterns: tt.args.patterns,
|
||||||
|
|
||||||
MisconfScannerOption: config.ScannerOption{
|
MisconfScannerOption: config.ScannerOption{
|
||||||
FilePatterns: tt.args.patterns,
|
PolicyPaths: tt.args.policy,
|
||||||
PolicyPaths: tt.args.policy,
|
DataPaths: tt.args.data,
|
||||||
DataPaths: tt.args.data,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
got, err := CalcKey(tt.args.key, tt.args.analyzerVersions, tt.args.hookVersions, artifactOpt)
|
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/artifact/local"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
|
|
||||||
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigScanner struct {
|
type ConfigScanner struct {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ func newMisconfPostHandler(artifactOpt artifact.Option) (handler.PostHandler, er
|
|||||||
tfOpts := addTFOpts(opts, artifactOpt.MisconfScannerOption)
|
tfOpts := addTFOpts(opts, artifactOpt.MisconfScannerOption)
|
||||||
|
|
||||||
return misconfPostHandler{
|
return misconfPostHandler{
|
||||||
filePatterns: artifactOpt.MisconfScannerOption.FilePatterns,
|
filePatterns: artifactOpt.FilePatterns,
|
||||||
scanners: map[string]scanners.FSScanner{
|
scanners: map[string]scanners.FSScanner{
|
||||||
types.Terraform: tfscanner.New(tfOpts...),
|
types.Terraform: tfscanner.New(tfOpts...),
|
||||||
types.CloudFormation: cfscanner.New(opts...),
|
types.CloudFormation: cfscanner.New(opts...),
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"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/artifact"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -56,7 +55,7 @@ func Test_Handle(t *testing.T) {
|
|||||||
result := &analyzer.AnalysisResult{
|
result := &analyzer.AnalysisResult{
|
||||||
Files: tt.files,
|
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)
|
assert.NoError(t, err)
|
||||||
blobInfo := &types.BlobInfo{}
|
blobInfo := &types.BlobInfo{}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ import (
|
|||||||
// config-policy: "custom-policy/policy"
|
// config-policy: "custom-policy/policy"
|
||||||
// policy-namespaces: "user"
|
// policy-namespaces: "user"
|
||||||
var (
|
var (
|
||||||
FilePatternsFlag = Flag{
|
|
||||||
Name: "file-patterns",
|
|
||||||
ConfigName: "misconfiguration.file-patterns",
|
|
||||||
Value: []string{},
|
|
||||||
Usage: "specify config file patterns, available with '--security-checks config'",
|
|
||||||
}
|
|
||||||
IncludeNonFailuresFlag = Flag{
|
IncludeNonFailuresFlag = Flag{
|
||||||
Name: "include-non-failures",
|
Name: "include-non-failures",
|
||||||
ConfigName: "misconfiguration.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.
|
// MisconfFlagGroup composes common printer flag structs used for commands providing misconfinguration scanning.
|
||||||
type MisconfFlagGroup struct {
|
type MisconfFlagGroup struct {
|
||||||
FilePatterns *Flag
|
|
||||||
IncludeNonFailures *Flag
|
IncludeNonFailures *Flag
|
||||||
SkipPolicyUpdate *Flag // deprecated
|
SkipPolicyUpdate *Flag // deprecated
|
||||||
Trace *Flag
|
Trace *Flag
|
||||||
@@ -106,7 +99,6 @@ type MisconfFlagGroup struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MisconfOptions struct {
|
type MisconfOptions struct {
|
||||||
FilePatterns []string
|
|
||||||
IncludeNonFailures bool
|
IncludeNonFailures bool
|
||||||
SkipPolicyUpdate bool // deprecated
|
SkipPolicyUpdate bool // deprecated
|
||||||
Trace bool
|
Trace bool
|
||||||
@@ -126,7 +118,6 @@ type MisconfOptions struct {
|
|||||||
|
|
||||||
func NewMisconfFlagGroup() *MisconfFlagGroup {
|
func NewMisconfFlagGroup() *MisconfFlagGroup {
|
||||||
return &MisconfFlagGroup{
|
return &MisconfFlagGroup{
|
||||||
FilePatterns: &FilePatternsFlag,
|
|
||||||
IncludeNonFailures: &IncludeNonFailuresFlag,
|
IncludeNonFailures: &IncludeNonFailuresFlag,
|
||||||
SkipPolicyUpdate: &SkipPolicyUpdateFlag,
|
SkipPolicyUpdate: &SkipPolicyUpdateFlag,
|
||||||
Trace: &TraceFlag,
|
Trace: &TraceFlag,
|
||||||
@@ -147,7 +138,6 @@ func (f *MisconfFlagGroup) Name() string {
|
|||||||
|
|
||||||
func (f *MisconfFlagGroup) Flags() []*Flag {
|
func (f *MisconfFlagGroup) Flags() []*Flag {
|
||||||
return []*Flag{
|
return []*Flag{
|
||||||
f.FilePatterns,
|
|
||||||
f.IncludeNonFailures,
|
f.IncludeNonFailures,
|
||||||
f.SkipPolicyUpdate,
|
f.SkipPolicyUpdate,
|
||||||
f.Trace,
|
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")
|
log.Logger.Warn("'--skip-policy-update' is no longer necessary as the built-in policies are embedded into the binary")
|
||||||
}
|
}
|
||||||
return MisconfOptions{
|
return MisconfOptions{
|
||||||
FilePatterns: getStringSlice(f.FilePatterns),
|
|
||||||
IncludeNonFailures: getBool(f.IncludeNonFailures),
|
IncludeNonFailures: getBool(f.IncludeNonFailures),
|
||||||
Trace: getBool(f.Trace),
|
Trace: getBool(f.Trace),
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ var (
|
|||||||
Value: fmt.Sprintf("%s,%s", types.SecurityCheckVulnerability, types.SecurityCheckSecret),
|
Value: fmt.Sprintf("%s,%s", types.SecurityCheckVulnerability, types.SecurityCheckSecret),
|
||||||
Usage: "comma-separated list of what security issues to detect (vuln,config,secret)",
|
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 {
|
type ScanFlagGroup struct {
|
||||||
@@ -42,6 +48,7 @@ type ScanFlagGroup struct {
|
|||||||
SkipFiles *Flag
|
SkipFiles *Flag
|
||||||
OfflineScan *Flag
|
OfflineScan *Flag
|
||||||
SecurityChecks *Flag
|
SecurityChecks *Flag
|
||||||
|
FilePatterns *Flag
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScanOptions struct {
|
type ScanOptions struct {
|
||||||
@@ -50,6 +57,7 @@ type ScanOptions struct {
|
|||||||
SkipFiles []string
|
SkipFiles []string
|
||||||
OfflineScan bool
|
OfflineScan bool
|
||||||
SecurityChecks []string
|
SecurityChecks []string
|
||||||
|
FilePatterns []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScanFlagGroup() *ScanFlagGroup {
|
func NewScanFlagGroup() *ScanFlagGroup {
|
||||||
@@ -58,6 +66,7 @@ func NewScanFlagGroup() *ScanFlagGroup {
|
|||||||
SkipFiles: &SkipFilesFlag,
|
SkipFiles: &SkipFilesFlag,
|
||||||
OfflineScan: &OfflineScanFlag,
|
OfflineScan: &OfflineScanFlag,
|
||||||
SecurityChecks: &SecurityChecksFlag,
|
SecurityChecks: &SecurityChecksFlag,
|
||||||
|
FilePatterns: &FilePatternsFlag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +75,7 @@ func (f *ScanFlagGroup) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *ScanFlagGroup) Flags() []*Flag {
|
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) {
|
func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
|
||||||
@@ -85,6 +94,7 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
|
|||||||
SkipFiles: getStringSlice(f.SkipFiles),
|
SkipFiles: getStringSlice(f.SkipFiles),
|
||||||
OfflineScan: getBool(f.OfflineScan),
|
OfflineScan: getBool(f.OfflineScan),
|
||||||
SecurityChecks: securityChecks,
|
SecurityChecks: securityChecks,
|
||||||
|
FilePatterns: getStringSlice(f.FilePatterns),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,10 @@ func TestManager_Register(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Confirm the analyzer is registered
|
// 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)
|
assert.Equal(t, tt.wantAnalyzerVersions, got)
|
||||||
|
|
||||||
// Confirm the post scanner is registered
|
// Confirm the post scanner is registered
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ type ScanOptions struct {
|
|||||||
ScanRemovedPackages bool
|
ScanRemovedPackages bool
|
||||||
ListAllPackages bool
|
ListAllPackages bool
|
||||||
LicenseCategories map[types.LicenseCategory][]string
|
LicenseCategories map[types.LicenseCategory][]string
|
||||||
|
FilePatterns []string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user