Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(misconf): introduce generic scanner #7515

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 7 additions & 57 deletions pkg/iac/scanners/dockerfile/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,26 @@ import (
"context"
"fmt"
"io"
"io/fs"
"path/filepath"
"strings"

"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/buildkit/frontend/dockerfile/parser"

"github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile"
"github.com/aquasecurity/trivy/pkg/log"
)

type Parser struct {
logger *log.Logger
}

// New creates a new Dockerfile parser
func New() *Parser {
return &Parser{
logger: log.WithPrefix("dockerfile parser"),
}
}

func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]*dockerfile.Dockerfile, error) {

files := make(map[string]*dockerfile.Dockerfile)
if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if err != nil {
return err
}
if entry.IsDir() {
return nil
}

df, err := p.ParseFile(ctx, target, path)
if err != nil {
p.logger.Error("Failed to parse Dockerfile", log.FilePath(path), log.Err(err))
return nil
}
files[path] = df
return nil
}); err != nil {
return nil, err
}
return files, nil
}

// ParseFile parses Dockerfile content from the provided filesystem path.
func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (*dockerfile.Dockerfile, error) {
f, err := fsys.Open(filepath.ToSlash(path))
if err != nil {
return nil, err
}
defer func() { _ = f.Close() }()
return p.parse(path, f)
}

func (p *Parser) parse(path string, r io.Reader) (*dockerfile.Dockerfile, error) {
func Parse(_ context.Context, r io.Reader, path string) (any, error) {
parsed, err := parser.Parse(r)
if err != nil {
return nil, fmt.Errorf("dockerfile parse error: %w", err)
}

var parsedFile dockerfile.Dockerfile
var stage dockerfile.Stage
var stageIndex int
var (
parsedFile dockerfile.Dockerfile
stage dockerfile.Stage
stageIndex int
)

fromValue := "args"
for _, child := range parsed.AST.Children {
child.Value = strings.ToLower(child.Value)
Expand Down
13 changes: 9 additions & 4 deletions pkg/iac/scanners/dockerfile/parser/parser_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package parser
package parser_test

import (
"context"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile"
"github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser"
)

func Test_Parser(t *testing.T) {
Expand All @@ -15,12 +19,13 @@ RUN make /app
CMD python /app/app.py
`

df, err := New().parse("Dockerfile", strings.NewReader(input))
res, err := parser.Parse(context.TODO(), strings.NewReader(input), "Dockerfile")
require.NoError(t, err)

assert.Len(t, df.Stages, 1)
df, ok := res.(*dockerfile.Dockerfile)
require.True(t, ok)

require.Len(t, df.Stages, 1)
assert.Len(t, df.Stages, 1)

assert.Equal(t, "ubuntu:18.04", df.Stages[0].Name)
commands := df.Stages[0].Commands
Expand Down
108 changes: 3 additions & 105 deletions pkg/iac/scanners/dockerfile/scanner.go
Original file line number Diff line number Diff line change
@@ -1,114 +1,12 @@
package dockerfile

import (
"context"
"io/fs"
"sync"

"github.com/aquasecurity/trivy/pkg/iac/framework"
"github.com/aquasecurity/trivy/pkg/iac/rego"
"github.com/aquasecurity/trivy/pkg/iac/scan"
"github.com/aquasecurity/trivy/pkg/iac/scanners"
"github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser"
"github.com/aquasecurity/trivy/pkg/iac/scanners/generic"
"github.com/aquasecurity/trivy/pkg/iac/scanners/options"
"github.com/aquasecurity/trivy/pkg/iac/types"
"github.com/aquasecurity/trivy/pkg/log"
)

var _ scanners.FSScanner = (*Scanner)(nil)
var _ options.ConfigurableScanner = (*Scanner)(nil)

type Scanner struct {
mu sync.Mutex
logger *log.Logger
parser *parser.Parser
regoScanner *rego.Scanner
options []options.ScannerOption
}

func (s *Scanner) SetIncludeDeprecatedChecks(bool) {}
func (s *Scanner) SetRegoOnly(bool) {}
func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {}

func (s *Scanner) Name() string {
return "Dockerfile"
}

func NewScanner(opts ...options.ScannerOption) *Scanner {
s := &Scanner{
options: opts,
logger: log.WithPrefix("dockerfile scanner"),
}
for _, opt := range opts {
opt(s)
}
s.parser = parser.New()
return s
}

func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) {

files, err := s.parser.ParseFS(ctx, fsys, path)
if err != nil {
return nil, err
}

if len(files) == 0 {
return nil, nil
}

var inputs []rego.Input
for path, dfile := range files {
inputs = append(inputs, rego.Input{
Path: path,
FS: fsys,
Contents: dfile.ToRego(),
})
}

results, err := s.scanRego(ctx, fsys, inputs...)
if err != nil {
return nil, err
}
return results, nil
}

func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) {
dockerfile, err := s.parser.ParseFile(ctx, fsys, path)
if err != nil {
return nil, err
}
s.logger.Debug("Scanning", log.FilePath(path))
return s.scanRego(ctx, fsys, rego.Input{
Path: path,
Contents: dockerfile.ToRego(),
})
}

func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.regoScanner != nil {
return s.regoScanner, nil
}

regoScanner := rego.NewScanner(types.SourceDockerfile, s.options...)
if err := regoScanner.LoadPolicies(srcFS); err != nil {
return nil, err
}
s.regoScanner = regoScanner
return regoScanner, nil
}

func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) {
regoScanner, err := s.initRegoScanner(srcFS)
if err != nil {
return nil, err
}
results, err := regoScanner.ScanInput(ctx, inputs...)
if err != nil {
return nil, err
}
results.SetSourceAndFilesystem("", srcFS, false)
return results, nil
func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner {
return generic.NewScanner("Dockerfile", types.SourceDockerfile, generic.ParseFunc(parser.Parse), opts...)
}
7 changes: 4 additions & 3 deletions pkg/iac/scanners/dockerfile/scanner_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dockerfile
package dockerfile_test

import (
"bytes"
Expand All @@ -13,6 +13,7 @@ import (
"github.com/aquasecurity/trivy/pkg/iac/rego"
"github.com/aquasecurity/trivy/pkg/iac/rego/schemas"
"github.com/aquasecurity/trivy/pkg/iac/scan"
"github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile"
)

const DS006PolicyWithDockerfileSchema = `# METADATA
Expand Down Expand Up @@ -219,7 +220,7 @@ USER root
"/rules/rule.rego": DS006LegacyWithOldStyleMetadata,
})

scanner := NewScanner(rego.WithPolicyDirs("rules"))
scanner := dockerfile.NewScanner(rego.WithPolicyDirs("rules"))

results, err := scanner.ScanFS(context.TODO(), fs, "code")
require.NoError(t, err)
Expand Down Expand Up @@ -563,7 +564,7 @@ COPY --from=dep /binary /`

var traceBuf bytes.Buffer

scanner := NewScanner(
scanner := dockerfile.NewScanner(
rego.WithPolicyDirs("rules"),
rego.WithEmbeddedLibraries(true),
rego.WithTrace(&traceBuf),
Expand Down
Loading