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

feat(misconf): support for ignoring by inline comments for Dockerfile #8115

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
11 changes: 9 additions & 2 deletions docs/docs/scanner/misconfiguration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,9 @@ From the Terraform [docs](https://developer.hashicorp.com/terraform/cli/config/c
If multiple variables evaluate to the same hostname, Trivy will choose the environment variable name where the dashes have not been encoded as double underscores.


### Skipping resources by inline comments
### Skipping detected misconfigurations by inline comments

Trivy supports ignoring misconfigured resources by inline comments for Terraform and CloudFormation configuration files only.
Trivy supports ignoring detected misconfigurations by inline comments for Terraform, CloudFormation (YAML) and Dockerfile configuration files only.

In cases where Trivy can detect comments of a specific format immediately adjacent to resource definitions, it is possible to ignore findings from a single source of resource definition (in contrast to `.trivyignore`, which has a directory-wide scope on all of the files scanned). The format for these comments is `trivy:ignore:<rule>` immediately following the format-specific line-comment [token](https://developer.hashicorp.com/terraform/language/syntax/configuration#comments).

Expand Down Expand Up @@ -503,6 +503,13 @@ Resources:
BucketName: test-bucket
```

Example for Dockerfile:
```Dockerfile
FROM scratch
# trivy:ignore:AVD-DS-0022
MAINTAINER [email protected]
```

#### Expiration Date

You can specify the expiration date of the ignore rule in `yyyy-mm-dd` format. This is a useful feature when you want to make sure that an ignored issue is not forgotten and worth revisiting in the future. For example:
Expand Down
72 changes: 72 additions & 0 deletions pkg/iac/scanners/dockerfile/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package dockerfile_test
import (
"bytes"
"context"
"strings"
"testing"
"testing/fstest"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -630,3 +632,73 @@ COPY --from=dep /binary /`
}

}

func Test_IgnoreByInlineComments(t *testing.T) {
tests := []struct {
name string
src string
expected bool
}{
{
name: "without ignore rule",
src: `FROM scratch
MAINTAINER [email protected]`,
expected: true,
},
{
name: "with ignore rule",
src: `FROM scratch
# trivy:ignore:USER-TEST-0001
MAINTAINER [email protected]`,
expected: false,
},
}

check := `# METADATA
# title: test
# schemas:
# - input: schema["dockerfile"]
# custom:
# avd_id: USER-TEST-0001
# short_code: maintainer-deprecated
# input:
# selector:
# - type: dockerfile
package user.test0001

import rego.v1

get_maintainer contains cmd if {
cmd := input.Stages[_].Commands[_]
cmd.Cmd == "maintainer"
}

deny contains res if {
cmd := get_maintainer[_]
msg := sprintf("MAINTAINER should not be used: 'MAINTAINER %s'", [cmd.Value[0]])
res := result.new(msg, cmd)
}
`

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fsys := fstest.MapFS{
"Dockerfile": &fstest.MapFile{Data: []byte(tt.src)},
}

scanner := dockerfile.NewScanner(
rego.WithPolicyReader(strings.NewReader(check)),
rego.WithPolicyNamespaces("user"),
rego.WithEmbeddedLibraries(true),
rego.WithRegoErrorLimits(0),
)
results, err := scanner.ScanFS(context.TODO(), fsys, ".")
require.NoError(t, err)
if tt.expected {
testutil.AssertRuleFound(t, "dockerfile-general-maintainer-deprecated", results, "")
} else {
testutil.AssertRuleNotFailed(t, "dockerfile-general-maintainer-deprecated", results, "")
}
})
}
}
32 changes: 32 additions & 0 deletions pkg/iac/scanners/generic/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import (
"sync"

"github.com/BurntSushi/toml"
"github.com/samber/lo"
"gopkg.in/yaml.v3"

"github.com/aquasecurity/trivy/pkg/iac/ignore"
"github.com/aquasecurity/trivy/pkg/iac/rego"
"github.com/aquasecurity/trivy/pkg/iac/scan"
"github.com/aquasecurity/trivy/pkg/iac/scanners/options"
Expand Down Expand Up @@ -122,9 +124,18 @@ func (s *GenericScanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (sc
return nil, err
}
results.SetSourceAndFilesystem("", fsys, false)

if err := s.applyIgnoreRules(fsys, results); err != nil {
return nil, err
}

return results, nil
}

func (s *GenericScanner) supportsIgnoreRules() bool {
return s.source == types.SourceDockerfile
}

func (s *GenericScanner) parseFS(ctx context.Context, fsys fs.FS, path string) (map[string]any, error) {
files := make(map[string]any)
if err := fs.WalkDir(fsys, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error {
Expand Down Expand Up @@ -173,6 +184,27 @@ func (s *GenericScanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) {
return regoScanner, nil
}

func (s *GenericScanner) applyIgnoreRules(fsys fs.FS, results scan.Results) error {
if !s.supportsIgnoreRules() {
return nil
}

uniqueFiles := lo.Uniq(lo.Map(results.GetFailed(), func(res scan.Result, _ int) string {
return res.Metadata().Range().GetFilename()
}))

for _, filename := range uniqueFiles {
content, err := fs.ReadFile(fsys, filename)
if err != nil {
return err
}

ignoreRules := ignore.Parse(string(content), filename, "")
results.Ignore(ignoreRules, nil)
}
return nil
}

func parseJson(ctx context.Context, r io.Reader, _ string) (any, error) {
var target any
if err := json.NewDecoder(r).Decode(&target); err != nil {
Expand Down
Loading