From 9361cdb7e28fd304d6fd2a1091feac64a6786672 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Tue, 5 Mar 2024 01:37:31 +0300 Subject: [PATCH] feat(terraform): Terraform Plan snapshot scanning support (#6176) Co-authored-by: Simar Co-authored-by: simar7 <1254783+simar7@users.noreply.github.com> --- docs/docs/coverage/iac/terraform.md | 27 +- .../references/configuration/cli/trivy_aws.md | 2 +- .../configuration/cli/trivy_config.md | 2 +- .../configuration/cli/trivy_filesystem.md | 2 +- .../configuration/cli/trivy_image.md | 2 +- .../configuration/cli/trivy_kubernetes.md | 2 +- .../configuration/cli/trivy_repository.md | 2 +- .../configuration/cli/trivy_rootfs.md | 2 +- .../references/configuration/cli/trivy_vm.md | 2 +- .../scanner/misconfiguration/custom/index.md | 2 +- docs/tutorials/misconfiguration/terraform.md | 17 +- go.mod | 9 +- go.sum | 14 +- integration/aws_cloud_test.go | 37 +- internal/testutil/localstack.go | 51 +++ magefiles/magefile.go | 6 + magefiles/terraformplan.go | 140 ++++++ pkg/fanal/analyzer/config/all/import.go | 3 +- .../{terraformplan.go => json/json.go} | 12 +- .../json_test.go} | 0 .../config/terraformplan/snapshot/snapshot.go | 37 ++ .../terraformplan/snapshot/snapshot_test.go | 38 ++ pkg/fanal/analyzer/const.go | 18 +- pkg/fanal/artifact/image/image_test.go | 110 ++--- pkg/fanal/artifact/local/fs_test.go | 402 ++++++++++++++---- .../snapshots/multiple-failures/main.tf | 16 + .../snapshots/multiple-failures/more.tf | 3 + .../snapshots/multiple-failures/tfplan | Bin 0 -> 2789 bytes .../terraformplan/snapshots/passed/main.tf | 12 + .../terraformplan/snapshots/passed/tfplan | Bin 0 -> 2575 bytes .../snapshots/single-failure/main.tf | 12 + .../snapshots/single-failure/tfplan | Bin 0 -> 2563 bytes pkg/fanal/artifact/repo/git_test.go | 4 +- pkg/fanal/types/const.go | 19 +- pkg/iac/detection/detect.go | 30 +- pkg/iac/detection/detect_test.go | 2 +- .../terraformplan/snapshot/scanner.go | 75 ++++ .../terraformplan/snapshot/scanner_test.go | 132 ++++++ .../terraformplan/snapshot/snapshot.go | 200 +++++++++ .../terraformplan/snapshot/snapshot_test.go | 110 +++++ .../just-resource/checks/s3-bucket-name.rego | 21 + .../snapshot/testdata/just-resource/main.tf | 3 + .../testdata/just-resource/terraform.tf | 8 + .../snapshot/testdata/just-resource/tfplan | Bin 0 -> 2760 bytes .../snapshot/testdata/nested-modules/main.tf | 3 + .../nested-modules/modules/s3/main.tf | 8 + .../modules/s3/modules/logging/main.tf | 10 + .../testdata/nested-modules/terraform.tf | 8 + .../snapshot/testdata/nested-modules/tfplan | Bin 0 -> 3487 bytes .../checks/ec2-userdata.rego | 21 + .../testdata/with-local-module/main.tf | 5 + .../with-local-module/modules/ec2/main.tf | 34 ++ .../testdata/with-local-module/terraform.tf | 8 + .../testdata/with-local-module/tfplan | Bin 0 -> 4583 bytes .../checks/s3-bucket-name.rego | 21 + .../testdata/with-remote-module/main.tf | 6 + .../testdata/with-remote-module/terraform.tf | 8 + .../testdata/with-remote-module/tfplan | Bin 0 -> 13256 bytes .../{ => tfjson}/parser/option.go | 0 .../{ => tfjson}/parser/parser.go | 0 .../{ => tfjson}/parser/plan_file.go | 0 .../terraformplan/{ => tfjson}/scanner.go | 6 +- .../{ => tfjson}/scanner_test.go | 2 +- .../{ => tfjson}/test/parser_test.go | 4 +- .../{ => tfjson}/test/scanner_test.go | 6 +- .../{ => tfjson}/test/testdata/plan.json | 0 pkg/iac/types/range.go | 4 +- pkg/misconf/scanner.go | 34 +- pkg/misconf/scanner_test.go | 4 +- pkg/x/io/io.go | 30 ++ 70 files changed, 1538 insertions(+), 270 deletions(-) create mode 100644 internal/testutil/localstack.go create mode 100644 magefiles/terraformplan.go rename pkg/fanal/analyzer/config/terraformplan/{terraformplan.go => json/json.go} (70%) rename pkg/fanal/analyzer/config/terraformplan/{terraformplan_test.go => json/json_test.go} (100%) create mode 100644 pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot.go create mode 100644 pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot_test.go create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/main.tf create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/more.tf create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/tfplan create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/main.tf create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/tfplan create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/main.tf create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/tfplan create mode 100644 pkg/iac/scanners/terraformplan/snapshot/scanner.go create mode 100644 pkg/iac/scanners/terraformplan/snapshot/scanner_test.go create mode 100644 pkg/iac/scanners/terraformplan/snapshot/snapshot.go create mode 100644 pkg/iac/scanners/terraformplan/snapshot/snapshot_test.go create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/checks/s3-bucket-name.rego create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/terraform.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/tfplan create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/modules/logging/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/terraform.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/tfplan create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/checks/ec2-userdata.rego create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/modules/ec2/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/terraform.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/tfplan create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/checks/s3-bucket-name.rego create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/main.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/terraform.tf create mode 100644 pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/tfplan rename pkg/iac/scanners/terraformplan/{ => tfjson}/parser/option.go (100%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/parser/parser.go (100%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/parser/plan_file.go (100%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/scanner.go (96%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/scanner_test.go (99%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/test/parser_test.go (78%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/test/scanner_test.go (87%) rename pkg/iac/scanners/terraformplan/{ => tfjson}/test/testdata/plan.json (100%) diff --git a/docs/docs/coverage/iac/terraform.md b/docs/docs/coverage/iac/terraform.md index dff17725524f..843126f54d3a 100644 --- a/docs/docs/coverage/iac/terraform.md +++ b/docs/docs/coverage/iac/terraform.md @@ -8,18 +8,23 @@ Trivy supports the scanners listed in the table below. It supports the following formats: -| Format | Supported | -|:---------:|:---------:| -| JSON | ✓ | -| HCL | ✓ | -| Plan JSON | ✓ | - -Trivy can scan the results of `terraform plan`. -You can scan by passing the file generated as shown below to Trivy: - +| Format | Supported | +|:-------------:|:---------:| +| JSON | ✓ | +| HCL | ✓ | +| Plan Snapshot | ✓ | +| Plan JSON | ✓ | + +Trivy can scan Terraform Plan files (snapshots) or their JSON representations. To create a Terraform Plan and scan it, run the following command: +```bash +terraform plan --out tfplan +trivy conf tfplan ``` -$ terraform plan --out tfplan.binary -$ terraform show -json tfplan.binary > tfplan.json + +To scan a Terraform Plan representation in JSON format, run the following command: +```bash +terraform show -json tfplan > tfplan.json +trivy conf tfplan.json ``` ## Misconfiguration diff --git a/docs/docs/references/configuration/cli/trivy_aws.md b/docs/docs/references/configuration/cli/trivy_aws.md index 0218ccb1e987..af1ebc44a834 100644 --- a/docs/docs/references/configuration/cli/trivy_aws.md +++ b/docs/docs/references/configuration/cli/trivy_aws.md @@ -86,7 +86,7 @@ trivy aws [flags] --include-non-failures include successes and exceptions, available with '--scanners misconfig' --list-all-pkgs enabling the option will output all packages regardless of vulnerability --max-cache-age duration The maximum age of the cloud cache. Cached data will be requeried from the cloud provider if it is older than this. (default 24h0m0s) - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --policy-bundle-repository string OCI registry URL to retrieve policy bundle from (default "ghcr.io/aquasecurity/trivy-policies:0") diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 79d99cad7331..865ecb6ba605 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -29,7 +29,7 @@ trivy config [flags] DIR --ignorefile string specify .trivyignore file (default ".trivyignore") --include-non-failures include successes and exceptions, available with '--scanners misconfig' --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 217518e18203..9a7a8fe24bb2 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -51,7 +51,7 @@ trivy filesystem [flags] PATH --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs enabling the option will output all packages regardless of vulnerability - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index bc27d91213b3..e5484111bc86 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -69,7 +69,7 @@ trivy image [flags] IMAGE_NAME --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs enabling the option will output all packages regardless of vulnerability - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 8ac7b84e08c6..0b4ff3cf02d0 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -61,7 +61,7 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg: --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --kubeconfig string specify the kubeconfig file path to use --list-all-pkgs enabling the option will output all packages regardless of vulnerability - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) -n, --namespace string specify a namespace to scan --no-progress suppress progress bar --node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.0.9") diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index a497a389ab68..de211fca43a2 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -51,7 +51,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs enabling the option will output all packages regardless of vulnerability - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 79deabae2a42..fc5b6d9240ca 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -53,7 +53,7 @@ trivy rootfs [flags] ROOTDIR --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs enabling the option will output all packages regardless of vulnerability - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 2ac579ca53bc..282c96ea3b11 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -47,7 +47,7 @@ trivy vm [flags] VM_IMAGE --include-non-failures include successes and exceptions, available with '--scanners misconfig' --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db") --list-all-pkgs enabling the option will output all packages regardless of vulnerability - --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan]) + --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/scanner/misconfiguration/custom/index.md b/docs/docs/scanner/misconfiguration/custom/index.md index ef34d0f20414..b1c16219e8f8 100644 --- a/docs/docs/scanner/misconfiguration/custom/index.md +++ b/docs/docs/scanner/misconfiguration/custom/index.md @@ -27,7 +27,7 @@ In the above general file formats, Trivy automatically identifies the following - CloudFormation (JSON/YAML) - Kubernetes (JSON/YAML) - Helm (YAML) -- Terraform Plan (JSON) +- Terraform Plan (JSON/Snapshot) This is useful for filtering inputs, as described below. diff --git a/docs/tutorials/misconfiguration/terraform.md b/docs/tutorials/misconfiguration/terraform.md index 53ad9ec9e755..8240e1ba53b2 100644 --- a/docs/tutorials/misconfiguration/terraform.md +++ b/docs/tutorials/misconfiguration/terraform.md @@ -104,22 +104,7 @@ The `trivy config` command is a sub-command of the `trivy fs` command. You can l ## Scanning Terraform Plan files -Instead of scanning your different Terraform resources individually, you could also scan your terraform plan output before it is deployed for misconfiguration. This will give you insights into any misconfiguration of your resources as they would become deployed. [Here](https://aquasecurity.github.io/trivy/latest/docs/scanner/misconfiguration/custom/examples/#terraform-plan) is the link to the documentation. - -First, create a terraform plan and save it to a file: -``` -terraform plan --out tfplan.binary -``` - -Next, convert the file into json format: -``` -terraform show -json tfplan.binary > tfplan.json -``` - -Lastly, scan the file with the `trivy config` command: -``` -trivy config ./tfplan.json -``` +Instead of scanning your different Terraform resources individually, you could also scan your Terraform Plan file before it is deployed for misconfiguration. This will give you insights into any misconfiguration of your resources as they would become deployed. [Here](https://aquasecurity.github.io/trivy/latest/docs/coverage/iac/terraform/#terraform) is the link to the documentation. Note that you need to be able to create a terraform init and plan without any errors. diff --git a/go.mod b/go.mod index 5bada118dd48..539c723bddf5 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/csaf-poc/csaf_distribution/v3 v3.0.0 github.com/docker/docker v25.0.3+incompatible github.com/docker/go-connections v0.5.0 - github.com/fatih/color v1.15.0 + github.com/fatih/color v1.16.0 github.com/go-git/go-git/v5 v5.11.0 github.com/go-openapi/runtime v0.27.1 github.com/go-openapi/strfmt v0.22.0 @@ -125,7 +125,10 @@ require ( github.com/apparentlymart/go-cidr v1.1.0 github.com/aws/smithy-go v1.20.1 github.com/hashicorp/go-uuid v1.0.3 + github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/hc-install v0.6.3 github.com/hashicorp/hcl/v2 v2.19.1 + github.com/hashicorp/terraform-exec v0.20.0 github.com/liamg/iamgo v0.0.9 github.com/liamg/memoryfs v1.6.0 github.com/mitchellh/go-homedir v1.1.0 @@ -294,9 +297,9 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/terraform-json v0.19.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -315,7 +318,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect diff --git a/go.sum b/go.sum index 2f2e06853135..ba486cb50204 100644 --- a/go.sum +++ b/go.sum @@ -772,8 +772,8 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -1112,6 +1112,8 @@ github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hc-install v0.6.3 h1:yE/r1yJvWbtrJ0STwScgEnCanb0U9v7zp0Gbkmcoxqs= +github.com/hashicorp/hc-install v0.6.3/go.mod h1:KamGdbodYzlufbWh4r9NRo8y6GLHWZP2GBtdnms1Ln0= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= @@ -1120,6 +1122,10 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= +github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= +github.com/hashicorp/terraform-json v0.19.0 h1:e9DBKC5sxDfiJT7Zoi+yRIwqLVtFur/fwK/FuE6AWsA= +github.com/hashicorp/terraform-json v0.19.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= @@ -1269,8 +1275,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= diff --git a/integration/aws_cloud_test.go b/integration/aws_cloud_test.go index 3b1ca9568fab..9a8cb8781474 100644 --- a/integration/aws_cloud_test.go +++ b/integration/aws_cloud_test.go @@ -4,16 +4,13 @@ package integration import ( "context" - "fmt" "testing" "time" - dockercontainer "github.com/docker/docker/api/types/container" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/modules/localstack" + "github.com/aquasecurity/trivy/internal/testutil" awscommands "github.com/aquasecurity/trivy/pkg/cloud/aws/commands" "github.com/aquasecurity/trivy/pkg/flag" ) @@ -53,7 +50,8 @@ func TestAwsCommandRun(t *testing.T) { ctx := context.Background() - localstackC, addr := setupLocalStack(t, ctx) + localstackC, addr, err := testutil.SetupLocalStack(ctx, "2.2.0") + require.NoError(t, err) defer localstackC.Terminate(ctx) for _, tt := range tests { @@ -78,32 +76,3 @@ func TestAwsCommandRun(t *testing.T) { } } - -func setupLocalStack(t *testing.T, ctx context.Context) (*localstack.LocalStackContainer, string) { - t.Helper() - t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") - container, err := localstack.RunContainer(ctx, testcontainers.CustomizeRequest( - testcontainers.GenericContainerRequest{ - ContainerRequest: testcontainers.ContainerRequest{ - Image: "localstack/localstack:2.2.0", - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, - }, - }, - )) - require.NoError(t, err) - - p, err := container.MappedPort(ctx, "4566/tcp") - require.NoError(t, err) - - provider, err := testcontainers.NewDockerProvider() - require.NoError(t, err) - defer provider.Close() - - host, err := provider.DaemonHost(ctx) - require.NoError(t, err) - - return container, fmt.Sprintf("http://%s:%d", host, p.Int()) - -} diff --git a/internal/testutil/localstack.go b/internal/testutil/localstack.go new file mode 100644 index 000000000000..71eaf5a3fcf9 --- /dev/null +++ b/internal/testutil/localstack.go @@ -0,0 +1,51 @@ +package testutil + +import ( + "context" + "fmt" + "os" + + dockercontainer "github.com/docker/docker/api/types/container" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/modules/localstack" +) + +func SetupLocalStack(ctx context.Context, version string) (*localstack.LocalStackContainer, string, error) { + + if err := os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true"); err != nil { + return nil, "", err + } + + container, err := localstack.RunContainer(ctx, testcontainers.CustomizeRequest( + testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "localstack/localstack:" + version, + HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { + hostConfig.AutoRemove = true + }, + }, + }, + )) + if err != nil { + return nil, "", err + } + + p, err := container.MappedPort(ctx, "4566/tcp") + if err != nil { + return nil, "", err + } + + provider, err := testcontainers.NewDockerProvider() + if err != nil { + return nil, "", err + } + defer provider.Close() + + host, err := provider.DaemonHost(ctx) + if err != nil { + return nil, "", err + } + + return container, fmt.Sprintf("http://%s:%d", host, p.Int()), nil + +} diff --git a/magefiles/magefile.go b/magefiles/magefile.go index cafd8a045c47..f3024c99d7c4 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "io/fs" @@ -181,6 +182,11 @@ func (Test) FixtureVMImages() error { return fixtureVMImages() } +// FixtureTerraformPlanSnapshots generates Terraform Plan files in test folders +func (Test) FixtureTerraformPlanSnapshots() error { + return fixtureTerraformPlanSnapshots(context.TODO()) +} + // GenerateModules compiles WASM modules for unit tests func (Test) GenerateModules() error { pattern := filepath.Join("pkg", "module", "testdata", "*", "*.go") diff --git a/magefiles/terraformplan.go b/magefiles/terraformplan.go new file mode 100644 index 000000000000..de3d9016b6ed --- /dev/null +++ b/magefiles/terraformplan.go @@ -0,0 +1,140 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + "os" + "path/filepath" + "strings" + + hversion "github.com/hashicorp/go-version" //nolint:gomodguard // hc-install uses hashicorp/go-version + "github.com/hashicorp/hc-install/product" + "github.com/hashicorp/hc-install/releases" + "github.com/hashicorp/terraform-exec/tfexec" + "golang.org/x/sync/errgroup" + + "github.com/aquasecurity/trivy/internal/testutil" +) + +const ( + terraformVersion = "1.7.3" + terraformParallelLimit = 5 + + tfplanFile = "tfplan" +) + +func fixtureTerraformPlanSnapshots(ctx context.Context) error { + localstackC, addr, err := testutil.SetupLocalStack(ctx, "3.1.0") + if err != nil { + return err + } + defer localstackC.Terminate(ctx) + + envs := []struct { + key string + val string + }{ + {"AWS_DEFAULT_REGION", "us-east-1"}, + {"AWS_ACCESS_KEY_ID", "test"}, + {"AWS_SECRET_ACCESS_KEY", "test"}, + {"AWS_ENDPOINT_URL", addr}, + } + + for _, env := range envs { + if err := os.Setenv(env.key, env.val); err != nil { + return err + } + } + + dirs := []string{ + "pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots", + "pkg/iac/scanners/terraformplan/snapshot/testdata", + } + + var workingDirs []string + + for _, dir := range dirs { + entries, err := os.ReadDir(filepath.FromSlash(dir)) + if err != nil { + return err + } + + for _, entry := range entries { + workingDirs = append(workingDirs, filepath.Join(dir, entry.Name())) + } + } + + installer := &releases.ExactVersion{ + Product: product.Terraform, + Version: hversion.Must(hversion.NewVersion(terraformVersion)), + } + + execPath, err := installer.Install(ctx) + if err != nil { + return fmt.Errorf("failed to install Terraform: %w", err) + } + + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(terraformParallelLimit) + + for _, workingDir := range workingDirs { + workingDir := workingDir + g.Go(func() error { + if err := os.Remove(tfplanFile); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + + if err := generatePlan(ctx, execPath, workingDir); err != nil { + return fmt.Errorf("failed to generate Terraform Plan: %w", err) + } + + return nil + }) + } + + return g.Wait() +} + +func generatePlan(ctx context.Context, execPath, workingDir string) error { + if err := cleanup(workingDir); err != nil { + return err + } + defer cleanup(workingDir) + + tf, err := tfexec.NewTerraform(workingDir, execPath) + if err != nil { + return fmt.Errorf("failed to run Terraform: %w", err) + } + + prefix := fmt.Sprintf("tfplan:%s:", filepath.Base(workingDir)) + tf.SetLogger(log.New(os.Stdout, prefix, log.LstdFlags)) + + if err = tf.Init(ctx, tfexec.Upgrade(true)); err != nil { + return fmt.Errorf("failed to run Init cmd: %w", err) + } + + if _, err := tf.Plan(ctx, tfexec.Out(tfplanFile)); err != nil { + return fmt.Errorf("failed to run Plan cmd: %w", err) + } + + return nil +} + +func cleanup(workingDir string) error { + entries, err := os.ReadDir(workingDir) + if err != nil { + return err + } + + for _, entry := range entries { + if entry.Name() == "terraform.tfstate" || strings.HasPrefix(entry.Name(), ".terraform") { + path := filepath.Join(workingDir, entry.Name()) + if err := os.RemoveAll(path); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + } + } + return nil +} diff --git a/pkg/fanal/analyzer/config/all/import.go b/pkg/fanal/analyzer/config/all/import.go index c66de2fa6d89..b171ab5e8a7f 100644 --- a/pkg/fanal/analyzer/config/all/import.go +++ b/pkg/fanal/analyzer/config/all/import.go @@ -7,5 +7,6 @@ import ( _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/helm" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/k8s" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraform" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraformplan" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraformplan/json" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraformplan/snapshot" ) diff --git a/pkg/fanal/analyzer/config/terraformplan/terraformplan.go b/pkg/fanal/analyzer/config/terraformplan/json/json.go similarity index 70% rename from pkg/fanal/analyzer/config/terraformplan/terraformplan.go rename to pkg/fanal/analyzer/config/terraformplan/json/json.go index a9d32dd6f627..5272f0f990f9 100644 --- a/pkg/fanal/analyzer/config/terraformplan/terraformplan.go +++ b/pkg/fanal/analyzer/config/terraformplan/json/json.go @@ -12,7 +12,7 @@ import ( ) const ( - analyzerType = analyzer.TypeTerraformPlan + analyzerType = analyzer.TypeTerraformPlanJSON version = 1 ) @@ -21,24 +21,24 @@ var requiredExts = []string{ } func init() { - analyzer.RegisterPostAnalyzer(analyzerType, newTerraformPlanConfigAnalyzer) + analyzer.RegisterPostAnalyzer(analyzerType, newTerraformPlanJSONConfigAnalyzer) } -// terraformPlanConfigAnalyzer is an analyzer for detecting misconfigurations in Terraform files. +// terraformPlanConfigAnalyzer is an analyzer for detecting misconfigurations in Terraform Plan files in JSON format. // It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. type terraformPlanConfigAnalyzer struct { *config.Analyzer } -func newTerraformPlanConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { - a, err := config.NewAnalyzer(analyzerType, version, misconf.NewTerraformPlanScanner, opts) +func newTerraformPlanJSONConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewTerraformPlanJSONScanner, opts) if err != nil { return nil, err } return &terraformPlanConfigAnalyzer{Analyzer: a}, nil } -// Required overrides config.Analyzer.Required() and checks if the given file is a Terraform file. +// Required overrides config.Analyzer.Required() and checks if the given file is a Terraform Plan file in JSON format. func (*terraformPlanConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { return slices.Contains(requiredExts, filepath.Ext(filePath)) } diff --git a/pkg/fanal/analyzer/config/terraformplan/terraformplan_test.go b/pkg/fanal/analyzer/config/terraformplan/json/json_test.go similarity index 100% rename from pkg/fanal/analyzer/config/terraformplan/terraformplan_test.go rename to pkg/fanal/analyzer/config/terraformplan/json/json_test.go diff --git a/pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot.go b/pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot.go new file mode 100644 index 000000000000..0597c137d96c --- /dev/null +++ b/pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot.go @@ -0,0 +1,37 @@ +package terraformplan + +import ( + "os" + "path/filepath" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" +) + +const ( + analyzerType = analyzer.TypeTerraformPlanSnapshot + version = 1 +) + +func init() { + analyzer.RegisterPostAnalyzer(analyzerType, newTerraformPlanSnapshotConfigAnalyzer) +} + +// terraformPlanConfigAnalyzer is an analyzer for detecting misconfigurations in Terraform Plan files in snapshot format. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type terraformPlanConfigAnalyzer struct { + *config.Analyzer +} + +func newTerraformPlanSnapshotConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewTerraformPlanSnapshotScanner, opts) + if err != nil { + return nil, err + } + return &terraformPlanConfigAnalyzer{Analyzer: a}, nil +} + +func (*terraformPlanConfigAnalyzer) Required(filePath string, fi os.FileInfo) bool { + return filepath.Ext(filePath) == ".tfplan" || filepath.Base(filePath) == "tfplan" +} diff --git a/pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot_test.go b/pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot_test.go new file mode 100644 index 000000000000..22bd69e5f357 --- /dev/null +++ b/pkg/fanal/analyzer/config/terraformplan/snapshot/snapshot_test.go @@ -0,0 +1,38 @@ +package terraformplan + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConfigAnalyzer_Required(t *testing.T) { + tests := []struct { + name string + filePath string + want bool + }{ + { + name: "tfplan as extension", + filePath: "/path/to/test.tfplan", + want: true, + }, + { + name: "without extension", + filePath: "/path/to/tfplan", + want: true, + }, + { + name: "bad path", + filePath: "/path/to/mytfplan.txt", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := terraformPlanConfigAnalyzer{} + got := a.Required(tt.filePath, nil) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index 93ad337ec83d..29ed8027118f 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -109,13 +109,14 @@ const ( // ================= // Structured Config // ================= - TypeAzureARM Type = Type(detection.FileTypeAzureARM) - TypeCloudFormation Type = Type(detection.FileTypeCloudFormation) - TypeDockerfile Type = Type(detection.FileTypeDockerfile) - TypeHelm Type = Type(detection.FileTypeHelm) - TypeKubernetes Type = Type(detection.FileTypeKubernetes) - TypeTerraform Type = Type(detection.FileTypeTerraform) - TypeTerraformPlan Type = Type(detection.FileTypeTerraformPlan) + TypeAzureARM Type = Type(detection.FileTypeAzureARM) + TypeCloudFormation Type = Type(detection.FileTypeCloudFormation) + TypeDockerfile Type = Type(detection.FileTypeDockerfile) + TypeHelm Type = Type(detection.FileTypeHelm) + TypeKubernetes Type = Type(detection.FileTypeKubernetes) + TypeTerraform Type = Type(detection.FileTypeTerraform) + TypeTerraformPlanJSON Type = Type(detection.FileTypeTerraformPlanJSON) + TypeTerraformPlanSnapshot Type = Type(detection.FileTypeTerraformPlanSnapshot) // ======== // License @@ -228,6 +229,7 @@ var ( TypeHelm, TypeKubernetes, TypeTerraform, - TypeTerraformPlan, + TypeTerraformPlanJSON, + TypeTerraformPlanSnapshot, } ) diff --git a/pkg/fanal/artifact/image/image_test.go b/pkg/fanal/artifact/image/image_test.go index d65df55f2fc5..a99823dde2c0 100644 --- a/pkg/fanal/artifact/image/image_test.go +++ b/pkg/fanal/artifact/image/image_test.go @@ -352,17 +352,17 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + BlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingArtifact: true, - MissingBlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + MissingBlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897", + BlobID: "sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -429,7 +429,7 @@ func TestArtifact_Inspect(t *testing.T) { Name: "../../test/testdata/alpine-311.tar.gz", Type: types.ArtifactContainerImage, ID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + BlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, ImageMetadata: types.ImageMetadata{ ID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", DiffIDs: []string{ @@ -488,25 +488,25 @@ func TestArtifact_Inspect(t *testing.T) { Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", - "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", - "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", - "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", + "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", + "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", + "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", }, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingBlobIDs: []string{ - "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", - "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", - "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", - "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", + "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", + "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", + "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", }, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", + BlobID: "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -594,7 +594,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", + BlobID: "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -690,7 +690,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", + BlobID: "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -898,7 +898,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + BlobID: "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1707,10 +1707,10 @@ func TestArtifact_Inspect(t *testing.T) { Type: types.ArtifactContainerImage, ID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", - "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", - "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", - "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", + "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", + "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", + "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", }, ImageMetadata: types.ImageMetadata{ ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4", @@ -1804,25 +1804,25 @@ func TestArtifact_Inspect(t *testing.T) { Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:dc2c102de2a4dc82bc017564a2c1a46646f071367bd51d3ddd80171dc5a78ffe", - "sha256:e6bc45e7459162f5a27dd7bd310a420f27378f2ea24c6ed5f0a083f2377bde24", - "sha256:8184bee7b1985e0abd72288946c246b90e1847b36fdcd50ca3d588b9371ec920", - "sha256:f1bb0ccf476528c6165a2f1a74d0e5a0cb06c9b254a0def6b1183db662e83dd4", + "sha256:e1187118cdbe8893fc2fd4b345f813d195ee6aaeb4820d4576694199f8c10350", + "sha256:12c266a627dc4014c3ee96936058ba98209056f4ffe0081bb5fca7ff91592cdb", + "sha256:47adac0e28b12338e99dedbd7e8b0ef1f7aaa28e646f637ab2db8908b80704c8", + "sha256:dd1082b33b17401fdc31bcbf60eaaecb9ce29e23956c50db6f34b2cc6cfa13c8", }, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingBlobIDs: []string{ - "sha256:dc2c102de2a4dc82bc017564a2c1a46646f071367bd51d3ddd80171dc5a78ffe", - "sha256:e6bc45e7459162f5a27dd7bd310a420f27378f2ea24c6ed5f0a083f2377bde24", - "sha256:8184bee7b1985e0abd72288946c246b90e1847b36fdcd50ca3d588b9371ec920", - "sha256:f1bb0ccf476528c6165a2f1a74d0e5a0cb06c9b254a0def6b1183db662e83dd4", + "sha256:e1187118cdbe8893fc2fd4b345f813d195ee6aaeb4820d4576694199f8c10350", + "sha256:12c266a627dc4014c3ee96936058ba98209056f4ffe0081bb5fca7ff91592cdb", + "sha256:47adac0e28b12338e99dedbd7e8b0ef1f7aaa28e646f637ab2db8908b80704c8", + "sha256:dd1082b33b17401fdc31bcbf60eaaecb9ce29e23956c50db6f34b2cc6cfa13c8", }, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:dc2c102de2a4dc82bc017564a2c1a46646f071367bd51d3ddd80171dc5a78ffe", + BlobID: "sha256:e1187118cdbe8893fc2fd4b345f813d195ee6aaeb4820d4576694199f8c10350", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1833,7 +1833,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:e6bc45e7459162f5a27dd7bd310a420f27378f2ea24c6ed5f0a083f2377bde24", + BlobID: "sha256:12c266a627dc4014c3ee96936058ba98209056f4ffe0081bb5fca7ff91592cdb", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1844,7 +1844,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:8184bee7b1985e0abd72288946c246b90e1847b36fdcd50ca3d588b9371ec920", + BlobID: "sha256:47adac0e28b12338e99dedbd7e8b0ef1f7aaa28e646f637ab2db8908b80704c8", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1856,7 +1856,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:f1bb0ccf476528c6165a2f1a74d0e5a0cb06c9b254a0def6b1183db662e83dd4", + BlobID: "sha256:dd1082b33b17401fdc31bcbf60eaaecb9ce29e23956c50db6f34b2cc6cfa13c8", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1872,10 +1872,10 @@ func TestArtifact_Inspect(t *testing.T) { Type: types.ArtifactContainerImage, ID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:dc2c102de2a4dc82bc017564a2c1a46646f071367bd51d3ddd80171dc5a78ffe", - "sha256:e6bc45e7459162f5a27dd7bd310a420f27378f2ea24c6ed5f0a083f2377bde24", - "sha256:8184bee7b1985e0abd72288946c246b90e1847b36fdcd50ca3d588b9371ec920", - "sha256:f1bb0ccf476528c6165a2f1a74d0e5a0cb06c9b254a0def6b1183db662e83dd4", + "sha256:e1187118cdbe8893fc2fd4b345f813d195ee6aaeb4820d4576694199f8c10350", + "sha256:12c266a627dc4014c3ee96936058ba98209056f4ffe0081bb5fca7ff91592cdb", + "sha256:47adac0e28b12338e99dedbd7e8b0ef1f7aaa28e646f637ab2db8908b80704c8", + "sha256:dd1082b33b17401fdc31bcbf60eaaecb9ce29e23956c50db6f34b2cc6cfa13c8", }, ImageMetadata: types.ImageMetadata{ ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4", @@ -1958,7 +1958,7 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + BlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ Err: xerrors.New("MissingBlobs failed"), @@ -1972,16 +1972,16 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + BlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ - MissingBlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + MissingBlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897", + BlobID: "sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -2041,18 +2041,18 @@ func TestArtifact_Inspect(t *testing.T) { Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", - "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", - "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", - "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", + "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", + "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", + "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", }, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingBlobIDs: []string{ - "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", - "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", - "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", - "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", + "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", + "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", + "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", }, }, }, @@ -2060,7 +2060,7 @@ func TestArtifact_Inspect(t *testing.T) { { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:db2b5b2f26f320b80ee549495888f111151fdb44fecb2c432dfd928e96978e74", + BlobID: "sha256:dd0a4f4754bf4590327be34f4266f63c92184352afadb72e4c9b162f76224000", BlobInfoAnything: true, }, @@ -2071,7 +2071,7 @@ func TestArtifact_Inspect(t *testing.T) { { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:718d6a3d0a24966f2d9f64e0c86695d1bb896fe81df634b85300d34142c80174", + BlobID: "sha256:f9e6a3065bb47f810916e90249076950a4b70785a27d3bcb90406d0ab342fa67", BlobInfoAnything: true, }, @@ -2082,7 +2082,7 @@ func TestArtifact_Inspect(t *testing.T) { { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:9f6bc14198fa983fd47079bcd82df1391bee8e529a8a131a0305874eb0a51ca0", + BlobID: "sha256:b6be0de11c6090f71dea119f43dd360335643420058e317baffb089f0dff4001", BlobInfoAnything: true, }, @@ -2093,7 +2093,7 @@ func TestArtifact_Inspect(t *testing.T) { { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:44264715b7132cd14cd956bf385d5f875603461f5adeafde7fdecabe2e266291", + BlobID: "sha256:37c561c19b169f5f9832f4b0060bf74ebc8d1c9e01662ad4fa21c394da159440", BlobInfoAnything: true, }, @@ -2110,17 +2110,17 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + BlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingArtifact: true, - MissingBlobIDs: []string{"sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897"}, + MissingBlobIDs: []string{"sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0"}, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:0366f81c3cbeac047536f9ca133b5632544c8342544922c3ed8a3e51c0250897", + BlobID: "sha256:1fd280c63e1416a2261e76454caa19a5b77c6bddedd48309c9687c4fe72b34c0", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", diff --git a/pkg/fanal/artifact/local/fs_test.go b/pkg/fanal/artifact/local/fs_test.go index 200aa970bfb9..bbaf684552ae 100644 --- a/pkg/fanal/artifact/local/fs_test.go +++ b/pkg/fanal/artifact/local/fs_test.go @@ -3,6 +3,7 @@ package local import ( "context" "errors" + "os" "path/filepath" "runtime" "testing" @@ -47,7 +48,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:ff28bff7756fb32d0a060b3b474b31a781a2d365dcd2789f47b4ae556a34947e", + BlobID: "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, OS: types.OS{ @@ -82,9 +83,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "host", Type: types.ArtifactFilesystem, - ID: "sha256:ff28bff7756fb32d0a060b3b474b31a781a2d365dcd2789f47b4ae556a34947e", + ID: "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", BlobIDs: []string{ - "sha256:ff28bff7756fb32d0a060b3b474b31a781a2d365dcd2789f47b4ae556a34947e", + "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", }, }, }, @@ -102,7 +103,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:d5fa75cdac006582a8f6bc4e3fcc8bfb70bd9d0403c24d8c2e3230d3f38a7ff5", + BlobID: "sha256:7db98974b2231d3e25f4890008c4d42f6f26a7da5a8aba99e954dec97f050bd6", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, @@ -112,9 +113,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "host", Type: types.ArtifactFilesystem, - ID: "sha256:d5fa75cdac006582a8f6bc4e3fcc8bfb70bd9d0403c24d8c2e3230d3f38a7ff5", + ID: "sha256:7db98974b2231d3e25f4890008c4d42f6f26a7da5a8aba99e954dec97f050bd6", BlobIDs: []string{ - "sha256:d5fa75cdac006582a8f6bc4e3fcc8bfb70bd9d0403c24d8c2e3230d3f38a7ff5", + "sha256:7db98974b2231d3e25f4890008c4d42f6f26a7da5a8aba99e954dec97f050bd6", }, }, }, @@ -125,7 +126,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:ff28bff7756fb32d0a060b3b474b31a781a2d365dcd2789f47b4ae556a34947e", + BlobID: "sha256:afc2bc421aac8c61d89d4dd1c1865efb5441e3877c8a4c919232729d7c574dab", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, OS: types.OS{ @@ -175,7 +176,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:09aa251b64e824d0ec71a8c469619e57c9bd91d885f26e4a840de94209acbe4f", + BlobID: "sha256:f1101f37560adf2f5d9c8fef2ac66beff236be3be94b3ea48c0fb6f86867775f", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Applications: []types.Application{ @@ -197,9 +198,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/requirements.txt", Type: types.ArtifactFilesystem, - ID: "sha256:09aa251b64e824d0ec71a8c469619e57c9bd91d885f26e4a840de94209acbe4f", + ID: "sha256:f1101f37560adf2f5d9c8fef2ac66beff236be3be94b3ea48c0fb6f86867775f", BlobIDs: []string{ - "sha256:09aa251b64e824d0ec71a8c469619e57c9bd91d885f26e4a840de94209acbe4f", + "sha256:f1101f37560adf2f5d9c8fef2ac66beff236be3be94b3ea48c0fb6f86867775f", }, }, }, @@ -210,7 +211,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:09aa251b64e824d0ec71a8c469619e57c9bd91d885f26e4a840de94209acbe4f", + BlobID: "sha256:f1101f37560adf2f5d9c8fef2ac66beff236be3be94b3ea48c0fb6f86867775f", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Applications: []types.Application{ @@ -232,9 +233,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/requirements.txt", Type: types.ArtifactFilesystem, - ID: "sha256:09aa251b64e824d0ec71a8c469619e57c9bd91d885f26e4a840de94209acbe4f", + ID: "sha256:f1101f37560adf2f5d9c8fef2ac66beff236be3be94b3ea48c0fb6f86867775f", BlobIDs: []string{ - "sha256:09aa251b64e824d0ec71a8c469619e57c9bd91d885f26e4a840de94209acbe4f", + "sha256:f1101f37560adf2f5d9c8fef2ac66beff236be3be94b3ea48c0fb6f86867775f", }, }, }, @@ -361,7 +362,7 @@ func TestBuildPathsToSkip(t *testing.T) { } } -var policyMetadata = types.PolicyMetadata{ +var terraformPolicyMetadata = types.PolicyMetadata{ ID: "TEST001", AVDID: "AVD-TEST-0001", Type: "Terraform Security Check", @@ -411,7 +412,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.asd", Provider: "Generic", @@ -430,9 +431,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/single-failure", Type: types.ArtifactFilesystem, - ID: "sha256:51123b27efc62be0db21fad4ccaf0839850f9f9162d225c6bd9d0e94089b2d8b", + ID: "sha256:1a6ce0acc3b57eb6c830c96fcd868fec1eb4d3b57ad51e481c76d85f22870a65", BlobIDs: []string{ - "sha256:51123b27efc62be0db21fad4ccaf0839850f9f9162d225c6bd9d0e94089b2d8b", + "sha256:1a6ce0acc3b57eb6c830c96fcd868fec1eb4d3b57ad51e481c76d85f22870a65", }, }, }, @@ -464,7 +465,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", @@ -477,7 +478,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", @@ -496,7 +497,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.three", Provider: "Generic", @@ -515,9 +516,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/multiple-failures", Type: types.ArtifactFilesystem, - ID: "sha256:e5159ce9589ca0fd714cbbb757628fffff31229a52310ea151ae1410be5f1f1b", + ID: "sha256:afc20cf0fc99c62bbc79b00cb9fbc70ba7ee76c946a6d560639ba9279344787d", BlobIDs: []string{ - "sha256:e5159ce9589ca0fd714cbbb757628fffff31229a52310ea151ae1410be5f1f1b", + "sha256:afc20cf0fc99c62bbc79b00cb9fbc70ba7ee76c946a6d560639ba9279344787d", }, }, }, @@ -545,9 +546,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/no-results", Type: types.ArtifactFilesystem, - ID: "sha256:0c31a344fe889e279aecf743d801ae5d40ee2841a45ed7820114c1094c41a966", + ID: "sha256:1827b6a0b0a17e0d623a2045e9d9c331ef613390eda2fed823969ee0dd730257", BlobIDs: []string{ - "sha256:0c31a344fe889e279aecf743d801ae5d40ee2841a45ed7820114c1094c41a966", + "sha256:1827b6a0b0a17e0d623a2045e9d9c331ef613390eda2fed823969ee0dd730257", }, }, }, @@ -578,7 +579,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { { Namespace: "user.something", Query: "data.user.something.deny", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", @@ -594,9 +595,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/passed", Type: types.ArtifactFilesystem, - ID: "sha256:4e2b9cba04625f1d9cc57f74640d039779b0ee176e958aaea37883e03842056d", + ID: "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", BlobIDs: []string{ - "sha256:4e2b9cba04625f1d9cc57f74640d039779b0ee176e958aaea37883e03842056d", + "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", }, }, }, @@ -628,7 +629,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", @@ -641,7 +642,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", @@ -660,9 +661,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/busted-relative-paths/child/main.tf", Type: types.ArtifactFilesystem, - ID: "sha256:aacaabaaef04916bc31b5200617a07ca5c92a4eab1b94783cde06cc4b24412d2", + ID: "sha256:8db34d644bfb98077180616caeab0b41a26d9029a47a23d4b36e1d6e45584919", BlobIDs: []string{ - "sha256:aacaabaaef04916bc31b5200617a07ca5c92a4eab1b94783cde06cc4b24412d2", + "sha256:8db34d644bfb98077180616caeab0b41a26d9029a47a23d4b36e1d6e45584919", }, }, }, @@ -694,7 +695,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { { Namespace: "user.something", Query: "data.user.something.deny", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Provider: "Generic", Service: "general", @@ -710,9 +711,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/tfvar-outside/tf", Type: types.ArtifactFilesystem, - ID: "sha256:4e2b9cba04625f1d9cc57f74640d039779b0ee176e958aaea37883e03842056d", + ID: "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", BlobIDs: []string{ - "sha256:4e2b9cba04625f1d9cc57f74640d039779b0ee176e958aaea37883e03842056d", + "sha256:eec58ef10d1b04df4af76b2472e615f8c27e253a16f90d7542670a7001d88915", }, }, }, @@ -743,7 +744,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.three", Provider: "Generic", @@ -762,7 +763,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.one", Provider: "Generic", @@ -781,7 +782,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { Namespace: "user.something", Query: "data.user.something.deny", Message: "Empty bucket name!", - PolicyMetadata: policyMetadata, + PolicyMetadata: terraformPolicyMetadata, CauseMetadata: types.CauseMetadata{ Resource: "aws_s3_bucket.two", Provider: "Generic", @@ -800,9 +801,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/relative-paths/child", Type: types.ArtifactFilesystem, - ID: "sha256:9c5c0038bf41e03f878ed27c569b93198a16b0d975e7fca4e90aa2a4eaf87402", + ID: "sha256:f13b89447db61be1c1e4099ef18aec7272091f8f2d3581643a9d1fabc74eda83", BlobIDs: []string{ - "sha256:9c5c0038bf41e03f878ed27c569b93198a16b0d975e7fca4e90aa2a4eaf87402", + "sha256:f13b89447db61be1c1e4099ef18aec7272091f8f2d3581643a9d1fabc74eda83", }, }, }, @@ -825,6 +826,261 @@ func TestTerraformMisconfigurationScan(t *testing.T) { } } +const emptyBucketCheck = `package user.something + +__rego_metadata__ := { + "id": "TEST001", + "avd_id": "AVD-TEST-0001", + "title": "Test policy", + "short_code": "empty-bucket-name", + "severity": "LOW", + "description": "This is a test policy.", + "recommended_actions": "Have a cup of tea.", + "url": "https://trivy.dev/", +} + +# taken from defsec rego lib to mimic behaviour +result(msg, cause) = result { + metadata := object.get(cause, "__defsec_metadata", cause) + result := { + "msg": msg, + "startline": object.get(metadata, "startline", 0), + "endline": object.get(metadata, "endline", 0), + "filepath": object.get(metadata, "filepath", ""), + "explicit": object.get(metadata, "explicit", false), + "managed": object.get(metadata, "managed", true), + "fskey": object.get(metadata, "fskey", ""), + "resource": object.get(metadata, "resource", ""), + } +} + +deny[res] { + bucket := input.aws.s3.buckets[_] + bucket.name.value == "" + res := result("Empty bucket name!", bucket) +}` + +var terraformPlanPolicyMetadata = types.PolicyMetadata{ + ID: "TEST001", + AVDID: "AVD-TEST-0001", + Type: "Terraform Plan Snapshot Security Check", + Title: "Test policy", + Description: "This is a test policy.", + Severity: "LOW", + RecommendedActions: "Have a cup of tea.", + References: []string{"https://trivy.dev/"}, +} + +func TestTerraformPlanSnapshotMisconfScan(t *testing.T) { + type fields struct { + dir string + } + tests := []struct { + name string + fields fields + putBlobExpectation cache.ArtifactCachePutBlobExpectation + want types.ArtifactReference + }{ + { + name: "single failure", + fields: fields{ + + dir: "./testdata/misconfig/terraformplan/snapshots/single-failure", + }, + putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ + Args: cache.ArtifactCachePutBlobArgs{ + BlobIDAnything: true, + BlobInfo: types.BlobInfo{ + SchemaVersion: 2, + Misconfigurations: []types.Misconfiguration{ + { + FileType: types.TerraformPlanSnapshot, + FilePath: "main.tf", + Failures: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "Empty bucket name!", + PolicyMetadata: terraformPlanPolicyMetadata, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.this", + Provider: "Generic", + Service: "general", + StartLine: 10, + EndLine: 12, + }, + }, + }, + }, + }, + }, + }, + Returns: cache.ArtifactCachePutBlobReturns{}, + }, + want: types.ArtifactReference{ + Name: "testdata/misconfig/terraformplan/snapshots/single-failure", + Type: types.ArtifactFilesystem, + ID: "sha256:732c38451bde877a94d1ff5b6f2019655bed04fd24b1169de195eeee1199045e", + BlobIDs: []string{ + "sha256:732c38451bde877a94d1ff5b6f2019655bed04fd24b1169de195eeee1199045e", + }, + }, + }, + { + name: "multiple failures", + fields: fields{ + dir: "./testdata/misconfig/terraformplan/snapshots/multiple-failures", + }, + putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ + Args: cache.ArtifactCachePutBlobArgs{ + BlobIDAnything: true, + BlobInfo: types.BlobInfo{ + SchemaVersion: 2, + Misconfigurations: []types.Misconfiguration{ + { + FileType: types.TerraformPlanSnapshot, + FilePath: "main.tf", + Failures: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "Empty bucket name!", + PolicyMetadata: terraformPlanPolicyMetadata, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.one", + Provider: "Generic", + Service: "general", + StartLine: 10, + EndLine: 12, + }, + }, + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "Empty bucket name!", + PolicyMetadata: terraformPlanPolicyMetadata, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.two", + Provider: "Generic", + Service: "general", + StartLine: 14, + EndLine: 16, + }, + }, + }, + }, + { + FileType: types.TerraformPlanSnapshot, + FilePath: "more.tf", + Failures: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "Empty bucket name!", + PolicyMetadata: terraformPlanPolicyMetadata, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.three", + Provider: "Generic", + Service: "general", + StartLine: 1, + EndLine: 3, + }, + }, + }, + }, + }, + }, + }, + Returns: cache.ArtifactCachePutBlobReturns{}, + }, + want: types.ArtifactReference{ + Name: "testdata/misconfig/terraformplan/snapshots/multiple-failures", + Type: types.ArtifactFilesystem, + ID: "sha256:752c0b470adfcfe7e21892cf4c6fc3bc28dba873c9c1696f40b71b7a51ad7231", + BlobIDs: []string{ + "sha256:752c0b470adfcfe7e21892cf4c6fc3bc28dba873c9c1696f40b71b7a51ad7231", + }, + }, + }, + { + name: "passed", + fields: fields{ + dir: "./testdata/misconfig/terraformplan/snapshots/passed", + }, + putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ + Args: cache.ArtifactCachePutBlobArgs{ + BlobIDAnything: true, + BlobInfo: types.BlobInfo{ + SchemaVersion: 2, + Misconfigurations: []types.Misconfiguration{ + { + FileType: types.TerraformPlanSnapshot, + FilePath: ".", + Successes: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + PolicyMetadata: terraformPlanPolicyMetadata, + CauseMetadata: types.CauseMetadata{ + Provider: "Generic", + Service: "general", + }, + }, + }, + }, + }, + }, + }, + Returns: cache.ArtifactCachePutBlobReturns{}, + }, + want: types.ArtifactReference{ + Name: "testdata/misconfig/terraformplan/snapshots/passed", + Type: types.ArtifactFilesystem, + ID: "sha256:8947704a08f54ab1df32cd905d6bca72edf1785a42702968bafa331172da7176", + BlobIDs: []string{ + "sha256:8947704a08f54ab1df32cd905d6bca72edf1785a42702968bafa331172da7176", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + tmpDir := t.TempDir() + f, err := os.Create(filepath.Join(tmpDir, "policy.rego")) + require.NoError(t, err) + defer f.Close() + + f.WriteString(emptyBucketCheck) + + c := new(cache.MockArtifactCache) + c.ApplyPutBlobExpectation(tt.putBlobExpectation) + + opt := artifact.Option{ + DisabledHandlers: []types.HandlerType{ + types.SystemFileFilteringPostHandler, + }, + SkipFiles: []string{"*.tf"}, + MisconfScannerOption: misconf.ScannerOption{ + RegoOnly: true, + DisableEmbeddedPolicies: true, + + DisableEmbeddedLibraries: false, + Namespaces: []string{"user"}, + PolicyPaths: []string{tmpDir}, + }, + } + + a, err := NewArtifact(tt.fields.dir, c, opt) + require.NoError(t, err) + + got, err := a.Inspect(context.Background()) + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + func TestCloudFormationMisconfigurationScan(t *testing.T) { type fields struct { dir string @@ -892,9 +1148,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:b2ae3759e901c7ba8b0aa690e551e3eec01b6e450533d5444a63969ffbb97adf", + ID: "sha256:889a94522970c6e55f1f7543914b2f0131f79f9c4526445fb95309f64a9947d7", BlobIDs: []string{ - "sha256:b2ae3759e901c7ba8b0aa690e551e3eec01b6e450533d5444a63969ffbb97adf", + "sha256:889a94522970c6e55f1f7543914b2f0131f79f9c4526445fb95309f64a9947d7", }, }, }, @@ -976,9 +1232,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:410c31b72c1da31a4b2974fe4405820ffe81c65880017491ca63ec6be2cd9424", + ID: "sha256:17c9c72a759856445e6d3847b2d5ed90c3bad3e4ee50cea0c812ef53c179f8ca", BlobIDs: []string{ - "sha256:410c31b72c1da31a4b2974fe4405820ffe81c65880017491ca63ec6be2cd9424", + "sha256:17c9c72a759856445e6d3847b2d5ed90c3bad3e4ee50cea0c812ef53c179f8ca", }, }, }, @@ -1008,9 +1264,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:5cafcecda4322751d7b281d9546f5789a46d82d19cc2adab614122d2ce3420b9", + ID: "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", BlobIDs: []string{ - "sha256:5cafcecda4322751d7b281d9546f5789a46d82d19cc2adab614122d2ce3420b9", + "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", }, }, }, @@ -1066,9 +1322,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/params/code/src", Type: types.ArtifactFilesystem, - ID: "sha256:0c66c19a4df3ecc11db9f90fbc921f1050325c05c480847369e07ee309e8a897", + ID: "sha256:267b572211115db6a2a4484a02317fbb6d4f050da0e95b1db4243d49889483de", BlobIDs: []string{ - "sha256:0c66c19a4df3ecc11db9f90fbc921f1050325c05c480847369e07ee309e8a897", + "sha256:267b572211115db6a2a4484a02317fbb6d4f050da0e95b1db4243d49889483de", }, }, }, @@ -1124,9 +1380,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:9f9b0773ca1ec4b257aae410798a635076eaed12afefac839b62efdc65d417e1", + ID: "sha256:8ca92725ce2f47b7ffb1b0a9e0359d59ac2b3b3f517ba42f66a859436057e54a", BlobIDs: []string{ - "sha256:9f9b0773ca1ec4b257aae410798a635076eaed12afefac839b62efdc65d417e1", + "sha256:8ca92725ce2f47b7ffb1b0a9e0359d59ac2b3b3f517ba42f66a859436057e54a", }, }, }, @@ -1212,9 +1468,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:a22d246cba3476acf2a3d6cfe88b5a895ab78cb328b76fe070fce9f1c77f80c7", + ID: "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", BlobIDs: []string{ - "sha256:a22d246cba3476acf2a3d6cfe88b5a895ab78cb328b76fe070fce9f1c77f80c7", + "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", }, }, }, @@ -1270,9 +1526,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:a22d246cba3476acf2a3d6cfe88b5a895ab78cb328b76fe070fce9f1c77f80c7", + ID: "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", BlobIDs: []string{ - "sha256:a22d246cba3476acf2a3d6cfe88b5a895ab78cb328b76fe070fce9f1c77f80c7", + "sha256:627cbf451ec7929dfe5151dfe0e2305ed855906bf79136f198528a0cc3f6e4f9", }, }, }, @@ -1300,9 +1556,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:5cafcecda4322751d7b281d9546f5789a46d82d19cc2adab614122d2ce3420b9", + ID: "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", BlobIDs: []string{ - "sha256:5cafcecda4322751d7b281d9546f5789a46d82d19cc2adab614122d2ce3420b9", + "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", }, }, }, @@ -1360,9 +1616,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:a9541fe2309a78505f147a688ebf8a2e107bad8351bb22e280f627d5ecf91b16", + ID: "sha256:4cc7f6bba417cc65c5391bc9c07fd1e205e21bdec87b271889433af18be1e454", BlobIDs: []string{ - "sha256:a9541fe2309a78505f147a688ebf8a2e107bad8351bb22e280f627d5ecf91b16", + "sha256:4cc7f6bba417cc65c5391bc9c07fd1e205e21bdec87b271889433af18be1e454", }, }, }, @@ -1452,9 +1708,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:ee77eca0b592b90536a467b20629c017b03e627c95427a3e7f4be2a9eb55c710", + ID: "sha256:d5ca0b4e96aaaeafa424a2250db6297a5182cb6ca5db49bc1ff11790f6cdbee9", BlobIDs: []string{ - "sha256:ee77eca0b592b90536a467b20629c017b03e627c95427a3e7f4be2a9eb55c710", + "sha256:d5ca0b4e96aaaeafa424a2250db6297a5182cb6ca5db49bc1ff11790f6cdbee9", }, }, }, @@ -1538,9 +1794,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:1892ab8d28210fe199dbea24de4f2073ddf5d4bf5b33aa32436d2e1f1facb588", + ID: "sha256:eef9fff2fe8f5c4a123c018b4f91db25d9676e7d171a3a683c2fbfbbbe82fa54", BlobIDs: []string{ - "sha256:1892ab8d28210fe199dbea24de4f2073ddf5d4bf5b33aa32436d2e1f1facb588", + "sha256:eef9fff2fe8f5c4a123c018b4f91db25d9676e7d171a3a683c2fbfbbbe82fa54", }, }, }, @@ -1568,9 +1824,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:70f3be8d859a21841ac42de298e9e805aa058593cf3e315e8ee0fa1f30ef5107", + ID: "sha256:2b54cf33feaa1fe1f5bf223f873ca6c3f7c3693b0bb3b0ce9e2e7fd79cd37b5a", BlobIDs: []string{ - "sha256:70f3be8d859a21841ac42de298e9e805aa058593cf3e315e8ee0fa1f30ef5107", + "sha256:2b54cf33feaa1fe1f5bf223f873ca6c3f7c3693b0bb3b0ce9e2e7fd79cd37b5a", }, }, }, @@ -1628,9 +1884,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:f0eb13ac1479d37da2bcfc7964de5f3a4d3f06982ec27f110c97e9c8cf1cde38", + ID: "sha256:dc7a0fd3ea2f13b0ea05f4e517a16e602b0fc17fbd72aa5e34107ef12a91a30b", BlobIDs: []string{ - "sha256:f0eb13ac1479d37da2bcfc7964de5f3a4d3f06982ec27f110c97e9c8cf1cde38", + "sha256:dc7a0fd3ea2f13b0ea05f4e517a16e602b0fc17fbd72aa5e34107ef12a91a30b", }, }, }, @@ -1717,9 +1973,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:ca17bc98d3b2dc8e7b001877324c51d08ba1b2a6764514a9a81adb3fb11f3c08", + ID: "sha256:c1a8bfd544b9041ad194382cc42b54289f70966d061ef501b267aec8fd07c5df", BlobIDs: []string{ - "sha256:ca17bc98d3b2dc8e7b001877324c51d08ba1b2a6764514a9a81adb3fb11f3c08", + "sha256:c1a8bfd544b9041ad194382cc42b54289f70966d061ef501b267aec8fd07c5df", }, }, }, @@ -1799,9 +2055,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:2e29f26823faf3042ff25c32734fa96bc24ebb09a4d92d5ea3ea5c60d7204114", + ID: "sha256:75bf0e88f8d2857be90fb8d10a350c04c1532ba7f510e1eb404a8bae30ce97d8", BlobIDs: []string{ - "sha256:2e29f26823faf3042ff25c32734fa96bc24ebb09a4d92d5ea3ea5c60d7204114", + "sha256:75bf0e88f8d2857be90fb8d10a350c04c1532ba7f510e1eb404a8bae30ce97d8", }, }, }, @@ -1829,9 +2085,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:5cafcecda4322751d7b281d9546f5789a46d82d19cc2adab614122d2ce3420b9", + ID: "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", BlobIDs: []string{ - "sha256:5cafcecda4322751d7b281d9546f5789a46d82d19cc2adab614122d2ce3420b9", + "sha256:26c76a2cb55cb0ef2c3a2dd79e237bddb508ca2c4cefdb103698a1972c8a9c2d", }, }, }, @@ -1885,9 +2141,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:a2a781172a5d3a7c70251322696b892cf16da89c937a85f5caf7d2e8c44eede4", + ID: "sha256:b9ba7c4eafec405c8b6998dbb98ee1c7f7830caf8487fd1461433ff82d8779e9", BlobIDs: []string{ - "sha256:a2a781172a5d3a7c70251322696b892cf16da89c937a85f5caf7d2e8c44eede4", + "sha256:b9ba7c4eafec405c8b6998dbb98ee1c7f7830caf8487fd1461433ff82d8779e9", }, }, }, diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/main.tf b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/main.tf new file mode 100644 index 000000000000..9eb5363beea5 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/main.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + aws = { + source = "aws" + version = "5.35.0" + } + } +} + +resource "aws_s3_bucket" "one" { + +} + +resource "aws_s3_bucket" "two" { + +} diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/more.tf b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/more.tf new file mode 100644 index 000000000000..e5cc0c1ea195 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/more.tf @@ -0,0 +1,3 @@ +resource "aws_s3_bucket" "three" { + +} diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/tfplan b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/multiple-failures/tfplan new file mode 100644 index 0000000000000000000000000000000000000000..80b80a786b3d8a234003e1641f2237d7beb876ad GIT binary patch literal 2789 zcmd5;dpy%^AO0bSax6=pBvFpDjq1^}vLb4)Ikr%anQes4m}Nxi4O3}05+#Quwwz-Q zQ3{bm(L-AYqbNj*L@TO2)AK$#yl?&ee(%ry$Nk5BeXskzzxQ>09jqmmfdCoNC?hx> z1Qvu8upFTH1Q9TSPL7g*m{A%RKbB-105S*N(_L53?9kJy_rIRRAAD!Rj|D>@O{m2F zw_`oGtcHe)NIoGh7Heh1P#S;<0|1K{xE@fR}OA zR0UMFxl~CjYPf#57&HLU+j8tWB=(Q9+a-BBG&XDtqVues!|Jvd zxs8e0Te&8X*y_uNWzVP59#uNxlrx5=19O#zZ%0|mCf2u27;VgP+UGQV^SmO~hm*hm z#%M^}CoSYKUP_Xze^)T85H9VqT|LLLpt|MTv+x(1N6x(*P4>!_+YZwS0BCkK#=->!ZzCJ>RAaC5_h>zA6>O-IsY^Q5&vu zsOrh{=QYc*x*mD~hOW3^yLah)A=7^BDW&_&;U>F+=PT@00;xT{bf4OuvCvFTM0DvcnF#wRDF6@( z9jv8gK6}mlsUr>mkum__@Y52L{$64s6buFbqsA=HAXe-)mOCNH)WW62UD+^Ypv6F6 zQNe<(UQWL5%?%Fva=a2ap|J{5x;NY+iH1WdXLB^BGl;a8~I`lpQ>`Vy^kDJ=Nc zqvhW!i&Zgd&=}4|T-qr4$a#(AtODlRSrcRv%a3Y z5E~{)G6`5C(KtJf*L-R5xuG9uV{K|%+xG}jw0jn_!MTwib%ztQ(u>C6MzU%jCI!;% z8vl^55(d?TvO<#kD)gSHzQc#3K2Fq}zSm!z>t12~Ty(d87e=D;m1>G;#rfrJ{}+1_ z8NZOd0SeLqgP`o398iX^1ei&Jy@73q9b_Nc!Jt(SW$T1y4-MoGiZA;ot3nM-!%&n_ z=a*SmFS;`l?g#-N0`(6e%?sd}A+!TqG(|CKSL9fRc0hOiJIG^myqP;f8!8#=T71w< zl;ZEBX5x))%^XG*%cT^|^PbOo0NMs_EK*9z<4`T^G9?G%1Gv0lhob<%(m(`5c z3Ifh0<&_y!pOefy#LSE9iY8d~BB|7dOHY$+M}(evv6GVtov*QB5WC)_^hG3>jIy(BL}wx}78ztl(bdx~fmd5Qi#RmxQgCrl-E}I;!5760@>; zvW8zyKE}Az-3|4$S8Sh-xUsIsmbfj*)Ue$Ykq+yr==o43dtg(s@HoOfKA~!`>C$6T zRP}Ad?ODHG&-S)*H_1;HCw%~ z_!As6Lf9wac9C~w0-)0#F7qE`vST)a3UsY0{yWYn$-Q~8lfA*IqKl>=m)=Vlu#p>W zc)A0pziKP`95@JBk};D>R?kl&p!aew>U(>(l;up(P!&=rMv&N4hlRDOA4nl_r@*m# zzZHFR({+KX>LZ+)@pX1_FFYciARm;cWu}#%J*9Qd3mIJ0bxIs@^FiTe*DYdOGPCg~ zuPP^MD&u)i@81m5c0S=u8?W`|Z7uLiQQcsRF&f+^$0rAh?Sgu zVh5^b7MJ=|)79R-%=u&^DEJ_&Z|}Zq9XyXaC2BP7;j60YF4ScI{g!4{?cO{~$1-rt z4Vza=afgXYN@2+L{ADvrd5)Kz>#}_$x(hvMsB8J*2tB__RkoLlS(lu}j?L7zq!9!- z?Sf6GIu~hfO}t9TfbhAwVEL9u=(AG&N0_5SP5ugLMrkJ{a#y}9VPj10@$!8G(DJGm zP%3n0UePt6^{$DHe~tK>Rvxn3Q$6@$LYiZRvn;1e$(sIJHHWSg`1_7&gRaJ7%kFPr zV;&*f2Mk6UZszc%l4;)D;t?Gj>8Se3Ut)=waZj%pMj{4R@mpzUWv4;<%&TS@ z0m7>>OlZNS~OmZ M0l=`R{{Vo$0DQzv_5c6? literal 0 HcmV?d00001 diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/main.tf b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/main.tf new file mode 100644 index 000000000000..64e9f0d2b965 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/main.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + aws = { + source = "aws" + version = "5.35.0" + } + } +} + +resource "aws_s3_bucket" "this" { + bucket = "test-bucket" +} diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/tfplan b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/passed/tfplan new file mode 100644 index 0000000000000000000000000000000000000000..ee00c44a36b3cda4c22a08300c536036636167ee GIT binary patch literal 2575 zcmd5;d05g}7yg+`T4)&RP)eDNnxeQ&i;Q8WDY=D`OX4!2NNONz z4}7YNjH*#Rvj-h{9%S(<;8((z=FSJN@x-+>8v zy*1V}PU6`1_EK?|!OZf0)n3Ci_BLahO;GgP9P_Ca&ZjO7`b~qQ?fa6-i6oBM<_5Z z9Ax5LK&PG(GJ0LxpP?bs`GM}+f*!_o-)?*AOlW^L#$YgaIX?r_l`O3s_Bqv38qH7a z(ZWTr=h~zh*L-1mx6D}EmLvPsU}`~d$S70Nsp*g=MuiX(G;&0aRW2BAG05Z&G&SwI za?P^FtV09pHn}}~&gs5kIq}briZMTHCAkkS%#00|H}5-Uv;(;z;0$unvbhJgrO}9% z;P5;jn2A8CypNb1q3>MCKdg|LJCXQM85R)(RymjN30;Xa_Kex2Y_xqv5@HsdU!!v+ zOQC1-laj4YjXb3@Zz|YtX`0N2?=~g#N9q<{763Q&h!FFQoak|tA9XoG8~r#e5;uJ| zfj{B(mr!4NIT9?uPdn&CEgBsTonL_ifKX^}vr+o(VHymy0RX__0AT;kLfZIGA)TS( zsXm`8>Bwc&CNl$2^b%c-a6KVUbzWO71)Hbj4YztVJHamu3S5cg0MR?Q>Xv%N_5=iL zrcqDN;)If0TEmffq3T+f3>rq#YznEx$dBhLKXKSlyfPxTDua;uCiv{No#~7_soR%b zX~?7**?*}%d2D`>EUNBQk$sygXpO3FV6acf|2tpEKYYC@0Ytx3dSqQaGTtu$K_z~s zt<&~sQ-eNOqH8&_DYe#O4{FiPjN}5!>mDdT9sT6N9WN`Z<7bo*?rGM~K%xb@X%7Tj zdX!uWVW3#P{t~Rr@!lT&#yGxvzn!1KZ&}A0jb!`h>Me#L`9nTAA)!lENyTGfm&la| z$xE$*GSB1#38z|>Bf_d`)zAOtp2)Z^-^!mn@5)P1->H}4rjx?pBZBIYDkhX-y*teu zm74b#P`_}BvxF^K3-c?yj+&{1WCml1@%q(lKi;+eafl~oOY1_!eU%|Q@`1pEI;{s$ z*?L3OLvuBoU4CQ>BT?>&$u(n5S^bo#d%vT8U-sjBwkF(Fj#?Q@!7Tbm3f`4oubqyH zcbRsH5F$(_K8(Gq6+W#Wsb|JNv3hRa4Ue59If%Pu@bYE=T6~v;|5z$BcDKSEO&hAe z@ntY*>bVJ1)v1~rF9*uzlU~|_-ZTst6STJO#a@L6qVMJi=)o}76%y96FGI`A^N))B z`FQJUDeII#vH1>j8>pXxT$;&}dw*5@zb?lyuEk)u0vJr&^&IV zvy`!LePog5le^L9XyYRca`JS zUl%~LT|?9Tj~_9$YW3nBajbyH-nShEC-{=V;4t)#VF>}a$nlnQeJ)YFr^F-P`Zg;Z zwa1SGWuA03<$}zO4MU$^nMA=eZ`yXk`p}1M$OWVv-Qm7Mxa=Rj*d(bJXH(ydH~GtD z>SxA?7s|iA%f#<*=CMwl#>zr(S}V~~i%M?;Esyux`ajt4Lkkbx;|UAuO3rkwcHUgY z1>4Xjp!qa#z&_&zZEoWQi8fUxz8Bs4QhTzYE`L}mJ>IL2J*h#UoP)^+>GoORZV?Vl zd!CFO}qfev_WD1#;N>{UiN`1_qx^sfq^E03=*~56>c6z?lbftCK zAT`E9?3z`Z9CEzm;;*Z`!Kh{tnx-XUZZ4v%D(t3?rmA^F*CDup!Ut;)mto8Z+lGTtX*7r2d-bPYt^<*j0 MqKg55oAm1LZ>jPIo&W#< literal 0 HcmV?d00001 diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/main.tf b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/main.tf new file mode 100644 index 000000000000..156495110856 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/main.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + aws = { + source = "aws" + version = "5.35.0" + } + } +} + +resource "aws_s3_bucket" "this" { + +} diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/tfplan b/pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots/single-failure/tfplan new file mode 100644 index 0000000000000000000000000000000000000000..c07b8d1757f5a6a2f958a27faee85d1105f2490b GIT binary patch literal 2563 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 literal 0 HcmV?d00001 diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf new file mode 100644 index 000000000000..a474edf3ea6e --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf @@ -0,0 +1,3 @@ +module "s3_bucket" { + source = "./modules/s3" +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/main.tf new file mode 100644 index 000000000000..d847578a1a00 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/main.tf @@ -0,0 +1,8 @@ +resource "aws_s3_bucket" "this" { + +} + +module "s3_log" { + source = "./modules/logging" + bucket = aws_s3_bucket.this.id +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/modules/logging/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/modules/logging/main.tf new file mode 100644 index 000000000000..b9cb54f04a5b --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/modules/logging/main.tf @@ -0,0 +1,10 @@ +resource "aws_s3_bucket_versioning" "this" { + bucket = var.bucket + versioning_configuration { + status = "Enabled" + } +} + +variable "bucket" { + type = string +} diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/terraform.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/terraform.tf new file mode 100644 index 000000000000..ac2d39c811e5 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/terraform.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + aws = { + source = "aws" + version = "5.35.0" + } + } +} diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/tfplan b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/tfplan new file mode 100644 index 0000000000000000000000000000000000000000..148507541588a250aa8025e8b68471fc14638aee GIT binary patch literal 3487 zcmd6pX;_l!8pko*rNnH^PMWE?fw*UpVvc5}W|ATejvET*0^vfTV`)=_so|35zT}P> zZmH$us4ZGZF5`k@ij`?)Q>JK{%M1sb(@379pU^wm+eEF(W*6$iSPNZElv7z~3}i+`5fc#LP=dZJE%k6~jjC!*IQE6hv`ErRR6(@( zba2$4pQjol^CCyvc)Y<&5*1Q!H_HJmPJ`93x(R^_U<|{sQtH>IzNK1s+Hu!5(`Vlu zM|fg+#lE@$&^-=u%4z@H#VhU;KqjPjI|>A-=i zYh9H5j;Uu%-6NfuH%D*kzJI6j3ttbl2dpM^zt@8_6>R0Qh{QgM=i$D{PFi2>`GV1ptui)~v*r znhhgk$@qVY_D~!`+R6}gem+kPmljjFdk&(O=v=7a3$=U6d&4aW30{tB0GwCZ3S;_E z`%Z;wC6j}AZhSFWb_BeTqM;LOcz-n6p@dup|MRC?e>GGpVt49M>oRd!Z$nRCQ%TLQ zNcz_jh7P|nz4|hZ4qip>S*DveZ!*n}!?HyzFwLA3L zM+NW1WuV%7(zZi?w;Ga`Zd)j^VU%T2&(igXqd5=&uQNvJo&#)3-!csix-^A@iBX8$bC=*V*)Y&qFG@!sRXF-(c~kB1@TBlcUji z(HLKTl!*(|3BRdvOO*UEmV`HU>M^{qF2m<%C7hX!@s{og4Lz|-Zpt8hTlL%uM^+KBAN*1It{fb8oO1D3s zLwGfmW%F6tALo`iUAO-*C+6o5=f8f*8^mIoAD;1al4XC4Y*ZR{B<>3~*JYa{a`cDm zhi7lgoY+&zk3zV|C)}QB%N`^}-)TX#ECp~q*)df+qn9TVojwIdEzU60?tX}-o%nDf zlCNj+W?^FHF2AFBw3$I`w|j2=8Gn~#=6Xz6Yux(ZDVqU27jjIIb!vmQ*KAw+ji{@zd z_2Tx4!6I{A)((mR+qNWI%%kqO*wKyJYUbS(`*D_rHH=CwJTjiJIk;5KQtd{;oLY*v zZ3wgX@+L%6=S?FwV-e%L0{n$y`D9gjJZI=hQ@95DJev05t`EngA|Oq1x1%km_kpAm z6sd8xgKa`CgK3?9%@o25uNz-^; zteAtLqz1Yivb1CSa1Nnrq148sF_yg`YcnHC$JI#$G%Mq1xAK7P_YTBjLN07%pai=4ali8w@fW9)-o9!J+>&LG zbpco+H&w;Jnm*#(I2q);Suw+2fu2;#EC;Y2njQ`OWz%*R$F|Q?Iix2c3ssMnsp|zh z(BCK)(ZQ$ontg=yJ~$`(WH$rbZ_6HoOx|xQ8WB&W`3zJ}YT`&gDa(bx25j7}JFG^3xaNSBhfPfFcT4Gu@2BTqNYOZHpet=C2pwzn0xT~+*=NUJSM$o$ua6E>a|Ph6FWAPGWX z>pSGykiw0B#j#d3xlYLS{A+Dw;r_g09II*)7l!>`HnKLBaIJlt5t0O8!~TwR5EHBh R5Uei+$P$##2kDike*;g`J_P^( literal 0 HcmV?d00001 diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/checks/ec2-userdata.rego b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/checks/ec2-userdata.rego new file mode 100644 index 000000000000..7ce95b9f9a54 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/checks/ec2-userdata.rego @@ -0,0 +1,21 @@ +# METADATA +# title: Test rego 2 +# description: Instances with userdata are not allowed +# schemas: +# - input: schema["cloud"] +# custom: +# avd_id: ID001 +# severity: MEDIUM +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ec2 +# provider: aws +package user.aws.ID001 + +deny[res] { + instance := input.aws.ec2.instances[_] + instance.userdata.value != "" + res := result.new("Instances with userdata are not allowed", instance.userdata) +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/main.tf new file mode 100644 index 000000000000..9755bb5807a5 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/main.tf @@ -0,0 +1,5 @@ +module "ec2_instance" { + source = "./modules/ec2" + instance_type = "t3.micro" + user_data = "test" +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/modules/ec2/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/modules/ec2/main.tf new file mode 100644 index 000000000000..488bb6200e29 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/modules/ec2/main.tf @@ -0,0 +1,34 @@ +data "aws_ami" "this" { + most_recent = true + owners = ["amazon"] + filter { + name = "architecture" + values = ["arm64"] + } + filter { + name = "name" + values = ["al2023-ami-2023*"] + } +} + +resource "aws_instance" "this" { + ami = data.aws_ami.this.id + instance_market_options { + spot_options { + max_price = 0.0031 + } + } + instance_type = var.instance_type + tags = { + Name = "test-spot" + } + user_data = var.user_data +} + +variable "instance_type" { + type = string +} + +variable "user_data" { + type = string +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/terraform.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/terraform.tf new file mode 100644 index 000000000000..ac2d39c811e5 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/terraform.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + aws = { + source = "aws" + version = "5.35.0" + } + } +} diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/tfplan b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/tfplan new file mode 100644 index 0000000000000000000000000000000000000000..ecd1456588fbefef71be596f32294cb56a9d02ac GIT binary patch literal 4583 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 ZzY(#H1_}QD2Kf0)M;R|Uv9!ml{{bHcn%MvV literal 0 HcmV?d00001 diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/checks/s3-bucket-name.rego b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/checks/s3-bucket-name.rego new file mode 100644 index 000000000000..798b1b705a49 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/checks/s3-bucket-name.rego @@ -0,0 +1,21 @@ +# METADATA +# title: Test rego +# description: A bucket named "test-bucket" is not allowed +# schemas: +# - input: schema["cloud"] +# custom: +# avd_id: ID001 +# severity: LOW +# input: +# selector: +# - type: cloud +# subtypes: +# - service: s3 +# provider: aws +package user.aws.ID001 + +deny[res] { + bucket := input.aws.s3.buckets[_] + bucket.name.value == "test-bucket" + res := result.new("Bucket not allowed", bucket.name) +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/main.tf new file mode 100644 index 000000000000..de5a5fb6f7dc --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/main.tf @@ -0,0 +1,6 @@ +module "s3_bucket" { + source = "terraform-aws-modules/s3-bucket/aws" + version = "4.1.0" + + bucket = "test-bucket" +} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/terraform.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/terraform.tf new file mode 100644 index 000000000000..ac2d39c811e5 --- /dev/null +++ b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/terraform.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + aws = { + source = "aws" + version = "5.35.0" + } + } +} diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/tfplan b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/tfplan new file mode 100644 index 0000000000000000000000000000000000000000..872ad20646cb20f6063f299416e99fb0b73fea91 GIT binary patch literal 13256 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