Skip to content

Commit

Permalink
refactor(misconf): simplify k8s scanner (#7717)
Browse files Browse the repository at this point in the history
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
  • Loading branch information
nikpivkin authored Oct 17, 2024
1 parent a7baa93 commit cd44bb4
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 705 deletions.
2 changes: 1 addition & 1 deletion pkg/iac/scanners/helm/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS)
file := file
s.logger.Debug("Processing rendered chart file", log.FilePath(file.TemplateFilePath))

manifests, err := kparser.New().Parse(strings.NewReader(file.ManifestContent), file.TemplateFilePath)
manifests, err := kparser.Parse(ctx, strings.NewReader(file.ManifestContent), file.TemplateFilePath)
if err != nil {
return nil, fmt.Errorf("unmarshal yaml: %w", err)
}
Expand Down
57 changes: 1 addition & 56 deletions pkg/iac/scanners/kubernetes/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,14 @@ import (
"encoding/json"
"fmt"
"io"
"io/fs"
"path/filepath"
"regexp"
"strings"

"gopkg.in/yaml.v3"
kyaml "sigs.k8s.io/yaml"

"github.com/aquasecurity/trivy/pkg/log"
)

type Parser struct {
logger *log.Logger
}

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

func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string][]any, error) {
files := make(map[string][]any)
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
}

parsed, err := p.ParseFile(ctx, target, path)
if err != nil {
p.logger.Error("Parse error", log.FilePath(path), log.Err(err))
return nil
}

files[path] = parsed
return nil
}); err != nil {
return nil, err
}
return files, nil
}

// ParseFile parses Kubernetes manifest from the provided filesystem path.
func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) ([]any, error) {
f, err := fsys.Open(filepath.ToSlash(path))
if err != nil {
return nil, err
}
defer func() { _ = f.Close() }()
return p.Parse(f, path)
}

func (p *Parser) Parse(r io.Reader, path string) ([]any, error) {

func Parse(_ context.Context, r io.Reader, path string) ([]any, error) {
contents, err := io.ReadAll(r)
if err != nil {
return nil, err
Expand Down
112 changes: 5 additions & 107 deletions pkg/iac/scanners/kubernetes/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,119 +3,17 @@ package kubernetes
import (
"context"
"io"
"io/fs"
"path/filepath"
"sort"
"sync"

"github.com/liamg/memoryfs"

"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/generic"
"github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser"
"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
options []options.ScannerOption
regoScanner *rego.Scanner
parser *parser.Parser
}

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

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

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

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.SourceKubernetes, s.options...)
if err := regoScanner.LoadPolicies(srcFS); err != nil {
return nil, err
}
s.regoScanner = regoScanner
return regoScanner, nil
func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner {
return generic.NewScanner("Kubernetes", types.SourceKubernetes, generic.ParseFunc(parse), opts...)
}

func (s *Scanner) ScanReader(ctx context.Context, filename string, reader io.Reader) (scan.Results, error) {
memfs := memoryfs.New()
if err := memfs.MkdirAll(filepath.Base(filename), 0o700); err != nil {
return nil, err
}
data, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
if err := memfs.WriteFile(filename, data, 0o644); err != nil {
return nil, err
}
return s.ScanFS(ctx, memfs, ".")
}

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

k8sFilesets, err := s.parser.ParseFS(ctx, target, dir)
if err != nil {
return nil, err
}

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

var inputs []rego.Input
for path, k8sFiles := range k8sFilesets {
for _, content := range k8sFiles {
inputs = append(inputs, rego.Input{
Path: path,
FS: target,
Contents: content,
})
}
}

regoScanner, err := s.initRegoScanner(target)
if err != nil {
return nil, err
}

s.logger.Debug("Scanning files", log.Int("count", len(inputs)))
results, err := regoScanner.ScanInput(ctx, inputs...)
if err != nil {
return nil, err
}
results.SetSourceAndFilesystem("", target, false)

sort.Slice(results, func(i, j int) bool {
return results[i].Rule().AVDID < results[j].Rule().AVDID
})
return results, nil
func parse(ctx context.Context, r io.Reader, path string) (any, error) {
return parser.Parse(ctx, r, path)
}
Loading

0 comments on commit cd44bb4

Please sign in to comment.