Skip to content

Commit

Permalink
fix(misconf): do not filter Terraform plan JSON by name (#7406)
Browse files Browse the repository at this point in the history
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
  • Loading branch information
nikpivkin authored Aug 28, 2024
1 parent 44e4686 commit 9d7264a
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 79 deletions.
22 changes: 11 additions & 11 deletions docs/docs/coverage/iac/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ Trivy scans Infrastructure as Code (IaC) files for

## Supported configurations

| Config type | File patterns |
|-------------------------------------|-----------------------------------------------|
| [Kubernetes](kubernetes.md) | \*.yml, \*.yaml, \*.json |
| [Docker](docker.md) | Dockerfile, Containerfile |
| [Terraform](terraform.md) | \*.tf, \*.tf.json, \*.tfvars |
| [Terraform Plan](terraform.md) | tfplan, \*.tfplan, \*.tfplan.json, \*.tf.json |
| [CloudFormation](cloudformation.md) | \*.yml, \*.yaml, \*.json |
| [Azure ARM Template](azure-arm.md) | \*.json |
| [Helm](helm.md) | \*.yaml, \*.tpl, \*.tar.gz, etc. |
| [YAML][json-and-yaml] | \*.yaml, \*.yml |
| [JSON][json-and-yaml] | \*.json |
| Config type | File patterns |
|-------------------------------------|----------------------------------|
| [Kubernetes](kubernetes.md) | \*.yml, \*.yaml, \*.json |
| [Docker](docker.md) | Dockerfile, Containerfile |
| [Terraform](terraform.md) | \*.tf, \*.tf.json, \*.tfvars |
| [Terraform Plan](terraform.md) | tfplan, \*.tfplan, \*.json |
| [CloudFormation](cloudformation.md) | \*.yml, \*.yaml, \*.json |
| [Azure ARM Template](azure-arm.md) | \*.json |
| [Helm](helm.md) | \*.yaml, \*.tpl, \*.tar.gz, etc. |
| [YAML][json-and-yaml] | \*.yaml, \*.yml |
| [JSON][json-and-yaml] | \*.json |

[misconf]: ../../scanner/misconfiguration/index.md
[secret]: ../../scanner/secret.md
Expand Down
16 changes: 10 additions & 6 deletions pkg/iac/detection/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,17 @@ func init() {

contents := make(map[string]any)
err := json.NewDecoder(r).Decode(&contents)
if err == nil {
if _, ok := contents["terraform_version"]; ok {
_, stillOk := contents["format_version"]
return stillOk
if err != nil {
return false
}

for _, k := range []string{"terraform_version", "format_version"} {
if _, ok := contents[k]; !ok {
return false
}
}

return true
}
return false
}
Expand Down Expand Up @@ -150,8 +155,7 @@ func init() {
return false
}

return (sniff.Parameters != nil && len(sniff.Parameters) > 0) ||
(sniff.Resources != nil && len(sniff.Resources) > 0)
return len(sniff.Parameters) > 0 || len(sniff.Resources) > 0
}

matchers[FileTypeDockerfile] = func(name string, _ io.ReadSeeker) bool {
Expand Down
38 changes: 19 additions & 19 deletions pkg/iac/scanners/terraformplan/tfjson/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"io"
"io/fs"

"github.com/bmatcuk/doublestar/v4"

"github.com/aquasecurity/trivy/pkg/iac/framework"
"github.com/aquasecurity/trivy/pkg/iac/scan"
"github.com/aquasecurity/trivy/pkg/iac/scanners/options"
Expand All @@ -17,11 +15,6 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
)

var tfPlanExts = []string{
"**/*tfplan.json",
"**/*tf.json",
}

type Scanner struct {
parser *parser.Parser
logger *log.Logger
Expand Down Expand Up @@ -93,25 +86,32 @@ func (s *Scanner) Name() string {
return "Terraform Plan JSON"
}

func (s *Scanner) ScanFS(ctx context.Context, inputFS fs.FS, dir string) (scan.Results, error) {
var filesFound []string
func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (scan.Results, error) {

var results scan.Results

for _, ext := range tfPlanExts {
files, err := doublestar.Glob(inputFS, ext, doublestar.WithFilesOnly())
walkFn := func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil, fmt.Errorf("unable to scan for terraform plan files: %w", err)
return err
}
filesFound = append(filesFound, files...)
}

var results scan.Results
for _, f := range filesFound {
res, err := s.ScanFile(f, inputFS)
if d.IsDir() {
return nil
}

res, err := s.ScanFile(path, fsys)
if err != nil {
return nil, err
return fmt.Errorf("failed to scan %s: %w", path, err)
}

results = append(results, res...)
return nil
}

if err := fs.WalkDir(fsys, dir, walkFn); err != nil {
return nil, err
}

return results, nil
}

Expand Down Expand Up @@ -148,7 +148,7 @@ func (s *Scanner) Scan(reader io.Reader) (scan.Results, error) {

planFS, err := planFile.ToFS()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert plan to FS: %w", err)
}

scanner := terraformScanner.New(s.options...)
Expand Down
73 changes: 30 additions & 43 deletions pkg/iac/scanners/terraformplan/tfjson/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,7 @@ import (
"github.com/aquasecurity/trivy/pkg/iac/scanners/options"
)

func Test_TerraformScanner(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
inputFile string
inputRego string
options []options.ScannerOption
}{
{
name: "old rego metadata",
inputFile: "test/testdata/plan.json",
inputRego: `
package defsec.abcdefg
const defaultCheck = `package defsec.abcdefg
__rego_metadata__ := {
"id": "TEST123",
Expand All @@ -48,48 +35,40 @@ deny[cause] {
bucket := input.aws.s3.buckets[_]
bucket.name.value == "tfsec-plan-testing"
cause := bucket.name
}
`,
}`

func Test_TerraformScanner(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
inputFile string
check string
options []options.ScannerOption
}{
{
name: "old rego metadata",
inputFile: "test/testdata/plan.json",
check: defaultCheck,
options: []options.ScannerOption{
options.ScannerWithPolicyDirs("rules"),
options.ScannerWithRegoOnly(true),
options.ScannerWithEmbeddedPolicies(false)},
},
},
{
name: "with user namespace",
inputFile: "test/testdata/plan.json",
inputRego: `
# METADATA
# title: Bad buckets are bad
# description: Bad buckets are bad because they are not good.
# scope: package
# schemas:
# - input: schema["input"]
# custom:
# avd_id: AVD-TEST-0123
# severity: CRITICAL
# short_code: very-bad-misconfig
# recommended_action: "Fix the s3 bucket"
package user.foobar.ABC001
deny[cause] {
bucket := input.aws.s3.buckets[_]
bucket.name.value == "tfsec-plan-testing"
cause := bucket.name
}
`,
check: defaultCheck,
options: []options.ScannerOption{
options.ScannerWithPolicyDirs("rules"),
options.ScannerWithRegoOnly(true),
options.ScannerWithEmbeddedPolicies(false),
options.ScannerWithPolicyNamespaces("user"),
},
},
{
name: "with templated plan json",
inputFile: "test/testdata/plan_with_template.json",
inputRego: `
check: `
# METADATA
# title: Bad buckets are bad
# description: Bad buckets are bad because they are not good.
Expand All @@ -113,19 +92,27 @@ deny[cause] {
options: []options.ScannerOption{
options.ScannerWithPolicyDirs("rules"),
options.ScannerWithRegoOnly(true),
options.ScannerWithEmbeddedPolicies(false),
options.ScannerWithPolicyNamespaces("user"),
},
},
{
name: "plan with arbitrary name",
inputFile: "test/testdata/arbitrary_name.json",
check: defaultCheck,
options: []options.ScannerOption{
options.ScannerWithPolicyDirs("rules"),
options.ScannerWithRegoOnly(true),
options.ScannerWithPolicyNamespaces("user"),
},
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
b, _ := os.ReadFile(tc.inputFile)
fs := testutil.CreateFS(t, map[string]string{
"/code/main.tfplan.json": string(b),
"/rules/test.rego": tc.inputRego,
"/rules/test.rego": tc.check,
})

so := append(tc.options, options.ScannerWithPolicyFilesystem(fs))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"format_version":"1.2","terraform_version":"1.8.1","planned_values":{"root_module":{"resources":[{"address":"aws_s3_bucket.this","mode":"managed","type":"aws_s3_bucket","name":"this","provider_name":"registry.opentofu.org/hashicorp/aws","schema_version":0,"values":{"bucket":"tfsec-plan-testing","force_destroy":false,"tags":null,"timeouts":null},"sensitive_values":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[],"website":[]}}]}},"resource_drift":[{"address":"aws_security_group.this","mode":"managed","type":"aws_security_group","name":"this","provider_name":"registry.opentofu.org/hashicorp/aws","change":{"actions":["delete"],"before":{"arn":"arn:aws:ec2:us-west-1:145167242158:security-group/sg-0f3ac859864629452","description":"Managed by Terraform","egress":[{"cidr_blocks":[],"description":"","from_port":0,"ipv6_cidr_blocks":[],"prefix_list_ids":[],"protocol":"-1","security_groups":[],"self":false,"to_port":0}],"id":"sg-0f3ac859864629452","ingress":[],"name":"test_sg","name_prefix":"","owner_id":"145167242158","revoke_rules_on_delete":false,"tags":null,"tags_all":{},"timeouts":null,"vpc_id":"vpc-06165c55f5a70fdfa"},"after":null,"after_unknown":{},"before_sensitive":{"egress":[{"cidr_blocks":[],"ipv6_cidr_blocks":[],"prefix_list_ids":[],"security_groups":[]}],"ingress":[],"tags_all":{}},"after_sensitive":false}}],"resource_changes":[{"address":"aws_s3_bucket.this","mode":"managed","type":"aws_s3_bucket","name":"this","provider_name":"registry.opentofu.org/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"bucket":"tfsec-plan-testing","force_destroy":false,"tags":null,"timeouts":null},"after_unknown":{"acceleration_status":true,"acl":true,"arn":true,"bucket_domain_name":true,"bucket_prefix":true,"bucket_regional_domain_name":true,"cors_rule":true,"grant":true,"hosted_zone_id":true,"id":true,"lifecycle_rule":true,"logging":true,"object_lock_configuration":true,"object_lock_enabled":true,"policy":true,"region":true,"replication_configuration":true,"request_payer":true,"server_side_encryption_configuration":true,"tags_all":true,"versioning":true,"website":true,"website_domain":true,"website_endpoint":true},"before_sensitive":false,"after_sensitive":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[],"website":[]}}}],"configuration":{"provider_config":{"aws":{"name":"aws","full_name":"registry.opentofu.org/hashicorp/aws"}},"root_module":{"resources":[{"address":"aws_s3_bucket.this","mode":"managed","type":"aws_s3_bucket","name":"this","provider_config_key":"aws","expressions":{"bucket":{"constant_value":"tfsec-plan-testing"}},"schema_version":0}]}},"timestamp":"2024-08-28T04:27:38Z","errored":false}

0 comments on commit 9d7264a

Please sign in to comment.