diff --git a/docs/docs/advanced/container/unpacked-filesystem.md b/docs/docs/advanced/container/unpacked-filesystem.md index 74232db9a26a..cbc27fbf07f5 100644 --- a/docs/docs/advanced/container/unpacked-filesystem.md +++ b/docs/docs/advanced/container/unpacked-filesystem.md @@ -113,4 +113,4 @@ Total: 20 (UNKNOWN: 0, LOW: 2, MEDIUM: 10, HIGH: 8, CRITICAL: 0) +--------------+------------------+----------+-------------------+---------------+---------------------------------------+ ``` - + \ No newline at end of file diff --git a/docs/docs/coverage/os/bottlerocket.md b/docs/docs/coverage/os/bottlerocket.md new file mode 100644 index 000000000000..8c3e39bb44c2 --- /dev/null +++ b/docs/docs/coverage/os/bottlerocket.md @@ -0,0 +1,15 @@ +# Bottlerocket +Trivy supports the following scanners for OS packages. + +| Scanner | Supported | +| :-----------: | :-------: | +| SBOM | ✓ | +| Vulnerability | - | +| License | - | + +Please see [here](index.md#supported-os) for supported versions. + +## SBOM +Trivy detects packages that are listed in the [software inventory]. + +[software inventory]: https://bottlerocket.dev/en/os/1.37.x/concepts/variants/#software-inventory diff --git a/docs/docs/coverage/os/index.md b/docs/docs/coverage/os/index.md index 74a40787c600..e82e680451d3 100644 --- a/docs/docs/coverage/os/index.md +++ b/docs/docs/coverage/os/index.md @@ -28,6 +28,7 @@ Trivy supports operating systems for | [Photon OS](photon.md) | 1.0, 2.0, 3.0, 4.0 | tndf/yum/rpm | | [Debian GNU/Linux](debian.md) | 7, 8, 9, 10, 11, 12 | apt/dpkg | | [Ubuntu](ubuntu.md) | All versions supported by Canonical | apt/dpkg | +| [Bottlerocket](bottlerocket.md) | 1.7.0 and upper | bottlerocket | | [OSs with installed Conda](../others/conda.md) | - | conda | ## Supported container images diff --git a/docs/docs/coverage/os/rhel.md b/docs/docs/coverage/os/rhel.md index 8300005a4968..5694c99b5999 100644 --- a/docs/docs/coverage/os/rhel.md +++ b/docs/docs/coverage/os/rhel.md @@ -22,6 +22,13 @@ Trivy detects packages that have been installed through package managers such as ## Vulnerability Red Hat offers its own security advisories, and these are utilized when scanning Red Hat Enterprise Linux (RHEL) for vulnerabilities. +### Content manifests +Red Hat’s security advisories use CPEs to identify product sets. For example, even packages installed in the same container image can have different CPEs. +For this reason, Red Hat’s container images include stored content manifests, which we convert to CPEs, and perform vulnerability scanning. + +Since this system ties each content manifest to its packages on a per-layer basis, +if layers get merged (for instance, by using `docker run` or `docker export`) we can no longer determine the correct CPE, which may lead to false detection. + ### Data Source See [here](../../scanner/vulnerability.md#data-sources). @@ -82,3 +89,5 @@ Trivy identifies licenses by examining the metadata of RPM packages. [NVD]: https://nvd.nist.gov/vuln/detail/CVE-2023-0464 [vulnerability statuses]: ../../configuration/filtering.md#by-status + +[content-set-default]: https://github.com/aquasecurity/trivy/blob/c80310d7690d8aeb7d3d77416c18c0c8b9aebe17/pkg/detector/ospkg/redhat/redhat.go#L25-L42 diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 2d977a4f1d18..076c95557c1a 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -36,6 +36,7 @@ trivy config [flags] DIR --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-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 9e99c441f268..2f432682baab 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -65,6 +65,7 @@ trivy filesystem [flags] PATH --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 + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index 4e90a2e19471..4ef7eb01f9c6 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -84,6 +84,7 @@ trivy image [flags] IMAGE_NAME --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 + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 2a637d307625..6f447285cb83 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -80,6 +80,7 @@ trivy kubernetes [flags] [CONTEXT] --node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.3.1") --node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp") --offline-scan do not issue API requests to identify dependencies + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 4265b790cd74..feb84988d239 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -64,6 +64,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --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 + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 4607a25f56a7..910368040a29 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -67,6 +67,7 @@ trivy rootfs [flags] ROOTDIR --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 + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index 42df969bfefd..23486a14667d 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -46,6 +46,7 @@ trivy sbom [flags] SBOM_PATH --list-all-pkgs output all packages in the JSON report regardless of vulnerability --no-progress suppress progress bar --offline-scan do not issue API requests to identify dependencies + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 49010ac26e85..a31ab63f112f 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -59,6 +59,7 @@ trivy vm [flags] VM_IMAGE --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 + --only-dirs strings specify the directories where the traversal is allowed -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) diff --git a/docs/docs/target/rootfs.md b/docs/docs/target/rootfs.md index 1b6b7438b6a4..8a403f57d5e2 100644 --- a/docs/docs/target/rootfs.md +++ b/docs/docs/target/rootfs.md @@ -13,3 +13,19 @@ $ trivy rootfs /path/to/rootfs Rootfs scanning works differently from the Filesystem scanning. You should use `trivy fs` to scan your local projects in CI/CD. See [here](../scanner/vulnerability.md) for the differences. + +!!! note + Scanning vulnerabilities for `Red Hat` has a limitation, see the [Red Hat](../coverage/os/rhel.md#content-manifests) page for details. + +## Performance Optimization + +By default, Trivy traverses all files from the specified root directory to find target files for scanning. +However, when you only need to scan specific files with absolute paths, you can avoid this traversal, which makes scanning faster. +For example, when scanning only OS packages, no full traversal is performed: + +```bash +$ trivy rootfs --pkg-types os --scanners vuln / +``` + +When scanning language-specific packages or secrets, traversal is necessary because the location of these files is unknown. +If you want to exclude specific directories from scanning for better performance, you can use the [--skip-dirs](../configuration/skipping.md) option. diff --git a/docs/docs/target/vm.md b/docs/docs/target/vm.md index 4bba67626463..8cbe5284af45 100644 --- a/docs/docs/target/vm.md +++ b/docs/docs/target/vm.md @@ -150,6 +150,9 @@ See [here](../scanner/vulnerability.md) for the detail. $ trivy vm [YOUR_VM_IMAGE] ``` +!!! note + Scanning `Red Hat` has a limitation, see the [Red Hat](../coverage/os/rhel.md#content-manifests) page for details. + ### Misconfigurations It is supported, but it is not useful in most cases. As mentioned [here](../scanner/misconfiguration/index.md), Trivy mainly supports Infrastructure as Code (IaC) files for misconfigurations. diff --git a/go.mod b/go.mod index 0b70013196f7..b59e3181326d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/aquasecurity/trivy -go 1.24 +go 1.24.0 require ( github.com/Azure/azure-sdk-for-go v68.0.0+incompatible @@ -11,72 +11,54 @@ require ( github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible github.com/Masterminds/sprig/v3 v3.3.0 github.com/NYTimes/gziphandler v1.1.1 - github.com/alecthomas/chroma v0.10.0 github.com/alicebob/miniredis/v2 v2.34.0 - github.com/apparentlymart/go-cidr v1.1.0 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce github.com/aquasecurity/go-npm-version v0.0.1 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/aquasecurity/go-version v0.0.1 - github.com/aquasecurity/iamgo v0.0.10 github.com/aquasecurity/jfather v0.0.8 github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 github.com/aquasecurity/tml v0.6.1 - github.com/aquasecurity/trivy-checks v1.7.1 github.com/aquasecurity/trivy-db v0.0.0-20250227071930-8bd8a9b89e2d github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 - github.com/aquasecurity/trivy-kubernetes v0.7.0 github.com/aws/aws-sdk-go-v2 v1.36.3 github.com/aws/aws-sdk-go-v2/config v1.29.8 github.com/aws/aws-sdk-go-v2/credentials v1.17.61 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.206.0 github.com/aws/aws-sdk-go-v2/service/ecr v1.42.0 - github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0 - github.com/aws/smithy-go v1.22.3 + github.com/aws/smithy-go v1.22.3 // indirect github.com/bitnami/go-version v0.0.0-20231130084017-bb00604d650c github.com/bmatcuk/doublestar/v4 v4.8.1 github.com/cenkalti/backoff/v4 v4.3.0 - github.com/cheggaaa/pb/v3 v3.1.6 github.com/containerd/containerd/v2 v2.0.2 github.com/containerd/platforms v1.0.0-rc.1 github.com/distribution/reference v0.6.0 github.com/docker/cli v27.5.0+incompatible - github.com/docker/docker v27.5.0+incompatible - github.com/docker/go-connections v0.5.0 + github.com/docker/docker v27.5.0+incompatible // indirect + github.com/docker/go-connections v0.6.0 github.com/docker/go-units v0.5.0 github.com/fatih/color v1.18.0 github.com/go-git/go-git/v5 v5.13.2 - github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 // Replace with encoding/json/v2 when proposal is accepted. Track https://github.com/golang/go/issues/71497 github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-redis/redis/v8 v8.11.5 github.com/gocsaf/csaf/v3 v3.1.1 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-containerregistry v0.20.3 - github.com/google/go-github/v62 v62.0.0 - github.com/google/licenseclassifier/v2 v2.0.0 github.com/google/uuid v1.6.0 github.com/google/wire v0.6.0 - github.com/hashicorp/go-getter v1.7.8 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.7 - github.com/hashicorp/go-uuid v1.0.3 - github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/hashicorp/hc-install v0.9.1 - github.com/hashicorp/hcl/v2 v2.23.0 - github.com/hashicorp/terraform-exec v0.22.0 github.com/in-toto/in-toto-golang v0.9.0 github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 - github.com/knqyf263/go-rpmdb v0.1.1 + github.com/knqyf263/go-rpmdb v0.1.2-0.20241125135340-7670f0f23c16 github.com/knqyf263/nested v0.0.1 github.com/kylelemons/godebug v1.1.0 github.com/liamg/memoryfs v1.6.0 - github.com/magefile/mage v1.15.0 github.com/masahiro331/go-disk v0.0.0-20240625071113-56c933208fee github.com/masahiro331/go-ebs-file v0.0.0-20240917043618-e6d2bea5c32e github.com/masahiro331/go-ext4-filesystem v0.0.0-20240620024024-ca14e6327bbd @@ -84,17 +66,16 @@ require ( github.com/masahiro331/go-vmdk-parser v0.0.0-20221225061455-612096e4bbbd github.com/masahiro331/go-xfs-filesystem v0.0.0-20231205045356-1b22259a6c44 github.com/mattn/go-shellwords v1.0.12 - github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 - github.com/mitchellh/mapstructure v1.5.0 + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/buildkit v0.18.2 - github.com/open-policy-agent/opa v1.1.0 + github.com/open-policy-agent/opa v0.70.0 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.1.0 + github.com/opencontainers/image-spec v1.1.1 github.com/openvex/discovery v0.1.1-0.20240802171711-7c54efc57553 github.com/openvex/go-vex v0.2.5 github.com/owenrumney/go-sarif/v2 v2.3.3 - github.com/owenrumney/squealer v1.2.11 github.com/package-url/packageurl-go v0.1.3 github.com/quasilyte/go-ruleguard/dsl v0.3.22 github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c @@ -116,41 +97,29 @@ require ( github.com/twitchtv/twirp v8.1.3+incompatible github.com/xeipuuv/gojsonschema v1.2.0 github.com/xlab/treeprint v1.2.0 - github.com/zclconf/go-cty v1.16.2 - github.com/zclconf/go-cty-yaml v1.1.0 go.etcd.io/bbolt v1.4.0 - golang.org/x/crypto v0.33.0 + golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/mod v0.23.0 - golang.org/x/net v0.35.0 - golang.org/x/sync v0.11.0 - golang.org/x/term v0.29.0 - golang.org/x/text v0.22.0 - golang.org/x/vuln v1.1.4 + golang.org/x/net v0.37.0 + golang.org/x/sync v0.12.0 + golang.org/x/term v0.30.0 + golang.org/x/text v0.23.0 golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 google.golang.org/protobuf v1.36.5 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.17.1 - k8s.io/api v0.32.2 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 modernc.org/sqlite v1.35.0 sigs.k8s.io/yaml v1.4.0 // indirect ) require ( - cel.dev/expr v0.19.0 // indirect - cloud.google.com/go v0.116.0 // indirect - cloud.google.com/go/auth v0.14.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.2.2 // indirect - cloud.google.com/go/monitoring v1.21.2 // indirect - cloud.google.com/go/storage v1.45.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect @@ -159,40 +128,27 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect github.com/DataDog/zstd v1.5.5 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect github.com/Intevation/gval v1.3.0 // indirect github.com/Intevation/jsonpath v0.2.1 // indirect - github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect - github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.9 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/ProtonMail/go-crypto v1.1.5 // indirect - github.com/VividCortex/ewma v1.2.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/agnivade/levenshtein v1.2.0 // indirect github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect - github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/blang/semver/v4 v4.0.0 // indirect github.com/briandowns/spinner v1.23.0 // indirect - github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.5.0 // indirect - github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/containerd/cgroups/v3 v3.0.3 // indirect - github.com/containerd/containerd v1.7.25 // indirect github.com/containerd/containerd/api v1.8.0 // indirect - github.com/containerd/continuity v0.4.5 // indirect + github.com/containerd/continuity v0.4.5 github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect @@ -202,34 +158,21 @@ require ( github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect - github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect - github.com/docker/go-metrics v0.0.1 // indirect - github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect - github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/envoyproxy/go-control-plane v0.13.1 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect - github.com/evanphx/json-patch v5.9.0+incompatible // indirect - github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect - github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -249,43 +192,21 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.1.2 // indirect github.com/google/certificate-transparency-go v1.1.8 // indirect - github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/s2a-go v0.1.9 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect - github.com/googleapis/gax-go/v2 v2.14.1 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/gosuri/uitable v0.0.4 // indirect - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect 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/golang-lru v0.6.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-7 // indirect - github.com/hashicorp/terraform-json v0.24.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect - github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect - github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect - github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/magiconair/properties v1.8.9 // indirect @@ -294,25 +215,18 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/docker-image-spec v1.3.1 github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/spdystream v0.5.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect github.com/moby/sys/user v0.3.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect - github.com/moby/term v0.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/moby/term v0.5.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect github.com/oklog/ulid v1.3.1 // indirect @@ -321,7 +235,6 @@ require ( github.com/opencontainers/selinux v1.11.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect @@ -335,8 +248,6 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rubenv/sql-migrate v1.7.1 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect @@ -365,9 +276,6 @@ require ( github.com/transparency-dev/merkle v0.0.2 // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/vbatts/tar-split v0.11.6 // indirect - github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -378,49 +286,26 @@ require ( go.mongodb.org/mongo-driver v1.14.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/sdk v1.34.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect - google.golang.org/api v0.218.0 // indirect - google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/time v0.11.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/grpc v1.70.0 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiextensions-apiserver v0.32.1 // indirect - k8s.io/apimachinery v0.32.2 // indirect - k8s.io/apiserver v0.32.1 // indirect - k8s.io/cli-runtime v0.32.1 // indirect - k8s.io/client-go v0.32.1 // indirect - k8s.io/component-base v0.32.1 // indirect + k8s.io/client-go v0.32.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect - k8s.io/kubectl v0.32.1 // indirect modernc.org/libc v1.61.13 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.8.2 // indirect - mvdan.cc/sh/v3 v3.10.0 // indirect - oras.land/oras-go v1.2.5 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/kustomize/api v0.18.0 // indirect - sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect tags.cncf.io/container-device-interface v0.8.0 // indirect tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect ) @@ -437,6 +322,16 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.16 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/moby/moby/api v1.53.0 + github.com/moby/moby/client v0.2.2 github.com/oklog/ulid/v2 v2.1.0 // indirect + github.com/onsi/gomega v1.35.1 // indirect github.com/samber/oops v1.15.0 // indirect ) + +// github.com/docker/docker v27 has known security vulnerabilities. +// The 27.x branch of moby/moby contains fixes beyond v27.5.1. +// Redirect to the latest 27.x commit until transitive dependencies +// migrate to github.com/moby/moby sub-modules. +replace github.com/docker/docker => github.com/moby/moby v27.5.2-0.20250218170852-77446557b0f8+incompatible diff --git a/go.sum b/go.sum index 830f886af54a..d37b4c0f6cf6 100644 --- a/go.sum +++ b/go.sum @@ -1,632 +1,26 @@ -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= cloud.google.com/go/kms v1.20.4 h1:CJ0hMpOg1ANN9tx/a/GPJ+Uxudy8k6f3fvGFuTHiE5A= cloud.google.com/go/kms v1.20.4/go.mod h1:gPLsp1r4FblUgBYPOcvI/bUPpdMg2Jm1ZVKU4tQUfcc= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= -cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= -cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM= -cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= -cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ= cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24= cuelang.org/go v0.8.1 h1:VFYsxIFSPY5KgSaH1jQ2GxHOrbu6Ga3kEI70yCZwnOg= cuelang.org/go v0.8.1/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M= @@ -649,8 +43,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0 h1:7rKG7Um github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0/go.mod h1:Wjo+24QJVhhl/L7jy6w9yzFF2yDOf3cKECAa8ecf9vE= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 h1:eXnN9kaS8TiDwXjoie3hMRLuwdUBUMW9KRgOqB3mCaw= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0/go.mod h1:XIpam8wumeZ5rVMuhdDQLMfIPDf1WO3IzrCRO3e3e3o= -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= @@ -678,38 +72,22 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo= github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg= -github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= -github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible h1:juIaKLLVhqzP55d8x4cSVgwyQv76Z55/fRv/UBr2KkQ= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1/go.mod h1:0wEl7vrAD8mehJyohS9HZy+WyEOaQO2mJx86Cvh93kM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw= github.com/Intevation/gval v1.3.0/go.mod h1:xmGyGpP5be12EL0P12h+dqiYG8qn2j3PJxIgkoOHO5o= github.com/Intevation/jsonpath v0.2.1 h1:rINNQJ0Pts5XTFEG+zamtdL7l9uuE1z0FBA+r55Sw+A= github.com/Intevation/jsonpath v0.2.1/go.mod h1:WnZ8weMmwAx/fAO3SutjYFU+v7DFreNYnibV7CiaYIw= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= -github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -717,29 +95,16 @@ github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6 github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= -github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= -github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= -github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= @@ -770,18 +135,9 @@ github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/x github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= -github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= -github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= -github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM= github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM= @@ -793,8 +149,6 @@ github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZ github.com/aquasecurity/go-version v0.0.0-20201107203531-5e48ac5d022a/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU= github.com/aquasecurity/go-version v0.0.1 h1:4cNl516agK0TCn5F7mmYN+xVs1E3S45LkgZk3cbaW2E= github.com/aquasecurity/go-version v0.0.1/go.mod h1:s1UU6/v2hctXcOa3OLwfj5d9yoXHa3ahf+ipSwEvGT0= -github.com/aquasecurity/iamgo v0.0.10 h1:t/HG/MI1eSephztDc+Rzh/YfgEa+NqgYRSfr6pHdSCQ= -github.com/aquasecurity/iamgo v0.0.10/go.mod h1:GI9IQJL2a+C+V2+i3vcwnNKuIJXZ+HAfqxZytwy+cPk= github.com/aquasecurity/jfather v0.0.8 h1:tUjPoLGdlkJU0qE7dSzd1MHk2nQFNPR0ZfF+6shaExE= github.com/aquasecurity/jfather v0.0.8/go.mod h1:Ag+L/KuR/f8vn8okUi8Wc1d7u8yOpi2QTaGX10h71oY= github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= @@ -803,21 +157,16 @@ github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqY github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8/go.mod h1:wXA9k3uuaxY3yu7gxrxZDPo/04FEMJtwyecdAlYrEIo= github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo= github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY= -github.com/aquasecurity/trivy-checks v1.7.1 h1:Pn+Mk0SkqY7adfZT6ZsRjCuum3svr7n5z3w+HpGXmbY= -github.com/aquasecurity/trivy-checks v1.7.1/go.mod h1:YhmXAXgRdYIAYIr+/k/oEYUWoW7ZgGctmnJiV17ZcU8= github.com/aquasecurity/trivy-db v0.0.0-20250227071930-8bd8a9b89e2d h1:T16WrTi21YsMLQVhtp1r1hOIYK3x4BjnftpL9cp64Eo= github.com/aquasecurity/trivy-db v0.0.0-20250227071930-8bd8a9b89e2d/go.mod h1:4bTsQPtMBN8v+UfUlE1aQBN1imftefnDafHBF85+aT8= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8= -github.com/aquasecurity/trivy-kubernetes v0.7.0 h1:0pRJFSslUYd9xzQIEw1c0mS7k1Vv489nH/LsxeU6yME= -github.com/aquasecurity/trivy-kubernetes v0.7.0/go.mod h1:O6JZMicTmZrsjEpGzsnBMhPTHAfpnTMqXTAMidG6M+M= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= @@ -836,8 +185,6 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/ebs v1.22.1 h1:SeDJWG4pmye+/aO6k+zt9clPTUy1MXqUmkW8rbAddQg= github.com/aws/aws-sdk-go-v2/service/ebs v1.22.1/go.mod h1:wRzaW0v9GGQS0h//wpsVDw3Hah5gs5UP+NxoyGeZIGM= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.206.0 h1:pVspPiBDDfDhVXFY+jpDd7yIOciDwQwYoPMb/80agTw= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.206.0/go.mod h1:ouvGEfHbLaIlWwpDpOVWPWR+YwO0HDv3vm5tYLq8ImY= github.com/aws/aws-sdk-go-v2/service/ecr v1.42.0 h1:qQxVtUF36VLVgY/hDnozEL3ls9F1fGKsVsEDHddXOTM= github.com/aws/aws-sdk-go-v2/service/ecr v1.42.0/go.mod h1:iQ1skgw1XRK+6Lgkb0I9ODatAP72WoTILh0zXQ5DtbU= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 h1:PpbXaecV3sLAS6rjQiaKw4/jyq3Z8gNzmoJupHAoBp0= @@ -848,8 +195,6 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/kms v1.37.8 h1:KbLZjYqhQ9hyB4HwXiheiflTlYQa0+Fz0Ms/rh5f3mk= github.com/aws/aws-sdk-go-v2/service/kms v1.37.8/go.mod h1:ANs9kBhK4Ghj9z1W+bsr3WsNaPF71qkgd6eE6Ekol/Y= -github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0 h1:EBm8lXevBWe+kK9VOU/IBeOI189WPRwPUc3LvJK9GOs= -github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU= github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 h1:2U9sF8nKy7UgyEeLiZTRg6ShBS22z8UnYpV6aRFL0is= github.com/aws/aws-sdk-go-v2/service/sso v1.25.0/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 h1:wjAdc85cXdQR5uLx5FwWvGIHm4OPJhTyzUHU8craXtE= @@ -860,12 +205,8 @@ github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8/go.mod h1:2JF49jcDOrLStIXN/j/K1EKRq8a8R2qRnlZA6/o/c7c= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bitnami/go-version v0.0.0-20231130084017-bb00604d650c h1:C4UZIaS+HAw+X6jGUsoP2ZbM99PuqhCttjomg1yhNAI= github.com/bitnami/go-version v0.0.0-20231130084017-bb00604d650c/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= @@ -874,20 +215,10 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38= github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/buildkite/agent/v3 v3.62.0 h1:yvzSjI8Lgifw883I8m9u8/L/Thxt4cLFd5aWPn3gg70= github.com/buildkite/agent/v3 v3.62.0/go.mod h1:jN6SokGXrVNNIpI0BGQ+j5aWeI3gin8F+3zwA5Q6gqM= github.com/buildkite/go-pipeline v0.3.2 h1:SW4EaXNwfjow7xDRPGgX0Rcx+dPj5C1kV9LKCLjWGtM= @@ -899,19 +230,10 @@ github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyz github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= -github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk= -github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -923,20 +245,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= -github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= @@ -945,8 +253,6 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= -github.com/containerd/containerd v1.7.25 h1:khEQOAXOEJalRO228yzVsuASLH42vT7DIo9Ss+9SMFQ= -github.com/containerd/containerd v1.7.25/go.mod h1:tWfHzVI0azhw4CT2vaIjsb2CoV4LJ9PrMPaULAr21Ok= github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/containerd/v2 v2.0.2 h1:GmH/tRBlTvrXOLwSpWE2vNAm8+MqI6nmxKpKBNKY8Wc= @@ -971,17 +277,14 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= -github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= -github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= @@ -992,10 +295,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v4 v4.5.1 h1:7DCIXrQjo1LKmM96YD+hLVJ2EEsyyoWxJfpdd56HLps= -github.com/dgraph-io/badger/v4 v4.5.1/go.mod h1:qn3Be0j3TfV4kPbVoK0arXCD1/nr1ftth6sbL5jxdoA= -github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I= -github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -1007,35 +310,18 @@ github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1G github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= -github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= -github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= -github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= -github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= -github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= @@ -1049,26 +335,7 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= -github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= -github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= -github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= 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= @@ -1076,8 +343,6 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= @@ -1093,20 +358,12 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= @@ -1115,24 +372,12 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= -github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= -github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 h1:yE7argOs92u+sSCRgqqe6eF+cDaVhSPlioy1UkA0p/w= -github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -1161,8 +406,6 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -1175,22 +418,13 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.8.1/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y= github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= @@ -1201,7 +435,6 @@ github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -1210,32 +443,19 @@ github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQg github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM= +github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -1245,88 +465,42 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/certificate-transparency-go v1.1.8 h1:LGYKkgZF7satzgTak9R4yzfJXEeYVAjV6/EAEJOf1to= github.com/google/certificate-transparency-go v1.1.8/go.mod h1:bV/o8r0TBKRf1X//iiiSgWrvII4d7/8OiA+3vG26gI8= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v24.12.23+incompatible h1:ubBKR94NR4pXUCY/MUsRVzd9umNW7ht7EG9hHfS9FX8= -github.com/google/flatbuffers v24.12.23+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= -github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= -github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA= -github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= -github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= @@ -1338,51 +512,20 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY= -github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -1391,35 +534,16 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= -github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU= github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= -github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hc-install v0.9.1 h1:gkqTfE3vVbafGQo6VZXcy2v5yoz2bE0+nhZXruCuODQ= -github.com/hashicorp/hc-install v0.9.1/go.mod h1:pWWvN/IrfeBK4XPeXXYkL6EjMufHkCK5DvwxeLKuBf0= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= -github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= -github.com/hashicorp/terraform-exec v0.22.0 h1:G5+4Sz6jYZfRYUCg6eQgDsqTzkNXV+fP8l+uRmZHj64= -github.com/hashicorp/terraform-exec v0.22.0/go.mod h1:bjVbsncaeh8jVdhttWYZuBGj21FcYw6Ia/XfHcNO7lQ= -github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4NbeATsYz8Q= -github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow= github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= @@ -1427,8 +551,6 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= @@ -1440,57 +562,33 @@ github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc= github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= -github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= -github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GXhHq+7LeOzx/haG7HSIZokl3/0GkoUFzsRJjg= github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8= github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 h1:dWzdsqjh1p2gNtRKqNwuBvKqMNwnLOPLzVZT1n6DK7s= github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23/go.mod h1:lUaIXCWzf7BRKTY5iEcrYy1TfgbYLYVIS/B2vPkJzOc= github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 h1:aC6MEAs3PE3lWD7lqrJfDxHd6hcced9R4JTZu85cJwU= github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= -github.com/knqyf263/go-rpmdb v0.1.1 h1:oh68mTCvp1XzxdU7EfafcWzzfstUZAEa3MW0IJye584= -github.com/knqyf263/go-rpmdb v0.1.1/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww= +github.com/knqyf263/go-rpmdb v0.1.2-0.20241125135340-7670f0f23c16 h1:a0SnL7L4aPQ/6/MiQmlp6LeiF6moPWD2FvGx/KaDpbs= +github.com/knqyf263/go-rpmdb v0.1.2-0.20241125135340-7670f0f23c16/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww= github.com/knqyf263/nested v0.0.1 h1:Sv26CegUMhjt19zqbBKntjwESdxe5hxVPSk0+AKjdUc= github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1499,10 +597,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= @@ -1510,20 +604,11 @@ github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2T github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/liamg/memoryfs v1.6.0 h1:jAFec2HI1PgMTem5gR7UT8zi9u4BfG5jorCRlLH06W8= github.com/liamg/memoryfs v1.6.0/go.mod h1:z7mfqXFQS8eSeBBsFjYLlxYRMRyiPktytvYCYTb3BSk= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI= github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= -github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -1540,7 +625,6 @@ github.com/masahiro331/go-vmdk-parser v0.0.0-20221225061455-612096e4bbbd h1:Y30E github.com/masahiro331/go-vmdk-parser v0.0.0-20221225061455-612096e4bbbd/go.mod h1:5f7mCJGW9cJb8SDn3z8qodGxpMCOo8d/2nls/tiwRrw= github.com/masahiro331/go-xfs-filesystem v0.0.0-20231205045356-1b22259a6c44 h1:VmSjn0UCyfXUNdePDr7uM/uZTnGSp+mKD5+cYkEoLx4= github.com/masahiro331/go-xfs-filesystem v0.0.0-20231205045356-1b22259a6c44/go.mod h1:QKBZqdn6teT0LK3QhAf3K6xakItd1LonOShOEC44idQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -1550,31 +634,20 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 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.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.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= @@ -1590,10 +663,14 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/moby v27.5.2-0.20250218170852-77446557b0f8+incompatible h1:0F9IFkucLqzWHDx9hCF/mdD4xSYojJsRmitbcGMYQwY= +github.com/moby/moby v27.5.2-0.20250218170852-77446557b0f8+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w= +github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= +github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM= +github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= -github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -1604,17 +681,12 @@ github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozillazg/docker-credential-acr-helper v0.3.0 h1:DVWFZ3/O8BP6Ue3iS/Olw+G07u1hCq1EOVCDZZjCIBI= @@ -1622,9 +694,6 @@ github.com/mozillazg/docker-credential-acr-helper v0.3.0/go.mod h1:cZlu3tof523uj github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= @@ -1645,20 +714,18 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/open-policy-agent/opa v1.1.0 h1:HMz2evdEMTyNqtdLjmu3Vyx06BmhNYAx67Yz3Ll9q2s= -github.com/open-policy-agent/opa v1.1.0/go.mod h1:T1pASQ1/vwfTa+e2fYcfpLCvWgYtqtiUv+IuA/dLPQs= +github.com/open-policy-agent/opa v0.70.0 h1:B3cqCN2iQAyKxK6+GI+N40uqkin+wzIrM7YA60t9x1U= +github.com/open-policy-agent/opa v0.70.0/go.mod h1:Y/nm5NY0BX0BqjBriKUiV81sCl8XOjjvqQG7dXrggtI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -1676,8 +743,6 @@ github.com/openvex/go-vex v0.2.5/go.mod h1:j+oadBxSUELkrKh4NfNb+BPo77U3q7gdKME88 github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/owenrumney/go-sarif/v2 v2.3.3 h1:ubWDJcF5i3L/EIOER+ZyQ03IfplbSU1BLOE26uKQIIU= github.com/owenrumney/go-sarif/v2 v2.3.3/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= -github.com/owenrumney/squealer v1.2.11 h1:vMudrj70VeOzY+t7Phz9Yo0wAgm4kXes9DcTLBVDqGY= -github.com/owenrumney/squealer v1.2.11/go.mod h1:8KOuitfOfmS/OtzgxQbxnnrbngAGopfgKB/BiGGpqGA= github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs= github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= @@ -1685,25 +750,12 @@ github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1712,27 +764,13 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= -github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf h1:014O62zIzQwvoD7Ekj3ePDF5bv9Xxy0w6AZk0qYbjUk= @@ -1743,27 +781,17 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/rubenv/sql-migrate v1.7.1 h1:f/o0WgfO/GqNuVg+6801K/KW3WdDSupzSjDYODmiUq4= -github.com/rubenv/sql-migrate v1.7.1/go.mod h1:Ob2Psprc0/3ggbM6wCzyYVFFuc6FyZrb2AS+ezLDFb4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c h1:8gOLsYwaY2JwlTMT4brS5/9XJdrdIbmk2obvQ748CC0= github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c/go.mod h1:kwM/7r/rVluTE8qJbHAffduuqmSv4knVQT2IajGvSiA= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= @@ -1786,7 +814,6 @@ github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3 github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= @@ -1819,7 +846,6 @@ github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.12 h1:jvY1B9bjP+t github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.12/go.mod h1:2uEeOb8xE2RC6OvzxKux1wkS39Zv8gA27z92m49xUTc= github.com/sigstore/timestamp-authority v1.2.2 h1:X4qyutnCQqJ0apMewFyx+3t7Tws00JQ/JonBiu3QvLE= github.com/sigstore/timestamp-authority v1.2.2/go.mod h1:nEah4Eq4wpliDjlY342rXclGSO7Kb9hoRrl9tqLW13A= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -1832,13 +858,9 @@ github.com/sosedoff/gitkit v0.4.0 h1:opyQJ/h9xMRLsz2ca/2CRXtstePcpldiZN8DpLLF8Os github.com/sosedoff/gitkit v0.4.0/go.mod h1:V3EpGZ0nvCBhXerPsbDeqtyReNb48cwP9KtkUYTKT5I= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= @@ -1852,7 +874,6 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+ github.com/spiffe/go-spiffe/v2 v2.2.0 h1:9Vf06UsvsDbLYK/zJ4sYsIsHmMFknUD+feA7IYoWMQY= github.com/spiffe/go-spiffe/v2 v2.2.0/go.mod h1:Urzb779b3+IwDJD2ZbN8fVl3Aa8G4N/PiUe6iXC0XxU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -1868,7 +889,6 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -1915,8 +935,6 @@ github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= -github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1924,11 +942,7 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= -github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4= @@ -1948,79 +962,49 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70= -github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= -github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= -github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= -github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= -go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.step.sm/crypto v0.57.0 h1:YjoRQDaJYAxHLVwjst0Bl0xcnoKzVwuHCJtEo2VSHYU= go.step.sm/crypto v0.57.0/go.mod h1:+Lwp5gOVPaTa3H/Ul/TzGbxQPXZZcKIUGMS0lG6n9Go= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -2029,233 +1013,78 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2263,473 +1092,99 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0= -golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= -golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I= -golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= -google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= -google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2738,28 +1193,18 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -2773,123 +1218,58 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.17.1 h1:gzVoAD+qVuoJU6KDMSAeo0xRJ6N1znRxz3wyuXRmJDk= -helm.sh/helm/v3 v3.17.1/go.mod h1:nvreuhuR+j78NkQcLC3TYoprCKStLyw5P4T7E5itv2w= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= -k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= -k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= -k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= -k8s.io/cli-runtime v0.32.1 h1:19nwZPlYGJPUDbhAxDIS2/oydCikvKMHsxroKNGA2mM= -k8s.io/cli-runtime v0.32.1/go.mod h1:NJPbeadVFnV2E7B7vF+FvU09mpwYlZCu8PqjzfuOnkY= -k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= -k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= -k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= -k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/kubectl v0.32.1 h1:/btLtXLQUU1rWx8AEvX9jrb9LaI6yeezt3sFALhB8M8= -k8s.io/kubectl v0.32.1/go.mod h1:sezNuyWi1STk4ZNPVRIFfgjqMI6XMf+oCVLjZen/pFQ= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw= modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= -mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= -oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= -oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo= -sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= -sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= -sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= sigs.k8s.io/release-utils v0.8.4 h1:4QVr3UgbyY/d9p74LBhg0njSVQofUsAZqYOzVZBhdBw= sigs.k8s.io/release-utils v0.8.4/go.mod h1:m1bHfscTemQp+z+pLCZnkXih9n0+WukIUU70n6nFnU0= sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= diff --git a/integration/client_server_test.go b/integration/client_server_test.go index 7e80fd9f16ed..7c01afe15adb 100644 --- a/integration/client_server_test.go +++ b/integration/client_server_test.go @@ -13,7 +13,6 @@ import ( "github.com/aquasecurity/trivy/pkg/types" - dockercontainer "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" @@ -733,9 +732,7 @@ func setupRedis(t *testing.T, ctx context.Context) (testcontainers.Container, st Name: "redis", Image: imageName, ExposedPorts: []string{port}, - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, + AutoRemove: true, } redis, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ diff --git a/integration/registry_test.go b/integration/registry_test.go index 3f0469e2c396..711f1ddd7a74 100644 --- a/integration/registry_test.go +++ b/integration/registry_test.go @@ -19,7 +19,6 @@ import ( "github.com/aquasecurity/trivy/pkg/types" - dockercontainer "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" @@ -59,9 +58,7 @@ func setupRegistry(ctx context.Context, baseDir string, authURL *url.URL) (testc Mounts: testcontainers.Mounts( testcontainers.BindMount(filepath.Join(baseDir, "data", "certs"), "/certs"), ), - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, + AutoRemove: true, WaitingFor: wait.ForHTTP("v2").WithTLS(true).WithAllowInsecure(true). WithStatusCodeMatcher(func(status int) bool { return status == http.StatusUnauthorized @@ -84,10 +81,8 @@ func setupAuthServer(ctx context.Context, baseDir string) (testcontainers.Contai testcontainers.BindMount(filepath.Join(baseDir, "data", "auth_config"), "/config"), testcontainers.BindMount(filepath.Join(baseDir, "data", "certs"), "/certs"), ), - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, - Cmd: []string{"/config/config.yml"}, + AutoRemove: true, + Cmd: []string{"/config/config.yml"}, } authC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ diff --git a/internal/testutil/docker.go b/internal/testutil/docker.go index 60e1bb3ab668..a635f53923d2 100644 --- a/internal/testutil/docker.go +++ b/internal/testutil/docker.go @@ -7,8 +7,7 @@ import ( "strings" "testing" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/client" + "github.com/moby/moby/client" "github.com/stretchr/testify/require" ) @@ -17,7 +16,7 @@ type DockerClient struct { } func NewDockerClient(t *testing.T) *DockerClient { - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + cli, err := client.New(client.FromEnv) require.NoError(t, err) return &DockerClient{Client: cli} } @@ -29,15 +28,15 @@ func (c *DockerClient) ImageLoad(t *testing.T, ctx context.Context, imageFile st defer testfile.Close() // Load image into docker engine - res, err := c.Client.ImageLoad(ctx, testfile, true) + res, err := c.Client.ImageLoad(ctx, testfile, client.ImageLoadWithQuiet(true)) require.NoError(t, err) - defer res.Body.Close() + defer res.Close() // Parse the response and extract the loaded image name var data struct { Stream string `json:"stream"` } - err = json.NewDecoder(res.Body).Decode(&data) + err = json.NewDecoder(res).Decode(&data) require.NoError(t, err) loadedImage := strings.TrimPrefix(data.Stream, "Loaded image: ") loadedImage = strings.TrimSpace(loadedImage) @@ -50,7 +49,7 @@ func (c *DockerClient) ImageLoad(t *testing.T, ctx context.Context, imageFile st func (c *DockerClient) ImageRemove(t *testing.T, ctx context.Context, imageID string) { t.Helper() - _, _ = c.Client.ImageRemove(ctx, imageID, image.RemoveOptions{ + _, _ = c.Client.ImageRemove(ctx, imageID, client.ImageRemoveOptions{ Force: true, PruneChildren: true, }) diff --git a/internal/testutil/localstack.go b/internal/testutil/localstack.go index 71eaf5a3fcf9..d21cd8d647f1 100644 --- a/internal/testutil/localstack.go +++ b/internal/testutil/localstack.go @@ -5,7 +5,6 @@ import ( "fmt" "os" - dockercontainer "github.com/docker/docker/api/types/container" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/localstack" ) @@ -19,10 +18,8 @@ func SetupLocalStack(ctx context.Context, version string) (*localstack.LocalStac container, err := localstack.RunContainer(ctx, testcontainers.CustomizeRequest( testcontainers.GenericContainerRequest{ ContainerRequest: testcontainers.ContainerRequest{ - Image: "localstack/localstack:" + version, - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, + Image: "localstack/localstack:" + version, + AutoRemove: true, }, }, )) diff --git a/magefiles/docs.go b/magefiles/docs.go deleted file mode 100644 index f37e0d5d04ea..000000000000 --- a/magefiles/docs.go +++ /dev/null @@ -1,163 +0,0 @@ -//go:build mage_docs - -package main - -import ( - "cmp" - "fmt" - "os" - "slices" - "strings" - - "github.com/spf13/cobra/doc" - - "github.com/aquasecurity/trivy/pkg/commands" - "github.com/aquasecurity/trivy/pkg/flag" - "github.com/aquasecurity/trivy/pkg/log" -) - -const ( - title = "Config file" - description = "Trivy can be customized by tweaking a `trivy.yaml` file.\n" + - "The config path can be overridden by the `--config` flag.\n\n" + - "An example is [here][example].\n\n" + - "These samples contain default values for flags." - footer = "[example]: https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/trivy-conf/trivy.yaml" -) - -// Generate CLI references -func main() { - // Set a dummy path for the documents - flag.CacheDirFlag.Default = "/path/to/cache" - flag.ModuleDirFlag.Default = "$HOME/.trivy/modules" - - // Set a dummy path not to load plugins - os.Setenv("XDG_DATA_HOME", os.TempDir()) - - cmd := commands.NewApp() - cmd.DisableAutoGenTag = true - if err := doc.GenMarkdownTree(cmd, "./docs/docs/references/configuration/cli"); err != nil { - log.Fatal("Fatal error", log.Err(err)) - } - if err := generateConfigDocs("./docs/docs/references/configuration/config-file.md"); err != nil { - log.Fatal("Fatal error in config file generation", log.Err(err)) - } -} - -// generateConfigDocs creates markdown file for Trivy config. -func generateConfigDocs(filename string) error { - // remoteFlags should contain Client and Server flags. - // NewClientFlags doesn't initialize `Listen` field - remoteFlags := flag.NewClientFlags() - remoteFlags.Listen = flag.ServerListenFlag.Clone() - - // These flags don't work from config file. - // Clear configName to skip them later. - globalFlags := flag.NewGlobalFlagGroup() - globalFlags.ConfigFile.ConfigName = "" - globalFlags.ShowVersion.ConfigName = "" - globalFlags.GenerateDefaultConfig.ConfigName = "" - - var allFlagGroups = []flag.FlagGroup{ - globalFlags, - flag.NewCacheFlagGroup(), - flag.NewCleanFlagGroup(), - remoteFlags, - flag.NewDBFlagGroup(), - flag.NewImageFlagGroup(), - flag.NewK8sFlagGroup(), - flag.NewLicenseFlagGroup(), - flag.NewMisconfFlagGroup(), - flag.NewModuleFlagGroup(), - flag.NewPackageFlagGroup(), - flag.NewRegistryFlagGroup(), - flag.NewRegoFlagGroup(), - flag.NewReportFlagGroup(), - flag.NewRepoFlagGroup(), - flag.NewScanFlagGroup(), - flag.NewSecretFlagGroup(), - flag.NewVulnerabilityFlagGroup(), - } - - f, err := os.Create(filename) - if err != nil { - return err - } - defer f.Close() - f.WriteString("# " + title + "\n\n") - f.WriteString(description + "\n") - - for _, group := range allFlagGroups { - f.WriteString("## " + group.Name() + " options\n") - writeFlags(group, f) - } - - f.WriteString(footer) - return nil -} - -func writeFlags(group flag.FlagGroup, w *os.File) { - flags := group.Flags() - // Sort flags to avoid duplicates of non-last parts of config file - slices.SortFunc(flags, func(a, b flag.Flagger) int { - return cmp.Compare(a.GetConfigName(), b.GetConfigName()) - }) - w.WriteString("\n```yaml\n") - - var lastParts []string - for _, flg := range flags { - if flg.GetConfigName() == "" || flg.Hidden() { - continue - } - // We need to split the config name on `.` to make the indentations needed in yaml. - parts := strings.Split(flg.GetConfigName(), ".") - for i := range parts { - // Skip already added part - if len(lastParts) >= i+1 && parts[i] == lastParts[i] { - continue - } - ind := strings.Repeat(" ", i) - // We need to add a comment and example values only for the last part of the config name. - isLastPart := i == len(parts)-1 - if isLastPart { - // Some `Flags` don't support flag for CLI. (e.g.`LicenseForbidden`). - if flg.GetName() != "" { - fmt.Fprintf(w, "%s# Same as '--%s'\n", ind, flg.GetName()) - } - } - w.WriteString(ind + parts[i] + ":") - if isLastPart { - writeFlagValue(flg.GetDefaultValue(), ind, w) - } - w.WriteString("\n") - } - lastParts = parts - } - w.WriteString("```\n") -} - -func writeFlagValue(val any, ind string, w *os.File) { - switch v := val.(type) { - case []string: - if len(v) > 0 { - w.WriteString("\n") - for _, vv := range v { - fmt.Fprintf(w, "%s - %s\n", ind, vv) - } - } else { - w.WriteString(" []\n") - } - case map[string][]string: - w.WriteString("\n") - for k, vv := range v { - fmt.Fprintf(w, "%s %s:\n", ind, k) - for _, vvv := range vv { - fmt.Fprintf(w, " %s - %s\n", ind, vvv) - } - } - case string: - fmt.Fprintf(w, " %q\n", v) - default: - fmt.Fprintf(w, " %v\n", v) - } -} diff --git a/magefiles/fixture.go b/magefiles/fixture.go deleted file mode 100644 index 39e5bd0ddae2..000000000000 --- a/magefiles/fixture.go +++ /dev/null @@ -1,136 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/google/go-containerregistry/pkg/authn" - "github.com/google/go-containerregistry/pkg/authn/github" - "github.com/google/go-containerregistry/pkg/crane" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/magefile/mage/sh" - - "github.com/aquasecurity/trivy/internal/testutil" -) - -const dir = "integration/testdata/fixtures/images/" - -var auth = crane.WithAuthFromKeychain(authn.NewMultiKeychain(authn.DefaultKeychain, github.Keychain)) - -func fixtureContainerImages() error { - var testImages = testutil.ImageName("", "", "") - - if err := os.MkdirAll(dir, 0750); err != nil { - return err - } - tags, err := crane.ListTags(testImages, auth) - if err != nil { - return err - } - // Save all tags for trivy-test-images - for _, tag := range tags { - if err := saveImage("", tag); err != nil { - return err - } - } - - // Save trivy-test-images/containerd image - if err := saveImage("containerd", "latest"); err != nil { - return err - } - return nil -} - -func saveImage(subpath, tag string) error { - fileName := tag + ".tar.gz" - imgName := testutil.ImageName("", tag, "") - if subpath != "" { - fileName = subpath + ".tar.gz" - imgName = testutil.ImageName(subpath, "", "") - } - filePath := filepath.Join(dir, fileName) - if exists(filePath) { - return nil - } - fmt.Printf("Downloading %s...\n", imgName) - - img, err := crane.Pull(imgName, auth) - if err != nil { - return err - } - tarPath := strings.TrimSuffix(filePath, ".gz") - if err = crane.Save(img, imgName, tarPath); err != nil { - return err - } - if err = sh.Run("gzip", tarPath); err != nil { - return err - } - return nil -} - -func fixtureVMImages() error { - var testVMImages = testutil.VMImageName("", "", "") - const ( - titleAnnotation = "org.opencontainers.image.title" - dir = "integration/testdata/fixtures/vm-images/" - ) - if err := os.MkdirAll(dir, 0750); err != nil { - return err - } - tags, err := crane.ListTags(testVMImages, auth) - if err != nil { - return err - } - for _, tag := range tags { - img, err := crane.Pull(fmt.Sprintf("%s:%s", testVMImages, tag), auth) - if err != nil { - return err - } - - manifest, err := img.Manifest() - if err != nil { - return err - } - - layers, err := img.Layers() - if err != nil { - return err - } - - for i, layer := range layers { - fileName, ok := manifest.Layers[i].Annotations[titleAnnotation] - if !ok { - continue - } - filePath := filepath.Join(dir, fileName) - if exists(filePath) { - return nil - } - fmt.Printf("Downloading %s...\n", fileName) - if err = saveLayer(layer, filePath); err != nil { - return err - } - } - } - return nil -} - -func saveLayer(layer v1.Layer, filePath string) error { - f, err := os.Create(filePath) - if err != nil { - return err - } - defer f.Close() - - c, err := layer.Compressed() - if err != nil { - return err - } - if _, err = io.Copy(f, c); err != nil { - return err - } - return nil -} diff --git a/magefiles/helm.go b/magefiles/helm.go deleted file mode 100644 index 443c57eed029..000000000000 --- a/magefiles/helm.go +++ /dev/null @@ -1,117 +0,0 @@ -//go:build mage_helm - -package main - -import ( - "fmt" - "log" - "os" - - "github.com/aquasecurity/go-version/pkg/semver" - - "github.com/magefile/mage/sh" - "golang.org/x/xerrors" - "gopkg.in/yaml.v3" -) - -const chartFile = "./helm/trivy/Chart.yaml" - -func main() { - trivyVersion, err := version() - if err != nil { - log.Fatalf("could not determine Trivy version: %v", err) - } - - newHelmVersion, err := bumpHelmChart(chartFile, trivyVersion) - if err != nil { - log.Fatalf("could not bump Trivy version to %q: %v", trivyVersion, err) - } - - log.Printf("Current helm version will bump up %q with Trivy %q", newHelmVersion, trivyVersion) - - newBranch := fmt.Sprintf("ci/helm-chart/bump-trivy-to-%s", trivyVersion) - title := fmt.Sprintf("ci(helm): bump Trivy version to %s for Trivy Helm Chart %s", trivyVersion, newHelmVersion) - description := fmt.Sprintf("This PR bumps Trivy up to the %s version for the Trivy Helm chart %s.", - trivyVersion, newHelmVersion) - - cmds := [][]string{ - []string{"git", "switch", "-c", newBranch}, - []string{"git", "add", chartFile}, - []string{"git", "commit", "-m", title}, - []string{"git", "push", "origin", newBranch}, - []string{"gh", "pr", "create", "--base", "main", "--head", newBranch, "--title", title, "--body", description, "--repo", "$GITHUB_REPOSITORY"}, - } - - if err := runShCommands(cmds); err != nil { - log.Fatal(err) - } - log.Print("Successfully created PR with a new helm version") -} - -type Chart struct { - Version string `yaml:"version"` - AppVersion string `yaml:"appVersion"` -} - -// bumpHelmChart bumps up helm and trivy versions inside a file (Chart.yaml) -// it returns a new helm version and error -func bumpHelmChart(filename, trivyVersion string) (string, error) { - input, err := os.ReadFile(filename) - if err != nil { - return "", xerrors.Errorf("could not read file %q: %w", filename, err) - } - currentHelmChart := &Chart{} - if err := yaml.Unmarshal(input, currentHelmChart); err != nil { - return "", xerrors.Errorf("could not unmarshal helm chart %q: %w", filename, err) - } - - newHelmVersion, err := buildNewHelmVersion(currentHelmChart.Version, currentHelmChart.AppVersion, trivyVersion) - if err != nil { - return "", xerrors.Errorf("could not build new helm version: %v", err) - } - cmds := [][]string{ - []string{"sed", "-i", "-e", fmt.Sprintf("s/appVersion: %s/appVersion: %s/g", currentHelmChart.AppVersion, trivyVersion), filename}, - []string{"sed", "-i", "-e", fmt.Sprintf("s/version: %s/version: %s/g", currentHelmChart.Version, newHelmVersion), filename}, - } - - if err := runShCommands(cmds); err != nil { - return "", xerrors.Errorf("could not update Helm Chart %q: %w", newHelmVersion, err) - } - return newHelmVersion, nil -} - -func runShCommands(cmds [][]string) error { - for _, cmd := range cmds { - if err := sh.Run(cmd[0], cmd[1:]...); err != nil { - return xerrors.Errorf("failed to run %v: %w", cmd, err) - } - } - return nil -} - -func buildNewHelmVersion(currentHelm, currentTrivy, newTrivy string) (string, error) { - currentHelmVersion, err := semver.Parse(currentHelm) - if err != nil { - return "", xerrors.Errorf("could not parse current helm version: %w", err) - } - - currentTrivyVersion, err := semver.Parse(currentTrivy) - if err != nil { - return "", xerrors.Errorf("could not parse current trivy version: %w", err) - } - - newTrivyVersion, err := semver.Parse(newTrivy) - if err != nil { - return "", xerrors.Errorf("could not parse new trivy version: %w", err) - } - - if newTrivyVersion.Major().Compare(currentTrivyVersion.Major()) > 0 { - return currentHelmVersion.IncMajor().String(), nil - } - - if newTrivyVersion.Minor().Compare(currentTrivyVersion.Minor()) > 0 { - return currentHelmVersion.IncMinor().String(), nil - } - - return currentHelmVersion.IncPatch().String(), nil -} diff --git a/magefiles/helm_test.go b/magefiles/helm_test.go deleted file mode 100644 index f2e3233d2879..000000000000 --- a/magefiles/helm_test.go +++ /dev/null @@ -1,92 +0,0 @@ -//go:build mage_helm - -package main - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewVersion(t *testing.T) { - tests := []struct { - name string - currentHelmVersion string - currentTrivyVersion string - newTrivyVersion string - newHelmVersion string - }{ - { - "created the first patch", - "0.1.0", - "0.55.0", - "0.55.1", - "0.1.1", - }, - { - "created the second patch", - "0.1.1", - "0.55.1", - "0.55.2", - "0.1.2", - }, - { - "created the second patch but helm chart was changed", - "0.1.2", - "0.55.1", - "0.55.2", - "0.1.3", - }, - { - "created a new minor version", - "0.1.1", - "0.55.1", - "0.56.0", - "0.2.0", - }, - { - "created a new major version", - "0.1.1", - "0.55.1", - "1.0.0", - "1.0.0", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - newHelmVersion, err := buildNewHelmVersion(test.currentHelmVersion, test.currentTrivyVersion, test.newTrivyVersion) - assert.NoError(t, err) - assert.Equal(t, test.newHelmVersion, newHelmVersion) - }) - } -} - -func TestBumpHelmChart_Success(t *testing.T) { - tempFile, err := os.CreateTemp(t.TempDir(), "Chart-*.yaml") - assert.NoError(t, err) - - content := ` -apiVersion: v2 -name: trivy -version: 0.8.0 -appVersion: 0.55.0 -description: Trivy helm chart -keywords: - - scanner - - trivy - - vulnerability -` - err = os.WriteFile(tempFile.Name(), []byte(content), 0644) - assert.NoError(t, err) - - newVersion, err := bumpHelmChart(tempFile.Name(), "0.55.1") - assert.NoError(t, err) - assert.Equal(t, "0.8.1", newVersion) - - updatedContent, err := os.ReadFile(tempFile.Name()) - assert.NoError(t, err) - assert.Contains(t, string(updatedContent), "appVersion: 0.55.1") - assert.Contains(t, string(updatedContent), "version: 0.8.1") -} diff --git a/magefiles/magefile.go b/magefiles/magefile.go deleted file mode 100644 index 67f1c28dd45f..000000000000 --- a/magefiles/magefile.go +++ /dev/null @@ -1,584 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io/fs" - "log/slog" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/magefile/mage/mg" - "github.com/magefile/mage/sh" - "github.com/magefile/mage/target" - - // Trivy packages should not be imported in Mage (see https://github.com/aquasecurity/trivy/pull/4242), - // but this package doesn't have so many dependencies, and Mage is still fast. - //mage:import gittest - gittest "github.com/aquasecurity/trivy/internal/gittest/testdata" - //mage:import rpm - rpm "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/rpm/testdata" - "github.com/aquasecurity/trivy/pkg/log" -) - -var ( - GOPATH = os.Getenv("GOPATH") - GOBIN = filepath.Join(GOPATH, "bin") - - ENV = map[string]string{ - "CGO_ENABLED": "0", - } -) - -var protoFiles = []string{ - "pkg/iac/scanners/terraformplan/snapshot/planproto/planfile.proto", -} - -func init() { - slog.SetDefault(log.New(log.NewHandler(os.Stderr, nil))) // stdout is suppressed in mage -} - -func version() (string, error) { - if ver, err := sh.Output("git", "describe", "--tags", "--always"); err != nil { - return "", err - } else { - // Strips the v prefix from the tag - return strings.TrimPrefix(ver, "v"), nil - } -} - -func buildLdflags() (string, error) { - ver, err := version() - if err != nil { - return "", err - } - return fmt.Sprintf("-s -w -X=github.com/aquasecurity/trivy/pkg/version/app.ver=%s", ver), nil -} - -type Tool mg.Namespace - -// Aqua installs aqua if not installed -func (Tool) Aqua() error { - if exists(filepath.Join(GOBIN, "aqua")) { - return nil - } - return sh.Run("go", "install", "github.com/aquaproj/aqua/v2/cmd/aqua@v2.2.1") -} - -// Wire installs wire if not installed -func (Tool) Wire() error { - if installed("wire") { - return nil - } - return sh.Run("go", "install", "github.com/google/wire/cmd/wire@v0.5.0") -} - -// Sass installs saas if not installed. npm is assumed to be available -func (Tool) Sass() error { - if installed("sass") { - return nil - } - return sh.Run("npm", "install", "-g", "saas") -} - -// PipTools installs PipTools if not installed. python is assumed to be available and relevant environment to have been activated -func (Tool) PipTools() error { - if installed("pip-compile") { - return nil - } - return sh.Run("python", "-m", "pip", "install", "pip-tools") -} - -// GolangciLint installs golangci-lint -func (t Tool) GolangciLint() error { - const version = "v1.64.2" - bin := filepath.Join(GOBIN, "golangci-lint") - if exists(bin) && t.matchGolangciLintVersion(bin, version) { - return nil - } - command := fmt.Sprintf("curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b %s %s", GOBIN, version) - return sh.Run("bash", "-c", command) -} - -func (Tool) matchGolangciLintVersion(bin, version string) bool { - out, err := sh.Output(bin, "version", "--format", "json") - if err != nil { - slog.Error("Unable to get golangci-lint version", slog.Any("err", err)) - return false - } - var output struct { - Version string `json:"Version"` - } - if err = json.Unmarshal([]byte(out), &output); err != nil { - slog.Error("Unable to parse golangci-lint version", slog.Any("err", err)) - return false - } - - version = strings.TrimPrefix(version, "v") - if output.Version != version { - slog.Info("golangci-lint version mismatch", slog.String("expected", version), slog.String("actual", output.Version)) - return false - } - return true -} - -// Labeler installs labeler -func (Tool) Labeler() error { - if exists(filepath.Join(GOBIN, "labeler")) { - return nil - } - return sh.Run("go", "install", "github.com/knqyf263/labeler@latest") -} - -// Kind installs kind cluster -func (Tool) Kind() error { - return sh.RunWithV(ENV, "go", "install", "sigs.k8s.io/kind@v0.19.0") -} - -// Goyacc installs goyacc -func (Tool) Goyacc() error { - if exists(filepath.Join(GOBIN, "goyacc")) { - return nil - } - return sh.Run("go", "install", "golang.org/x/tools/cmd/goyacc@v0.7.0") -} - -// Wire generates the wire_gen.go file for each package -func Wire() error { - mg.Deps(Tool{}.Wire) - return sh.RunV("wire", "gen", "./pkg/commands/...", "./pkg/rpc/...", "./pkg/k8s/...") -} - -// Protoc parses PROTO_FILES and generates the Go code for client/server mode -func Protoc() error { - // It is called in the protoc container - if _, ok := os.LookupEnv("TRIVY_PROTOC_CONTAINER"); ok { - rpcProtoFiles, err := findRPCProtoFiles() - if err != nil { - return err - } - for _, file := range rpcProtoFiles { - // Check if the generated Go file is up-to-date - dst := strings.TrimSuffix(file, ".proto") + ".pb.go" - if updated, err := target.Path(dst, file); err != nil { - return err - } else if !updated { - continue - } - - // Generate - if err = sh.RunV("protoc", "--twirp_out", ".", "--twirp_opt", "paths=source_relative", - "--go_out", ".", "--go_opt", "paths=source_relative", file); err != nil { - return err - } - } - - for _, file := range protoFiles { - if err := sh.RunV("protoc", ".", "paths=source_relative", "--go_out", ".", "--go_opt", - "paths=source_relative", file); err != nil { - return err - } - } - return nil - } - - // It is called on the host - if err := sh.RunV("bash", "-c", "docker build -t trivy-protoc - < Dockerfile.protoc"); err != nil { - return err - } - return sh.Run("docker", "run", "--rm", "-it", "--platform", "linux/x86_64", "-v", "${PWD}:/app", "-w", "/app", "trivy-protoc", "mage", "protoc") -} - -// Yacc generates parser -func Yacc() error { - mg.Deps(Tool{}.Goyacc) - return sh.Run("go", "generate", "./pkg/licensing/expression/...") -} - -type Test mg.Namespace - -// FixtureContainerImages downloads and extracts required images -func (Test) FixtureContainerImages() error { - return fixtureContainerImages() -} - -// FixtureVMImages downloads and extracts required VM images -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") - if err := compileWasmModules(pattern); err != nil { - return err - } - return nil -} - -// GenerateExampleModules compiles example Wasm modules for integration tests -func (Test) GenerateExampleModules() error { - pattern := filepath.Join("examples", "module", "*", "*.go") - if err := compileWasmModules(pattern); err != nil { - return err - } - return nil -} - -// UpdateGolden updates golden files for integration tests -func (Test) UpdateGolden() error { - return sh.RunWithV(ENV, "go", "test", "-tags=integration", "./integration/...", "./pkg/fanal/test/integration/...", "-update") -} - -func compileWasmModules(pattern string) error { - goFiles, err := filepath.Glob(pattern) - if err != nil { - return err - } - - for _, src := range goFiles { - // e.g. examples/module/spring4shell/spring4shell.go - // => examples/module/spring4shell/spring4shell.wasm - dst := strings.TrimSuffix(src, ".go") + ".wasm" - if updated, err := target.Path(dst, src); err != nil { - return err - } else if !updated { - continue - } - // Check if TinyGo is installed - if !installed("tinygo") { - return errors.New("need to install TinyGo, follow https://tinygo.org/getting-started/install/") - } - if err = sh.Run("go", "generate", src); err != nil { - return err - } - } - return nil -} - -// Unit runs unit tests -func (t Test) Unit() error { - mg.Deps(t.GenerateModules, rpm.Fixtures, gittest.Fixtures) - return sh.RunWithV(ENV, "go", "test", "-v", "-short", "-coverprofile=coverage.txt", "-covermode=atomic", "./...") -} - -// Integration runs integration tests -func (t Test) Integration() error { - mg.Deps(t.FixtureContainerImages) - return sh.RunWithV(ENV, "go", "test", "-timeout", "15m", "-v", "-tags=integration", "./integration/...", "./pkg/fanal/test/integration/...") -} - -// K8s runs k8s integration tests -func (t Test) K8s() error { - mg.Deps(Tool{}.Kind) - - err := sh.RunWithV(ENV, "kind", "create", "cluster", "--name", "kind-test") - if err != nil { - return err - } - defer func() { - _ = sh.RunWithV(ENV, "kind", "delete", "cluster", "--name", "kind-test") - }() - // wait for the kind cluster is running correctly - err = sh.RunWithV(ENV, "kubectl", "wait", "--for=condition=Ready", "nodes", "--all", "--timeout=300s") - if err != nil { - return fmt.Errorf("can't wait for the kind cluster: %w", err) - } - - err = sh.RunWithV(ENV, "kubectl", "apply", "-f", "./integration/testdata/fixtures/k8s/test_nginx.yaml") - if err != nil { - return fmt.Errorf("can't create a test deployment: %w", err) - } - - // create an environment for limited user test - err = initk8sLimitedUserEnv() - if err != nil { - return fmt.Errorf("can't create environment for limited user: %w", err) - } - - // print all resources for info - err = sh.RunWithV(ENV, "kubectl", "get", "all", "-A") - if err != nil { - return err - } - - return sh.RunWithV(ENV, "go", "test", "-v", "-tags=k8s_integration", "./integration/...") -} - -func initk8sLimitedUserEnv() error { - commands := [][]string{ - {"kubectl", "create", "namespace", "limitedns"}, - {"kubectl", "create", "-f", "./integration/testdata/fixtures/k8s/limited-pod.yaml"}, - {"kubectl", "create", "serviceaccount", "limiteduser"}, - {"kubectl", "create", "-f", "./integration/testdata/fixtures/k8s/limited-role.yaml"}, - {"kubectl", "create", "-f", "./integration/testdata/fixtures/k8s/limited-binding.yaml"}, - {"cp", "./integration/testdata/fixtures/k8s/kube-config-template", "./integration/limitedconfig"}, - } - - for _, cmd := range commands { - if err := sh.RunV(cmd[0], cmd[1:]...); err != nil { - return err - } - } - envs := make(map[string]string) - var err error - envs["CA"], err = sh.Output("kubectl", "config", "view", "-o", "jsonpath=\"{.clusters[?(@.name == 'kind-kind-test')].cluster.certificate-authority-data}\"", "--flatten") - if err != nil { - return err - } - envs["URL"], err = sh.Output("kubectl", "config", "view", "-o", "jsonpath=\"{.clusters[?(@.name == 'kind-kind-test')].cluster.server}\"") - if err != nil { - return err - } - envs["TOKEN"], err = sh.Output("kubectl", "create", "token", "limiteduser", "--duration=8760h") - if err != nil { - return err - } - commandsWith := [][]string{ - {"sed", "-i", "-e", "s|{{CA}}|$CA|g", "./integration/limitedconfig"}, - {"sed", "-i", "-e", "s|{{URL}}|$URL|g", "./integration/limitedconfig"}, - {"sed", "-i", "-e", "s|{{TOKEN}}|$TOKEN|g", "./integration/limitedconfig"}, - } - for _, cmd := range commandsWith { - if err := sh.RunWithV(envs, cmd[0], cmd[1:]...); err != nil { - return err - } - } - return nil -} - -// Module runs Wasm integration tests -func (t Test) Module() error { - mg.Deps(t.FixtureContainerImages, t.GenerateExampleModules) - return sh.RunWithV(ENV, "go", "test", "-v", "-tags=module_integration", "./integration/...") -} - -// UpdateModuleGolden updates golden files for Wasm integration tests -func (t Test) UpdateModuleGolden() error { - mg.Deps(t.FixtureContainerImages, t.GenerateExampleModules) - return sh.RunWithV(ENV, "go", "test", "-v", "-tags=module_integration", "./integration/...", "-update") -} - -// VM runs VM integration tests -func (t Test) VM() error { - mg.Deps(t.FixtureVMImages) - return sh.RunWithV(ENV, "go", "test", "-v", "-tags=vm_integration", "./integration/...") -} - -// UpdateVMGolden updates golden files for integration tests -func (t Test) UpdateVMGolden() error { - mg.Deps(t.FixtureVMImages) - return sh.RunWithV(ENV, "go", "test", "-v", "-tags=vm_integration", "./integration/...", "-update") -} - -type Lint mg.Namespace - -// Run runs linters -func (Lint) Run() error { - mg.Deps(Tool{}.GolangciLint) - return sh.RunV("golangci-lint", "run") -} - -// Fix auto fixes linters -func (Lint) Fix() error { - mg.Deps(Tool{}.GolangciLint) - return sh.RunV("golangci-lint", "run", "--fix") -} - -// Fmt formats Go code and proto files -func Fmt() error { - // Check if clang-format is installed - if !installed("clang-format") { - return errors.New("need to install clang-format") - } - - // Format proto files - rpcProtoFiles, err := findRPCProtoFiles() - if err != nil { - return err - } - - allProtoFiles := append(protoFiles, rpcProtoFiles...) - for _, file := range allProtoFiles { - if err = sh.Run("clang-format", "-i", file); err != nil { - return err - } - } - - // Format Go code - return sh.Run("go", "fmt", "./...") -} - -// Tidy makes sure go.mod matches the source code in the module -func Tidy() error { - return sh.RunV("go", "mod", "tidy") -} - -// Build builds Trivy -func Build() error { - if updated, err := target.Dir("trivy", "pkg", "cmd"); err != nil { - return err - } else if !updated { - return nil - } - - ldflags, err := buildLdflags() - if err != nil { - return err - } - wd, err := os.Getwd() - if err != nil { - return err - } - return sh.RunWith(ENV, "go", "build", "-ldflags", ldflags, filepath.Join(wd, "cmd", "trivy")) -} - -// Install installs Trivy -func Install() error { - ldflags, err := buildLdflags() - if err != nil { - return err - } - wd, err := os.Getwd() - if err != nil { - return err - } - return sh.RunWith(ENV, "go", "install", "-ldflags", ldflags, filepath.Join(wd, "cmd", "trivy")) -} - -// Clean cleans up the fixtures -func Clean() error { - fixtureDir := filepath.Join("integration", "testdata", "fixtures") - paths := []string{ - filepath.Join(fixtureDir, "images"), - filepath.Join(fixtureDir, "vm-images"), - } - for _, p := range paths { - if err := sh.Rm(p); err != nil { - return err - } - } - return nil -} - -// Label updates labels -func Label() error { - mg.Deps(Tool{}.Labeler) - return sh.RunV("labeler", "apply", "misc/triage/labels.yaml", "-l", "5") -} - -type Docs mg.Namespace - -// Prepare CSS -func (Docs) Css() error { - const ( - homepageSass = "docs/assets/css/trivy_v1_homepage.scss" - ) - homepageCss := strings.TrimSuffix(homepageSass, ".scss") + ".min.css" - if updated, err := target.Path(homepageCss, homepageSass); err != nil { - return err - } else if !updated { - return nil - } - return sh.Run("sass", "--no-source-map", "--style=compressed", homepageSass, homepageCss) -} - -// Prepare python requirements -func (Docs) Pip() error { - const ( - requirementsIn = "docs/build/requirements.in" - ) - requirementsTxt := strings.TrimSuffix(requirementsIn, ".in") + ".txt" - if updated, err := target.Path(requirementsTxt, requirementsIn); err != nil { - return err - } else if !updated { - return nil - } - return sh.Run("pip-compile", requirementsIn, "--output-file", requirementsTxt) -} - -// Serve launches MkDocs development server to preview the documentation page -func (Docs) Serve() error { - const ( - mkdocsImage = "trivy-docs:dev" - mkdocsPort = "8000" - ) - if err := sh.Run("docker", "build", "-t", mkdocsImage, "docs/build"); err != nil { - return err - } - return sh.Run("docker", "run", "--name", "mkdocs-serve", "--rm", "-v", "${PWD}:/docs", "-p", mkdocsPort+":8000", mkdocsImage) -} - -// Generate generates CLI references -func (Docs) Generate() error { - return sh.RunWith(ENV, "go", "run", "-tags=mage_docs", "./magefiles") -} - -func findRPCProtoFiles() ([]string, error) { - var files []string - err := filepath.WalkDir("rpc", func(path string, d fs.DirEntry, err error) error { - switch { - case err != nil: - return err - case d.IsDir(): - return nil - case filepath.Ext(path) == ".proto": - files = append(files, path) - } - return nil - }) - if err != nil { - return nil, err - } - return files, nil -} - -func exists(filename string) bool { - _, err := os.Stat(filename) - return err == nil -} - -func installed(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -type Schema mg.Namespace - -// Generate generates Cloud Schema for misconfiguration scanning -func (Schema) Generate() error { - return sh.RunWith(ENV, "go", "run", "-tags=mage_schema", "./magefiles", "--", "generate") -} - -// Verify verifies Cloud Schema for misconfiguration scanning -func (Schema) Verify() error { - return sh.RunWith(ENV, "go", "run", "-tags=mage_schema", "./magefiles", "--", "verify") -} - -// VEX generates a VEX document for Trivy -func VEX(_ context.Context, dir string) error { - return sh.RunWith(ENV, "go", "run", "-tags=mage_vex", "./magefiles/vex.go", "--dir", dir) -} - -type Helm mg.Namespace - -// UpdateVersion updates a version for Trivy Helm Chart and creates a PR -func (Helm) UpdateVersion() error { - return sh.RunWith(ENV, "go", "run", "-tags=mage_helm", "./magefiles") -} - -type SPDX mg.Namespace - -// UpdateLicenseExceptions updates 'exception.json' with SPDX license exceptions -func (SPDX) UpdateLicenseExceptions() error { - return sh.RunWith(ENV, "go", "run", "-tags=mage_spdx", "./magefiles/spdx.go") -} diff --git a/magefiles/schema.go b/magefiles/schema.go deleted file mode 100644 index 6cbf8b950ad0..000000000000 --- a/magefiles/schema.go +++ /dev/null @@ -1,72 +0,0 @@ -//go:build mage_schema - -package main - -import ( - "bytes" - "encoding/json" - "errors" - "log" - "os" - - "github.com/aquasecurity/trivy/pkg/iac/rego/schemas" -) - -const ( - schemaPath = "pkg/iac/rego/schemas/cloud.json" -) - -func main() { - if len(os.Args) < 3 { - log.Fatalf("invalid schema command args: %s", os.Args) - } - - switch os.Args[2] { - case "generate": - if err := GenSchema(); err != nil { - log.Fatalf(err.Error()) - } - log.Println("schema generated") - case "verify": - if err := VerifySchema(); err != nil { - log.Fatalf(err.Error()) - } - log.Println("schema valid") - } -} - -// GenSchema generates the Trivy IaC schema -func GenSchema() error { - schema, err := schemas.Build() - if err != nil { - return err - } - data, err := json.MarshalIndent(schema, "", " ") - if err != nil { - return err - } - if err := os.WriteFile(schemaPath, data, 0600); err != nil { - return err - } - return nil -} - -// VerifySchema verifies a generated schema for validity -func VerifySchema() error { - schema, err := schemas.Build() - if err != nil { - return err - } - data, err := json.MarshalIndent(schema, "", " ") - if err != nil { - return err - } - existing, err := os.ReadFile(schemaPath) - if err != nil { - return err - } - if !bytes.Equal(data, existing) { - return errors.New("schema is out of date:\n\nplease run 'mage schema:generate' and commit the changes\n") - } - return nil -} diff --git a/magefiles/spdx.go b/magefiles/spdx.go deleted file mode 100644 index 258afcd0b1cd..000000000000 --- a/magefiles/spdx.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build mage_spdx - -package main - -import ( - "context" - "encoding/json" - "os" - "path/filepath" - "sort" - - "github.com/samber/lo" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/downloader" - "github.com/aquasecurity/trivy/pkg/log" -) - -const ( - exceptionFileName = "exceptions.json" - exceptionDir = "./pkg/licensing/expression" - exceptionURL = "https://spdx.org/licenses/exceptions.json" -) - -type Exceptions struct { - Exceptions []Exception `json:"exceptions"` -} - -type Exception struct { - ID string `json:"licenseExceptionId"` -} - -func main() { - if err := run(); err != nil { - log.Fatal("Fatal error", log.Err(err)) - } - -} - -// run downloads exceptions.json file, takes only IDs and saves into `expression` package. -func run() error { - tmpDir, err := downloader.DownloadToTempDir(context.Background(), exceptionURL, downloader.Options{}) - if err != nil { - return xerrors.Errorf("unable to download exceptions.json file: %w", err) - } - tmpFile, err := os.ReadFile(filepath.Join(tmpDir, exceptionFileName)) - if err != nil { - return xerrors.Errorf("unable to read exceptions.json file: %w", err) - } - - exceptions := Exceptions{} - if err = json.Unmarshal(tmpFile, &exceptions); err != nil { - return xerrors.Errorf("unable to unmarshal exceptions.json file: %w", err) - } - - exs := lo.Map(exceptions.Exceptions, func(ex Exception, _ int) string { - return ex.ID - }) - sort.Strings(exs) - - exceptionFile := filepath.Join(exceptionDir, exceptionFileName) - f, err := os.Create(exceptionFile) - if err != nil { - return xerrors.Errorf("unable to create file %s: %w", exceptionFile, err) - } - defer f.Close() - - e, err := json.Marshal(exs) - if err != nil { - return xerrors.Errorf("unable to marshal exceptions list: %w", err) - } - - if _, err = f.Write(e); err != nil { - return xerrors.Errorf("unable to write exceptions list: %w", err) - } - - return nil -} diff --git a/magefiles/terraformplan.go b/magefiles/terraformplan.go deleted file mode 100644 index de3d9016b6ed..000000000000 --- a/magefiles/terraformplan.go +++ /dev/null @@ -1,140 +0,0 @@ -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/magefiles/vex.go b/magefiles/vex.go deleted file mode 100644 index f5eb52e2efd7..000000000000 --- a/magefiles/vex.go +++ /dev/null @@ -1,425 +0,0 @@ -//go:build mage_vex - -package main - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/json" - "errors" - "flag" - "fmt" - "io" - "os" - "os/exec" - "path" - "strings" - "time" - - "github.com/openvex/go-vex/pkg/vex" - "github.com/package-url/packageurl-go" - "github.com/samber/lo" - "golang.org/x/vuln/scan" - - "github.com/aquasecurity/go-version/pkg/version" - "github.com/aquasecurity/trivy/pkg/log" -) - -const ( - repoURL = "https://github.com/aquasecurity/trivy" - minVersion = "0.40.0" -) - -var ( - minVer, _ = version.Parse(minVersion) - - // Product ID for Trivy - productID = &packageurl.PackageURL{ - Type: packageurl.TypeGolang, - // According to https://github.com/package-url/purl-spec/issues/63, - // It's probably better to leave namespace empty and put a module name into `name`. - Namespace: "", - Name: "github.com/aquasecurity/trivy", - } -) - -// VulnerabilityFinding is for parsing govulncheck JSON output -type VulnerabilityFinding struct { - Finding Finding `json:"finding"` -} - -type Finding struct { - OSV string `json:"osv"` - FixedVersion string `json:"fixed_version"` - Trace []Trace `json:"trace"` -} - -type Trace struct { - Module string `json:"module"` - Version string `json:"version"` - Package string `json:"package"` -} - -// UniqueKey is used to identify unique vulnerability-subcomponent pairs -type UniqueKey struct { - VulnerabilityID vex.VulnerabilityID - SubcomponentID string -} - -func main() { - if err := run(); err != nil { - log.Fatal("Fatal error", log.Err(err)) - } -} - -// run is the main entry point for the VEX generator -func run() error { - log.InitLogger(false, false) - - // Parse command-line flags - cloneDir := flag.String("dir", "trivy", "Directory to clone the repository") - output := flag.String("output", ".vex/trivy.openvex.json", "Output file") - flag.Parse() - - ctx := context.Background() - - // Clone or pull the Trivy repository - if _, err := cloneOrPullRepo(ctx, *cloneDir); err != nil { - return err - } - - defer func() { - // Ensure we are on the main branch after processing - _, _ = checkoutMain(ctx, *cloneDir) - }() - - // Save the current working directory - wd, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed to get current working directory: %w", err) - } - - // Change to the target directory as govulncheck doesn't support Dir - // cf. https://github.com/golang/go/blob/6d89b38ed86e0bfa0ddaba08dc4071e6bb300eea/src/os/exec/exec.go#L171-L174 - if err = os.Chdir(*cloneDir); err != nil { - return fmt.Errorf("failed to change to directory %s: %w", *cloneDir, err) - } - - // Get the latest tags from the repository - tags, err := getLatestTags(ctx) - if err != nil { - return err - } - log.Info("Latest tags", log.Any("tags", tags)) - - // Maps to store "not_affected" statements across Trivy versions - notAffectedVulns := make(map[UniqueKey][]vex.Statement) - - // Indicate one or more Trivy versions are affected by the vulnerability. - // This means that the version cannot be omitted later. - affectedVulns := make(map[UniqueKey]struct{}) - - // Process each tag - for _, tag := range tags { - notAffected, affected, err := processTag(ctx, tag) - if err != nil { - return err - } - log.Info("Processed tag", log.String("tag", tag), - log.Int("not_affected", len(notAffected)), log.Int("affected", len(affected))) - lo.Assign(affectedVulns, affected) - for k, v := range notAffected { - notAffectedVulns[k] = append(notAffectedVulns[k], v) - } - } - - // Change back to the original directory - if err = os.Chdir(wd); err != nil { - return fmt.Errorf("failed to change back to original directory: %w", err) - } - - // Generate the final VEX document - if err = updateVEX(*output, combineDocs(notAffectedVulns, affectedVulns)); err != nil { - return err - } - - return nil -} - -// cloneOrPullRepo clones the Trivy repository or pulls updates if it already exists -func cloneOrPullRepo(ctx context.Context, dir string) ([]byte, error) { - if _, err := os.Stat(dir); os.IsNotExist(err) { - return runCommandWithTimeout(ctx, 20*time.Minute, "git", "clone", repoURL, dir) - } - - if _, err := checkoutMain(ctx, dir); err != nil { - return nil, fmt.Errorf("failed to checkout main: %w", err) - } - return runCommandWithTimeout(ctx, 2*time.Minute, "git", "-C", dir, "pull", "--tags") -} - -// checkoutMain checks out the main branch of the repository -func checkoutMain(ctx context.Context, dir string) ([]byte, error) { - return runCommandWithTimeout(ctx, 1*time.Minute, "git", "-C", dir, "checkout", "main") -} - -// getLatestTags retrieves and sorts the latest tags from the repository -func getLatestTags(ctx context.Context) ([]string, error) { - output, err := runCommandWithTimeout(ctx, 1*time.Minute, "git", "tag") - if err != nil { - return nil, fmt.Errorf("failed to get tags: %w", err) - } - - tags := strings.Split(strings.TrimSpace(string(output)), "\n") - versions := make([]string, 0, len(tags)) - - for _, tag := range tags { - v, err := version.Parse(tag) - if err != nil { - continue - } - if v.GreaterThanOrEqual(minVer) { - versions = append(versions, tag) - } - } - - return versions, nil -} - -// processTag processes a single tag, running govulncheck and generating VEX statements -func processTag(ctx context.Context, tag string) (map[UniqueKey]vex.Statement, map[UniqueKey]struct{}, error) { - log.Info("Processing tag...", log.String("tag", tag)) - if _, err := runCommandWithTimeout(ctx, 1*time.Minute, "git", "checkout", tag); err != nil { - return nil, nil, fmt.Errorf("failed to checkout tag %s: %w", tag, err) - } - - // Run govulncheck and generate VEX document - vexDoc, err := generateVEX(ctx) - if err != nil { - return nil, nil, fmt.Errorf("failed to run govulncheck: %w", err) - } - - // Run govulncheck and generate JSON result - // Need to generate JSON as well as OpenVEX for the following reasons: - // - Subcomponent - // - OpenVEX from govulncheck doesn't fill in subcomponents. - // - Status - // - govulncheck uses "not_affected" for all vulnerabilities. Need to determine "fixed" vulnerabilities. - // cf. https://github.com/golang/go/issues/68338 - findings, err := generateJSON(ctx) - if err != nil { - return nil, nil, fmt.Errorf("failed to run govulncheck: %w", err) - } - - product := *productID // Clone Trivy PURL - product.Version = tag - notAffected := make(map[UniqueKey]vex.Statement) - affected := make(map[UniqueKey]struct{}) - - // Update VEX document generated by govulncheck - for _, stmt := range vexDoc.Statements { - finding, ok := findings[stmt.Vulnerability.Name] - if !ok { - // Considered as "fixed" vulnerabilities - // cf. https://github.com/golang/go/issues/68338 - continue - } else if len(finding.Finding.Trace) == 0 { - continue - } - - namespace, name := path.Split(finding.Finding.Trace[0].Module) - subcomponent := &packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: namespace, - Name: name, - } - - key := UniqueKey{ - VulnerabilityID: stmt.Vulnerability.Name, - SubcomponentID: subcomponent.String(), - } - - if stmt.Status == vex.StatusAffected { - affected[key] = struct{}{} - continue - } else if stmt.Status != vex.StatusNotAffected { - continue - } - - // Update the statement with product and subcomponent information - stmt.Products = []vex.Product{ - { - // Fill in components manually - // cf. https://github.com/golang/go/issues/68152 - Component: vex.Component{ - ID: product.String(), - Identifiers: map[vex.IdentifierType]string{ - vex.PURL: product.String(), - }, - }, - Subcomponents: []vex.Subcomponent{ - { - Component: vex.Component{ - ID: key.SubcomponentID, - Identifiers: map[vex.IdentifierType]string{ - vex.PURL: key.SubcomponentID, - }, - }, - }, - }, - }, - } - notAffected[key] = stmt - } - - return notAffected, affected, nil -} - -// generateVEX runs govulncheck with OpenVEX format and parses the output -func generateVEX(ctx context.Context) (*vex.VEX, error) { - buf, err := runGovulncheck(ctx, "openvex") - if err != nil { - return nil, fmt.Errorf("failed to run govulncheck: %w", err) - } - - vexDoc, err := vex.Parse(buf.Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to parse govulncheck output: %w", err) - } - return vexDoc, nil -} - -// generateJSON runs govulncheck with JSON format and parses the output -func generateJSON(ctx context.Context) (map[vex.VulnerabilityID]VulnerabilityFinding, error) { - buf, err := runGovulncheck(ctx, "json") - if err != nil { - return nil, fmt.Errorf("failed to run govulncheck: %w", err) - } - - decoder := json.NewDecoder(buf) - findings := make(map[vex.VulnerabilityID]VulnerabilityFinding) - for { - var finding VulnerabilityFinding - if err := decoder.Decode(&finding); err == io.EOF { - break - } else if err != nil { - return nil, fmt.Errorf("failed to decode govulncheck output: %w", err) - } - findings[vex.VulnerabilityID(finding.Finding.OSV)] = finding - } - return findings, nil -} - -// runGovulncheck executes the govulncheck command with the specified format -func runGovulncheck(ctx context.Context, format string) (*bytes.Buffer, error) { - var buf bytes.Buffer - cmd := scan.Command(ctx, "-format", format, "./...") - cmd.Stdout = &buf - - log.Info("Running govulncheck", log.String("format", format)) - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("failed to start govulncheck: %w", err) - } - - if err := cmd.Wait(); err != nil { - return nil, fmt.Errorf("failed to run govulncheck: %w", err) - } - return &buf, nil -} - -// combineDocs merges the VEX statements from all processed tags -func combineDocs(notAffected map[UniqueKey][]vex.Statement, affected map[UniqueKey]struct{}) []vex.Statement { - log.Info("Combining VEX documents") - statements := make(map[UniqueKey]vex.Statement) - for key, stmts := range notAffected { - for _, stmt := range stmts { - if _, ok := affected[key]; !ok { - // All versions are "not_affected" or "fixed" by the vulnerability, omitting a version in PURL - // => pkg:golang/github.com/aquasecurity/trivy - stmt.Products[0].ID = productID.String() - stmt.Products[0].Identifiers[vex.PURL] = productID.String() - statements[key] = stmt - break - } - - // At least one version is "affected" by the vulnerability, so we need to include the version in PURL. - // => pkg:golang/github.com/aquasecurity/trivy@0.52.0 - if s, ok := statements[key]; ok { - s.Products = append(s.Products, stmt.Products...) - statements[key] = s - } else { - statements[key] = stmt - } - } - } - return lo.Values(statements) -} - -// runCommandWithTimeout executes a command with a specified timeout -func runCommandWithTimeout(ctx context.Context, timeout time.Duration, name string, args ...string) ([]byte, error) { - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - cmd := exec.CommandContext(ctx, name, args...) - log.Info("Executing command", log.String("cmd", cmd.String())) - - output, err := cmd.CombinedOutput() - if err != nil { - return output, fmt.Errorf("%w, output: %s", err, string(output)) - } - - if errors.Is(ctx.Err(), context.DeadlineExceeded) { - return nil, fmt.Errorf("command timed out after %v", timeout) - } - - return output, nil -} - -// updateVEX updates the final VEX document with the combined statements -func updateVEX(output string, statements []vex.Statement) error { - doc, err := vex.Load(output) - if errors.Is(err, os.ErrNotExist) { - doc = &vex.VEX{} - } else if err != nil { - return err - } - - vex.SortStatements(statements, time.Now()) - d := &vex.VEX{ - Metadata: vex.Metadata{ - Context: "https://openvex.dev/ns/v0.2.0", - Author: "Aqua Security", - Timestamp: lo.ToPtr(time.Now()), - Version: doc.Version + 1, - Tooling: "https://github.com/aquasecurity/trivy/tree/main/magefiles/vex.go", - }, - Statements: statements, - } - h, err := hashVEX(d) - if err != nil { - return err - } - d.ID = "aquasecurity/trivy:" + h - - f, err := os.Create(output) - if err != nil { - return err - } - defer f.Close() - - e := json.NewEncoder(f) - e.SetIndent("", " ") - if err = e.Encode(d); err != nil { - return err - } - return err -} - -func hashVEX(d *vex.VEX) (string, error) { - out, err := json.Marshal(d) - if err != nil { - return "", err - } - return fmt.Sprintf("%x", sha256.Sum256(out)), nil -} diff --git a/mkdocs.yml b/mkdocs.yml index 6b02dacba591..fdb477e02458 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -75,6 +75,7 @@ nav: - Alpine Linux: docs/coverage/os/alpine.md - Amazon Linux: docs/coverage/os/amazon.md - Azure Linux (CBL-Mariner): docs/coverage/os/azure.md + - Bottlerocket: docs/coverage/os/bottlerocket.md - CentOS: docs/coverage/os/centos.md - Chainguard: docs/coverage/os/chainguard.md - Debian: docs/coverage/os/debian.md diff --git a/pkg/cache/key.go b/pkg/cache/key.go index 0ad0fde82fe1..6c0df9119434 100644 --- a/pkg/cache/key.go +++ b/pkg/cache/key.go @@ -30,6 +30,7 @@ func CalcKey(id string, analyzerVersions analyzer.Versions, hookVersions map[str HookVersions map[string]int SkipFiles []string SkipDirs []string + OnlyDirs []string FilePatterns []string `json:",omitempty"` DetectionPriority types.DetectionPriority `json:",omitempty"` }{ @@ -38,6 +39,7 @@ func CalcKey(id string, analyzerVersions analyzer.Versions, hookVersions map[str hookVersions, artifactOpt.WalkerOption.SkipFiles, artifactOpt.WalkerOption.SkipDirs, + artifactOpt.WalkerOption.OnlyDirs, artifactOpt.FilePatterns, artifactOpt.DetectionPriority, } diff --git a/pkg/cloud/aws/config/config.go b/pkg/cloud/aws/config/config.go deleted file mode 100644 index e173840cdaec..000000000000 --- a/pkg/cloud/aws/config/config.go +++ /dev/null @@ -1,47 +0,0 @@ -package config - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - awsconfig "github.com/aws/aws-sdk-go-v2/config" - "golang.org/x/xerrors" -) - -func EndpointResolver(endpoint string) aws.EndpointResolverWithOptionsFunc { - return func(_, reg string, options ...any) (aws.Endpoint, error) { - return aws.Endpoint{ - PartitionID: "aws", - URL: endpoint, - SigningRegion: reg, - Source: aws.EndpointSourceCustom, - }, nil - } -} - -func MakeAWSOptions(region, endpoint string) []func(*awsconfig.LoadOptions) error { - var options []func(*awsconfig.LoadOptions) error - - if region != "" { - options = append(options, awsconfig.WithRegion(region)) - } - - if endpoint != "" { - options = append(options, awsconfig.WithEndpointResolverWithOptions(EndpointResolver(endpoint))) - } - - return options -} - -func LoadDefaultAWSConfig(ctx context.Context, region, endpoint string) (aws.Config, error) { - cfg, err := awsconfig.LoadDefaultConfig(ctx, MakeAWSOptions(region, endpoint)...) - if err != nil { - return aws.Config{}, xerrors.Errorf("aws config load error: %w", err) - } - - if cfg.Region == "" { - return aws.Config{}, xerrors.New("aws region is required") - } - - return cfg, nil -} diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 86e3428e5925..0da3c8b3970e 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -673,6 +673,7 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { SkipDirs: flag.SkipDirsFlag.Clone(), SkipFiles: flag.SkipFilesFlag.Clone(), FilePatterns: flag.FilePatternsFlag.Clone(), + OnlyDirs: flag.OnlyDirsFlag.Clone(), } configFlags := &flag.Flags{ diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 7acdcc83ace5..49f3f4ad4fef 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -465,11 +465,6 @@ func disabledAnalyzers(opts flag.Options) []analyzer.Type { analyzers = append(analyzers, analyzer.TypeHistoryDockerfile) } - // Skip executable file analysis if Rekor isn't a specified SBOM source. - if !slices.Contains(opts.SBOMSources, types.SBOMSourceRekor) { - analyzers = append(analyzers, analyzer.TypeExecutable) - } - // Disable RPM archive analyzer unless the environment variable is set // TODO: add '--enable-analyzers' and delete this environment variable if os.Getenv("TRIVY_EXPERIMENTAL_RPM_ARCHIVE") == "" { @@ -479,6 +474,19 @@ func disabledAnalyzers(opts flag.Options) []analyzer.Type { return analyzers } +func disabledHandlers(opts flag.Options) []ftypes.HandlerType { + var handlers []ftypes.HandlerType + // Disable the post handler for filtering system file when detection priority is comprehensive. + if opts.DetectionPriority == ftypes.PriorityComprehensive { + handlers = append(handlers, ftypes.SystemFileFilteringPostHandler) + } + // Skip unpackaged executable file analysis with Rekor if Rekor isn't a specificed SBOM source. + if !slices.Contains(opts.SBOMSources, types.SBOMSourceRekor) { + handlers = append(handlers, ftypes.UnpackagedPostHandler) + } + return handlers +} + func disabledMisconfigAnalyzers(included []analyzer.Type) []analyzer.Type { _, missing := lo.Difference(analyzer.TypeConfigFiles, included) if len(missing) > 0 { @@ -550,10 +558,6 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan fileChecksum = true } - // Disable the post handler for filtering system file when detection priority is comprehensive. - disabledHandlers := lo.Ternary(opts.DetectionPriority == ftypes.PriorityComprehensive, - []ftypes.HandlerType{ftypes.SystemFileFilteringPostHandler}, nil) - return ScannerConfig{ Target: target, CacheOptions: opts.CacheOpts(), @@ -561,7 +565,7 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan ServerOption: opts.ClientScannerOpts(), ArtifactOption: artifact.Option{ DisabledAnalyzers: disabledAnalyzers(opts), - DisabledHandlers: disabledHandlers, + DisabledHandlers: disabledHandlers(opts), FilePatterns: opts.FilePatterns, Parallel: opts.Parallel, Offline: opts.OfflineScan, @@ -608,6 +612,7 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan WalkerOption: walker.Option{ SkipFiles: opts.SkipFiles, SkipDirs: opts.SkipDirs, + OnlyDirs: opts.OnlyDirs, }, }, }, scanOptions, nil diff --git a/pkg/compliance/report/json.go b/pkg/compliance/report/json.go deleted file mode 100644 index 6dd33fc4a8a3..000000000000 --- a/pkg/compliance/report/json.go +++ /dev/null @@ -1,41 +0,0 @@ -package report - -import ( - "encoding/json" - "fmt" - "io" - - "golang.org/x/xerrors" -) - -type JSONWriter struct { - Output io.Writer - Report string -} - -// Write writes the results in JSON format -func (jw JSONWriter) Write(report *ComplianceReport) error { - var output []byte - var err error - - var v any - switch jw.Report { - case allReport: - v = report - case summaryReport: - v = BuildSummary(report) - default: - return xerrors.Errorf(`report %q not supported. Use "summary" or "all"`, jw.Report) - } - - output, err = json.MarshalIndent(v, "", " ") - if err != nil { - return xerrors.Errorf("failed to marshal json: %w", err) - } - - if _, err = fmt.Fprintln(jw.Output, string(output)); err != nil { - return xerrors.Errorf("failed to write json: %w", err) - } - - return nil -} diff --git a/pkg/compliance/report/json_test.go b/pkg/compliance/report/json_test.go deleted file mode 100644 index 31680b4ff513..000000000000 --- a/pkg/compliance/report/json_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package report_test - -import ( - "bytes" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/compliance/report" - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestJSONWriter_Write(t *testing.T) { - input := &report.ComplianceReport{ - ID: "1234", - Title: "NSA", - RelatedResources: []string{"https://example.com"}, - Results: []*report.ControlCheckResult{ - { - ID: "1.0", - Name: "Non-root containers", - Severity: "MEDIUM", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV012", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Severity: "LOW", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV013", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - }, - } - - tests := []struct { - name string - reportType string - input *report.ComplianceReport - want string - }{ - { - name: "build summary json output report", - reportType: "summary", - input: input, - want: filepath.Join("testdata", "summary.json"), - }, - { - name: "build full json output report", - reportType: "all", - input: input, - want: filepath.Join("testdata", "all.json"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - buf := new(bytes.Buffer) - tr := report.JSONWriter{ - Report: tt.reportType, - Output: buf, - } - err := tr.Write(tt.input) - require.NoError(t, err) - - want, err := os.ReadFile(tt.want) - require.NoError(t, err) - - assert.JSONEq(t, string(want), buf.String()) - }) - } -} diff --git a/pkg/compliance/report/report.go b/pkg/compliance/report/report.go deleted file mode 100644 index d4b31668ed6d..000000000000 --- a/pkg/compliance/report/report.go +++ /dev/null @@ -1,137 +0,0 @@ -package report - -import ( - "context" - "io" - - "golang.org/x/xerrors" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy/pkg/compliance/spec" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/types" -) - -const ( - allReport = "all" - summaryReport = "summary" -) - -type Option struct { - Format types.Format - Report string - Output io.Writer - Severities []dbTypes.Severity - ColumnHeading []string -} - -// ComplianceReport represents a kubernetes scan report -type ComplianceReport struct { - ID string - Title string - Description string - Version string - RelatedResources []string - Results []*ControlCheckResult -} - -type ControlCheckResult struct { - ID string - Name string - Description string - DefaultStatus iacTypes.ControlStatus `json:",omitempty"` - Severity string - Results types.Results -} - -// SummaryReport represents a kubernetes scan report with consolidated findings -type SummaryReport struct { - SchemaVersion int `json:",omitempty"` - ID string - Title string - SummaryControls []ControlCheckSummary `json:",omitempty"` -} - -type ControlCheckSummary struct { - ID string - Name string - Severity string - TotalFail *int `json:",omitempty"` -} - -// Writer defines the result write operation -type Writer interface { - Write(ComplianceReport) error -} - -// Write writes the results in the given format -func Write(ctx context.Context, report *ComplianceReport, option Option) error { - switch option.Format { - case types.FormatJSON: - jwriter := JSONWriter{ - Output: option.Output, - Report: option.Report, - } - return jwriter.Write(report) - case types.FormatTable: - if !report.empty() { - complianceWriter := &TableWriter{ - Output: option.Output, - Report: option.Report, - Severities: option.Severities, - } - err := complianceWriter.Write(ctx, report) - if err != nil { - return err - } - } - return nil - default: - return xerrors.Errorf(`unknown format %q. Use "json" or "table"`, option.Format) - } -} - -func (r ComplianceReport) empty() bool { - return len(r.Results) == 0 -} - -// buildControlCheckResults create compliance results data -func buildControlCheckResults(checksMap map[string]types.Results, controls []iacTypes.Control) []*ControlCheckResult { - var complianceResults []*ControlCheckResult - for _, control := range controls { - var results types.Results - for _, c := range control.Checks { - results = append(results, checksMap[c.ID]...) - } - complianceResults = append(complianceResults, &ControlCheckResult{ - Name: control.Name, - ID: control.ID, - Description: control.Description, - Severity: string(control.Severity), - DefaultStatus: control.DefaultStatus, - Results: results, - }) - } - return complianceResults -} - -// buildComplianceReportResults create compliance results data -func buildComplianceReportResults(checksMap map[string]types.Results, s iacTypes.Spec) *ComplianceReport { - controlCheckResult := buildControlCheckResults(checksMap, s.Controls) - return &ComplianceReport{ - ID: s.ID, - Title: s.Title, - Description: s.Description, - Version: s.Version, - RelatedResources: s.RelatedResources, - Results: controlCheckResult, - } -} - -func BuildComplianceReport(scanResults []types.Results, cs spec.ComplianceSpec) (*ComplianceReport, error) { - // aggregate checks by ID - aggregateChecksByID := spec.AggregateAllChecksBySpecID(scanResults, cs) - - // build compliance report results - return buildComplianceReportResults(aggregateChecksByID, cs.Spec), nil -} diff --git a/pkg/compliance/report/report_stub.go b/pkg/compliance/report/report_stub.go new file mode 100644 index 000000000000..26a54de6143a --- /dev/null +++ b/pkg/compliance/report/report_stub.go @@ -0,0 +1,38 @@ +package report + +import ( + "context" + "io" + + "golang.org/x/xerrors" + + dbTypes "github.com/aquasecurity/trivy-db/pkg/types" + "github.com/aquasecurity/trivy/pkg/compliance/spec" + "github.com/aquasecurity/trivy/pkg/types" +) + +type Option struct { + Format types.Format + Report string + Output io.Writer + Severities []dbTypes.Severity + ColumnHeading []string +} + +// ComplianceReport represents a kubernetes scan report +type ComplianceReport struct { +} + +// Writer defines the result write operation +type Writer interface { + Write(ComplianceReport) error +} + +// Write writes the results in the given format +func Write(ctx context.Context, report *ComplianceReport, option Option) error { + return xerrors.New("pkg/compliance not implemented") +} + +func BuildComplianceReport(scanResults []types.Results, cs spec.ComplianceSpec) (*ComplianceReport, error) { + return nil, xerrors.New("pkg/compliance not implemented") +} diff --git a/pkg/compliance/report/report_test.go b/pkg/compliance/report/report_test.go deleted file mode 100644 index 5481299b57ea..000000000000 --- a/pkg/compliance/report/report_test.go +++ /dev/null @@ -1,241 +0,0 @@ -package report_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" - "github.com/aquasecurity/trivy/pkg/compliance/report" - "github.com/aquasecurity/trivy/pkg/compliance/spec" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestBuildComplianceReport(t *testing.T) { - type args struct { - scanResults []types.Results - cs spec.ComplianceSpec - } - tests := []struct { - name string - args args - want *report.ComplianceReport - wantErr assert.ErrorAssertionFunc - }{ - { - name: "happy", - args: args{ - scanResults: []types.Results{ - { - { - Target: "Deployment/metrics-server", - Class: types.ClassConfig, - Type: ftypes.Kubernetes, - MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 0, - }, - Misconfigurations: []types.DetectedMisconfiguration{ - { - Type: "Kubernetes Security Check", - ID: "KSV001", - AVDID: "AVD-KSV-0001", - Title: "Process can elevate its own privileges", - Description: "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.", - Message: "Container 'metrics-server' of Deployment 'metrics-server' should set 'securityContext.allowPrivilegeEscalation' to false", - Namespace: "builtin.kubernetes.KSV001", - Query: "data.builtin.kubernetes.KSV001.deny", - Resolution: "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", - Severity: dbTypes.SeverityMedium.String(), - PrimaryURL: "https://avd.aquasec.com/misconfig/ksv001", - References: []string{ - "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", - "https://avd.aquasec.com/misconfig/ksv001", - }, - Status: types.MisconfStatusPassed, - }, - { - Type: "Kubernetes Security Check", - ID: "KSV002", - AVDID: "AVD-KSV-9999", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - { - { - Target: "rancher/metrics-server:v0.3.6 (debian 9.9)", - Class: types.ClassOSPkg, - Type: "debian", - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "DLA-2424-1", - VendorIDs: []string{"DLA-2424-1"}, - PkgName: "tzdata", - InstalledVersion: "2019a-0+deb9u1", - FixedVersion: "2020d-0+deb9u1", - Layer: ftypes.Layer{ - DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", - }, - DataSource: &dbTypes.DataSource{ - ID: vulnerability.Debian, - Name: "Debian Security Tracker", - URL: "https://salsa.debian.org/security-tracker-team/security-tracker", - }, - Vulnerability: dbTypes.Vulnerability{ - Title: "tzdata - new upstream version", - Severity: dbTypes.SeverityUnknown.String(), - }, - }, - }, - }, - }, - }, - cs: spec.ComplianceSpec{ - Spec: iacTypes.Spec{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - Version: "1.0", - RelatedResources: []string{ - "https://example.com", - }, - Controls: []iacTypes.Control{ - { - ID: "1.0", - Name: "Non-root containers", - Description: "Check that container is not running as root", - Severity: "MEDIUM", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-KSV-0001"}, - }, - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Description: "Check that container root file system is immutable", - Severity: "LOW", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-KSV-0002"}, - }, - }, - { - ID: "1.2", - Name: "tzdata - new upstream version", - Description: "Bad tzdata package", - Severity: "CRITICAL", - Checks: []iacTypes.SpecCheck{ - {ID: "DLA-2424-1"}, - }, - }, - }, - }, - }, - }, - want: &report.ComplianceReport{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - Version: "1.0", - RelatedResources: []string{ - "https://example.com", - }, - Results: []*report.ControlCheckResult{ - { - ID: "1.0", - Name: "Non-root containers", - Description: "Check that container is not running as root", - Severity: "MEDIUM", - Results: types.Results{ - { - Target: "Deployment/metrics-server", - Class: types.ClassConfig, - Type: ftypes.Kubernetes, - MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 0, - }, - Misconfigurations: []types.DetectedMisconfiguration{ - { - Type: "Kubernetes Security Check", - ID: "KSV001", - AVDID: "AVD-KSV-0001", - Title: "Process can elevate its own privileges", - Description: "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.", - Message: "Container 'metrics-server' of Deployment 'metrics-server' should set 'securityContext.allowPrivilegeEscalation' to false", - Namespace: "builtin.kubernetes.KSV001", - Query: "data.builtin.kubernetes.KSV001.deny", - Resolution: "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", - Severity: dbTypes.SeverityMedium.String(), - PrimaryURL: "https://avd.aquasec.com/misconfig/ksv001", - References: []string{ - "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", - "https://avd.aquasec.com/misconfig/ksv001", - }, - Status: types.MisconfStatusPassed, - }, - }, - }, - }, - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Description: "Check that container root file system is immutable", - Severity: "LOW", - Results: nil, - }, - { - ID: "1.2", - Name: "tzdata - new upstream version", - Description: "Bad tzdata package", - Severity: "CRITICAL", - Results: types.Results{ - { - Target: "rancher/metrics-server:v0.3.6 (debian 9.9)", - Class: types.ClassOSPkg, - Type: "debian", - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "DLA-2424-1", - VendorIDs: []string{"DLA-2424-1"}, - PkgName: "tzdata", - InstalledVersion: "2019a-0+deb9u1", - FixedVersion: "2020d-0+deb9u1", - Layer: ftypes.Layer{ - DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", - }, - DataSource: &dbTypes.DataSource{ - ID: vulnerability.Debian, - Name: "Debian Security Tracker", - URL: "https://salsa.debian.org/security-tracker-team/security-tracker", - }, - Vulnerability: dbTypes.Vulnerability{ - Title: "tzdata - new upstream version", - Severity: dbTypes.SeverityUnknown.String(), - }, - }, - }, - }, - }, - }, - }, - }, - wantErr: assert.NoError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := report.BuildComplianceReport(tt.args.scanResults, tt.args.cs) - if !tt.wantErr(t, err, fmt.Sprintf("BuildComplianceReport(%v, %v)", tt.args.scanResults, tt.args.cs)) { - return - } - assert.Equalf(t, tt.want, got, "BuildComplianceReport(%v, %v)", tt.args.scanResults, tt.args.cs) - }) - } -} diff --git a/pkg/compliance/report/summary.go b/pkg/compliance/report/summary.go deleted file mode 100644 index 0262249e7214..000000000000 --- a/pkg/compliance/report/summary.go +++ /dev/null @@ -1,111 +0,0 @@ -package report - -import ( - "fmt" - "io" - "strconv" - "strings" - - "github.com/samber/lo" - "golang.org/x/xerrors" - - "github.com/aquasecurity/table" -) - -func BuildSummary(cr *ComplianceReport) *SummaryReport { - var ccma []ControlCheckSummary - for _, control := range cr.Results { - ccm := ControlCheckSummary{ - ID: control.ID, - Name: control.Name, - Severity: control.Severity, - } - if !strings.Contains(control.Name, "Manual") { - ccm.TotalFail = lo.ToPtr(len(control.Results)) - } - ccma = append(ccma, ccm) - } - return &SummaryReport{ - ID: cr.ID, - Title: cr.Title, - SummaryControls: ccma, - } -} - -type SummaryWriter struct { - Output io.Writer -} - -func NewSummaryWriter(output io.Writer) SummaryWriter { - return SummaryWriter{ - Output: output, - } -} - -// Write writes the results in a summarized table format -func (s SummaryWriter) Write(report *ComplianceReport) error { - if _, err := fmt.Fprintln(s.Output); err != nil { - return xerrors.Errorf("failed to write summary report: %w", err) - } - - if _, err := fmt.Fprintf(s.Output, "Summary Report for compliance: %s\n", report.Title); err != nil { - return xerrors.Errorf("failed to write summary report: %w", err) - } - sr := BuildSummary(report) - t := table.New(s.Output) - t.SetRowLines(false) - configureHeader(t, s.columns()) - - for _, summaryControl := range sr.SummaryControls { - rowParts := s.generateSummary(summaryControl) - t.AddRow(rowParts...) - } - - t.Render() - - return nil -} - -func (s SummaryWriter) columns() []string { - return []string{ - ControlIDColumn, - SeverityColumn, - ControlNameColumn, - StatusColumn, - IssuesColumn, - } -} - -func (s SummaryWriter) generateSummary(summaryControls ControlCheckSummary) []string { - // "-" means manual checks - numOfIssues := "-" - status := "-" - if summaryControls.TotalFail != nil { - if *summaryControls.TotalFail == 0 { - status = "PASS" - } else { - status = "FAIL" - } - numOfIssues = strconv.Itoa(*summaryControls.TotalFail) - } - return []string{ - summaryControls.ID, - summaryControls.Severity, - summaryControls.Name, - status, - numOfIssues, - } -} - -func configureHeader(t *table.Table, columnHeading []string) { - headerAlignment := []table.Alignment{ - table.AlignLeft, - table.AlignCenter, - table.AlignLeft, - table.AlignCenter, - table.AlignCenter, - } - t.SetHeaders(columnHeading...) - t.SetAlignment(headerAlignment...) - t.SetAutoMergeHeaders(true) -} diff --git a/pkg/compliance/report/summary_test.go b/pkg/compliance/report/summary_test.go deleted file mode 100644 index 1dbf862d59ba..000000000000 --- a/pkg/compliance/report/summary_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package report_test - -import ( - "testing" - - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/compliance/report" - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestBuildSummary(t *testing.T) { - tests := []struct { - name string - reportType string - input *report.ComplianceReport - want *report.SummaryReport - }{ - { - name: "build report summary config only", - reportType: "summary", - input: &report.ComplianceReport{ - ID: "1234", - Title: "NSA", - RelatedResources: []string{"https://example.com"}, - Results: []*report.ControlCheckResult{ - { - ID: "1.0", - Name: "Non-root containers", - Severity: "MEDIUM", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV012", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Severity: "LOW", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV013", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - }, - }, - want: &report.SummaryReport{ - SchemaVersion: 0, - ID: "1234", - Title: "NSA", - SummaryControls: []report.ControlCheckSummary{ - { - ID: "1.0", - Name: "Non-root containers", - Severity: "MEDIUM", - TotalFail: lo.ToPtr(1), - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Severity: "LOW", - TotalFail: lo.ToPtr(1), - }, - }, - }, - }, - { - name: "build full json output report", - reportType: "all", - input: &report.ComplianceReport{ - ID: "1234", - Title: "NSA", - RelatedResources: []string{"https://example.com"}, - Results: []*report.ControlCheckResult{ - { - ID: "1.0", - Name: "Non-root containers", - Severity: "MEDIUM", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV012", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Severity: "LOW", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV013", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - { - ID: "1.2", - Name: "tzdata - new upstream version", - Severity: "LOW", - Results: types.Results{ - { - Vulnerabilities: []types.DetectedVulnerability{ - {VulnerabilityID: "CVE-9999-0001"}, - {VulnerabilityID: "CVE-9999-0002"}, - }, - }, - }, - }, - }, - }, - want: &report.SummaryReport{ - SchemaVersion: 0, - ID: "1234", - Title: "NSA", - SummaryControls: []report.ControlCheckSummary{ - { - ID: "1.0", - Name: "Non-root containers", - Severity: "MEDIUM", - TotalFail: lo.ToPtr(1), - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Severity: "LOW", - TotalFail: lo.ToPtr(1), - }, - { - ID: "1.2", - Name: "tzdata - new upstream version", - Severity: "LOW", - TotalFail: lo.ToPtr(1), - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := report.BuildSummary(tt.input) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/pkg/compliance/report/table.go b/pkg/compliance/report/table.go deleted file mode 100644 index b3ceed569980..000000000000 --- a/pkg/compliance/report/table.go +++ /dev/null @@ -1,51 +0,0 @@ -package report - -import ( - "context" - "io" - - "golang.org/x/xerrors" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - pkgReport "github.com/aquasecurity/trivy/pkg/report/table" - "github.com/aquasecurity/trivy/pkg/types" -) - -type TableWriter struct { - Report string - Output io.Writer - Severities []dbTypes.Severity - ColumnHeading []string -} - -const ( - ControlIDColumn = "ID" - SeverityColumn = "Severity" - ControlNameColumn = "Control Name" - StatusColumn = "Status" - IssuesColumn = "Issues" -) - -func (tw TableWriter) Write(ctx context.Context, report *ComplianceReport) error { - switch tw.Report { - case allReport: - t := pkgReport.NewWriter(pkgReport.Options{ - Output: tw.Output, - Severities: tw.Severities, - }) - for _, cr := range report.Results { - r := types.Report{Results: cr.Results} - err := t.Write(ctx, r) - if err != nil { - return err - } - } - case summaryReport: - writer := NewSummaryWriter(tw.Output) - return writer.Write(report) - default: - return xerrors.Errorf(`report %q not supported. Use "summary" or "all"`, tw.Report) - } - - return nil -} diff --git a/pkg/compliance/report/table_test.go b/pkg/compliance/report/table_test.go deleted file mode 100644 index 839909a94359..000000000000 --- a/pkg/compliance/report/table_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package report_test - -import ( - "bytes" - "context" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/compliance/report" - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestTableWriter_Write(t *testing.T) { - - tests := []struct { - name string - reportType string - input *report.ComplianceReport - want string - }{ - { - name: "build summary table", - reportType: "summary", - input: &report.ComplianceReport{ - ID: "1234", - Title: "NSA", - RelatedResources: []string{"https://example.com"}, - Results: []*report.ControlCheckResult{ - { - ID: "1.0", - Name: "Non-root containers", - Severity: "MEDIUM", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV012", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - { - ID: "1.1", - Name: "Immutable container file systems", - Severity: "LOW", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV013", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - }, - }, - want: filepath.Join("testdata", "table_summary.txt"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - buf := new(bytes.Buffer) - tr := report.TableWriter{ - Report: tt.reportType, - Output: buf, - } - err := tr.Write(context.Background(), tt.input) - require.NoError(t, err) - - want, err := os.ReadFile(tt.want) - want = bytes.ReplaceAll(want, []byte("\r"), []byte("")) - - require.NoError(t, err) - - assert.Equal(t, string(want), buf.String()) - }) - } -} diff --git a/pkg/compliance/report/testdata/all.json b/pkg/compliance/report/testdata/all.json deleted file mode 100644 index 8c39673c8a96..000000000000 --- a/pkg/compliance/report/testdata/all.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "ID": "1234", - "Title": "NSA", - "Description": "", - "Version": "", - "RelatedResources": [ - "https://example.com" - ], - "Results": [ - { - "ID": "1.0", - "Name": "Non-root containers", - "Description": "", - "Severity": "MEDIUM", - "Results": [ - { - "Target": "", - "Misconfigurations": [ - { - "AVDID": "AVD-KSV012", - "Status": "FAIL", - "Layer": {}, - "CauseMetadata": { - "Code": { - "Lines": null - }, - "RenderedCause": {} - } - } - ] - } - ] - }, - { - "ID": "1.1", - "Name": "Immutable container file systems", - "Description": "", - "Severity": "LOW", - "Results": [ - { - "Target": "", - "Misconfigurations": [ - { - "AVDID": "AVD-KSV013", - "Status": "FAIL", - "Layer": {}, - "CauseMetadata": { - "Code": { - "Lines": null - }, - "RenderedCause": {} - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/compliance/report/testdata/summary.json b/pkg/compliance/report/testdata/summary.json deleted file mode 100644 index 2ee48756272b..000000000000 --- a/pkg/compliance/report/testdata/summary.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "ID": "1234", - "Title": "NSA", - "SummaryControls": [ - { - "ID": "1.0", - "Name": "Non-root containers", - "Severity": "MEDIUM", - "TotalFail": 1 - }, - { - "ID": "1.1", - "Name": "Immutable container file systems", - "Severity": "LOW", - "TotalFail": 1 - } - ] -} \ No newline at end of file diff --git a/pkg/compliance/report/testdata/table_all.txt b/pkg/compliance/report/testdata/table_all.txt deleted file mode 100644 index 159d5f8590ad..000000000000 --- a/pkg/compliance/report/testdata/table_all.txt +++ /dev/null @@ -1,52 +0,0 @@ - -Deployment/metrics-server (kubernetes) -====================================== -Tests: 1 (SUCCESSES: 1, FAILURES: 0) -Failures: 0 () - -MEDIUM: Container 'metrics-server' of Deployment 'metrics-server' should set 'securityContext.allowPrivilegeEscalation' to false -════════════════════════════════════════ -A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node. - -See https://avd.aquasec.com/misconfig/ksv001 -──────────────────────────────────────── - Deployment/metrics-server:132-140 -──────────────────────────────────────── - 132 ┌ - image: rancher/metrics-server:v0.3.6 - 133 │ imagePullPolicy: IfNotPresent - 134 │ name: metrics-server - 135 │ resources: {} - 136 │ terminationMessagePath: /dev/termination-log - 137 │ terminationMessagePolicy: File - 138 │ volumeMounts: - 139 │ - mountPath: /tmp - 140 └ name: tmp-dir -──────────────────────────────────────── - - - -Deployment/metrics-server (kubernetes) -====================================== -Tests: 1 (SUCCESSES: 1, FAILURES: 0) -Failures: 0 () - -LOW: Container 'metrics-server' of Deployment 'metrics-server' should add 'ALL' to 'securityContext.capabilities.drop' -════════════════════════════════════════ -The container should drop all default capabilities and add only those that are needed for its execution. - -See https://avd.aquasec.com/misconfig/ksv003 -──────────────────────────────────────── - Deployment/metrics-server:132-140 -──────────────────────────────────────── - 132 ┌ - image: rancher/metrics-server:v0.3.6 - 133 │ imagePullPolicy: IfNotPresent - 134 │ name: metrics-server - 135 │ resources: {} - 136 │ terminationMessagePath: /dev/termination-log - 137 │ terminationMessagePolicy: File - 138 │ volumeMounts: - 139 │ - mountPath: /tmp - 140 └ name: tmp-dir -──────────────────────────────────────── - - diff --git a/pkg/compliance/report/testdata/table_summary.txt b/pkg/compliance/report/testdata/table_summary.txt deleted file mode 100644 index 5600ae6a8cc2..000000000000 --- a/pkg/compliance/report/testdata/table_summary.txt +++ /dev/null @@ -1,8 +0,0 @@ - -Summary Report for compliance: NSA -┌─────┬──────────┬──────────────────────────────────┬────────┬────────┐ -│ ID │ Severity │ Control Name │ Status │ Issues │ -├─────┼──────────┼──────────────────────────────────┼────────┼────────┤ -│ 1.0 │ MEDIUM │ Non-root containers │ FAIL │ 1 │ -│ 1.1 │ LOW │ Immutable container file systems │ FAIL │ 1 │ -└─────┴──────────┴──────────────────────────────────┴────────┴────────┘ diff --git a/pkg/compliance/spec/compliance.go b/pkg/compliance/spec/compliance.go deleted file mode 100644 index 794d244ca9b4..000000000000 --- a/pkg/compliance/spec/compliance.go +++ /dev/null @@ -1,126 +0,0 @@ -package spec - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "golang.org/x/xerrors" - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy-checks/pkg/specs" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" - "github.com/aquasecurity/trivy/pkg/types" -) - -type Severity string - -// ComplianceSpec represent the compliance specification -type ComplianceSpec struct { - Spec iacTypes.Spec `yaml:"spec"` -} - -const ( - FailStatus iacTypes.ControlStatus = "FAIL" - PassStatus iacTypes.ControlStatus = "PASS" - WarnStatus iacTypes.ControlStatus = "WARN" -) - -// Scanners reads spec control and determines the scanners by check ID prefix -func (cs *ComplianceSpec) Scanners() (types.Scanners, error) { - scannerTypes := set.New[types.Scanner]() - for _, control := range cs.Spec.Controls { - for _, check := range control.Checks { - scannerType := scannerByCheckID(check.ID) - if scannerType == types.UnknownScanner { - return nil, xerrors.Errorf("unsupported check ID: %s", check.ID) - } - scannerTypes.Append(scannerType) - } - } - return scannerTypes.Items(), nil -} - -// CheckIDs return list of compliance check IDs -func (cs *ComplianceSpec) CheckIDs() map[types.Scanner][]string { - checkIDsMap := make(map[types.Scanner][]string) - for _, control := range cs.Spec.Controls { - for _, check := range control.Checks { - scannerType := scannerByCheckID(check.ID) - checkIDsMap[scannerType] = append(checkIDsMap[scannerType], check.ID) - } - } - return checkIDsMap -} - -func scannerByCheckID(checkID string) types.Scanner { - checkID = strings.ToLower(checkID) - switch { - case strings.HasPrefix(checkID, "cve-") || strings.HasPrefix(checkID, "dla-"): - return types.VulnerabilityScanner - case strings.HasPrefix(checkID, "avd-"): - return types.MisconfigScanner - case strings.HasPrefix(checkID, "vuln-"): // custom id for filtering vulnerabilities by severity - return types.VulnerabilityScanner - case strings.HasPrefix(checkID, "secret-"): // custom id for filtering secrets by severity - return types.SecretScanner - default: - return types.UnknownScanner - } -} - -func checksDir(cacheDir string) string { - return filepath.Join(cacheDir, "policy") -} - -func complianceSpecDir(cacheDir string) string { - return filepath.Join(checksDir(cacheDir), "content", "specs", "compliance") -} - -// GetComplianceSpec accepct compliance flag name/path and return builtin or file system loaded spec -func GetComplianceSpec(specNameOrPath, cacheDir string) (ComplianceSpec, error) { - if specNameOrPath == "" { - return ComplianceSpec{}, nil - } - - var b []byte - var err error - if strings.HasPrefix(specNameOrPath, "@") { // load user specified spec from disk - b, err = os.ReadFile(strings.TrimPrefix(specNameOrPath, "@")) - if err != nil { - return ComplianceSpec{}, fmt.Errorf("error retrieving compliance spec from path: %w", err) - } - log.Debug("Compliance spec loaded from specified path", log.String("path", specNameOrPath)) - } else { - _, err := os.Stat(filepath.Join(checksDir(cacheDir), "metadata.json")) - if err != nil { // cache corrupt or bundle does not exist, load embedded version - b = []byte(specs.GetSpec(specNameOrPath)) - log.Debug("Compliance spec loaded from embedded library", log.String("spec", specNameOrPath)) - } else { - // load from bundle on disk - b, err = LoadFromBundle(cacheDir, specNameOrPath) - if err != nil { - return ComplianceSpec{}, err - } - log.Debug("Compliance spec loaded from disk bundle", log.String("spec", specNameOrPath)) - } - } - - var complianceSpec ComplianceSpec - if err = yaml.Unmarshal(b, &complianceSpec); err != nil { - return ComplianceSpec{}, xerrors.Errorf("spec yaml decode error: %w", err) - } - return complianceSpec, nil - -} - -func LoadFromBundle(cacheDir, specNameOrPath string) ([]byte, error) { - b, err := os.ReadFile(filepath.Join(complianceSpecDir(cacheDir), specNameOrPath+".yaml")) - if err != nil { - return nil, fmt.Errorf("error retrieving compliance spec from bundle %s: %w", specNameOrPath, err) - } - return b, nil -} diff --git a/pkg/compliance/spec/compliance_stub.go b/pkg/compliance/spec/compliance_stub.go new file mode 100644 index 000000000000..372b59304e9a --- /dev/null +++ b/pkg/compliance/spec/compliance_stub.go @@ -0,0 +1,23 @@ +package spec + +import ( + "golang.org/x/xerrors" + + iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" + "github.com/aquasecurity/trivy/pkg/types" +) + +// ComplianceSpec represent the compliance specification +type ComplianceSpec struct { + Spec iacTypes.Spec `yaml:"spec"` +} + +// GetComplianceSpec accepct compliance flag name/path and return builtin or file system loaded spec +func GetComplianceSpec(specNameOrPath, cacheDir string) (ComplianceSpec, error) { + return ComplianceSpec{}, xerrors.New("pkg/compliance not implemented") +} + +// Scanners reads spec control and determines the scanners by check ID prefix +func (cs *ComplianceSpec) Scanners() (types.Scanners, error) { + return nil, xerrors.New("pkg/compliance not implemented") +} diff --git a/pkg/compliance/spec/compliance_test.go b/pkg/compliance/spec/compliance_test.go deleted file mode 100644 index b49f169ac7a6..000000000000 --- a/pkg/compliance/spec/compliance_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package spec_test - -import ( - "path/filepath" - "sort" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/compliance/spec" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestComplianceSpec_Scanners(t *testing.T) { - tests := []struct { - name string - spec iacTypes.Spec - want types.Scanners - wantErr assert.ErrorAssertionFunc - }{ - { - name: "get config scanner type by check id prefix", - spec: iacTypes.Spec{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - RelatedResources: []string{ - "https://example.com", - }, - Version: "1.0", - Controls: []iacTypes.Control{ - { - Name: "Non-root containers", - Description: "Check that container is not running as root", - ID: "1.0", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-KSV012"}, - }, - }, - { - Name: "Check that encryption resource has been set", - Description: "Control checks whether encryption resource has been set", - ID: "1.1", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-1.2.31"}, - {ID: "AVD-1.2.32"}, - }, - }, - }, - }, - want: []types.Scanner{types.MisconfigScanner}, - wantErr: assert.NoError, - }, - { - name: "get config and vuln scanners types by check id prefix", - spec: iacTypes.Spec{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - RelatedResources: []string{ - "https://example.com", - }, - Version: "1.0", - Controls: []iacTypes.Control{ - { - Name: "Non-root containers", - Description: "Check that container is not running as root", - ID: "1.0", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-KSV012"}, - }, - }, - { - Name: "Check that encryption resource has been set", - Description: "Control checks whether encryption resource has been set", - ID: "1.1", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-1.2.31"}, - {ID: "AVD-1.2.32"}, - }, - }, - { - Name: "Ensure no critical vulnerabilities", - Description: "Control checks whether critical vulnerabilities are not found", - ID: "7.0", - Checks: []iacTypes.SpecCheck{ - {ID: "CVE-9999-9999"}, - }, - }, - }, - }, - want: []types.Scanner{ - types.MisconfigScanner, - types.VulnerabilityScanner, - }, - wantErr: assert.NoError, - }, - { - name: "unknown prefix", - spec: iacTypes.Spec{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - RelatedResources: []string{ - "https://example.com", - }, - Version: "1.0", - Controls: []iacTypes.Control{ - { - Name: "Unknown", - ID: "1.0", - Checks: []iacTypes.SpecCheck{ - {ID: "UNKNOWN-001"}, - }, - }, - }, - }, - wantErr: assert.Error, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cs := &spec.ComplianceSpec{ - Spec: tt.spec, - } - got, err := cs.Scanners() - if !tt.wantErr(t, err, "Scanners()") { - return - } - sort.Slice(got, func(i, j int) bool { - return got[i] < got[j] - }) // for consistency - assert.Equalf(t, tt.want, got, "Scanners()") - }) - } -} - -func TestComplianceSpec_CheckIDs(t *testing.T) { - tests := []struct { - name string - spec iacTypes.Spec - want map[types.Scanner][]string - }{ - { - name: "get config scanner type by check id prefix", - spec: iacTypes.Spec{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - RelatedResources: []string{ - "https://example.com", - }, - Version: "1.0", - Controls: []iacTypes.Control{ - { - Name: "Non-root containers", - Description: "Check that container is not running as root", - ID: "1.0", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-KSV012"}, - }, - }, - { - Name: "Check that encryption resource has been set", - Description: "Control checks whether encryption resource has been set", - ID: "1.1", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-1.2.31"}, - {ID: "AVD-1.2.32"}, - }, - }, - }, - }, - want: map[types.Scanner][]string{ - types.MisconfigScanner: { - "AVD-KSV012", - "AVD-1.2.31", - "AVD-1.2.32", - }, - }, - }, - { - name: "get config and vuln scanners types by check id prefix", - spec: iacTypes.Spec{ - ID: "1234", - Title: "NSA", - Description: "National Security Agency - Kubernetes Hardening Guidance", - RelatedResources: []string{ - "https://example.com", - }, - Version: "1.0", - Controls: []iacTypes.Control{ - { - Name: "Non-root containers", - Description: "Check that container is not running as root", - ID: "1.0", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-KSV012"}, - }, - }, - { - Name: "Check that encryption resource has been set", - Description: "Control checks whether encryption resource has been set", - ID: "1.1", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-1.2.31"}, - {ID: "AVD-1.2.32"}, - }, - }, - { - Name: "Ensure no critical vulnerabilities", - Description: "Control checks whether critical vulnerabilities are not found", - ID: "7.0", - Checks: []iacTypes.SpecCheck{ - {ID: "CVE-9999-9999"}, - }, - }, - }, - }, - want: map[types.Scanner][]string{ - types.MisconfigScanner: { - "AVD-KSV012", - "AVD-1.2.31", - "AVD-1.2.32", - }, - types.VulnerabilityScanner: { - "CVE-9999-9999", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cs := &spec.ComplianceSpec{ - Spec: tt.spec, - } - got := cs.CheckIDs() - assert.Equalf(t, tt.want, got, "CheckIDs()") - }) - } -} - -func TestComplianceSpec_LoadFromDiskBundle(t *testing.T) { - - t.Run("load user specified spec from disk", func(t *testing.T) { - cs, err := spec.GetComplianceSpec(filepath.Join("@testdata", "testcache", "policy", "content", "specs", "compliance", "testspec.yaml"), filepath.Join("testdata", "testcache")) - require.NoError(t, err) - assert.Equal(t, spec.ComplianceSpec{Spec: iacTypes.Spec{ - ID: "test-spec-1.2", - Title: "Test Spec", - Description: "This is a test spec", - RelatedResources: []string{ - "https://www.google.ca", - }, - Version: "1.2", - Controls: []iacTypes.Control{ - { - Name: "moar-testing", - Description: "Test needs foo bar baz", - ID: "1.1", - Checks: []iacTypes.SpecCheck{ - {ID: "AVD-TEST-1234"}, - }, - Severity: "LOW", - }, - }, - }}, cs) - }) - - t.Run("load user specified spec from disk fails", func(t *testing.T) { - _, err := spec.GetComplianceSpec("@doesnotexist", "does-not-matter") - require.ErrorContains(t, err, "error retrieving compliance spec from path") - }) - - t.Run("bundle does not exist", func(t *testing.T) { - cs, err := spec.GetComplianceSpec("aws-cis-1.2", "does-not-matter") - require.NoError(t, err) - assert.Equal(t, "aws-cis-1.2", cs.Spec.ID) - }) - - t.Run("load spec from disk", func(t *testing.T) { - cs, err := spec.GetComplianceSpec("testspec", filepath.Join("testdata", "testcache")) - require.NoError(t, err) - assert.Equal(t, "test-spec-1.2", cs.Spec.ID) - }) - - t.Run("load spec yaml unmarshal failure", func(t *testing.T) { - _, err := spec.GetComplianceSpec("invalid", filepath.Join("testdata", "testcache")) - require.ErrorContains(t, err, "spec yaml decode error") - }) -} diff --git a/pkg/compliance/spec/custom.go b/pkg/compliance/spec/custom.go deleted file mode 100644 index 8e7ed6679340..000000000000 --- a/pkg/compliance/spec/custom.go +++ /dev/null @@ -1,74 +0,0 @@ -package spec - -import ( - "github.com/samber/lo" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy/pkg/types" -) - -// We might be going to rewrite these functions in Rego, -// but we'll keep them for now until we need flexibility. -var customIDs = map[string]func(types.Result) types.Result{ - "VULN-CRITICAL": filterCriticalVulns, - "VULN-HIGH": filterHighVulns, - "SECRET-CRITICAL": filterCriticalSecrets, - "SECRET-HIGH": filterHighSecrets, -} - -func mapCustomIDsToFilteredResults(result types.Result, checkIDs map[types.Scanner][]string, - mapCheckByID map[string]types.Results) { - for _, ids := range checkIDs { - for _, id := range ids { - filterFunc, ok := customIDs[id] - if !ok { - continue - } - filtered := filterFunc(result) - if filtered.IsEmpty() { - continue - } - mapCheckByID[id] = types.Results{filtered} - } - } -} - -func filterCriticalVulns(result types.Result) types.Result { - return filterVulns(result, dbTypes.SeverityCritical) -} - -func filterHighVulns(result types.Result) types.Result { - return filterVulns(result, dbTypes.SeverityHigh) -} - -func filterVulns(result types.Result, severity dbTypes.Severity) types.Result { - filtered := lo.Filter(result.Vulnerabilities, func(vuln types.DetectedVulnerability, _ int) bool { - return vuln.Severity == severity.String() - }) - return types.Result{ - Target: result.Target, - Class: result.Class, - Type: result.Type, - Vulnerabilities: filtered, - } -} - -func filterCriticalSecrets(result types.Result) types.Result { - return filterSecrets(result, dbTypes.SeverityCritical) -} - -func filterHighSecrets(result types.Result) types.Result { - return filterSecrets(result, dbTypes.SeverityHigh) -} - -func filterSecrets(result types.Result, severity dbTypes.Severity) types.Result { - filtered := lo.Filter(result.Secrets, func(secret types.DetectedSecret, _ int) bool { - return secret.Severity == severity.String() - }) - return types.Result{ - Target: result.Target, - Class: result.Class, - Type: result.Type, - Secrets: filtered, - } -} diff --git a/pkg/compliance/spec/mapper.go b/pkg/compliance/spec/mapper.go deleted file mode 100644 index 8c683753ad90..000000000000 --- a/pkg/compliance/spec/mapper.go +++ /dev/null @@ -1,69 +0,0 @@ -package spec - -import ( - "slices" - - "github.com/aquasecurity/trivy/pkg/types" -) - -// MapSpecCheckIDToFilteredResults map spec check id to filtered scan results -func MapSpecCheckIDToFilteredResults(result types.Result, checkIDs map[types.Scanner][]string) map[string]types.Results { - mapCheckByID := make(map[string]types.Results) - for _, vuln := range result.Vulnerabilities { - // Skip irrelevant check IDs - if !slices.Contains(checkIDs[types.VulnerabilityScanner], vuln.VulnerabilityID) { - continue - } - mapCheckByID[vuln.VulnerabilityID] = append(mapCheckByID[vuln.VulnerabilityID], types.Result{ - Target: result.Target, - Class: result.Class, - Type: result.Type, - Vulnerabilities: []types.DetectedVulnerability{vuln}, - }) - } - for _, m := range result.Misconfigurations { - // Skip irrelevant check IDs - if !slices.Contains(checkIDs[types.MisconfigScanner], m.AVDID) { - continue - } - - mapCheckByID[m.AVDID] = append(mapCheckByID[m.AVDID], types.Result{ - Target: result.Target, - Class: result.Class, - Type: result.Type, - MisconfSummary: misconfigSummary(m), - Misconfigurations: []types.DetectedMisconfiguration{m}, - }) - } - - // Evaluate custom IDs - mapCustomIDsToFilteredResults(result, checkIDs, mapCheckByID) - - return mapCheckByID -} - -func misconfigSummary(misconfig types.DetectedMisconfiguration) *types.MisconfSummary { - rms := types.MisconfSummary{} - switch misconfig.Status { - case types.MisconfStatusPassed: - rms.Successes = 1 - case types.MisconfStatusFailure: - rms.Failures = 1 - } - return &rms -} - -// AggregateAllChecksBySpecID aggregates all scan results and map it to spec ids -func AggregateAllChecksBySpecID(multiResults []types.Results, cs ComplianceSpec) map[string]types.Results { - checkIDs := cs.CheckIDs() - complianceArr := make(map[string]types.Results, 0) - for _, resResult := range multiResults { - for _, result := range resResult { - m := MapSpecCheckIDToFilteredResults(result, checkIDs) - for id, checks := range m { - complianceArr[id] = append(complianceArr[id], checks...) - } - } - } - return complianceArr -} diff --git a/pkg/compliance/spec/mapper_test.go b/pkg/compliance/spec/mapper_test.go deleted file mode 100644 index 070c06a8ce96..000000000000 --- a/pkg/compliance/spec/mapper_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package spec_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/compliance/spec" - "github.com/aquasecurity/trivy/pkg/fanal/secret" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestMapSpecCheckIDToFilteredResults(t *testing.T) { - checkIDs := map[types.Scanner][]string{ - types.MisconfigScanner: { - "AVD-KSV012", - "AVD-1.2.31", - "AVD-1.2.32", - }, - types.VulnerabilityScanner: { - "CVE-9999-9999", - "VULN-CRITICAL", - }, - types.SecretScanner: { - "SECRET-CRITICAL", - }, - } - tests := []struct { - name string - checkIDs map[types.Scanner][]string - result types.Result - want map[string]types.Results - }{ - { - name: "misconfiguration", - checkIDs: checkIDs, - result: types.Result{ - Target: "target", - Class: types.ClassConfig, - Type: ftypes.Kubernetes, - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV012", - Status: types.MisconfStatusFailure, - }, - { - AVDID: "AVD-KSV013", - Status: types.MisconfStatusFailure, - }, - { - AVDID: "AVD-1.2.31", - Status: types.MisconfStatusFailure, - }, - }, - }, - want: map[string]types.Results{ - "AVD-KSV012": { - { - Target: "target", - Class: types.ClassConfig, - Type: ftypes.Kubernetes, - MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - }, - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-KSV012", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - "AVD-1.2.31": { - { - Target: "target", - Class: types.ClassConfig, - Type: ftypes.Kubernetes, - MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - }, - Misconfigurations: []types.DetectedMisconfiguration{ - { - AVDID: "AVD-1.2.31", - Status: types.MisconfStatusFailure, - }, - }, - }, - }, - }, - }, - { - name: "secret", - checkIDs: checkIDs, - result: types.Result{ - Target: "target", - Class: types.ClassSecret, - Secrets: []types.DetectedSecret{ - { - RuleID: "aws-access-key-id", - Category: secret.CategoryAWS, - Severity: "CRITICAL", - Title: "AWS Access Key ID", - Code: ftypes.Code{ - Lines: []ftypes.Line{ - { - Number: 2, - Content: "AWS_ACCESS_KEY_ID=*****", - }, - }, - }, - }, - { - RuleID: "aws-account-id", - Category: secret.CategoryAWS, - Severity: "HIGH", - Title: "AWS Account ID", - Code: ftypes.Code{ - Lines: []ftypes.Line{ - { - Number: 1, - Content: "AWS_ACCOUNT_ID=*****", - }, - }, - }, - }, - }, - }, - want: map[string]types.Results{ - "SECRET-CRITICAL": { - { - Target: "target", - Class: types.ClassSecret, - Secrets: []types.DetectedSecret{ - { - RuleID: "aws-access-key-id", - Category: secret.CategoryAWS, - Severity: "CRITICAL", - Title: "AWS Access Key ID", - Code: ftypes.Code{ - Lines: []ftypes.Line{ - { - Number: 2, - Content: "AWS_ACCESS_KEY_ID=*****", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := spec.MapSpecCheckIDToFilteredResults(tt.result, tt.checkIDs) - assert.Equalf(t, tt.want, got, "MapSpecCheckIDToFilteredResults()") - }) - } -} diff --git a/pkg/compliance/spec/testdata/testcache/policy/content/specs/compliance/invalid.yaml b/pkg/compliance/spec/testdata/testcache/policy/content/specs/compliance/invalid.yaml deleted file mode 100644 index 29dcba2e15af..000000000000 --- a/pkg/compliance/spec/testdata/testcache/policy/content/specs/compliance/invalid.yaml +++ /dev/null @@ -1 +0,0 @@ -this is not yaml but easier to read \ No newline at end of file diff --git a/pkg/compliance/spec/testdata/testcache/policy/content/specs/compliance/testspec.yaml b/pkg/compliance/spec/testdata/testcache/policy/content/specs/compliance/testspec.yaml deleted file mode 100644 index ec1f75f52970..000000000000 --- a/pkg/compliance/spec/testdata/testcache/policy/content/specs/compliance/testspec.yaml +++ /dev/null @@ -1,15 +0,0 @@ -spec: - id: test-spec-1.2 - title: Test Spec - description: This is a test spec - version: "1.2" - relatedResources: - - https://www.google.ca - controls: - - id: "1.1" - name: moar-testing - description: |- - Test needs foo bar baz - checks: - - id: AVD-TEST-1234 - severity: LOW \ No newline at end of file diff --git a/pkg/compliance/spec/testdata/testcache/policy/metadata.json b/pkg/compliance/spec/testdata/testcache/policy/metadata.json deleted file mode 100644 index ba37beda3850..000000000000 --- a/pkg/compliance/spec/testdata/testcache/policy/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"Digest":"sha256:ef2d9ad4fce0f933b20a662004d7e55bf200987c180e7f2cd531af631f408bb3","DownloadedAt":"2024-08-07T20:07:48.917915-06:00"} \ No newline at end of file diff --git a/pkg/dependency/parser/executable/executable.go b/pkg/dependency/parser/executable/executable.go new file mode 100644 index 000000000000..de0b622f144c --- /dev/null +++ b/pkg/dependency/parser/executable/executable.go @@ -0,0 +1,76 @@ +// Ported from https://github.com/golang/go/blob/b5a861782312d2b3a4f71e33d9a0c2b01a40fe5f/src/debug/buildinfo/buildinfo.go + +package executable + +import ( + "bytes" + "debug/elf" + "errors" + "fmt" + "io" +) + +var errUnrecognizedFormat = errors.New("unrecognized file format") + +// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF). +type Exe interface { + // ReadData reads and returns up to size byte starting at virtual address addr. + ReadData(addr, size uint64) ([]byte, error) + + // DataStart returns the writable data segment start address. + DataStart() (uint64, uint64) +} + +// openExe opens file and returns it as an exe. +func OpenExe(r io.ReaderAt) (Exe, error) { + ident := make([]byte, 16) + if n, err := r.ReadAt(ident, 0); n < len(ident) || err != nil { + return nil, errUnrecognizedFormat + } + + switch { + case bytes.HasPrefix(ident, []byte("\x7FELF")): + f, err := elf.NewFile(r) + if err != nil { + return nil, errUnrecognizedFormat + } + return &elfExe{f}, nil + default: + return nil, errUnrecognizedFormat + } + + return nil, errUnrecognizedFormat +} + +// elfExe is the ELF implementation of the exe interface. +type elfExe struct { + f *elf.File +} + +func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { + for _, prog := range x.f.Progs { + if prog.Vaddr > addr || addr > prog.Vaddr+prog.Filesz-1 { + continue + } + n := prog.Vaddr + prog.Filesz - addr + if n > size { + n = size + } + data := make([]byte, n) + _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) + if err != nil { + return nil, err + } + return data, nil + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *elfExe) DataStart() (uint64, uint64) { + for _, s := range x.f.Sections { + if s.Name == ".rodata" { + return s.Addr, s.SectionHeader.Size + } + } + return 0, 0 +} diff --git a/pkg/dependency/parser/executable/java/parse.go b/pkg/dependency/parser/executable/java/parse.go new file mode 100644 index 000000000000..206b526e49b7 --- /dev/null +++ b/pkg/dependency/parser/executable/java/parse.go @@ -0,0 +1,67 @@ +package javaparser + +import ( + "regexp" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/dependency" + exe "github.com/aquasecurity/trivy/pkg/dependency/parser/executable" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + xio "github.com/aquasecurity/trivy/pkg/x/io" +) + +var ( + ErrUnrecognizedExe = xerrors.New("unrecognized executable format") +) + +type Parser struct{} + +func NewParser() *Parser { + return &Parser{} +} + +var versReg = regexp.MustCompile(`(\x00([0-9\.]+)\x00([0-9a-z\+-\._]+)\x00openjdk)?\x00java(\x00([0-9\.]+)\x00([0-9a-z\+-\._]+))?\x00`) + +// Parse scans file to try to report the Python version. +func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { + x, err := exe.OpenExe(r) + if err != nil { + return nil, nil, ErrUnrecognizedExe + } + + name, vers := findVers(x) + if vers == "" { + return nil, nil, nil + } + + var libs []ftypes.Package + libs = append(libs, ftypes.Package{ + ID: dependency.ID(ftypes.JavaExecutable, name, vers), + Name: name, + Version: vers, + }) + + return libs, nil, nil +} + +// findVers finds and returns the Java version in the executable x. +func findVers(x exe.Exe) (vers, mod string) { + text, size := x.DataStart() + data, err := x.ReadData(text, size) + if err != nil { + return + } + + match := versReg.FindSubmatch(data) + if match != nil { + if match[3] != nil { + vers = string(match[3]) + } + if match[6] != nil { + vers = string(match[6]) + } + } + + return "java", vers +} diff --git a/pkg/dependency/parser/executable/java/parse_test.go b/pkg/dependency/parser/executable/java/parse_test.go new file mode 100644 index 000000000000..c8ccf30253aa --- /dev/null +++ b/pkg/dependency/parser/executable/java/parse_test.go @@ -0,0 +1,87 @@ +package javaparser + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func TestParse(t *testing.T) { + tests := []struct { + name string + inputFile string + want []types.Package + wantErr string + }{ + { + name: "jre-8u421-linux-x64", + inputFile: "testdata/java.jre-8u421-linux-x64.elf", + want: []types.Package{ + { + ID: "java@1.8.0_421-b09", + Name: "java", + Version: "1.8.0_421-b09", + }, + }, + }, + { + name: "OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7", + inputFile: "testdata/java.OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.elf", + want: []types.Package{ + { + ID: "java@1.8.0_44-b02", + Name: "java", + Version: "1.8.0_44-b02", + }, + }, + }, + { + name: "openjdk-23+37_linux-x64_bin", + inputFile: "testdata/java.openjdk-23+37_linux-x64_bin.elf", + want: []types.Package{ + { + ID: "java@23+37-2369", + Name: "java", + Version: "23+37-2369", + }, + }, + }, + { + name: "openjdk-8u44-linux-x64", + inputFile: "testdata/java.openjdk-8u44-linux-x64.elf", + want: []types.Package{ + { + ID: "java@1.8.0_44-b02", + Name: "java", + Version: "1.8.0_44-b02", + }, + }, + }, + { + name: "sad path", + inputFile: "testdata/dummy", + wantErr: "unrecognized executable format", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + + parser := NewParser() + got, _, err := parser.Parse(f) + if tt.wantErr != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/dependency/parser/executable/java/testdata/dummy b/pkg/dependency/parser/executable/java/testdata/dummy new file mode 100644 index 000000000000..26bf640459d3 --- /dev/null +++ b/pkg/dependency/parser/executable/java/testdata/dummy @@ -0,0 +1 @@ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/pkg/dependency/parser/executable/java/testdata/java.OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.elf b/pkg/dependency/parser/executable/java/testdata/java.OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.elf new file mode 100755 index 000000000000..15d8ff9d7c6f Binary files /dev/null and b/pkg/dependency/parser/executable/java/testdata/java.OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.elf differ diff --git a/pkg/dependency/parser/executable/java/testdata/java.elf b/pkg/dependency/parser/executable/java/testdata/java.elf new file mode 100755 index 000000000000..0345c9e395ad Binary files /dev/null and b/pkg/dependency/parser/executable/java/testdata/java.elf differ diff --git a/pkg/dependency/parser/executable/java/testdata/java.jre-8u421-linux-x64.elf b/pkg/dependency/parser/executable/java/testdata/java.jre-8u421-linux-x64.elf new file mode 100755 index 000000000000..0345c9e395ad Binary files /dev/null and b/pkg/dependency/parser/executable/java/testdata/java.jre-8u421-linux-x64.elf differ diff --git a/pkg/dependency/parser/executable/java/testdata/java.openjdk-23+37_linux-x64_bin.elf b/pkg/dependency/parser/executable/java/testdata/java.openjdk-23+37_linux-x64_bin.elf new file mode 100755 index 000000000000..e10f48f50a1b Binary files /dev/null and b/pkg/dependency/parser/executable/java/testdata/java.openjdk-23+37_linux-x64_bin.elf differ diff --git a/pkg/dependency/parser/executable/java/testdata/java.openjdk-8u44-linux-x64.elf b/pkg/dependency/parser/executable/java/testdata/java.openjdk-8u44-linux-x64.elf new file mode 100755 index 000000000000..15d8ff9d7c6f Binary files /dev/null and b/pkg/dependency/parser/executable/java/testdata/java.openjdk-8u44-linux-x64.elf differ diff --git a/pkg/dependency/parser/executable/nodejs/parse.go b/pkg/dependency/parser/executable/nodejs/parse.go new file mode 100644 index 000000000000..84e52519ff42 --- /dev/null +++ b/pkg/dependency/parser/executable/nodejs/parse.go @@ -0,0 +1,72 @@ +// Ported from https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go + +package nodejsparser + +import ( + "bytes" + "regexp" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/dependency" + exe "github.com/aquasecurity/trivy/pkg/dependency/parser/executable" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + xio "github.com/aquasecurity/trivy/pkg/x/io" +) + +var ( + ErrUnrecognizedExe = xerrors.New("unrecognized executable format") +) + +type Parser struct{} + +func NewParser() *Parser { + return &Parser{} +} + +var versReg = regexp.MustCompile(`node\.js\/v(\d{1,3}\.\d{1,3}\.\d{1,3})`) + +// Parse scans file to try to report the NodeJS version. +func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { + x, err := exe.OpenExe(r) + if err != nil { + return nil, nil, ErrUnrecognizedExe + } + + mod, vers := findVers(x) + if vers == "" { + return nil, nil, nil + } + + var libs []ftypes.Package + libs = append(libs, ftypes.Package{ + ID: dependency.ID(ftypes.NodeJsExecutable, mod, vers), + Name: mod, + Version: vers, + }) + + return libs, nil, nil +} + +// findVers finds and returns the NodeJS version in the executable x. +func findVers(x exe.Exe) (vers, mod string) { + text, size := x.DataStart() + data, err := x.ReadData(text, size) + if err != nil { + return + } + + for len(data) > 0 { + // Extract the version number + // split by null characters + head, tail, _ := bytes.Cut(data, []byte("\000")) + match := versReg.FindSubmatch(head) + if match != nil { + vers = string(match[1]) + break + } + data = tail + } + + return "node", vers +} diff --git a/pkg/dependency/parser/executable/nodejs/parse_test.go b/pkg/dependency/parser/executable/nodejs/parse_test.go new file mode 100644 index 000000000000..3bf07081cdd6 --- /dev/null +++ b/pkg/dependency/parser/executable/nodejs/parse_test.go @@ -0,0 +1,54 @@ +package nodejsparser + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func TestParse(t *testing.T) { + tests := []struct { + name string + inputFile string + want []types.Package + wantDep []types.Dependency + wantErr string + }{ + { + name: "ELF12", + inputFile: "testdata/node.12.elf", + want: []types.Package{ + { + ID: "node@12.16.3", + Name: "node", + Version: "12.16.3", + }, + }, + }, + { + name: "sad path", + inputFile: "testdata/dummy", + wantErr: "unrecognized executable format", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + parser := NewParser() + got, _, err := parser.Parse(f) + if tt.wantErr != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/dependency/parser/executable/nodejs/testdata/dummy b/pkg/dependency/parser/executable/nodejs/testdata/dummy new file mode 100644 index 000000000000..26bf640459d3 --- /dev/null +++ b/pkg/dependency/parser/executable/nodejs/testdata/dummy @@ -0,0 +1 @@ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/pkg/dependency/parser/executable/nodejs/testdata/node.12.elf b/pkg/dependency/parser/executable/nodejs/testdata/node.12.elf new file mode 100644 index 000000000000..f91f9c958c6f Binary files /dev/null and b/pkg/dependency/parser/executable/nodejs/testdata/node.12.elf differ diff --git a/pkg/dependency/parser/executable/nodejs/testdata/php.elf b/pkg/dependency/parser/executable/nodejs/testdata/php.elf new file mode 100644 index 000000000000..6f5592299136 Binary files /dev/null and b/pkg/dependency/parser/executable/nodejs/testdata/php.elf differ diff --git a/pkg/dependency/parser/executable/php/parse.go b/pkg/dependency/parser/executable/php/parse.go new file mode 100644 index 000000000000..724c1152f27a --- /dev/null +++ b/pkg/dependency/parser/executable/php/parse.go @@ -0,0 +1,73 @@ +// Ported from https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go + +package phpparser + +import ( + "bytes" + "regexp" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/dependency" + exe "github.com/aquasecurity/trivy/pkg/dependency/parser/executable" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + xio "github.com/aquasecurity/trivy/pkg/x/io" +) + +var ( + ErrUnrecognizedExe = xerrors.New("unrecognized executable format") + ErrNonPythonBinary = xerrors.New("non Python binary") +) + +type Parser struct{} + +func NewParser() *Parser { + return &Parser{} +} + +var versReg = regexp.MustCompile(`(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`) + +// Parse scans file to try to report the Python version. +func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { + x, err := exe.OpenExe(r) + if err != nil { + return nil, nil, ErrUnrecognizedExe + } + + name, vers := findVers(x) + if vers == "" { + return nil, nil, nil + } + + var libs []ftypes.Package + libs = append(libs, ftypes.Package{ + ID: dependency.ID(ftypes.PhpExecutable, name, vers), + Name: name, + Version: vers, + }) + + return libs, nil, nil +} + +// findVers finds and returns the PHP version in the executable x. +func findVers(x exe.Exe) (vers, mod string) { + text, size := x.DataStart() + data, err := x.ReadData(text, size) + if err != nil { + return + } + + for len(data) > 0 { + // Extract the version number + // split by null characters + head, tail, _ := bytes.Cut(data, []byte("\000")) + match := versReg.FindSubmatch(head) + if match != nil { + vers = string(match[1]) + break + } + data = tail + } + + return "php", vers +} diff --git a/pkg/dependency/parser/executable/php/parse_test.go b/pkg/dependency/parser/executable/php/parse_test.go new file mode 100644 index 000000000000..a08552fe8ef0 --- /dev/null +++ b/pkg/dependency/parser/executable/php/parse_test.go @@ -0,0 +1,54 @@ +package phpparser + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func TestParse(t *testing.T) { + tests := []struct { + name string + inputFile string + want []types.Package + wantErr string + }{ + { + name: "ELF", + inputFile: "testdata/php.elf", + want: []types.Package{ + { + ID: "php@8.0.7", + Name: "php", + Version: "8.0.7", + }, + }, + }, + { + name: "sad path", + inputFile: "testdata/dummy", + wantErr: "unrecognized executable format", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + + parser := NewParser() + got, _, err := parser.Parse(f) + if tt.wantErr != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/dependency/parser/executable/php/testdata/dummy b/pkg/dependency/parser/executable/php/testdata/dummy new file mode 100644 index 000000000000..26bf640459d3 --- /dev/null +++ b/pkg/dependency/parser/executable/php/testdata/dummy @@ -0,0 +1 @@ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/pkg/iac/rego/testdata/policies/._sysfile.rego b/pkg/dependency/parser/executable/php/testdata/php.elf similarity index 100% rename from pkg/iac/rego/testdata/policies/._sysfile.rego rename to pkg/dependency/parser/executable/php/testdata/php.elf diff --git a/pkg/dependency/parser/executable/python/parse_test.go b/pkg/dependency/parser/executable/python/parse_test.go new file mode 100644 index 000000000000..4b9bd7fa2cf9 --- /dev/null +++ b/pkg/dependency/parser/executable/python/parse_test.go @@ -0,0 +1,76 @@ +package pythonparser + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func TestParse(t *testing.T) { + tests := []struct { + name string + inputFile string + want []types.Package + wantDep []types.Dependency + wantErr string + }{ + { + name: "ELF2.7", + inputFile: "testdata/python2.7.elf", + want: []types.Package{ + { + ID: "python@2.7.18", + Name: "python", + Version: "2.7.18", + }, + }, + }, + { + name: "ELF3.9", + inputFile: "testdata/python3.9.elf", + want: []types.Package{ + { + ID: "python@3.9.19", + Name: "python", + Version: "3.9.19", + }, + }, + }, + { + name: "ELF3.10", + inputFile: "testdata/python3.10.elf", + want: []types.Package{ + { + ID: "python@3.10.12", + Name: "python", + Version: "3.10.12", + }, + }, + }, + { + name: "sad path", + inputFile: "testdata/dummy", + wantErr: "unrecognized executable format", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + parser := NewParser() + got, _, err := parser.Parse(f) + if tt.wantErr != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/dependency/parser/executable/python/parser.go b/pkg/dependency/parser/executable/python/parser.go new file mode 100644 index 000000000000..b6782b7e92e8 --- /dev/null +++ b/pkg/dependency/parser/executable/python/parser.go @@ -0,0 +1,74 @@ +// Ported from https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go + +package pythonparser + +import ( + "bytes" + "regexp" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/dependency" + exe "github.com/aquasecurity/trivy/pkg/dependency/parser/executable" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + xio "github.com/aquasecurity/trivy/pkg/x/io" +) + +var ( + ErrUnrecognizedExe = xerrors.New("unrecognized executable format") + ErrNonPythonBinary = xerrors.New("non Python binary") +) + +type Parser struct{} + +func NewParser() *Parser { + return &Parser{} +} + +var versReg = regexp.MustCompile(`^\d{1,4}\.\d{1,4}\.\d{1,4}[-._a-zA-Z0-9]*$`) + +// Parse scans file to try to report the Python version. +func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { + x, err := exe.OpenExe(r) + if err != nil { + return nil, nil, ErrUnrecognizedExe + } + + name, vers := findVers(x) + if vers == "" { + return nil, nil, nil + } + + var libs []ftypes.Package + libs = append(libs, ftypes.Package{ + ID: dependency.ID(ftypes.PythonExecutable, name, vers), + Name: name, + Version: vers, + }) + + return libs, nil, nil +} + +// findVers finds and returns the Python version in the executable x. +func findVers(x exe.Exe) (mod, vers string) { + text, size := x.DataStart() + data, err := x.ReadData(text, size) + if err != nil { + return + } + + // Python's version pattern is [NUL]3.11.2[NUL] + for len(data) > 0 { + // Extract the version number + // split by null characters, this is important so that we don't match for version number-like strings without the null character + head, tail, _ := bytes.Cut(data, []byte("\000")) + match := versReg.FindSubmatch(head) + if match != nil { + vers = string(match[0]) + break + } + data = tail + } + + return "python", vers +} diff --git a/pkg/dependency/parser/executable/python/testdata/dummy b/pkg/dependency/parser/executable/python/testdata/dummy new file mode 100644 index 000000000000..26bf640459d3 --- /dev/null +++ b/pkg/dependency/parser/executable/python/testdata/dummy @@ -0,0 +1 @@ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/pkg/dependency/parser/executable/python/testdata/python2.7.elf b/pkg/dependency/parser/executable/python/testdata/python2.7.elf new file mode 100644 index 000000000000..4cfee55c3d44 Binary files /dev/null and b/pkg/dependency/parser/executable/python/testdata/python2.7.elf differ diff --git a/pkg/dependency/parser/executable/python/testdata/python3.10.elf b/pkg/dependency/parser/executable/python/testdata/python3.10.elf new file mode 100644 index 000000000000..a7ac65d3c156 Binary files /dev/null and b/pkg/dependency/parser/executable/python/testdata/python3.10.elf differ diff --git a/pkg/dependency/parser/executable/python/testdata/python3.9.elf b/pkg/dependency/parser/executable/python/testdata/python3.9.elf new file mode 100644 index 000000000000..d75bbfa4fcf0 Binary files /dev/null and b/pkg/dependency/parser/executable/python/testdata/python3.9.elf differ diff --git a/pkg/detector/ospkg/bottlerocket/bottlerocket.go b/pkg/detector/ospkg/bottlerocket/bottlerocket.go new file mode 100644 index 000000000000..9dcd6ac43383 --- /dev/null +++ b/pkg/detector/ospkg/bottlerocket/bottlerocket.go @@ -0,0 +1,28 @@ +package bottlerocket + +import ( + "context" + + osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" +) + +// Scanner implements the Bottlerocket scanner +type Scanner struct { +} + +// NewScanner is the factory method for Scanner +func NewScanner() *Scanner { + return &Scanner{} +} + +func (s *Scanner) Detect(ctx context.Context, _ string, _ *ftypes.Repository, _ []ftypes.Package) ([]types.DetectedVulnerability, error) { + log.InfoContext(ctx, "Vulnerability detection of Bottlerocket packages is currently not supported.") + return nil, nil +} + +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, nil, osFamily, osver.Minor(osVer)) +} diff --git a/pkg/detector/ospkg/detect.go b/pkg/detector/ospkg/detect.go index fedc8d31c9c2..ec712ded82a5 100644 --- a/pkg/detector/ospkg/detect.go +++ b/pkg/detector/ospkg/detect.go @@ -11,6 +11,7 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/alpine" "github.com/aquasecurity/trivy/pkg/detector/ospkg/amazon" "github.com/aquasecurity/trivy/pkg/detector/ospkg/azure" + "github.com/aquasecurity/trivy/pkg/detector/ospkg/bottlerocket" "github.com/aquasecurity/trivy/pkg/detector/ospkg/chainguard" "github.com/aquasecurity/trivy/pkg/detector/ospkg/debian" "github.com/aquasecurity/trivy/pkg/detector/ospkg/oracle" @@ -34,6 +35,7 @@ var ( ftypes.Alma: alma.NewScanner(), ftypes.Amazon: amazon.NewScanner(), ftypes.Azure: azure.NewAzureScanner(), + ftypes.Bottlerocket: bottlerocket.NewScanner(), ftypes.CBLMariner: azure.NewMarinerScanner(), ftypes.Debian: debian.NewScanner(), ftypes.Ubuntu: ubuntu.NewScanner(), diff --git a/pkg/downloader/download.go b/pkg/downloader/download.go index 96fd3ce49008..4f6e5c182404 100644 --- a/pkg/downloader/download.go +++ b/pkg/downloader/download.go @@ -1,30 +1,26 @@ package downloader import ( - "cmp" + "compress/bzip2" + "compress/gzip" "context" "crypto/tls" "errors" - "maps" + "io" "net/http" "net/url" "os" - "strings" - "time" + "path/filepath" - "github.com/google/go-github/v62/github" - getter "github.com/hashicorp/go-getter" - "github.com/samber/lo" "golang.org/x/xerrors" ) var ErrSkipDownload = errors.New("skip download") type Options struct { - Insecure bool - Auth Auth - ETag string - ClientMode getter.ClientMode + Insecure bool + Auth Auth + ETag string } type Auth struct { @@ -54,148 +50,54 @@ func DownloadToTempDir(ctx context.Context, src string, opts Options) (string, e // Download downloads the configured source to the destination. func Download(ctx context.Context, src, dst, pwd string, opts Options) (string, error) { - // go-getter doesn't allow the dst directory already exists if the src is directory. - _ = os.RemoveAll(dst) + var rc io.ReadCloser - var clientOpts []getter.ClientOption - if opts.Insecure { - clientOpts = append(clientOpts, getter.WithInsecure()) - } - - // Clone the global map so that it will not be accessed concurrently. - getters := maps.Clone(getter.Getters) - - // Overwrite the file getter so that a file will be copied - getters["file"] = &getter.FileGetter{Copy: true} - - // Since "httpGetter" is a global pointer and the state is shared, - // once it is executed without "WithInsecure()", - // it cannot enable WithInsecure() afterwards because its state is preserved. - // Therefore, we need to create a new "HttpGetter" instance every time. - // cf. https://github.com/hashicorp/go-getter/blob/5a63fd9c0d5b8da8a6805e8c283f46f0dacb30b3/get.go#L63-L65 - transport := NewCustomTransport(opts) - httpGetter := &getter.HttpGetter{ - Netrc: true, - Client: &http.Client{ - Transport: transport, - Timeout: time.Minute * 5, - }, - } - getters["http"] = httpGetter - getters["https"] = httpGetter - - // Build the client - client := &getter.Client{ - Ctx: ctx, - Src: src, - Dst: dst, - Pwd: pwd, - Getters: getters, - Mode: lo.Ternary(opts.ClientMode == 0, getter.ClientModeAny, opts.ClientMode), - Options: clientOpts, - } - - if err := client.Get(); err != nil { - return "", xerrors.Errorf("failed to download %s: %w", src, err) - } - - return transport.newETag, nil -} - -type CustomTransport struct { - auth Auth - cachedETag string - newETag string - insecure bool -} - -func NewCustomTransport(opts Options) *CustomTransport { - return &CustomTransport{ - auth: opts.Auth, - cachedETag: opts.ETag, - insecure: opts.Insecure, - } -} - -func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) { - if t.cachedETag != "" { - req.Header.Set("If-None-Match", t.cachedETag) - } - if t.auth.Token != "" { - req.Header.Set("Authorization", "Bearer "+t.auth.Token) - } else if t.auth.Username != "" || t.auth.Password != "" { - req.SetBasicAuth(t.auth.Username, t.auth.Password) - } - - var transport http.RoundTripper - if req.URL.Host == "github.com" { - transport = NewGitHubTransport(req.URL, t.insecure, t.auth.Token) - } - if transport == nil { - transport = httpTransport(t.insecure) - } - - res, err := transport.RoundTrip(req) + u, err := url.ParseRequestURI(src) if err != nil { - return nil, xerrors.Errorf("failed to round trip: %w", err) - } - - switch res.StatusCode { - case http.StatusOK, http.StatusPartialContent: - // Update the ETag - t.newETag = res.Header.Get("ETag") - case http.StatusNotModified: - return nil, ErrSkipDownload - } - - return res, nil -} - -func NewGitHubTransport(u *url.URL, insecure bool, token string) http.RoundTripper { - client := newGitHubClient(insecure, token) - ss := strings.SplitN(u.Path, "/", 4) - if len(ss) < 4 || strings.HasPrefix(ss[3], "archive/") || strings.HasPrefix(ss[3], "releases/") || - strings.HasPrefix(ss[3], "tags/") { - // Use the default transport from go-github for authentication - return client.Client().Transport + return "", xerrors.Errorf("failed to parse url: %w", err) + } + if u.Scheme != "" { + insecure := false + if opts.Insecure { + insecure = true + } + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}, + } + client := &http.Client{Transport: tr} + resp, err := client.Get(src) + if err != nil { + return "", xerrors.Errorf("failed to get: %w", err) + } + rc = resp.Body + } else { + f, err := os.Open(src) + if err != nil { + return "", xerrors.Errorf("failed to open: %w", err) + } + rc = f + } + defer rc.Close() + + r, err := uncompress(rc, src) + if err != nil { + return "", xerrors.Errorf("failed to uncompress: %w", err) } - return &GitHubContentTransport{ - owner: ss[1], - repo: ss[2], - filePath: ss[3], - client: client, - } -} - -// GitHubContentTransport is a round tripper for downloading the GitHub content. -type GitHubContentTransport struct { - owner string - repo string - filePath string - client *github.Client -} - -// RoundTrip calls the GitHub API to download the content. -func (t *GitHubContentTransport) RoundTrip(req *http.Request) (*http.Response, error) { - _, res, err := t.client.Repositories.DownloadContents(req.Context(), t.owner, t.repo, t.filePath, nil) + err = Untar(r, dst) if err != nil { - return nil, xerrors.Errorf("failed to get the file content: %w", err) + return "", xerrors.Errorf("failed to untar: %w", err) } - return res.Response, nil -} -func newGitHubClient(insecure bool, token string) *github.Client { - client := github.NewClient(&http.Client{Transport: httpTransport(insecure)}) - token = cmp.Or(token, os.Getenv("GITHUB_TOKEN")) - if token != "" { - client = client.WithAuthToken(token) - } - return client + return "", nil } -func httpTransport(insecure bool) *http.Transport { - tr := http.DefaultTransport.(*http.Transport).Clone() - tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: insecure} - return tr +func uncompress(r io.Reader, name string) (io.Reader, error) { + switch filepath.Ext(name) { + case ".bz2": + return bzip2.NewReader(r), nil + case ".gz": + return gzip.NewReader(r) + } + return r, nil } diff --git a/pkg/downloader/untar.go b/pkg/downloader/untar.go new file mode 100644 index 000000000000..b09c9c623325 --- /dev/null +++ b/pkg/downloader/untar.go @@ -0,0 +1,85 @@ +package downloader + +import ( + "archive/tar" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" +) + +// From golang.org/x/build/internal/untar/untar.go + +// Untar reads the gzip-compressed tar file from r and writes it into dir. +func Untar(r io.Reader, dir string) error { + return untar(r, dir) +} + +func untar(r io.Reader, dir string) (err error) { + madeDir := map[string]bool{} + tr := tar.NewReader(r) + for { + f, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + log.Printf("tar reading error: %v", err) + return fmt.Errorf("tar error: %v", err) + } + if !validRelPath(f.Name) { + return fmt.Errorf("tar contained invalid name error %q", f.Name) + } + rel := filepath.FromSlash(f.Name) + abs := filepath.Join(dir, rel) + + mode := f.FileInfo().Mode() + switch f.Typeflag { + case tar.TypeReg: + // Make the directory. This is redundant because it should + // already be made by a directory entry in the tar + // beforehand. Thus, don't check for errors; the next + // write will fail with the same error. + dir := filepath.Dir(abs) + if !madeDir[dir] { + if err := os.MkdirAll(filepath.Dir(abs), 0755); err != nil { + return err + } + madeDir[dir] = true + } + wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm()) + if err != nil { + return err + } + n, err := io.Copy(wf, tr) + if closeErr := wf.Close(); closeErr != nil && err == nil { + err = closeErr + } + if err != nil { + return fmt.Errorf("error writing to %s: %v", abs, err) + } + if n != f.Size { + return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size) + } + case tar.TypeDir: + if err := os.MkdirAll(abs, 0755); err != nil { + return err + } + madeDir[abs] = true + case tar.TypeXGlobalHeader: + // git archive generates these. Ignore them. + default: + return fmt.Errorf("tar file entry %s contained unsupported file type %v", f.Name, mode) + } + } + return nil +} + +func validRelPath(p string) bool { + if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "..") { + return false + } + return true +} diff --git a/pkg/fanal/analyzer/all/import.go b/pkg/fanal/analyzer/all/import.go index c9d50dd4f924..e18d436cfc45 100644 --- a/pkg/fanal/analyzer/all/import.go +++ b/pkg/fanal/analyzer/all/import.go @@ -1,12 +1,7 @@ package all import ( - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/buildinfo" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/executable" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/apk" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/dockerfile" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/secret" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/c/conan" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/conda/environment" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/conda/meta" @@ -46,9 +41,8 @@ import ( _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/release" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/ubuntu" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/bottlerocket_inventory" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/dpkg" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/rpm" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/repo/apk" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/sbom" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret" ) diff --git a/pkg/fanal/analyzer/analyzer.go b/pkg/fanal/analyzer/analyzer.go index abedb4b90638..9a0f7923a3a2 100644 --- a/pkg/fanal/analyzer/analyzer.go +++ b/pkg/fanal/analyzer/analyzer.go @@ -10,6 +10,7 @@ import ( "sort" "strings" "sync" + "time" "github.com/samber/lo" "golang.org/x/sync/semaphore" @@ -118,6 +119,13 @@ type CustomGroup interface { Group() Group } +// StaticPathAnalyzer is an interface for analyzers that can specify static file paths +// instead of traversing the entire filesystem. +type StaticPathAnalyzer interface { + // StaticPaths returns a list of static file paths to analyze + StaticPaths() []string +} + type Opener func() (xio.ReadSeekCloserAt, error) type AnalyzerGroup struct { @@ -147,8 +155,13 @@ type PostAnalysisInput struct { } type AnalysisOptions struct { - Offline bool - FileChecksum bool + Offline bool + OfflineJar bool + FileChecksum bool + FileChecksumJar bool + WalkErrCallback func(string, error) error + AnalyzerTimeout time.Duration + PostAnalyzerTimeout time.Duration } type AnalysisResult struct { @@ -423,6 +436,11 @@ func (ag AnalyzerGroup) AnalyzeFile(ctx context.Context, wg *sync.WaitGroup, lim ag.logger.Debug("Permission error", log.FilePath(filePath)) break } else if err != nil { + if opts.WalkErrCallback != nil { + if errCb := opts.WalkErrCallback(filePath, err); errCb == nil { + break + } + } return xerrors.Errorf("unable to open %s: %w", filePath, err) } @@ -473,6 +491,7 @@ func (ag AnalyzerGroup) RequiredPostAnalyzers(filePath string, info os.FileInfo) // The obtained results are merged into the "result". // This function may be called concurrently and must be thread-safe. func (ag AnalyzerGroup) PostAnalyze(ctx context.Context, compositeFS *CompositeFS, result *AnalysisResult, opts AnalysisOptions) error { + var errs error for _, a := range ag.postAnalyzers { fsys, ok := compositeFS.Get(a.Type()) if !ok { @@ -502,16 +521,36 @@ func (ag AnalyzerGroup) PostAnalyze(ctx context.Context, compositeFS *CompositeF return xerrors.Errorf("unable to filter filesystem: %w", err) } - res, err := a.PostAnalyze(ctx, PostAnalysisInput{ + res, err := postAnalyzeWithTimeout(ctx, a, PostAnalysisInput{ FS: filteredFS, Options: opts, - }) + }, opts.PostAnalyzerTimeout) if err != nil { - return xerrors.Errorf("post analysis error: %w", err) + if errors.Is(err, context.DeadlineExceeded) { + appCount := 0 + if res != nil { + appCount = len(res.Applications) + } + err = xerrors.Errorf("%s post analysis timeout after %d results: %w", a.Type(), appCount, err) + errs = errors.Join(errs, err) + } else { + return xerrors.Errorf("post analysis error: %w", err) + } + } else { + // Merge result only if the PostAnalyzer completed successfully. + result.Merge(res) } - result.Merge(res) } - return nil + return errs +} + +func postAnalyzeWithTimeout(ctx context.Context, a PostAnalyzer, input PostAnalysisInput, t time.Duration) (*AnalysisResult, error) { + if t > 0 { + ctx1, cancel := context.WithTimeout(ctx, t) + defer cancel() + ctx = ctx1 + } + return a.PostAnalyze(ctx, input) } // PostAnalyzerFS returns a composite filesystem that contains multiple filesystems for each post-analyzer @@ -527,3 +566,36 @@ func (ag AnalyzerGroup) filePatternMatch(analyzerType Type, filePath string) boo } return false } + +// StaticPaths collects static paths from all enabled analyzers +// It returns the collected paths and a boolean indicating if all enabled analyzers implement StaticPathAnalyzer +func (ag AnalyzerGroup) StaticPaths(disabled []Type) ([]string, bool) { + var paths []string + + for _, a := range ag.analyzers { + // Skip disabled analyzers + if slices.Contains(disabled, a.Type()) { + continue + } + + // If any analyzer doesn't implement StaticPathAnalyzer, return false + staticPathAnalyzer, ok := a.(StaticPathAnalyzer) + if !ok { + return nil, false + } + + // Collect paths from StaticPathAnalyzer + paths = append(paths, staticPathAnalyzer.StaticPaths()...) + } + + // PostAnalyzers don't implement StaticPathAnalyzer. + // So if at least one postAnalyzer is enabled - we should not use StaticPath. + if allPostAnalyzersDisabled := lo.EveryBy(ag.postAnalyzers, func(a PostAnalyzer) bool { + return slices.Contains(disabled, a.Type()) + }); !allPostAnalyzersDisabled { + return nil, false + } + + // Remove duplicates + return lo.Uniq(paths), true +} diff --git a/pkg/fanal/analyzer/buildinfo/content_manifest.go b/pkg/fanal/analyzer/buildinfo/content_manifest.go index ccb4b175a645..8b7a0feabe6c 100644 --- a/pkg/fanal/analyzer/buildinfo/content_manifest.go +++ b/pkg/fanal/analyzer/buildinfo/content_manifest.go @@ -63,3 +63,7 @@ func (a contentManifestAnalyzer) Type() analyzer.Type { func (a contentManifestAnalyzer) Version() int { return contentManifestAnalyzerVersion } + +func (a contentManifestAnalyzer) StaticPaths() []string { + return contentSetsDirs.Items() +} diff --git a/pkg/fanal/analyzer/buildinfo/dockerfile.go b/pkg/fanal/analyzer/buildinfo/dockerfile.go index 10ae20b4a4c9..f0dfbaa4cc55 100644 --- a/pkg/fanal/analyzer/buildinfo/dockerfile.go +++ b/pkg/fanal/analyzer/buildinfo/dockerfile.go @@ -157,3 +157,7 @@ func setKVValue(kvpo instructions.KeyValuePairOptional, values map[string]string } return kvpo } + +func (a dockerfileAnalyzer) StaticPaths() []string { + return []string{"root/buildinfo"} +} diff --git a/pkg/fanal/analyzer/config/all/import.go b/pkg/fanal/analyzer/config/all/import.go index 74ba00ba49a8..1a6c64721f26 100644 --- a/pkg/fanal/analyzer/config/all/import.go +++ b/pkg/fanal/analyzer/config/all/import.go @@ -1,14 +1 @@ package all - -import ( - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/azurearm" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/cloudformation" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/dockerfile" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/helm" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/json" - _ "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/json" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraformplan/snapshot" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/yaml" -) diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index d59d7285c189..0452121f3f6a 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -28,12 +28,13 @@ const ( TypeUbuntuESM Type = "ubuntu-esm" // OS Package - TypeApk Type = "apk" - TypeDpkg Type = "dpkg" - TypeDpkgLicense Type = "dpkg-license" // For analyzing licenses - TypeRpm Type = "rpm" - TypeRpmArchive Type = "rpm-archive" - TypeRpmqa Type = "rpmqa" + TypeApk Type = "apk" + TypeBottlerocketInventory Type = "bottlerocket-inventory" + TypeDpkg Type = "dpkg" + TypeDpkgLicense Type = "dpkg-license" // For analyzing licenses + TypeRpm Type = "rpm" + TypeRpmArchive Type = "rpm-archive" + TypeRpmqa Type = "rpmqa" // OS Package Repository TypeApkRepo Type = "apk-repo" @@ -165,6 +166,7 @@ var ( TypeSUSE, TypeUbuntu, TypeApk, + TypeBottlerocketInventory, TypeDpkg, TypeDpkgLicense, TypeRpm, @@ -178,6 +180,7 @@ var ( TypeGemSpec, TypeCargo, TypeComposer, + TypeComposerVendor, TypeJar, TypePom, TypeGradleLock, @@ -192,6 +195,7 @@ var ( TypeCondaPkg, TypeCondaEnv, TypePythonPkg, + TypePythonPkgEgg, TypePip, TypePipenv, TypePoetry, @@ -205,6 +209,7 @@ var ( TypePubSpecLock, TypeMixLock, TypeJulia, + TypeSBOM, } // TypeLockfiles has all lock file analyzers diff --git a/pkg/fanal/analyzer/executable/executable.go b/pkg/fanal/analyzer/executable/executable.go index b484cb0fde3a..6c7dc950b3dc 100644 --- a/pkg/fanal/analyzer/executable/executable.go +++ b/pkg/fanal/analyzer/executable/executable.go @@ -3,11 +3,16 @@ package executable import ( "context" "os" + "path/filepath" + "regexp" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/digest" + javaparser "github.com/aquasecurity/trivy/pkg/dependency/parser/executable/java" + nodejsparser "github.com/aquasecurity/trivy/pkg/dependency/parser/executable/nodejs" + phpparser "github.com/aquasecurity/trivy/pkg/dependency/parser/executable/php" + pythonparser "github.com/aquasecurity/trivy/pkg/dependency/parser/executable/python" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language" + "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/fanal/utils" ) @@ -21,6 +26,60 @@ const version = 1 // so that it can search for SBOM attestation in post-handler. type executableAnalyzer struct{} +// Returns boolean argument in first argument, indicating whether the Executable version is detectable +func isDetectableLibraryExecutable(fileInfo os.FileInfo) (bool, types.TargetType, error) { + isPythonExecutable := isDetectablePythonExecutable(fileInfo) + if isPythonExecutable { + return true, types.PythonExecutable, nil + } + isNodeJsExecutable := isDetectableNodeJsExecutable(fileInfo) + if isNodeJsExecutable { + return true, types.NodeJsExecutable, nil + } + isPhpExecutable := isDetectablePhpExecutable(fileInfo) + if isPhpExecutable { + return true, types.PhpExecutable, nil + } + isJavaExecutable := isDetectableJavaExecutable(fileInfo) + if isJavaExecutable { + return true, types.JavaExecutable, nil + } + return false, types.TargetType(""), nil +} + +var ( + pythonLibNameRegex = regexp.MustCompile("^libpython[0-9]+(?:[.0-9])+[a-z]?[.]so.*$") + pythonExecutableNameRegex = regexp.MustCompile("(?:.*/|^)python(?P[0-9]+(?:[.0-9])+)?$") + nodejsExecutableNameRegex = regexp.MustCompile("(?:.*/|^)node(?P[0-9]+(?:[.0-9])+)?$") + phpExecutableNameRegex = regexp.MustCompile("(.*/|^)php[0-9]*$") + phpLibNameRegex = regexp.MustCompile("(.*/|^)libphp[0-9a-z.-]*[.]so$") + phpFpmNameRegex = regexp.MustCompile("(.*/|^)php-fpm[0-9]*$") + phpCgiNameRegex = regexp.MustCompile("(.*/|^)php-cgi[0-9]*$") +) + +func isDetectablePythonExecutable(fileInfo os.FileInfo) bool { + isPythonExecutable := pythonExecutableNameRegex.FindSubmatch([]byte(fileInfo.Name())) + isPythonLibSo := pythonLibNameRegex.FindSubmatch([]byte(fileInfo.Name())) + return (isPythonExecutable != nil || isPythonLibSo != nil) +} + +func isDetectableNodeJsExecutable(fileInfo os.FileInfo) bool { + isNodeJsExecutable := nodejsExecutableNameRegex.FindSubmatch([]byte(fileInfo.Name())) + return (isNodeJsExecutable != nil) +} + +func isDetectablePhpExecutable(fileInfo os.FileInfo) bool { + isPHPExecutable := phpExecutableNameRegex.FindSubmatch([]byte(fileInfo.Name())) + isPHPLib := phpLibNameRegex.FindSubmatch([]byte(fileInfo.Name())) + isPHPFpm := phpFpmNameRegex.FindSubmatch([]byte(fileInfo.Name())) + isPHPCgi := phpCgiNameRegex.FindSubmatch([]byte(fileInfo.Name())) + return (isPHPExecutable != nil || isPHPLib != nil || isPHPFpm != nil || isPHPCgi != nil) +} + +func isDetectableJavaExecutable(fileInfo os.FileInfo) bool { + return filepath.Base(fileInfo.Name()) == "java" +} + func (a executableAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { // Skip non-binaries isBinary, err := utils.IsBinary(input.Content, input.Info.Size()) @@ -28,16 +87,28 @@ func (a executableAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisIn return nil, nil } - dig, err := digest.CalcSHA256(input.Content) - if err != nil { - return nil, xerrors.Errorf("sha256 error: %w", err) + isDetectableLib, binaryType, err := isDetectableLibraryExecutable(input.Info) + if isDetectableLib && binaryType != "" && err == nil { + var res *analyzer.AnalysisResult = nil + switch binaryType { + case types.PythonExecutable: + res, err = language.Analyze(types.PythonExecutable, input.FilePath, input.Content, pythonparser.NewParser()) + case types.NodeJsExecutable: + res, err = language.Analyze(types.NodeJsExecutable, input.FilePath, input.Content, nodejsparser.NewParser()) + case types.PhpExecutable: + res, err = language.Analyze(types.PhpExecutable, input.FilePath, input.Content, phpparser.NewParser()) + case types.JavaExecutable: + res, err = language.Analyze(types.JavaExecutable, input.FilePath, input.Content, javaparser.NewParser()) + } + if err != nil { + return nil, err + } + if res != nil { + return res, nil + } } - return &analyzer.AnalysisResult{ - Digests: map[string]string{ - input.FilePath: dig.String(), - }, - }, nil + return nil, nil } func (a executableAnalyzer) Required(_ string, fileInfo os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/executable/executable_test.go b/pkg/fanal/analyzer/executable/executable_test.go index 774359163a6a..08931154956b 100644 --- a/pkg/fanal/analyzer/executable/executable_test.go +++ b/pkg/fanal/analyzer/executable/executable_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" ) func Test_executableAnalyzer_Analyze(t *testing.T) { @@ -20,17 +21,89 @@ func Test_executableAnalyzer_Analyze(t *testing.T) { { name: "binary", filePath: "testdata/binary", - want: &analyzer.AnalysisResult{ - Digests: map[string]string{ - "testdata/binary": "sha256:9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a", - }, - }, + want: nil, }, { name: "text", filePath: "testdata/hello.txt", want: nil, }, + { + name: "Python binary", + filePath: "testdata/python2.7", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.PythonExecutable, + FilePath: "testdata/python2.7", + Packages: types.Packages{ + { + ID: "python@2.7.18", + Name: "python", + Version: "2.7.18", + }, + }, + }, + }, + }, + }, + { + name: "Php Binary", + filePath: "testdata/php", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.PhpExecutable, + FilePath: "testdata/php", + Packages: types.Packages{ + { + ID: "php@8.0.7", + Name: "php", + Version: "8.0.7", + }, + }, + }, + }, + }, + }, + { + name: "NodeJS Binary", + filePath: "testdata/node", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.NodeJsExecutable, + FilePath: "testdata/node", + Packages: types.Packages{ + { + ID: "node@12.16.3", + Name: "node", + Version: "12.16.3", + }, + }, + }, + }, + }, + }, + { + name: "Java Binary", + filePath: "testdata/java", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.JavaExecutable, + FilePath: "testdata/java", + Packages: types.Packages{ + { + ID: "java@1.8.0_421-b09", + Name: "java", + Version: "1.8.0_421-b09", + }, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/fanal/analyzer/executable/testdata/java b/pkg/fanal/analyzer/executable/testdata/java new file mode 100755 index 000000000000..0345c9e395ad Binary files /dev/null and b/pkg/fanal/analyzer/executable/testdata/java differ diff --git a/pkg/fanal/analyzer/executable/testdata/node b/pkg/fanal/analyzer/executable/testdata/node new file mode 100755 index 000000000000..f91f9c958c6f Binary files /dev/null and b/pkg/fanal/analyzer/executable/testdata/node differ diff --git a/pkg/fanal/analyzer/executable/testdata/php b/pkg/fanal/analyzer/executable/testdata/php new file mode 100755 index 000000000000..6f5592299136 Binary files /dev/null and b/pkg/fanal/analyzer/executable/testdata/php differ diff --git a/pkg/fanal/analyzer/executable/testdata/python2.7 b/pkg/fanal/analyzer/executable/testdata/python2.7 new file mode 100755 index 000000000000..4cfee55c3d44 Binary files /dev/null and b/pkg/fanal/analyzer/executable/testdata/python2.7 differ diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go index 5dcd0fc19a7b..b42cfa194bb5 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go @@ -10,7 +10,7 @@ import ( "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/image" + imageutils "github.com/aquasecurity/trivy/pkg/fanal/image/utils" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/iac/detection" "github.com/aquasecurity/trivy/pkg/mapfs" @@ -79,7 +79,7 @@ func (a *historyAnalyzer) Analyze(ctx context.Context, input analyzer.ConfigAnal func imageConfigToDockerfile(cfg *v1.ConfigFile) []byte { dockerfile := new(bytes.Buffer) var userFound bool - baseLayerIndex := image.GuessBaseImageIndex(cfg.History) + baseLayerIndex := imageutils.GuessBaseImageIndex(cfg.History) for i := baseLayerIndex + 1; i < len(cfg.History); i++ { h := cfg.History[i] var createdBy string diff --git a/pkg/fanal/analyzer/language/c/conan/conan.go b/pkg/fanal/analyzer/language/c/conan/conan.go index a32591dae7fe..16236e9f058c 100644 --- a/pkg/fanal/analyzer/language/c/conan/conan.go +++ b/pkg/fanal/analyzer/language/c/conan/conan.go @@ -43,19 +43,19 @@ func newConanLockAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, er }, nil } -func (a conanLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a conanLockAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { required := func(filePath string, d fs.DirEntry) bool { // we need all file got from `a.Required` function (conan.lock files) and from file-patterns. return true } - licenses, err := licensesFromCache() + licenses, err := licensesFromCache(ctx, input.Options.WalkErrCallback) if err != nil { a.logger.Debug("Unable to parse cache directory to obtain licenses", log.Err(err)) } var apps []types.Application - if err = fsutils.WalkDir(input.FS, ".", required, func(filePath string, _ fs.DirEntry, r io.Reader) error { + err = fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, _ fs.DirEntry, r io.Reader) error { app, err := language.Parse(types.Conan, filePath, r, a.parser) if err != nil { return xerrors.Errorf("%s parse error: %w", filePath, err) @@ -77,16 +77,18 @@ func (a conanLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAna sort.Sort(app.Packages) apps = append(apps, *app) return nil - }); err != nil { + }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } + if err != nil { return nil, xerrors.Errorf("unable to parse conan lock file: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } -func licensesFromCache() (map[string]string, error) { +func licensesFromCache(ctx context.Context, errCallback func(path string, err error) error) (map[string]string, error) { cacheDir, err := detectCacheDir() if err != nil { return nil, err @@ -97,7 +99,7 @@ func licensesFromCache() (map[string]string, error) { } licenses := make(map[string]string) - if err := fsutils.WalkDir(os.DirFS(cacheDir), ".", required, func(filePath string, _ fs.DirEntry, r io.Reader) error { + err = fsutils.WalkDir(ctx, os.DirFS(cacheDir), ".", required, errCallback, func(filePath string, _ fs.DirEntry, r io.Reader) error { scanner := bufio.NewScanner(r) var name, license string for scanner.Scan() { @@ -128,8 +130,9 @@ func licensesFromCache() (map[string]string, error) { licenses[name] = license return nil - }); err != nil { - return nil, xerrors.Errorf("the Conan cache dir (%s) walk error: %w", cacheDir, err) + }) + if err != nil { + return licenses, xerrors.Errorf("the Conan cache dir (%s) walk error: %w", cacheDir, err) } return licenses, nil } diff --git a/pkg/fanal/analyzer/language/conda/meta/meta.go b/pkg/fanal/analyzer/language/conda/meta/meta.go index 091587f3435c..00f2a1e56c1a 100644 --- a/pkg/fanal/analyzer/language/conda/meta/meta.go +++ b/pkg/fanal/analyzer/language/conda/meta/meta.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "github.com/aquasecurity/trivy/pkg/dependency/parser/conda/meta" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" @@ -27,6 +28,9 @@ func (a metaAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) ( return language.AnalyzePackage(types.CondaPkg, input.FilePath, input.Content, p, input.Options.FileChecksum) } func (a metaAnalyzer) Required(filePath string, _ os.FileInfo) bool { + if !strings.HasSuffix(filePath, ".json") || !strings.Contains(filePath, "conda-meta") { + return false + } return fileRegex.MatchString(filepath.ToSlash(filePath)) } diff --git a/pkg/fanal/analyzer/language/dart/pub/pubspec.go b/pkg/fanal/analyzer/language/dart/pub/pubspec.go index 30fc2dadb18b..50045a5da3fc 100644 --- a/pkg/fanal/analyzer/language/dart/pub/pubspec.go +++ b/pkg/fanal/analyzer/language/dart/pub/pubspec.go @@ -44,12 +44,12 @@ func newPubSpecLockAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyze }, nil } -func (a pubSpecLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a pubSpecLockAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application // get all DependsOn from cache dir // lib ID -> DependsOn names - allDependsOn, err := a.findDependsOn() + allDependsOn, err := a.findDependsOn(ctx) if err != nil { a.logger.Warn("Unable to parse cache dir", log.Err(err)) } @@ -58,7 +58,7 @@ func (a pubSpecLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostA return filepath.Base(path) == types.PubSpecLock } - err = fsutils.WalkDir(input.FS, ".", required, func(path string, _ fs.DirEntry, r io.Reader) error { + err = fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, _ fs.DirEntry, r io.Reader) error { app, err := language.Parse(types.Pub, path, r, a.parser) if err != nil { return xerrors.Errorf("unable to parse %q: %w", path, err) @@ -89,16 +89,17 @@ func (a pubSpecLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostA apps = append(apps, *app) return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + return result, xerrors.Errorf("walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } -func (a pubSpecLockAnalyzer) findDependsOn() (map[string][]string, error) { +func (a pubSpecLockAnalyzer) findDependsOn(ctx context.Context) (map[string][]string, error) { dir := cacheDir() if !fsutils.DirExists(dir) { a.logger.Debug("Cache dir not found. Need 'dart pub get' to fill dependency relationships", @@ -111,7 +112,7 @@ func (a pubSpecLockAnalyzer) findDependsOn() (map[string][]string, error) { } deps := make(map[string][]string) - if err := fsutils.WalkDir(os.DirFS(dir), ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, os.DirFS(dir), ".", required, fsutils.DefaultWalkErrorCallback, func(path string, d fs.DirEntry, r io.Reader) error { id, dependsOn, err := parsePubSpecYaml(r) if err != nil { a.logger.Debug("Unable to parse pubspec.yaml", log.FilePath(path), log.Err(err)) @@ -122,8 +123,9 @@ func (a pubSpecLockAnalyzer) findDependsOn() (map[string][]string, error) { } return nil - }); err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + }) + if err != nil { + return deps, xerrors.Errorf("walk error: %w", err) } return deps, nil } diff --git a/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go b/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go index b7400048edac..548fad38634d 100644 --- a/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go +++ b/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go @@ -52,7 +52,7 @@ func newNugetLibraryAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, }, nil } -func (a *nugetLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a *nugetLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application foundLicenses := make(map[string][]string) if a.licenseParser.packagesDir == "" { @@ -65,7 +65,7 @@ func (a *nugetLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.Pos return true } - err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { // Set the default parser parser := a.lockParser @@ -101,13 +101,14 @@ func (a *nugetLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.Pos apps = append(apps, *app) return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("NuGet walk error: %w", err) + return result, xerrors.Errorf("NuGet walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a *nugetLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/golang/mod/mod.go b/pkg/fanal/analyzer/language/golang/mod/mod.go index bb6117f3a100..a2e1dbe1a3de 100644 --- a/pkg/fanal/analyzer/language/golang/mod/mod.go +++ b/pkg/fanal/analyzer/language/golang/mod/mod.go @@ -64,14 +64,14 @@ func newGoModAnalyzer(opt analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, erro }, nil } -func (a *gomodAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a *gomodAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.GoMod } - err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, _ io.Reader) error { + werr := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, d fs.DirEntry, _ io.Reader) error { // Parse go.mod gomod, err := parse(input.FS, path, a.modParser) if err != nil { @@ -93,20 +93,23 @@ func (a *gomodAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalys apps = append(apps, *gomod) return nil }) - if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) - } - if err = a.fillAdditionalData(apps); err != nil { + if err := a.fillAdditionalData(apps); err != nil { a.logger.Warn("Unable to collect additional info", log.Err(err)) } // Add orphan indirect dependencies under the main module a.addOrphanIndirectDepsUnderRoot(apps) - return &analyzer.AnalysisResult{ + result := &analyzer.AnalysisResult{ Applications: apps, - }, nil + } + + if werr != nil { + return result, xerrors.Errorf("gomod walk error: %w", werr) + } + + return result, nil } func (a *gomodAnalyzer) Required(filePath string, _ os.FileInfo) bool { @@ -337,6 +340,8 @@ func findLicense(dir string, classifierConfidenceLevel float64) ([]string, error return nil, nil case err != nil && !errors.Is(err, io.EOF): return nil, fmt.Errorf("finding a known open source license: %w", err) + case err != nil && license != nil && len(license.Findings) > 0: + return license.Findings.Names(), err case license == nil || len(license.Findings) == 0: return nil, nil } diff --git a/pkg/fanal/analyzer/language/java/gradle/lockfile.go b/pkg/fanal/analyzer/language/java/gradle/lockfile.go index ce7fc2c31e59..37826f6841a8 100644 --- a/pkg/fanal/analyzer/language/java/gradle/lockfile.go +++ b/pkg/fanal/analyzer/language/java/gradle/lockfile.go @@ -42,8 +42,8 @@ func newGradleLockAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, e }, nil } -func (a gradleLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { - poms, err := a.parsePoms() +func (a gradleLockAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { + poms, err := a.parsePoms(ctx) if err != nil { a.logger.Warn("Unable to get licenses and dependencies", log.Err(err)) } @@ -53,7 +53,7 @@ func (a gradleLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAn } var apps []types.Application - err = fsutils.WalkDir(input.FS, ".", required, func(filePath string, _ fs.DirEntry, r io.Reader) error { + err = fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, _ fs.DirEntry, r io.Reader) error { var app *types.Application app, err = language.Parse(types.Gradle, filePath, r, a.parser) if err != nil { @@ -94,13 +94,14 @@ func (a gradleLockAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAn apps = append(apps, *app) return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + return result, xerrors.Errorf("walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a gradleLockAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/java/gradle/pom.go b/pkg/fanal/analyzer/language/java/gradle/pom.go index fcad9969b306..e82d008f42ba 100644 --- a/pkg/fanal/analyzer/language/java/gradle/pom.go +++ b/pkg/fanal/analyzer/language/java/gradle/pom.go @@ -1,6 +1,7 @@ package gradle import ( + "context" "encoding/xml" "io" "io/fs" @@ -65,7 +66,7 @@ func (props *Properties) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error return nil } -func (a gradleLockAnalyzer) parsePoms() (map[string]pomXML, error) { +func (a gradleLockAnalyzer) parsePoms(ctx context.Context) (map[string]pomXML, error) { cacheDir := a.detectCacheDir() // Cache dir is not found if cacheDir == "" { @@ -77,7 +78,7 @@ func (a gradleLockAnalyzer) parsePoms() (map[string]pomXML, error) { } var poms = make(map[string]pomXML) - err := fsutils.WalkDir(os.DirFS(cacheDir), ".", required, func(path string, _ fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, os.DirFS(cacheDir), ".", required, fsutils.DefaultWalkErrorCallback, func(path string, _ fs.DirEntry, r io.Reader) error { pom, err := parsePom(r, path) if err != nil { a.logger.Debug("Unable to parse pom", log.FilePath(path), log.Err(err)) @@ -90,7 +91,7 @@ func (a gradleLockAnalyzer) parsePoms() (map[string]pomXML, error) { return nil }) if err != nil { - return nil, xerrors.Errorf("gradle licenses walk error: %w", err) + return poms, xerrors.Errorf("gradle licenses walk error: %w", err) } return poms, nil diff --git a/pkg/fanal/analyzer/language/java/jar/jar.go b/pkg/fanal/analyzer/language/java/jar/jar.go index 42d1004a2f44..942e0dc9a8fe 100644 --- a/pkg/fanal/analyzer/language/java/jar/jar.go +++ b/pkg/fanal/analyzer/language/java/jar/jar.go @@ -43,22 +43,30 @@ func newJavaLibraryAnalyzer(options analyzer.AnalyzerOptions) (analyzer.PostAnal } func (a *javaLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { - // TODO: think about the sonatype API and "--offline" - client, err := javadb.NewClient() - if err != nil { - return nil, xerrors.Errorf("Unable to initialize the Java DB: %s", err) - } - defer func() { _ = client.Close() }() + offline := input.Options.OfflineJar + + var javadbClient jar.Client + + if !offline { + // TODO: think about the sonatype API and "--offline" + client, err := javadb.NewClient() + if err != nil { + return nil, xerrors.Errorf("Unable to initialize the Java DB: %s", err) + } + defer func() { _ = client.Close() }() + + // Skip analyzing JAR files as the nil client means the Java DB was not downloaded successfully. + if client == nil { + return nil, nil + } - // Skip analyzing JAR files as the nil client means the Java DB was not downloaded successfully. - if client == nil { - return nil, nil + javadbClient = client } // It will be called on each JAR file onFile := func(path string, info fs.FileInfo, r xio.ReadSeekerAt) (*types.Application, error) { - p := jar.NewParser(client, jar.WithSize(info.Size()), jar.WithFilePath(path)) - return language.ParsePackage(types.Jar, path, r, p, input.Options.FileChecksum) + p := jar.NewParser(javadbClient, jar.WithSize(info.Size()), jar.WithOffline(offline), jar.WithFilePath(path)) + return language.ParsePackage(types.Jar, path, r, p, input.Options.FileChecksum || input.Options.FileChecksumJar) } var apps []types.Application @@ -70,13 +78,15 @@ func (a *javaLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.Po return nil } - if err = parallel.WalkDir(ctx, input.FS, ".", a.parallel, onFile, onResult); err != nil { - return nil, xerrors.Errorf("walk dir error: %w", err) + err := parallel.WalkDir(ctx, input.FS, ".", a.parallel, onFile, onResult) + result := &analyzer.AnalysisResult{ + Applications: apps, + } + if err != nil { + return result, xerrors.Errorf("jar walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a *javaLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/julia/pkg/pkg.go b/pkg/fanal/analyzer/language/julia/pkg/pkg.go index 66d715e061b2..c172adf4f21e 100644 --- a/pkg/fanal/analyzer/language/julia/pkg/pkg.go +++ b/pkg/fanal/analyzer/language/julia/pkg/pkg.go @@ -50,14 +50,14 @@ func newJuliaAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) }, nil } -func (a juliaAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a juliaAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.JuliaManifest } - err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { // Parse Manifest.toml app, err := a.parseJuliaManifest(path, r) if err != nil { @@ -76,13 +76,14 @@ func (a juliaAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysi apps = append(apps, *app) return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("julia walk error: %w", err) + return result, xerrors.Errorf("julia walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a juliaAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/nodejs/license/license.go b/pkg/fanal/analyzer/language/nodejs/license/license.go index b6a0f3fb7678..a700623282e1 100644 --- a/pkg/fanal/analyzer/language/nodejs/license/license.go +++ b/pkg/fanal/analyzer/language/nodejs/license/license.go @@ -6,6 +6,7 @@ import ( "io/fs" "path" "strings" + "context" "golang.org/x/xerrors" @@ -30,7 +31,7 @@ func NewLicense(classifierConfidenceLevel float64) *License { } } -func (l *License) Traverse(fsys fs.FS, root string) (map[string][]string, error) { +func (l *License) Traverse(ctx context.Context, fsys fs.FS, root string) (map[string][]string, error) { licenses := make(map[string][]string) walkDirFunc := func(pkgJSONPath string, d fs.DirEntry, r io.Reader) error { pkg, err := l.parser.Parse(r) @@ -59,7 +60,7 @@ func (l *License) Traverse(fsys fs.FS, root string) (map[string][]string, error) } return nil } - if err := fsutils.WalkDir(fsys, root, fsutils.RequiredFile(types.NpmPkg), walkDirFunc); err != nil { + if err := fsutils.WalkDir(ctx, fsys, root, fsutils.RequiredFile(types.NpmPkg), fsutils.DefaultWalkErrorCallback, walkDirFunc); err != nil { return nil, xerrors.Errorf("walk error: %w", err) } diff --git a/pkg/fanal/analyzer/language/nodejs/npm/npm.go b/pkg/fanal/analyzer/language/nodejs/npm/npm.go index 870ba1be88e7..b669f41ab322 100644 --- a/pkg/fanal/analyzer/language/nodejs/npm/npm.go +++ b/pkg/fanal/analyzer/language/nodejs/npm/npm.go @@ -44,16 +44,16 @@ func newNpmLibraryAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, e }, nil } -func (a npmLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a npmLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { // Parse package-lock.json required := func(path string, _ fs.DirEntry) bool { return filepath.Base(path) == types.NpmPkgLock } var apps []types.Application - err := fsutils.WalkDir(input.FS, ".", required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { // Find all licenses from package.json files under node_modules dirs - licenses, err := a.findLicenses(input.FS, filePath) + licenses, err := a.findLicenses(ctx, input.FS, filePath, input.Options.WalkErrCallback) if err != nil { a.logger.Error("Unable to collect licenses", log.Err(err)) licenses = make(map[string][]string) @@ -76,13 +76,14 @@ func (a npmLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAn apps = append(apps, *app) return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("package-lock.json/package.json walk error: %w", err) + return result, xerrors.Errorf("package-lock.json/package.json walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a npmLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool { @@ -124,7 +125,7 @@ func (a npmLibraryAnalyzer) parseNpmPkgLock(fsys fs.FS, filePath string) (*types return language.Parse(types.Npm, filePath, file, a.lockParser) } -func (a npmLibraryAnalyzer) findLicenses(fsys fs.FS, lockPath string) (map[string][]string, error) { +func (a npmLibraryAnalyzer) findLicenses(ctx context.Context, fsys fs.FS, lockPath string, errCallback func(filePath string, err error) error) (map[string][]string, error) { dir := path.Dir(lockPath) root := path.Join(dir, "node_modules") if _, err := fs.Stat(fsys, root); errors.Is(err, fs.ErrNotExist) { @@ -142,7 +143,7 @@ func (a npmLibraryAnalyzer) findLicenses(fsys fs.FS, lockPath string) (map[strin // Note that fs.FS is always slashed regardless of the platform, // and path.Join should be used rather than filepath.Join. licenses := make(map[string][]string) - err := fsutils.WalkDir(fsys, root, required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, fsys, root, required, errCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { pkg, err := a.packageParser.Parse(r) if err != nil { return xerrors.Errorf("unable to parse %q: %w", filePath, err) @@ -152,7 +153,7 @@ func (a npmLibraryAnalyzer) findLicenses(fsys fs.FS, lockPath string) (map[strin return nil }) if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + return licenses, xerrors.Errorf("walk error: %w", err) } return licenses, nil } diff --git a/pkg/fanal/analyzer/language/nodejs/pnpm/pnpm.go b/pkg/fanal/analyzer/language/nodejs/pnpm/pnpm.go index 9c8f51a38265..3924a1a39320 100644 --- a/pkg/fanal/analyzer/language/nodejs/pnpm/pnpm.go +++ b/pkg/fanal/analyzer/language/nodejs/pnpm/pnpm.go @@ -41,16 +41,16 @@ func newPnpmAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) }, nil } -func (a pnpmAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a pnpmAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.PnpmLock } - err := fsutils.WalkDir(input.FS, ".", required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { // Find licenses - licenses, err := a.findLicenses(input.FS, filePath) + licenses, err := a.findLicenses(ctx, input.FS, filePath, input.Options.WalkErrCallback) if err != nil { a.logger.Error("Unable to collect licenses", log.Err(err)) licenses = make(map[string][]string) @@ -75,13 +75,14 @@ func (a pnpmAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("pnpm walk error: %w", err) + return result, xerrors.Errorf("pnpm walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a pnpmAnalyzer) Required(filePath string, _ os.FileInfo) bool { @@ -108,7 +109,7 @@ func (a pnpmAnalyzer) Version() int { return version } -func (a pnpmAnalyzer) findLicenses(fsys fs.FS, lockPath string) (map[string][]string, error) { +func (a pnpmAnalyzer) findLicenses(ctx context.Context, fsys fs.FS, lockPath string, errCallback func(filePath string, err error) error) (map[string][]string, error) { dir := path.Dir(lockPath) root := path.Join(dir, "node_modules") if _, err := fs.Stat(fsys, root); errors.Is(err, fs.ErrNotExist) { @@ -126,7 +127,7 @@ func (a pnpmAnalyzer) findLicenses(fsys fs.FS, lockPath string) (map[string][]st // Note that fs.FS is always slashed regardless of the platform, // and path.Join should be used rather than filepath.Join. licenses := make(map[string][]string) - err := fsutils.WalkDir(fsys, root, required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, fsys, root, required, errCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { pkg, err := a.packageJsonParser.Parse(r) if err != nil { return xerrors.Errorf("unable to parse %q: %w", filePath, err) @@ -136,7 +137,7 @@ func (a pnpmAnalyzer) findLicenses(fsys fs.FS, lockPath string) (map[string][]st return nil }) if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + return licenses, xerrors.Errorf("walk error: %w", err) } return licenses, nil } diff --git a/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go b/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go index 984f72983ec9..4a0b4d5aff3b 100644 --- a/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go +++ b/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go @@ -67,14 +67,14 @@ func (p *parserWithPatterns) Parse(r xio.ReadSeekerAt) ([]types.Package, []types return pkgs, deps, err } -func (a yarnAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a yarnAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.YarnLock } - err := fsutils.WalkDir(input.FS, ".", required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { parser := &parserWithPatterns{} // Parse yarn.lock app, err := language.Parse(types.Yarn, filePath, r, parser) @@ -84,13 +84,13 @@ func (a yarnAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis return nil } - licenses, err := a.traverseLicenses(input.FS, filePath) + licenses, err := a.traverseLicenses(ctx, input.FS, filePath) if err != nil { a.logger.Debug("Unable to traverse licenses", log.Err(err)) } // Parse package.json alongside yarn.lock to find direct deps and mark dev deps - if err = a.analyzeDependencies(input.FS, path.Dir(filePath), app, parser.patterns); err != nil { + if err = a.analyzeDependencies(ctx, input.FS, path.Dir(filePath), app, parser.patterns); err != nil { a.logger.Warn("Unable to parse package.json to remove dev dependencies", log.FilePath(path.Join(path.Dir(filePath), types.NpmPkg)), log.Err(err)) } @@ -106,13 +106,14 @@ func (a yarnAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("yarn walk error: %w", err) + return result, xerrors.Errorf("yarn walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a yarnAnalyzer) Required(filePath string, _ os.FileInfo) bool { @@ -160,9 +161,9 @@ func (a yarnAnalyzer) Version() int { // analyzeDependencies analyzes the package.json file next to yarn.lock, // distinguishing between direct and transitive dependencies as well as production and development dependencies. -func (a yarnAnalyzer) analyzeDependencies(fsys fs.FS, dir string, app *types.Application, patterns map[string][]string) error { +func (a yarnAnalyzer) analyzeDependencies(ctx context.Context, fsys fs.FS, dir string, app *types.Application, patterns map[string][]string) error { packageJsonPath := path.Join(dir, types.NpmPkg) - directDeps, directDevDeps, err := a.parsePackageJsonDependencies(fsys, packageJsonPath) + directDeps, directDevDeps, err := a.parsePackageJsonDependencies(ctx, fsys, packageJsonPath) if errors.Is(err, fs.ErrNotExist) { a.logger.Debug("package.json not found", log.FilePath(packageJsonPath)) return nil @@ -265,7 +266,7 @@ func (a yarnAnalyzer) walkIndirectDependencies(pkg types.Package, pkgIDs, deps m } } -func (a yarnAnalyzer) parsePackageJsonDependencies(fsys fs.FS, filePath string) (map[string]string, map[string]string, error) { +func (a yarnAnalyzer) parsePackageJsonDependencies(ctx context.Context, fsys fs.FS, filePath string) (map[string]string, map[string]string, error) { // Parse package.json f, err := fsys.Open(filePath) if err != nil { @@ -283,7 +284,7 @@ func (a yarnAnalyzer) parsePackageJsonDependencies(fsys fs.FS, filePath string) devDependencies := rootPkg.DevDependencies if len(rootPkg.Workspaces) > 0 { - pkgs, err := a.traverseWorkspaces(fsys, path.Dir(filePath), rootPkg.Workspaces) + pkgs, err := a.traverseWorkspaces(ctx, fsys, path.Dir(filePath), rootPkg.Workspaces) if err != nil { return nil, nil, xerrors.Errorf("traverse workspaces error: %w", err) } @@ -296,7 +297,7 @@ func (a yarnAnalyzer) parsePackageJsonDependencies(fsys fs.FS, filePath string) return dependencies, devDependencies, nil } -func (a yarnAnalyzer) traverseWorkspaces(fsys fs.FS, dir string, workspaces []string) ([]packagejson.Package, error) { +func (a yarnAnalyzer) traverseWorkspaces(ctx context.Context, fsys fs.FS, dir string, workspaces []string) ([]packagejson.Package, error) { var pkgs []packagejson.Package required := func(path string, _ fs.DirEntry) bool { @@ -321,7 +322,7 @@ func (a yarnAnalyzer) traverseWorkspaces(fsys fs.FS, dir string, workspaces []st return nil, err } for _, match := range matches { - if err := fsutils.WalkDir(fsys, match, required, walkDirFunc); err != nil { + if err := fsutils.WalkDir(ctx, fsys, match, required, fsutils.DefaultWalkErrorCallback, walkDirFunc); err != nil { return nil, xerrors.Errorf("walk error: %w", err) } } @@ -330,7 +331,7 @@ func (a yarnAnalyzer) traverseWorkspaces(fsys fs.FS, dir string, workspaces []st return pkgs, nil } -func (a yarnAnalyzer) traverseLicenses(fsys fs.FS, lockPath string) (map[string][]string, error) { +func (a yarnAnalyzer) traverseLicenses(ctx context.Context, fsys fs.FS, lockPath string) (map[string][]string, error) { sub, err := fs.Sub(fsys, path.Dir(lockPath)) if err != nil { return nil, xerrors.Errorf("fs error: %w", err) @@ -338,14 +339,14 @@ func (a yarnAnalyzer) traverseLicenses(fsys fs.FS, lockPath string) (map[string] var errs error // Yarn v1 - licenses, err := a.traverseYarnClassicPkgs(sub) + licenses, err := a.traverseYarnClassicPkgs(ctx, sub) if err == nil { return licenses, nil } errs = multierror.Append(errs, err) // Yarn v2+ - licenses, err = a.traverseYarnModernPkgs(sub) + licenses, err = a.traverseYarnModernPkgs(ctx, sub) if err == nil { return licenses, nil } @@ -354,11 +355,11 @@ func (a yarnAnalyzer) traverseLicenses(fsys fs.FS, lockPath string) (map[string] return nil, errs } -func (a yarnAnalyzer) traverseYarnClassicPkgs(fsys fs.FS) (map[string][]string, error) { - return a.license.Traverse(fsys, "node_modules") +func (a yarnAnalyzer) traverseYarnClassicPkgs(ctx context.Context, fsys fs.FS) (map[string][]string, error) { + return a.license.Traverse(ctx, fsys, "node_modules") } -func (a yarnAnalyzer) traverseYarnModernPkgs(fsys fs.FS) (map[string][]string, error) { +func (a yarnAnalyzer) traverseYarnModernPkgs(ctx context.Context, fsys fs.FS) (map[string][]string, error) { sub, err := fs.Sub(fsys, ".yarn") if err != nil { return nil, xerrors.Errorf("fs error: %w", err) @@ -367,13 +368,13 @@ func (a yarnAnalyzer) traverseYarnModernPkgs(fsys fs.FS) (map[string][]string, e var errs error licenses := make(map[string][]string) - if ll, err := a.traverseUnpluggedDir(sub); err != nil { + if ll, err := a.traverseUnpluggedDir(ctx, sub); err != nil { errs = multierror.Append(errs, err) } else { licenses = lo.Assign(licenses, ll) } - if ll, err := a.traverseCacheDir(sub); err != nil { + if ll, err := a.traverseCacheDir(ctx, sub); err != nil { errs = multierror.Append(errs, err) } else { licenses = lo.Assign(licenses, ll) @@ -386,16 +387,16 @@ func (a yarnAnalyzer) traverseYarnModernPkgs(fsys fs.FS) (map[string][]string, e return licenses, nil } -func (a yarnAnalyzer) traverseUnpluggedDir(fsys fs.FS) (map[string][]string, error) { +func (a yarnAnalyzer) traverseUnpluggedDir(ctx context.Context, fsys fs.FS) (map[string][]string, error) { // `unplugged` hold machine-specific build artifacts // Traverse .yarn/unplugged dir - return a.license.Traverse(fsys, "unplugged") + return a.license.Traverse(ctx, fsys, "unplugged") } -func (a yarnAnalyzer) traverseCacheDir(fsys fs.FS) (map[string][]string, error) { +func (a yarnAnalyzer) traverseCacheDir(ctx context.Context, fsys fs.FS) (map[string][]string, error) { // Traverse .yarn/cache dir licenses := make(map[string][]string) - err := fsutils.WalkDir(fsys, "cache", fsutils.RequiredExt(".zip"), + err := fsutils.WalkDir(ctx, fsys, "cache", fsutils.RequiredExt(".zip"), fsutils.DefaultWalkErrorCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { fi, err := d.Info() if err != nil { @@ -412,7 +413,7 @@ func (a yarnAnalyzer) traverseCacheDir(fsys fs.FS) (map[string][]string, error) return xerrors.Errorf("zip reader error: %w", err) } - if l, err := a.license.Traverse(zr, "node_modules"); err != nil { + if l, err := a.license.Traverse(ctx, zr, "node_modules"); err != nil { return xerrors.Errorf("license traverse error: %w", err) } else { licenses = lo.Assign(licenses, l) @@ -421,7 +422,7 @@ func (a yarnAnalyzer) traverseCacheDir(fsys fs.FS) (map[string][]string, error) }) if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + return licenses, xerrors.Errorf("walk error: %w", err) } return licenses, nil diff --git a/pkg/fanal/analyzer/language/php/composer/composer.go b/pkg/fanal/analyzer/language/php/composer/composer.go index d0b3f4466352..a44b21476e1c 100644 --- a/pkg/fanal/analyzer/language/php/composer/composer.go +++ b/pkg/fanal/analyzer/language/php/composer/composer.go @@ -43,14 +43,14 @@ func newComposerAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, err }, nil } -func (a composerAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a composerAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.ComposerLock } - err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { // Parse composer.lock app, err := a.parseComposerLock(path, r) if err != nil { @@ -69,13 +69,14 @@ func (a composerAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnal return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("composer walk error: %w", err) + return result, xerrors.Errorf("composer walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a composerAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/python/packaging/packaging.go b/pkg/fanal/analyzer/language/python/packaging/packaging.go index 944a5abde331..5282a6b43434 100644 --- a/pkg/fanal/analyzer/language/python/packaging/packaging.go +++ b/pkg/fanal/analyzer/language/python/packaging/packaging.go @@ -58,7 +58,7 @@ type packagingAnalyzer struct { } // PostAnalyze analyzes egg and wheel files. -func (a packagingAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a packagingAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application @@ -66,7 +66,7 @@ func (a packagingAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAna return filepath.Base(path) == "METADATA" || isEggFile(path) } - err := fsutils.WalkDir(input.FS, ".", required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { rsa, ok := r.(xio.ReadSeekerAt) if !ok { return xerrors.New("invalid reader") @@ -99,12 +99,14 @@ func (a packagingAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAna return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } + if err != nil { - return nil, xerrors.Errorf("python package walk error: %w", err) + return result, xerrors.Errorf("python package walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } type fileOpener func(filePath string) (io.ReadCloser, error) diff --git a/pkg/fanal/analyzer/language/python/pip/pip.go b/pkg/fanal/analyzer/language/python/pip/pip.go index 670dd195bb50..e8368a201b0e 100644 --- a/pkg/fanal/analyzer/language/python/pip/pip.go +++ b/pkg/fanal/analyzer/language/python/pip/pip.go @@ -51,7 +51,7 @@ func newPipLibraryAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer }, nil } -func (a pipLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a pipLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application sitePackagesDir, err := a.pythonSitePackagesDir() @@ -66,7 +66,7 @@ func (a pipLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAn useMinVersion := a.detectionPriority == types.PriorityComprehensive - if err = fsutils.WalkDir(input.FS, ".", required, func(pathPath string, d fs.DirEntry, r io.Reader) error { + err = fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(pathPath string, d fs.DirEntry, r io.Reader) error { app, err := language.Parse(types.Pip, pathPath, r, pip.NewParser(useMinVersion)) if err != nil { return xerrors.Errorf("unable to parse requirements.txt: %w", err) @@ -85,13 +85,15 @@ func (a pipLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAn apps = append(apps, *app) return nil - }); err != nil { - return nil, xerrors.Errorf("pip walt error: %w", err) + }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } + if err != nil { + return result, xerrors.Errorf("pip walt error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a pipLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/python/poetry/poetry.go b/pkg/fanal/analyzer/language/python/poetry/poetry.go index e0721bb3d6a9..a011de361fbb 100644 --- a/pkg/fanal/analyzer/language/python/poetry/poetry.go +++ b/pkg/fanal/analyzer/language/python/poetry/poetry.go @@ -41,14 +41,14 @@ func newPoetryAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error }, nil } -func (a poetryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a poetryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.PoetryLock } - err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { // Parse poetry.lock app, err := a.parsePoetryLock(path, r) if err != nil { @@ -66,13 +66,14 @@ func (a poetryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalys return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("poetry walk error: %w", err) + return result, xerrors.Errorf("poetry walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a poetryAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/python/uv/uv.go b/pkg/fanal/analyzer/language/python/uv/uv.go index b192815a6322..050df70386d2 100644 --- a/pkg/fanal/analyzer/language/python/uv/uv.go +++ b/pkg/fanal/analyzer/language/python/uv/uv.go @@ -35,13 +35,13 @@ func NewUvAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { }, nil } -func (a *uvAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a *uvAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.UvLock } - err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { // Parse uv.lock app, err := language.Parse(types.Uv, path, r, a.lockParser) if err != nil { @@ -55,13 +55,16 @@ func (a *uvAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisI return nil }) + + result := &analyzer.AnalysisResult{ + Applications: apps, + } + if err != nil { - return nil, xerrors.Errorf("walk error: %w", err) + return result, xerrors.Errorf("walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a *uvAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/language/ruby/gemspec/gemspec.go b/pkg/fanal/analyzer/language/ruby/gemspec/gemspec.go index e422771bc565..f785f1b12552 100644 --- a/pkg/fanal/analyzer/language/ruby/gemspec/gemspec.go +++ b/pkg/fanal/analyzer/language/ruby/gemspec/gemspec.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "github.com/aquasecurity/trivy/pkg/dependency/parser/ruby/gemspec" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" @@ -28,6 +29,9 @@ func (a gemspecLibraryAnalyzer) Analyze(_ context.Context, input analyzer.Analys } func (a gemspecLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool { + if !strings.HasSuffix(filePath, ".gemspec") { + return false + } return fileRegex.MatchString(filepath.ToSlash(filePath)) } diff --git a/pkg/fanal/analyzer/language/rust/cargo/cargo.go b/pkg/fanal/analyzer/language/rust/cargo/cargo.go index ab436bf95fe2..d89e03fed3c4 100644 --- a/pkg/fanal/analyzer/language/rust/cargo/cargo.go +++ b/pkg/fanal/analyzer/language/rust/cargo/cargo.go @@ -53,14 +53,14 @@ func newCargoAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) }, nil } -func (a cargoAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a cargoAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var apps []types.Application required := func(path string, d fs.DirEntry) bool { return filepath.Base(path) == types.CargoLock } - err := fsutils.WalkDir(input.FS, ".", required, func(filePath string, d fs.DirEntry, r io.Reader) error { + err := fsutils.WalkDir(ctx, input.FS, ".", required, input.Options.WalkErrCallback, func(filePath string, d fs.DirEntry, r io.Reader) error { // Parse Cargo.lock app, err := a.parseCargoLock(filePath, r) if err != nil { @@ -79,13 +79,14 @@ func (a cargoAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysi return nil }) + result := &analyzer.AnalysisResult{ + Applications: apps, + } if err != nil { - return nil, xerrors.Errorf("cargo walk error: %w", err) + return result, xerrors.Errorf("cargo walk error: %w", err) } - return &analyzer.AnalysisResult{ - Applications: apps, - }, nil + return result, nil } func (a cargoAnalyzer) Required(filePath string, _ os.FileInfo) bool { diff --git a/pkg/fanal/analyzer/os/alpine/alpine.go b/pkg/fanal/analyzer/os/alpine/alpine.go index da3d2da00d89..f5451ecf2da1 100644 --- a/pkg/fanal/analyzer/os/alpine/alpine.go +++ b/pkg/fanal/analyzer/os/alpine/alpine.go @@ -48,3 +48,8 @@ func (a alpineOSAnalyzer) Type() analyzer.Type { func (a alpineOSAnalyzer) Version() int { return version } + +// StaticPaths returns the static paths of the alpine analyzer +func (a alpineOSAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/analyzer/os/amazonlinux/amazonlinux.go b/pkg/fanal/analyzer/os/amazonlinux/amazonlinux.go index 1c3302088e80..7ac62c7118dc 100644 --- a/pkg/fanal/analyzer/os/amazonlinux/amazonlinux.go +++ b/pkg/fanal/analyzer/os/amazonlinux/amazonlinux.go @@ -73,3 +73,8 @@ func (a amazonlinuxOSAnalyzer) Type() analyzer.Type { func (a amazonlinuxOSAnalyzer) Version() int { return version } + +// StaticPaths returns the static paths of the amazonlinux analyzer +func (a amazonlinuxOSAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/analyzer/os/debian/debian.go b/pkg/fanal/analyzer/os/debian/debian.go index 8d6c7a9014df..89fb0b9c16bc 100644 --- a/pkg/fanal/analyzer/os/debian/debian.go +++ b/pkg/fanal/analyzer/os/debian/debian.go @@ -48,3 +48,8 @@ func (a debianOSAnalyzer) Type() analyzer.Type { func (a debianOSAnalyzer) Version() int { return version } + +// StaticPaths returns the static paths of the debian analyzer +func (a debianOSAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/analyzer/os/redhatbase/alma.go b/pkg/fanal/analyzer/os/redhatbase/alma.go index 11b4aa94008b..7fa2548a50fd 100644 --- a/pkg/fanal/analyzer/os/redhatbase/alma.go +++ b/pkg/fanal/analyzer/os/redhatbase/alma.go @@ -60,3 +60,8 @@ func (a almaOSAnalyzer) Type() analyzer.Type { func (a almaOSAnalyzer) Version() int { return almaAnalyzerVersion } + +// StaticPaths returns the static paths of the alma analyzer +func (a almaOSAnalyzer) StaticPaths() []string { + return a.requiredFiles() +} diff --git a/pkg/fanal/analyzer/os/redhatbase/centos.go b/pkg/fanal/analyzer/os/redhatbase/centos.go index 2401e6a76b21..52830c8b935e 100644 --- a/pkg/fanal/analyzer/os/redhatbase/centos.go +++ b/pkg/fanal/analyzer/os/redhatbase/centos.go @@ -60,3 +60,8 @@ func (a centOSAnalyzer) Type() analyzer.Type { func (a centOSAnalyzer) Version() int { return centosAnalyzerVersion } + +// StaticPaths returns the static paths of the centos analyzer +func (a centOSAnalyzer) StaticPaths() []string { + return a.requiredFiles() +} diff --git a/pkg/fanal/analyzer/os/redhatbase/fedora.go b/pkg/fanal/analyzer/os/redhatbase/fedora.go index 1391544ccf75..ef1031967653 100644 --- a/pkg/fanal/analyzer/os/redhatbase/fedora.go +++ b/pkg/fanal/analyzer/os/redhatbase/fedora.go @@ -62,3 +62,8 @@ func (a fedoraOSAnalyzer) Type() analyzer.Type { func (a fedoraOSAnalyzer) Version() int { return fedoraAnalyzerVersion } + +// StaticPaths returns the static paths of the fedora analyzer +func (a fedoraOSAnalyzer) StaticPaths() []string { + return a.requiredFiles() +} diff --git a/pkg/fanal/analyzer/os/redhatbase/oracle.go b/pkg/fanal/analyzer/os/redhatbase/oracle.go index d487b1bae9aa..e1744c5a309e 100644 --- a/pkg/fanal/analyzer/os/redhatbase/oracle.go +++ b/pkg/fanal/analyzer/os/redhatbase/oracle.go @@ -56,3 +56,8 @@ func (a oracleOSAnalyzer) Type() analyzer.Type { func (a oracleOSAnalyzer) Version() int { return oracleAnalyzerVersion } + +// StaticPaths returns the static paths of the oracle analyzer +func (a oracleOSAnalyzer) StaticPaths() []string { + return a.requiredFiles() +} diff --git a/pkg/fanal/analyzer/os/redhatbase/redhatbase.go b/pkg/fanal/analyzer/os/redhatbase/redhatbase.go index f3a9df357bab..4a134b19de4a 100644 --- a/pkg/fanal/analyzer/os/redhatbase/redhatbase.go +++ b/pkg/fanal/analyzer/os/redhatbase/redhatbase.go @@ -3,14 +3,17 @@ package redhatbase import ( "bufio" "context" + "fmt" "io" "os" "regexp" "slices" + "strconv" "strings" "golang.org/x/xerrors" + "github.com/aquasecurity/go-version/pkg/semver" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" fos "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os" "github.com/aquasecurity/trivy/pkg/fanal/types" @@ -72,6 +75,44 @@ func (a redhatOSAnalyzer) parseRelease(r io.Reader) (types.OS, error) { Family: types.Fedora, Name: result[2], }, nil + case "red hat enterprise linux coreos": + // https://access.redhat.com/articles/6907891 + + var major, minor, rel int + + split := strings.SplitN(result[2], ".", 4) + major, _ = strconv.Atoi(split[0]) + if len(split) > 1 { + minor, _ = strconv.Atoi(split[1]) + } + if len(split) > 2 { + rel, _ = strconv.Atoi(split[2]) + } + + coreosVersion, err := semver.Parse(fmt.Sprintf("%d.%d.%d", major, minor, rel)) + if err == nil { + var rhelVersion string + if coreos4_16, _ := semver.Parse("4.16.0"); coreosVersion.GreaterThanOrEqual(coreos4_16) { + rhelVersion = "9.4" + } else if coreos4_13, _ := semver.Parse("4.13.0"); coreosVersion.GreaterThanOrEqual(coreos4_13) { + rhelVersion = "9.2" + } else if coreos4_11, _ := semver.Parse("4.11.0"); coreosVersion.GreaterThanOrEqual(coreos4_11) { + rhelVersion = "8.6" + } else if coreos4_7_24, _ := semver.Parse("4.7.24"); coreosVersion.GreaterThanOrEqual(coreos4_7_24) { + rhelVersion = "8.4" + } else if coreos4_7_0, _ := semver.Parse("4.7.0"); coreosVersion.GreaterThanOrEqual(coreos4_7_0) { + rhelVersion = "8.3" + } else { + rhelVersion = "8.2" + } + + return types.OS{ + Family: types.RedHat, + Name: rhelVersion, + }, nil + } + + fallthrough default: return types.OS{ Family: types.RedHat, @@ -97,3 +138,8 @@ func (a redhatOSAnalyzer) Type() analyzer.Type { func (a redhatOSAnalyzer) Version() int { return redhatAnalyzerVersion } + +// StaticPaths returns the static paths of the redhatbase analyzer +func (a redhatOSAnalyzer) StaticPaths() []string { + return a.requiredFiles() +} diff --git a/pkg/fanal/analyzer/os/redhatbase/rocky.go b/pkg/fanal/analyzer/os/redhatbase/rocky.go index 95043ec2d3c3..8e1360408d48 100644 --- a/pkg/fanal/analyzer/os/redhatbase/rocky.go +++ b/pkg/fanal/analyzer/os/redhatbase/rocky.go @@ -60,3 +60,8 @@ func (a rockyOSAnalyzer) Type() analyzer.Type { func (a rockyOSAnalyzer) Version() int { return rockyAnalyzerVersion } + +// StaticPaths returns the static paths of the rocky analyzer +func (a rockyOSAnalyzer) StaticPaths() []string { + return a.requiredFiles() +} diff --git a/pkg/fanal/analyzer/os/release/release.go b/pkg/fanal/analyzer/os/release/release.go index 67a18714ac1a..9e40758ff90c 100644 --- a/pkg/fanal/analyzer/os/release/release.go +++ b/pkg/fanal/analyzer/os/release/release.go @@ -20,6 +20,8 @@ const version = 1 var requiredFiles = []string{ "etc/os-release", "usr/lib/os-release", + "aarch64-bottlerocket-linux-gnu/sys-root/usr/lib/os-release", + "x86_64-bottlerocket-linux-gnu/sys-root/usr/lib/os-release", } type osReleaseAnalyzer struct{} @@ -49,6 +51,8 @@ func (a osReleaseAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInp switch id { case "alpine": family = types.Alpine + case "bottlerocket": + family = types.Bottlerocket case "opensuse-tumbleweed": family = types.OpenSUSETumbleweed case "opensuse-leap", "opensuse": // opensuse for leap:42, opensuse-leap for leap:15 @@ -96,3 +100,8 @@ func (a osReleaseAnalyzer) Type() analyzer.Type { func (a osReleaseAnalyzer) Version() int { return version } + +// StaticPaths returns the static paths of the os-release analyzer +func (a osReleaseAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/analyzer/os/release/release_test.go b/pkg/fanal/analyzer/os/release/release_test.go index 532e01d9efef..5c00b9d5326d 100644 --- a/pkg/fanal/analyzer/os/release/release_test.go +++ b/pkg/fanal/analyzer/os/release/release_test.go @@ -150,6 +150,16 @@ func Test_osReleaseAnalyzer_Analyze(t *testing.T) { }, }, }, + { + name: "Bottlerocket", + inputFile: "testdata/bottlerocket", + want: &analyzer.AnalysisResult{ + OS: types.OS{ + Family: types.Bottlerocket, + Name: "1.34.0", + }, + }, + }, { name: "Unknown OS", inputFile: "testdata/unknown", diff --git a/pkg/fanal/analyzer/os/release/testdata/bottlerocket b/pkg/fanal/analyzer/os/release/testdata/bottlerocket new file mode 100644 index 000000000000..3bde7c6b6e3e --- /dev/null +++ b/pkg/fanal/analyzer/os/release/testdata/bottlerocket @@ -0,0 +1,11 @@ +NAME=Bottlerocket +ID=bottlerocket +VERSION="1.34.0 (aws-ecs-2)" +PRETTY_NAME="Bottlerocket OS 1.34.0 (aws-ecs-2)" +VARIANT_ID=aws-ecs-2 +VERSION_ID=1.34.0 +BUILD_ID=18d04e52 +HOME_URL="https://github.com/bottlerocket-os/bottlerocket" +SUPPORT_URL="https://github.com/bottlerocket-os/bottlerocket/discussions" +BUG_REPORT_URL="https://github.com/bottlerocket-os/bottlerocket/issues" +DOCUMENTATION_URL="https://bottlerocket.dev" diff --git a/pkg/fanal/analyzer/os/ubuntu/esm.go b/pkg/fanal/analyzer/os/ubuntu/esm.go index d6fa38d30c16..21961f2d2cea 100644 --- a/pkg/fanal/analyzer/os/ubuntu/esm.go +++ b/pkg/fanal/analyzer/os/ubuntu/esm.go @@ -59,6 +59,11 @@ func (a ubuntuESMAnalyzer) Version() int { return ESMAnalyzerVersion } +// StaticPaths returns the static paths of the ubuntu ESM analyzer +func (a ubuntuESMAnalyzer) StaticPaths() []string { + return ESMRequiredFiles +} + // structs to parse ESM status type status struct { Services []service `json:"services"` diff --git a/pkg/fanal/analyzer/os/ubuntu/ubuntu.go b/pkg/fanal/analyzer/os/ubuntu/ubuntu.go index 75a24756365d..9cc9cbb84c0b 100644 --- a/pkg/fanal/analyzer/os/ubuntu/ubuntu.go +++ b/pkg/fanal/analyzer/os/ubuntu/ubuntu.go @@ -62,3 +62,8 @@ func (a ubuntuOSAnalyzer) Type() analyzer.Type { func (a ubuntuOSAnalyzer) Version() int { return version } + +// StaticPaths returns the static paths of the ubuntu analyzer +func (a ubuntuOSAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/analyzer/pkg/apk/apk.go b/pkg/fanal/analyzer/pkg/apk/apk.go index 69cc72eb5d3f..a8fdf8ce5329 100644 --- a/pkg/fanal/analyzer/pkg/apk/apk.go +++ b/pkg/fanal/analyzer/pkg/apk/apk.go @@ -209,6 +209,11 @@ func (a alpinePkgAnalyzer) Version() int { return analyzerVersion } +// StaticPaths returns a list of static file paths to analyze +func (a alpinePkgAnalyzer) StaticPaths() []string { + return requiredFiles +} + // decodeChecksumLine decodes checksum line func (a alpinePkgAnalyzer) decodeChecksumLine(ctx context.Context, line string) digest.Digest { if len(line) < 2 { diff --git a/pkg/fanal/analyzer/pkg/bottlerocket_inventory/bottlerocket_inventory.go b/pkg/fanal/analyzer/pkg/bottlerocket_inventory/bottlerocket_inventory.go new file mode 100644 index 000000000000..ec83b1f4ac8f --- /dev/null +++ b/pkg/fanal/analyzer/pkg/bottlerocket_inventory/bottlerocket_inventory.go @@ -0,0 +1,110 @@ +package bottlerocket_inventory + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "slices" + "strconv" + "time" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func init() { + analyzer.RegisterAnalyzer(newBottlerocketInventoryAnalyzer()) +} + +const analyzerVersion = 1 + +var requiredFiles = []string{ + "aarch64-bottlerocket-linux-gnu/sys-root/usr/share/bottlerocket/application-inventory.json", + "x86_64-bottlerocket-linux-gnu/sys-root/usr/share/bottlerocket/application-inventory.json", +} + +type bottlerocketInventoryAnalyzer struct{} + +func newBottlerocketInventoryAnalyzer() *bottlerocketInventoryAnalyzer { + return &bottlerocketInventoryAnalyzer{} +} + +type ApplicationInventory struct { + Content []struct { + Name string `json:"Name"` + Publisher string `json:"Publisher"` + Version string `json:"Version"` + Release string `json:"Release"` + Epoch string `json:"Epoch"` + InstalledTime time.Time `json:"InstalledTime"` + ApplicationType string `json:"ApplicationType"` + Architecture string `json:"Architecture"` + URL string `json:"Url"` + Summary string `json:"Summary"` + } `json:"Content"` +} + +func (a bottlerocketInventoryAnalyzer) Analyze(ctx context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { + parsedInventorys, err := a.parseApplicationInventory(ctx, input.Content) + if err != nil { + return nil, err + } + + return &analyzer.AnalysisResult{ + PackageInfos: []types.PackageInfo{ + { + FilePath: input.FilePath, + Packages: parsedInventorys, + }, + }, + }, nil +} + +func (a bottlerocketInventoryAnalyzer) parseApplicationInventory(_ context.Context, r io.Reader) ([]types.Package, error) { + var pkgs []types.Package + + var applicationInventory ApplicationInventory + if err := json.NewDecoder(r).Decode(&applicationInventory); err != nil { + return nil, err + } + + for _, app := range applicationInventory.Content { + epoch, err := strconv.Atoi(app.Epoch) + if err != nil { + return nil, err + } + pkg := types.Package{ + Arch: app.Architecture, + Epoch: epoch, + Name: app.Name, + Version: app.Version, + } + + if pkg.Name != "" && pkg.Version != "" { + pkg.ID = fmt.Sprintf("%s@%s", pkg.Name, pkg.Version) + } + + pkgs = append(pkgs, pkg) + } + + return pkgs, nil +} + +func (a bottlerocketInventoryAnalyzer) Required(filePath string, _ os.FileInfo) bool { + return slices.Contains(requiredFiles, filePath) +} + +func (a bottlerocketInventoryAnalyzer) Type() analyzer.Type { + return analyzer.TypeBottlerocketInventory +} + +func (a bottlerocketInventoryAnalyzer) Version() int { + return analyzerVersion +} + +// StaticPaths returns a list of static file paths to analyze +func (a bottlerocketInventoryAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/analyzer/pkg/bottlerocket_inventory/bottlerocket_inventory_test.go b/pkg/fanal/analyzer/pkg/bottlerocket_inventory/bottlerocket_inventory_test.go new file mode 100644 index 000000000000..8e570303326c --- /dev/null +++ b/pkg/fanal/analyzer/pkg/bottlerocket_inventory/bottlerocket_inventory_test.go @@ -0,0 +1,62 @@ +package bottlerocket_inventory + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +var pkgs = []types.Package{ + { + ID: "glibc@2.40", + Name: "glibc", + Version: "2.40", + Epoch: 1, + Arch: "x86_64", + }, + { + ID: "kernel-6.1@6.1.128", + Name: "kernel-6.1", + Version: "6.1.128", + Epoch: 0, + Arch: "x86_64", + }, + { + ID: "systemd@252.22", + Name: "systemd", + Version: "252.22", + Epoch: 0, + Arch: "x86_64", + }, +} + +func TestParseApplicationInventory(t *testing.T) { + var tests = []struct { + name string + path string + wantPkgs []types.Package + }{ + { + name: "happy path", + path: "./testdata/application-inventory.json", + wantPkgs: pkgs, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := bottlerocketInventoryAnalyzer{} + f, err := os.Open(tt.path) + require.NoError(t, err) + defer f.Close() + gotPkgs, err := a.parseApplicationInventory(t.Context(), f) + require.NoError(t, err) + + assert.Equal(t, tt.wantPkgs, gotPkgs) + }) + } +} diff --git a/pkg/fanal/analyzer/pkg/bottlerocket_inventory/testdata/application-inventory.json b/pkg/fanal/analyzer/pkg/bottlerocket_inventory/testdata/application-inventory.json new file mode 100644 index 000000000000..3183c4e1e186 --- /dev/null +++ b/pkg/fanal/analyzer/pkg/bottlerocket_inventory/testdata/application-inventory.json @@ -0,0 +1,40 @@ +{ + "Content": [ + { + "Name": "glibc", + "Publisher": "bottlerocket-core-kit", + "Version": "2.40", + "Release": "1.1740525475.e3a5862c.br1", + "Epoch": "1", + "InstalledTime": "2025-02-27T20:55:41Z", + "ApplicationType": "Unspecified", + "Architecture": "x86_64", + "Url": "http://www.gnu.org/software/glibc/", + "Summary": "The GNU libc libraries" + }, + { + "Name": "kernel-6.1", + "Publisher": "bottlerocket-kernel-kit", + "Version": "6.1.128", + "Release": "1.1740603423.4d405dc9.br1", + "Epoch": "0", + "InstalledTime": "2025-02-27T20:55:41Z", + "ApplicationType": "Unspecified", + "Architecture": "x86_64", + "Url": "https://www.kernel.org/", + "Summary": "The Linux kernel" + }, + { + "Name": "systemd", + "Publisher": "bottlerocket-core-kit", + "Version": "252.22", + "Release": "1.1740525475.e3a5862c.br1", + "Epoch": "0", + "InstalledTime": "2025-02-27T20:55:41Z", + "ApplicationType": "Unspecified", + "Architecture": "x86_64", + "Url": "https://www.freedesktop.org/wiki/Software/systemd", + "Summary": "System and Service Manager" + } + ] +} diff --git a/pkg/fanal/analyzer/pkg/dpkg/copyright.go b/pkg/fanal/analyzer/pkg/dpkg/copyright.go index ac98c2e404c3..29a5fa8bb3e9 100644 --- a/pkg/fanal/analyzer/pkg/dpkg/copyright.go +++ b/pkg/fanal/analyzer/pkg/dpkg/copyright.go @@ -149,3 +149,7 @@ func normalizeLicense(s string) string { return strings.TrimSpace(s) } + +func (a *dpkgLicenseAnalyzer) StaticPaths() []string { + return []string{"usr/share/doc/"} +} diff --git a/pkg/fanal/analyzer/pkg/dpkg/dpkg.go b/pkg/fanal/analyzer/pkg/dpkg/dpkg.go index 9002af2949ab..5e58046b20c0 100644 --- a/pkg/fanal/analyzer/pkg/dpkg/dpkg.go +++ b/pkg/fanal/analyzer/pkg/dpkg/dpkg.go @@ -54,7 +54,7 @@ var ( dpkgSrcCaptureRegexpNames = dpkgSrcCaptureRegexp.SubexpNames() ) -func (a dpkgAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { +func (a dpkgAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { var systemInstalledFiles []string var packageInfos []types.PackageInfo @@ -64,26 +64,52 @@ func (a dpkgAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis a.logger.Debug("Unable to parse the available file", log.FilePath(availableFile), log.Err(err)) } - required := func(path string, d fs.DirEntry) bool { - return path != availableFile - } - packageFiles := make(map[string][]string) - // parse other files - err = fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { - // parse list files - if a.isListFile(filepath.Split(path)) { - scanner := bufio.NewScanner(r) - systemFiles, err := a.parseDpkgInfoList(scanner) - if err != nil { - return err + // parse list files + err = fsutils.WalkDir(ctx, input.FS, infoDir, fsutils.RequiredExt(".list"), input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { + scanner := bufio.NewScanner(r) + systemFiles, err := a.parseDpkgInfoList(scanner) + if err != nil { + return err + } + packageFiles[strings.TrimSuffix(filepath.Base(path), ".list")] = systemFiles + systemInstalledFiles = append(systemInstalledFiles, systemFiles...) + return nil + }) + if err != nil && !os.IsNotExist(err) { + return nil, xerrors.Errorf("dpkg walk error: %w", err) + } + + // parse root status file + parseRootStatusFile := func() ([]types.PackageInfo, error) { + f, err := input.FS.Open(statusFile) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } + errOpen := fmt.Errorf("failed to open %s: %w", statusFile, err) + if input.Options.WalkErrCallback != nil { + return nil, input.Options.WalkErrCallback(statusFile, errOpen) } - packageFiles[strings.TrimSuffix(filepath.Base(path), ".list")] = systemFiles - systemInstalledFiles = append(systemInstalledFiles, systemFiles...) - return nil + return nil, errOpen } - // parse status files + defer f.Close() + + infos, err := a.parseDpkgStatus(statusFile, f, digests) + if err != nil { + return nil, xerrors.Errorf("dpkg status parse error: %w", err) + } + return infos, nil + } + rootStatusInfos, err := parseRootStatusFile() + if err != nil { + return nil, err + } + packageInfos = append(packageInfos, rootStatusInfos...) + + // parse status files + err = fsutils.WalkDir(ctx, input.FS, statusDir, fsutils.RequiredAll(), input.Options.WalkErrCallback, func(path string, d fs.DirEntry, r io.Reader) error { infos, err := a.parseDpkgStatus(path, r, digests) if err != nil { return err @@ -91,7 +117,7 @@ func (a dpkgAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis packageInfos = append(packageInfos, infos...) return nil }) - if err != nil { + if err != nil && !os.IsNotExist(err) { return nil, xerrors.Errorf("dpkg walk error: %w", err) } @@ -185,7 +211,7 @@ func (a dpkgAnalyzer) parseDpkgAvailable(fsys fs.FS) (map[string]digest.Digest, return pkgs, nil } -// parseDpkgStatus parses /var/lib/dpkg/status or /var/lib/dpkg/status/* +// parseDpkgStatus parses /var/lib/dpkg/status or /var/lib/dpkg/status.d/* func (a dpkgAnalyzer) parseDpkgStatus(filePath string, r io.Reader, digests map[string]digest.Digest) ([]types.PackageInfo, error) { var pkg *types.Package pkgs := make(map[string]*types.Package) @@ -372,3 +398,13 @@ func (a dpkgAnalyzer) Type() analyzer.Type { func (a dpkgAnalyzer) Version() int { return analyzerVersion } + +// StaticPaths returns a list of static file paths to analyze +func (a dpkgAnalyzer) StaticPaths() []string { + return []string{ + statusFile, + availableFile, + statusDir, + infoDir, + } +} diff --git a/pkg/fanal/analyzer/pkg/dpkg/scanner.go b/pkg/fanal/analyzer/pkg/dpkg/scanner.go index d29fe951f652..9cee8d8a1773 100644 --- a/pkg/fanal/analyzer/pkg/dpkg/scanner.go +++ b/pkg/fanal/analyzer/pkg/dpkg/scanner.go @@ -15,9 +15,9 @@ type dpkgScanner struct { func NewScanner(r io.Reader) *dpkgScanner { s := bufio.NewScanner(r) // Package data may exceed default buffer size - // Increase the buffer default size by 2 times - buf := make([]byte, 0, 128*1024) - s.Buffer(buf, 128*1024) + // Increase the buffer default size by 4 times + buf := make([]byte, 0, 256*1024) + s.Buffer(buf, 256*1024) s.Split(emptyLineSplit) return &dpkgScanner{Scanner: s} diff --git a/pkg/fanal/analyzer/pkg/rpm/rpm.go b/pkg/fanal/analyzer/pkg/rpm/rpm.go index 54caec1994b2..515331d7ccf4 100644 --- a/pkg/fanal/analyzer/pkg/rpm/rpm.go +++ b/pkg/fanal/analyzer/pkg/rpm/rpm.go @@ -38,6 +38,7 @@ var ( // SQLite3 "usr/lib/sysimage/rpm/rpmdb.sqlite", + "usr/share/rpm/rpmdb.sqlite", "var/lib/rpm/rpmdb.sqlite", } @@ -208,6 +209,11 @@ func (a rpmPkgAnalyzer) Version() int { return version } +// StaticPaths returns a list of static file paths to analyze +func (a rpmPkgAnalyzer) StaticPaths() []string { + return requiredFiles +} + // splitFileName returns a name, version, release, epoch, arch: // // e.g. diff --git a/pkg/fanal/analyzer/pkg/rpm/rpmqa.go b/pkg/fanal/analyzer/pkg/rpm/rpmqa.go index 55f4feaa9232..68d209655d39 100644 --- a/pkg/fanal/analyzer/pkg/rpm/rpmqa.go +++ b/pkg/fanal/analyzer/pkg/rpm/rpmqa.go @@ -92,3 +92,7 @@ func (a rpmqaPkgAnalyzer) Type() analyzer.Type { func (a rpmqaPkgAnalyzer) Version() int { return versionRpmqa } + +func (a rpmqaPkgAnalyzer) StaticPaths() []string { + return requiredRpmqaFiles +} diff --git a/pkg/fanal/analyzer/repo/apk/apk.go b/pkg/fanal/analyzer/repo/apk/apk.go index cdbc23d5e076..c7538d538158 100644 --- a/pkg/fanal/analyzer/repo/apk/apk.go +++ b/pkg/fanal/analyzer/repo/apk/apk.go @@ -96,3 +96,7 @@ func (a apkRepoAnalyzer) Type() analyzer.Type { func (a apkRepoAnalyzer) Version() int { return version } + +func (a apkRepoAnalyzer) StaticPaths() []string { + return requiredFiles +} diff --git a/pkg/fanal/artifact/artifact.go b/pkg/fanal/artifact/artifact.go index 6dfa81fa735c..3d58cc57e7f2 100644 --- a/pkg/fanal/artifact/artifact.go +++ b/pkg/fanal/artifact/artifact.go @@ -3,33 +3,39 @@ package artifact import ( "context" "sort" + "time" - "github.com/google/go-containerregistry/pkg/v1" + v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/fanal/walker" "github.com/aquasecurity/trivy/pkg/misconf" "github.com/aquasecurity/trivy/pkg/sbom/core" + "github.com/aquasecurity/trivy/pkg/utils/fsutils" ) type Option struct { - Type Type - AnalyzerGroup analyzer.Group // It is empty in OSS - DisabledAnalyzers []analyzer.Type - DisabledHandlers []types.HandlerType - FilePatterns []string - Parallel int - NoProgress bool - Insecure bool - Offline bool - AppDirs []string - SBOMSources []string - RekorURL string - AWSRegion string - AWSEndpoint string - FileChecksum bool // For SPDX - DetectionPriority types.DetectionPriority + Type Type + AnalyzerGroup analyzer.Group // It is empty in OSS + DisabledAnalyzers []analyzer.Type + DisabledHandlers []types.HandlerType + FilePatterns []string + Parallel int + NoProgress bool + Insecure bool + Offline bool + OfflineJar bool + AppDirs []string + SBOMSources []string + RekorURL string + AWSRegion string + AWSEndpoint string + FileChecksum bool // For SPDX + FileChecksumJar bool + DetectionPriority types.DetectionPriority + AnalyzerTimeout time.Duration + PostAnalyzerTimeout time.Duration // Original is the original target location, e.g. "github.com/aquasecurity/trivy" // Currently, it is used only for remote git repositories @@ -78,9 +84,17 @@ func (o *Option) Sort() { }) sort.Strings(o.WalkerOption.SkipFiles) sort.Strings(o.WalkerOption.SkipDirs) + sort.Strings(o.WalkerOption.OnlyDirs) sort.Strings(o.FilePatterns) } +func (o *Option) GetWalkerErrorCallback() walker.ErrorCallback { + if o == nil || o.WalkerOption.ErrorCallback == nil { + return fsutils.DefaultWalkErrorCallback + } + return o.WalkerOption.ErrorCallback +} + type Artifact interface { Inspect(ctx context.Context) (reference Reference, err error) Clean(reference Reference) error diff --git a/pkg/fanal/artifact/container/container.go b/pkg/fanal/artifact/container/container.go new file mode 100644 index 000000000000..c3f434919ae3 --- /dev/null +++ b/pkg/fanal/artifact/container/container.go @@ -0,0 +1,467 @@ +// Should stay in sync with github.com/aquasecurity/trivy/pkg/fanal/artifact/image/image.go. + +package local + +import ( + "context" + "errors" + "os" + "path" + "path/filepath" + "slices" + "strings" + "sync" + + "github.com/containerd/continuity/devices" + "github.com/containerd/continuity/sysx" + "golang.org/x/sys/unix" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/wire" + "github.com/samber/lo" + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/cache" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/artifact" + "github.com/aquasecurity/trivy/pkg/fanal/handler" + imageutils "github.com/aquasecurity/trivy/pkg/fanal/image/utils" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/fanal/walker" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/parallel" + "github.com/aquasecurity/trivy/pkg/semaphore" +) + +var ( + ArtifactSet = wire.NewSet( + walker.NewFS, + wire.Bind(new(Walker), new(*walker.FS)), + NewArtifact, + ) + _ Walker = (*walker.FS)(nil) +) + +type Walker interface { + Walk(ctx context.Context, root string, opt walker.Option, fn walker.WalkFunc) error +} + +type Artifact struct { + logger *log.Logger + container types.Container + cache cache.ArtifactCache + walker Walker + analyzer analyzer.AnalyzerGroup + configAnalyzer analyzer.ConfigAnalyzerGroup // analyzer for container image config + handlerManager handler.Manager + walkerOption walker.Option + + artifactOption artifact.Option +} + +type LayerInfo struct { + DiffID string + Path string + CreatedBy string // can be empty +} + +func NewArtifact(ctr types.Container, c cache.ArtifactCache, w Walker, opt artifact.Option) (artifact.Artifact, error) { + handlerManager, err := handler.NewManager(opt) + if err != nil { + return nil, xerrors.Errorf("handler init error: %w", err) + } + + a, err := analyzer.NewAnalyzerGroup(opt.AnalyzerOptions()) + if err != nil { + return nil, xerrors.Errorf("analyzer group error: %w", err) + } + + artifact := Artifact{ + logger: log.WithPrefix("container"), + container: ctr, + cache: c, + walker: w, + analyzer: a, + handlerManager: handlerManager, + artifactOption: opt, + // keep a copy of walker options to mutate it and not + // make these changes part of the cache key calculations. + walkerOption: opt.WalkerOption, + } + + // Don't skip non-regular files. + artifact.walkerOption.AllFiles = true + + return artifact, nil +} + +func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { + imageID, err := a.container.ID() + if err != nil { + return artifact.Reference{}, xerrors.Errorf("unable to get the image ID: %w", err) + } + a.logger.Debug("Detected image ID", log.String("image_id", imageID)) + + configFile, err := a.container.ConfigFile() + if err != nil { + return artifact.Reference{}, xerrors.Errorf("unable to get the image's config file: %w", err) + } + + diffIDs := a.diffIDs(configFile) + a.logger.Debug("Detected diff ID", log.Any("diff_ids", diffIDs)) + + // Try to detect base layers. + baseDiffIDs := a.guessBaseLayers(diffIDs, configFile) + a.logger.Debug("Detected base layers", log.Any("diff_ids", baseDiffIDs)) + + // Convert image ID and layer IDs to cache keys + imageKey, layerKeys, err := a.calcCacheKeys(imageID, diffIDs) + if err != nil { + return artifact.Reference{}, err + } + + // Parse histories and extract a list of "created_by" + layerKeyMap := a.consolidateCreatedBy(diffIDs, layerKeys, configFile) + + missingImage, missingLayers, err := a.cache.MissingBlobs(imageKey, layerKeys) + if err != nil { + return artifact.Reference{}, xerrors.Errorf("unable to get missing layers: %w", err) + } + + missingImageKey := imageKey + if missingImage { + a.logger.Debug("Missing image ID in cache", log.String("image_id", imageID)) + } else { + missingImageKey = "" + } + + if err = a.inspect(ctx, missingImageKey, missingLayers, baseDiffIDs, layerKeyMap, configFile); err != nil { + return artifact.Reference{}, xerrors.Errorf("analyze error: %w", err) + } + + return artifact.Reference{ + Name: a.container.Name(), + Type: artifact.TypeContainerImage, + ID: imageKey, + BlobIDs: layerKeys, + ImageMetadata: artifact.ImageMetadata{ + ID: imageID, + DiffIDs: diffIDs, + RepoTags: a.container.RepoTags(), + RepoDigests: a.container.RepoDigests(), + ConfigFile: *configFile, + }, + }, nil +} + +func (Artifact) Clean(_ artifact.Reference) error { + return nil +} + +func (a Artifact) calcCacheKeys(imageID string, diffIDs []string) (string, []string, error) { + // Pass an empty config scanner option so that the cache key can be the same, even when policies are updated. + imageKey, err := cache.CalcKey(imageID, a.analyzer.AnalyzerVersions(), nil, artifact.Option{}) + if err != nil { + return "", nil, err + } + + hookVersions := a.handlerManager.Versions() + var layerKeys []string + for _, diffID := range diffIDs { + blobKey, err := cache.CalcKey(diffID, a.analyzer.AnalyzerVersions(), hookVersions, a.artifactOption) + if err != nil { + return "", nil, err + } + layerKeys = append(layerKeys, blobKey) + } + return imageKey, layerKeys, nil +} + +func (a Artifact) consolidateCreatedBy(diffIDs, layerKeys []string, configFile *v1.ConfigFile) map[string]LayerInfo { + // save createdBy fields in order of layers + var createdBy []string + for _, h := range configFile.History { + // skip histories for empty layers + if h.EmptyLayer { + continue + } + c := strings.TrimPrefix(h.CreatedBy, "/bin/sh -c ") + c = strings.TrimPrefix(c, "#(nop) ") + createdBy = append(createdBy, c) + } + + // If history detected incorrect - use only diffID + // TODO: our current logic may not detect empty layers correctly in rare cases. + validCreatedBy := len(diffIDs) == len(createdBy) + + layerKeyMap := make(map[string]LayerInfo) + for i, diffID := range diffIDs { + c := "" + if validCreatedBy { + c = createdBy[i] + } + + layerKey := layerKeys[i] + path := "" + for _, layer := range a.container.Layers() { + if diffID == layer.DiffID { + path = layer.Path + } + } + layerKeyMap[layerKey] = LayerInfo{ + Path: path, + DiffID: diffID, + CreatedBy: c, + } + } + return layerKeyMap +} + +func (a Artifact) inspect(ctx context.Context, missingImage string, layerKeys, baseDiffIDs []string, + layerKeyMap map[string]LayerInfo, configFile *v1.ConfigFile) error { + + var osFound types.OS + p := parallel.NewPipeline(a.artifactOption.Parallel, false, layerKeys, func(ctx context.Context, + layerKey string) (any, error) { + layer := layerKeyMap[layerKey] + + // If it is a base layer, secret scanning should not be performed. + var disabledAnalyzers []analyzer.Type + if slices.Contains(baseDiffIDs, layer.DiffID) { + disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeSecret) + } + + layerInfo, err := a.inspectLayer(ctx, layer, disabledAnalyzers) + if err != nil { + return nil, xerrors.Errorf("failed to analyze layer (%s): %w", layer.DiffID, err) + } + if err = a.cache.PutBlob(layerKey, layerInfo); err != nil { + return nil, xerrors.Errorf("failed to store layer: %s in cache: %w", layerKey, err) + } + if lo.IsNotEmpty(layerInfo.OS) { + osFound = layerInfo.OS + } + return nil, nil + + }, nil) + + if err := p.Do(ctx); err != nil { + return xerrors.Errorf("pipeline error: %w", err) + } + + if missingImage != "" { + if err := a.inspectConfig(ctx, missingImage, osFound, configFile); err != nil { + return xerrors.Errorf("unable to analyze config: %w", err) + } + } + + return nil +} + +func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disabled []analyzer.Type) (types.BlobInfo, error) { + a.logger.Debug("Missing diff ID in cache", log.String("diff_id", layerInfo.DiffID)) + + layerDigest, err := a.uncompressedLayer(layerInfo.DiffID) + if err != nil { + return types.BlobInfo{}, xerrors.Errorf("unable to get uncompressed layer %s: %w", layerInfo.DiffID, err) + } + + // Prepare variables + var wg sync.WaitGroup + opts := analyzer.AnalysisOptions{ + Offline: a.artifactOption.Offline, + OfflineJar: a.artifactOption.OfflineJar, + FileChecksum: a.artifactOption.FileChecksum, + FileChecksumJar: a.artifactOption.FileChecksumJar, + WalkErrCallback: a.artifactOption.GetWalkerErrorCallback(), + PostAnalyzerTimeout: a.artifactOption.PostAnalyzerTimeout, + } + result := analyzer.NewAnalysisResult() + limit := semaphore.New(a.artifactOption.Parallel) + + // Prepare filesystem for post analysis + composite, err := a.analyzer.PostAnalyzerFS() + if err != nil { + return types.BlobInfo{}, xerrors.Errorf("unable to get post analysis filesystem: %w", err) + } + defer composite.Cleanup() + + whFiles := make([]string, 0) + opqDirs := make([]string, 0) + + err = a.walker.Walk(ctx, layerInfo.Path, a.walkerOption, func(ctx context.Context, filePath string, info os.FileInfo, opener analyzer.Opener) error { + dir := layerInfo.Path + + // When the directory is the same as the filePath, a file was given + // instead of a directory, rewrite the file path and directory in this case. + if filePath == "." { + dir, filePath = path.Split(layerInfo.Path) + } + + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + // Skip symbolic links, since they may point to files located in another layer. + return nil + } + + if info.Mode()&os.ModeCharDevice != 0 { + maj, min, err := devices.DeviceInfo(info) + if err != nil { + return xerrors.Errorf("DeviceInfo: %w", err) + } + if maj == 0 && min == 0 { + whFiles = append(whFiles, filePath) + return nil + } + } + + if info.Mode()&os.ModeDir != 0 { + xattrs := []string{ + "user.overlay.opaque", + "trusted.overlay.opaque", + } + for _, xattr := range xattrs { + opaque, err := sysx.LGetxattr(filePath, xattr) + if err != nil { + // ENODATA means the xattr is not set — not an error. + if errors.Is(err, unix.ENODATA) { + continue + } + return xerrors.Errorf("Lgetattr: %w", err) + } + if len(opaque) == 1 && opaque[0] == 'y' { + opqDirs = append(opqDirs, filePath) + return nil + } + } + } + + if err := a.analyzer.AnalyzeFile(ctx, &wg, limit, result, dir, filePath, info, opener, nil, opts); err != nil { + return xerrors.Errorf("analyze file (%s): %w", filePath, err) + } + + // Skip post analysis if the file is not required + analyzerTypes := a.analyzer.RequiredPostAnalyzers(filePath, info) + if len(analyzerTypes) == 0 { + return nil + } + + // Build filesystem for post analysis + if err := composite.CreateLink(analyzerTypes, dir, filePath, filepath.Join(dir, filePath)); err != nil { + return xerrors.Errorf("failed to create link: %w", err) + } + + return nil + }) + if err != nil { + return types.BlobInfo{}, xerrors.Errorf("walk error: %w", err) + } + + // Wait for all the goroutine to finish. + wg.Wait() + + // Post-analysis + if err = a.analyzer.PostAnalyze(ctx, composite, result, opts); err != nil { + return types.BlobInfo{}, xerrors.Errorf("post analysis error: %w", err) + } + + // Sort the analysis result for consistent results + result.Sort() + + blobInfo := types.BlobInfo{ + SchemaVersion: types.BlobJSONSchemaVersion, + Digest: layerDigest, + DiffID: layerInfo.DiffID, + CreatedBy: layerInfo.CreatedBy, + OpaqueDirs: opqDirs, + WhiteoutFiles: whFiles, + OS: result.OS, + Repository: result.Repository, + PackageInfos: result.PackageInfos, + Applications: result.Applications, + Misconfigurations: result.Misconfigurations, + Secrets: result.Secrets, + Licenses: result.Licenses, + CustomResources: result.CustomResources, + + // For Red Hat + BuildInfo: result.BuildInfo, + } + + // Call post handlers to modify blob info + if err = a.handlerManager.PostHandle(ctx, result, &blobInfo); err != nil { + return types.BlobInfo{}, xerrors.Errorf("post handler error: %w", err) + } + + return blobInfo, nil +} + +func (a Artifact) diffIDs(configFile *v1.ConfigFile) []string { + if configFile == nil { + return nil + } + return lo.Map(configFile.RootFS.DiffIDs, func(diffID v1.Hash, _ int) string { + return diffID.String() + }) +} + +func (a Artifact) uncompressedLayer(diffID string) (string, error) { + layer, err := a.container.LayerByDiffID(diffID) + if err != nil { + return "", err + } + return layer.Digest, nil +} + +func (a Artifact) inspectConfig(ctx context.Context, imageID string, osFound types.OS, config *v1.ConfigFile) error { + result := lo.FromPtr(a.configAnalyzer.AnalyzeImageConfig(ctx, osFound, config)) + + info := types.ArtifactInfo{ + SchemaVersion: types.ArtifactJSONSchemaVersion, + Architecture: config.Architecture, + Created: config.Created.Time, + DockerVersion: config.DockerVersion, + OS: config.OS, + Misconfiguration: result.Misconfiguration, + Secret: result.Secret, + HistoryPackages: result.HistoryPackages, + } + + if err := a.cache.PutArtifact(imageID, info); err != nil { + return xerrors.Errorf("failed to put image info into the cache: %w", err) + } + + return nil +} + +// guessBaseLayers guesses layers in base image (call base layers). +func (a Artifact) guessBaseLayers(diffIDs []string, configFile *v1.ConfigFile) []string { + if configFile == nil { + return nil + } + + baseImageIndex := imageutils.GuessBaseImageIndex(configFile.History) + if baseImageIndex < 0 { + baseImageIndex = 0 + } + + // Diff IDs don't include empty layers, so the index is different from histories + var diffIDIndex int + var baseDiffIDs []string + for i, h := range configFile.History { + // It is no longer base layer. + if i > baseImageIndex { + break + } + // Empty layers are not included in diff IDs. + if h.EmptyLayer { + continue + } + + if diffIDIndex >= len(diffIDs) { + // something wrong... + return nil + } + baseDiffIDs = append(baseDiffIDs, diffIDs[diffIDIndex]) + diffIDIndex++ + } + return baseDiffIDs +} diff --git a/pkg/fanal/artifact/image/image.go b/pkg/fanal/artifact/image/image.go index cfded3028c78..6df42aed78b1 100644 --- a/pkg/fanal/artifact/image/image.go +++ b/pkg/fanal/artifact/image/image.go @@ -2,7 +2,6 @@ package image import ( "context" - "errors" "fmt" "io" "os" @@ -21,7 +20,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/handler" - "github.com/aquasecurity/trivy/pkg/fanal/image" + imageutils "github.com/aquasecurity/trivy/pkg/fanal/image/utils" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/fanal/walker" "github.com/aquasecurity/trivy/pkg/log" @@ -66,11 +65,6 @@ func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (a return nil, xerrors.Errorf("config analyzer group error: %w", err) } - cacheDir, err := os.MkdirTemp("", "layers") - if err != nil { - return nil, xerrors.Errorf("failed to create a cache layers temp dir: %w", err) - } - return Artifact{ logger: log.WithPrefix("image"), image: img, @@ -81,7 +75,6 @@ func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (a handlerManager: handlerManager, artifactOption: opt, - layerCacheDir: cacheDir, }, nil } @@ -100,24 +93,6 @@ func (a Artifact) Inspect(ctx context.Context) (ref artifact.Reference, err erro diffIDs := a.diffIDs(configFile) a.logger.Debug("Detected diff ID", log.Any("diff_ids", diffIDs)) - defer func() { - if rerr := os.RemoveAll(a.layerCacheDir); rerr != nil { - log.Error("Failed to remove layer cache", log.Err(rerr)) - } - }() - if err := a.checkImageSize(ctx, diffIDs); err != nil { - return artifact.Reference{}, err - } - - // Try retrieving a remote SBOM document - if res, err := a.retrieveRemoteSBOM(ctx); err == nil { - // Found SBOM - return res, nil - } else if !errors.Is(err, errNoSBOMFound) { - // Fail on unexpected error, otherwise it falls into the usual scanning. - return artifact.Reference{}, xerrors.Errorf("remote SBOM fetching error: %w", err) - } - // Try to detect base layers. baseDiffIDs := a.guessBaseLayers(diffIDs, configFile) a.logger.Debug("Detected base layers", log.Any("diff_ids", baseDiffIDs)) @@ -368,8 +343,12 @@ func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disable // Prepare variables var wg sync.WaitGroup opts := analyzer.AnalysisOptions{ - Offline: a.artifactOption.Offline, - FileChecksum: a.artifactOption.FileChecksum, + Offline: a.artifactOption.Offline, + OfflineJar: a.artifactOption.OfflineJar, + FileChecksum: a.artifactOption.FileChecksum, + FileChecksumJar: a.artifactOption.FileChecksumJar, + WalkErrCallback: a.artifactOption.GetWalkerErrorCallback(), + PostAnalyzerTimeout: a.artifactOption.PostAnalyzerTimeout, } result := analyzer.NewAnalysisResult() limit := semaphore.New(a.artifactOption.Parallel) @@ -382,7 +361,7 @@ func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disable defer composite.Cleanup() // Walk a tar layer - opqDirs, whFiles, err := a.walker.Walk(rc, func(filePath string, info os.FileInfo, opener analyzer.Opener) error { + opqDirs, whFiles, err := a.walker.Walk(ctx, rc, func(ctx context.Context, filePath string, info os.FileInfo, opener analyzer.Opener) error { if err = a.analyzer.AnalyzeFile(ctx, &wg, limit, result, "", filePath, info, opener, disabled, opts); err != nil { return xerrors.Errorf("failed to analyze %s: %w", filePath, err) } @@ -524,7 +503,7 @@ func (a Artifact) guessBaseLayers(diffIDs []string, configFile *v1.ConfigFile) [ return nil } - baseImageIndex := image.GuessBaseImageIndex(configFile.History) + baseImageIndex := imageutils.GuessBaseImageIndex(configFile.History) // Diff IDs don't include empty layers, so the index is different from histories var diffIDIndex int diff --git a/pkg/fanal/artifact/image/remote_sbom.go b/pkg/fanal/artifact/image/remote_sbom.go deleted file mode 100644 index d07d9cafe3ac..000000000000 --- a/pkg/fanal/artifact/image/remote_sbom.go +++ /dev/null @@ -1,193 +0,0 @@ -package image - -import ( - "context" - "errors" - "fmt" - "os" - "path/filepath" - "slices" - - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/samber/lo" - "golang.org/x/xerrors" - - sbomatt "github.com/aquasecurity/trivy/pkg/attestation/sbom" - "github.com/aquasecurity/trivy/pkg/fanal/artifact" - "github.com/aquasecurity/trivy/pkg/fanal/artifact/sbom" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/oci" - "github.com/aquasecurity/trivy/pkg/remote" - "github.com/aquasecurity/trivy/pkg/types" -) - -var errNoSBOMFound = xerrors.New("remote SBOM not found") - -type inspectRemoteSBOM func(context.Context) (artifact.Reference, error) - -func (a Artifact) retrieveRemoteSBOM(ctx context.Context) (artifact.Reference, error) { - for _, sbomSource := range a.artifactOption.SBOMSources { - var inspect inspectRemoteSBOM - switch sbomSource { - case types.SBOMSourceOCI: - inspect = a.inspectOCIReferrerSBOM - case types.SBOMSourceRekor: - inspect = a.inspectRekorSBOMAttestation - default: - // Never reach here as the "--sbom-sources" values are validated beforehand - continue - } - - ref, err := inspect(ctx) - if errors.Is(err, errNoSBOMFound) { - // Try the next SBOM source - a.logger.Debug("No SBOM found in the source", log.String("source", sbomSource)) - continue - } else if err != nil { - return artifact.Reference{}, xerrors.Errorf("SBOM searching error: %w", err) - } - return ref, nil - } - return artifact.Reference{}, errNoSBOMFound -} - -func (a Artifact) inspectOCIReferrerSBOM(ctx context.Context) (artifact.Reference, error) { - digest, err := repoDigest(a.image, a.artifactOption.Insecure) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("repo digest error: %w", err) - } - - // Fetch referrers - index, err := remote.Referrers(ctx, digest, a.artifactOption.ImageOption.RegistryOptions) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("unable to fetch referrers: %w", err) - } - manifest, err := index.IndexManifest() - if err != nil { - return artifact.Reference{}, xerrors.Errorf("unable to get manifest: %w", err) - } - for _, m := range lo.FromPtr(manifest).Manifests { - // Unsupported artifact type - if !slices.Contains(oci.SupportedSBOMArtifactTypes, m.ArtifactType) { - continue - } - res, err := a.parseReferrer(ctx, digest.Context().String(), m) - if err != nil { - a.logger.Warn("Error with SBOM via OCI referrers", - log.String("digest", m.Digest.String()), log.Err(err)) - continue - } - return res, nil - } - return artifact.Reference{}, errNoSBOMFound -} - -func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descriptor) (artifact.Reference, error) { - const fileName string = "referrer.sbom" - repoName := fmt.Sprintf("%s@%s", repo, desc.Digest) - - tmpDir, err := os.MkdirTemp("", "trivy-sbom-*") - if err != nil { - return artifact.Reference{}, xerrors.Errorf("mkdir temp error: %w", err) - } - defer os.RemoveAll(tmpDir) - - // Download SBOM to local filesystem - referrer := oci.NewArtifact(repoName, a.artifactOption.ImageOption.RegistryOptions) - if err = referrer.Download(ctx, tmpDir, oci.DownloadOption{ - MediaType: desc.ArtifactType, - Filename: fileName, - Quiet: true, - }); err != nil { - return artifact.Reference{}, xerrors.Errorf("SBOM download error: %w", err) - } - - res, err := a.inspectSBOMFile(ctx, filepath.Join(tmpDir, fileName)) - if err != nil { - return res, xerrors.Errorf("SBOM error: %w", err) - } - - // Found SBOM - a.logger.Info("Found SBOM in the OCI referrers", log.String("type", string(res.Type))) - - return res, nil -} - -func (a Artifact) inspectRekorSBOMAttestation(ctx context.Context) (artifact.Reference, error) { - digest, err := repoDigest(a.image, a.artifactOption.Insecure) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("repo digest error: %w", err) - } - - client, err := sbomatt.NewRekor(a.artifactOption.RekorURL) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("failed to create rekor client: %w", err) - } - - raw, err := client.RetrieveSBOM(ctx, digest.DigestStr()) - if errors.Is(err, sbomatt.ErrNoSBOMAttestation) { - return artifact.Reference{}, errNoSBOMFound - } else if err != nil { - return artifact.Reference{}, xerrors.Errorf("failed to retrieve SBOM attestation: %w", err) - } - - f, err := os.CreateTemp("", "sbom-*") - if err != nil { - return artifact.Reference{}, xerrors.Errorf("failed to create a temporary file: %w", err) - } - defer os.Remove(f.Name()) - - if _, err = f.Write(raw); err != nil { - return artifact.Reference{}, xerrors.Errorf("copy error: %w", err) - } - if err = f.Close(); err != nil { - return artifact.Reference{}, xerrors.Errorf("failed to close %s: %w", f.Name(), err) - } - res, err := a.inspectSBOMFile(ctx, f.Name()) - if err != nil { - return res, xerrors.Errorf("SBOM error: %w", err) - } - - // Found SBOM - a.logger.Info("Found SBOM in Rekor", log.String("type", string(res.Type)), - log.String("url", a.artifactOption.RekorURL)) - - return res, nil -} - -func (a Artifact) inspectSBOMFile(ctx context.Context, filePath string) (artifact.Reference, error) { - ar, err := sbom.NewArtifact(filePath, a.cache, a.artifactOption) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("failed to new artifact: %w", err) - } - - results, err := ar.Inspect(ctx) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("failed to inspect: %w", err) - } - results.Name = a.image.Name() - - return results, nil -} - -func repoDigest(img ftypes.Image, insecure bool) (name.Digest, error) { - repoNameFull := img.Name() - ref, err := name.ParseReference(repoNameFull) - if err != nil { - return name.Digest{}, xerrors.Errorf("image name parse error: %w", err) - } - - for _, rd := range img.RepoDigests() { - opts := lo.Ternary(insecure, []name.Option{name.Insecure}, nil) - digest, err := name.NewDigest(rd, opts...) - if err != nil { - continue - } - if ref.Context().String() == digest.Context().String() { - return digest, nil - } - } - return name.Digest{}, xerrors.Errorf("no repo digest found: %w", errNoSBOMFound) -} diff --git a/pkg/fanal/artifact/image/remote_sbom_test.go b/pkg/fanal/artifact/image/remote_sbom_test.go deleted file mode 100644 index 808ecc72897e..000000000000 --- a/pkg/fanal/artifact/image/remote_sbom_test.go +++ /dev/null @@ -1,321 +0,0 @@ -package image_test - -import ( - "context" - "net/http" - "net/http/httptest" - "net/url" - "os" - "testing" - - v1 "github.com/google/go-containerregistry/pkg/v1" - fakei "github.com/google/go-containerregistry/pkg/v1/fake" - typesv1 "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/package-url/packageurl-go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/cachetest" - "github.com/aquasecurity/trivy/pkg/fanal/artifact" - image2 "github.com/aquasecurity/trivy/pkg/fanal/artifact/image" - "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/rekortest" -) - -func TestMain(m *testing.M) { - log.InitLogger(false, true) - os.Exit(m.Run()) -} - -type fakeImage struct { - name string - repoDigests []string - v1.Image - types.ImageExtension -} - -func (f fakeImage) ID() (string, error) { - return "", nil -} - -func (f fakeImage) Name() string { - return f.name -} - -func (f fakeImage) RepoDigests() []string { - return f.repoDigests -} - -func TestArtifact_InspectRekorAttestation(t *testing.T) { - type fields struct { - imageName string - repoDigests []string - } - tests := []struct { - name string - fields fields - artifactOpt artifact.Option - wantBlobs []cachetest.WantBlob - want artifact.Reference - wantErr string - }{ - { - name: "happy path", - fields: fields{ - imageName: "test/image:10", - repoDigests: []string{ - "test/image@sha256:782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02", - }, - }, - artifactOpt: artifact.Option{ - SBOMSources: []string{"rekor"}, - }, - wantBlobs: []cachetest.WantBlob{ - { - ID: "sha256:066b9998617ffb7dfe0a3219ac5c3efc1008a6223606fcf474e7d5c965e4e8da", - BlobInfo: types.BlobInfo{ - SchemaVersion: types.BlobJSONSchemaVersion, - OS: types.OS{ - Family: "alpine", - Name: "3.16.2", - }, - PackageInfos: []types.PackageInfo{ - { - Packages: types.Packages{ - { - ID: "musl@1.2.3-r0", - Name: "musl", - Version: "1.2.3-r0", - Identifier: types.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.2", - }, - }, - }, - BOMRef: "pkg:apk/alpine/musl@1.2.3-r0?distro=3.16.2", - }, - SrcName: "musl", - SrcVersion: "1.2.3-r0", - Licenses: []string{"MIT"}, - Layer: types.Layer{ - DiffID: "sha256:994393dc58e7931862558d06e46aa2bb17487044f670f310dffe1d24e4d1eec7", - }, - }, - }, - }, - }, - }, - }, - }, - want: artifact.Reference{ - Name: "test/image:10", - Type: artifact.TypeCycloneDX, - ID: "sha256:066b9998617ffb7dfe0a3219ac5c3efc1008a6223606fcf474e7d5c965e4e8da", - BlobIDs: []string{ - "sha256:066b9998617ffb7dfe0a3219ac5c3efc1008a6223606fcf474e7d5c965e4e8da", - }, - }, - }, - { - name: "error", - fields: fields{ - imageName: "test/image:10", - repoDigests: []string{ - "test/image@sha256:123456e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02", - }, - }, - artifactOpt: artifact.Option{ - SBOMSources: []string{"rekor"}, - }, - wantErr: "remote SBOM fetching error", - }, - } - - log.InitLogger(false, true) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ts := rekortest.NewServer(t) - defer ts.Close() - - // Set the testing URL - tt.artifactOpt.RekorURL = ts.URL() - - c := cachetest.NewCache(t, nil) - - fi := &fakei.FakeImage{} - fi.ConfigFileReturns(&v1.ConfigFile{}, nil) - - img := &fakeImage{ - name: tt.fields.imageName, - repoDigests: tt.fields.repoDigests, - Image: fi, - } - a, err := image2.NewArtifact(img, c, tt.artifactOpt) - require.NoError(t, err) - - got, err := a.Inspect(context.Background()) - if tt.wantErr != "" { - assert.ErrorContains(t, err, tt.wantErr) - return - } - defer a.Clean(got) - - require.NoError(t, err, tt.name) - got.BOM = nil - assert.Equal(t, tt.want, got) - - cachetest.AssertBlobs(t, c, tt.wantBlobs) - }) - } -} - -func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/v2": - _, err := w.Write([]byte("ok")) - assert.NoError(t, err) - case "/v2/test/image/referrers/sha256:782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02": - w.Header().Set("Content-Type", string(typesv1.OCIImageIndex)) - http.ServeFile(w, r, "testdata/index.json") - case "/v2/test/image/manifests/sha256:37c89af4907fa0af078aeba12d6f18dc0c63937c010030baaaa88e958f0719a5": - http.ServeFile(w, r, "testdata/manifest.json") - case "/v2/test/image/blobs/sha256:9e05dda2a2dcdd526c9204be8645ae48742861c27f093bf496a6397834acecf2": - http.ServeFile(w, r, "testdata/cyclonedx.json") - } - })) - defer ts.Close() - - u, err := url.Parse(ts.URL) - require.NoError(t, err) - registry := u.Host - - type fields struct { - imageName string - repoDigests []string - } - - tests := []struct { - name string - fields fields - artifactOpt artifact.Option - wantBlobs []cachetest.WantBlob - want artifact.Reference - wantErr string - }{ - { - name: "happy path", - fields: fields{ - imageName: registry + "/test/image:10", - repoDigests: []string{ - registry + "/test/image@sha256:782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02", - }, - }, - artifactOpt: artifact.Option{ - SBOMSources: []string{"oci"}, - }, - wantBlobs: []cachetest.WantBlob{ - { - ID: "sha256:a06ed679a3289fba254040e1ce8f3467fadcc454ee3d0d4720f6978065f56684", - BlobInfo: types.BlobInfo{ - SchemaVersion: types.BlobJSONSchemaVersion, - Applications: []types.Application{ - { - Type: types.GoBinary, - Packages: types.Packages{ - { - ID: "github.com/opencontainers/go-digest@v1.0.0", - Name: "github.com/opencontainers/go-digest", - Version: "v1.0.0", - Identifier: types.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/opencontainers", - Name: "go-digest", - Version: "v1.0.0", - }, - BOMRef: "pkg:golang/github.com/opencontainers/go-digest@v1.0.0", - }, - }, - { - ID: "golang.org/x/sync@v0.1.0", - Name: "golang.org/x/sync", - Version: "v0.1.0", - Identifier: types.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "golang.org/x", - Name: "sync", - Version: "v0.1.0", - }, - BOMRef: "pkg:golang/golang.org/x/sync@v0.1.0", - }, - }, - }, - }, - }, - }, - }, - }, - want: artifact.Reference{ - Name: registry + "/test/image:10", - Type: artifact.TypeCycloneDX, - ID: "sha256:a06ed679a3289fba254040e1ce8f3467fadcc454ee3d0d4720f6978065f56684", - BlobIDs: []string{ - "sha256:a06ed679a3289fba254040e1ce8f3467fadcc454ee3d0d4720f6978065f56684", - }, - }, - }, - { - name: "404", - fields: fields{ - imageName: registry + "/test/image:unknown", - repoDigests: []string{ - registry + "/test/image@sha256:123456e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02", - }, - }, - artifactOpt: artifact.Option{ - SBOMSources: []string{"oci"}, - }, - wantErr: "unable to get manifest", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := cachetest.NewCache(t, nil) - - fi := &fakei.FakeImage{} - fi.ConfigFileReturns(&v1.ConfigFile{}, nil) - - img := &fakeImage{ - name: tt.fields.imageName, - repoDigests: tt.fields.repoDigests, - Image: fi, - } - a, err := image2.NewArtifact(img, c, tt.artifactOpt) - require.NoError(t, err) - - got, err := a.Inspect(context.Background()) - if tt.wantErr != "" { - assert.ErrorContains(t, err, tt.wantErr) - return - } - defer a.Clean(got) - - require.NoError(t, err, tt.name) - got.BOM = nil - assert.Equal(t, tt.want, got) - - cachetest.AssertBlobs(t, c, tt.wantBlobs) - }) - } -} diff --git a/pkg/fanal/artifact/local/fs.go b/pkg/fanal/artifact/local/fs.go index a0f220bbe9cf..54e270298dfa 100644 --- a/pkg/fanal/artifact/local/fs.go +++ b/pkg/fanal/artifact/local/fs.go @@ -5,19 +5,19 @@ import ( "context" "crypto/sha256" "errors" + "io/fs" "os" "path" "path/filepath" "strings" "sync" - "github.com/go-git/go-git/v5" "github.com/google/wire" - "github.com/opencontainers/go-digest" "github.com/samber/lo" "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/cache" + "github.com/aquasecurity/trivy/pkg/digest" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/handler" @@ -25,6 +25,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/walker" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/semaphore" + "github.com/aquasecurity/trivy/pkg/utils/fsutils" "github.com/aquasecurity/trivy/pkg/uuid" ) @@ -39,7 +40,7 @@ var ( ) type Walker interface { - Walk(root string, opt walker.Option, fn walker.WalkFunc) error + Walk(ctx context.Context, root string, opt walker.Option, fn walker.WalkFunc) error } type Artifact struct { @@ -79,21 +80,24 @@ func NewArtifact(rootPath string, c cache.ArtifactCache, w Walker, opt artifact. artifactOption: opt, } - art.logger.Debug("Analyzing...", log.String("root", art.rootPath), - lo.Ternary(opt.Original != "", log.String("original", opt.Original), log.Nil)) - - // Check if the directory is a git repository and clean - if hash, err := gitCommitHash(art.rootPath); err == nil { - art.logger.Debug("Using the latest commit hash for calculating cache key", log.String("commit_hash", hash)) - art.commitHash = hash - } else if !errors.Is(err, git.ErrRepositoryNotExists) { - // Only log if the file path is a git repository - art.logger.Debug("Random cache key will be used", log.Err(err)) - } + /* + art.logger.Debug("Analyzing...", log.String("root", art.rootPath), + lo.Ternary(opt.Original != "", log.String("original", opt.Original), log.Nil)) + + // Check if the directory is a git repository and clean + if hash, err := gitCommitHash(art.rootPath); err == nil { + art.logger.Debug("Using the latest commit hash for calculating cache key", log.String("commit_hash", hash)) + art.commitHash = hash + } else if !errors.Is(err, git.ErrRepositoryNotExists) { + // Only log if the file path is a git repository + art.logger.Debug("Random cache key will be used", log.Err(err)) + } + */ return art, nil } +/* // gitCommitHash returns the latest commit hash if the git repository is clean, otherwise returns an error func gitCommitHash(dir string) (string, error) { repo, err := git.PlainOpen(dir) @@ -125,6 +129,7 @@ func gitCommitHash(dir string) (string, error) { return head.Hash().String(), nil } +*/ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { // Calculate cache key @@ -156,8 +161,13 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { result := analyzer.NewAnalysisResult() limit := semaphore.New(a.artifactOption.Parallel) opts := analyzer.AnalysisOptions{ - Offline: a.artifactOption.Offline, - FileChecksum: a.artifactOption.FileChecksum, + Offline: a.artifactOption.Offline, + OfflineJar: a.artifactOption.OfflineJar, + FileChecksum: a.artifactOption.FileChecksum, + FileChecksumJar: a.artifactOption.FileChecksumJar, + WalkErrCallback: a.artifactOption.GetWalkerErrorCallback(), + AnalyzerTimeout: a.artifactOption.AnalyzerTimeout, + PostAnalyzerTimeout: a.artifactOption.PostAnalyzerTimeout, } // Prepare filesystem for post analysis @@ -167,43 +177,34 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { } defer composite.Cleanup() - err = a.walker.Walk(a.rootPath, a.artifactOption.WalkerOption, func(filePath string, info os.FileInfo, opener analyzer.Opener) error { - dir := a.rootPath - - // When the directory is the same as the filePath, a file was given - // instead of a directory, rewrite the file path and directory in this case. - if filePath == "." { - dir, filePath = path.Split(a.rootPath) - } - - if err := a.analyzer.AnalyzeFile(ctx, &wg, limit, result, dir, filePath, info, opener, nil, opts); err != nil { - return xerrors.Errorf("analyze file (%s): %w", filePath, err) - } - - // Skip post analysis if the file is not required - analyzerTypes := a.analyzer.RequiredPostAnalyzers(filePath, info) - if len(analyzerTypes) == 0 { - return nil + // Use static paths instead of traversing the filesystem when all analyzers implement StaticPathAnalyzer + // so that we can analyze files faster + if paths, canUseStaticPaths := a.analyzer.StaticPaths(a.artifactOption.DisabledAnalyzers); canUseStaticPaths { + // Analyze files in static paths + a.logger.Debug("Analyzing files in static paths") + if err = a.analyzeWithStaticPaths(ctx, &wg, limit, result, composite, opts, paths); err != nil { + return artifact.Reference{}, xerrors.Errorf("analyze with static paths: %w", err) } - - // Build filesystem for post analysis - if err := composite.CreateLink(analyzerTypes, dir, filePath, filepath.Join(dir, filePath)); err != nil { - return xerrors.Errorf("failed to create link: %w", err) + } else { + // Analyze files by traversing the root directory + if err = a.analyzeWithRootDirWithTimeout(ctx, &wg, limit, result, composite, opts); err != nil { + if !errors.Is(err, context.DeadlineExceeded) { + return artifact.Reference{}, xerrors.Errorf("analyze with traversal: %w", err) + } } - - return nil - }) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("walk filesystem: %w", err) } + err1 := err // Wait for all the goroutine to finish. wg.Wait() // Post-analysis if err = a.analyzer.PostAnalyze(ctx, composite, result, opts); err != nil { - return artifact.Reference{}, xerrors.Errorf("post analysis error: %w", err) + if !errors.Is(err, context.DeadlineExceeded) { + return artifact.Reference{}, xerrors.Errorf("post analysis error: %w", err) + } } + err2 := err // Sort the analysis result for consistent results result.Sort() @@ -218,6 +219,9 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { Secrets: result.Secrets, Licenses: result.Licenses, CustomResources: result.CustomResources, + + // For Red Hat + BuildInfo: result.BuildInfo, } if err = a.handlerManager.PostHandle(ctx, result, &blobInfo); err != nil { @@ -243,7 +247,72 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { Type: a.artifactOption.Type, ID: cacheKey, // use a cache key as pseudo artifact ID BlobIDs: []string{cacheKey}, - }, nil + }, errors.Join(err1, err2) +} + +func (a Artifact) analyzeWithRootDir(ctx context.Context, wg *sync.WaitGroup, limit *semaphore.Weighted, + result *analyzer.AnalysisResult, composite *analyzer.CompositeFS, opts analyzer.AnalysisOptions) error { + + root := a.rootPath + relativePath := "" + + // When the root path is a file, rewrite the root path and relative path + if fsutils.FileExists(a.rootPath) { + root, relativePath = path.Split(a.rootPath) + } + return a.analyzeWithTraversal(ctx, root, relativePath, wg, limit, result, composite, opts) +} + +func (a Artifact) analyzeWithRootDirWithTimeout(ctx context.Context, wg *sync.WaitGroup, limit *semaphore.Weighted, + result *analyzer.AnalysisResult, composite *analyzer.CompositeFS, opts analyzer.AnalysisOptions) error { + if opts.AnalyzerTimeout > 0 { + ctx1, cancel := context.WithTimeout(ctx, opts.AnalyzerTimeout) + defer cancel() + ctx = ctx1 + } + return a.analyzeWithRootDir(ctx, wg, limit, result, composite, opts) +} + +// analyzeWithStaticPaths analyzes files using static paths from analyzers +func (a Artifact) analyzeWithStaticPaths(ctx context.Context, wg *sync.WaitGroup, limit *semaphore.Weighted, + result *analyzer.AnalysisResult, composite *analyzer.CompositeFS, opts analyzer.AnalysisOptions, + staticPaths []string) error { + + // Process each static path + for _, relativePath := range staticPaths { + if err := a.analyzeWithTraversal(ctx, a.rootPath, relativePath, wg, limit, result, composite, opts); errors.Is(err, fs.ErrNotExist) { + continue + } else if err != nil { + return xerrors.Errorf("analyze with traversal: %w", err) + } + } + + return nil +} + +// analyzeWithTraversal analyzes files by traversing the entire filesystem +func (a Artifact) analyzeWithTraversal(ctx context.Context, root, relativePath string, wg *sync.WaitGroup, limit *semaphore.Weighted, + result *analyzer.AnalysisResult, composite *analyzer.CompositeFS, opts analyzer.AnalysisOptions) error { + + return a.walker.Walk(ctx, filepath.Join(root, relativePath), a.artifactOption.WalkerOption, func(ctx context.Context, filePath string, info os.FileInfo, opener analyzer.Opener) error { + filePath = path.Join(relativePath, filePath) + if err := a.analyzer.AnalyzeFile(ctx, wg, limit, result, root, filePath, info, opener, nil, opts); err != nil { + return xerrors.Errorf("analyze file (%s): %w", filePath, err) + } + + // Skip post analysis if the file is not required + analyzerTypes := a.analyzer.RequiredPostAnalyzers(filePath, info) + if len(analyzerTypes) == 0 { + return nil + } + + // Build filesystem for post analysis + if err := composite.CreateLink(analyzerTypes, root, filePath, filepath.Join(root, filePath)); err != nil { + return xerrors.Errorf("failed to create link: %w", err) + } + + return nil + }) } func (a Artifact) Clean(reference artifact.Reference) error { diff --git a/pkg/fanal/artifact/local/fs_test.go b/pkg/fanal/artifact/local/fs_test.go index 3f493488f296..992dd6890664 100644 --- a/pkg/fanal/artifact/local/fs_test.go +++ b/pkg/fanal/artifact/local/fs_test.go @@ -2397,3 +2397,126 @@ func TestYAMLConfigScan(t *testing.T) { }) } } + +// recordingWalker wraps an existing walker and records which paths were walked +type recordingWalker struct { + base Walker + walkedRoots []string +} + +func newRecordingWalker(base Walker) *recordingWalker { + return &recordingWalker{ + base: base, + } +} + +func (w *recordingWalker) Walk(root string, option walker.Option, walkFn walker.WalkFunc) error { + w.walkedRoots = append(w.walkedRoots, filepath.ToSlash(root)) + // Call the original walker + return w.base.Walk(root, option, walkFn) +} + +// TestArtifact_AnalysisStrategy tests the different analysis strategies +func TestArtifact_AnalysisStrategy(t *testing.T) { + // Use testdata/alpine directly + testDir := "testdata/alpine" + + tests := []struct { + name string + disabledAnalyzers []analyzer.Type + wantRoots []string + }{ + { + name: "static paths", + disabledAnalyzers: append(analyzer.TypeConfigFiles, analyzer.TypePip, analyzer.TypeSecret), + wantRoots: []string{ + "testdata/alpine/etc/alpine-release", + "testdata/alpine/lib/apk/db/installed", + }, + }, + { + name: "traversing root dir", + wantRoots: []string{ + testDir, // only the root directory is walked + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a new artifact with the recording walker + baseWalker := walker.NewFS() + rw := newRecordingWalker(baseWalker) + + // Create artifact with recording walker + a, err := NewArtifact(testDir, cache.NewMemoryCache(), rw, artifact.Option{ + DisabledAnalyzers: tt.disabledAnalyzers, + }) + require.NoError(t, err) + + // Run the inspection + _, err = a.Inspect(t.Context()) + require.NoError(t, err) + + // Check if the walked roots match the expected roots + assert.ElementsMatch(t, tt.wantRoots, rw.walkedRoots) + }) + } +} + +// TestAnalyzerGroup_StaticPaths tests the StaticPaths method of AnalyzerGroup +func TestAnalyzerGroup_StaticPaths(t *testing.T) { + tests := []struct { + name string + disabledAnalyzers []analyzer.Type + want []string + wantAllStatic bool + }{ + { + name: "all analyzers implement StaticPathAnalyzer", + disabledAnalyzers: append(analyzer.TypeConfigFiles, analyzer.TypePip, analyzer.TypeSecret), + want: []string{ + "lib/apk/db/installed", + "etc/alpine-release", + }, + wantAllStatic: true, + }, + { + name: "some analyzers don't implement StaticPathAnalyzer", + want: []string{}, + wantAllStatic: false, + }, + { + name: "only PostAnalyzers are enabled", + disabledAnalyzers: []analyzer.Type{ + analyzer.TypePip, + analyzer.TypeSecret, + }, + want: []string{}, + wantAllStatic: false, + }, + { + name: "disable all analyzers", + disabledAnalyzers: append(analyzer.TypeConfigFiles, analyzer.TypePip, analyzer.TypeApk, analyzer.TypeAlpine, analyzer.TypeSecret), + want: []string{}, + wantAllStatic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a new analyzer group + a, err := analyzer.NewAnalyzerGroup(analyzer.AnalyzerOptions{}) + require.NoError(t, err) + + // Get static paths + gotPaths, gotAllStatic := a.StaticPaths(tt.disabledAnalyzers) + + // Check if all analyzers implement StaticPathAnalyzer + assert.Equal(t, tt.wantAllStatic, gotAllStatic) + + // Check paths + assert.ElementsMatch(t, tt.want, gotPaths) + }) + } +} diff --git a/pkg/fanal/artifact/repo/git.go b/pkg/fanal/artifact/repo/git.go index ebd7458f33c7..f0534e5c31f1 100644 --- a/pkg/fanal/artifact/repo/git.go +++ b/pkg/fanal/artifact/repo/git.go @@ -1,6 +1,7 @@ package repo import ( + "context" "net/url" "os" @@ -28,7 +29,7 @@ var ( ) type Walker interface { - Walk(root string, opt walker.Option, fn walker.WalkFunc) error + Walk(ctx context.Context, root string, opt walker.Option, fn walker.WalkFunc) error } func NewArtifact(target string, c cache.ArtifactCache, w Walker, artifactOpt artifact.Option) (artifact.Artifact, func(), error) { diff --git a/pkg/fanal/artifact/vm/ami.go b/pkg/fanal/artifact/vm/ami.go deleted file mode 100644 index 23b72990ae74..000000000000 --- a/pkg/fanal/artifact/vm/ami.go +++ /dev/null @@ -1,65 +0,0 @@ -package vm - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/cloud/aws/config" - "github.com/aquasecurity/trivy/pkg/fanal/artifact" - "github.com/aquasecurity/trivy/pkg/log" -) - -type AMI struct { - *EBS - - imageID string -} - -func newAMI(imageID string, storage Storage, region, endpoint string) (*AMI, error) { - // TODO: propagate context - ctx := context.TODO() - cfg, err := config.LoadDefaultAWSConfig(ctx, region, endpoint) - if err != nil { - return nil, err - } - client := ec2.NewFromConfig(cfg) - output, err := client.DescribeImages(ctx, &ec2.DescribeImagesInput{ - ImageIds: []string{imageID}, - }) - if err != nil { - return nil, xerrors.Errorf("ec2.DescribeImages: %w", err) - } else if len(output.Images) == 0 { - return nil, xerrors.Errorf("%s not found", imageID) - } - - // Take the first snapshot - for _, mapping := range output.Images[0].BlockDeviceMappings { - snapshotID := aws.ToString(mapping.Ebs.SnapshotId) - if snapshotID == "" { - continue - } - log.WithPrefix("ami").Info("Snapshot found", log.String("snapshot_id", snapshotID)) - ebs, err := newEBS(snapshotID, storage, region, endpoint) - if err != nil { - return nil, xerrors.Errorf("new EBS error: %w", err) - } - return &AMI{ - EBS: ebs, - imageID: imageID, - }, nil - } - - return nil, xerrors.New("no snapshot found") -} - -func (a *AMI) Inspect(ctx context.Context) (artifact.Reference, error) { - ref, err := a.EBS.Inspect(ctx) - if err != nil { - return artifact.Reference{}, err - } - ref.Name = a.imageID - return ref, nil -} diff --git a/pkg/fanal/artifact/vm/ami_stub.go b/pkg/fanal/artifact/vm/ami_stub.go new file mode 100644 index 000000000000..f9f02a2510b8 --- /dev/null +++ b/pkg/fanal/artifact/vm/ami_stub.go @@ -0,0 +1,28 @@ +package vm + +import ( + "context" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/fanal/artifact" +) + +type AMI struct { + *EBS + + imageID string +} + +func newAMI(imageID string, storage Storage, region, endpoint string) (*AMI, error) { + return nil, xerrors.New("pkg/cloud not implemented") +} + +func (a *AMI) Inspect(ctx context.Context) (artifact.Reference, error) { + ref, err := a.EBS.Inspect(ctx) + if err != nil { + return artifact.Reference{}, err + } + ref.Name = a.imageID + return ref, nil +} diff --git a/pkg/fanal/artifact/vm/ebs.go b/pkg/fanal/artifact/vm/ebs_stub.go similarity index 89% rename from pkg/fanal/artifact/vm/ebs.go rename to pkg/fanal/artifact/vm/ebs_stub.go index 280236cb371f..ede29a810555 100644 --- a/pkg/fanal/artifact/vm/ebs.go +++ b/pkg/fanal/artifact/vm/ebs_stub.go @@ -9,7 +9,6 @@ import ( "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/cache" - "github.com/aquasecurity/trivy/pkg/cloud/aws/config" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/log" ) @@ -27,17 +26,7 @@ type EBS struct { } func newEBS(snapshotID string, vm Storage, region, endpoint string) (*EBS, error) { - ebs, err := ebsfile.New(context.TODO(), config.MakeAWSOptions(region, endpoint)...) - if err != nil { - return nil, xerrors.Errorf("new ebsfile error: %w", err) - } - - return &EBS{ - Storage: vm, - logger: log.WithPrefix("ebs"), - snapshotID: snapshotID, - ebs: ebs, - }, nil + return nil, xerrors.New("pkg/cloud not implemented") } func (a *EBS) Inspect(ctx context.Context) (artifact.Reference, error) { diff --git a/pkg/fanal/artifact/vm/vm.go b/pkg/fanal/artifact/vm/vm.go index 56f7a0f5fa88..2e4bd7f09db8 100644 --- a/pkg/fanal/artifact/vm/vm.go +++ b/pkg/fanal/artifact/vm/vm.go @@ -42,7 +42,7 @@ var ( ) type Walker interface { - Walk(*io.SectionReader, string, walker.Option, walker.WalkFunc) error + Walk(context.Context, *io.SectionReader, string, walker.Option, walker.WalkFunc) error } func NewArtifact(target string, c cache.ArtifactCache, w Walker, opt artifact.Option) (artifact.Artifact, error) { @@ -97,8 +97,11 @@ func (a *Storage) Analyze(ctx context.Context, r *io.SectionReader) (types.BlobI result := analyzer.NewAnalysisResult() opts := analyzer.AnalysisOptions{ - Offline: a.artifactOption.Offline, - FileChecksum: a.artifactOption.FileChecksum, + Offline: a.artifactOption.Offline, + OfflineJar: a.artifactOption.OfflineJar, + FileChecksum: a.artifactOption.FileChecksum, + FileChecksumJar: a.artifactOption.FileChecksumJar, + WalkErrCallback: a.artifactOption.GetWalkerErrorCallback(), } // Prepare filesystem for post analysis @@ -109,7 +112,7 @@ func (a *Storage) Analyze(ctx context.Context, r *io.SectionReader) (types.BlobI defer composite.Cleanup() // TODO: Always walk from the root directory. Consider whether there is a need to be able to set optional - err = a.walker.Walk(r, "/", a.artifactOption.WalkerOption, func(filePath string, info os.FileInfo, opener analyzer.Opener) error { + err = a.walker.Walk(ctx, r, "/", a.artifactOption.WalkerOption, func(ctx context.Context, filePath string, info os.FileInfo, opener analyzer.Opener) error { path := strings.TrimPrefix(filePath, "/") if err := a.analyzer.AnalyzeFile(ctx, &wg, limit, result, "/", path, info, opener, nil, opts); err != nil { return xerrors.Errorf("analyze file (%s): %w", path, err) @@ -157,6 +160,9 @@ func (a *Storage) Analyze(ctx context.Context, r *io.SectionReader) (types.BlobI Secrets: result.Secrets, Licenses: result.Licenses, CustomResources: result.CustomResources, + + // For Red Hat + BuildInfo: result.BuildInfo, } if err = a.handlerManager.PostHandle(ctx, result, &blobInfo); err != nil { diff --git a/pkg/fanal/container/container.go b/pkg/fanal/container/container.go new file mode 100644 index 000000000000..ee0ecfa2f0b8 --- /dev/null +++ b/pkg/fanal/container/container.go @@ -0,0 +1,75 @@ +package container + +import ( + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/fanal/types" + + "github.com/distribution/reference" + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +type container struct { + refCanonical reference.Canonical + tags []string + imageID string + configFile *v1.ConfigFile + layers []types.LayerPath +} + +func NewContainer(refCanonical reference.Canonical, tags []string, imageID string, configFile *v1.ConfigFile, layers []types.LayerPath) *container { + return &container{ + refCanonical: refCanonical, + tags: tags, + imageID: imageID, + configFile: configFile, + layers: layers, + } +} + +func (ctr *container) Name() string { + return reference.FamiliarName(ctr.refCanonical) + "@" + ctr.refCanonical.Digest().String() +} + +func (ctr *container) ID() (string, error) { + return ctr.imageID, nil +} + +func (ctr *container) RepoTags() []string { + familiarName := reference.FamiliarName(ctr.refCanonical) + repoTags := make([]string, 0, len(ctr.tags)) + for _, tag := range ctr.tags { + repoTags = append(repoTags, familiarName+":"+tag) + } + return repoTags +} + +func (ctr *container) RepoDigests() []string { + return []string{reference.FamiliarName(ctr.refCanonical) + "@" + ctr.refCanonical.Digest().String()} +} + +func (ctr *container) ConfigFile() (*v1.ConfigFile, error) { + return ctr.configFile, nil +} + +func (ctr *container) Layers() []types.LayerPath { + return ctr.layers +} + +func (ctr *container) LayerByDiffID(diffID string) (types.LayerPath, error) { + for _, layer := range ctr.layers { + if layer.DiffID == diffID { + return layer, nil + } + } + return types.LayerPath{}, xerrors.Errorf("unable to find diffID %s", diffID) +} + +func (ctr *container) LayerByDigest(digest string) (types.LayerPath, error) { + for _, layer := range ctr.layers { + if layer.Digest == digest { + return layer, nil + } + } + return types.LayerPath{}, xerrors.Errorf("unable to find digest %s", digest) +} diff --git a/pkg/fanal/handler/all/import.go b/pkg/fanal/handler/all/import.go index 37c7c5545708..8e916b6f27c8 100644 --- a/pkg/fanal/handler/all/import.go +++ b/pkg/fanal/handler/all/import.go @@ -2,5 +2,4 @@ package all import ( _ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile" - _ "github.com/aquasecurity/trivy/pkg/fanal/handler/unpackaged" ) diff --git a/pkg/fanal/image/daemon/containerd.go b/pkg/fanal/image/daemon/containerd.go index faa5c8d98c3f..d9dc6bc8bcac 100644 --- a/pkg/fanal/image/daemon/containerd.go +++ b/pkg/fanal/image/daemon/containerd.go @@ -16,10 +16,10 @@ import ( "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/platforms" "github.com/distribution/reference" - api "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/go-connections/nat" v1 "github.com/google/go-containerregistry/pkg/v1" + dockerspec "github.com/moby/docker-image-spec/specs-go/v1" + dockerimage "github.com/moby/moby/api/types/image" + dockerClient "github.com/moby/moby/client" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/samber/lo" @@ -53,7 +53,7 @@ func (n familiarNamed) String() string { } func imageWriter(c *client.Client, img client.Image, platform types.Platform) imageSave { - return func(ctx context.Context, ref []string) (io.ReadCloser, error) { + return func(ctx context.Context, ref []string, _ ...dockerClient.ImageSaveOption) (dockerClient.ImageSaveResult, error) { if len(ref) < 1 { return nil, xerrors.New("no image reference") } @@ -211,7 +211,7 @@ func readImageConfig(ctx context.Context, img client.Image) (ocispec.Image, ocis } // ported from https://github.com/containerd/nerdctl/blob/d110fea18018f13c3f798fa6565e482f3ff03591/pkg/inspecttypes/dockercompat/dockercompat.go#L279-L321 -func inspect(ctx context.Context, img client.Image, ref reference.Reference) (api.ImageInspect, []v1.History, reference.Reference, error) { +func inspect(ctx context.Context, img client.Image, ref reference.Reference) (dockerimage.InspectResponse, []v1.History, reference.Reference, error) { if _, ok := ref.(reference.Digested); ok { ref = familiarNamed(img.Name()) } @@ -228,7 +228,7 @@ func inspect(ctx context.Context, img client.Image, ref reference.Reference) (ap imgConfig, imgConfigDesc, err := readImageConfig(ctx, img) if err != nil { - return api.ImageInspect{}, nil, nil, err + return dockerimage.InspectResponse{}, nil, nil, err } var lastHistory ocispec.History @@ -247,37 +247,25 @@ func inspect(ctx context.Context, img client.Image, ref reference.Reference) (ap }) } - portSet := make(nat.PortSet) - for k := range imgConfig.Config.ExposedPorts { - portSet[nat.Port(k)] = struct{}{} - } - created := "" if lastHistory.Created != nil { created = lastHistory.Created.Format(time.RFC3339Nano) } - return api.ImageInspect{ + return dockerimage.InspectResponse{ ID: imgConfigDesc.Digest.String(), RepoTags: []string{fmt.Sprintf("%s:%s", repository, tag)}, RepoDigests: []string{fmt.Sprintf("%s@%s", repository, img.Target().Digest)}, Comment: lastHistory.Comment, Created: created, Author: lastHistory.Author, - Config: &container.Config{ - User: imgConfig.Config.User, - ExposedPorts: portSet, - Env: imgConfig.Config.Env, - Cmd: imgConfig.Config.Cmd, - Volumes: imgConfig.Config.Volumes, - WorkingDir: imgConfig.Config.WorkingDir, - Entrypoint: imgConfig.Config.Entrypoint, - Labels: imgConfig.Config.Labels, + Config: &dockerspec.DockerOCIImageConfig{ + ImageConfig: imgConfig.Config, }, Architecture: imgConfig.Architecture, Os: imgConfig.OS, - RootFS: api.RootFS{ - Type: imgConfig.RootFS.Type, + RootFS: dockerimage.RootFS{ + Type: imgConfig.RootFS.Type, Layers: lo.Map(imgConfig.RootFS.DiffIDs, func(d digest.Digest, _ int) string { return d.String() }), diff --git a/pkg/fanal/image/daemon/docker.go b/pkg/fanal/image/daemon/docker.go index 45581c99b3c8..14772f082e3a 100644 --- a/pkg/fanal/image/daemon/docker.go +++ b/pkg/fanal/image/daemon/docker.go @@ -4,8 +4,8 @@ import ( "context" "os" - "github.com/docker/docker/client" "github.com/google/go-containerregistry/pkg/name" + "github.com/moby/moby/client" "golang.org/x/xerrors" ) @@ -16,13 +16,12 @@ func DockerImage(ref name.Reference, host string) (Image, func(), error) { opts := []client.Opt{ client.FromEnv, - client.WithAPIVersionNegotiation(), } if host != "" { // adding host parameter to the last assuming it will pick up more preference opts = append(opts, client.WithHost(host)) } - c, err := client.NewClientWithOpts(opts...) + c, err := client.New(opts...) if err != nil { return nil, cleanup, xerrors.Errorf("failed to initialize a docker client: %w", err) @@ -37,10 +36,10 @@ func DockerImage(ref name.Reference, host string) (Image, func(), error) { // or // @ pattern like "alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300" imageID := ref.Name() - inspect, _, err := c.ImageInspectWithRaw(context.Background(), imageID) + inspect, err := c.ImageInspect(context.Background(), imageID) if err != nil { imageID = ref.String() // pattern like `5ac716b05a9c` - inspect, _, err = c.ImageInspectWithRaw(context.Background(), imageID) + inspect, err = c.ImageInspect(context.Background(), imageID) if err != nil { return nil, cleanup, xerrors.Errorf("unable to inspect the image (%s): %w", imageID, err) } @@ -64,7 +63,7 @@ func DockerImage(ref name.Reference, host string) (Image, func(), error) { return &image{ opener: imageOpener(context.Background(), imageID, f, c.ImageSave), - inspect: inspect, - history: configHistory(history), + inspect: inspect.InspectResponse, + history: configHistory(history.Items), }, cleanup, nil } diff --git a/pkg/fanal/image/daemon/docker_test.go b/pkg/fanal/image/daemon/docker_test.go index 3ae519bdf938..7bf52a946de4 100644 --- a/pkg/fanal/image/daemon/docker_test.go +++ b/pkg/fanal/image/daemon/docker_test.go @@ -3,7 +3,7 @@ package daemon import ( "testing" - "github.com/docker/docker/api/types" + dimage "github.com/moby/moby/api/types/image" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/stretchr/testify/assert" @@ -14,7 +14,7 @@ func TestDockerImage(t *testing.T) { type fields struct { Image v1.Image opener opener - inspect types.ImageInspect + inspect dimage.InspectResponse } tests := []struct { name string diff --git a/pkg/fanal/image/daemon/image.go b/pkg/fanal/image/daemon/image.go index d650851ad242..901f0e75a67b 100644 --- a/pkg/fanal/image/daemon/image.go +++ b/pkg/fanal/image/daemon/image.go @@ -8,11 +8,11 @@ import ( "sync" "time" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - dimage "github.com/docker/docker/api/types/image" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" + dockerspec "github.com/moby/docker-image-spec/specs-go/v1" + dimage "github.com/moby/moby/api/types/image" + "github.com/moby/moby/client" "github.com/samber/lo" "golang.org/x/xerrors" @@ -29,7 +29,7 @@ var mu sync.Mutex type opener func() (v1.Image, error) -type imageSave func(context.Context, []string) (io.ReadCloser, error) +type imageSave func(context.Context, []string, ...client.ImageSaveOption) (client.ImageSaveResult, error) func imageOpener(ctx context.Context, ref string, f *os.File, imageSave imageSave) opener { return func() (v1.Image, error) { @@ -61,7 +61,7 @@ func imageOpener(ctx context.Context, ref string, f *os.File, imageSave imageSav type image struct { v1.Image opener opener - inspect types.ImageInspect + inspect dimage.InspectResponse history []v1.History } @@ -125,14 +125,12 @@ func (img *image) ConfigFile() (*v1.ConfigFile, error) { } return &v1.ConfigFile{ - Architecture: img.inspect.Architecture, - Author: img.inspect.Author, - Container: img.inspect.Container, - Created: created, - DockerVersion: img.inspect.DockerVersion, - Config: img.imageConfig(img.inspect.Config), - History: img.history, - OS: img.inspect.Os, + Architecture: img.inspect.Architecture, + Author: img.inspect.Author, + Created: created, + Config: img.imageConfig(lo.FromPtr(img.inspect.Config)), + History: img.history, + OS: img.inspect.Os, RootFS: v1.RootFS{ Type: img.inspect.RootFS.Type, DiffIDs: diffIDs, @@ -185,34 +183,33 @@ func (img *image) diffIDs() ([]v1.Hash, error) { return diffIDs, nil } -func (img *image) imageConfig(config *container.Config) v1.Config { - if config == nil { - return v1.Config{} +func (img *image) imageConfig(config dockerspec.DockerOCIImageConfig) v1.Config { + c := v1.Config{ + // OCI-compliant fields + User: config.User, + Cmd: config.Cmd, + Entrypoint: config.Entrypoint, + Env: config.Env, + Labels: config.Labels, + WorkingDir: config.WorkingDir, + StopSignal: config.StopSignal, + ArgsEscaped: config.ArgsEscaped, + OnBuild: config.OnBuild, + Shell: config.Shell, } - c := v1.Config{ - AttachStderr: config.AttachStderr, - AttachStdin: config.AttachStdin, - AttachStdout: config.AttachStdout, - Cmd: config.Cmd, - Domainname: config.Domainname, - Entrypoint: config.Entrypoint, - Env: config.Env, - Hostname: config.Hostname, - Image: config.Image, - Labels: config.Labels, - OnBuild: config.OnBuild, - OpenStdin: config.OpenStdin, - StdinOnce: config.StdinOnce, - Tty: config.Tty, - User: config.User, - Volumes: config.Volumes, - WorkingDir: config.WorkingDir, - ArgsEscaped: config.ArgsEscaped, - NetworkDisabled: config.NetworkDisabled, - MacAddress: config.MacAddress, - StopSignal: config.StopSignal, - Shell: config.Shell, + if len(config.ExposedPorts) > 0 { + c.ExposedPorts = make(map[string]struct{}) //nolint: gocritic + for port := range config.ExposedPorts { + c.ExposedPorts[port] = struct{}{} + } + } + + if len(config.Volumes) > 0 { + c.Volumes = make(map[string]struct{}) //nolint: gocritic + for volume := range config.Volumes { + c.Volumes[volume] = struct{}{} + } } if config.Healthcheck != nil { @@ -225,13 +222,6 @@ func (img *image) imageConfig(config *container.Config) v1.Config { } } - if len(config.ExposedPorts) > 0 { - c.ExposedPorts = make(map[string]struct{}) //nolint: gocritic - for port := range config.ExposedPorts { - c.ExposedPorts[port.Port()] = struct{}{} - } - } - return c } diff --git a/pkg/fanal/image/daemon/image_test.go b/pkg/fanal/image/daemon/image_test.go index 35ac8f278137..ea2385bdd7b1 100644 --- a/pkg/fanal/image/daemon/image_test.go +++ b/pkg/fanal/image/daemon/image_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - dimage "github.com/docker/docker/api/types/image" + dimage "github.com/moby/moby/api/types/image" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/stretchr/testify/assert" @@ -166,10 +166,9 @@ func Test_image_ConfigFile(t *testing.T) { name: "one diff_id", imageName: "alpine:3.11", want: &v1.ConfigFile{ - Architecture: "amd64", - OS: "linux", - Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)}, - DockerVersion: "18.09.7", + Architecture: "amd64", + OS: "linux", + Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)}, History: []v1.History{ { Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 0, time.UTC)}, @@ -194,7 +193,6 @@ func Test_image_ConfigFile(t *testing.T) { }, Config: v1.Config{ Cmd: []string{"/bin/sh"}, - Image: "sha256:74df73bb19fbfc7fb5ab9a8234b3d98ee2fb92df5b824496679802685205ab8c", Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, ArgsEscaped: true, }, diff --git a/pkg/fanal/image/daemon/podman.go b/pkg/fanal/image/daemon/podman.go index 51766ae01822..467c784219bc 100644 --- a/pkg/fanal/image/daemon/podman.go +++ b/pkg/fanal/image/daemon/podman.go @@ -4,14 +4,13 @@ import ( "context" "encoding/json" "fmt" - "io" "net" "net/http" "os" "path/filepath" - api "github.com/docker/docker/api/types" - dimage "github.com/docker/docker/api/types/image" + dimage "github.com/moby/moby/api/types/image" + "github.com/moby/moby/client" "golang.org/x/xerrors" ) @@ -52,25 +51,25 @@ type errResponse struct { Message string } -func (p podmanClient) imageInspect(imageName string) (api.ImageInspect, error) { +func (p podmanClient) imageInspect(imageName string) (dimage.InspectResponse, error) { url := fmt.Sprintf(inspectURL, imageName) resp, err := p.c.Get(url) if err != nil { - return api.ImageInspect{}, xerrors.Errorf("http error: %w", err) + return dimage.InspectResponse{}, xerrors.Errorf("http error: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { var res errResponse if err = json.NewDecoder(resp.Body).Decode(&res); err != nil { - return api.ImageInspect{}, xerrors.Errorf("unknown status code from Podman: %d", resp.StatusCode) + return dimage.InspectResponse{}, xerrors.Errorf("unknown status code from Podman: %d", resp.StatusCode) } - return api.ImageInspect{}, xerrors.New(res.Message) + return dimage.InspectResponse{}, xerrors.New(res.Message) } - var inspect api.ImageInspect + var inspect dimage.InspectResponse if err = json.NewDecoder(resp.Body).Decode(&inspect); err != nil { - return api.ImageInspect{}, xerrors.Errorf("unable to decode JSON: %w", err) + return dimage.InspectResponse{}, xerrors.Errorf("unable to decode JSON: %w", err) } return inspect, nil } @@ -98,7 +97,7 @@ func (p podmanClient) imageHistoryInspect(imageName string) ([]dimage.HistoryRes return history, nil } -func (p podmanClient) imageSave(_ context.Context, imageNames []string) (io.ReadCloser, error) { +func (p podmanClient) imageSave(_ context.Context, imageNames []string, _ ...client.ImageSaveOption) (client.ImageSaveResult, error) { if len(imageNames) < 1 { return nil, xerrors.Errorf("no specified image") } diff --git a/pkg/fanal/image/daemon/podman_test.go b/pkg/fanal/image/daemon/podman_test.go index 106d821a0e32..8522f3141e2a 100644 --- a/pkg/fanal/image/daemon/podman_test.go +++ b/pkg/fanal/image/daemon/podman_test.go @@ -7,7 +7,7 @@ import ( "runtime" "testing" - "github.com/docker/docker/api/types" + dimage "github.com/moby/moby/api/types/image" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/stretchr/testify/assert" @@ -49,7 +49,7 @@ func TestPodmanImage(t *testing.T) { type fields struct { Image v1.Image opener opener - inspect types.ImageInspect + inspect dimage.InspectResponse } tests := []struct { name string diff --git a/pkg/fanal/image/image.go b/pkg/fanal/image/image.go index ea2df2c9ddc0..9f0b82792e2a 100644 --- a/pkg/fanal/image/image.go +++ b/pkg/fanal/image/image.go @@ -3,7 +3,6 @@ package image import ( "context" "fmt" - "strings" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -78,60 +77,3 @@ func LayerIDs(img v1.Image) ([]string, error) { } return layerIDs, nil } - -// GuessBaseImageIndex tries to guess index of base layer -// -// e.g. In the following example, we should detect layers in debian:8. -// -// FROM debian:8 -// RUN apt-get update -// COPY mysecret / -// ENTRYPOINT ["entrypoint.sh"] -// CMD ["somecmd"] -// -// debian:8 may be like -// -// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / -// CMD ["/bin/sh"] -// -// In total, it would be like: -// -// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / -// CMD ["/bin/sh"] # empty layer (detected) -// RUN apt-get update -// COPY mysecret / -// ENTRYPOINT ["entrypoint.sh"] # empty layer (skipped) -// CMD ["somecmd"] # empty layer (skipped) -// -// This method tries to detect CMD in the second line and assume the first line is a base layer. -// 1. Iterate histories from the bottom. -// 2. Skip all the empty layers at the bottom. In the above example, "entrypoint.sh" and "somecmd" will be skipped -// 3. If it finds CMD, it assumes that it is the end of base layers. -// 4. It gets all the layers as base layers above the CMD found in #3. -func GuessBaseImageIndex(histories []v1.History) int { - baseImageIndex := -1 - var foundNonEmpty bool - for i := len(histories) - 1; i >= 0; i-- { - h := histories[i] - - // Skip the last CMD, ENTRYPOINT, etc. - if !foundNonEmpty { - if h.EmptyLayer { - continue - } - foundNonEmpty = true - } - - if !h.EmptyLayer { - continue - } - - // Detect CMD instruction in base image - if strings.HasPrefix(h.CreatedBy, "/bin/sh -c #(nop) CMD") || - strings.HasPrefix(h.CreatedBy, "CMD") { // BuildKit - baseImageIndex = i - break - } - } - return baseImageIndex -} diff --git a/pkg/fanal/image/image_test.go b/pkg/fanal/image/image_test.go index 555fe79cf509..67e3a91e33e5 100644 --- a/pkg/fanal/image/image_test.go +++ b/pkg/fanal/image/image_test.go @@ -73,10 +73,9 @@ func TestNewDockerImage(t *testing.T) { wantID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", wantRepoTags: []string{"alpine:3.11"}, wantConfigFile: &v1.ConfigFile{ - Architecture: "amd64", - OS: "linux", - Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)}, - DockerVersion: "18.09.7", + Architecture: "amd64", + OS: "linux", + Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)}, History: []v1.History{ { Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 0, time.UTC)}, @@ -116,10 +115,9 @@ func TestNewDockerImage(t *testing.T) { wantID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", wantRepoTags: []string{"alpine:3.11"}, wantConfigFile: &v1.ConfigFile{ - Architecture: "amd64", - OS: "linux", - Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)}, - DockerVersion: "18.09.7", + Architecture: "amd64", + OS: "linux", + Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)}, History: []v1.History{ { Created: v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 0, time.UTC)}, diff --git a/pkg/fanal/image/utils/base.go b/pkg/fanal/image/utils/base.go new file mode 100644 index 000000000000..d449d355572e --- /dev/null +++ b/pkg/fanal/image/utils/base.go @@ -0,0 +1,64 @@ +package utils + +import ( + "strings" + + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// GuessBaseImageIndex tries to guess index of base layer +// +// e.g. In the following example, we should detect layers in debian:8. +// +// FROM debian:8 +// RUN apt-get update +// COPY mysecret / +// ENTRYPOINT ["entrypoint.sh"] +// CMD ["somecmd"] +// +// debian:8 may be like +// +// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / +// CMD ["/bin/sh"] +// +// In total, it would be like: +// +// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / +// CMD ["/bin/sh"] # empty layer (detected) +// RUN apt-get update +// COPY mysecret / +// ENTRYPOINT ["entrypoint.sh"] # empty layer (skipped) +// CMD ["somecmd"] # empty layer (skipped) +// +// This method tries to detect CMD in the second line and assume the first line is a base layer. +// 1. Iterate histories from the bottom. +// 2. Skip all the empty layers at the bottom. In the above example, "entrypoint.sh" and "somecmd" will be skipped +// 3. If it finds CMD, it assumes that it is the end of base layers. +// 4. It gets all the layers as base layers above the CMD found in #3. +func GuessBaseImageIndex(histories []v1.History) int { + baseImageIndex := -1 + var foundNonEmpty bool + for i := len(histories) - 1; i >= 0; i-- { + h := histories[i] + + // Skip the last CMD, ENTRYPOINT, etc. + if !foundNonEmpty { + if h.EmptyLayer { + continue + } + foundNonEmpty = true + } + + if !h.EmptyLayer { + continue + } + + // Detect CMD instruction in base image + if strings.HasPrefix(h.CreatedBy, "/bin/sh -c #(nop) CMD") || + strings.HasPrefix(h.CreatedBy, "CMD") { // BuildKit + baseImageIndex = i + break + } + } + return baseImageIndex +} diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index 375c5cd77c62..b0df0c8355b9 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -18,7 +18,6 @@ import ( "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/pkg/namespaces" - dockercontainer "github.com/docker/docker/api/types/container" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/samber/lo" "github.com/stretchr/testify/assert" @@ -91,9 +90,7 @@ func startContainerd(t *testing.T, ctx context.Context, hostPath string) { Mounts: testcontainers.Mounts( testcontainers.BindMount(hostPath, "/run"), ), - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, + AutoRemove: true, WaitingFor: wait.ForLog("containerd successfully booted"), } containerdC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ diff --git a/pkg/fanal/test/integration/docker/docker.go b/pkg/fanal/test/integration/docker/docker.go index 82a1671fb8d1..3a787b9cae18 100644 --- a/pkg/fanal/test/integration/docker/docker.go +++ b/pkg/fanal/test/integration/docker/docker.go @@ -10,9 +10,8 @@ import ( "os" "os/exec" - "github.com/docker/docker/api/types/image" - apiregistry "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/client" + apiregistry "github.com/moby/moby/api/types/registry" + "github.com/moby/moby/client" ) type RegistryConfig struct { @@ -50,7 +49,7 @@ type Docker struct { } func New() (Docker, error) { - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + cli, err := client.New(client.FromEnv) if err != nil { return Docker{}, err } @@ -72,7 +71,7 @@ func (d Docker) Logout(conf RegistryConfig) error { // ReplicateImage tags the given imagePath and pushes it to the given dest registry. func (d Docker) ReplicateImage(ctx context.Context, imageRef, imagePath string, dest RegistryConfig) error { // remove existing Image if any - _, _ = d.cli.ImageRemove(ctx, imageRef, image.RemoveOptions{ + _, _ = d.cli.ImageRemove(ctx, imageRef, client.ImageRemoveOptions{ Force: true, PruneChildren: true, }) @@ -84,26 +83,29 @@ func (d Docker) ReplicateImage(ctx context.Context, imageRef, imagePath string, defer testfile.Close() // load image into docker engine - resp, err := d.cli.ImageLoad(ctx, testfile, true) + resp, err := d.cli.ImageLoad(ctx, testfile) if err != nil { return err } - if _, err := io.Copy(io.Discard, resp.Body); err != nil { + if _, err := io.Copy(io.Discard, resp); err != nil { return err } - defer resp.Body.Close() + defer resp.Close() targetImageRef := fmt.Sprintf("%s/%s", dest.URL.Host, imageRef) - if err = d.cli.ImageTag(ctx, imageRef, targetImageRef); err != nil { + if _, err = d.cli.ImageTag(ctx, client.ImageTagOptions{ + Source: imageRef, + Target: targetImageRef, + }); err != nil { return err } defer func() { - _, _ = d.cli.ImageRemove(ctx, imageRef, image.RemoveOptions{ + _, _ = d.cli.ImageRemove(ctx, imageRef, client.ImageRemoveOptions{ Force: true, PruneChildren: true, }) - _, _ = d.cli.ImageRemove(ctx, targetImageRef, image.RemoveOptions{ + _, _ = d.cli.ImageRemove(ctx, targetImageRef, client.ImageRemoveOptions{ Force: true, PruneChildren: true, }) @@ -114,7 +116,7 @@ func (d Docker) ReplicateImage(ctx context.Context, imageRef, imagePath string, return err } - pushOut, err := d.cli.ImagePush(ctx, targetImageRef, image.PushOptions{ + pushOut, err := d.cli.ImagePush(ctx, targetImageRef, client.ImagePushOptions{ RegistryAuth: auth, }) if err != nil { diff --git a/pkg/fanal/test/integration/registry_test.go b/pkg/fanal/test/integration/registry_test.go index e6fc7445cc87..1cc7af9398dc 100644 --- a/pkg/fanal/test/integration/registry_test.go +++ b/pkg/fanal/test/integration/registry_test.go @@ -12,8 +12,6 @@ import ( "path/filepath" "testing" - dockercontainer "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -62,9 +60,7 @@ func TestTLSRegistry(t *testing.T) { testcontainers.BindMount(filepath.Join(baseDir, "data", "registry", "certs"), "/certs"), testcontainers.BindMount(filepath.Join(baseDir, "data", "registry", "auth"), "/auth"), ), - HostConfigModifier: func(hostConfig *dockercontainer.HostConfig) { - hostConfig.AutoRemove = true - }, + AutoRemove: true, WaitingFor: wait.ForLog("listening on [::]:5443"), } @@ -228,12 +224,6 @@ func analyze(ctx context.Context, imageRef string, opt types.ImageOptions) (*typ return nil, err } - cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - return nil, err - } - cli.NegotiateAPIVersion(ctx) - img, cleanup, err := image.NewContainerImage(ctx, imageRef, opt) if err != nil { return nil, err diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index 4ffeb2fb5660..bd85bacab9dd 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -25,6 +25,7 @@ const ( Alpine OSType = "alpine" Amazon OSType = "amazon" Azure OSType = "azurelinux" + Bottlerocket OSType = "bottlerocket" CBLMariner OSType = "cbl-mariner" CentOS OSType = "centos" Chainguard OSType = "chainguard" @@ -194,3 +195,11 @@ const ( JuliaProject = "Project.toml" JuliaManifest = "Manifest.toml" ) + +// Detectable executable types +const ( + PythonExecutable LangType = "python" + NodeJsExecutable LangType = "nodejs" + PhpExecutable LangType = "php" + JavaExecutable LangType = "java" +) diff --git a/pkg/fanal/types/container.go b/pkg/fanal/types/container.go new file mode 100644 index 000000000000..95e63396ac5d --- /dev/null +++ b/pkg/fanal/types/container.go @@ -0,0 +1,20 @@ +package types + +import v1 "github.com/google/go-containerregistry/pkg/v1" + +type Container interface { + Name() string + ID() (string, error) + ConfigFile() (*v1.ConfigFile, error) + Layers() []LayerPath + LayerByDiffID(string) (LayerPath, error) + LayerByDigest(string) (LayerPath, error) + RepoTags() []string + RepoDigests() []string +} + +type LayerPath struct { + DiffID string + Digest string + Path string +} diff --git a/pkg/fanal/utils/utils.go b/pkg/fanal/utils/utils.go index 5f96990cd703..e0f2ef15097e 100644 --- a/pkg/fanal/utils/utils.go +++ b/pkg/fanal/utils/utils.go @@ -108,6 +108,32 @@ func SkipPath(path string, skipPaths []string) bool { return false } +func OnlyPath(path string, onlyPaths []string) bool { + if len(onlyPaths) == 0 { + return false + } + + if path == "" || path == "." { + return false + } + + path = strings.TrimLeft(path, "/") + + for _, pattern := range onlyPaths { + if strings.HasPrefix(pattern, path+"/") { + return false + } + match, err := doublestar.Match(pattern, path) + if err != nil { + return false // return early if bad pattern + } else if match { + return false + } + } + log.Debug("Skipping path", log.String("path", path)) + return true +} + func ExtractPrintableBytes(content xio.ReadSeekerAt) ([]byte, error) { const minLength = 4 // Minimum length of strings to extract var result []byte diff --git a/pkg/fanal/walker/cached_file.go b/pkg/fanal/walker/cached_file.go index fc963c010409..192dc15cd719 100644 --- a/pkg/fanal/walker/cached_file.go +++ b/pkg/fanal/walker/cached_file.go @@ -50,7 +50,7 @@ func (o *cachedFile) Open() (xio.ReadSeekCloserAt, error) { o.filePath = f.Name() } else { - b, err := io.ReadAll(o.reader) + b, err := readAll(o.reader, int(o.size)) if err != nil { o.err = xerrors.Errorf("unable to read the file: %w", err) return @@ -80,3 +80,24 @@ func (o *cachedFile) open() (xio.ReadSeekCloserAt, error) { func (o *cachedFile) Clean() error { return os.Remove(o.filePath) } + +// readAll works like io.ReadAll, except it takes a pre-allocated buffer. Useful +// in our case when we know in advance the expected size of the reader content. +func readAll(r io.Reader, capacity int) ([]byte, error) { + b := make([]byte, 0, capacity) + for { + n, err := r.Read(b[len(b):cap(b)]) + b = b[:len(b)+n] + if err != nil { + if err == io.EOF { + err = nil + } + return b, err + } + + if len(b) == cap(b) { + // Add more capacity (let append pick how much). + b = append(b, 0)[:len(b)] + } + } +} diff --git a/pkg/fanal/walker/fs.go b/pkg/fanal/walker/fs.go index 3ebfb7258459..2bba1f0696b4 100644 --- a/pkg/fanal/walker/fs.go +++ b/pkg/fanal/walker/fs.go @@ -1,6 +1,7 @@ package walker import ( + "context" "errors" "io/fs" "os" @@ -22,13 +23,14 @@ func NewFS() *FS { } // Walk walks the filesystem rooted at root, calling fn for each unfiltered file. -func (w *FS) Walk(root string, opt Option, fn WalkFunc) error { +func (w *FS) Walk(ctx context.Context, root string, opt Option, fn WalkFunc) error { opt.SkipFiles = w.BuildSkipPaths(root, opt.SkipFiles) opt.SkipDirs = w.BuildSkipPaths(root, opt.SkipDirs) opt.SkipDirs = append(opt.SkipDirs, defaultSkipDirs...) + opt.OnlyDirs = w.BuildSkipPaths(root, opt.OnlyDirs) - walkDirFunc := w.WalkDirFunc(root, fn, opt) - walkDirFunc = w.onError(walkDirFunc) + walkDirFunc := w.WalkDirFunc(ctx, root, fn, opt) + walkDirFunc = w.onError(walkDirFunc, opt) // Walk the filesystem if err := filepath.WalkDir(root, walkDirFunc); err != nil { @@ -38,7 +40,7 @@ func (w *FS) Walk(root string, opt Option, fn WalkFunc) error { return nil } -func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc { +func (w *FS) WalkDirFunc(ctx context.Context, root string, fn WalkFunc, opt Option) fs.WalkDirFunc { return func(filePath string, d fs.DirEntry, err error) error { if err != nil { return err @@ -57,11 +59,16 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc { if utils.SkipPath(relPath, opt.SkipDirs) { return filepath.SkipDir } + if utils.OnlyPath(relPath, opt.OnlyDirs) { + return filepath.SkipDir + } return nil - case !d.Type().IsRegular(): + case !opt.AllFiles && !d.Type().IsRegular(): return nil case utils.SkipPath(relPath, opt.SkipFiles): return nil + case utils.OnlyPath(relPath, opt.OnlyDirs): + return nil } info, err := d.Info() @@ -69,7 +76,13 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc { return xerrors.Errorf("file info error: %w", err) } - if err = fn(relPath, info, fileOpener(filePath)); err != nil { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + if err = fn(ctx, relPath, info, fileOpener(filePath)); err != nil { return xerrors.Errorf("failed to analyze file: %w", err) } @@ -77,7 +90,7 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc { } } -func (w *FS) onError(wrapped fs.WalkDirFunc) fs.WalkDirFunc { +func (w *FS) onError(wrapped fs.WalkDirFunc, opt Option) fs.WalkDirFunc { return func(filePath string, d fs.DirEntry, err error) error { err = wrapped(filePath, d, err) switch { @@ -87,7 +100,15 @@ func (w *FS) onError(wrapped fs.WalkDirFunc) fs.WalkDirFunc { // Ignore permission errors case os.IsPermission(err): return nil + case errors.Is(err, context.DeadlineExceeded): + return xerrors.Errorf("timeout on %s: %w", filePath, err) case err != nil: + if opt.ErrorCallback != nil { + err = opt.ErrorCallback(filePath, err) + if err == nil { + return nil + } + } // halt traversal on any other error return xerrors.Errorf("unknown error with %s: %w", filePath, err) } diff --git a/pkg/fanal/walker/tar.go b/pkg/fanal/walker/tar.go index 0c7d0bee9d5d..c04cf5b51464 100644 --- a/pkg/fanal/walker/tar.go +++ b/pkg/fanal/walker/tar.go @@ -2,6 +2,7 @@ package walker import ( "archive/tar" + "context" "io" "io/fs" "path" @@ -23,16 +24,18 @@ var parentDir = ".." + utils.PathSeparator type LayerTar struct { skipFiles []string skipDirs []string + onlyDirs []string } func NewLayerTar(opt Option) LayerTar { return LayerTar{ skipFiles: utils.CleanSkipPaths(opt.SkipFiles), skipDirs: utils.CleanSkipPaths(opt.SkipDirs), + onlyDirs: utils.CleanSkipPaths(opt.OnlyDirs), } } -func (w LayerTar) Walk(layer io.Reader, analyzeFn WalkFunc) ([]string, []string, error) { +func (w LayerTar) Walk(ctx context.Context, layer io.Reader, analyzeFn WalkFunc) ([]string, []string, error) { var opqDirs, whFiles, skippedDirs []string tr := tar.NewReader(layer) for { @@ -81,21 +84,21 @@ func (w LayerTar) Walk(layer io.Reader, analyzeFn WalkFunc) ([]string, []string, } // A regular file will reach here. - if err = w.processFile(filePath, tr, hdr.FileInfo(), analyzeFn); err != nil { + if err = w.processFile(ctx, filePath, tr, hdr.FileInfo(), analyzeFn); err != nil { return nil, nil, xerrors.Errorf("failed to process the file: %w", err) } } return opqDirs, whFiles, nil } -func (w LayerTar) processFile(filePath string, tr *tar.Reader, fi fs.FileInfo, analyzeFn WalkFunc) error { +func (w LayerTar) processFile(ctx context.Context, filePath string, tr *tar.Reader, fi fs.FileInfo, analyzeFn WalkFunc) error { cf := newCachedFile(fi.Size(), tr) defer func() { // nolint _ = cf.Clean() }() - if err := analyzeFn(filePath, fi, cf.Open); err != nil { + if err := analyzeFn(ctx, filePath, fi, cf.Open); err != nil { return xerrors.Errorf("failed to analyze file: %w", err) } diff --git a/pkg/fanal/walker/vm.go b/pkg/fanal/walker/vm.go index f8ef63ce0252..0290f1698798 100644 --- a/pkg/fanal/walker/vm.go +++ b/pkg/fanal/walker/vm.go @@ -1,6 +1,7 @@ package walker import ( + "context" "bytes" "io" "io/fs" @@ -55,7 +56,7 @@ func NewVM() *VM { } } -func (w *VM) Walk(vreader *io.SectionReader, root string, opt Option, fn WalkFunc) error { +func (w *VM) Walk(ctx context.Context, vreader *io.SectionReader, root string, opt Option, fn WalkFunc) error { w.skipFiles = opt.SkipFiles w.skipDirs = append(opt.SkipDirs, defaultSkipDirs...) @@ -82,7 +83,7 @@ func (w *VM) Walk(vreader *io.SectionReader, root string, opt Option, fn WalkFun } // Walk each partition - if err = w.diskWalk(root, partition); err != nil { + if err = w.diskWalk(ctx, root, partition); err != nil { w.logger.Warn("Partition error", log.Err(err)) } } @@ -90,7 +91,7 @@ func (w *VM) Walk(vreader *io.SectionReader, root string, opt Option, fn WalkFun } // Inject disk partitioning processes from externally with diskWalk. -func (w *VM) diskWalk(root string, partition types.Partition) error { +func (w *VM) diskWalk(ctx context.Context, root string, partition types.Partition) error { w.logger.Debug("Found partition", log.String("name", partition.Name())) sr := partition.GetSectionReader() @@ -113,7 +114,7 @@ func (w *VM) diskWalk(root string, partition types.Partition) error { err = fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { // Walk filesystem - return w.fsWalk(fsys, path, d, err) + return w.fsWalk(ctx, fsys, path, d, err) }) if err != nil { return xerrors.Errorf("filesystem walk error: %w", err) @@ -121,7 +122,7 @@ func (w *VM) diskWalk(root string, partition types.Partition) error { return nil } -func (w *VM) fsWalk(fsys fs.FS, path string, d fs.DirEntry, err error) error { +func (w *VM) fsWalk(ctx context.Context, fsys fs.FS, path string, d fs.DirEntry, err error) error { if err != nil { return xerrors.Errorf("fs.Walk error: %w", err) } @@ -156,7 +157,7 @@ func (w *VM) fsWalk(fsys fs.FS, path string, d fs.DirEntry, err error) error { cvf := newCachedVMFile(fsys, pathName) defer cvf.Clean() - if err = w.analyzeFn(path, fi, cvf.Open); err != nil { + if err = w.analyzeFn(ctx, path, fi, cvf.Open); err != nil { return xerrors.Errorf("failed to analyze file: %w", err) } return nil diff --git a/pkg/fanal/walker/walk.go b/pkg/fanal/walker/walk.go index 0785cef41606..fd8ebba24e17 100644 --- a/pkg/fanal/walker/walk.go +++ b/pkg/fanal/walker/walk.go @@ -1,6 +1,7 @@ package walker import ( + "context" "os" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" @@ -15,9 +16,14 @@ var defaultSkipDirs = []string{ "dev", } +type ErrorCallback func(filePath string, err error) error + type Option struct { - SkipFiles []string - SkipDirs []string + SkipFiles []string + SkipDirs []string + OnlyDirs []string + AllFiles bool + ErrorCallback ErrorCallback } -type WalkFunc func(filePath string, info os.FileInfo, opener analyzer.Opener) error +type WalkFunc func(ctx context.Context, filePath string, info os.FileInfo, opener analyzer.Opener) error diff --git a/pkg/fanal/walker/walk_test.go b/pkg/fanal/walker/walk_test.go index 3c066529393f..697aa6f0c717 100644 --- a/pkg/fanal/walker/walk_test.go +++ b/pkg/fanal/walker/walk_test.go @@ -149,3 +149,87 @@ func TestSkipDir(t *testing.T) { }) } } + +func Test_onlyDir(t *testing.T) { + tests := []struct { + name string + onlyDirs []string + wants map[string]bool + }{ + { + name: "double star", + onlyDirs: []string{"/etc/**"}, + wants: map[string]bool{ + "/etc/foo/bar": false, + "/var/log/bar": true, + }, + }, + { + name: "double star", + onlyDirs: []string{"/**"}, + wants: map[string]bool{ + "/foo": false, + "/etc/foo": false, + "/var/log/bar": false, + }, + }, + { + name: "single star", + onlyDirs: []string{"/*"}, + wants: map[string]bool{ + "/foo": false, + "/etc/foo": true, + "/etc/foo/bar": true, + }, + }, + { + name: "single star", + onlyDirs: []string{"/etc/foo/*"}, + wants: map[string]bool{ + "/etc/foo/bar": false, + "/etc/foo2/bar": true, + "/var/etc/foo2/bar": true, + }, + }, + { + name: "single star", + onlyDirs: []string{"/*/bar"}, + wants: map[string]bool{ + "/etc/bar/bar": true, + "/etc/foo/bar": true, + "/etc/bar/foo": true, + "/etc/foo/bar/foo": true, + }, + }, + { + name: "double star", + onlyDirs: []string{"**/bar"}, + wants: map[string]bool{ + "/etc/bar/bar": false, + "/etc/foo/bar": false, + "/etc/bar/foo": true, + "/etc/foo/bar/foo": true, + }, + }, + { + name: "double star and single star", + onlyDirs: []string{"**/foo/*/bar"}, + wants: map[string]bool{ + "/foo/foo/bar/bar": false, + "/etc/foo/bar": true, + "/etc/bar/foo": true, + "/etc/foo/bar/bar": false, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for dir, want := range tt.wants { + dir = filepath.ToSlash(filepath.Clean(dir)) + got := utils.OnlyPath(dir, utils.CleanSkipPaths(tt.onlyDirs)) + assert.Equal(t, want, got, "onlyDirs: %s, dir: %s", tt.onlyDirs, dir) + } + }) + } +} diff --git a/pkg/flag/kubernetes_flags.go b/pkg/flag/kubernetes_flags.go index b8ec034d7169..acbfa33f16b5 100644 --- a/pkg/flag/kubernetes_flags.go +++ b/pkg/flag/kubernetes_flags.go @@ -3,11 +3,7 @@ package flag import ( "errors" "fmt" - "strconv" "strings" - - "github.com/samber/lo" - corev1 "k8s.io/api/core/v1" ) var ( @@ -114,7 +110,7 @@ type K8sFlagGroup struct { type K8sOptions struct { KubeConfig string K8sVersion string - Tolerations []corev1.Toleration + Tolerations []string NodeCollectorImageRef string NodeCollectorNamespace string ExcludeOwned bool @@ -133,7 +129,6 @@ func NewK8sFlagGroup() *K8sFlagGroup { return &K8sFlagGroup{ KubeConfig: KubeConfigFlag.Clone(), K8sVersion: K8sVersionFlag.Clone(), - Tolerations: TolerationsFlag.Clone(), DisableNodeCollector: DisableNodeCollector.Clone(), NodeCollectorNamespace: NodeCollectorNamespace.Clone(), ExcludeOwned: ExcludeOwned.Clone(), @@ -158,7 +153,6 @@ func (f *K8sFlagGroup) Flags() []Flagger { f.KubeConfig, f.K8sVersion, f.DisableNodeCollector, - f.Tolerations, f.NodeCollectorNamespace, f.ExcludeOwned, f.ExcludeNodes, @@ -178,11 +172,6 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) { return K8sOptions{}, err } - tolerations, err := optionToTolerations(f.Tolerations.Value()) - if err != nil { - return K8sOptions{}, err - } - exludeNodeLabels := make(map[string]string) exludeNodes := f.ExcludeNodes.Value() for _, exludeNodeValue := range exludeNodes { @@ -202,7 +191,6 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) { return K8sOptions{ KubeConfig: f.KubeConfig.Value(), K8sVersion: f.K8sVersion.Value(), - Tolerations: tolerations, DisableNodeCollector: f.DisableNodeCollector.Value(), NodeCollectorNamespace: f.NodeCollectorNamespace.Value(), ExcludeOwned: f.ExcludeOwned.Value(), @@ -217,40 +205,3 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) { Burst: f.Burst.Value(), }, nil } - -func optionToTolerations(tolerationsOptions []string) ([]corev1.Toleration, error) { - var tolerations []corev1.Toleration - for _, toleration := range tolerationsOptions { - tolerationParts := strings.Split(toleration, ":") - if len(tolerationParts) < 2 { - return []corev1.Toleration{}, errors.New("toleration must include key and effect") - } - if corev1.TaintEffect(tolerationParts[1]) != corev1.TaintEffectNoSchedule && - corev1.TaintEffect(tolerationParts[1]) != corev1.TaintEffectPreferNoSchedule && - corev1.TaintEffect(tolerationParts[1]) != corev1.TaintEffectNoExecute { - return []corev1.Toleration{}, errors.New("toleration effect must be a valid value") - } - keyValue := strings.Split(tolerationParts[0], "=") - operator := corev1.TolerationOpEqual - if keyValue[1] == "" { - operator = corev1.TolerationOpExists - } - toleration := corev1.Toleration{ - Key: keyValue[0], - Value: keyValue[1], - Operator: operator, - Effect: corev1.TaintEffect(tolerationParts[1]), - } - var tolerationSec int - var err error - if len(tolerationParts) == 3 { - tolerationSec, err = strconv.Atoi(tolerationParts[2]) - if err != nil { - return nil, errors.New("TolerationSeconds must must be a number") - } - toleration.TolerationSeconds = lo.ToPtr(int64(tolerationSec)) - } - tolerations = append(tolerations, toleration) - } - return tolerations, nil -} diff --git a/pkg/flag/kubernetes_flags_test.go b/pkg/flag/kubernetes_flags_test.go deleted file mode 100644 index 81b9fa94145a..000000000000 --- a/pkg/flag/kubernetes_flags_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package flag - -import ( - "testing" - - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" -) - -func TestOptionToToleration(t *testing.T) { - - tests := []struct { - name string - tolerationsOptions []string - want []corev1.Toleration - }{ - { - name: "no execute", - tolerationsOptions: []string{"key1=CriticalAddonsOnly:NoExecute:3600"}, - want: []corev1.Toleration{ - { - Key: "key1", - Operator: "Equal", - Value: "CriticalAddonsOnly", - Effect: "NoExecute", - TolerationSeconds: lo.ToPtr(int64(3600)), - }, - }, - }, - { - name: "no schedule", - tolerationsOptions: []string{"key1=CriticalAddonsOnly:NoSchedule"}, - want: []corev1.Toleration{ - { - Key: "key1", - Operator: "Equal", - Value: "CriticalAddonsOnly", - Effect: "NoSchedule", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := optionToTolerations(tt.tolerationsOptions) - require.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/pkg/flag/scan_flags.go b/pkg/flag/scan_flags.go index 15a1e04ce139..a806163183a2 100644 --- a/pkg/flag/scan_flags.go +++ b/pkg/flag/scan_flags.go @@ -31,6 +31,12 @@ var ( ConfigName: "scan.offline", Usage: "do not issue API requests to identify dependencies", } + OnlyDirsFlag = Flag[[]string]{ + Name: "only-dirs", + ConfigName: "scan.only-dirs", + Default: []string{}, + Usage: "specify the directories where the traversal is allowed", + } ScannersFlag = Flag[[]string]{ Name: "scanners", ConfigName: "scan.scanners", @@ -123,6 +129,7 @@ var ( type ScanFlagGroup struct { SkipDirs *Flag[[]string] SkipFiles *Flag[[]string] + OnlyDirs *Flag[[]string] OfflineScan *Flag[bool] Scanners *Flag[[]string] FilePatterns *Flag[[]string] @@ -138,6 +145,7 @@ type ScanOptions struct { Target string SkipDirs []string SkipFiles []string + OnlyDirs []string OfflineScan bool Scanners types.Scanners FilePatterns []string @@ -152,6 +160,7 @@ func NewScanFlagGroup() *ScanFlagGroup { return &ScanFlagGroup{ SkipDirs: SkipDirsFlag.Clone(), SkipFiles: SkipFilesFlag.Clone(), + OnlyDirs: OnlyDirsFlag.Clone(), OfflineScan: OfflineScanFlag.Clone(), Scanners: ScannersFlag.Clone(), FilePatterns: FilePatternsFlag.Clone(), @@ -172,6 +181,7 @@ func (f *ScanFlagGroup) Flags() []Flagger { return []Flagger{ f.SkipDirs, f.SkipFiles, + f.OnlyDirs, f.OfflineScan, f.Scanners, f.FilePatterns, @@ -216,6 +226,7 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) { Target: target, SkipDirs: f.SkipDirs.Value(), SkipFiles: f.SkipFiles.Value(), + OnlyDirs: f.OnlyDirs.Value(), OfflineScan: f.OfflineScan.Value(), Scanners: xstrings.ToTSlice[types.Scanner](f.Scanners.Value()), FilePatterns: f.FilePatterns.Value(), diff --git a/pkg/flag/scan_flags_test.go b/pkg/flag/scan_flags_test.go index e0f8ffb9deb6..6263a563a03a 100644 --- a/pkg/flag/scan_flags_test.go +++ b/pkg/flag/scan_flags_test.go @@ -16,6 +16,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) { type fields struct { skipDirs []string skipFiles []string + onlyDirs []string offlineScan bool scanners string distro string @@ -134,6 +135,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) { t.Cleanup(viper.Reset) setSliceValue(flag.SkipDirsFlag.ConfigName, tt.fields.skipDirs) setSliceValue(flag.SkipFilesFlag.ConfigName, tt.fields.skipFiles) + setSliceValue(flag.OnlyDirsFlag.ConfigName, tt.fields.onlyDirs) setValue(flag.OfflineScanFlag.ConfigName, tt.fields.offlineScan) setValue(flag.ScannersFlag.ConfigName, tt.fields.scanners) setValue(flag.DistroFlag.ConfigName, tt.fields.distro) @@ -142,6 +144,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) { f := &flag.ScanFlagGroup{ SkipDirs: flag.SkipDirsFlag.Clone(), SkipFiles: flag.SkipFilesFlag.Clone(), + OnlyDirs: flag.OnlyDirsFlag.Clone(), OfflineScan: flag.OfflineScanFlag.Clone(), Scanners: flag.ScannersFlag.Clone(), DistroFlag: flag.DistroFlag.Clone(), diff --git a/pkg/iac/adapters/arm/adapt.go b/pkg/iac/adapters/arm/adapt.go deleted file mode 100644 index a6bcbec2a42e..000000000000 --- a/pkg/iac/adapters/arm/adapt.go +++ /dev/null @@ -1,49 +0,0 @@ -package arm - -import ( - "context" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/appservice" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/authorization" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/compute" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/container" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/database" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/datafactory" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/datalake" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/keyvault" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/monitor" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/network" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/securitycenter" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/storage" - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm/synapse" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure" - scanner "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/state" -) - -// Adapt adapts an azure arm instance -func Adapt(ctx context.Context, deployment scanner.Deployment) *state.State { - return &state.State{ - Azure: adaptAzure(deployment), - } -} - -func adaptAzure(deployment scanner.Deployment) azure.Azure { - - return azure.Azure{ - AppService: appservice.Adapt(deployment), - Authorization: authorization.Adapt(deployment), - Compute: compute.Adapt(deployment), - Container: container.Adapt(deployment), - Database: database.Adapt(deployment), - DataFactory: datafactory.Adapt(deployment), - DataLake: datalake.Adapt(deployment), - KeyVault: keyvault.Adapt(deployment), - Monitor: monitor.Adapt(deployment), - Network: network.Adapt(deployment), - SecurityCenter: securitycenter.Adapt(deployment), - Storage: storage.Adapt(deployment), - Synapse: synapse.Adapt(deployment), - } - -} diff --git a/pkg/iac/adapters/arm/appservice/adapt.go b/pkg/iac/adapters/arm/appservice/adapt.go deleted file mode 100644 index d5512cc5d3b4..000000000000 --- a/pkg/iac/adapters/arm/appservice/adapt.go +++ /dev/null @@ -1,58 +0,0 @@ -package appservice - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(deployment azure.Deployment) appservice.AppService { - return appservice.AppService{ - Services: adaptServices(deployment), - FunctionApps: adaptFunctionApps(deployment), - } -} - -func adaptFunctionApps(deployment azure.Deployment) []appservice.FunctionApp { - var functionApps []appservice.FunctionApp - - for _, resource := range deployment.GetResourcesByType("Microsoft.Web/sites") { - functionApps = append(functionApps, adaptFunctionApp(resource)) - } - return functionApps -} - -func adaptServices(deployment azure.Deployment) []appservice.Service { - var services []appservice.Service - for _, resource := range deployment.GetResourcesByType("Microsoft.Web/sites") { - services = append(services, adaptService(resource)) - } - return services -} - -func adaptFunctionApp(resource azure.Resource) appservice.FunctionApp { - return appservice.FunctionApp{ - Metadata: resource.Metadata, - HTTPSOnly: resource.Properties.GetMapValue("httpsOnly").AsBoolValue(false, resource.Properties.GetMetadata()), - } -} - -func adaptService(resource azure.Resource) appservice.Service { - return appservice.Service{ - Metadata: resource.Metadata, - EnableClientCert: resource.Properties.GetMapValue("clientCertEnabled").AsBoolValue(false, resource.Properties.GetMetadata()), - Identity: struct{ Type iacTypes.StringValue }{ - Type: resource.Properties.GetMapValue("identity").GetMapValue("type").AsStringValue("", resource.Properties.GetMetadata()), - }, - Authentication: struct{ Enabled iacTypes.BoolValue }{ - Enabled: resource.Properties.GetMapValue("siteAuthSettings").GetMapValue("enabled").AsBoolValue(false, resource.Properties.GetMetadata()), - }, - Site: struct { - EnableHTTP2 iacTypes.BoolValue - MinimumTLSVersion iacTypes.StringValue - }{ - EnableHTTP2: resource.Properties.GetMapValue("httpsOnly").AsBoolValue(false, resource.Properties.GetMetadata()), - MinimumTLSVersion: resource.Properties.GetMapValue("minTlsVersion").AsStringValue("", resource.Properties.GetMetadata()), - }, - } -} diff --git a/pkg/iac/adapters/arm/authorization/adapt.go b/pkg/iac/adapters/arm/authorization/adapt.go deleted file mode 100644 index 08798665eaab..000000000000 --- a/pkg/iac/adapters/arm/authorization/adapt.go +++ /dev/null @@ -1,38 +0,0 @@ -package authorization - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/authorization" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) authorization.Authorization { - return authorization.Authorization{ - RoleDefinitions: adaptRoleDefinitions(deployment), - } -} - -func adaptRoleDefinitions(deployment azure.Deployment) (roleDefinitions []authorization.RoleDefinition) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Authorization/roleDefinitions") { - roleDefinitions = append(roleDefinitions, adaptRoleDefinition(resource)) - } - return roleDefinitions -} - -func adaptRoleDefinition(resource azure.Resource) authorization.RoleDefinition { - - return authorization.RoleDefinition{ - Metadata: resource.Metadata, - Permissions: adaptPermissions(resource), - AssignableScopes: resource.Properties.GetMapValue("assignableScopes").AsStringValuesList(""), - } -} - -func adaptPermissions(resource azure.Resource) (permissions []authorization.Permission) { - for _, permission := range resource.Properties.GetMapValue("permissions").AsList() { - permissions = append(permissions, authorization.Permission{ - Metadata: resource.Metadata, - Actions: permission.GetMapValue("actions").AsStringValuesList(""), - }) - } - return permissions -} diff --git a/pkg/iac/adapters/arm/compute/adapt.go b/pkg/iac/adapters/arm/compute/adapt.go deleted file mode 100644 index bdcbfac500fd..000000000000 --- a/pkg/iac/adapters/arm/compute/adapt.go +++ /dev/null @@ -1,85 +0,0 @@ -package compute - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/compute" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(deployment azure.Deployment) compute.Compute { - return compute.Compute{ - LinuxVirtualMachines: adaptLinuxVirtualMachines(deployment), - WindowsVirtualMachines: adaptWindowsVirtualMachines(deployment), - ManagedDisks: adaptManagedDisks(deployment), - } -} - -func adaptManagedDisks(deployment azure.Deployment) (managedDisks []compute.ManagedDisk) { - - for _, resource := range deployment.GetResourcesByType("Microsoft.Compute/disks") { - managedDisks = append(managedDisks, adaptManagedDisk(resource)) - } - - return managedDisks -} - -func adaptManagedDisk(resource azure.Resource) compute.ManagedDisk { - hasEncryption := resource.Properties.HasKey("encryption") - - return compute.ManagedDisk{ - Metadata: resource.Metadata, - Encryption: compute.Encryption{ - Metadata: resource.Metadata, - Enabled: iacTypes.Bool(hasEncryption, resource.Metadata), - }, - } -} - -func adaptWindowsVirtualMachines(deployment azure.Deployment) (windowsVirtualMachines []compute.WindowsVirtualMachine) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Compute/virtualMachines") { - if resource.Properties.GetMapValue("osProfile").GetMapValue("windowsConfiguration").AsMap() != nil { - windowsVirtualMachines = append(windowsVirtualMachines, adaptWindowsVirtualMachine(resource)) - } - } - - return windowsVirtualMachines -} - -func adaptWindowsVirtualMachine(resource azure.Resource) compute.WindowsVirtualMachine { - return compute.WindowsVirtualMachine{ - Metadata: resource.Metadata, - VirtualMachine: compute.VirtualMachine{ - Metadata: resource.Metadata, - CustomData: resource.Properties.GetMapValue("osProfile"). - GetMapValue("customData").AsStringValue("", resource.Metadata), - }, - } -} - -func adaptLinuxVirtualMachines(deployment azure.Deployment) (linuxVirtualMachines []compute.LinuxVirtualMachine) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Compute/virtualMachines") { - if resource.Properties.GetMapValue("osProfile").GetMapValue("linuxConfiguration").AsMap() != nil { - linuxVirtualMachines = append(linuxVirtualMachines, adaptLinuxVirtualMachine(resource)) - } - } - - return linuxVirtualMachines -} - -func adaptLinuxVirtualMachine(resource azure.Resource) compute.LinuxVirtualMachine { - return compute.LinuxVirtualMachine{ - Metadata: resource.Metadata, - VirtualMachine: compute.VirtualMachine{ - Metadata: resource.Metadata, - CustomData: resource.Properties.GetMapValue("osProfile"). - GetMapValue("customData").AsStringValue("", resource.Metadata), - }, - OSProfileLinuxConfig: compute.OSProfileLinuxConfig{ - Metadata: resource.Metadata, - DisablePasswordAuthentication: resource.Properties.GetMapValue("osProfile"). - GetMapValue("linuxConfiguration"). - GetMapValue("disablePasswordAuthentication").AsBoolValue(false, resource.Metadata), - }, - } - -} diff --git a/pkg/iac/adapters/arm/compute/adapt_test.go b/pkg/iac/adapters/arm/compute/adapt_test.go deleted file mode 100644 index 983b4d489171..000000000000 --- a/pkg/iac/adapters/arm/compute/adapt_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package compute - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_AdaptLinuxVM(t *testing.T) { - - input := azure2.Deployment{ - Resources: []azure2.Resource{ - { - Type: azure2.NewValue("Microsoft.Compute/virtualMachines", types.NewTestMetadata()), - Properties: azure2.NewValue(map[string]azure2.Value{ - "osProfile": azure2.NewValue(map[string]azure2.Value{ - "linuxConfiguration": azure2.NewValue(map[string]azure2.Value{ - "disablePasswordAuthentication": azure2.NewValue(true, types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, - }, - } - - output := Adapt(input) - - require.Len(t, output.LinuxVirtualMachines, 1) - require.Empty(t, output.WindowsVirtualMachines) - - linuxVM := output.LinuxVirtualMachines[0] - assert.True(t, linuxVM.OSProfileLinuxConfig.DisablePasswordAuthentication.IsTrue()) - -} - -func Test_AdaptWindowsVM(t *testing.T) { - - input := azure2.Deployment{ - Resources: []azure2.Resource{ - { - Type: azure2.NewValue("Microsoft.Compute/virtualMachines", types.NewTestMetadata()), - Properties: azure2.NewValue(map[string]azure2.Value{ - "osProfile": azure2.NewValue(map[string]azure2.Value{ - "windowsConfiguration": azure2.NewValue(make(map[string]azure2.Value), types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, - }, - } - - output := Adapt(input) - - require.Empty(t, output.LinuxVirtualMachines) - require.Len(t, output.WindowsVirtualMachines, 1) -} diff --git a/pkg/iac/adapters/arm/container/adapt.go b/pkg/iac/adapters/arm/container/adapt.go deleted file mode 100644 index 2172553e4cfb..000000000000 --- a/pkg/iac/adapters/arm/container/adapt.go +++ /dev/null @@ -1,17 +0,0 @@ -package container - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) container.Container { - return container.Container{ - KubernetesClusters: adaptKubernetesClusters(deployment), - } -} - -func adaptKubernetesClusters(deployment azure.Deployment) []container.KubernetesCluster { - - return nil -} diff --git a/pkg/iac/adapters/arm/database/adapt.go b/pkg/iac/adapters/arm/database/adapt.go deleted file mode 100644 index 834865fc22d0..000000000000 --- a/pkg/iac/adapters/arm/database/adapt.go +++ /dev/null @@ -1,35 +0,0 @@ -package database - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) database.Database { - return database.Database{ - MSSQLServers: adaptMSSQLServers(deployment), - MariaDBServers: adaptMariaDBServers(deployment), - MySQLServers: adaptMySQLServers(deployment), - PostgreSQLServers: adaptPostgreSQLServers(deployment), - } -} - -func adaptMySQLServers(deployment azure.Deployment) (mysqlDbServers []database.MySQLServer) { - for _, resource := range deployment.GetResourcesByType("Microsoft.DBforMySQL/servers") { - mysqlDbServers = append(mysqlDbServers, adaptMySQLServer(resource, deployment)) - } - return mysqlDbServers -} - -func adaptMySQLServer(resource azure.Resource, deployment azure.Deployment) database.MySQLServer { - return database.MySQLServer{ - Metadata: resource.Metadata, - Server: database.Server{ - Metadata: resource.Metadata, - EnableSSLEnforcement: resource.Properties.GetMapValue("sslEnforcement").AsBoolValue(false, resource.Metadata), - MinimumTLSVersion: resource.Properties.GetMapValue("minimalTlsVersion").AsStringValue("TLSEnforcementDisabled", resource.Metadata), - EnablePublicNetworkAccess: resource.Properties.GetMapValue("publicNetworkAccess").AsBoolValue(false, resource.Metadata), - FirewallRules: addFirewallRule(resource), - }, - } -} diff --git a/pkg/iac/adapters/arm/database/firewall.go b/pkg/iac/adapters/arm/database/firewall.go deleted file mode 100644 index ff6ff5a56cd3..000000000000 --- a/pkg/iac/adapters/arm/database/firewall.go +++ /dev/null @@ -1,18 +0,0 @@ -package database - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func addFirewallRule(resource azure.Resource) []database.FirewallRule { - var rules []database.FirewallRule - for _, rule := range resource.Properties.GetMapValue("firewallRules").AsMap() { - rules = append(rules, database.FirewallRule{ - Metadata: rule.Metadata, - StartIP: rule.GetMapValue("startIpAddress").AsStringValue("", rule.Metadata), - EndIP: rule.GetMapValue("endIpAddress").AsStringValue("", rule.Metadata), - }) - } - return rules -} diff --git a/pkg/iac/adapters/arm/database/maria.go b/pkg/iac/adapters/arm/database/maria.go deleted file mode 100644 index 083c3b0540e5..000000000000 --- a/pkg/iac/adapters/arm/database/maria.go +++ /dev/null @@ -1,27 +0,0 @@ -package database - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func adaptMariaDBServers(deployment azure.Deployment) (mariaDbServers []database.MariaDBServer) { - for _, resource := range deployment.GetResourcesByType("Microsoft.DBforMariaDB/servers") { - mariaDbServers = append(mariaDbServers, adaptMariaDBServer(resource, deployment)) - } - return mariaDbServers - -} - -func adaptMariaDBServer(resource azure.Resource, deployment azure.Deployment) database.MariaDBServer { - return database.MariaDBServer{ - Metadata: resource.Metadata, - Server: database.Server{ - Metadata: resource.Metadata, - EnableSSLEnforcement: resource.Properties.GetMapValue("sslEnforcement").AsBoolValue(false, resource.Metadata), - MinimumTLSVersion: resource.Properties.GetMapValue("minimalTlsVersion").AsStringValue("TLSEnforcementDisabled", resource.Metadata), - EnablePublicNetworkAccess: resource.Properties.GetMapValue("publicNetworkAccess").AsBoolValue(false, resource.Metadata), - FirewallRules: addFirewallRule(resource), - }, - } -} diff --git a/pkg/iac/adapters/arm/database/mssql.go b/pkg/iac/adapters/arm/database/mssql.go deleted file mode 100644 index 95cc41ed6b25..000000000000 --- a/pkg/iac/adapters/arm/database/mssql.go +++ /dev/null @@ -1,61 +0,0 @@ -package database - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptMSSQLServers(deployment azure2.Deployment) (msSQlServers []database.MSSQLServer) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Sql/servers") { - msSQlServers = append(msSQlServers, adaptMSSQLServer(resource, deployment)) - } - return msSQlServers -} - -func adaptMSSQLServer(resource azure2.Resource, deployment azure2.Deployment) database.MSSQLServer { - return database.MSSQLServer{ - Metadata: resource.Metadata, - Server: database.Server{ - Metadata: resource.Metadata, - EnableSSLEnforcement: resource.Properties.GetMapValue("sslEnforcement").AsBoolValue(false, resource.Metadata), - MinimumTLSVersion: resource.Properties.GetMapValue("minimalTlsVersion").AsStringValue("TLSEnforcementDisabled", resource.Metadata), - EnablePublicNetworkAccess: resource.Properties.GetMapValue("publicNetworkAccess").AsBoolValue(false, resource.Metadata), - FirewallRules: addFirewallRule(resource), - }, - ExtendedAuditingPolicies: adaptExtendedAuditingPolicies(resource, deployment), - SecurityAlertPolicies: adaptSecurityAlertPolicies(resource, deployment), - } -} - -func adaptExtendedAuditingPolicies(resource azure2.Resource, deployment azure2.Deployment) (policies []database.ExtendedAuditingPolicy) { - - for _, policy := range deployment.GetResourcesByType("Microsoft.Sql/servers/extendedAuditingSettings") { - policies = append(policies, database.ExtendedAuditingPolicy{ - Metadata: policy.Metadata, - RetentionInDays: policy.Properties.GetMapValue("retentionDays").AsIntValue(0, policy.Metadata), - }) - } - - return policies -} - -func adaptSecurityAlertPolicies(resource azure2.Resource, deployment azure2.Deployment) (policies []database.SecurityAlertPolicy) { - for _, policy := range deployment.GetResourcesByType("Microsoft.Sql/servers/securityAlertPolicies") { - policies = append(policies, database.SecurityAlertPolicy{ - Metadata: policy.Metadata, - EmailAddresses: adaptStringList(policy.Properties.GetMapValue("emailAddresses")), - DisabledAlerts: adaptStringList(policy.Properties.GetMapValue("disabledAlerts")), - EmailAccountAdmins: policy.Properties.GetMapValue("emailAccountAdmins").AsBoolValue(false, policy.Metadata), - }) - } - return policies -} - -func adaptStringList(value azure2.Value) []iacTypes.StringValue { - var list []iacTypes.StringValue - for _, v := range value.AsList() { - list = append(list, v.AsStringValue("", value.Metadata)) - } - return list -} diff --git a/pkg/iac/adapters/arm/database/postgresql.go b/pkg/iac/adapters/arm/database/postgresql.go deleted file mode 100644 index 155667c65f39..000000000000 --- a/pkg/iac/adapters/arm/database/postgresql.go +++ /dev/null @@ -1,64 +0,0 @@ -package database - -import ( - "fmt" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptPostgreSQLServers(deployment azure.Deployment) (databases []database.PostgreSQLServer) { - for _, resource := range deployment.GetResourcesByType("Microsoft.DBforPostgreSQL/servers") { - databases = append(databases, adaptPostgreSQLServer(resource, deployment)) - } - - return databases -} - -func adaptPostgreSQLServer(resource azure.Resource, deployment azure.Deployment) database.PostgreSQLServer { - return database.PostgreSQLServer{ - Metadata: resource.Metadata, - Server: database.Server{ - Metadata: resource.Metadata, - EnableSSLEnforcement: resource.Properties.GetMapValue("sslEnforcement").AsBoolValue(false, resource.Metadata), - MinimumTLSVersion: resource.Properties.GetMapValue("minimalTlsVersion").AsStringValue("TLSEnforcementDisabled", resource.Metadata), - EnablePublicNetworkAccess: resource.Properties.GetMapValue("publicNetworkAccess").AsBoolValue(false, resource.Metadata), - FirewallRules: addFirewallRule(resource), - }, - Config: adaptPostgreSQLConfiguration(resource, deployment), - } -} - -func adaptPostgreSQLConfiguration(resource azure.Resource, deployment azure.Deployment) database.PostgresSQLConfig { - - parent := fmt.Sprintf("%s/", resource.Name.AsString()) - - config := database.PostgresSQLConfig{ - Metadata: resource.Metadata, - LogCheckpoints: iacTypes.BoolDefault(false, resource.Metadata), - ConnectionThrottling: iacTypes.BoolDefault(false, resource.Metadata), - LogConnections: iacTypes.BoolDefault(false, resource.Metadata), - } - - for _, configuration := range deployment.GetResourcesByType("Microsoft.DBforPostgreSQL/servers/configurations") { - if strings.HasPrefix(configuration.Name.AsString(), parent) { - val := configuration.Properties.GetMapValue("value") - if strings.HasSuffix(configuration.Name.AsString(), "log_checkpoints") { - config.LogCheckpoints = val.AsBoolValue(false, configuration.Metadata) - continue - } - if strings.HasSuffix(configuration.Name.AsString(), "log_connections") { - config.LogConnections = val.AsBoolValue(false, configuration.Metadata) - continue - } - if strings.HasSuffix(configuration.Name.AsString(), "connection_throttling") { - config.ConnectionThrottling = val.AsBoolValue(false, configuration.Metadata) - continue - } - } - } - - return config -} diff --git a/pkg/iac/adapters/arm/datafactory/adapt.go b/pkg/iac/adapters/arm/datafactory/adapt.go deleted file mode 100644 index 3dbb44ecd0b0..000000000000 --- a/pkg/iac/adapters/arm/datafactory/adapt.go +++ /dev/null @@ -1,27 +0,0 @@ -package datafactory - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datafactory" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) datafactory.DataFactory { - - return datafactory.DataFactory{ - DataFactories: adaptDataFactories(deployment), - } -} - -func adaptDataFactories(deployment azure.Deployment) (factories []datafactory.Factory) { - for _, resource := range deployment.GetResourcesByType("Microsoft.DataFactory/factories") { - factories = append(factories, adaptDataFactory(resource)) - } - return factories -} - -func adaptDataFactory(resource azure.Resource) datafactory.Factory { - return datafactory.Factory{ - Metadata: resource.Metadata, - EnablePublicNetwork: resource.Properties.GetMapValue("publicNetworkAccess").AsBoolValue(true, resource.Metadata), - } -} diff --git a/pkg/iac/adapters/arm/datalake/adapt.go b/pkg/iac/adapters/arm/datalake/adapt.go deleted file mode 100644 index 301650d50114..000000000000 --- a/pkg/iac/adapters/arm/datalake/adapt.go +++ /dev/null @@ -1,28 +0,0 @@ -package datalake - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datalake" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) datalake.DataLake { - - return datalake.DataLake{ - Stores: adaptStores(deployment), - } -} - -func adaptStores(deployment azure.Deployment) (stores []datalake.Store) { - for _, resource := range deployment.GetResourcesByType("Microsoft.DataLakeStore/accounts") { - stores = append(stores, adaptStore(resource)) - } - - return stores -} - -func adaptStore(resource azure.Resource) datalake.Store { - return datalake.Store{ - Metadata: resource.Metadata, - EnableEncryption: resource.Properties.GetMapValue("encryptionState").AsBoolValue(false, resource.Metadata), - } -} diff --git a/pkg/iac/adapters/arm/keyvault/adapt.go b/pkg/iac/adapters/arm/keyvault/adapt.go deleted file mode 100644 index 71a1fbd54f33..000000000000 --- a/pkg/iac/adapters/arm/keyvault/adapt.go +++ /dev/null @@ -1,64 +0,0 @@ -package keyvault - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/keyvault" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) keyvault.KeyVault { - return keyvault.KeyVault{ - Vaults: adaptVaults(deployment), - } -} - -func adaptVaults(deployment azure.Deployment) (vaults []keyvault.Vault) { - for _, resource := range deployment.GetResourcesByType("Microsoft.KeyVault/vaults") { - vaults = append(vaults, adaptVault(resource, deployment)) - } - - return vaults -} - -func adaptVault(resource azure.Resource, deployment azure.Deployment) keyvault.Vault { - return keyvault.Vault{ - Metadata: resource.Metadata, - Secrets: adaptSecrets(resource, deployment), - Keys: adaptKeys(resource, deployment), - EnablePurgeProtection: resource.Properties.GetMapValue("enablePurgeProtection").AsBoolValue(false, resource.Metadata), - SoftDeleteRetentionDays: resource.Properties.GetMapValue("softDeleteRetentionInDays").AsIntValue(7, resource.Metadata), - NetworkACLs: keyvault.NetworkACLs{ - Metadata: resource.Metadata, - DefaultAction: resource.Properties.GetMapValue("properties").GetMapValue("networkAcls").GetMapValue("defaultAction").AsStringValue("", resource.Metadata), - }, - } -} - -func adaptKeys(resource azure.Resource, deployment azure.Deployment) (keys []keyvault.Key) { - for _, resource := range deployment.GetResourcesByType("Microsoft.KeyVault/vaults/keys") { - keys = append(keys, adaptKey(resource)) - } - - return keys -} - -func adaptKey(resource azure.Resource) keyvault.Key { - return keyvault.Key{ - Metadata: resource.Metadata, - ExpiryDate: resource.Properties.GetMapValue("attributes").GetMapValue("exp").AsTimeValue(resource.Metadata), - } -} - -func adaptSecrets(resource azure.Resource, deployment azure.Deployment) (secrets []keyvault.Secret) { - for _, resource := range deployment.GetResourcesByType("Microsoft.KeyVault/vaults/secrets") { - secrets = append(secrets, adaptSecret(resource)) - } - return secrets -} - -func adaptSecret(resource azure.Resource) keyvault.Secret { - return keyvault.Secret{ - Metadata: resource.Metadata, - ContentType: resource.Properties.GetMapValue("contentType").AsStringValue("", resource.Metadata), - ExpiryDate: resource.Properties.GetMapValue("attributes").GetMapValue("exp").AsTimeValue(resource.Metadata), - } -} diff --git a/pkg/iac/adapters/arm/monitor/adapt.go b/pkg/iac/adapters/arm/monitor/adapt.go deleted file mode 100644 index e821ec256c92..000000000000 --- a/pkg/iac/adapters/arm/monitor/adapt.go +++ /dev/null @@ -1,45 +0,0 @@ -package monitor - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/monitor" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(deployment azure.Deployment) monitor.Monitor { - return monitor.Monitor{ - LogProfiles: adaptLogProfiles(deployment), - } -} - -func adaptLogProfiles(deployment azure.Deployment) (logProfiles []monitor.LogProfile) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Insights/logProfiles") { - logProfiles = append(logProfiles, adaptLogProfile(resource)) - } - return logProfiles -} - -func adaptLogProfile(resource azure.Resource) monitor.LogProfile { - categories := resource.Properties.GetMapValue("categories").AsList() - var categoriesList []types.StringValue - for _, category := range categories { - categoriesList = append(categoriesList, category.AsStringValue("", category.Metadata)) - } - - locations := resource.Properties.GetMapValue("locations").AsList() - var locationsList []types.StringValue - for _, location := range locations { - locationsList = append(locationsList, location.AsStringValue("", location.Metadata)) - } - - return monitor.LogProfile{ - Metadata: resource.Metadata, - RetentionPolicy: monitor.RetentionPolicy{ - Metadata: resource.Metadata, - Enabled: resource.Properties.GetMapValue("retentionPolicy").GetMapValue("enabled").AsBoolValue(false, resource.Metadata), - Days: resource.Properties.GetMapValue("retentionPolicy").GetMapValue("days").AsIntValue(0, resource.Metadata), - }, - Categories: categoriesList, - Locations: locationsList, - } -} diff --git a/pkg/iac/adapters/arm/network/adapt.go b/pkg/iac/adapters/arm/network/adapt.go deleted file mode 100644 index 214cf75c3c90..000000000000 --- a/pkg/iac/adapters/arm/network/adapt.go +++ /dev/null @@ -1,126 +0,0 @@ -package network - -import ( - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/network" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(deployment azure.Deployment) network.Network { - return network.Network{ - SecurityGroups: adaptSecurityGroups(deployment), - NetworkWatcherFlowLogs: adaptNetworkWatcherFlowLogs(deployment), - } -} - -func adaptSecurityGroups(deployment azure.Deployment) (sgs []network.SecurityGroup) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Network/networkSecurityGroups") { - sgs = append(sgs, adaptSecurityGroup(resource, deployment)) - } - return sgs - -} - -func adaptSecurityGroup(resource azure.Resource, deployment azure.Deployment) network.SecurityGroup { - return network.SecurityGroup{ - Metadata: resource.Metadata, - Rules: adaptSecurityGroupRules(deployment), - } -} - -func adaptSecurityGroupRules(deployment azure.Deployment) (rules []network.SecurityGroupRule) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Network/networkSecurityGroups/securityRules") { - rules = append(rules, adaptSecurityGroupRule(resource)) - } - return rules -} - -func adaptSecurityGroupRule(resource azure.Resource) network.SecurityGroupRule { - sourceAddressPrefixes := resource.Properties.GetMapValue("sourceAddressPrefixes").AsStringValuesList("") - sourceAddressPrefixes = append(sourceAddressPrefixes, resource.Properties.GetMapValue("sourceAddressPrefix").AsStringValue("", resource.Metadata)) - - var sourcePortRanges []network.PortRange - for _, portRange := range resource.Properties.GetMapValue("sourcePortRanges").AsList() { - sourcePortRanges = append(sourcePortRanges, expandRange(portRange.AsString(), resource.Metadata)) - } - sourcePortRanges = append(sourcePortRanges, expandRange(resource.Properties.GetMapValue("sourcePortRange").AsString(), resource.Metadata)) - - destinationAddressPrefixes := resource.Properties.GetMapValue("destinationAddressPrefixes").AsStringValuesList("") - destinationAddressPrefixes = append(destinationAddressPrefixes, resource.Properties.GetMapValue("destinationAddressPrefix").AsStringValue("", resource.Metadata)) - - var destinationPortRanges []network.PortRange - for _, portRange := range resource.Properties.GetMapValue("destinationPortRanges").AsList() { - destinationPortRanges = append(destinationPortRanges, expandRange(portRange.AsString(), resource.Metadata)) - } - destinationPortRanges = append(destinationPortRanges, expandRange(resource.Properties.GetMapValue("destinationPortRange").AsString(), resource.Metadata)) - - allow := iacTypes.BoolDefault(false, resource.Metadata) - if resource.Properties.GetMapValue("access").AsString() == "Allow" { - allow = iacTypes.Bool(true, resource.Metadata) - } - - outbound := iacTypes.BoolDefault(false, resource.Metadata) - if resource.Properties.GetMapValue("direction").AsString() == "Outbound" { - outbound = iacTypes.Bool(true, resource.Metadata) - } - - return network.SecurityGroupRule{ - Metadata: resource.Metadata, - Outbound: outbound, - Allow: allow, - SourceAddresses: sourceAddressPrefixes, - SourcePorts: sourcePortRanges, - DestinationAddresses: destinationAddressPrefixes, - DestinationPorts: destinationPortRanges, - Protocol: resource.Properties.GetMapValue("protocol").AsStringValue("", resource.Metadata), - } -} - -func adaptNetworkWatcherFlowLogs(deployment azure.Deployment) (flowLogs []network.NetworkWatcherFlowLog) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Network/networkWatchers/flowLogs") { - flowLogs = append(flowLogs, adaptNetworkWatcherFlowLog(resource)) - } - return flowLogs -} - -func adaptNetworkWatcherFlowLog(resource azure.Resource) network.NetworkWatcherFlowLog { - return network.NetworkWatcherFlowLog{ - Metadata: resource.Metadata, - RetentionPolicy: network.RetentionPolicy{ - Metadata: resource.Metadata, - Enabled: resource.Properties.GetMapValue("retentionPolicy").GetMapValue("enabled").AsBoolValue(false, resource.Metadata), - Days: resource.Properties.GetMapValue("retentionPolicy").GetMapValue("days").AsIntValue(0, resource.Metadata), - }, - } -} - -func expandRange(r string, m iacTypes.Metadata) network.PortRange { - start := 0 - end := 65535 - switch { - case r == "*": - case strings.Contains(r, "-"): - if parts := strings.Split(r, "-"); len(parts) == 2 { - if p1, err := strconv.ParseInt(parts[0], 10, 32); err == nil { - start = int(p1) - } - if p2, err := strconv.ParseInt(parts[1], 10, 32); err == nil { - end = int(p2) - } - } - default: - if val, err := strconv.ParseInt(r, 10, 32); err == nil { - start = int(val) - end = int(val) - } - } - - return network.PortRange{ - Metadata: m, - Start: iacTypes.Int(start, m), - End: iacTypes.Int(end, m), - } -} diff --git a/pkg/iac/adapters/arm/securitycenter/adapt.go b/pkg/iac/adapters/arm/securitycenter/adapt.go deleted file mode 100644 index 69a5de5b8d79..000000000000 --- a/pkg/iac/adapters/arm/securitycenter/adapt.go +++ /dev/null @@ -1,43 +0,0 @@ -package securitycenter - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/securitycenter" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" -) - -func Adapt(deployment azure.Deployment) securitycenter.SecurityCenter { - return securitycenter.SecurityCenter{ - Contacts: adaptContacts(deployment), - Subscriptions: adaptSubscriptions(deployment), - } -} - -func adaptContacts(deployment azure.Deployment) (contacts []securitycenter.Contact) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Security/securityContacts") { - contacts = append(contacts, adaptContact(resource)) - } - - return contacts -} - -func adaptContact(resource azure.Resource) securitycenter.Contact { - return securitycenter.Contact{ - Metadata: resource.Metadata, - EnableAlertNotifications: resource.Properties.GetMapValue("email").AsBoolValue(false, resource.Metadata), - Phone: resource.Properties.GetMapValue("phone").AsStringValue("", resource.Metadata), - } -} - -func adaptSubscriptions(deployment azure.Deployment) (subscriptions []securitycenter.SubscriptionPricing) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Security/pricings") { - subscriptions = append(subscriptions, adaptSubscription(resource)) - } - return subscriptions -} - -func adaptSubscription(resource azure.Resource) securitycenter.SubscriptionPricing { - return securitycenter.SubscriptionPricing{ - Metadata: resource.Metadata, - Tier: resource.Properties.GetMapValue("pricingTier").AsStringValue("Free", resource.Metadata), - } -} diff --git a/pkg/iac/adapters/arm/storage/adapt.go b/pkg/iac/adapters/arm/storage/adapt.go deleted file mode 100644 index 26a0d10c8616..000000000000 --- a/pkg/iac/adapters/arm/storage/adapt.go +++ /dev/null @@ -1,72 +0,0 @@ -package storage - -import ( - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(deployment azure.Deployment) storage.Storage { - return storage.Storage{ - Accounts: adaptAccounts(deployment), - } -} - -func adaptAccounts(deployment azure.Deployment) []storage.Account { - var accounts []storage.Account - for _, resource := range deployment.GetResourcesByType("Microsoft.Storage/storageAccounts") { - - acl := resource.Properties.GetMapValue("networkAcls") - - var bypasses []types.StringValue - bypassProp := acl.GetMapValue("bypass") - for _, bypass := range strings.Split(bypassProp.AsString(), ",") { - bypasses = append(bypasses, types.String(strings.TrimSpace(bypass), bypassProp.GetMetadata())) - } - - networkRule := storage.NetworkRule{ - Metadata: acl.GetMetadata(), - Bypass: bypasses, - AllowByDefault: types.Bool(acl.GetMapValue("defaultAction").EqualTo("Allow"), acl.GetMetadata()), - } - - var queues []storage.Queue - for _, queueResource := range resource.GetResourcesByType("queueServices/queues") { - queues = append(queues, storage.Queue{ - Metadata: queueResource.Metadata, - Name: queueResource.Name.AsStringValue("", queueResource.Metadata), - }) - } - - var containers []storage.Container - for _, containerResource := range resource.GetResourcesByType("containerServices/containers") { - containers = append(containers, storage.Container{ - Metadata: containerResource.Metadata, - PublicAccess: containerResource.Properties.GetMapValue("publicAccess").AsStringValue("None", containerResource.Metadata), - }) - } - - account := storage.Account{ - Metadata: resource.Metadata, - NetworkRules: []storage.NetworkRule{networkRule}, - EnforceHTTPS: resource.Properties.GetMapValue("supportsHttpsTrafficOnly").AsBoolValue(false, resource.Properties.GetMetadata()), - Containers: containers, - QueueProperties: storage.QueueProperties{ - Metadata: resource.Properties.GetMetadata(), - EnableLogging: types.BoolDefault(false, resource.Properties.GetMetadata()), - }, - MinimumTLSVersion: resource.Properties.GetMapValue("minimumTlsVersion").AsStringValue("", resource.Properties.GetMetadata()), - Queues: queues, - } - - publicNetworkAccess := resource.Properties.GetMapValue("publicNetworkAccess") - account.PublicNetworkAccess = types.Bool( - publicNetworkAccess.AsStringValue("Enabled", publicNetworkAccess.Metadata).EqualTo("Enabled"), - publicNetworkAccess.Metadata, - ) - accounts = append(accounts, account) - } - return accounts -} diff --git a/pkg/iac/adapters/arm/storage/adapt_test.go b/pkg/iac/adapters/arm/storage/adapt_test.go deleted file mode 100644 index 372986f23529..000000000000 --- a/pkg/iac/adapters/arm/storage/adapt_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package storage - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_AdaptStorageDefaults(t *testing.T) { - - input := azure.Deployment{ - Resources: []azure.Resource{ - { - Type: azure.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), - Properties: azure.NewValue(make(map[string]azure.Value), types.NewTestMetadata()), - }, - }, - } - - output := Adapt(input) - - require.Len(t, output.Accounts, 1) - - account := output.Accounts[0] - assert.Equal(t, "", account.MinimumTLSVersion.Value()) - assert.False(t, account.EnforceHTTPS.Value()) - assert.True(t, account.PublicNetworkAccess.Value()) - -} - -func Test_AdaptStorage(t *testing.T) { - - input := azure.Deployment{ - Resources: []azure.Resource{ - { - Type: azure.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), - Name: azure.Value{}, - Properties: azure.NewValue(map[string]azure.Value{ - "minimumTlsVersion": azure.NewValue("TLS1_2", types.NewTestMetadata()), - "supportsHttpsTrafficOnly": azure.NewValue(true, types.NewTestMetadata()), - "publicNetworkAccess": azure.NewValue("Disabled", types.NewTestMetadata()), - "networkAcls": azure.NewValue(map[string]azure.Value{ - "bypass": azure.NewValue("Logging, Metrics", types.NewTestMetadata()), - "defaultAction": azure.NewValue("Allow", types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, types.NewTestMetadata()), - }, - }, - } - - output := Adapt(input) - - require.Len(t, output.Accounts, 1) - - expected := storage.Storage{ - Accounts: []storage.Account{{ - MinimumTLSVersion: types.StringTest("TLS1_2"), - EnforceHTTPS: types.BoolTest(true), - PublicNetworkAccess: types.BoolTest(false), - NetworkRules: []storage.NetworkRule{{ - Bypass: []types.StringValue{ - types.StringTest("Logging"), - types.StringTest("Metrics"), - }, - AllowByDefault: types.BoolTest(true), - }}, - }}, - } - - testutil.AssertDefsecEqual(t, expected, output) -} diff --git a/pkg/iac/adapters/arm/synapse/adapt.go b/pkg/iac/adapters/arm/synapse/adapt.go deleted file mode 100644 index 649831e16a9a..000000000000 --- a/pkg/iac/adapters/arm/synapse/adapt.go +++ /dev/null @@ -1,34 +0,0 @@ -package synapse - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/synapse" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(deployment azure.Deployment) synapse.Synapse { - return synapse.Synapse{ - Workspaces: adaptWorkspaces(deployment), - } -} - -func adaptWorkspaces(deployment azure.Deployment) (workspaces []synapse.Workspace) { - for _, resource := range deployment.GetResourcesByType("Microsoft.Synapse/workspaces") { - workspaces = append(workspaces, adaptWorkspace(resource)) - } - return workspaces -} - -func adaptWorkspace(resource azure.Resource) synapse.Workspace { - - managedVirtualNetwork := resource.Properties.GetMapValue("managedVirtualNetwork").AsString() - enableManagedVirtualNetwork := types.BoolDefault(false, resource.Metadata) - if managedVirtualNetwork == "default" { - enableManagedVirtualNetwork = types.Bool(true, resource.Metadata) - } - - return synapse.Workspace{ - Metadata: resource.Metadata, - EnableManagedVirtualNetwork: enableManagedVirtualNetwork, - } -} diff --git a/pkg/iac/adapters/cloudformation/adapt.go b/pkg/iac/adapters/cloudformation/adapt.go deleted file mode 100644 index da2d2595892d..000000000000 --- a/pkg/iac/adapters/cloudformation/adapt.go +++ /dev/null @@ -1,14 +0,0 @@ -package cloudformation - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/state" -) - -// Adapt adapts the Cloudformation instance -func Adapt(cfFile parser.FileContext) *state.State { - return &state.State{ - AWS: aws.Adapt(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/accessanalyzer/accessanalyzer.go b/pkg/iac/adapters/cloudformation/aws/accessanalyzer/accessanalyzer.go deleted file mode 100644 index e6f7031a58d5..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/accessanalyzer/accessanalyzer.go +++ /dev/null @@ -1,13 +0,0 @@ -package accessanalyzer - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/accessanalyzer" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an AccessAnalyzer instance -func Adapt(cfFile parser.FileContext) accessanalyzer.AccessAnalyzer { - return accessanalyzer.AccessAnalyzer{ - Analyzers: getAccessAnalyzer(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/accessanalyzer/accessanalyzer_test.go b/pkg/iac/adapters/cloudformation/aws/accessanalyzer/accessanalyzer_test.go deleted file mode 100644 index 04e67c2b6818..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/accessanalyzer/accessanalyzer_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package accessanalyzer - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/accessanalyzer" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected accessanalyzer.AccessAnalyzer - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Analyzer: - Type: 'AWS::AccessAnalyzer::Analyzer' - Properties: - AnalyzerName: MyAccountAnalyzer -`, - expected: accessanalyzer.AccessAnalyzer{ - Analyzers: []accessanalyzer.Analyzer{ - { - Name: types.StringTest("MyAccountAnalyzer"), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Analyzer: - Type: 'AWS::AccessAnalyzer::Analyzer' -`, - expected: accessanalyzer.AccessAnalyzer{ - Analyzers: []accessanalyzer.Analyzer{ - {}, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/accessanalyzer/analyzer.go b/pkg/iac/adapters/cloudformation/aws/accessanalyzer/analyzer.go deleted file mode 100644 index c81802d60e0e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/accessanalyzer/analyzer.go +++ /dev/null @@ -1,24 +0,0 @@ -package accessanalyzer - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/accessanalyzer" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getAccessAnalyzer(ctx parser.FileContext) (analyzers []accessanalyzer.Analyzer) { - - analyzersList := ctx.GetResourcesByType("AWS::AccessAnalyzer::Analyzer") - - for _, r := range analyzersList { - aa := accessanalyzer.Analyzer{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("AnalyzerName"), - ARN: r.StringDefault(""), - Active: types.BoolDefault(false, r.Metadata()), - } - - analyzers = append(analyzers, aa) - } - return analyzers -} diff --git a/pkg/iac/adapters/cloudformation/aws/adapt.go b/pkg/iac/adapters/cloudformation/aws/adapt.go deleted file mode 100644 index e8c10edf0855..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/adapt.go +++ /dev/null @@ -1,74 +0,0 @@ -package aws - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/apigateway" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/elasticsearch" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/elb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/kinesis" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/lambda" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/ssm" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/aws/workspaces" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a Cloudformation AWS instance -func Adapt(cfFile parser.FileContext) aws.AWS { - return aws.AWS{ - APIGateway: apigateway.Adapt(cfFile), - Athena: athena.Adapt(cfFile), - Cloudfront: cloudfront.Adapt(cfFile), - CloudTrail: cloudtrail.Adapt(cfFile), - CloudWatch: cloudwatch.Adapt(cfFile), - CodeBuild: codebuild.Adapt(cfFile), - Config: config.Adapt(cfFile), - DocumentDB: documentdb.Adapt(cfFile), - DynamoDB: dynamodb.Adapt(cfFile), - EC2: ec2.Adapt(cfFile), - ECR: ecr.Adapt(cfFile), - ECS: ecs.Adapt(cfFile), - EFS: efs.Adapt(cfFile), - IAM: iam.Adapt(cfFile), - EKS: eks.Adapt(cfFile), - ElastiCache: elasticache.Adapt(cfFile), - Elasticsearch: elasticsearch.Adapt(cfFile), - ELB: elb.Adapt(cfFile), - MSK: msk.Adapt(cfFile), - MQ: mq.Adapt(cfFile), - Kinesis: kinesis.Adapt(cfFile), - Lambda: lambda.Adapt(cfFile), - Neptune: neptune.Adapt(cfFile), - RDS: rds.Adapt(cfFile), - Redshift: redshift.Adapt(cfFile), - S3: s3.Adapt(cfFile), - SAM: sam.Adapt(cfFile), - SNS: sns.Adapt(cfFile), - SQS: sqs.Adapt(cfFile), - SSM: ssm.Adapt(cfFile), - WorkSpaces: workspaces.Adapt(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/apigateway/apigateway.go b/pkg/iac/adapters/cloudformation/aws/apigateway/apigateway.go deleted file mode 100644 index 096ad174a002..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/apigateway/apigateway.go +++ /dev/null @@ -1,22 +0,0 @@ -package apigateway - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway" - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an APIGateway instance -func Adapt(cfFile parser.FileContext) apigateway.APIGateway { - return apigateway.APIGateway{ - V1: v1.APIGateway{ - APIs: adaptAPIsV1(cfFile), - DomainNames: adaptDomainNamesV1(cfFile), - }, - V2: v2.APIGateway{ - APIs: adaptAPIsV2(cfFile), - DomainNames: adaptDomainNamesV2(cfFile), - }, - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/apigateway/apigateway_test.go b/pkg/iac/adapters/cloudformation/aws/apigateway/apigateway_test.go deleted file mode 100644 index 4386a5baa51c..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/apigateway/apigateway_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package apigateway - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway" - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected apigateway.APIGateway - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyRestApi: - Type: 'AWS::ApiGateway::RestApi' - Properties: - Description: A test API - Name: MyRestAPI - ApiResource: - Type: AWS::ApiGateway::Resource - Properties: - RestApiId: !Ref MyRestApi - MethodPOST: - Type: AWS::ApiGateway::Method - Properties: - RestApiId: !Ref MyRestApi - ResourceId: !Ref ApiResource - HttpMethod: POST - AuthorizationType: COGNITO_USER_POOLS - ApiKeyRequired: true - Stage: - Type: AWS::ApiGateway::Stage - Properties: - StageName: Prod - RestApiId: !Ref MyRestApi - TracingEnabled: true - AccessLogSetting: - DestinationArn: test-arn - MethodSettings: - - CacheDataEncrypted: true - CachingEnabled: true - HttpMethod: POST - MyDomainName: - Type: AWS::ApiGateway::DomainName - Properties: - DomainName: mydomainame.us-east-1.com - SecurityPolicy: "TLS_1_2" - - MyApi2: - Type: 'AWS::ApiGatewayV2::Api' - Properties: - Name: MyApi2 - ProtocolType: WEBSOCKET - MyStage2: - Type: 'AWS::ApiGatewayV2::Stage' - Properties: - StageName: Prod - ApiId: !Ref MyApi2 - AccessLogSettings: - DestinationArn: some-arn - MyDomainName2: - Type: 'AWS::ApiGatewayV2::DomainName' - Properties: - DomainName: mydomainame.us-east-1.com - DomainNameConfigurations: - - SecurityPolicy: "TLS_1_2" -`, - expected: apigateway.APIGateway{ - V1: v1.APIGateway{ - APIs: []v1.API{ - { - Name: types.StringTest("MyRestAPI"), - Stages: []v1.Stage{ - { - Name: types.StringTest("Prod"), - XRayTracingEnabled: types.BoolTest(true), - AccessLogging: v1.AccessLogging{ - CloudwatchLogGroupARN: types.StringTest("test-arn"), - }, - RESTMethodSettings: []v1.RESTMethodSettings{ - { - Method: types.StringTest("POST"), - CacheDataEncrypted: types.BoolTest(true), - CacheEnabled: types.BoolTest(true), - }, - }, - }, - }, - Resources: []v1.Resource{ - { - Methods: []v1.Method{ - { - HTTPMethod: types.StringTest("POST"), - AuthorizationType: types.StringTest("COGNITO_USER_POOLS"), - APIKeyRequired: types.BoolTest(true), - }, - }, - }, - }, - }, - }, - DomainNames: []v1.DomainName{ - { - Name: types.StringTest("mydomainame.us-east-1.com"), - SecurityPolicy: types.StringTest("TLS_1_2"), - }, - }, - }, - V2: v2.APIGateway{ - APIs: []v2.API{ - { - Name: types.StringTest("MyApi2"), - ProtocolType: types.StringTest("WEBSOCKET"), - Stages: []v2.Stage{ - { - Name: types.StringTest("Prod"), - AccessLogging: v2.AccessLogging{ - CloudwatchLogGroupARN: types.StringTest("some-arn"), - }, - }, - }, - }, - }, - DomainNames: []v2.DomainName{ - { - Name: types.StringTest("mydomainame.us-east-1.com"), - SecurityPolicy: types.StringTest("TLS_1_2"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyApi: - Type: 'AWS::ApiGatewayV2::Api' - MyStage: - Type: 'AWS::ApiGatewayV2::Stage' - MyStage2: - Type: 'AWS::ApiGatewayV2::Stage' - Properties: - ApiId: !Ref MyApi -`, - expected: apigateway.APIGateway{ - V2: v2.APIGateway{ - APIs: []v2.API{ - { - Stages: []v2.Stage{{}}, - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/apigateway/apiv1.go b/pkg/iac/adapters/cloudformation/aws/apigateway/apiv1.go deleted file mode 100644 index 2a2c46f44f5e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/apigateway/apiv1.go +++ /dev/null @@ -1,108 +0,0 @@ -package apigateway - -import ( - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func adaptAPIsV1(fctx parser.FileContext) []v1.API { - var apis []v1.API - - stages := make(map[string]*parser.Resource) - for _, stageResource := range fctx.GetResourcesByType("AWS::ApiGateway::Stage") { - restApiID := stageResource.GetStringProperty("RestApiId") - if restApiID.IsEmpty() { - continue - } - - stages[restApiID.Value()] = stageResource - } - - resources := make(map[string]*parser.Resource) - for _, resource := range fctx.GetResourcesByType("AWS::ApiGateway::Resource") { - restApiID := resource.GetStringProperty("RestApiId") - if restApiID.IsEmpty() { - continue - } - - resources[restApiID.Value()] = resource - } - - for _, apiResource := range fctx.GetResourcesByType("AWS::ApiGateway::RestApi") { - - api := v1.API{ - Metadata: apiResource.Metadata(), - Name: apiResource.GetStringProperty("Name"), - } - - if stageResource, exists := stages[apiResource.ID()]; exists { - stage := v1.Stage{ - Metadata: stageResource.Metadata(), - Name: stageResource.GetStringProperty("StageName"), - XRayTracingEnabled: stageResource.GetBoolProperty("TracingEnabled"), - } - - if logSetting := stageResource.GetProperty("AccessLogSetting"); logSetting.IsNotNil() { - stage.AccessLogging = v1.AccessLogging{ - Metadata: logSetting.Metadata(), - CloudwatchLogGroupARN: logSetting.GetStringProperty("DestinationArn"), - } - } - - if methodSettings := stageResource.GetProperty("MethodSettings"); methodSettings.IsList() { - for _, methodSetting := range methodSettings.AsList() { - stage.RESTMethodSettings = append(stage.RESTMethodSettings, v1.RESTMethodSettings{ - Metadata: methodSetting.Metadata(), - Method: methodSetting.GetStringProperty("HttpMethod"), - CacheDataEncrypted: methodSetting.GetBoolProperty("CacheDataEncrypted"), - CacheEnabled: methodSetting.GetBoolProperty("CachingEnabled"), - }) - } - } - - api.Stages = append(api.Stages, stage) - } - - if resource, exists := resources[apiResource.ID()]; exists { - res := v1.Resource{ - Metadata: resource.Metadata(), - } - - for _, methodResource := range fctx.GetResourcesByType("AWS::ApiGateway::Method") { - resourceID := methodResource.GetStringProperty("ResourceId") - // TODO: handle RootResourceId - if resourceID.Value() != resource.ID() { - continue - } - - res.Methods = append(res.Methods, v1.Method{ - Metadata: methodResource.Metadata(), - HTTPMethod: methodResource.GetStringProperty("HttpMethod"), - AuthorizationType: methodResource.GetStringProperty("AuthorizationType"), - APIKeyRequired: methodResource.GetBoolProperty("ApiKeyRequired"), - }) - - } - - api.Resources = append(api.Resources, res) - } - - apis = append(apis, api) - } - - return apis -} - -func adaptDomainNamesV1(fctx parser.FileContext) []v1.DomainName { - var domainNames []v1.DomainName - - for _, domainNameResource := range fctx.GetResourcesByType("AWS::ApiGateway::DomainName") { - domainNames = append(domainNames, v1.DomainName{ - Metadata: domainNameResource.Metadata(), - Name: domainNameResource.GetStringProperty("DomainName"), - SecurityPolicy: domainNameResource.GetStringProperty("SecurityPolicy"), - }) - } - - return domainNames -} diff --git a/pkg/iac/adapters/cloudformation/aws/apigateway/apiv2.go b/pkg/iac/adapters/cloudformation/aws/apigateway/apiv2.go deleted file mode 100644 index d3a34a98d91e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/apigateway/apiv2.go +++ /dev/null @@ -1,91 +0,0 @@ -package apigateway - -import ( - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptAPIsV2(cfFile parser.FileContext) (apis []v2.API) { - - apiResources := cfFile.GetResourcesByType("AWS::ApiGatewayV2::Api") - for _, apiRes := range apiResources { - api := v2.API{ - Metadata: apiRes.Metadata(), - Name: apiRes.GetStringProperty("Name"), - ProtocolType: apiRes.GetStringProperty("ProtocolType"), - Stages: getStages(apiRes.ID(), cfFile), - } - apis = append(apis, api) - } - - return apis -} - -func getStages(apiId string, cfFile parser.FileContext) []v2.Stage { - var apiStages []v2.Stage - - stageResources := cfFile.GetResourcesByType("AWS::ApiGatewayV2::Stage") - for _, r := range stageResources { - stageApiId := r.GetStringProperty("ApiId") - if stageApiId.Value() != apiId { - continue - } - - s := v2.Stage{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("StageName"), - AccessLogging: getAccessLogging(r), - } - apiStages = append(apiStages, s) - } - - return apiStages -} - -func getAccessLogging(r *parser.Resource) v2.AccessLogging { - - loggingProp := r.GetProperty("AccessLogSettings") - if loggingProp.IsNil() { - return v2.AccessLogging{ - Metadata: r.Metadata(), - CloudwatchLogGroupARN: types.StringDefault("", r.Metadata()), - } - } - - destinationProp := r.GetProperty("AccessLogSettings.DestinationArn") - - if destinationProp.IsNil() { - return v2.AccessLogging{ - Metadata: loggingProp.Metadata(), - CloudwatchLogGroupARN: types.StringDefault("", r.Metadata()), - } - } - return v2.AccessLogging{ - Metadata: destinationProp.Metadata(), - CloudwatchLogGroupARN: destinationProp.AsStringValue(), - } -} - -func adaptDomainNamesV2(fctx parser.FileContext) []v2.DomainName { - var domainNames []v2.DomainName - - for _, domainNameResource := range fctx.GetResourcesByType("AWS::ApiGateway::DomainName") { - - domainName := v2.DomainName{ - Metadata: domainNameResource.Metadata(), - Name: domainNameResource.GetStringProperty("DomainName"), - SecurityPolicy: domainNameResource.GetStringProperty("SecurityPolicy"), - } - - if domainNameCfgs := domainNameResource.GetProperty("DomainNameConfigurations"); domainNameCfgs.IsList() { - for _, domainNameCfg := range domainNameCfgs.AsList() { - domainName.SecurityPolicy = domainNameCfg.GetStringProperty("SecurityPolicy") - } - } - - domainNames = append(domainNames, domainName) - } - - return domainNames -} diff --git a/pkg/iac/adapters/cloudformation/aws/athena/athena.go b/pkg/iac/adapters/cloudformation/aws/athena/athena.go deleted file mode 100644 index b23a1936c179..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/athena/athena.go +++ /dev/null @@ -1,14 +0,0 @@ -package athena - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an Athena instance -func Adapt(cfFile parser.FileContext) athena.Athena { - return athena.Athena{ - Databases: nil, - Workgroups: getWorkGroups(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/athena/athena_test.go b/pkg/iac/adapters/cloudformation/aws/athena/athena_test.go deleted file mode 100644 index 097de6fa303d..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/athena/athena_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package athena - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected athena.Athena - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyAthenaWorkGroup: - Type: AWS::Athena::WorkGroup - Properties: - Name: MyCustomWorkGroup - WorkGroupConfiguration: - EnforceWorkGroupConfiguration: true - ResultConfiguration: - EncryptionOption: SSE_KMS -`, - expected: athena.Athena{ - Workgroups: []athena.Workgroup{ - { - Name: types.StringTest("MyCustomWorkGroup"), - EnforceConfiguration: types.BoolTest(true), - Encryption: athena.EncryptionConfiguration{ - Type: types.StringTest("SSE_KMS"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyAthenaWorkGroup: - Type: AWS::Athena::WorkGroup -`, - expected: athena.Athena{ - Workgroups: []athena.Workgroup{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } - -} diff --git a/pkg/iac/adapters/cloudformation/aws/athena/workgroup.go b/pkg/iac/adapters/cloudformation/aws/athena/workgroup.go deleted file mode 100644 index 916e2ac4b6f8..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/athena/workgroup.go +++ /dev/null @@ -1,30 +0,0 @@ -package athena - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getWorkGroups(cfFile parser.FileContext) []athena.Workgroup { - - var workgroups []athena.Workgroup - - workgroupResources := cfFile.GetResourcesByType("AWS::Athena::WorkGroup") - - for _, r := range workgroupResources { - - wg := athena.Workgroup{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("Name"), - Encryption: athena.EncryptionConfiguration{ - Metadata: r.Metadata(), - Type: r.GetStringProperty("WorkGroupConfiguration.ResultConfiguration.EncryptionConfiguration.EncryptionOption"), - }, - EnforceConfiguration: r.GetBoolProperty("WorkGroupConfiguration.EnforceWorkGroupConfiguration"), - } - - workgroups = append(workgroups, wg) - } - - return workgroups -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudfront/cloudfront.go b/pkg/iac/adapters/cloudformation/aws/cloudfront/cloudfront.go deleted file mode 100644 index 0cfe00145907..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudfront/cloudfront.go +++ /dev/null @@ -1,13 +0,0 @@ -package cloudfront - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a CloudFront instance -func Adapt(cfFile parser.FileContext) cloudfront.Cloudfront { - return cloudfront.Cloudfront{ - Distributions: getDistributions(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudfront/cloudfront_test.go b/pkg/iac/adapters/cloudformation/aws/cloudfront/cloudfront_test.go deleted file mode 100644 index 6c0ec7348b33..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudfront/cloudfront_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package cloudfront - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected cloudfront.Cloudfront - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - cloudfrontdistribution: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - WebACLId: "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111" - Logging: - Bucket: "myawslogbucket.s3.amazonaws.com" - ViewerCertificate: - MinimumProtocolVersion: SSLv3 - DefaultCacheBehavior: - ViewerProtocolPolicy: "redirect-to-https" -`, - expected: cloudfront.Cloudfront{ - Distributions: []cloudfront.Distribution{ - { - WAFID: types.StringTest("a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"), - Logging: cloudfront.Logging{ - Bucket: types.StringTest("myawslogbucket.s3.amazonaws.com"), - }, - ViewerCertificate: cloudfront.ViewerCertificate{ - MinimumProtocolVersion: types.StringTest("SSLv3"), - }, - DefaultCacheBehaviour: cloudfront.CacheBehaviour{ - ViewerProtocolPolicy: types.StringTest("redirect-to-https"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - cloudfrontdistribution: - Type: AWS::CloudFront::Distribution -`, - expected: cloudfront.Cloudfront{ - Distributions: []cloudfront.Distribution{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudfront/distribution.go b/pkg/iac/adapters/cloudformation/aws/cloudfront/distribution.go deleted file mode 100644 index 70c5052bcd55..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudfront/distribution.go +++ /dev/null @@ -1,45 +0,0 @@ -package cloudfront - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getDistributions(ctx parser.FileContext) (distributions []cloudfront.Distribution) { - - distributionResources := ctx.GetResourcesByType("AWS::CloudFront::Distribution") - - for _, r := range distributionResources { - distribution := cloudfront.Distribution{ - Metadata: r.Metadata(), - WAFID: r.GetStringProperty("DistributionConfig.WebACLId"), - Logging: cloudfront.Logging{ - Metadata: r.Metadata(), - Bucket: r.GetStringProperty("DistributionConfig.Logging.Bucket"), - }, - DefaultCacheBehaviour: getDefaultCacheBehaviour(r), - OrdererCacheBehaviours: nil, - ViewerCertificate: cloudfront.ViewerCertificate{ - Metadata: r.Metadata(), - MinimumProtocolVersion: r.GetStringProperty("DistributionConfig.ViewerCertificate.MinimumProtocolVersion"), - }, - } - - distributions = append(distributions, distribution) - } - - return distributions -} - -func getDefaultCacheBehaviour(r *parser.Resource) cloudfront.CacheBehaviour { - defaultCache := r.GetProperty("DistributionConfig.DefaultCacheBehavior") - if defaultCache.IsNil() { - return cloudfront.CacheBehaviour{ - Metadata: r.Metadata(), - } - } - return cloudfront.CacheBehaviour{ - Metadata: defaultCache.Metadata(), - ViewerProtocolPolicy: defaultCache.GetStringProperty("ViewerProtocolPolicy"), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudtrail/cloudtrail.go b/pkg/iac/adapters/cloudformation/aws/cloudtrail/cloudtrail.go deleted file mode 100644 index 04270579dbcd..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudtrail/cloudtrail.go +++ /dev/null @@ -1,13 +0,0 @@ -package cloudtrail - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a CloudTrail instance -func Adapt(cfFile parser.FileContext) cloudtrail.CloudTrail { - return cloudtrail.CloudTrail{ - Trails: getCloudTrails(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudtrail/cloudtrail_test.go b/pkg/iac/adapters/cloudformation/aws/cloudtrail/cloudtrail_test.go deleted file mode 100644 index 5dcebb291035..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudtrail/cloudtrail_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package cloudtrail - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected cloudtrail.CloudTrail - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Trail: - Type: AWS::CloudTrail::Trail - Properties: - S3BucketName: MyBucket - IsLogging: true - TrailName: MyTrail - EnableLogFileValidation: true - IsMultiRegionTrail: true - CloudWatchLogsLogGroupArn: cw-arn - KmsKeyId: my-kms-key -`, - expected: cloudtrail.CloudTrail{ - Trails: []cloudtrail.Trail{ - { - Name: types.StringTest("MyTrail"), - BucketName: types.StringTest("MyBucket"), - IsLogging: types.BoolTest(true), - IsMultiRegion: types.BoolTest(true), - EnableLogFileValidation: types.BoolTest(true), - CloudWatchLogsLogGroupArn: types.StringTest("cw-arn"), - KMSKeyID: types.StringTest("my-kms-key"), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Trail: - Type: AWS::CloudTrail::Trail - `, - expected: cloudtrail.CloudTrail{ - Trails: []cloudtrail.Trail{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudtrail/trails.go b/pkg/iac/adapters/cloudformation/aws/cloudtrail/trails.go deleted file mode 100644 index fc9c3871cbba..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudtrail/trails.go +++ /dev/null @@ -1,27 +0,0 @@ -package cloudtrail - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getCloudTrails(ctx parser.FileContext) (trails []cloudtrail.Trail) { - - cloudtrailResources := ctx.GetResourcesByType("AWS::CloudTrail::Trail") - - for _, r := range cloudtrailResources { - ct := cloudtrail.Trail{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("TrailName"), - EnableLogFileValidation: r.GetBoolProperty("EnableLogFileValidation"), - IsMultiRegion: r.GetBoolProperty("IsMultiRegionTrail"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - CloudWatchLogsLogGroupArn: r.GetStringProperty("CloudWatchLogsLogGroupArn"), - IsLogging: r.GetBoolProperty("IsLogging"), - BucketName: r.GetStringProperty("S3BucketName"), - } - - trails = append(trails, ct) - } - return trails -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudwatch/cloudwatch.go b/pkg/iac/adapters/cloudformation/aws/cloudwatch/cloudwatch.go deleted file mode 100644 index 0c4a59e43189..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudwatch/cloudwatch.go +++ /dev/null @@ -1,13 +0,0 @@ -package cloudwatch - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a Cloudwatch instance -func Adapt(cfFile parser.FileContext) cloudwatch.CloudWatch { - return cloudwatch.CloudWatch{ - LogGroups: getLogGroups(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudwatch/cloudwatch_test.go b/pkg/iac/adapters/cloudformation/aws/cloudwatch/cloudwatch_test.go deleted file mode 100644 index c8a7bd95c9a3..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudwatch/cloudwatch_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package cloudwatch - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected cloudwatch.CloudWatch - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - myLogGroup: - Type: AWS::Logs::LogGroup - Properties: - LogGroupName: my-log-group - RetentionInDays: 7 - KmsKeyId: my-kms - -`, - expected: cloudwatch.CloudWatch{ - LogGroups: []cloudwatch.LogGroup{ - { - Name: types.StringTest("my-log-group"), - RetentionInDays: types.IntTest(7), - KMSKeyID: types.StringTest("my-kms"), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - myLogGroup: - Type: AWS::Logs::LogGroup - `, - expected: cloudwatch.CloudWatch{ - LogGroups: []cloudwatch.LogGroup{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/cloudwatch/log_group.go b/pkg/iac/adapters/cloudformation/aws/cloudwatch/log_group.go deleted file mode 100644 index 09e039129781..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/cloudwatch/log_group.go +++ /dev/null @@ -1,23 +0,0 @@ -package cloudwatch - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getLogGroups(ctx parser.FileContext) (logGroups []cloudwatch.LogGroup) { - - logGroupResources := ctx.GetResourcesByType("AWS::Logs::LogGroup") - - for _, r := range logGroupResources { - group := cloudwatch.LogGroup{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("LogGroupName"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - RetentionInDays: r.GetIntProperty("RetentionInDays"), - } - logGroups = append(logGroups, group) - } - - return logGroups -} diff --git a/pkg/iac/adapters/cloudformation/aws/codebuild/codebuild.go b/pkg/iac/adapters/cloudformation/aws/codebuild/codebuild.go deleted file mode 100644 index 8951f230ee98..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/codebuild/codebuild.go +++ /dev/null @@ -1,13 +0,0 @@ -package codebuild - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a CodeBuild instance -func Adapt(cfFile parser.FileContext) codebuild.CodeBuild { - return codebuild.CodeBuild{ - Projects: getProjects(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/codebuild/codebuild_test.go b/pkg/iac/adapters/cloudformation/aws/codebuild/codebuild_test.go deleted file mode 100644 index 06eaa19402e6..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/codebuild/codebuild_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package codebuild - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected codebuild.CodeBuild - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Project: - Type: AWS::CodeBuild::Project - Properties: - Artifacts: - EncryptionDisabled: true - SecondaryArtifacts: - - EncryptionDisabled: true -`, - expected: codebuild.CodeBuild{ - Projects: []codebuild.Project{ - { - ArtifactSettings: codebuild.ArtifactSettings{ - EncryptionEnabled: types.BoolTest(false), - }, - SecondaryArtifactSettings: []codebuild.ArtifactSettings{ - { - EncryptionEnabled: types.BoolTest(false), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Project: - Type: AWS::CodeBuild::Project - `, - expected: codebuild.CodeBuild{ - Projects: []codebuild.Project{ - { - ArtifactSettings: codebuild.ArtifactSettings{ - EncryptionEnabled: types.BoolTest(true), - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/codebuild/project.go b/pkg/iac/adapters/cloudformation/aws/codebuild/project.go deleted file mode 100644 index 554fc8afecea..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/codebuild/project.go +++ /dev/null @@ -1,63 +0,0 @@ -package codebuild - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getProjects(ctx parser.FileContext) (projects []codebuild.Project) { - - projectResources := ctx.GetResourcesByType("AWS::CodeBuild::Project") - - for _, r := range projectResources { - project := codebuild.Project{ - Metadata: r.Metadata(), - ArtifactSettings: getArtifactSettings(r), - SecondaryArtifactSettings: getSecondaryArtifactSettings(r), - } - - projects = append(projects, project) - } - - return projects -} - -func getSecondaryArtifactSettings(r *parser.Resource) (secondaryArtifacts []codebuild.ArtifactSettings) { - secondaryArtifactsList := r.GetProperty("SecondaryArtifacts") - if secondaryArtifactsList.IsNil() || !secondaryArtifactsList.IsList() { - return - } - - for _, a := range secondaryArtifactsList.AsList() { - settings := codebuild.ArtifactSettings{ - Metadata: secondaryArtifactsList.Metadata(), - EncryptionEnabled: types.BoolDefault(true, secondaryArtifactsList.Metadata()), - } - encryptionDisabled := a.GetProperty("EncryptionDisabled") - if encryptionDisabled.IsBool() { - settings.EncryptionEnabled = types.Bool(!encryptionDisabled.AsBool(), encryptionDisabled.Metadata()) - } - secondaryArtifacts = append(secondaryArtifacts, settings) - } - - return secondaryArtifacts -} - -func getArtifactSettings(r *parser.Resource) codebuild.ArtifactSettings { - - settings := codebuild.ArtifactSettings{ - Metadata: r.Metadata(), - EncryptionEnabled: types.BoolDefault(true, r.Metadata()), - } - - artifactsProperty := r.GetProperty("Artifacts") - if artifactsProperty.IsNotNil() { - encryptionDisabled := artifactsProperty.GetProperty("EncryptionDisabled") - if encryptionDisabled.IsBool() { - settings.EncryptionEnabled = types.Bool(!encryptionDisabled.AsBool(), encryptionDisabled.Metadata()) - } - } - - return settings -} diff --git a/pkg/iac/adapters/cloudformation/aws/config/adapt_test.go b/pkg/iac/adapters/cloudformation/aws/config/adapt_test.go deleted file mode 100644 index e6dc652da7b1..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/config/adapt_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package config - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected config.Config - }{ - { - name: "Config aggregator with AccountAggregationSources", - source: `AWSTemplateFormatVersion: "2010-09-09" -Resources: - ConfigurationAggregator: - Type: AWS::Config::ConfigurationAggregator - Properties: - AccountAggregationSources: - - AllAwsRegions: "true" -`, - expected: config.Config{ - ConfigurationAggregrator: config.ConfigurationAggregrator{ - SourceAllRegions: types.BoolTest(true), - }, - }, - }, - { - name: "Config aggregator with OrganizationAggregationSource", - source: `AWSTemplateFormatVersion: "2010-09-09" -Resources: - ConfigurationAggregator: - Type: AWS::Config::ConfigurationAggregator - Properties: - OrganizationAggregationSource: - AllAwsRegions: "true" -`, - expected: config.Config{ - ConfigurationAggregrator: config.ConfigurationAggregrator{ - SourceAllRegions: types.BoolTest(true), - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } - -} diff --git a/pkg/iac/adapters/cloudformation/aws/config/aggregator.go b/pkg/iac/adapters/cloudformation/aws/config/aggregator.go deleted file mode 100644 index 72447398b80f..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/config/aggregator.go +++ /dev/null @@ -1,41 +0,0 @@ -package config - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getConfigurationAggregator(ctx parser.FileContext) config.ConfigurationAggregrator { - - aggregator := config.ConfigurationAggregrator{ - Metadata: iacTypes.NewUnmanagedMetadata(), - SourceAllRegions: iacTypes.BoolDefault(false, ctx.Metadata()), - } - - aggregatorResources := ctx.GetResourcesByType("AWS::Config::ConfigurationAggregator") - - if len(aggregatorResources) == 0 { - return aggregator - } - - return config.ConfigurationAggregrator{ - Metadata: aggregatorResources[0].Metadata(), - SourceAllRegions: isSourcingAllRegions(aggregatorResources[0]), - } -} - -func isSourcingAllRegions(r *parser.Resource) iacTypes.BoolValue { - accountProp := r.GetProperty("AccountAggregationSources") - - if accountProp.IsNotNil() && accountProp.IsList() { - for _, a := range accountProp.AsList() { - regionsProp := a.GetProperty("AllAwsRegions") - if regionsProp.IsNotNil() { - return a.GetBoolProperty("AllAwsRegions") - } - } - } - - return r.GetBoolProperty("OrganizationAggregationSource.AllAwsRegions") -} diff --git a/pkg/iac/adapters/cloudformation/aws/config/config.go b/pkg/iac/adapters/cloudformation/aws/config/config.go deleted file mode 100644 index a9634252694e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/config/config.go +++ /dev/null @@ -1,13 +0,0 @@ -package config - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a configurationaggregator instance -func Adapt(cfFile parser.FileContext) config.Config { - return config.Config{ - ConfigurationAggregrator: getConfigurationAggregator(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/documentdb/cluster.go b/pkg/iac/adapters/cloudformation/aws/documentdb/cluster.go deleted file mode 100644 index f37467dc4100..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/documentdb/cluster.go +++ /dev/null @@ -1,58 +0,0 @@ -package documentdb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(ctx parser.FileContext) (clusters []documentdb.Cluster) { - - clusterResources := ctx.GetResourcesByType("AWS::DocDB::DBCluster") - - for _, r := range clusterResources { - cluster := documentdb.Cluster{ - Metadata: r.Metadata(), - Identifier: r.GetStringProperty("DBClusterIdentifier"), - EnabledLogExports: getLogExports(r), - Instances: nil, - BackupRetentionPeriod: r.GetIntProperty("BackupRetentionPeriod", 1), - StorageEncrypted: r.GetBoolProperty("StorageEncrypted"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - } - - updateInstancesOnCluster(&cluster, ctx) - - clusters = append(clusters, cluster) - } - return clusters -} - -func updateInstancesOnCluster(cluster *documentdb.Cluster, ctx parser.FileContext) { - - instanceResources := ctx.GetResourcesByType("AWS::DocDB::DBInstance") - - for _, r := range instanceResources { - clusterIdentifier := r.GetStringProperty("DBClusterIdentifier") - if cluster.Identifier.EqualTo(clusterIdentifier.Value()) { - cluster.Instances = append(cluster.Instances, documentdb.Instance{ - Metadata: r.Metadata(), - KMSKeyID: cluster.KMSKeyID, - }) - } - } -} - -func getLogExports(r *parser.Resource) (logExports []types.StringValue) { - - exportsList := r.GetProperty("EnableCloudwatchLogsExports") - - if exportsList.IsNil() || exportsList.IsNotList() { - return logExports - } - - for _, export := range exportsList.AsList() { - logExports = append(logExports, export.AsStringValue()) - } - return logExports -} diff --git a/pkg/iac/adapters/cloudformation/aws/documentdb/documentdb.go b/pkg/iac/adapters/cloudformation/aws/documentdb/documentdb.go deleted file mode 100644 index 40b0c6ffae44..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/documentdb/documentdb.go +++ /dev/null @@ -1,13 +0,0 @@ -package documentdb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adaps a documentDB instance -func Adapt(cfFile parser.FileContext) documentdb.DocumentDB { - return documentdb.DocumentDB{ - Clusters: getClusters(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/documentdb/documentdb_test.go b/pkg/iac/adapters/cloudformation/aws/documentdb/documentdb_test.go deleted file mode 100644 index 3e60155e9dfb..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/documentdb/documentdb_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package documentdb - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected documentdb.DocumentDB - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - myDBCluster: - Type: 'AWS::DocDB::DBCluster' - Properties: - BackupRetentionPeriod: 8 - DBClusterIdentifier: sample-cluster - KmsKeyId: your-kms-key-id - StorageEncrypted: true - EnableCloudwatchLogsExports: - - audit - - general - myDBInstance: - Type: 'AWS::DocDB::DBInstance' - Properties: - DBClusterIdentifier: sample-cluster - KmsKeyId: your-kms-key-id -`, - expected: documentdb.DocumentDB{ - Clusters: []documentdb.Cluster{ - { - Identifier: types.StringTest("sample-cluster"), - BackupRetentionPeriod: types.IntTest(8), - KMSKeyID: types.StringTest("your-kms-key-id"), - StorageEncrypted: types.BoolTest(true), - EnabledLogExports: []types.StringValue{ - types.StringTest("audit"), - types.StringTest("general"), - }, - Instances: []documentdb.Instance{ - { - KMSKeyID: types.StringTest("your-kms-key-id"), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - myDBCluster: - Type: 'AWS::DocDB::DBCluster' - `, - expected: documentdb.DocumentDB{ - Clusters: []documentdb.Cluster{ - { - BackupRetentionPeriod: types.IntTest(1), - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/dynamodb/cluster.go b/pkg/iac/adapters/cloudformation/aws/dynamodb/cluster.go deleted file mode 100644 index 8a350236134c..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/dynamodb/cluster.go +++ /dev/null @@ -1,36 +0,0 @@ -package dynamodb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(file parser.FileContext) (clusters []dynamodb.DAXCluster) { - - clusterResources := file.GetResourcesByType("AWS::DAX::Cluster") - - for _, r := range clusterResources { - cluster := dynamodb.DAXCluster{ - Metadata: r.Metadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - KMSKeyID: iacTypes.StringDefault("", r.Metadata()), - }, - PointInTimeRecovery: iacTypes.BoolUnresolvable(r.Metadata()), - } - - if sseProp := r.GetProperty("SSESpecification"); sseProp.IsNotNil() { - cluster.ServerSideEncryption = dynamodb.ServerSideEncryption{ - Metadata: sseProp.Metadata(), - Enabled: r.GetBoolProperty("SSESpecification.SSEEnabled"), - KMSKeyID: iacTypes.StringUnresolvable(sseProp.Metadata()), - } - } - - clusters = append(clusters, cluster) - } - - return clusters -} diff --git a/pkg/iac/adapters/cloudformation/aws/dynamodb/dynamodb.go b/pkg/iac/adapters/cloudformation/aws/dynamodb/dynamodb.go deleted file mode 100644 index c841e0671520..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/dynamodb/dynamodb.go +++ /dev/null @@ -1,13 +0,0 @@ -package dynamodb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a dynamodb instance -func Adapt(cfFile parser.FileContext) dynamodb.DynamoDB { - return dynamodb.DynamoDB{ - DAXClusters: getClusters(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/dynamodb/dynamodb_test.go b/pkg/iac/adapters/cloudformation/aws/dynamodb/dynamodb_test.go deleted file mode 100644 index ce62e85cde5e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/dynamodb/dynamodb_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package dynamodb - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected dynamodb.DynamoDB - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - daxCluster: - Type: AWS::DAX::Cluster - Properties: - SSESpecification: - SSEEnabled: true -`, - expected: dynamodb.DynamoDB{ - DAXClusters: []dynamodb.DAXCluster{ - { - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Enabled: types.BoolTest(true), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - daxCluster: - Type: AWS::DAX::Cluster - `, - expected: dynamodb.DynamoDB{ - DAXClusters: []dynamodb.DAXCluster{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go b/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go deleted file mode 100644 index c56471aa2bad..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go +++ /dev/null @@ -1,377 +0,0 @@ -package ec2 - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected ec2.EC2 - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyEC2Instance: - Type: AWS::EC2::Instance - Properties: - ImageId: "ami-79fd7eee" - KeyName: "testkey" - BlockDeviceMappings: - - DeviceName: "/dev/sdm" - Ebs: - VolumeType: "io1" - Iops: "200" - DeleteOnTermination: "false" - VolumeSize: "20" - Encrypted: true - - DeviceName: "/dev/sdk" - NoDevice: {} - NewVolume: - Type: AWS::EC2::Volume - Properties: - KmsKeyId: alias/my_cmk - Encrypted: true - mySubnet: - Type: AWS::EC2::Subnet - Properties: - MapPublicIpOnLaunch: true - InstanceSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupName: default - GroupDescription: Allow http to client host - VpcId: vpc-id - SecurityGroupIngress: - - IpProtocol: tcp - Description: ingress - FromPort: "80" - ToPort: 80 - CidrIp: 0.0.0.0/0 - SecurityGroupEgress: - - IpProtocol: -1 - Description: egress - FromPort: 80 - ToPort: "80" - CidrIp: "0.0.0.0/0" - myNetworkAcl: - Type: AWS::EC2::NetworkAcl - Properties: - VpcId: vpc-1122334455aabbccd - InboundRule: - Type: AWS::EC2::NetworkAclEntry - Properties: - NetworkAclId: - Ref: myNetworkAcl - Egress: true - Protocol: 6 - RuleAction: allow - CidrBlock: 172.16.0.0/24 - PortRange: - From: 22 - To: "23" - myLaunchConfig: - Type: AWS::AutoScaling::LaunchConfiguration - Properties: - LaunchConfigurationName: test-cfg - InstanceId: !Ref MyEC2Instance - AssociatePublicIpAddress: true - SecurityGroups: - - !Ref InstanceSecurityGroup - UserData: test - BlockDeviceMappings: - - DeviceName: /dev/sda1 - Ebs: - VolumeSize: '30' - VolumeType: gp3 - Encrypted: true - - DeviceName: /dev/sdm - Ebs: - VolumeSize: '100' - DeleteOnTermination: false - MetadataOptions: - HttpTokens: required - HttpEndpoint: disabled -`, - expected: ec2.EC2{ - Instances: []ec2.Instance{ - { - MetadataOptions: ec2.MetadataOptions{ - HttpEndpoint: types.StringDefault("enabled", types.NewTestMetadata()), - HttpTokens: types.StringDefault("optional", types.NewTestMetadata()), - }, - RootBlockDevice: &ec2.BlockDevice{ - Encrypted: types.BoolDefault(true, types.NewTestMetadata()), - }, - EBSBlockDevices: []*ec2.BlockDevice{ - { - Encrypted: types.BoolDefault(false, types.NewTestMetadata()), - }, - }, - }, - }, - Volumes: []ec2.Volume{ - { - Encryption: ec2.Encryption{ - KMSKeyID: types.StringTest("alias/my_cmk"), - Enabled: types.BoolTest(true), - }, - }, - }, - Subnets: []ec2.Subnet{ - { - MapPublicIpOnLaunch: types.BoolTest(true), - }, - }, - SecurityGroups: []ec2.SecurityGroup{ - { - IsDefault: types.BoolTest(true), - Description: types.StringTest("Allow http to client host"), - VPCID: types.StringTest("vpc-id"), - IngressRules: []ec2.SecurityGroupRule{ - { - Description: types.StringTest("ingress"), - CIDRs: []types.StringValue{ - types.StringTest("0.0.0.0/0"), - }, - FromPort: types.IntTest(80), - ToPort: types.IntTest(80), - Protocol: types.StringTest("tcp"), - }, - }, - EgressRules: []ec2.SecurityGroupRule{ - { - Description: types.StringTest("egress"), - CIDRs: []types.StringValue{ - types.StringTest("0.0.0.0/0"), - }, - FromPort: types.IntTest(80), - ToPort: types.IntTest(80), - Protocol: types.StringTest("-1"), - }, - }, - }, - }, - NetworkACLs: []ec2.NetworkACL{ - { - Rules: []ec2.NetworkACLRule{ - { - Type: types.StringTest(ec2.TypeEgress), - Action: types.StringTest(ec2.ActionAllow), - Protocol: types.StringTest("6"), - CIDRs: []types.StringValue{ - types.StringTest("172.16.0.0/24"), - }, - FromPort: types.IntTest(22), - ToPort: types.IntTest(23), - }, - }, - }, - }, - LaunchConfigurations: []ec2.LaunchConfiguration{ - { - Name: types.StringTest("test-cfg"), - AssociatePublicIP: types.BoolTest(true), - RootBlockDevice: &ec2.BlockDevice{ - Encrypted: types.BoolTest(true), - }, - EBSBlockDevices: []*ec2.BlockDevice{ - { - Encrypted: types.BoolTest(false), - }, - }, - UserData: types.StringTest("test"), - MetadataOptions: ec2.MetadataOptions{ - HttpTokens: types.StringTest("required"), - HttpEndpoint: types.StringTest("disabled"), - }, - }, - }, - }, - }, - { - name: "ec2 instance with launch template, ref to name", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyLaunchTemplate: - Type: AWS::EC2::LaunchTemplate - Properties: - LaunchTemplateName: MyTemplate - LaunchTemplateData: - MetadataOptions: - HttpEndpoint: enabled - HttpTokens: required - MyEC2Instance: - Type: AWS::EC2::Instance - Properties: - ImageId: "ami-79fd7eee" - LaunchTemplate: - LaunchTemplateName: MyTemplate -`, - expected: ec2.EC2{ - LaunchTemplates: []ec2.LaunchTemplate{ - { - Name: types.StringTest("MyTemplate"), - Instance: ec2.Instance{ - MetadataOptions: ec2.MetadataOptions{ - HttpEndpoint: types.StringTest("enabled"), - HttpTokens: types.StringTest("required"), - }, - }, - }, - }, - Instances: []ec2.Instance{ - { - MetadataOptions: ec2.MetadataOptions{ - HttpEndpoint: types.StringTest("enabled"), - HttpTokens: types.StringTest("required"), - }, - RootBlockDevice: &ec2.BlockDevice{ - Encrypted: types.BoolTest(false), - }, - }, - }, - }, - }, - { - name: "ec2 instance with launch template, ref to id", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyLaunchTemplate: - Type: AWS::EC2::LaunchTemplate - Properties: - LaunchTemplateName: MyTemplate - LaunchTemplateData: - MetadataOptions: - HttpEndpoint: enabled - HttpTokens: required - MyEC2Instance: - Type: AWS::EC2::Instance - Properties: - ImageId: "ami-79fd7eee" - LaunchTemplate: - LaunchTemplateId: !Ref MyLaunchTemplate -`, - expected: ec2.EC2{ - LaunchTemplates: []ec2.LaunchTemplate{ - { - Name: types.StringTest("MyTemplate"), - Instance: ec2.Instance{ - MetadataOptions: ec2.MetadataOptions{ - HttpEndpoint: types.StringTest("enabled"), - HttpTokens: types.StringTest("required"), - }, - }, - }, - }, - Instances: []ec2.Instance{ - { - MetadataOptions: ec2.MetadataOptions{ - HttpEndpoint: types.StringTest("enabled"), - HttpTokens: types.StringTest("required"), - }, - RootBlockDevice: &ec2.BlockDevice{ - Encrypted: types.BoolTest(false), - }, - }, - }, - }, - }, - { - name: "security group with ingress and egress rules", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MySecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupName: MySecurityGroup - GroupDescription: MySecurityGroup - InboundRule: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !Ref MySecurityGroup - Description: Inbound - CidrIp: 0.0.0.0/0 - OutboundRule: - Type: AWS::EC2::SecurityGroupEgress - Properties: - GroupId: !GetAtt MySecurityGroup.GroupId - Description: Outbound - CidrIp: 0.0.0.0/0 - RuleWithoutGroup: - Type: AWS::EC2::SecurityGroupIngress - Properties: - CidrIpv6: ::/0 - Description: Inbound -`, - expected: ec2.EC2{ - SecurityGroups: []ec2.SecurityGroup{ - { - Description: types.StringTest("MySecurityGroup"), - IngressRules: []ec2.SecurityGroupRule{ - { - Description: types.StringTest("Inbound"), - CIDRs: []types.StringValue{ - types.StringTest("0.0.0.0/0"), - }, - FromPort: types.IntTest(-1), - ToPort: types.IntTest(-1), - }, - }, - EgressRules: []ec2.SecurityGroupRule{ - { - Description: types.StringTest("Outbound"), - CIDRs: []types.StringValue{ - types.StringTest("0.0.0.0/0"), - }, - FromPort: types.IntTest(-1), - ToPort: types.IntTest(-1), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `--- -AWSTemplateFormatVersion: 2010-09-09 -Description: Godd example of excessive ports -Resources: - NetworkACL: - Type: AWS::EC2::NetworkAcl - Rule: - Type: AWS::EC2::NetworkAclEntry - Properties: - NetworkAclId: - Ref: NetworkACL`, - expected: ec2.EC2{ - NetworkACLs: []ec2.NetworkACL{ - { - Rules: []ec2.NetworkACLRule{ - { - Action: types.StringTest("allow"), - Type: types.StringTest("ingress"), - FromPort: types.IntTest(-1), - ToPort: types.IntTest(-1), - }, - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } - -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/ec2.go b/pkg/iac/adapters/cloudformation/aws/ec2/ec2.go deleted file mode 100644 index 93056580c9b4..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/ec2.go +++ /dev/null @@ -1,20 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an EC2 instance -func Adapt(cfFile parser.FileContext) ec2.EC2 { - return ec2.EC2{ - LaunchConfigurations: getLaunchConfigurations(cfFile), - LaunchTemplates: getLaunchTemplates(cfFile), - Instances: getInstances(cfFile), - VPCs: nil, - NetworkACLs: getNetworkACLs(cfFile), - SecurityGroups: getSecurityGroups(cfFile), - Subnets: getSubnets(cfFile), - Volumes: getVolumes(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/instance.go b/pkg/iac/adapters/cloudformation/aws/ec2/instance.go deleted file mode 100644 index 7b6f149e0168..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/instance.go +++ /dev/null @@ -1,106 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getInstances(ctx parser.FileContext) (instances []ec2.Instance) { - instanceResources := ctx.GetResourcesByType("AWS::EC2::Instance") - - for _, r := range instanceResources { - instance := ec2.Instance{ - Metadata: r.Metadata(), - // metadata not supported by CloudFormation at the moment - - // https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/655 - MetadataOptions: ec2.MetadataOptions{ - Metadata: r.Metadata(), - HttpTokens: iacTypes.StringDefault("optional", r.Metadata()), - HttpEndpoint: iacTypes.StringDefault("enabled", r.Metadata()), - }, - UserData: r.GetStringProperty("UserData"), - } - - if launchTemplate, ok := findRelatedLaunchTemplate(ctx, r); ok { - instance = launchTemplate.Instance - } - - if instance.RootBlockDevice == nil { - instance.RootBlockDevice = &ec2.BlockDevice{ - Metadata: r.Metadata(), - Encrypted: iacTypes.BoolDefault(false, r.Metadata()), - } - } - - blockDevices := getBlockDevices(r) - for i, device := range blockDevices { - copyDevice := device - if i == 0 { - instance.RootBlockDevice = copyDevice - continue - } - instance.EBSBlockDevices = append(instance.EBSBlockDevices, device) - } - instances = append(instances, instance) - } - - return instances -} - -func findRelatedLaunchTemplate(fctx parser.FileContext, r *parser.Resource) (ec2.LaunchTemplate, bool) { - launchTemplateRef := r.GetProperty("LaunchTemplate.LaunchTemplateName") - if launchTemplateRef.IsString() { - res := findLaunchTemplateByName(fctx, launchTemplateRef) - if res != nil { - return adaptLaunchTemplate(res), true - } - } - - launchTemplateRef = r.GetProperty("LaunchTemplate.LaunchTemplateId") - if !launchTemplateRef.IsString() { - return ec2.LaunchTemplate{}, false - } - - resource := fctx.GetResourceByLogicalID(launchTemplateRef.AsString()) - if resource == nil { - return ec2.LaunchTemplate{}, false - } - return adaptLaunchTemplate(resource), true -} - -func findLaunchTemplateByName(fctx parser.FileContext, prop *parser.Property) *parser.Resource { - for _, res := range fctx.GetResourcesByType("AWS::EC2::LaunchTemplate") { - templateName := res.GetProperty("LaunchTemplateName") - if templateName.IsNotString() { - continue - } - - if prop.EqualTo(templateName.AsString()) { - return res - } - } - - return nil -} - -func getBlockDevices(r *parser.Resource) []*ec2.BlockDevice { - var blockDevices []*ec2.BlockDevice - - devicesProp := r.GetProperty("BlockDeviceMappings") - - if devicesProp.IsNil() { - return blockDevices - } - - for _, d := range devicesProp.AsList() { - device := &ec2.BlockDevice{ - Metadata: d.Metadata(), - Encrypted: d.GetBoolProperty("Ebs.Encrypted"), - } - - blockDevices = append(blockDevices, device) - } - - return blockDevices -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/launch_configuration.go b/pkg/iac/adapters/cloudformation/aws/ec2/launch_configuration.go deleted file mode 100644 index e99459b5d4f0..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/launch_configuration.go +++ /dev/null @@ -1,48 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getLaunchConfigurations(file parser.FileContext) (launchConfigurations []ec2.LaunchConfiguration) { - launchConfigResources := file.GetResourcesByType("AWS::AutoScaling::LaunchConfiguration") - - for _, r := range launchConfigResources { - - launchConfig := ec2.LaunchConfiguration{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("LaunchConfigurationName"), - AssociatePublicIP: r.GetBoolProperty("AssociatePublicIpAddress"), - MetadataOptions: ec2.MetadataOptions{ - Metadata: r.Metadata(), - HttpTokens: types.StringDefault("optional", r.Metadata()), - HttpEndpoint: types.StringDefault("enabled", r.Metadata()), - }, - UserData: r.GetStringProperty("UserData"), - } - - if opts := r.GetProperty("MetadataOptions"); opts.IsNotNil() { - launchConfig.MetadataOptions = ec2.MetadataOptions{ - Metadata: opts.Metadata(), - HttpTokens: opts.GetStringProperty("HttpTokens", "optional"), - HttpEndpoint: opts.GetStringProperty("HttpEndpoint", "enabled"), - } - } - - blockDevices := getBlockDevices(r) - for i, device := range blockDevices { - copyDevice := device - if i == 0 { - launchConfig.RootBlockDevice = copyDevice - continue - } - launchConfig.EBSBlockDevices = append(launchConfig.EBSBlockDevices, device) - } - - launchConfigurations = append(launchConfigurations, launchConfig) - - } - return launchConfigurations -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/launch_template.go b/pkg/iac/adapters/cloudformation/aws/ec2/launch_template.go deleted file mode 100644 index c138ed3284e1..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/launch_template.go +++ /dev/null @@ -1,56 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getLaunchTemplates(file parser.FileContext) (templates []ec2.LaunchTemplate) { - launchConfigResources := file.GetResourcesByType("AWS::EC2::LaunchTemplate") - - for _, r := range launchConfigResources { - templates = append(templates, adaptLaunchTemplate(r)) - } - return templates -} - -func adaptLaunchTemplate(r *parser.Resource) ec2.LaunchTemplate { - launchTemplate := ec2.LaunchTemplate{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("LaunchTemplateName", ""), - Instance: ec2.Instance{ - Metadata: r.Metadata(), - MetadataOptions: ec2.MetadataOptions{ - Metadata: r.Metadata(), - HttpTokens: types.StringDefault("optional", r.Metadata()), - HttpEndpoint: types.StringDefault("enabled", r.Metadata()), - }, - UserData: types.StringDefault("", r.Metadata()), - }, - } - - if data := r.GetProperty("LaunchTemplateData"); data.IsNotNil() { - if opts := data.GetProperty("MetadataOptions"); opts.IsNotNil() { - launchTemplate.MetadataOptions = ec2.MetadataOptions{ - Metadata: opts.Metadata(), - HttpTokens: opts.GetStringProperty("HttpTokens", "optional"), - HttpEndpoint: opts.GetStringProperty("HttpEndpoint", "enabled"), - } - } - - launchTemplate.Instance.UserData = data.GetStringProperty("UserData", "") - - blockDevices := getBlockDevices(r) - for i, device := range blockDevices { - copyDevice := device - if i == 0 { - launchTemplate.RootBlockDevice = copyDevice - } else { - launchTemplate.EBSBlockDevices = append(launchTemplate.EBSBlockDevices, device) - } - } - } - - return launchTemplate -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/nacl.go b/pkg/iac/adapters/cloudformation/aws/ec2/nacl.go deleted file mode 100644 index a91a12569994..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/nacl.go +++ /dev/null @@ -1,82 +0,0 @@ -package ec2 - -import ( - "strconv" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getNetworkACLs(ctx parser.FileContext) (acls []ec2.NetworkACL) { - for _, aclResource := range ctx.GetResourcesByType("AWS::EC2::NetworkAcl") { - acl := ec2.NetworkACL{ - Metadata: aclResource.Metadata(), - Rules: getRules(aclResource.ID(), ctx), - IsDefaultRule: iacTypes.BoolDefault(false, aclResource.Metadata()), - } - acls = append(acls, acl) - } - return acls -} - -func getRules(id string, ctx parser.FileContext) (rules []ec2.NetworkACLRule) { - for _, ruleResource := range ctx.GetResourcesByType("AWS::EC2::NetworkAclEntry") { - aclID := ruleResource.GetProperty("NetworkAclId") - if aclID.IsString() && aclID.AsString() == id { - - rule := ec2.NetworkACLRule{ - Metadata: ruleResource.Metadata(), - Type: iacTypes.StringDefault(ec2.TypeIngress, ruleResource.Metadata()), - Action: iacTypes.StringDefault(ec2.ActionAllow, ruleResource.Metadata()), - FromPort: iacTypes.IntDefault(-1, ruleResource.Metadata()), - ToPort: iacTypes.IntDefault(-1, ruleResource.Metadata()), - CIDRs: nil, - } - - if egressProperty := ruleResource.GetProperty("Egress"); egressProperty.IsBool() { - if egressProperty.AsBool() { - rule.Type = iacTypes.String(ec2.TypeEgress, egressProperty.Metadata()) - } else { - rule.Type = iacTypes.String(ec2.TypeIngress, egressProperty.Metadata()) - } - } - - if actionProperty := ruleResource.GetProperty("RuleAction"); actionProperty.IsString() { - if actionProperty.AsString() == ec2.ActionAllow { - rule.Action = iacTypes.String(ec2.ActionAllow, actionProperty.Metadata()) - } else { - rule.Action = iacTypes.String(ec2.ActionDeny, actionProperty.Metadata()) - } - } - - if protocolProperty := ruleResource.GetProperty("Protocol"); protocolProperty.IsInt() { - protocol := protocolProperty.AsIntValue().Value() - rule.Protocol = iacTypes.String(strconv.Itoa(protocol), protocolProperty.Metadata()) - } - - if ipv4Cidr := ruleResource.GetProperty("CidrBlock"); ipv4Cidr.IsString() { - rule.CIDRs = append(rule.CIDRs, ipv4Cidr.AsStringValue()) - } - - if ipv6Cidr := ruleResource.GetProperty("Ipv6CidrBlock"); ipv6Cidr.IsString() { - rule.CIDRs = append(rule.CIDRs, ipv6Cidr.AsStringValue()) - } - - portRange := ruleResource.GetProperty("PortRange") - fromPort := portRange.GetProperty("From").ConvertTo(cftypes.Int) - if fromPort.IsInt() { - rule.FromPort = fromPort.AsIntValue() - } - - toPort := portRange.GetProperty("To").ConvertTo(cftypes.Int) - if toPort.IsInt() { - rule.ToPort = toPort.AsIntValue() - } - - rules = append(rules, rule) - } - } - return rules -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/security_group.go b/pkg/iac/adapters/cloudformation/aws/ec2/security_group.go deleted file mode 100644 index e85a91cdecb6..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/security_group.go +++ /dev/null @@ -1,108 +0,0 @@ -package ec2 - -import ( - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getSecurityGroups(ctx parser.FileContext) []ec2.SecurityGroup { - mGroups := make(map[string]ec2.SecurityGroup) - - for _, r := range ctx.GetResourcesByType("AWS::EC2::SecurityGroup") { - group := ec2.SecurityGroup{ - Metadata: r.Metadata(), - Description: r.GetStringProperty("GroupDescription"), - IngressRules: getIngressRules(r), - EgressRules: getEgressRules(r), - IsDefault: types.Bool(r.GetStringProperty("GroupName").EqualTo("default"), r.Metadata()), - VPCID: r.GetStringProperty("VpcId"), - } - - mGroups[r.ID()] = group - } - - for _, r := range ctx.GetResourcesByType("AWS::EC2::SecurityGroupIngress") { - groupID := r.GetProperty("GroupId").AsString() - - if group, ok := mGroups[groupID]; ok { - group.IngressRules = append(group.IngressRules, adaptRule(r)) - mGroups[groupID] = group - } - } - - for _, r := range ctx.GetResourcesByType("AWS::EC2::SecurityGroupEgress") { - groupID := r.GetProperty("GroupId").AsString() - - if group, ok := mGroups[groupID]; ok { - group.EgressRules = append(group.EgressRules, adaptRule(r)) - mGroups[groupID] = group - } - } - - if len(mGroups) > 0 { - return lo.Values(mGroups) - } - return nil -} - -func getIngressRules(r *parser.Resource) (sgRules []ec2.SecurityGroupRule) { - if ingressProp := r.GetProperty("SecurityGroupIngress"); ingressProp.IsList() { - for _, ingress := range ingressProp.AsList() { - sgRules = append(sgRules, adaptRule(ingress)) - } - } - - return sgRules -} - -func getEgressRules(r *parser.Resource) (sgRules []ec2.SecurityGroupRule) { - if egressProp := r.GetProperty("SecurityGroupEgress"); egressProp.IsList() { - for _, egress := range egressProp.AsList() { - sgRules = append(sgRules, adaptRule(egress)) - } - } - return sgRules -} - -func adaptRule(r interface { - GetProperty(string) *parser.Property - Metadata() types.Metadata - GetStringProperty(string, ...string) types.StringValue -}) ec2.SecurityGroupRule { - rule := ec2.SecurityGroupRule{ - Metadata: r.Metadata(), - Description: r.GetStringProperty("Description"), - FromPort: types.IntDefault(-1, r.Metadata()), - ToPort: types.IntDefault(-1, r.Metadata()), - } - - v4Cidr := r.GetProperty("CidrIp") - if v4Cidr.IsString() && v4Cidr.AsStringValue().IsNotEmpty() { - rule.CIDRs = append(rule.CIDRs, types.StringExplicit(v4Cidr.AsString(), v4Cidr.Metadata())) - } - v6Cidr := r.GetProperty("CidrIpv6") - if v6Cidr.IsString() && v6Cidr.AsStringValue().IsNotEmpty() { - rule.CIDRs = append(rule.CIDRs, types.StringExplicit(v6Cidr.AsString(), v6Cidr.Metadata())) - } - - fromPort := r.GetProperty("FromPort").ConvertTo(cftypes.Int) - if fromPort.IsInt() { - rule.FromPort = fromPort.AsIntValue() - } - - toPort := r.GetProperty("ToPort").ConvertTo(cftypes.Int) - if toPort.IsInt() { - rule.ToPort = toPort.AsIntValue() - } - - protocol := r.GetProperty("IpProtocol").ConvertTo(cftypes.String) - if protocol.IsString() { - rule.Protocol = protocol.AsStringValue() - } - - return rule -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/subnet.go b/pkg/iac/adapters/cloudformation/aws/ec2/subnet.go deleted file mode 100644 index 725a441429e1..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/subnet.go +++ /dev/null @@ -1,21 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getSubnets(ctx parser.FileContext) (subnets []ec2.Subnet) { - - subnetResources := ctx.GetResourcesByType("AWS::EC2::Subnet") - for _, r := range subnetResources { - - subnet := ec2.Subnet{ - Metadata: r.Metadata(), - MapPublicIpOnLaunch: r.GetBoolProperty("MapPublicIpOnLaunch"), - } - - subnets = append(subnets, subnet) - } - return subnets -} diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/volume.go b/pkg/iac/adapters/cloudformation/aws/ec2/volume.go deleted file mode 100644 index 35c22e053cfc..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ec2/volume.go +++ /dev/null @@ -1,25 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getVolumes(ctx parser.FileContext) (volumes []ec2.Volume) { - - volumeResources := ctx.GetResourcesByType("AWS::EC2::Volume") - for _, r := range volumeResources { - - volume := ec2.Volume{ - Metadata: r.Metadata(), - Encryption: ec2.Encryption{ - Metadata: r.Metadata(), - Enabled: r.GetBoolProperty("Encrypted"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - }, - } - - volumes = append(volumes, volume) - } - return volumes -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecr/ecr.go b/pkg/iac/adapters/cloudformation/aws/ecr/ecr.go deleted file mode 100644 index c7552132d862..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecr/ecr.go +++ /dev/null @@ -1,13 +0,0 @@ -package ecr - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an ECR instance -func Adapt(cfFile parser.FileContext) ecr.ECR { - return ecr.ECR{ - Repositories: getRepositories(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecr/ecr_test.go b/pkg/iac/adapters/cloudformation/aws/ecr/ecr_test.go deleted file mode 100644 index bf411dddbf8a..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecr/ecr_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package ecr - -import ( - "testing" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected ecr.ECR - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - -`, - expected: ecr.ECR{}, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyRepository: - Type: AWS::ECR::Repository - Properties: - RepositoryName: "test-repository" - ImageScanningConfiguration: - ScanOnPush: true - EncryptionConfiguration: - EncryptionType: KMS - KmsKey: mykey - ImageTagMutability: IMMUTABLE - RepositoryPolicyText: - Version: "2012-10-17" - Statement: - - - Sid: AllowPushPull - Effect: Allow - Principal: - AWS: - - "arn:aws:iam::123456789012:user/Alice" - Action: - - "ecr:GetDownloadUrlForLayer" - - "ecr:BatchGetImage" - `, - expected: ecr.ECR{ - Repositories: []ecr.Repository{ - { - ImageTagsImmutable: types.BoolTest(true), - ImageScanning: ecr.ImageScanning{ - ScanOnPush: types.BoolTest(true), - }, - Encryption: ecr.Encryption{ - Type: types.StringTest("KMS"), - KMSKeyID: types.StringTest("mykey"), - }, - Policies: []iam.Policy{ - { - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithVersion("2012-10-17"). - WithStatement( - iamgo.NewStatementBuilder(). - WithSid("AllowPushPull"). - WithEffect("Allow"). - WithAWSPrincipals( - []string{"arn:aws:iam::123456789012:user/Alice"}, - ). - WithActions( - []string{ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - }, - ). - Build(), - ). - Build(), - } - }(), - }, - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecr/repository.go b/pkg/iac/adapters/cloudformation/aws/ecr/repository.go deleted file mode 100644 index e0491c121b4d..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecr/repository.go +++ /dev/null @@ -1,87 +0,0 @@ -package ecr - -import ( - "errors" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getRepositories(ctx parser.FileContext) (repositories []ecr.Repository) { - - repositoryResources := ctx.GetResourcesByType("AWS::ECR::Repository") - - for _, r := range repositoryResources { - - repository := ecr.Repository{ - Metadata: r.Metadata(), - ImageScanning: ecr.ImageScanning{ - Metadata: r.Metadata(), - ScanOnPush: iacTypes.BoolDefault(false, r.Metadata()), - }, - ImageTagsImmutable: hasImmutableImageTags(r), - Policies: nil, - Encryption: ecr.Encryption{ - Metadata: r.Metadata(), - Type: iacTypes.StringDefault(ecr.EncryptionTypeAES256, r.Metadata()), - KMSKeyID: iacTypes.StringDefault("", r.Metadata()), - }, - } - - if imageScanningProp := r.GetProperty("ImageScanningConfiguration"); imageScanningProp.IsNotNil() { - repository.ImageScanning = ecr.ImageScanning{ - Metadata: imageScanningProp.Metadata(), - ScanOnPush: imageScanningProp.GetBoolProperty("ScanOnPush", false), - } - } - - if encProp := r.GetProperty("EncryptionConfiguration"); encProp.IsNotNil() { - repository.Encryption = ecr.Encryption{ - Metadata: encProp.Metadata(), - Type: encProp.GetStringProperty("EncryptionType", ecr.EncryptionTypeAES256), - KMSKeyID: encProp.GetStringProperty("KmsKey", ""), - } - } - - if policy, err := getPolicy(r); err == nil { - repository.Policies = append(repository.Policies, *policy) - } - - repositories = append(repositories, repository) - } - - return repositories -} - -func getPolicy(r *parser.Resource) (*iam.Policy, error) { - policyProp := r.GetProperty("RepositoryPolicyText") - if policyProp.IsNil() { - return nil, errors.New("missing policy") - } - - parsed, err := iamgo.Parse(policyProp.GetJsonBytes()) - if err != nil { - return nil, err - } - - return &iam.Policy{ - Metadata: policyProp.Metadata(), - Name: iacTypes.StringDefault("", policyProp.Metadata()), - Document: iam.Document{ - Metadata: policyProp.Metadata(), - Parsed: *parsed, - }, - Builtin: iacTypes.Bool(false, policyProp.Metadata()), - }, nil -} - -func hasImmutableImageTags(r *parser.Resource) iacTypes.BoolValue { - mutabilityProp := r.GetProperty("ImageTagMutability") - if mutabilityProp.IsNil() { - return iacTypes.BoolDefault(false, r.Metadata()) - } - return iacTypes.Bool(mutabilityProp.EqualTo("IMMUTABLE"), mutabilityProp.Metadata()) -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecs/cluster.go b/pkg/iac/adapters/cloudformation/aws/ecs/cluster.go deleted file mode 100644 index ce000d8d1457..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecs/cluster.go +++ /dev/null @@ -1,57 +0,0 @@ -package ecs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(ctx parser.FileContext) (clusters []ecs.Cluster) { - - clusterResources := ctx.GetResourcesByType("AWS::ECS::Cluster") - - for _, r := range clusterResources { - - cluster := ecs.Cluster{ - Metadata: r.Metadata(), - Settings: getClusterSettings(r), - } - - clusters = append(clusters, cluster) - - } - - return clusters -} - -func getClusterSettings(r *parser.Resource) ecs.ClusterSettings { - - clusterSettings := ecs.ClusterSettings{ - Metadata: r.Metadata(), - ContainerInsightsEnabled: types.BoolDefault(false, r.Metadata()), - } - - clusterSettingMap := r.GetProperty("ClusterSettings") - if clusterSettingMap.IsNil() || clusterSettingMap.IsNotList() { - return clusterSettings - } - - clusterSettings.Metadata = clusterSettingMap.Metadata() - - for _, setting := range clusterSettingMap.AsList() { - checkProperty(setting, &clusterSettings) - } - - return clusterSettings -} - -func checkProperty(setting *parser.Property, clusterSettings *ecs.ClusterSettings) { - settingMap := setting.AsMap() - name := settingMap["Name"] - if name.IsNotNil() && name.EqualTo("containerInsights") { - value := settingMap["Value"] - if value.IsNotNil() && !value.EqualTo("disabled") { - clusterSettings.ContainerInsightsEnabled = types.Bool(true, value.Metadata()) - } - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecs/ecs.go b/pkg/iac/adapters/cloudformation/aws/ecs/ecs.go deleted file mode 100644 index 5707cdddcd8e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecs/ecs.go +++ /dev/null @@ -1,14 +0,0 @@ -package ecs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an ECS instance -func Adapt(cfFile parser.FileContext) ecs.ECS { - return ecs.ECS{ - Clusters: getClusters(cfFile), - TaskDefinitions: getTaskDefinitions(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecs/ecs_test.go b/pkg/iac/adapters/cloudformation/aws/ecs/ecs_test.go deleted file mode 100644 index 5581c3a44be7..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecs/ecs_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package ecs - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected ecs.ECS - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - ECSCluster: - Type: 'AWS::ECS::Cluster' - Properties: - ClusterName: MyFargateCluster - ClusterSettings: - - Name: containerInsights - Value: enabled - taskdefinition: - Type: AWS::ECS::TaskDefinition - Properties: - ContainerDefinitions: - - - Name: "busybox" - Image: "busybox" - Cpu: "256" - Memory: "512" - Essential: true - Privileged: true - Environment: - - Name: entryPoint - Value: 'sh, -c' - Volumes: - - - Host: - SourcePath: "/var/lib/docker/vfs/dir/" - Name: "my-vol" - EFSVolumeConfiguration: - TransitEncryption: enabled -`, - expected: ecs.ECS{ - Clusters: []ecs.Cluster{ - { - Settings: ecs.ClusterSettings{ - ContainerInsightsEnabled: types.BoolTest(true), - }, - }, - }, - TaskDefinitions: []ecs.TaskDefinition{ - { - Volumes: []ecs.Volume{ - { - EFSVolumeConfiguration: ecs.EFSVolumeConfiguration{ - TransitEncryptionEnabled: types.BoolTest(true), - }, - }, - }, - ContainerDefinitions: []ecs.ContainerDefinition{ - { - Name: types.StringTest("busybox"), - Image: types.StringTest("busybox"), - CPU: types.StringTest("256"), - Memory: types.StringTest("512"), - Essential: types.BoolTest(true), - Privileged: types.BoolTest(true), - Environment: []ecs.EnvVar{ - { - Name: types.StringTest("entryPoint"), - Value: types.StringTest("sh, -c"), - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "ecs Cluster Enhanced Container Insights", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - ECSCluster: - Type: 'AWS::ECS::Cluster' - Properties: - ClusterName: MyFargateCluster - ClusterSettings: - - Name: containerInsights - Value: enhanced -`, - expected: ecs.ECS{ - Clusters: []ecs.Cluster{ - { - Settings: ecs.ClusterSettings{ - ContainerInsightsEnabled: types.BoolTest(true), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - ECSCluster: - Type: 'AWS::ECS::Cluster' - taskdefinition: - Type: AWS::ECS::TaskDefinition - `, - expected: ecs.ECS{ - Clusters: []ecs.Cluster{{}}, - TaskDefinitions: []ecs.TaskDefinition{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ecs/task_definition.go b/pkg/iac/adapters/cloudformation/aws/ecs/task_definition.go deleted file mode 100644 index c9b8e59a4a86..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ecs/task_definition.go +++ /dev/null @@ -1,86 +0,0 @@ -package ecs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getTaskDefinitions(ctx parser.FileContext) (taskDefinitions []ecs.TaskDefinition) { - - taskDefResources := ctx.GetResourcesByType("AWS::ECS::TaskDefinition") - - for _, r := range taskDefResources { - definitions, _ := getContainerDefinitions(r) - taskDef := ecs.TaskDefinition{ - Metadata: r.Metadata(), - Volumes: getVolumes(r), - ContainerDefinitions: definitions, - } - taskDefinitions = append(taskDefinitions, taskDef) - } - - return taskDefinitions -} - -func getContainerDefinitions(r *parser.Resource) ([]ecs.ContainerDefinition, error) { - var definitions []ecs.ContainerDefinition - containerDefs := r.GetProperty("ContainerDefinitions") - if containerDefs.IsNil() || containerDefs.IsNotList() { - return definitions, nil - } - for _, containerDef := range containerDefs.AsList() { - - var envVars []ecs.EnvVar - envVarsList := containerDef.GetProperty("Environment") - if envVarsList.IsNotNil() && envVarsList.IsList() { - for _, envVar := range envVarsList.AsList() { - envVars = append(envVars, ecs.EnvVar{ - Name: envVar.GetStringProperty("Name"), - Value: envVar.GetStringProperty("Value"), - }) - } - } - definition := ecs.ContainerDefinition{ - Metadata: containerDef.Metadata(), - Name: containerDef.GetStringProperty("Name"), - Image: containerDef.GetStringProperty("Image"), - CPU: containerDef.GetStringProperty("Cpu"), - Memory: containerDef.GetStringProperty("Memory"), - Essential: containerDef.GetBoolProperty("Essential"), - Privileged: containerDef.GetBoolProperty("Privileged"), - Environment: envVars, - PortMappings: nil, - } - definitions = append(definitions, definition) - } - if containerDefs.IsNotNil() && containerDefs.IsString() { - return ecs.CreateDefinitionsFromString(r.Metadata(), containerDefs.AsString()) - } - return definitions, nil -} - -func getVolumes(r *parser.Resource) (volumes []ecs.Volume) { - - volumesList := r.GetProperty("Volumes") - if volumesList.IsNil() || volumesList.IsNotList() { - return volumes - } - - for _, v := range volumesList.AsList() { - volume := ecs.Volume{ - Metadata: r.Metadata(), - EFSVolumeConfiguration: ecs.EFSVolumeConfiguration{ - Metadata: r.Metadata(), - TransitEncryptionEnabled: types.BoolDefault(false, r.Metadata()), - }, - } - transitProp := v.GetProperty("EFSVolumeConfiguration.TransitEncryption") - if transitProp.IsNotNil() && transitProp.EqualTo("enabled", parser.IgnoreCase) { - volume.EFSVolumeConfiguration.TransitEncryptionEnabled = types.Bool(true, transitProp.Metadata()) - } - - volumes = append(volumes, volume) - } - return volumes -} diff --git a/pkg/iac/adapters/cloudformation/aws/efs/efs.go b/pkg/iac/adapters/cloudformation/aws/efs/efs.go deleted file mode 100644 index bda1f15ffbfb..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/efs/efs.go +++ /dev/null @@ -1,13 +0,0 @@ -package efs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an EFS instance -func Adapt(cfFile parser.FileContext) efs.EFS { - return efs.EFS{ - FileSystems: getFileSystems(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/efs/efs_test.go b/pkg/iac/adapters/cloudformation/aws/efs/efs_test.go deleted file mode 100644 index a22d769020b6..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/efs/efs_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package efs - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected efs.EFS - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - FileSystemResource: - Type: 'AWS::EFS::FileSystem' - Properties: - Encrypted: true -`, - expected: efs.EFS{ - FileSystems: []efs.FileSystem{ - { - Encrypted: types.BoolTest(true), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - FileSystemResource: - Type: 'AWS::EFS::FileSystem' - `, - expected: efs.EFS{ - FileSystems: []efs.FileSystem{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/efs/filesystem.go b/pkg/iac/adapters/cloudformation/aws/efs/filesystem.go deleted file mode 100644 index 728b09079f2a..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/efs/filesystem.go +++ /dev/null @@ -1,23 +0,0 @@ -package efs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getFileSystems(ctx parser.FileContext) (filesystems []efs.FileSystem) { - - filesystemResources := ctx.GetResourcesByType("AWS::EFS::FileSystem") - - for _, r := range filesystemResources { - - filesystem := efs.FileSystem{ - Metadata: r.Metadata(), - Encrypted: r.GetBoolProperty("Encrypted"), - } - - filesystems = append(filesystems, filesystem) - } - - return filesystems -} diff --git a/pkg/iac/adapters/cloudformation/aws/eks/cluster.go b/pkg/iac/adapters/cloudformation/aws/eks/cluster.go deleted file mode 100644 index c960924e33d4..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/eks/cluster.go +++ /dev/null @@ -1,94 +0,0 @@ -package eks - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(ctx parser.FileContext) (clusters []eks.Cluster) { - - clusterResources := ctx.GetResourcesByType("AWS::EKS::Cluster") - - for _, r := range clusterResources { - cluster := eks.Cluster{ - Metadata: r.Metadata(), - Logging: getLogging(r), - Encryption: getEncryptionConfig(r), - PublicAccessEnabled: r.GetBoolProperty("ResourcesVpcConfig.EndpointPublicAccess"), - PublicAccessCIDRs: getPublicCIDRs(r), - } - - clusters = append(clusters, cluster) - } - return clusters -} - -func getPublicCIDRs(r *parser.Resource) []iacTypes.StringValue { - publicAccessCidrs := r.GetProperty("ResourcesVpcConfig.PublicAccessCidrs") - if publicAccessCidrs.IsNotList() { - return nil - } - - var cidrs []iacTypes.StringValue - for _, el := range publicAccessCidrs.AsList() { - cidrs = append(cidrs, el.AsStringValue()) - } - - return cidrs -} - -func getEncryptionConfig(r *parser.Resource) eks.Encryption { - - encryptionConfigs := r.GetProperty("EncryptionConfig") - if encryptionConfigs.IsNotList() { - return eks.Encryption{ - Metadata: r.Metadata(), - } - } - - for _, encryptionConfig := range encryptionConfigs.AsList() { - resources := encryptionConfig.GetProperty("Resources") - hasSecrets := resources.IsList() && resources.Contains("secrets") - return eks.Encryption{ - Metadata: encryptionConfig.Metadata(), - KMSKeyID: encryptionConfig.GetStringProperty("Provider.KeyArn"), - Secrets: iacTypes.Bool(hasSecrets, resources.Metadata()), - } - } - - return eks.Encryption{ - Metadata: r.Metadata(), - } -} - -func getLogging(r *parser.Resource) eks.Logging { - enabledTypes := r.GetProperty("Logging.ClusterLogging.EnabledTypes") - if enabledTypes.IsNotList() { - return eks.Logging{ - Metadata: r.Metadata(), - } - } - - logging := eks.Logging{ - Metadata: enabledTypes.Metadata(), - } - - for _, typeConf := range enabledTypes.AsList() { - switch typ := typeConf.GetProperty("Type"); typ.AsString() { - case "api": - logging.API = iacTypes.Bool(true, typ.Metadata()) - case "audit": - logging.Audit = iacTypes.Bool(true, typ.Metadata()) - case "authenticator": - logging.Authenticator = iacTypes.Bool(true, typ.Metadata()) - case "controllerManager": - logging.ControllerManager = iacTypes.Bool(true, typ.Metadata()) - case "scheduler": - logging.Scheduler = iacTypes.Bool(true, typ.Metadata()) - } - - } - - return logging -} diff --git a/pkg/iac/adapters/cloudformation/aws/eks/eks.go b/pkg/iac/adapters/cloudformation/aws/eks/eks.go deleted file mode 100644 index fe6933c19a8e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/eks/eks.go +++ /dev/null @@ -1,13 +0,0 @@ -package eks - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an EKS instance -func Adapt(cfFile parser.FileContext) eks.EKS { - return eks.EKS{ - Clusters: getClusters(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/eks/eks_test.go b/pkg/iac/adapters/cloudformation/aws/eks/eks_test.go deleted file mode 100644 index 36981f6bf544..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/eks/eks_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package eks - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected eks.EKS - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - EKSCluster: - Type: AWS::EKS::Cluster - Properties: - Logging: - ClusterLogging: - EnabledTypes: - - Type: api - - Type: audit - - Type: authenticator - - Type: controllerManager - - Type: scheduler - EncryptionConfig: - - Provider: - KeyArn: alias/mykey - Resources: [secrets] - ResourcesVpcConfig: - EndpointPublicAccess: True - PublicAccessCidrs: - - 0.0.0.0/0 -`, - expected: eks.EKS{ - Clusters: []eks.Cluster{ - { - Logging: eks.Logging{ - API: types.BoolTest(true), - Audit: types.BoolTest(true), - Authenticator: types.BoolTest(true), - ControllerManager: types.BoolTest(true), - Scheduler: types.BoolTest(true), - }, - Encryption: eks.Encryption{ - KMSKeyID: types.StringTest("alias/mykey"), - Secrets: types.BoolTest(true), - }, - PublicAccessEnabled: types.BoolTest(true), - PublicAccessCIDRs: []types.StringValue{ - types.StringTest("0.0.0.0/0"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - EKSCluster: - Type: AWS::EKS::Cluster - `, - expected: eks.EKS{ - Clusters: []eks.Cluster{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticache/cluster.go b/pkg/iac/adapters/cloudformation/aws/elasticache/cluster.go deleted file mode 100644 index bdbefbbbb6c2..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticache/cluster.go +++ /dev/null @@ -1,24 +0,0 @@ -package elasticache - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getClusterGroups(ctx parser.FileContext) (clusters []elasticache.Cluster) { - - clusterResources := ctx.GetResourcesByType("AWS::ElastiCache::CacheCluster") - - for _, r := range clusterResources { - cluster := elasticache.Cluster{ - Metadata: r.Metadata(), - Engine: r.GetStringProperty("Engine"), - NodeType: r.GetStringProperty("CacheNodeType"), - SnapshotRetentionLimit: r.GetIntProperty("SnapshotRetentionLimit"), - } - - clusters = append(clusters, cluster) - } - - return clusters -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticache/elasticache.go b/pkg/iac/adapters/cloudformation/aws/elasticache/elasticache.go deleted file mode 100644 index 1d69000ff9ea..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticache/elasticache.go +++ /dev/null @@ -1,15 +0,0 @@ -package elasticache - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an ElasticCache instance -func Adapt(cfFile parser.FileContext) elasticache.ElastiCache { - return elasticache.ElastiCache{ - Clusters: getClusterGroups(cfFile), - ReplicationGroups: getReplicationGroups(cfFile), - SecurityGroups: getSecurityGroups(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticache/elasticache_test.go b/pkg/iac/adapters/cloudformation/aws/elasticache/elasticache_test.go deleted file mode 100644 index e7e3d018b14c..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticache/elasticache_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package elasticache - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected elasticache.ElastiCache - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - ElasticacheCluster: - Type: 'AWS::ElastiCache::CacheCluster' - Properties: - Engine: memcached - CacheNodeType: cache.t2.micro - SnapshotRetentionLimit: 5 - myReplicationGroup: - Type: 'AWS::ElastiCache::ReplicationGroup' - Properties: - TransitEncryptionEnabled: true - AtRestEncryptionEnabled: true - mySecGroup: - Type: AWS::ElastiCache::SecurityGroup - Properties: - Description: test -`, - expected: elasticache.ElastiCache{ - Clusters: []elasticache.Cluster{ - { - Engine: types.StringTest("memcached"), - NodeType: types.StringTest("cache.t2.micro"), - SnapshotRetentionLimit: types.IntTest(5), - }, - }, - ReplicationGroups: []elasticache.ReplicationGroup{ - { - TransitEncryptionEnabled: types.BoolTest(true), - AtRestEncryptionEnabled: types.BoolTest(true), - }, - }, - SecurityGroups: []elasticache.SecurityGroup{ - { - Description: types.StringTest("test"), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - ElasticacheCluster: - Type: 'AWS::ElastiCache::CacheCluster' - myReplicationGroup: - Type: 'AWS::ElastiCache::ReplicationGroup' - mySecGroup: - Type: AWS::ElastiCache::SecurityGroup - `, - expected: elasticache.ElastiCache{ - Clusters: []elasticache.Cluster{{}}, - ReplicationGroups: []elasticache.ReplicationGroup{{}}, - SecurityGroups: []elasticache.SecurityGroup{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticache/replication_group.go b/pkg/iac/adapters/cloudformation/aws/elasticache/replication_group.go deleted file mode 100644 index 95b6a7916370..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticache/replication_group.go +++ /dev/null @@ -1,23 +0,0 @@ -package elasticache - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getReplicationGroups(ctx parser.FileContext) (replicationGroups []elasticache.ReplicationGroup) { - - replicationGroupResources := ctx.GetResourcesByType("AWS::ElastiCache::ReplicationGroup") - - for _, r := range replicationGroupResources { - replicationGroup := elasticache.ReplicationGroup{ - Metadata: r.Metadata(), - TransitEncryptionEnabled: r.GetBoolProperty("TransitEncryptionEnabled"), - AtRestEncryptionEnabled: r.GetBoolProperty("AtRestEncryptionEnabled"), - } - - replicationGroups = append(replicationGroups, replicationGroup) - } - - return replicationGroups -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticache/security_group.go b/pkg/iac/adapters/cloudformation/aws/elasticache/security_group.go deleted file mode 100644 index 8be6e68f6903..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticache/security_group.go +++ /dev/null @@ -1,22 +0,0 @@ -package elasticache - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getSecurityGroups(ctx parser.FileContext) (securityGroups []elasticache.SecurityGroup) { - - sgResources := ctx.GetResourcesByType("AWS::ElastiCache::SecurityGroup") - - for _, r := range sgResources { - - sg := elasticache.SecurityGroup{ - Metadata: r.Metadata(), - Description: r.GetStringProperty("Description"), - } - securityGroups = append(securityGroups, sg) - } - - return securityGroups -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticsearch/domain.go b/pkg/iac/adapters/cloudformation/aws/elasticsearch/domain.go deleted file mode 100644 index 2ca77a5d7448..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticsearch/domain.go +++ /dev/null @@ -1,88 +0,0 @@ -package elasticsearch - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticsearch" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getDomains(ctx parser.FileContext) (domains []elasticsearch.Domain) { - - domainResources := ctx.GetResourcesByType("AWS::Elasticsearch::Domain", "AWS::OpenSearchService::Domain") - - for _, r := range domainResources { - - domain := elasticsearch.Domain{ - Metadata: r.Metadata(), - DomainName: r.GetStringProperty("DomainName"), - AccessPolicies: r.GetStringProperty("AccessPolicies"), - VpcId: iacTypes.String("", r.Metadata()), - LogPublishing: elasticsearch.LogPublishing{ - Metadata: r.Metadata(), - AuditEnabled: iacTypes.BoolDefault(false, r.Metadata()), - CloudWatchLogGroupArn: iacTypes.String("", r.Metadata()), - }, - TransitEncryption: elasticsearch.TransitEncryption{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - }, - AtRestEncryption: elasticsearch.AtRestEncryption{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - KmsKeyId: iacTypes.String("", r.Metadata()), - }, - Endpoint: elasticsearch.Endpoint{ - Metadata: r.Metadata(), - EnforceHTTPS: iacTypes.BoolDefault(false, r.Metadata()), - }, - ServiceSoftwareOptions: elasticsearch.ServiceSoftwareOptions{ - Metadata: r.Metadata(), - CurrentVersion: iacTypes.String("", r.Metadata()), - NewVersion: iacTypes.String("", r.Metadata()), - UpdateStatus: iacTypes.String("", r.Metadata()), - UpdateAvailable: iacTypes.Bool(false, r.Metadata()), - }, - } - - if r.Type() == "AWS::OpenSearchService::Domain" { - domain.DedicatedMasterEnabled = r.GetBoolProperty("ClusterConfig.DedicatedMasterEnabled") - } else { - domain.DedicatedMasterEnabled = r.GetBoolProperty("ElasticsearchClusterConfig.DedicatedMasterEnabled") - } - - if prop := r.GetProperty("LogPublishingOptions"); prop.IsNotNil() { - domain.LogPublishing = elasticsearch.LogPublishing{ - Metadata: prop.Metadata(), - AuditEnabled: prop.GetBoolProperty("AUDIT_LOGS.Enabled"), - CloudWatchLogGroupArn: prop.GetStringProperty("AUDIT_LOGS.CloudWatchLogsLogGroupArn"), - } - } - - if prop := r.GetProperty("NodeToNodeEncryptionOptions"); prop.IsNotNil() { - domain.TransitEncryption = elasticsearch.TransitEncryption{ - Metadata: prop.Metadata(), - Enabled: prop.GetBoolProperty("Enabled"), - } - } - - if prop := r.GetProperty("EncryptionAtRestOptions"); prop.IsNotNil() { - domain.AtRestEncryption = elasticsearch.AtRestEncryption{ - Metadata: prop.Metadata(), - Enabled: prop.GetBoolProperty("Enabled"), - KmsKeyId: prop.GetStringProperty("KmsKeyId"), - } - } - - if prop := r.GetProperty("DomainEndpointOptions"); prop.IsNotNil() { - domain.Endpoint = elasticsearch.Endpoint{ - Metadata: prop.Metadata(), - EnforceHTTPS: prop.GetBoolProperty("EnforceHTTPS"), - TLSPolicy: prop.GetStringProperty("TLSSecurityPolicy"), - } - } - - domains = append(domains, domain) - } - - return domains -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticsearch/elasticsearch.go b/pkg/iac/adapters/cloudformation/aws/elasticsearch/elasticsearch.go deleted file mode 100644 index b3e230b0435b..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticsearch/elasticsearch.go +++ /dev/null @@ -1,13 +0,0 @@ -package elasticsearch - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticsearch" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an ElasticSearch instance -func Adapt(cfFile parser.FileContext) elasticsearch.Elasticsearch { - return elasticsearch.Elasticsearch{ - Domains: getDomains(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elasticsearch/elasticsearch_test.go b/pkg/iac/adapters/cloudformation/aws/elasticsearch/elasticsearch_test.go deleted file mode 100644 index afb9c3a81e22..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elasticsearch/elasticsearch_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package elasticsearch - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticsearch" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected elasticsearch.Elasticsearch - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - OpenSearchServiceDomain: - Type: AWS::OpenSearchService::Domain - Properties: - DomainName: 'test' - ClusterConfig: - DedicatedMasterEnabled: true - NodeToNodeEncryptionOptions: - Enabled: true - EncryptionAtRestOptions: - Enabled: true - KmsKeyId: mykey - DomainEndpointOptions: - EnforceHTTPS: true - TLSSecurityPolicy: Policy-Min-TLS-1-0-2019-07 - AccessPolicies: - Version: '2012-10-17' - Statement: - - - Effect: 'Allow' - Principal: - AWS: 'arn:aws:iam::123456789012:user/opensearch-user' - Action: 'es:*' - Resource: 'arn:aws:es:us-east-1:846973539254:domain/test/*' - LogPublishingOptions: - AUDIT_LOGS: - CloudWatchLogsLogGroupArn: 'arn:aws:logs:us-east-1:123456789012:log-group:/aws/opensearch/domains/opensearch-application-logs' - Enabled: true -`, - expected: elasticsearch.Elasticsearch{ - Domains: []elasticsearch.Domain{ - { - DomainName: types.StringTest("test"), - DedicatedMasterEnabled: types.BoolTest(true), - LogPublishing: elasticsearch.LogPublishing{ - AuditEnabled: types.BoolTest(true), - CloudWatchLogGroupArn: types.StringTest("arn:aws:logs:us-east-1:123456789012:log-group:/aws/opensearch/domains/opensearch-application-logs"), - }, - TransitEncryption: elasticsearch.TransitEncryption{ - Enabled: types.BoolTest(true), - }, - AtRestEncryption: elasticsearch.AtRestEncryption{ - Enabled: types.BoolTest(true), - KmsKeyId: types.StringTest("mykey"), - }, - Endpoint: elasticsearch.Endpoint{ - EnforceHTTPS: types.BoolTest(true), - TLSPolicy: types.StringTest("Policy-Min-TLS-1-0-2019-07"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - OpenSearchServiceDomain: - Type: AWS::OpenSearchService::Domain - `, - expected: elasticsearch.Elasticsearch{ - Domains: []elasticsearch.Domain{{}}, - }, - }, - { - name: "Elasticsearch", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - ElasticsearchDomain: - Type: AWS::Elasticsearch::Domain - Properties: - ElasticsearchClusterConfig: - DedicatedMasterEnabled: true - `, - expected: elasticsearch.Elasticsearch{ - Domains: []elasticsearch.Domain{ - { - DedicatedMasterEnabled: types.BoolTest(true), - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elb/adapt_test.go b/pkg/iac/adapters/cloudformation/aws/elb/adapt_test.go deleted file mode 100644 index 2c5ee494e66d..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elb/adapt_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package elb - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elb" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected elb.ELB - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: "2010-09-09" -Resources: - LoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - DependsOn: - - ALBLogsBucketPermission - Properties: - Name: "k8s-dev" - Scheme: internal - IpAddressType: ipv4 - LoadBalancerAttributes: - - Key: routing.http2.enabled - Value: "true" - - Key: deletion_protection.enabled - Value: "true" - - Key: routing.http.drop_invalid_header_fields.enabled - Value: "true" - - Key: access_logs.s3.enabled - Value: "true" - Tags: - - Key: ingress.k8s.aws/resource - Value: LoadBalancer - - Key: elbv2.k8s.aws/cluster - Value: "biomage-dev" - Type: application - Listener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: 'redirect' - RedirectConfig: - Port: 443 - Protocol: HTTPS - StatusCode: HTTP_302 - LoadBalancerArn: !Ref LoadBalancer - Protocol: HTTPS - SslPolicy: "ELBSecurityPolicy-TLS-1-2-2017-01" -`, - expected: elb.ELB{ - LoadBalancers: []elb.LoadBalancer{ - { - Type: types.StringTest("application"), - DropInvalidHeaderFields: types.BoolTest(true), - Internal: types.Bool(true, types.NewTestMetadata()), - Listeners: []elb.Listener{ - { - Protocol: types.StringTest("HTTPS"), - TLSPolicy: types.StringTest("ELBSecurityPolicy-TLS-1-2-2017-01"), - DefaultActions: []elb.Action{ - { - Type: types.StringTest("redirect"), - }, - }, - }, - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elb/elb.go b/pkg/iac/adapters/cloudformation/aws/elb/elb.go deleted file mode 100644 index 2ff1d2c74777..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elb/elb.go +++ /dev/null @@ -1,13 +0,0 @@ -package elb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elb" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an ELB instance -func Adapt(cfFile parser.FileContext) elb.ELB { - return elb.ELB{ - LoadBalancers: getLoadBalancers(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/elb/loadbalancer.go b/pkg/iac/adapters/cloudformation/aws/elb/loadbalancer.go deleted file mode 100644 index 002b6487ba43..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/elb/loadbalancer.go +++ /dev/null @@ -1,81 +0,0 @@ -package elb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elb" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getLoadBalancers(ctx parser.FileContext) (loadbalancers []elb.LoadBalancer) { - - loadBalanacerResources := ctx.GetResourcesByType("AWS::ElasticLoadBalancingV2::LoadBalancer") - - for _, r := range loadBalanacerResources { - lb := elb.LoadBalancer{ - Metadata: r.Metadata(), - Type: r.GetStringProperty("Type", "application"), - DropInvalidHeaderFields: checkForDropInvalidHeaders(r), - Internal: isInternal(r), - Listeners: getListeners(r, ctx), - } - loadbalancers = append(loadbalancers, lb) - } - - return loadbalancers -} - -func getListeners(lbr *parser.Resource, ctx parser.FileContext) (listeners []elb.Listener) { - - listenerResources := ctx.GetResourcesByType("AWS::ElasticLoadBalancingV2::Listener") - - for _, r := range listenerResources { - if r.GetStringProperty("LoadBalancerArn").Value() == lbr.ID() { - listener := elb.Listener{ - Metadata: r.Metadata(), - Protocol: r.GetStringProperty("Protocol", "HTTP"), - TLSPolicy: r.GetStringProperty("SslPolicy", ""), - DefaultActions: getDefaultListenerActions(r), - } - - listeners = append(listeners, listener) - } - } - return listeners -} - -func getDefaultListenerActions(r *parser.Resource) (actions []elb.Action) { - defaultActionsProp := r.GetProperty("DefaultActions") - if defaultActionsProp.IsNotList() { - return actions - } - for _, action := range defaultActionsProp.AsList() { - actions = append(actions, elb.Action{ - Metadata: action.Metadata(), - Type: action.GetProperty("Type").AsStringValue(), - }) - } - return actions -} - -func isInternal(r *parser.Resource) types.BoolValue { - schemeProp := r.GetProperty("Scheme") - if schemeProp.IsNotString() { - return r.BoolDefault(false) - } - return types.Bool(schemeProp.EqualTo("internal", parser.IgnoreCase), schemeProp.Metadata()) -} - -func checkForDropInvalidHeaders(r *parser.Resource) types.BoolValue { - attributesProp := r.GetProperty("LoadBalancerAttributes") - if attributesProp.IsNotList() { - return types.BoolDefault(false, r.Metadata()) - } - - for _, attr := range attributesProp.AsList() { - if attr.GetStringProperty("Key").Value() == "routing.http.drop_invalid_header_fields.enabled" { - return attr.GetBoolProperty("Value") - } - } - - return r.BoolDefault(false) -} diff --git a/pkg/iac/adapters/cloudformation/aws/iam/iam.go b/pkg/iac/adapters/cloudformation/aws/iam/iam.go deleted file mode 100644 index d3aab1646534..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/iam/iam.go +++ /dev/null @@ -1,27 +0,0 @@ -package iam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -// Adapt adapts an IAM instance -func Adapt(cfFile parser.FileContext) iam.IAM { - return iam.IAM{ - PasswordPolicy: iam.PasswordPolicy{ - Metadata: iacTypes.NewUnmanagedMetadata(), - ReusePreventionCount: iacTypes.IntDefault(0, iacTypes.NewUnmanagedMetadata()), - RequireLowercase: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - RequireUppercase: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - RequireNumbers: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - RequireSymbols: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - MaxAgeDays: iacTypes.IntDefault(0, iacTypes.NewUnmanagedMetadata()), - MinimumLength: iacTypes.IntDefault(0, iacTypes.NewUnmanagedMetadata()), - }, - Policies: getPolicies(cfFile), - Groups: getGroups(cfFile), - Users: getUsers(cfFile), - Roles: getRoles(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/iam/iam_test.go b/pkg/iac/adapters/cloudformation/aws/iam/iam_test.go deleted file mode 100644 index 7f68e2c6aef0..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/iam/iam_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package iam - -import ( - "testing" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected iam.IAM - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - myIAMPolicy: - Type: 'AWS::IAM::Policy' - Properties: - PolicyName: TestPolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - 'cloudformation:Describe*' - Resource: '*' - Groups: - - !Ref MyGroup - Users: - - !Ref PublishUser - Roles: - - !Ref MyRole - MyGroup: - Type: AWS::IAM::Group - Properties: - GroupName: TestGroup - Policies: - - PolicyName: TestGroupPolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Resource: arn:*:cloudfront::*:distribution/* - Action: - - cloudfront:CreateDistribution - MyUser: - Type: AWS::IAM::User - Properties: - UserName: TestUser - Policies: - - PolicyName: TestUserPolicy - PolicyDocument: - Statement: - - Action: 's3:*' - Effect: Allow - Resource: - - 'arn:aws:s3:::testbucket' - MyRole: - Type: 'AWS::IAM::Role' - Properties: - RoleName: TestRole - Policies: - - PolicyName: TestRolePolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - 'sts:AssumeRole' - AccessKey: - Type: AWS::IAM::AccessKey - Properties: - UserName: !Ref MyUser - Status: Active -`, - expected: iam.IAM{ - Policies: []iam.Policy{ - { - Name: types.StringTest("TestPolicy"), - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithVersion("2012-10-17"). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"cloudformation:Describe*"}). - WithResources([]string{"*"}). - Build(), - ). - Build(), - } - }(), - }, - }, - Users: []iam.User{ - { - Name: types.StringTest("TestUser"), - Policies: []iam.Policy{ - { - Name: types.StringTest("TestUserPolicy"), - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"s3:*"}). - WithResources([]string{"arn:aws:s3:::testbucket"}). - Build(), - ). - Build(), - } - }(), - }, - }, - }, - }, - Groups: []iam.Group{ - { - Name: types.StringTest("TestGroup"), - Policies: []iam.Policy{ - { - Name: types.StringTest("TestGroupPolicy"), - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithVersion("2012-10-17"). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"cloudfront:CreateDistribution"}). - WithResources([]string{"arn:*:cloudfront::*:distribution/*"}). - Build(), - ). - Build(), - } - }(), - }, - }, - }, - }, - Roles: []iam.Role{ - { - Name: types.StringTest("TestRole"), - Policies: []iam.Policy{ - { - Name: types.StringTest("TestRolePolicy"), - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithVersion("2012-10-17"). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"sts:AssumeRole"}). - Build(), - ). - Build(), - } - }(), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - - `, - expected: iam.IAM{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/iam/policy.go b/pkg/iac/adapters/cloudformation/aws/iam/policy.go deleted file mode 100644 index d787dac2539b..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/iam/policy.go +++ /dev/null @@ -1,126 +0,0 @@ -package iam - -import ( - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getPolicies(ctx parser.FileContext) (policies []iam.Policy) { - for _, policyResource := range ctx.GetResourcesByType("AWS::IAM::Policy") { - - policy := iam.Policy{ - Metadata: policyResource.Metadata(), - Name: policyResource.GetStringProperty("PolicyName"), - Document: iam.Document{ - Metadata: policyResource.Metadata(), - Parsed: iamgo.Document{}, - }, - Builtin: iacTypes.Bool(false, policyResource.Metadata()), - } - - if policyProp := policyResource.GetProperty("PolicyDocument"); policyProp.IsNotNil() { - doc, err := iamgo.Parse(policyProp.GetJsonBytes()) - if err != nil { - continue - } - policy.Document.Parsed = *doc - } - - policies = append(policies, policy) - } - return policies -} - -func getRoles(ctx parser.FileContext) (roles []iam.Role) { - for _, roleResource := range ctx.GetResourcesByType("AWS::IAM::Role") { - policyProp := roleResource.GetProperty("Policies") - roleName := roleResource.GetStringProperty("RoleName") - - roles = append(roles, iam.Role{ - Metadata: roleResource.Metadata(), - Name: roleName, - Policies: getPoliciesDocs(policyProp), - }) - } - return roles -} - -func getUsers(ctx parser.FileContext) (users []iam.User) { - for _, userResource := range ctx.GetResourcesByType("AWS::IAM::User") { - policyProp := userResource.GetProperty("Policies") - userName := userResource.GetStringProperty("UserName") - - users = append(users, iam.User{ - Metadata: userResource.Metadata(), - Name: userName, - LastAccess: iacTypes.TimeUnresolvable(userResource.Metadata()), - Policies: getPoliciesDocs(policyProp), - AccessKeys: getAccessKeys(ctx, userName.Value()), - }) - } - return users -} - -func getAccessKeys(ctx parser.FileContext, username string) (accessKeys []iam.AccessKey) { - // TODO: also search for a key by the logical id of the resource - for _, keyResource := range ctx.GetResourcesByType("AWS::IAM::AccessKey") { - keyUsername := keyResource.GetStringProperty("UserName") - if !keyUsername.EqualTo(username) { - continue - } - active := iacTypes.BoolDefault(false, keyResource.Metadata()) - if statusProp := keyResource.GetProperty("Status"); statusProp.IsString() { - active = iacTypes.Bool(statusProp.AsString() == "Active", statusProp.Metadata()) - } - - accessKeys = append(accessKeys, iam.AccessKey{ - Metadata: keyResource.Metadata(), - AccessKeyId: iacTypes.StringUnresolvable(keyResource.Metadata()), - CreationDate: iacTypes.TimeUnresolvable(keyResource.Metadata()), - LastAccess: iacTypes.TimeUnresolvable(keyResource.Metadata()), - Active: active, - }) - } - return accessKeys -} - -func getGroups(ctx parser.FileContext) (groups []iam.Group) { - for _, groupResource := range ctx.GetResourcesByType("AWS::IAM::Group") { - policyProp := groupResource.GetProperty("Policies") - groupName := groupResource.GetStringProperty("GroupName") - - groups = append(groups, iam.Group{ - Metadata: groupResource.Metadata(), - Name: groupName, - Policies: getPoliciesDocs(policyProp), - }) - } - return groups -} - -func getPoliciesDocs(policiesProp *parser.Property) []iam.Policy { - var policies []iam.Policy - - for _, policy := range policiesProp.AsList() { - policyProp := policy.GetProperty("PolicyDocument") - policyName := policy.GetStringProperty("PolicyName") - - doc, err := iamgo.Parse(policyProp.GetJsonBytes()) - if err != nil { - continue - } - - policies = append(policies, iam.Policy{ - Metadata: policyProp.Metadata(), - Name: policyName, - Document: iam.Document{ - Metadata: policyProp.Metadata(), - Parsed: *doc, - }, - Builtin: iacTypes.Bool(false, policyProp.Metadata()), - }) - } - return policies -} diff --git a/pkg/iac/adapters/cloudformation/aws/kinesis/kinesis.go b/pkg/iac/adapters/cloudformation/aws/kinesis/kinesis.go deleted file mode 100644 index c48ad26046bf..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/kinesis/kinesis.go +++ /dev/null @@ -1,13 +0,0 @@ -package kinesis - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/kinesis" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a Kinesis instance -func Adapt(cfFile parser.FileContext) kinesis.Kinesis { - return kinesis.Kinesis{ - Streams: getStreams(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/kinesis/kinesis_test.go b/pkg/iac/adapters/cloudformation/aws/kinesis/kinesis_test.go deleted file mode 100644 index ce38afadf806..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/kinesis/kinesis_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package kinesis - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/kinesis" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected kinesis.Kinesis - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - MyStream: - Type: 'AWS::Kinesis::Stream' - Properties: - StreamEncryption: - EncryptionType: KMS - KeyId: key -`, - expected: kinesis.Kinesis{ - Streams: []kinesis.Stream{ - { - Encryption: kinesis.Encryption{ - Type: types.StringTest("KMS"), - KMSKeyID: types.StringTest("key"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyStream: - Type: 'AWS::Kinesis::Stream' - `, - expected: kinesis.Kinesis{ - Streams: []kinesis.Stream{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/kinesis/stream.go b/pkg/iac/adapters/cloudformation/aws/kinesis/stream.go deleted file mode 100644 index 6c10ec6134c0..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/kinesis/stream.go +++ /dev/null @@ -1,30 +0,0 @@ -package kinesis - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/kinesis" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getStreams(ctx parser.FileContext) (streams []kinesis.Stream) { - - streamResources := ctx.GetResourcesByType("AWS::Kinesis::Stream") - - for _, r := range streamResources { - - stream := kinesis.Stream{ - Metadata: r.Metadata(), - } - - if prop := r.GetProperty("StreamEncryption"); prop.IsNotNil() { - stream.Encryption = kinesis.Encryption{ - Metadata: prop.Metadata(), - Type: prop.GetStringProperty("EncryptionType", "KMS"), - KMSKeyID: prop.GetStringProperty("KeyId"), - } - } - - streams = append(streams, stream) - } - - return streams -} diff --git a/pkg/iac/adapters/cloudformation/aws/lambda/function.go b/pkg/iac/adapters/cloudformation/aws/lambda/function.go deleted file mode 100644 index f91a565d193a..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/lambda/function.go +++ /dev/null @@ -1,48 +0,0 @@ -package lambda - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/lambda" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getFunctions(ctx parser.FileContext) (functions []lambda.Function) { - - functionResources := ctx.GetResourcesByType("AWS::Lambda::Function") - - for _, r := range functionResources { - - function := lambda.Function{ - Metadata: r.Metadata(), - Permissions: getPermissions(r, ctx), - } - - if prop := r.GetProperty("TracingConfig"); prop.IsNotNil() { - function.Tracing = lambda.Tracing{ - Metadata: prop.Metadata(), - Mode: prop.GetStringProperty("Mode"), - } - } - - functions = append(functions, function) - } - - return functions -} - -func getPermissions(funcR *parser.Resource, ctx parser.FileContext) (perms []lambda.Permission) { - - permissionResources := ctx.GetResourcesByType("AWS::Lambda::Permission") - - for _, r := range permissionResources { - if prop := r.GetStringProperty("FunctionName"); prop.EqualTo(funcR.ID()) { - perm := lambda.Permission{ - Metadata: r.Metadata(), - Principal: r.GetStringProperty("Principal"), - SourceARN: r.GetStringProperty("SourceArn"), - } - perms = append(perms, perm) - } - } - - return perms -} diff --git a/pkg/iac/adapters/cloudformation/aws/lambda/lambda.go b/pkg/iac/adapters/cloudformation/aws/lambda/lambda.go deleted file mode 100644 index f89710d47e1e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/lambda/lambda.go +++ /dev/null @@ -1,13 +0,0 @@ -package lambda - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/lambda" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a lambda instance -func Adapt(cfFile parser.FileContext) lambda.Lambda { - return lambda.Lambda{ - Functions: getFunctions(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/lambda/lambda_test.go b/pkg/iac/adapters/cloudformation/aws/lambda/lambda_test.go deleted file mode 100644 index 4262181f89ee..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/lambda/lambda_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package lambda - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/lambda" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected lambda.Lambda - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - lambdaFunction: - Type: AWS::Lambda::Function - Properties: - TracingConfig: - Mode: Active - permission: - Type: AWS::Lambda::Permission - Properties: - FunctionName: !Ref lambdaFunction - Action: lambda:InvokeFunction - Principal: s3.amazonaws.com - SourceArn: arn -`, - expected: lambda.Lambda{ - Functions: []lambda.Function{ - { - Tracing: lambda.Tracing{ - Mode: types.StringTest("Active"), - }, - Permissions: []lambda.Permission{ - { - Principal: types.StringTest("s3.amazonaws.com"), - SourceARN: types.StringTest("arn"), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - lambdaFunction: - Type: AWS::Lambda::Function - permission: - Type: AWS::Lambda::Permission - Properties: - FunctionName: !Ref lambdaFunction - `, - expected: lambda.Lambda{ - Functions: []lambda.Function{ - { - Permissions: []lambda.Permission{{}}, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/mq/broker.go b/pkg/iac/adapters/cloudformation/aws/mq/broker.go deleted file mode 100644 index 9e52023c11da..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/mq/broker.go +++ /dev/null @@ -1,33 +0,0 @@ -package mq - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getBrokers(ctx parser.FileContext) (brokers []mq.Broker) { - for _, r := range ctx.GetResourcesByType("AWS::AmazonMQ::Broker") { - - broker := mq.Broker{ - Metadata: r.Metadata(), - PublicAccess: r.GetBoolProperty("PubliclyAccessible"), - Logging: mq.Logging{ - Metadata: r.Metadata(), - General: types.BoolDefault(false, r.Metadata()), - Audit: types.BoolDefault(false, r.Metadata()), - }, - } - - if prop := r.GetProperty("Logs"); prop.IsNotNil() { - broker.Logging = mq.Logging{ - Metadata: prop.Metadata(), - General: prop.GetBoolProperty("General"), - Audit: prop.GetBoolProperty("Audit"), - } - } - - brokers = append(brokers, broker) - } - return brokers -} diff --git a/pkg/iac/adapters/cloudformation/aws/mq/mq.go b/pkg/iac/adapters/cloudformation/aws/mq/mq.go deleted file mode 100644 index 744e86f69a11..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/mq/mq.go +++ /dev/null @@ -1,13 +0,0 @@ -package mq - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an MQ instance -func Adapt(cfFile parser.FileContext) mq.MQ { - return mq.MQ{ - Brokers: getBrokers(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/mq/mq_test.go b/pkg/iac/adapters/cloudformation/aws/mq/mq_test.go deleted file mode 100644 index b4f1d5048898..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/mq/mq_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package mq - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected mq.MQ - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - BasicBroker: - Type: "AWS::AmazonMQ::Broker" - Properties: - PubliclyAccessible: true - Logs: - Audit: true - General: true -`, - expected: mq.MQ{ - Brokers: []mq.Broker{ - { - PublicAccess: types.BoolTest(true), - Logging: mq.Logging{ - Audit: types.BoolTest(true), - General: types.BoolTest(true), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - BasicBroker: - Type: "AWS::AmazonMQ::Broker" - `, - expected: mq.MQ{ - Brokers: []mq.Broker{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/msk/cluster.go b/pkg/iac/adapters/cloudformation/aws/msk/cluster.go deleted file mode 100644 index c55d5745e22b..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/msk/cluster.go +++ /dev/null @@ -1,80 +0,0 @@ -package msk - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(ctx parser.FileContext) (clusters []msk.Cluster) { - for _, r := range ctx.GetResourcesByType("AWS::MSK::Cluster") { - - cluster := msk.Cluster{ - Metadata: r.Metadata(), - EncryptionInTransit: msk.EncryptionInTransit{ - Metadata: r.Metadata(), - ClientBroker: iacTypes.StringDefault("TLS", r.Metadata()), - }, - EncryptionAtRest: msk.EncryptionAtRest{ - Metadata: r.Metadata(), - KMSKeyARN: iacTypes.StringDefault("", r.Metadata()), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - }, - Logging: msk.Logging{ - Metadata: r.Metadata(), - Broker: msk.BrokerLogging{ - Metadata: r.Metadata(), - S3: msk.S3Logging{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - }, - Cloudwatch: msk.CloudwatchLogging{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - }, - Firehose: msk.FirehoseLogging{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - }, - }, - }, - } - - if encProp := r.GetProperty("EncryptionInfo.EncryptionInTransit"); encProp.IsNotNil() { - cluster.EncryptionInTransit = msk.EncryptionInTransit{ - Metadata: encProp.Metadata(), - ClientBroker: encProp.GetStringProperty("ClientBroker", "TLS"), - } - } - - if encAtRestProp := r.GetProperty("EncryptionInfo.EncryptionAtRest"); encAtRestProp.IsNotNil() { - cluster.EncryptionAtRest = msk.EncryptionAtRest{ - Metadata: encAtRestProp.Metadata(), - KMSKeyARN: encAtRestProp.GetStringProperty("DataVolumeKMSKeyId", ""), - Enabled: iacTypes.BoolDefault(true, encAtRestProp.Metadata()), - } - } - - if loggingProp := r.GetProperty("LoggingInfo"); loggingProp.IsNotNil() { - cluster.Logging.Metadata = loggingProp.Metadata() - if brokerLoggingProp := loggingProp.GetProperty("BrokerLogs"); brokerLoggingProp.IsNotNil() { - cluster.Logging.Broker.Metadata = brokerLoggingProp.Metadata() - if s3Prop := brokerLoggingProp.GetProperty("S3"); s3Prop.IsNotNil() { - cluster.Logging.Broker.S3.Metadata = s3Prop.Metadata() - cluster.Logging.Broker.S3.Enabled = s3Prop.GetBoolProperty("Enabled", false) - } - if cwProp := brokerLoggingProp.GetProperty("CloudWatchLogs"); cwProp.IsNotNil() { - cluster.Logging.Broker.Cloudwatch.Metadata = cwProp.Metadata() - cluster.Logging.Broker.Cloudwatch.Enabled = cwProp.GetBoolProperty("Enabled", false) - } - if fhProp := brokerLoggingProp.GetProperty("Firehose"); fhProp.IsNotNil() { - cluster.Logging.Broker.Firehose.Metadata = fhProp.Metadata() - cluster.Logging.Broker.Firehose.Enabled = fhProp.GetBoolProperty("Enabled", false) - } - } - } - - clusters = append(clusters, cluster) - } - return clusters -} diff --git a/pkg/iac/adapters/cloudformation/aws/msk/msk.go b/pkg/iac/adapters/cloudformation/aws/msk/msk.go deleted file mode 100644 index 76d7964e17a7..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/msk/msk.go +++ /dev/null @@ -1,13 +0,0 @@ -package msk - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an MSK instance -func Adapt(cfFile parser.FileContext) msk.MSK { - return msk.MSK{ - Clusters: getClusters(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/msk/msk_test.go b/pkg/iac/adapters/cloudformation/aws/msk/msk_test.go deleted file mode 100644 index 2cc5a6cd0945..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/msk/msk_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package msk - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected msk.MSK - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - cluster: - Type: AWS::MSK::Cluster - Properties: - EncryptionInfo: - EncryptionInTransit: - ClientBroker: 'PLAINTEXT' - EncryptionAtRest: - DataVolumeKMSKeyId: key - LoggingInfo: - BrokerLogs: - S3: - Enabled: true - CloudWatchLogs: - Enabled: true - Firehose: - Enabled: true -`, - expected: msk.MSK{ - Clusters: []msk.Cluster{ - { - EncryptionInTransit: msk.EncryptionInTransit{ - ClientBroker: types.StringTest("PLAINTEXT"), - }, - EncryptionAtRest: msk.EncryptionAtRest{ - KMSKeyARN: types.StringTest("key"), - Enabled: types.BoolTest(true), - }, - Logging: msk.Logging{ - Broker: msk.BrokerLogging{ - S3: msk.S3Logging{ - Enabled: types.BoolTest(true), - }, - Firehose: msk.FirehoseLogging{ - Enabled: types.BoolTest(true), - }, - Cloudwatch: msk.CloudwatchLogging{ - Enabled: types.BoolTest(true), - }, - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - cluster: - Type: AWS::MSK::Cluster - `, - expected: msk.MSK{ - Clusters: []msk.Cluster{{ - EncryptionInTransit: msk.EncryptionInTransit{ - ClientBroker: types.StringTest("TLS"), - }, - }}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/neptune/cluster.go b/pkg/iac/adapters/cloudformation/aws/neptune/cluster.go deleted file mode 100644 index 33012cbd2236..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/neptune/cluster.go +++ /dev/null @@ -1,34 +0,0 @@ -package neptune - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(ctx parser.FileContext) (clusters []neptune.Cluster) { - for _, r := range ctx.GetResourcesByType("AWS::Neptune::DBCluster") { - - cluster := neptune.Cluster{ - Metadata: r.Metadata(), - Logging: neptune.Logging{ - Metadata: r.Metadata(), - Audit: getAuditLog(r), - }, - StorageEncrypted: r.GetBoolProperty("StorageEncrypted"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - } - clusters = append(clusters, cluster) - } - return clusters -} - -func getAuditLog(r *parser.Resource) types.BoolValue { - if logsProp := r.GetProperty("EnableCloudwatchLogsExports"); logsProp.IsList() { - if logsProp.Contains("audit") { - return types.Bool(true, logsProp.Metadata()) - } - } - - return types.BoolDefault(false, r.Metadata()) -} diff --git a/pkg/iac/adapters/cloudformation/aws/neptune/neptune.go b/pkg/iac/adapters/cloudformation/aws/neptune/neptune.go deleted file mode 100644 index 2d63e6f45f69..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/neptune/neptune.go +++ /dev/null @@ -1,13 +0,0 @@ -package neptune - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a Neptune instance -func Adapt(cfFile parser.FileContext) neptune.Neptune { - return neptune.Neptune{ - Clusters: getClusters(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/neptune/neptune_test.go b/pkg/iac/adapters/cloudformation/aws/neptune/neptune_test.go deleted file mode 100644 index 8e63a481ff2e..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/neptune/neptune_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package neptune - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected neptune.Neptune - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - cluster: - Type: AWS::Neptune::DBCluster - Properties: - StorageEncrypted: true - KmsKeyId: key - EnableCloudwatchLogsExports: - - audit -`, - expected: neptune.Neptune{ - Clusters: []neptune.Cluster{ - { - StorageEncrypted: types.BoolTest(true), - KMSKeyID: types.StringTest("key"), - Logging: neptune.Logging{ - Audit: types.BoolTest(true), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - cluster: - Type: AWS::Neptune::DBCluster - `, - expected: neptune.Neptune{ - Clusters: []neptune.Cluster{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/rds/adapt_test.go b/pkg/iac/adapters/cloudformation/aws/rds/adapt_test.go deleted file mode 100644 index 4875395c8506..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/rds/adapt_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package rds - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected rds.RDS - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - RDSCluster: - Type: 'AWS::RDS::DBCluster' - Properties: - DBClusterIdentifier: my-cluster1 - Engine: aurora-postgresql - StorageEncrypted: true - KmsKeyId: "your-kms-key-id" - PerformanceInsightsEnabled: true - PerformanceInsightsKmsKeyId: "test-kms-key-id" - PublicAccess: true - DeletionProtection: true - BackupRetentionPeriod: 2 - RDSDBInstance1: - Type: 'AWS::RDS::DBInstance' - Properties: - Engine: aurora-mysql - EngineVersion: "5.7.12" - DBInstanceIdentifier: test - DBClusterIdentifier: - Ref: RDSCluster - PubliclyAccessible: 'false' - DBInstanceClass: db.r3.xlarge - StorageEncrypted: true - KmsKeyId: "your-kms-key-id" - EnablePerformanceInsights: true - PerformanceInsightsKMSKeyId: "test-kms-key-id2" - MultiAZ: true - AutoMinorVersionUpgrade: true - DBInstanceArn: "arn:aws:rds:us-east-2:123456789012:db:my-mysql-instance-1" - EnableIAMDatabaseAuthentication: true - EnableCloudwatchLogsExports: - - "error" - - "general" - DBParameterGroupName: "testgroup" - Tags: - - Key: "keyname1" - Value: "value1" - - Key: "keyname2" - Value: "value2" - RDSDBParameterGroup: - Type: 'AWS::RDS::DBParameterGroup' - Properties: - Description: "CloudFormation Sample MySQL Parameter Group" - DBParameterGroupName: "testgroup" - Parameters: - sql_mode: IGNORE_SPACE - DbSecurityByEC2SecurityGroup: - Type: AWS::RDS::DBSecurityGroup - Properties: - GroupDescription: "Ingress for Amazon EC2 security group" -`, - expected: rds.RDS{ - Classic: rds.Classic{ - DBSecurityGroups: []rds.DBSecurityGroup{{}}, - }, - ParameterGroups: []rds.ParameterGroups{ - { - DBParameterGroupName: types.StringTest("testgroup"), - }, - }, - Clusters: []rds.Cluster{ - { - BackupRetentionPeriodDays: types.IntTest(2), - Engine: types.StringTest("aurora-postgresql"), - Encryption: rds.Encryption{ - EncryptStorage: types.BoolTest(true), - KMSKeyID: types.StringTest("your-kms-key-id"), - }, - PerformanceInsights: rds.PerformanceInsights{ - Enabled: types.BoolTest(true), - KMSKeyID: types.StringTest("test-kms-key-id"), - }, - PublicAccess: types.BoolTest(false), - DeletionProtection: types.BoolTest(true), - Instances: []rds.ClusterInstance{ - { - Instance: rds.Instance{ - StorageEncrypted: types.BoolTest(true), - Encryption: rds.Encryption{ - EncryptStorage: types.BoolTest(true), - KMSKeyID: types.StringTest("your-kms-key-id"), - }, - DBInstanceIdentifier: types.StringTest("test"), - PubliclyAccessible: types.BoolTest(false), - PublicAccess: types.BoolTest(false), - BackupRetentionPeriodDays: types.IntTest(1), - Engine: types.StringTest("aurora-mysql"), - EngineVersion: types.StringTest("5.7.12"), - MultiAZ: types.BoolTest(true), - AutoMinorVersionUpgrade: types.BoolTest(true), - DBInstanceArn: types.StringTest("arn:aws:rds:us-east-2:123456789012:db:my-mysql-instance-1"), - IAMAuthEnabled: types.BoolTest(true), - PerformanceInsights: rds.PerformanceInsights{ - Enabled: types.BoolTest(true), - KMSKeyID: types.StringTest("test-kms-key-id2"), - }, - EnabledCloudwatchLogsExports: []types.StringValue{ - types.StringTest("error"), - types.StringTest("general"), - }, - DBParameterGroups: []rds.DBParameterGroupsList{ - { - DBParameterGroupName: types.StringTest("testgroup"), - }, - }, - TagList: []rds.TagList{ - {}, - {}, - }, - }, - ClusterIdentifier: types.StringTest("RDSCluster"), - }, - }, - }, - }, - }, - }, - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - RDSCluster: - Type: 'AWS::RDS::DBCluster' - RDSDBInstance1: - Type: 'AWS::RDS::DBInstance' - RDSDBParameterGroup: - Type: 'AWS::RDS::DBParameterGroup' - DbSecurityByEC2SecurityGroup: - Type: AWS::RDS::DBSecurityGroup -`, - expected: rds.RDS{ - Classic: rds.Classic{ - DBSecurityGroups: []rds.DBSecurityGroup{{}}, - }, - ParameterGroups: []rds.ParameterGroups{{}}, - Clusters: []rds.Cluster{{ - Engine: types.StringTest("aurora"), - BackupRetentionPeriodDays: types.IntTest(1), - }}, - Instances: []rds.Instance{{ - BackupRetentionPeriodDays: types.IntTest(1), - PublicAccess: types.BoolTest(true), - DBParameterGroups: []rds.DBParameterGroupsList{{}}, - }}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } - -} diff --git a/pkg/iac/adapters/cloudformation/aws/rds/cluster.go b/pkg/iac/adapters/cloudformation/aws/rds/cluster.go deleted file mode 100644 index 87bbc7ae64ba..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/rds/cluster.go +++ /dev/null @@ -1,48 +0,0 @@ -package rds - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClusters(ctx parser.FileContext) (clusters map[string]rds.Cluster) { - clusters = make(map[string]rds.Cluster) - for _, clusterResource := range ctx.GetResourcesByType("AWS::RDS::DBCluster") { - clusters[clusterResource.ID()] = rds.Cluster{ - Metadata: clusterResource.Metadata(), - BackupRetentionPeriodDays: clusterResource.GetIntProperty("BackupRetentionPeriod", 1), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: clusterResource.Metadata(), - Enabled: clusterResource.GetBoolProperty("PerformanceInsightsEnabled"), - KMSKeyID: clusterResource.GetStringProperty("PerformanceInsightsKmsKeyId"), - }, - Encryption: rds.Encryption{ - Metadata: clusterResource.Metadata(), - EncryptStorage: clusterResource.GetBoolProperty("StorageEncrypted"), - KMSKeyID: clusterResource.GetStringProperty("KmsKeyId"), - }, - PublicAccess: iacTypes.BoolDefault(false, clusterResource.Metadata()), - Engine: clusterResource.GetStringProperty("Engine", rds.EngineAurora), - LatestRestorableTime: iacTypes.TimeUnresolvable(clusterResource.Metadata()), - DeletionProtection: clusterResource.GetBoolProperty("DeletionProtection"), - } - } - return clusters -} - -func getClassic(ctx parser.FileContext) rds.Classic { - return rds.Classic{ - DBSecurityGroups: getClassicSecurityGroups(ctx), - } -} - -func getClassicSecurityGroups(ctx parser.FileContext) (groups []rds.DBSecurityGroup) { - for _, dbsgResource := range ctx.GetResourcesByType("AWS::RDS::DBSecurityGroup") { - group := rds.DBSecurityGroup{ - Metadata: dbsgResource.Metadata(), - } - groups = append(groups, group) - } - return groups -} diff --git a/pkg/iac/adapters/cloudformation/aws/rds/instance.go b/pkg/iac/adapters/cloudformation/aws/rds/instance.go deleted file mode 100644 index 256eada02aac..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/rds/instance.go +++ /dev/null @@ -1,133 +0,0 @@ -package rds - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getClustersAndInstances(ctx parser.FileContext) ([]rds.Cluster, []rds.Instance) { - - clusterMap := getClusters(ctx) - - var orphans []rds.Instance - - for _, r := range ctx.GetResourcesByType("AWS::RDS::DBInstance") { - - instance := rds.Instance{ - Metadata: r.Metadata(), - BackupRetentionPeriodDays: r.GetIntProperty("BackupRetentionPeriod", 1), - ReplicationSourceARN: r.GetStringProperty("SourceDBInstanceIdentifier"), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: r.Metadata(), - Enabled: r.GetBoolProperty("EnablePerformanceInsights"), - KMSKeyID: r.GetStringProperty("PerformanceInsightsKMSKeyId"), - }, - Encryption: rds.Encryption{ - Metadata: r.Metadata(), - EncryptStorage: r.GetBoolProperty("StorageEncrypted"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - }, - PublicAccess: r.GetBoolProperty("PubliclyAccessible", true), - Engine: r.GetStringProperty("Engine"), - IAMAuthEnabled: r.GetBoolProperty("EnableIAMDatabaseAuthentication"), - DeletionProtection: r.GetBoolProperty("DeletionProtection", false), - DBInstanceArn: r.GetStringProperty("DBInstanceArn"), - StorageEncrypted: r.GetBoolProperty("StorageEncrypted", false), - DBInstanceIdentifier: r.GetStringProperty("DBInstanceIdentifier"), - DBParameterGroups: getDBParameterGroups(ctx, r), - TagList: getTagList(r), - EnabledCloudwatchLogsExports: getEnabledCloudwatchLogsExports(r), - EngineVersion: r.GetStringProperty("EngineVersion"), - AutoMinorVersionUpgrade: r.GetBoolProperty("AutoMinorVersionUpgrade"), - MultiAZ: r.GetBoolProperty("MultiAZ"), - PubliclyAccessible: r.GetBoolProperty("PubliclyAccessible"), - LatestRestorableTime: types.TimeUnresolvable(r.Metadata()), - ReadReplicaDBInstanceIdentifiers: getReadReplicaDBInstanceIdentifiers(r), - } - - if clusterID := r.GetProperty("DBClusterIdentifier"); clusterID.IsString() { - if cluster, exist := clusterMap[clusterID.AsString()]; exist { - cluster.Instances = append(cluster.Instances, rds.ClusterInstance{ - Instance: instance, - ClusterIdentifier: clusterID.AsStringValue(), - }) - clusterMap[clusterID.AsString()] = cluster - } - } else { - orphans = append(orphans, instance) - } - } - - clusters := make([]rds.Cluster, 0, len(clusterMap)) - - for _, cluster := range clusterMap { - clusters = append(clusters, cluster) - } - - return clusters, orphans -} - -func getDBParameterGroups(ctx parser.FileContext, r *parser.Resource) (dbParameterGroup []rds.DBParameterGroupsList) { - - var parameterGroupList []rds.DBParameterGroupsList - - dbParameterGroupName := r.GetStringProperty("DBParameterGroupName") - - for _, r := range ctx.GetResourcesByType("AWS::RDS::DBParameterGroup") { - name := r.GetStringProperty("DBParameterGroupName") - // TODO: find by resource logical id - if !dbParameterGroupName.EqualTo(name.Value()) { - continue - } - dbpmgl := rds.DBParameterGroupsList{ - Metadata: r.Metadata(), - DBParameterGroupName: name, - KMSKeyID: types.StringUnresolvable(r.Metadata()), - } - parameterGroupList = append(dbParameterGroup, dbpmgl) - } - - return parameterGroupList -} - -func getEnabledCloudwatchLogsExports(r *parser.Resource) (enabledcloudwatchlogexportslist []types.StringValue) { - enabledCloudwatchLogExportList := r.GetProperty("EnableCloudwatchLogsExports") - - if enabledCloudwatchLogExportList.IsNil() || enabledCloudwatchLogExportList.IsNotList() { - return enabledcloudwatchlogexportslist - } - - for _, ecle := range enabledCloudwatchLogExportList.AsList() { - enabledcloudwatchlogexportslist = append(enabledcloudwatchlogexportslist, ecle.AsStringValue()) - } - return enabledcloudwatchlogexportslist -} - -func getTagList(r *parser.Resource) (taglist []rds.TagList) { - tagLists := r.GetProperty("Tags") - - if tagLists.IsNil() || tagLists.IsNotList() { - return taglist - } - - for _, tl := range tagLists.AsList() { - taglist = append(taglist, rds.TagList{ - Metadata: tl.Metadata(), - }) - } - return taglist -} - -func getReadReplicaDBInstanceIdentifiers(r *parser.Resource) (readreplicadbidentifier []types.StringValue) { - readReplicaDBIdentifier := r.GetProperty("SourceDBInstanceIdentifier") - - if readReplicaDBIdentifier.IsNil() || readReplicaDBIdentifier.IsNotList() { - return readreplicadbidentifier - } - - for _, rr := range readReplicaDBIdentifier.AsList() { - readreplicadbidentifier = append(readreplicadbidentifier, rr.AsStringValue()) - } - return readreplicadbidentifier -} diff --git a/pkg/iac/adapters/cloudformation/aws/rds/parameter_groups.go b/pkg/iac/adapters/cloudformation/aws/rds/parameter_groups.go deleted file mode 100644 index f47c2f70a706..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/rds/parameter_groups.go +++ /dev/null @@ -1,44 +0,0 @@ -package rds - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getParameterGroups(ctx parser.FileContext) (parametergroups []rds.ParameterGroups) { - - for _, r := range ctx.GetResourcesByType("AWS::RDS::DBParameterGroup") { - - paramgroup := rds.ParameterGroups{ - Metadata: r.Metadata(), - DBParameterGroupName: r.GetStringProperty("DBParameterGroupName"), - DBParameterGroupFamily: r.GetStringProperty("DBParameterGroupFamily"), - Parameters: getParameters(r), - } - - parametergroups = append(parametergroups, paramgroup) - } - - return parametergroups -} - -func getParameters(r *parser.Resource) (parameters []rds.Parameters) { - - dBParam := r.GetProperty("Parameters") - - // TODO: parameters is JSON - // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbparametergroup.html#cfn-rds-dbparametergroup-parameters - if dBParam.IsNil() || dBParam.IsNotList() { - return parameters - } - - for _, dbp := range dBParam.AsList() { - parameters = append(parameters, rds.Parameters{ - Metadata: dbp.Metadata(), - ParameterName: types.StringDefault("", dbp.Metadata()), - ParameterValue: types.StringDefault("", dbp.Metadata()), - }) - } - return parameters -} diff --git a/pkg/iac/adapters/cloudformation/aws/rds/rds.go b/pkg/iac/adapters/cloudformation/aws/rds/rds.go deleted file mode 100644 index 25118c20bbba..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/rds/rds.go +++ /dev/null @@ -1,18 +0,0 @@ -package rds - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an RDS instance -func Adapt(cfFile parser.FileContext) rds.RDS { - clusters, orphans := getClustersAndInstances(cfFile) - return rds.RDS{ - Instances: orphans, - Clusters: clusters, - Classic: getClassic(cfFile), - ParameterGroups: getParameterGroups(cfFile), - Snapshots: nil, - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/redshift/cluster.go b/pkg/iac/adapters/cloudformation/aws/redshift/cluster.go deleted file mode 100644 index c8acf8997af0..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/redshift/cluster.go +++ /dev/null @@ -1,49 +0,0 @@ -package redshift - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getClusters(ctx parser.FileContext) (clusters []redshift.Cluster) { - for _, r := range ctx.GetResourcesByType("AWS::Redshift::Cluster") { - - cluster := redshift.Cluster{ - Metadata: r.Metadata(), - ClusterIdentifier: r.GetStringProperty("ClusterIdentifier"), - AllowVersionUpgrade: r.GetBoolProperty("AllowVersionUpgrade", true), - NodeType: r.GetStringProperty("NodeType"), - NumberOfNodes: r.GetIntProperty("NumberOfNodes", 1), - PubliclyAccessible: r.GetBoolProperty("PubliclyAccessible"), - MasterUsername: r.GetStringProperty("MasterUsername"), - AutomatedSnapshotRetentionPeriod: r.GetIntProperty("AutomatedSnapshotRetentionPeriod", 1), - Encryption: redshift.Encryption{ - Metadata: r.Metadata(), - Enabled: r.GetBoolProperty("Encrypted"), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - }, - EndPoint: redshift.EndPoint{ - Metadata: r.Metadata(), - Port: r.GetIntProperty("Endpoint.Port"), - }, - SubnetGroupName: r.GetStringProperty("ClusterSubnetGroupName", ""), - } - - clusters = append(clusters, cluster) - } - return clusters -} - -func getParameters(ctx parser.FileContext) (parameter []redshift.ClusterParameter) { - var parameters []redshift.ClusterParameter - for _, r := range ctx.GetResourcesByType("AWS::Redshift::ClusterParameterGroup") { - for _, par := range r.GetProperty("Parameters").AsList() { - parameters = append(parameters, redshift.ClusterParameter{ - Metadata: par.Metadata(), - ParameterName: par.GetStringProperty("ParameterName"), - ParameterValue: par.GetStringProperty("ParameterValue"), - }) - } - } - return parameters -} diff --git a/pkg/iac/adapters/cloudformation/aws/redshift/redshift.go b/pkg/iac/adapters/cloudformation/aws/redshift/redshift.go deleted file mode 100644 index 899b6c50e147..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/redshift/redshift.go +++ /dev/null @@ -1,16 +0,0 @@ -package redshift - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a RedShift instance -func Adapt(cfFile parser.FileContext) redshift.Redshift { - return redshift.Redshift{ - Clusters: getClusters(cfFile), - SecurityGroups: getSecurityGroups(cfFile), - ClusterParameters: getParameters(cfFile), - ReservedNodes: nil, - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/redshift/redshift_test.go b/pkg/iac/adapters/cloudformation/aws/redshift/redshift_test.go deleted file mode 100644 index a14117a21961..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/redshift/redshift_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package redshift - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected redshift.Redshift - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - myCluster: - Type: "AWS::Redshift::Cluster" - Properties: - DBName: "mydb" - ClusterIdentifier: myexamplecluster - AllowVersionUpgrade: false - MasterUsername: "master" - NodeType: "ds2.xlarge" - NumberOfNodes: 2 - PubliclyAccessible: true - AutomatedSnapshotRetentionPeriod: 2 - Encrypted: true - KmsKeyId: key - Endpoint: - Port: 2000 - ClusterSubnetGroupName: test - myClusterParameterGroup: - Type: "AWS::Redshift::ClusterParameterGroup" - Properties: - Parameters: - - - ParameterName: "enable_user_activity_logging" - ParameterValue: "true" - mySecGroup: - Type: AWS::Redshift::ClusterSecurityGroup - Properties: - Description: test - `, - expected: redshift.Redshift{ - Clusters: []redshift.Cluster{ - { - ClusterIdentifier: types.StringTest("myexamplecluster"), - AllowVersionUpgrade: types.BoolTest(false), - MasterUsername: types.StringTest("master"), - NodeType: types.StringTest("ds2.xlarge"), - NumberOfNodes: types.IntTest(2), - PubliclyAccessible: types.BoolTest(true), - AutomatedSnapshotRetentionPeriod: types.IntTest(2), - Encryption: redshift.Encryption{ - Enabled: types.BoolTest(true), - KMSKeyID: types.StringTest("key"), - }, - EndPoint: redshift.EndPoint{ - Port: types.IntTest(2000), - }, - SubnetGroupName: types.StringTest("test"), - }, - }, - ClusterParameters: []redshift.ClusterParameter{ - { - ParameterName: types.StringTest("enable_user_activity_logging"), - ParameterValue: types.StringTest("true"), - }, - }, - SecurityGroups: []redshift.SecurityGroup{ - { - Description: types.StringTest("test"), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - myCluster: - Type: "AWS::Redshift::Cluster" - mySecGroup: - Type: AWS::Redshift::ClusterSecurityGroup - myClusterParameterGroup: - Type: "AWS::Redshift::ClusterParameterGroup" -`, - expected: redshift.Redshift{ - Clusters: []redshift.Cluster{ - { - AllowVersionUpgrade: types.BoolTest(true), - AutomatedSnapshotRetentionPeriod: types.IntTest(1), - NumberOfNodes: types.IntTest(1), - }, - }, - SecurityGroups: []redshift.SecurityGroup{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/redshift/security_group.go b/pkg/iac/adapters/cloudformation/aws/redshift/security_group.go deleted file mode 100644 index 0223306783ae..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/redshift/security_group.go +++ /dev/null @@ -1,17 +0,0 @@ -package redshift - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getSecurityGroups(ctx parser.FileContext) (groups []redshift.SecurityGroup) { - for _, groupResource := range ctx.GetResourcesByType("AWS::Redshift::ClusterSecurityGroup") { - group := redshift.SecurityGroup{ - Metadata: groupResource.Metadata(), - Description: groupResource.GetProperty("Description").AsStringValue(), - } - groups = append(groups, group) - } - return groups -} diff --git a/pkg/iac/adapters/cloudformation/aws/s3/bucket.go b/pkg/iac/adapters/cloudformation/aws/s3/bucket.go deleted file mode 100644 index 957278a93a29..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/s3/bucket.go +++ /dev/null @@ -1,193 +0,0 @@ -package s3 - -import ( - "cmp" - "regexp" - "slices" - "strings" - - s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -var aclConvertRegex = regexp.MustCompile(`[A-Z][^A-Z]*`) - -func getBuckets(cfFile parser.FileContext) []s3.Bucket { - var buckets []s3.Bucket - bucketResources := cfFile.GetResourcesByType("AWS::S3::Bucket") - - for _, r := range bucketResources { - s3b := s3.Bucket{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("BucketName"), - PublicAccessBlock: getPublicAccessBlock(r), - Encryption: getEncryption(r, cfFile), - Versioning: s3.Versioning{ - Metadata: r.Metadata(), - Enabled: hasVersioning(r), - MFADelete: iacTypes.BoolUnresolvable(r.Metadata()), - }, - Logging: getLogging(r), - ACL: convertAclValue(r.GetStringProperty("AccessControl", "private")), - LifecycleConfiguration: getLifecycle(r), - AccelerateConfigurationStatus: r.GetStringProperty("AccelerateConfiguration.AccelerationStatus"), - Website: getWebsite(r), - BucketLocation: iacTypes.String("", r.Metadata()), - Objects: nil, - BucketPolicies: getBucketPolicies(cfFile, r), - } - - buckets = append(buckets, s3b) - } - - slices.SortFunc(buckets, func(a, b s3.Bucket) int { - return cmp.Compare(a.Name.Value(), b.Name.Value()) - }) - - return buckets -} - -func getPublicAccessBlock(r *parser.Resource) *s3.PublicAccessBlock { - block := r.GetProperty("PublicAccessBlockConfiguration") - if block.IsNil() { - return nil - } - - return &s3.PublicAccessBlock{ - Metadata: block.Metadata(), - BlockPublicACLs: block.GetBoolProperty("BlockPublicAcls"), - BlockPublicPolicy: block.GetBoolProperty("BlockPublicPolicy"), - IgnorePublicACLs: block.GetBoolProperty("IgnorePublicAcls"), - RestrictPublicBuckets: block.GetBoolProperty("RestrictPublicBuckets"), - } -} - -func convertAclValue(aclValue iacTypes.StringValue) iacTypes.StringValue { - matches := aclConvertRegex.FindAllString(aclValue.Value(), -1) - - return iacTypes.String(strings.ToLower(strings.Join(matches, "-")), aclValue.GetMetadata()) -} - -func getLogging(r *parser.Resource) s3.Logging { - logging := s3.Logging{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - TargetBucket: iacTypes.StringDefault("", r.Metadata()), - } - - if config := r.GetProperty("LoggingConfiguration"); config.IsNotNil() { - logging.TargetBucket = config.GetStringProperty("DestinationBucketName") - if logging.TargetBucket.IsNotEmpty() || !logging.TargetBucket.GetMetadata().IsResolvable() { - logging.Enabled = iacTypes.Bool(true, config.Metadata()) - } - } - return logging -} - -func hasVersioning(r *parser.Resource) iacTypes.BoolValue { - versioningProp := r.GetProperty("VersioningConfiguration.Status") - - if versioningProp.IsNil() { - return iacTypes.BoolDefault(false, r.Metadata()) - } - - versioningEnabled := false - if versioningProp.EqualTo("Enabled") { - versioningEnabled = true - - } - return iacTypes.Bool(versioningEnabled, versioningProp.Metadata()) -} - -func getEncryption(r *parser.Resource, _ parser.FileContext) s3.Encryption { - encryption := s3.Encryption{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - Algorithm: iacTypes.StringDefault("", r.Metadata()), - KMSKeyId: iacTypes.StringDefault("", r.Metadata()), - } - - if encryptProps := r.GetProperty("BucketEncryption.ServerSideEncryptionConfiguration"); encryptProps.IsNotNil() { - for _, rule := range encryptProps.AsList() { - algo := rule.GetProperty("ServerSideEncryptionByDefault.SSEAlgorithm") - if algo.IsString() { - algoVal := algo.AsString() - isValidAlgo := slices.Contains(s3types.ServerSideEncryption("").Values(), s3types.ServerSideEncryption(algoVal)) - encryption.Enabled = iacTypes.Bool(isValidAlgo, algo.Metadata()) - encryption.Algorithm = algo.AsStringValue() - } - - kmsKeyProp := rule.GetProperty("ServerSideEncryptionByDefault.KMSMasterKeyID") - if !kmsKeyProp.IsEmpty() && kmsKeyProp.IsString() { - encryption.KMSKeyId = kmsKeyProp.AsStringValue() - } - } - } - - return encryption -} - -func getLifecycle(resource *parser.Resource) []s3.Rules { - RuleProp := resource.GetProperty("LifecycleConfiguration.Rules") - - var rule []s3.Rules - - if RuleProp.IsNil() || RuleProp.IsNotList() { - return rule - } - - for _, r := range RuleProp.AsList() { - rule = append(rule, s3.Rules{ - Metadata: r.Metadata(), - Status: r.GetStringProperty("Status"), - }) - } - return rule -} - -func getWebsite(r *parser.Resource) *s3.Website { - if block := r.GetProperty("WebsiteConfiguration"); block.IsNil() { - return nil - } else { - return &s3.Website{ - Metadata: block.Metadata(), - } - } -} - -func getBucketPolicies(fctx parser.FileContext, r *parser.Resource) []iam.Policy { - - var policies []iam.Policy - for _, bucketPolicy := range fctx.GetResourcesByType("AWS::S3::BucketPolicy") { - bucket := bucketPolicy.GetStringProperty("Bucket") - if bucket.NotEqualTo(r.GetStringProperty("BucketName").Value()) && bucket.NotEqualTo(r.ID()) { - continue - } - - doc := bucketPolicy.GetProperty("PolicyDocument") - if doc.IsNil() { - continue - } - - parsed, err := iamgo.Parse(doc.GetJsonBytes()) - if err != nil { - continue - } - policies = append(policies, iam.Policy{ - Metadata: doc.Metadata(), - Name: iacTypes.StringDefault("", doc.Metadata()), - Document: iam.Document{ - Metadata: doc.Metadata(), - Parsed: *parsed, - }, - Builtin: iacTypes.Bool(false, doc.Metadata()), - }) - } - - return policies -} diff --git a/pkg/iac/adapters/cloudformation/aws/s3/s3.go b/pkg/iac/adapters/cloudformation/aws/s3/s3.go deleted file mode 100644 index 988b51adc378..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/s3/s3.go +++ /dev/null @@ -1,13 +0,0 @@ -package s3 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an S3 instance -func Adapt(cfFile parser.FileContext) s3.S3 { - return s3.S3{ - Buckets: getBuckets(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go b/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go deleted file mode 100644 index 87ca88a97432..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/s3/s3_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package s3 - -import ( - "testing" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected s3.S3 - }{ - { - name: "complete s3 bucket", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Key: - Type: "AWS::KMS::Key" - LoggingBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: logging-bucket - Bucket: - Type: AWS::S3::Bucket - Properties: - BucketName: test-bucket - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - KMSMasterKeyID: - Fn::GetAtt: - - Key - - Arn - SSEAlgorithm: aws:kms - AccessControl: AwsExecRead - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - LoggingConfiguration: - DestinationBucketName: !Ref LoggingBucket - LogFilePrefix: testing-logs - LifecycleConfiguration: - Rules: - - Id: GlacierRule - Prefix: glacier - Status: Enabled - ExpirationInDays: 365 - AccelerateConfiguration: - AccelerationStatus: Enabled - VersioningConfiguration: - Status: Enabled - WebsiteConfiguration: - IndexDocument: index.html - SampleBucketPolicy: - Type: AWS::S3::BucketPolicy - Properties: - Bucket: !Ref Bucket - PolicyDocument: - Version: 2012-10-17 - Statement: - - Action: - - 's3:GetObject' - Effect: Allow - Resource: !Join - - 'arn:aws:s3:::testbucket/*' - Principal: '*' -`, - expected: s3.S3{ - Buckets: []s3.Bucket{ - { - Name: types.StringTest("logging-bucket"), - }, - { - Name: types.StringTest("test-bucket"), - Encryption: s3.Encryption{ - Enabled: types.BoolTest(true), - Algorithm: types.StringTest("aws:kms"), - KMSKeyId: types.StringTest("Key"), - }, - ACL: types.StringTest("aws-exec-read"), - PublicAccessBlock: &s3.PublicAccessBlock{ - BlockPublicACLs: types.BoolTest(true), - BlockPublicPolicy: types.BoolTest(true), - IgnorePublicACLs: types.BoolTest(true), - RestrictPublicBuckets: types.BoolTest(true), - }, - Logging: s3.Logging{ - TargetBucket: types.StringTest("LoggingBucket"), - Enabled: types.BoolTest(true), - }, - LifecycleConfiguration: []s3.Rules{ - { - Status: types.StringTest("Enabled"), - }, - }, - AccelerateConfigurationStatus: types.StringTest("Enabled"), - Versioning: s3.Versioning{ - Enabled: types.BoolTest(true), - }, - Website: &s3.Website{}, - BucketPolicies: []iam.Policy{ - { - Document: iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithStatement( - iamgo.NewStatementBuilder(). - WithActions([]string{"s3:GetObject"}). - WithAllPrincipals(true). - WithEffect("Allow"). - WithResources([]string{"arn:aws:s3:::testbucket/*"}). - Build(), - ). - WithVersion("2012-10-17T00:00:00Z"). - Build(), - }, - }, - }, - }, - }, - }, - }, - { - name: "empty s3 bucket", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Bucket: - Type: AWS::S3::Bucket - Properties: - BucketName: test-bucket`, - expected: s3.S3{ - Buckets: []s3.Bucket{ - { - Name: types.StringTest("test-bucket"), - Encryption: s3.Encryption{ - Enabled: types.BoolDefault(false, types.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "incorrect SSE algorithm", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - Bucket: - Type: AWS::S3::Bucket - Properties: - BucketName: test-bucket - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - KMSMasterKeyID: alias/my-key - SSEAlgorithm: aes256 -`, - expected: s3.S3{ - Buckets: []s3.Bucket{ - { - Name: types.StringTest("test-bucket"), - Encryption: s3.Encryption{ - Enabled: types.BoolDefault(false, types.NewTestMetadata()), - KMSKeyId: types.StringTest("alias/my-key"), - Algorithm: types.StringTest("aes256"), - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } - -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/api.go b/pkg/iac/adapters/cloudformation/aws/sam/api.go deleted file mode 100644 index 4d4f04e6e83a..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/api.go +++ /dev/null @@ -1,96 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getApis(cfFile parser.FileContext) (apis []sam.API) { - - apiResources := cfFile.GetResourcesByType("AWS::Serverless::Api") - for _, r := range apiResources { - api := sam.API{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("Name", ""), - TracingEnabled: r.GetBoolProperty("TracingEnabled"), - DomainConfiguration: getDomainConfiguration(r), - AccessLogging: getAccessLogging(r), - RESTMethodSettings: getRestMethodSettings(r), - } - - apis = append(apis, api) - } - - return apis -} - -func getRestMethodSettings(r *parser.Resource) sam.RESTMethodSettings { - - settings := sam.RESTMethodSettings{ - Metadata: r.Metadata(), - CacheDataEncrypted: iacTypes.BoolDefault(false, r.Metadata()), - LoggingEnabled: iacTypes.BoolDefault(false, r.Metadata()), - DataTraceEnabled: iacTypes.BoolDefault(false, r.Metadata()), - MetricsEnabled: iacTypes.BoolDefault(false, r.Metadata()), - } - - // TODO: MethodSettings is list - // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-methodsettings - settingsProp := r.GetProperty("MethodSettings") - if settingsProp.IsNotNil() { - - settings = sam.RESTMethodSettings{ - Metadata: settingsProp.Metadata(), - CacheDataEncrypted: settingsProp.GetBoolProperty("CacheDataEncrypted"), - LoggingEnabled: iacTypes.BoolDefault(false, settingsProp.Metadata()), - DataTraceEnabled: settingsProp.GetBoolProperty("DataTraceEnabled"), - MetricsEnabled: settingsProp.GetBoolProperty("MetricsEnabled"), - } - - if loggingLevel := settingsProp.GetProperty("LoggingLevel"); loggingLevel.IsNotNil() { - if loggingLevel.EqualTo("OFF", parser.IgnoreCase) { - settings.LoggingEnabled = iacTypes.Bool(false, loggingLevel.Metadata()) - } else { - settings.LoggingEnabled = iacTypes.Bool(true, loggingLevel.Metadata()) - } - } - } - - return settings -} - -func getAccessLogging(r *parser.Resource) sam.AccessLogging { - - logging := sam.AccessLogging{ - Metadata: r.Metadata(), - CloudwatchLogGroupARN: iacTypes.StringDefault("", r.Metadata()), - } - - if access := r.GetProperty("AccessLogSetting"); access.IsNotNil() { - logging = sam.AccessLogging{ - Metadata: access.Metadata(), - CloudwatchLogGroupARN: access.GetStringProperty("DestinationArn", ""), - } - } - - return logging -} - -func getDomainConfiguration(r *parser.Resource) sam.DomainConfiguration { - - domainConfig := sam.DomainConfiguration{ - Metadata: r.Metadata(), - } - - if domain := r.GetProperty("Domain"); domain.IsNotNil() { - domainConfig = sam.DomainConfiguration{ - Metadata: domain.Metadata(), - Name: domain.GetStringProperty("DomainName"), - SecurityPolicy: domain.GetStringProperty("SecurityPolicy"), - } - } - - return domainConfig - -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/function.go b/pkg/iac/adapters/cloudformation/aws/sam/function.go deleted file mode 100644 index 2bfc488e1a18..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/function.go +++ /dev/null @@ -1,58 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getFunctions(cfFile parser.FileContext) (functions []sam.Function) { - - functionResources := cfFile.GetResourcesByType("AWS::Serverless::Function") - for _, r := range functionResources { - function := sam.Function{ - Metadata: r.Metadata(), - FunctionName: r.GetStringProperty("FunctionName"), - Tracing: r.GetStringProperty("Tracing"), - ManagedPolicies: nil, - Policies: nil, - } - - setFunctionPolicies(r, &function) - functions = append(functions, function) - } - - return functions -} - -func setFunctionPolicies(r *parser.Resource, function *sam.Function) { - policies := r.GetProperty("Policies") - if policies.IsNotNil() { - if policies.IsString() { - function.ManagedPolicies = append(function.ManagedPolicies, policies.AsStringValue()) - } else if policies.IsList() { - for _, property := range policies.AsList() { - if property.IsMap() { - parsed, err := iamgo.Parse(property.GetJsonBytes(true)) - if err != nil { - continue - } - policy := iam.Policy{ - Metadata: property.Metadata(), - Name: iacTypes.StringDefault("", property.Metadata()), - Document: iam.Document{ - Metadata: property.Metadata(), - Parsed: *parsed, - }, - Builtin: iacTypes.Bool(false, property.Metadata()), - } - function.Policies = append(function.Policies, policy) - } else if property.IsString() { - function.ManagedPolicies = append(function.ManagedPolicies, property.AsStringValue()) - } - } - } - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/http_api.go b/pkg/iac/adapters/cloudformation/aws/sam/http_api.go deleted file mode 100644 index 02f9ba6c5ef9..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/http_api.go +++ /dev/null @@ -1,65 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getHttpApis(cfFile parser.FileContext) (apis []sam.HttpAPI) { - - apiResources := cfFile.GetResourcesByType("AWS::Serverless::HttpApi") - for _, r := range apiResources { - api := sam.HttpAPI{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("Name", ""), - DomainConfiguration: getDomainConfiguration(r), - AccessLogging: getAccessLoggingV2(r), - DefaultRouteSettings: getRouteSettings(r), - } - - apis = append(apis, api) - } - - return apis -} - -func getAccessLoggingV2(r *parser.Resource) sam.AccessLogging { - - logging := sam.AccessLogging{ - Metadata: r.Metadata(), - CloudwatchLogGroupARN: types.StringDefault("", r.Metadata()), - } - - if access := r.GetProperty("AccessLogSettings"); access.IsNotNil() { - logging = sam.AccessLogging{ - Metadata: access.Metadata(), - CloudwatchLogGroupARN: access.GetStringProperty("DestinationArn", ""), - } - } - - return logging -} - -func getRouteSettings(r *parser.Resource) sam.RouteSettings { - - routeSettings := sam.RouteSettings{ - Metadata: r.Metadata(), - LoggingEnabled: types.BoolDefault(false, r.Metadata()), - DataTraceEnabled: types.BoolDefault(false, r.Metadata()), - DetailedMetricsEnabled: types.BoolDefault(false, r.Metadata()), - } - - if route := r.GetProperty("DefaultRouteSettings"); route.IsNotNil() { - routeSettings = sam.RouteSettings{ - Metadata: route.Metadata(), - // TODO: LoggingLevel is string - LoggingEnabled: route.GetBoolProperty("LoggingLevel"), - DataTraceEnabled: route.GetBoolProperty("DataTraceEnabled"), - DetailedMetricsEnabled: route.GetBoolProperty("DetailedMetricsEnabled"), - } - } - - return routeSettings - -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/sam.go b/pkg/iac/adapters/cloudformation/aws/sam/sam.go deleted file mode 100644 index 0f08854aa697..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/sam.go +++ /dev/null @@ -1,17 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an SAM instance -func Adapt(cfFile parser.FileContext) sam.SAM { - return sam.SAM{ - APIs: getApis(cfFile), - HttpAPIs: getHttpApis(cfFile), - Functions: getFunctions(cfFile), - StateMachines: getStateMachines(cfFile), - SimpleTables: getSimpleTables(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/sam_test.go b/pkg/iac/adapters/cloudformation/aws/sam/sam_test.go deleted file mode 100644 index 9bd8ab6bd641..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/sam_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package sam - -import ( - "testing" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected sam.SAM - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - ApiGatewayApi: - Type: AWS::Serverless::Api - Properties: - StageName: prod - Name: test - TracingEnabled: true - Domain: - DomainName: domain - SecurityPolicy: "TLS_1_2" - MethodSettings: - - DataTraceEnabled: true - CacheDataEncrypted: true - MetricsEnabled: true - LoggingLevel: INFO - AccessLogSetting: - DestinationArn: 'arn:aws:logs:us-east-1:123456789:log-group:my-log-group' - HttpApi: - Type: AWS::Serverless::HttpApi - Properties: - Name: test - Domain: - DomainName: test - SecurityPolicy: "TLS_1_2" - AccessLogSettings: - DestinationArn: 'arn:aws:logs:us-east-1:123456789:log-group:my-log-group' - DefaultRouteSettings: - LoggingLevel: INFO - DataTraceEnabled: true - DetailedMetricsEnabled: true - myFunction: - Type: AWS::Serverless::Function - Properties: - FunctionName: test - Tracing: Active - Policies: - - AWSLambdaExecute - - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - s3:GetObject - Resource: 'arn:aws:s3:::my-bucket/*' - MySampleStateMachine: - Type: AWS::Serverless::StateMachine - Properties: - Logging: - Level: ALL - Tracing: - Enabled: true - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - "cloudwatch:*" - Resource: "*" - myTable: - Type: AWS::Serverless::SimpleTable - Properties: - TableName: my-table - SSESpecification: - SSEEnabled: "true" - KMSMasterKeyId: "kmskey" -`, - expected: sam.SAM{ - APIs: []sam.API{ - { - Name: types.StringTest("test"), - TracingEnabled: types.BoolTest(true), - DomainConfiguration: sam.DomainConfiguration{ - Name: types.StringTest("domain"), - SecurityPolicy: types.StringTest("TLS_1_2"), - }, - AccessLogging: sam.AccessLogging{ - CloudwatchLogGroupARN: types.StringTest("arn:aws:logs:us-east-1:123456789:log-group:my-log-group"), - }, - }, - }, - HttpAPIs: []sam.HttpAPI{ - { - Name: types.StringTest("test"), - DomainConfiguration: sam.DomainConfiguration{ - Name: types.StringTest("test"), - SecurityPolicy: types.StringTest("TLS_1_2"), - }, - AccessLogging: sam.AccessLogging{ - CloudwatchLogGroupARN: types.StringTest("arn:aws:logs:us-east-1:123456789:log-group:my-log-group"), - }, - DefaultRouteSettings: sam.RouteSettings{ - DataTraceEnabled: types.BoolTest(true), - DetailedMetricsEnabled: types.BoolTest(true), - }, - }, - }, - Functions: []sam.Function{ - { - FunctionName: types.StringTest("test"), - Tracing: types.StringTest("Active"), - Policies: []iam.Policy{ - { - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithVersion("2012-10-17"). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"s3:GetObject"}). - WithResources([]string{"arn:aws:s3:::my-bucket/*"}). - Build(), - ). - Build(), - } - }(), - }, - }, - ManagedPolicies: []types.StringValue{ - types.StringTest("AWSLambdaExecute"), - }, - }, - }, - StateMachines: []sam.StateMachine{ - { - LoggingConfiguration: sam.LoggingConfiguration{ - LoggingEnabled: types.BoolTest(true), - }, - Tracing: sam.TracingConfiguration{ - Enabled: types.BoolTest(true), - }, - Policies: []iam.Policy{ - { - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithVersion("2012-10-17"). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"cloudwatch:*"}). - WithResources([]string{"*"}). - Build(), - ). - Build(), - } - }(), - }, - }, - }, - }, - SimpleTables: []sam.SimpleTable{ - { - TableName: types.StringTest("my-table"), - SSESpecification: sam.SSESpecification{ - Enabled: types.BoolTest(true), - KMSMasterKeyID: types.StringTest("kmskey"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - ApiGatewayApi: - Type: AWS::Serverless::Api - HttpApi: - Type: AWS::Serverless::HttpApi - myFunction: - Type: AWS::Serverless::Function - MySampleStateMachine: - Type: AWS::Serverless::StateMachine - myTable: - Type: AWS::Serverless::SimpleTable -`, - expected: sam.SAM{ - APIs: []sam.API{{}}, - HttpAPIs: []sam.HttpAPI{{}}, - Functions: []sam.Function{{}}, - StateMachines: []sam.StateMachine{{}}, - SimpleTables: []sam.SimpleTable{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/state_machines.go b/pkg/iac/adapters/cloudformation/aws/sam/state_machines.go deleted file mode 100644 index 5b1be5618ec4..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/state_machines.go +++ /dev/null @@ -1,81 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getStateMachines(cfFile parser.FileContext) (stateMachines []sam.StateMachine) { - - stateMachineResources := cfFile.GetResourcesByType("AWS::Serverless::StateMachine") - for _, r := range stateMachineResources { - stateMachine := sam.StateMachine{ - Metadata: r.Metadata(), - Name: r.GetStringProperty("Name"), - LoggingConfiguration: sam.LoggingConfiguration{ - Metadata: r.Metadata(), - LoggingEnabled: iacTypes.BoolDefault(false, r.Metadata()), - }, - ManagedPolicies: nil, - Policies: nil, - Tracing: getTracingConfiguration(r), - } - - // TODO: By default, the level is set to OFF - if logging := r.GetProperty("Logging"); logging.IsNotNil() { - stateMachine.LoggingConfiguration.Metadata = logging.Metadata() - if level := logging.GetProperty("Level"); level.IsNotNil() { - stateMachine.LoggingConfiguration.LoggingEnabled = iacTypes.Bool(!level.EqualTo("OFF"), level.Metadata()) - } - } - - setStateMachinePolicies(r, &stateMachine) - stateMachines = append(stateMachines, stateMachine) - } - - return stateMachines -} - -func getTracingConfiguration(r *parser.Resource) sam.TracingConfiguration { - tracing := r.GetProperty("Tracing") - if tracing.IsNil() { - return sam.TracingConfiguration{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - } - } - - return sam.TracingConfiguration{ - Metadata: tracing.Metadata(), - Enabled: tracing.GetBoolProperty("Enabled"), - } -} - -func setStateMachinePolicies(r *parser.Resource, stateMachine *sam.StateMachine) { - policies := r.GetProperty("Policies") - if policies.IsNotNil() { - if policies.IsString() { - stateMachine.ManagedPolicies = append(stateMachine.ManagedPolicies, policies.AsStringValue()) - } else if policies.IsList() { - for _, property := range policies.AsList() { - parsed, err := iamgo.Parse(property.GetJsonBytes(true)) - if err != nil { - continue - } - policy := iam.Policy{ - Metadata: property.Metadata(), - Name: iacTypes.StringDefault("", property.Metadata()), - Document: iam.Document{ - Metadata: property.Metadata(), - Parsed: *parsed, - }, - Builtin: iacTypes.Bool(false, property.Metadata()), - } - stateMachine.Policies = append(stateMachine.Policies, policy) - } - } - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sam/tables.go b/pkg/iac/adapters/cloudformation/aws/sam/tables.go deleted file mode 100644 index 89e66acdf514..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sam/tables.go +++ /dev/null @@ -1,39 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getSimpleTables(cfFile parser.FileContext) (tables []sam.SimpleTable) { - - tableResources := cfFile.GetResourcesByType("AWS::Serverless::SimpleTable") - for _, r := range tableResources { - table := sam.SimpleTable{ - Metadata: r.Metadata(), - TableName: r.GetStringProperty("TableName"), - SSESpecification: getSSESpecification(r), - } - - tables = append(tables, table) - } - - return tables -} - -func getSSESpecification(r *parser.Resource) sam.SSESpecification { - if sse := r.GetProperty("SSESpecification"); sse.IsNotNil() { - return sam.SSESpecification{ - Metadata: sse.Metadata(), - Enabled: sse.GetBoolProperty("SSEEnabled"), - KMSMasterKeyID: sse.GetStringProperty("KMSMasterKeyId"), - } - } - - return sam.SSESpecification{ - Metadata: r.Metadata(), - Enabled: iacTypes.BoolDefault(false, r.Metadata()), - KMSMasterKeyID: iacTypes.StringDefault("", r.Metadata()), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sns/sns.go b/pkg/iac/adapters/cloudformation/aws/sns/sns.go deleted file mode 100644 index 4264077bca57..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sns/sns.go +++ /dev/null @@ -1,13 +0,0 @@ -package sns - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a SNS instance -func Adapt(cfFile parser.FileContext) sns.SNS { - return sns.SNS{ - Topics: getTopics(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sns/sns_test.go b/pkg/iac/adapters/cloudformation/aws/sns/sns_test.go deleted file mode 100644 index 25f271db2073..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sns/sns_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package sns - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected sns.SNS - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - MySNSTopic: - Type: AWS::SNS::Topic - Properties: - KmsMasterKeyId: mykey -`, - expected: sns.SNS{ - Topics: []sns.Topic{ - { - Encryption: sns.Encryption{ - KMSKeyID: types.StringTest("mykey"), - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MySNSTopic: - Type: AWS::SNS::Topic - `, - expected: sns.SNS{ - Topics: []sns.Topic{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sns/topic.go b/pkg/iac/adapters/cloudformation/aws/sns/topic.go deleted file mode 100644 index 59c5672bea82..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sns/topic.go +++ /dev/null @@ -1,24 +0,0 @@ -package sns - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getTopics(ctx parser.FileContext) (topics []sns.Topic) { - for _, r := range ctx.GetResourcesByType("AWS::SNS::Topic") { - - topic := sns.Topic{ - Metadata: r.Metadata(), - ARN: types.StringDefault("", r.Metadata()), - Encryption: sns.Encryption{ - Metadata: r.Metadata(), - KMSKeyID: r.GetStringProperty("KmsMasterKeyId"), - }, - } - - topics = append(topics, topic) - } - return topics -} diff --git a/pkg/iac/adapters/cloudformation/aws/sqs/queue.go b/pkg/iac/adapters/cloudformation/aws/sqs/queue.go deleted file mode 100644 index a260fa00e08c..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sqs/queue.go +++ /dev/null @@ -1,62 +0,0 @@ -package sqs - -import ( - "errors" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func getQueues(ctx parser.FileContext) (queues []sqs.Queue) { - for _, r := range ctx.GetResourcesByType("AWS::SQS::Queue") { - queue := sqs.Queue{ - Metadata: r.Metadata(), - QueueURL: iacTypes.StringDefault("", r.Metadata()), - Encryption: sqs.Encryption{ - Metadata: r.Metadata(), - ManagedEncryption: iacTypes.Bool(false, r.Metadata()), - KMSKeyID: r.GetStringProperty("KmsMasterKeyId"), - }, - } - if policy, err := getPolicy(r.ID(), ctx); err == nil { - queue.Policies = append(queue.Policies, *policy) - } - queues = append(queues, queue) - } - return queues -} - -func getPolicy(id string, ctx parser.FileContext) (*iam.Policy, error) { - for _, policyResource := range ctx.GetResourcesByType("AWS::SQS::QueuePolicy") { - documentProp := policyResource.GetProperty("PolicyDocument") - if documentProp.IsNil() { - continue - } - queuesProp := policyResource.GetProperty("Queues") - if queuesProp.IsNil() { - continue - } - for _, queueRef := range queuesProp.AsList() { - if queueRef.IsString() && queueRef.AsString() == id { - raw := documentProp.GetJsonBytes() - parsed, err := iamgo.Parse(raw) - if err != nil { - continue - } - return &iam.Policy{ - Metadata: documentProp.Metadata(), - Name: iacTypes.StringDefault("", documentProp.Metadata()), - Document: iam.Document{ - Metadata: documentProp.Metadata(), - Parsed: *parsed, - }, - Builtin: iacTypes.Bool(false, documentProp.Metadata()), - }, nil - } - } - } - return nil, errors.New("no matching policy found") -} diff --git a/pkg/iac/adapters/cloudformation/aws/sqs/sqs.go b/pkg/iac/adapters/cloudformation/aws/sqs/sqs.go deleted file mode 100644 index 4f01ba6861b0..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sqs/sqs.go +++ /dev/null @@ -1,13 +0,0 @@ -package sqs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an SQS instance -func Adapt(cfFile parser.FileContext) sqs.SQS { - return sqs.SQS{ - Queues: getQueues(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/sqs/sqs_test.go b/pkg/iac/adapters/cloudformation/aws/sqs/sqs_test.go deleted file mode 100644 index 634eddea9ab4..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/sqs/sqs_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package sqs - -import ( - "testing" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected sqs.SQS - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - MyQueue: - Type: AWS::SQS::Queue - Properties: - QueueName: "SampleQueue" - KmsMasterKeyId: mykey - SampleSQSPolicy: - Type: AWS::SQS::QueuePolicy - Properties: - Queues: - - !Ref MyQueue - PolicyDocument: - Statement: - - - Action: - - "SQS:SendMessage" - Effect: "Allow" - Resource: "arn:aws:sqs:us-east-2:444455556666:queue2" -`, - expected: sqs.SQS{ - Queues: []sqs.Queue{ - { - Encryption: sqs.Encryption{ - KMSKeyID: types.StringTest("mykey"), - }, - Policies: []iam.Policy{ - { - Document: func() iam.Document { - return iam.Document{ - Parsed: iamgo.NewPolicyBuilder(). - WithStatement( - iamgo.NewStatementBuilder(). - WithEffect("Allow"). - WithActions([]string{"SQS:SendMessage"}). - WithResources([]string{"arn:aws:sqs:us-east-2:444455556666:queue2"}). - Build(), - ). - Build(), - } - }(), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MySNSTopic: - Type: AWS::SQS::Queue - `, - expected: sqs.SQS{ - Queues: []sqs.Queue{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ssm/secret.go b/pkg/iac/adapters/cloudformation/aws/ssm/secret.go deleted file mode 100644 index ae3a43ee28f7..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ssm/secret.go +++ /dev/null @@ -1,18 +0,0 @@ -package ssm - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ssm" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getSecrets(ctx parser.FileContext) (secrets []ssm.Secret) { - for _, r := range ctx.GetResourcesByType("AWS::SecretsManager::Secret") { - secret := ssm.Secret{ - Metadata: r.Metadata(), - KMSKeyID: r.GetStringProperty("KmsKeyId"), - } - - secrets = append(secrets, secret) - } - return secrets -} diff --git a/pkg/iac/adapters/cloudformation/aws/ssm/ssm.go b/pkg/iac/adapters/cloudformation/aws/ssm/ssm.go deleted file mode 100644 index 88bc5485fe33..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ssm/ssm.go +++ /dev/null @@ -1,13 +0,0 @@ -package ssm - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ssm" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts an SSM instance -func Adapt(cfFile parser.FileContext) ssm.SSM { - return ssm.SSM{ - Secrets: getSecrets(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/ssm/ssm_test.go b/pkg/iac/adapters/cloudformation/aws/ssm/ssm_test.go deleted file mode 100644 index 9709207fec66..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/ssm/ssm_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package ssm - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ssm" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected ssm.SSM - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - MySecretA: - Type: 'AWS::SecretsManager::Secret' - Properties: - Name: MySecretForAppA - KmsKeyId: alias/exampleAlias -`, - expected: ssm.SSM{ - Secrets: []ssm.Secret{ - { - KMSKeyID: types.StringTest("alias/exampleAlias"), - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MySecretA: - Type: 'AWS::SecretsManager::Secret' - `, - expected: ssm.SSM{ - Secrets: []ssm.Secret{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/workspaces/workspace.go b/pkg/iac/adapters/cloudformation/aws/workspaces/workspace.go deleted file mode 100644 index 3966468849cc..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/workspaces/workspace.go +++ /dev/null @@ -1,31 +0,0 @@ -package workspaces - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/workspaces" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -func getWorkSpaces(ctx parser.FileContext) (workSpaces []workspaces.WorkSpace) { - for _, r := range ctx.GetResourcesByType("AWS::WorkSpaces::Workspace") { - workspace := workspaces.WorkSpace{ - Metadata: r.Metadata(), - RootVolume: workspaces.Volume{ - Metadata: r.Metadata(), - Encryption: workspaces.Encryption{ - Metadata: r.Metadata(), - Enabled: r.GetBoolProperty("RootVolumeEncryptionEnabled"), - }, - }, - UserVolume: workspaces.Volume{ - Metadata: r.Metadata(), - Encryption: workspaces.Encryption{ - Metadata: r.Metadata(), - Enabled: r.GetBoolProperty("UserVolumeEncryptionEnabled"), - }, - }, - } - - workSpaces = append(workSpaces, workspace) - } - return workSpaces -} diff --git a/pkg/iac/adapters/cloudformation/aws/workspaces/workspaces.go b/pkg/iac/adapters/cloudformation/aws/workspaces/workspaces.go deleted file mode 100644 index 578fa0d507c3..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/workspaces/workspaces.go +++ /dev/null @@ -1,13 +0,0 @@ -package workspaces - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/workspaces" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -// Adapt adapts a Workspaces instance -func Adapt(cfFile parser.FileContext) workspaces.WorkSpaces { - return workspaces.WorkSpaces{ - WorkSpaces: getWorkSpaces(cfFile), - } -} diff --git a/pkg/iac/adapters/cloudformation/aws/workspaces/workspaces_test.go b/pkg/iac/adapters/cloudformation/aws/workspaces/workspaces_test.go deleted file mode 100644 index 41e821e6466d..000000000000 --- a/pkg/iac/adapters/cloudformation/aws/workspaces/workspaces_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package workspaces - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation/testutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/workspaces" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected workspaces.WorkSpaces - }{ - { - name: "complete", - source: `AWSTemplateFormatVersion: '2010-09-09' -Resources: - MyWorkSpace: - Type: AWS::WorkSpaces::Workspace - Properties: - RootVolumeEncryptionEnabled: true - UserVolumeEncryptionEnabled: true -`, - expected: workspaces.WorkSpaces{ - WorkSpaces: []workspaces.WorkSpace{ - { - RootVolume: workspaces.Volume{ - Encryption: workspaces.Encryption{ - Enabled: types.BoolTest(true), - }, - }, - UserVolume: workspaces.Volume{ - Encryption: workspaces.Encryption{ - Enabled: types.BoolTest(true), - }, - }, - }, - }, - }, - }, - { - name: "empty", - source: `AWSTemplateFormatVersion: 2010-09-09 -Resources: - MyWorkSpace: - Type: AWS::WorkSpaces::Workspace - `, - expected: workspaces.WorkSpaces{ - WorkSpaces: []workspaces.WorkSpace{{}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testutil.AdaptAndCompare(t, tt.source, tt.expected, Adapt) - }) - } -} diff --git a/pkg/iac/adapters/cloudformation/testutil/testutil.go b/pkg/iac/adapters/cloudformation/testutil/testutil.go deleted file mode 100644 index f908519d4106..000000000000 --- a/pkg/iac/adapters/cloudformation/testutil/testutil.go +++ /dev/null @@ -1,25 +0,0 @@ -package testutil - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" -) - -type adaptFn[T any] func(fctx parser.FileContext) T - -func AdaptAndCompare[T any](t *testing.T, source string, expected any, fn adaptFn[T]) { - fsys := testutil.CreateFS(t, map[string]string{ - "main.yaml": source, - }) - - fctx, err := parser.New().ParseFile(context.TODO(), fsys, "main.yaml") - require.NoError(t, err) - - adapted := fn(*fctx) - testutil.AssertDefsecEqual(t, expected, adapted) -} diff --git a/pkg/iac/adapters/terraform/adapt.go b/pkg/iac/adapters/terraform/adapt.go deleted file mode 100644 index 9028e7cc15f2..000000000000 --- a/pkg/iac/adapters/terraform/adapt.go +++ /dev/null @@ -1,31 +0,0 @@ -package terraform - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/azure" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/cloudstack" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/digitalocean" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/github" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/google" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/kubernetes" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/openstack" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/oracle" - "github.com/aquasecurity/trivy/pkg/iac/state" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) *state.State { - return &state.State{ - AWS: aws.Adapt(modules), - Azure: azure.Adapt(modules), - CloudStack: cloudstack.Adapt(modules), - DigitalOcean: digitalocean.Adapt(modules), - GitHub: github.Adapt(modules), - Google: google.Adapt(modules), - Kubernetes: kubernetes.Adapt(modules), - Nifcloud: nifcloud.Adapt(modules), - OpenStack: openstack.Adapt(modules), - Oracle: oracle.Adapt(modules), - } -} diff --git a/pkg/iac/adapters/terraform/aws/accessanalyzer/accessanalyzer.go b/pkg/iac/adapters/terraform/aws/accessanalyzer/accessanalyzer.go deleted file mode 100644 index 93c76f979d55..000000000000 --- a/pkg/iac/adapters/terraform/aws/accessanalyzer/accessanalyzer.go +++ /dev/null @@ -1,40 +0,0 @@ -package accessanalyzer - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/accessanalyzer" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) accessanalyzer.AccessAnalyzer { - return accessanalyzer.AccessAnalyzer{ - Analyzers: adaptTrails(modules), - } -} - -func adaptTrails(modules terraform.Modules) []accessanalyzer.Analyzer { - var analyzer []accessanalyzer.Analyzer - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_accessanalyzer_analyzer") { - analyzer = append(analyzer, adaptAnalyzers(resource)) - } - } - return analyzer -} - -func adaptAnalyzers(resource *terraform.Block) accessanalyzer.Analyzer { - - analyzerName := resource.GetAttribute("analyzer_name") - analyzerNameAttr := analyzerName.AsStringValueOrDefault("", resource) - - arnAnalyzer := resource.GetAttribute("arn") - arnAnalyzerAttr := arnAnalyzer.AsStringValueOrDefault("", resource) - - return accessanalyzer.Analyzer{ - Metadata: resource.GetMetadata(), - Name: analyzerNameAttr, - ARN: arnAnalyzerAttr, - Active: types.BoolDefault(false, resource.GetMetadata()), - } -} diff --git a/pkg/iac/adapters/terraform/aws/adapt.go b/pkg/iac/adapters/terraform/aws/adapt.go deleted file mode 100644 index c9d9d94ecea6..000000000000 --- a/pkg/iac/adapters/terraform/aws/adapt.go +++ /dev/null @@ -1,79 +0,0 @@ -package aws - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/apigateway" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/elasticsearch" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/elb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/emr" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/kinesis" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/kms" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/lambda" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/provider" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/ssm" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/workspaces" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) aws.AWS { - return aws.AWS{ - Meta: aws.Meta{ - TFProviders: provider.Adapt(modules), - }, - APIGateway: apigateway.Adapt(modules), - Athena: athena.Adapt(modules), - Cloudfront: cloudfront.Adapt(modules), - CloudTrail: cloudtrail.Adapt(modules), - CloudWatch: cloudwatch.Adapt(modules), - CodeBuild: codebuild.Adapt(modules), - Config: config.Adapt(modules), - DocumentDB: documentdb.Adapt(modules), - DynamoDB: dynamodb.Adapt(modules), - EC2: ec2.Adapt(modules), - ECR: ecr.Adapt(modules), - ECS: ecs.Adapt(modules), - EFS: efs.Adapt(modules), - EKS: eks.Adapt(modules), - ElastiCache: elasticache.Adapt(modules), - Elasticsearch: elasticsearch.Adapt(modules), - ELB: elb.Adapt(modules), - EMR: emr.Adapt(modules), - IAM: iam.Adapt(modules), - Kinesis: kinesis.Adapt(modules), - KMS: kms.Adapt(modules), - Lambda: lambda.Adapt(modules), - MQ: mq.Adapt(modules), - MSK: msk.Adapt(modules), - Neptune: neptune.Adapt(modules), - RDS: rds.Adapt(modules), - Redshift: redshift.Adapt(modules), - S3: s3.Adapt(modules), - SNS: sns.Adapt(modules), - SQS: sqs.Adapt(modules), - SSM: ssm.Adapt(modules), - WorkSpaces: workspaces.Adapt(modules), - } -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/adapt.go b/pkg/iac/adapters/terraform/aws/apigateway/adapt.go deleted file mode 100644 index bf0e4ca86379..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/adapt.go +++ /dev/null @@ -1,21 +0,0 @@ -package apigateway - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway" - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) apigateway.APIGateway { - return apigateway.APIGateway{ - V1: v1.APIGateway{ - APIs: adaptAPIsV1(modules), - DomainNames: adaptDomainNamesV1(modules), - }, - V2: v2.APIGateway{ - APIs: adaptAPIsV2(modules), - DomainNames: adaptDomainNamesV2(modules), - }, - } -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go b/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go deleted file mode 100644 index 8d2b0155fee5..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package apigateway - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway" - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected apigateway.APIGateway - }{ - { - name: "basic", - terraform: ` -resource "aws_api_gateway_rest_api" "MyDemoAPI" { - name = "MyDemoAPI" - description = "This is my API for demonstration purposes" -} -resource "aws_api_gateway_resource" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id -} -resource "aws_api_gateway_method" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id - resource_id = aws_api_gateway_resource.example.id - http_method = "GET" - authorization = "NONE" -} -resource "aws_apigatewayv2_api" "example" { - name = "tfsec" - protocol_type = "HTTP" -} - - -resource "aws_apigatewayv2_stage" "example" { - api_id = aws_apigatewayv2_api.example.id - name = "tfsec" - access_log_settings { - destination_arn = "arn:123" - } -} - -resource "aws_api_gateway_domain_name" "example" { - domain_name = "v1.com" - security_policy = "TLS_1_0" -} - -resource "aws_apigatewayv2_domain_name" "example" { - domain_name = "v2.com" - domain_name_configuration { - security_policy = "TLS_1_2" - } -} -`, - expected: apigateway.APIGateway{ - V1: v1.APIGateway{ - APIs: []v1.API{ - { - Metadata: iacTypes.Metadata{}, - Name: String("MyDemoAPI"), - Resources: []v1.Resource{ - { - Methods: []v1.Method{ - { - HTTPMethod: String(http.MethodGet), - AuthorizationType: String("NONE"), - APIKeyRequired: Bool(false), - }, - }, - }, - }, - }, - }, - DomainNames: []v1.DomainName{ - { - Name: String("v1.com"), - SecurityPolicy: String("TLS_1_0"), - }, - }, - }, - V2: v2.APIGateway{ - APIs: []v2.API{ - { - Name: String("tfsec"), - ProtocolType: String("HTTP"), - Stages: []v2.Stage{ - { - Name: String("tfsec"), - AccessLogging: v2.AccessLogging{ - CloudwatchLogGroupARN: String("arn:123"), - }, - }, - }, - }, - }, - DomainNames: []v2.DomainName{ - { - Name: String("v2.com"), - SecurityPolicy: String("TLS_1_2"), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Int(i int) iacTypes.IntValue { - return iacTypes.Int(i, iacTypes.NewTestMetadata()) -} - -func Bool(b bool) iacTypes.BoolValue { - return iacTypes.Bool(b, iacTypes.NewTestMetadata()) -} - -func String(s string) iacTypes.StringValue { - return iacTypes.String(s, iacTypes.NewTestMetadata()) -} -func TestLines(t *testing.T) { - src := ` - resource "aws_api_gateway_rest_api" "MyDemoAPI" { - name = "MyDemoAPI" - description = "This is my API for demonstration purposes" - } - - resource "aws_api_gateway_resource" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id - } - - resource "aws_api_gateway_method" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id - resource_id = aws_api_gateway_resource.example.id - http_method = "GET" - authorization = "NONE" - api_key_required = true - } - - resource "aws_apigatewayv2_api" "example" { - name = "tfsec" - protocol_type = "HTTP" - } - - resource "aws_apigatewayv2_stage" "example" { - api_id = aws_apigatewayv2_api.example.id - name = "tfsec" - access_log_settings { - destination_arn = "arn:123" - } - } - - resource "aws_api_gateway_domain_name" "example" { - domain_name = "v1.com" - security_policy = "TLS_1_0" - } - - ` - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.V1.APIs, 1) - require.Len(t, adapted.V2.APIs, 1) - require.Len(t, adapted.V1.DomainNames, 1) - - apiV1 := adapted.V1.APIs[0] - apiV2 := adapted.V2.APIs[0] - domainName := adapted.V1.DomainNames[0] - - assert.Equal(t, 2, apiV1.Metadata.Range().GetStartLine()) - assert.Equal(t, 5, apiV1.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, apiV1.Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, apiV1.Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, apiV1.Resources[0].Methods[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 17, apiV1.Resources[0].Methods[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 14, apiV1.Resources[0].Methods[0].HTTPMethod.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, apiV1.Resources[0].Methods[0].HTTPMethod.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 15, apiV1.Resources[0].Methods[0].AuthorizationType.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 15, apiV1.Resources[0].Methods[0].AuthorizationType.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 16, apiV1.Resources[0].Methods[0].APIKeyRequired.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 16, apiV1.Resources[0].Methods[0].APIKeyRequired.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 19, apiV2.Metadata.Range().GetStartLine()) - assert.Equal(t, 22, apiV2.Metadata.Range().GetEndLine()) - - assert.Equal(t, 20, apiV2.Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 20, apiV2.Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 21, apiV2.ProtocolType.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 21, apiV2.ProtocolType.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 24, apiV2.Stages[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 30, apiV2.Stages[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 26, apiV2.Stages[0].Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 26, apiV2.Stages[0].Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 27, apiV2.Stages[0].AccessLogging.Metadata.Range().GetStartLine()) - assert.Equal(t, 29, apiV2.Stages[0].AccessLogging.Metadata.Range().GetEndLine()) - - assert.Equal(t, 28, apiV2.Stages[0].AccessLogging.CloudwatchLogGroupARN.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 28, apiV2.Stages[0].AccessLogging.CloudwatchLogGroupARN.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 32, domainName.Metadata.Range().GetStartLine()) - assert.Equal(t, 35, domainName.Metadata.Range().GetEndLine()) - - assert.Equal(t, 33, domainName.Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 33, domainName.Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 34, domainName.SecurityPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, domainName.SecurityPolicy.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/apiv1.go b/pkg/iac/adapters/terraform/aws/apigateway/apiv1.go deleted file mode 100644 index 3f03817b9e1b..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/apiv1.go +++ /dev/null @@ -1,115 +0,0 @@ -package apigateway - -import ( - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptAPIResourcesV1(modules terraform.Modules, apiBlock *terraform.Block) []v1.Resource { - var resources []v1.Resource - for _, resourceBlock := range modules.GetReferencingResources(apiBlock, "aws_api_gateway_resource", "rest_api_id") { - method := v1.Resource{ - Metadata: resourceBlock.GetMetadata(), - Methods: adaptAPIMethodsV1(modules, resourceBlock), - } - resources = append(resources, method) - } - return resources -} - -func adaptAPIMethodsV1(modules terraform.Modules, resourceBlock *terraform.Block) []v1.Method { - var methods []v1.Method - for _, methodBlock := range modules.GetReferencingResources(resourceBlock, "aws_api_gateway_method", "resource_id") { - method := v1.Method{ - Metadata: methodBlock.GetMetadata(), - HTTPMethod: methodBlock.GetAttribute("http_method").AsStringValueOrDefault("", methodBlock), - AuthorizationType: methodBlock.GetAttribute("authorization").AsStringValueOrDefault("", methodBlock), - APIKeyRequired: methodBlock.GetAttribute("api_key_required").AsBoolValueOrDefault(false, methodBlock), - } - methods = append(methods, method) - } - return methods -} - -func adaptAPIsV1(modules terraform.Modules) []v1.API { - - var apis []v1.API - apiStageIDs := modules.GetChildResourceIDMapByType("aws_api_gateway_stage") - - for _, apiBlock := range modules.GetResourcesByType("aws_api_gateway_rest_api") { - api := v1.API{ - Metadata: apiBlock.GetMetadata(), - Name: apiBlock.GetAttribute("name").AsStringValueOrDefault("", apiBlock), - Stages: nil, - Resources: adaptAPIResourcesV1(modules, apiBlock), - } - - for _, stageBlock := range modules.GetReferencingResources(apiBlock, "aws_api_gateway_stage", "rest_api_id") { - apiStageIDs.Resolve(stageBlock.ID()) - stage := adaptStageV1(stageBlock, modules) - - api.Stages = append(api.Stages, stage) - } - - apis = append(apis, api) - } - - orphanResources := modules.GetResourceByIDs(apiStageIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := v1.API{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Name: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - } - for _, stage := range orphanResources { - orphanage.Stages = append(orphanage.Stages, adaptStageV1(stage, modules)) - } - apis = append(apis, orphanage) - } - - return apis -} - -func adaptStageV1(stageBlock *terraform.Block, modules terraform.Modules) v1.Stage { - stage := v1.Stage{ - Metadata: stageBlock.GetMetadata(), - Name: stageBlock.GetAttribute("name").AsStringValueOrDefault("", stageBlock), - AccessLogging: v1.AccessLogging{ - Metadata: stageBlock.GetMetadata(), - CloudwatchLogGroupARN: iacTypes.StringDefault("", stageBlock.GetMetadata()), - }, - XRayTracingEnabled: stageBlock.GetAttribute("xray_tracing_enabled").AsBoolValueOrDefault(false, stageBlock), - } - for _, methodSettings := range modules.GetReferencingResources(stageBlock, "aws_api_gateway_method_settings", "stage_name") { - - restMethodSettings := v1.RESTMethodSettings{ - Metadata: methodSettings.GetMetadata(), - Method: iacTypes.String("", methodSettings.GetMetadata()), - CacheDataEncrypted: iacTypes.BoolDefault(false, methodSettings.GetMetadata()), - CacheEnabled: iacTypes.BoolDefault(false, methodSettings.GetMetadata()), - } - - if settings := methodSettings.GetBlock("settings"); settings.IsNotNil() { - if encrypted := settings.GetAttribute("cache_data_encrypted"); encrypted.IsNotNil() { - restMethodSettings.CacheDataEncrypted = settings.GetAttribute("cache_data_encrypted").AsBoolValueOrDefault(false, settings) - } - if encrypted := settings.GetAttribute("caching_enabled"); encrypted.IsNotNil() { - restMethodSettings.CacheEnabled = settings.GetAttribute("caching_enabled").AsBoolValueOrDefault(false, settings) - } - } - - stage.RESTMethodSettings = append(stage.RESTMethodSettings, restMethodSettings) - } - - stage.Name = stageBlock.GetAttribute("stage_name").AsStringValueOrDefault("", stageBlock) - if accessLogging := stageBlock.GetBlock("access_log_settings"); accessLogging.IsNotNil() { - stage.AccessLogging.Metadata = accessLogging.GetMetadata() - stage.AccessLogging.CloudwatchLogGroupARN = accessLogging.GetAttribute("destination_arn").AsStringValueOrDefault("", accessLogging) - } else { - stage.AccessLogging.Metadata = stageBlock.GetMetadata() - stage.AccessLogging.CloudwatchLogGroupARN = iacTypes.StringDefault("", stageBlock.GetMetadata()) - } - - return stage -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go b/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go deleted file mode 100644 index 9932ac864e7b..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package apigateway - -import ( - "net/http" - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" -) - -func Test_adaptAPIMethodsV1(t *testing.T) { - tests := []struct { - name string - terraform string - expected []v1.Method - }{ - { - name: "defaults", - terraform: ` -resource "aws_api_gateway_rest_api" "MyDemoAPI" { - name = "MyDemoAPI" - description = "This is my API for demonstration purposes" -} - -resource "aws_api_gateway_resource" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id -} - -resource "aws_api_gateway_method" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id - resource_id = aws_api_gateway_resource.example.id - http_method = "GET" - authorization = "NONE" -} -`, - expected: []v1.Method{ - { - HTTPMethod: String(http.MethodGet), - AuthorizationType: String("NONE"), - APIKeyRequired: Bool(false), - }, - }, - }, - { - name: "basic", - terraform: ` -resource "aws_api_gateway_rest_api" "MyDemoAPI" { - name = "MyDemoAPI" - description = "This is my API for demonstration purposes" -} - -resource "aws_api_gateway_resource" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id -} - -resource "aws_api_gateway_method" "example" { - rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id - resource_id = aws_api_gateway_resource.example.id - http_method = "GET" - authorization = "NONE" - api_key_required = true -} -`, - expected: []v1.Method{ - { - HTTPMethod: String(http.MethodGet), - AuthorizationType: String("NONE"), - APIKeyRequired: Bool(true), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - restApiBlock := modules.GetBlocks()[1] - adapted := adaptAPIMethodsV1(modules, restApiBlock) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptAPIsV1(t *testing.T) { - tests := []struct { - name string - terraform string - expected []v1.API - }{ - { - name: "defaults", - terraform: ` -resource "aws_api_gateway_rest_api" "example" { - -} -`, - expected: []v1.API{ - { - Name: String(""), - }, - }, - }, - { - name: "full", - terraform: ` -resource "aws_api_gateway_rest_api" "example" { - name = "tfsec" -} -`, - expected: []v1.API{ - { - Name: String("tfsec"), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptAPIsV1(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/apiv2.go b/pkg/iac/adapters/terraform/aws/apigateway/apiv2.go deleted file mode 100644 index 8a6d12679802..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/apiv2.go +++ /dev/null @@ -1,69 +0,0 @@ -package apigateway - -import ( - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptAPIsV2(modules terraform.Modules) []v2.API { - - var apis []v2.API - apiStageIDs := modules.GetChildResourceIDMapByType("aws_apigatewayv2_stage") - - for _, module := range modules { - for _, apiBlock := range module.GetResourcesByType("aws_apigatewayv2_api") { - api := v2.API{ - Metadata: apiBlock.GetMetadata(), - Name: apiBlock.GetAttribute("name").AsStringValueOrDefault("", apiBlock), - ProtocolType: apiBlock.GetAttribute("protocol_type").AsStringValueOrDefault("", apiBlock), - Stages: nil, - } - - for _, stageBlock := range module.GetReferencingResources(apiBlock, "aws_apigatewayv2_stage", "api_id") { - apiStageIDs.Resolve(stageBlock.ID()) - - stage := adaptStageV2(stageBlock) - - api.Stages = append(api.Stages, stage) - } - - apis = append(apis, api) - } - } - - orphanResources := modules.GetResourceByIDs(apiStageIDs.Orphans()...) - if len(orphanResources) > 0 { - orphanage := v2.API{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Name: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - ProtocolType: iacTypes.StringUnresolvable(iacTypes.NewUnmanagedMetadata()), - Stages: nil, - } - for _, stage := range orphanResources { - orphanage.Stages = append(orphanage.Stages, adaptStageV2(stage)) - } - apis = append(apis, orphanage) - } - - return apis -} - -func adaptStageV2(stageBlock *terraform.Block) v2.Stage { - stage := v2.Stage{ - Metadata: stageBlock.GetMetadata(), - Name: stageBlock.GetAttribute("name").AsStringValueOrDefault("", stageBlock), - AccessLogging: v2.AccessLogging{ - Metadata: stageBlock.GetMetadata(), - CloudwatchLogGroupARN: iacTypes.StringDefault("", stageBlock.GetMetadata()), - }, - } - if accessLogging := stageBlock.GetBlock("access_log_settings"); accessLogging.IsNotNil() { - stage.AccessLogging.Metadata = accessLogging.GetMetadata() - stage.AccessLogging.CloudwatchLogGroupARN = accessLogging.GetAttribute("destination_arn").AsStringValueOrDefault("", accessLogging) - } else { - stage.AccessLogging.Metadata = stageBlock.GetMetadata() - stage.AccessLogging.CloudwatchLogGroupARN = iacTypes.StringDefault("", stageBlock.GetMetadata()) - } - return stage -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/apiv2_test.go b/pkg/iac/adapters/terraform/aws/apigateway/apiv2_test.go deleted file mode 100644 index fa73981c80e3..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/apiv2_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package apigateway - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" -) - -func Test_adaptAPIsV2(t *testing.T) { - tests := []struct { - name string - terraform string - expected []v2.API - }{ - { - name: "defaults", - terraform: ` -resource "aws_apigatewayv2_api" "example" { - protocol_type = "HTTP" -} -`, - expected: []v2.API{ - { - Name: String(""), - ProtocolType: String("HTTP"), - }, - }, - }, - { - name: "full", - terraform: ` -resource "aws_apigatewayv2_api" "example" { - name = "tfsec" - protocol_type = "HTTP" -} -`, - expected: []v2.API{ - { - Name: String("tfsec"), - ProtocolType: String("HTTP"), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptAPIsV2(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptStageV2(t *testing.T) { - tests := []struct { - name string - terraform string - expected v2.Stage - }{ - { - name: "defaults", - terraform: ` -resource "aws_apigatewayv2_stage" "example" { - -} -`, - expected: v2.Stage{ - Name: String(""), - AccessLogging: v2.AccessLogging{ - CloudwatchLogGroupARN: String(""), - }, - }, - }, - { - name: "basics", - terraform: ` -resource "aws_apigatewayv2_stage" "example" { - name = "tfsec" - access_log_settings { - destination_arn = "arn:123" - } -} -`, - expected: v2.Stage{ - Name: String("tfsec"), - AccessLogging: v2.AccessLogging{ - CloudwatchLogGroupARN: String("arn:123"), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptStageV2(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/namesv1.go b/pkg/iac/adapters/terraform/aws/apigateway/namesv1.go deleted file mode 100644 index 7bc6af3edd4c..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/namesv1.go +++ /dev/null @@ -1,24 +0,0 @@ -package apigateway - -import ( - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptDomainNamesV1(modules terraform.Modules) []v1.DomainName { - - var domainNames []v1.DomainName - - for _, module := range modules { - for _, nameBlock := range module.GetResourcesByType("aws_api_gateway_domain_name") { - domainName := v1.DomainName{ - Metadata: nameBlock.GetMetadata(), - Name: nameBlock.GetAttribute("domain_name").AsStringValueOrDefault("", nameBlock), - SecurityPolicy: nameBlock.GetAttribute("security_policy").AsStringValueOrDefault("TLS_1_0", nameBlock), - } - domainNames = append(domainNames, domainName) - } - } - - return domainNames -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/namesv1_test.go b/pkg/iac/adapters/terraform/aws/apigateway/namesv1_test.go deleted file mode 100644 index bff0ac47848e..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/namesv1_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package apigateway - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" -) - -func Test_adaptDomainNamesV1(t *testing.T) { - tests := []struct { - name string - terraform string - expected []v1.DomainName - }{ - { - name: "defaults", - terraform: ` -resource "aws_api_gateway_domain_name" "example" { -} -`, - expected: []v1.DomainName{ - { - Name: String(""), - SecurityPolicy: String("TLS_1_0"), - }, - }, - }, - { - name: "basic", - terraform: ` -resource "aws_api_gateway_domain_name" "example" { - domain_name = "testing.com" - security_policy = "TLS_1_2" -} -`, - expected: []v1.DomainName{ - { - Name: String("testing.com"), - SecurityPolicy: String("TLS_1_2"), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptDomainNamesV1(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/namesv2.go b/pkg/iac/adapters/terraform/aws/apigateway/namesv2.go deleted file mode 100644 index e7beb2d6ed2f..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/namesv2.go +++ /dev/null @@ -1,28 +0,0 @@ -package apigateway - -import ( - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptDomainNamesV2(modules terraform.Modules) []v2.DomainName { - - var domainNames []v2.DomainName - - for _, module := range modules { - for _, nameBlock := range module.GetResourcesByType("aws_apigatewayv2_domain_name") { - domainName := v2.DomainName{ - Metadata: nameBlock.GetMetadata(), - Name: nameBlock.GetAttribute("domain_name").AsStringValueOrDefault("", nameBlock), - SecurityPolicy: types.StringDefault("TLS_1_0", nameBlock.GetMetadata()), - } - if config := nameBlock.GetBlock("domain_name_configuration"); config.IsNotNil() { - domainName.SecurityPolicy = config.GetAttribute("security_policy").AsStringValueOrDefault("TLS_1_0", config) - } - domainNames = append(domainNames, domainName) - } - } - - return domainNames -} diff --git a/pkg/iac/adapters/terraform/aws/apigateway/namesv2_test.go b/pkg/iac/adapters/terraform/aws/apigateway/namesv2_test.go deleted file mode 100644 index 25c40ebcc231..000000000000 --- a/pkg/iac/adapters/terraform/aws/apigateway/namesv2_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package apigateway - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" -) - -func Test_adaptDomainNamesV2(t *testing.T) { - tests := []struct { - name string - terraform string - expected []v2.DomainName - }{ - { - name: "defaults", - terraform: ` -resource "aws_apigatewayv2_domain_name" "example" { -} -`, - expected: []v2.DomainName{ - { - Name: String(""), - SecurityPolicy: String("TLS_1_0"), - }, - }, - }, - { - name: "fully populated", - terraform: ` -resource "aws_apigatewayv2_domain_name" "example" { - domain_name = "testing.com" - domain_name_configuration { - security_policy = "TLS_1_2" - } -} -`, - expected: []v2.DomainName{ - { - Name: String("testing.com"), - SecurityPolicy: String("TLS_1_2"), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptDomainNamesV2(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/aws/athena/adapt.go b/pkg/iac/adapters/terraform/aws/athena/adapt.go deleted file mode 100644 index f8f56df99e87..000000000000 --- a/pkg/iac/adapters/terraform/aws/athena/adapt.go +++ /dev/null @@ -1,80 +0,0 @@ -package athena - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) athena.Athena { - return athena.Athena{ - Databases: adaptDatabases(modules), - Workgroups: adaptWorkgroups(modules), - } -} - -func adaptDatabases(modules terraform.Modules) []athena.Database { - var databases []athena.Database - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_athena_database") { - databases = append(databases, adaptDatabase(resource)) - } - } - return databases -} - -func adaptWorkgroups(modules terraform.Modules) []athena.Workgroup { - var workgroups []athena.Workgroup - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_athena_workgroup") { - workgroups = append(workgroups, adaptWorkgroup(resource)) - } - } - return workgroups -} - -func adaptDatabase(resource *terraform.Block) athena.Database { - database := athena.Database{ - Metadata: resource.GetMetadata(), - Name: resource.GetAttribute("name").AsStringValueOrDefault("", resource), - Encryption: athena.EncryptionConfiguration{ - Metadata: resource.GetMetadata(), - Type: iacTypes.StringDefault("", resource.GetMetadata()), - }, - } - if encryptionConfigBlock := resource.GetBlock("encryption_configuration"); encryptionConfigBlock.IsNotNil() { - database.Encryption.Metadata = encryptionConfigBlock.GetMetadata() - encryptionOptionAttr := encryptionConfigBlock.GetAttribute("encryption_option") - database.Encryption.Type = encryptionOptionAttr.AsStringValueOrDefault("", encryptionConfigBlock) - } - - return database -} - -func adaptWorkgroup(resource *terraform.Block) athena.Workgroup { - workgroup := athena.Workgroup{ - Metadata: resource.GetMetadata(), - Name: resource.GetAttribute("name").AsStringValueOrDefault("", resource), - Encryption: athena.EncryptionConfiguration{ - Metadata: resource.GetMetadata(), - Type: iacTypes.StringDefault("", resource.GetMetadata()), - }, - EnforceConfiguration: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - if configBlock := resource.GetBlock("configuration"); configBlock.IsNotNil() { - - enforceWGConfigAttr := configBlock.GetAttribute("enforce_workgroup_configuration") - workgroup.EnforceConfiguration = enforceWGConfigAttr.AsBoolValueOrDefault(true, configBlock) - - if resultConfigBlock := configBlock.GetBlock("result_configuration"); configBlock.IsNotNil() { - if encryptionConfigBlock := resultConfigBlock.GetBlock("encryption_configuration"); encryptionConfigBlock.IsNotNil() { - encryptionOptionAttr := encryptionConfigBlock.GetAttribute("encryption_option") - workgroup.Encryption.Metadata = encryptionConfigBlock.GetMetadata() - workgroup.Encryption.Type = encryptionOptionAttr.AsStringValueOrDefault("", encryptionConfigBlock) - } - } - } - - return workgroup -} diff --git a/pkg/iac/adapters/terraform/aws/athena/adapt_test.go b/pkg/iac/adapters/terraform/aws/athena/adapt_test.go deleted file mode 100644 index 274167e71599..000000000000 --- a/pkg/iac/adapters/terraform/aws/athena/adapt_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package athena - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/athena" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptDatabase(t *testing.T) { - tests := []struct { - name string - terraform string - expected athena.Database - }{ - { - name: "athena database", - terraform: ` - resource "aws_athena_database" "my_wg" { - name = "database_name" - - encryption_configuration { - encryption_option = "SSE_KMS" - } - } -`, - expected: athena.Database{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("database_name", iacTypes.NewTestMetadata()), - Encryption: athena.EncryptionConfiguration{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String(athena.EncryptionTypeSSEKMS, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptDatabase(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptWorkgroup(t *testing.T) { - tests := []struct { - name string - terraform string - expected athena.Workgroup - }{ - { - name: "encryption type SSE KMS", - terraform: ` - resource "aws_athena_workgroup" "my_wg" { - name = "example" - - configuration { - enforce_workgroup_configuration = true - - result_configuration { - encryption_configuration { - encryption_option = "SSE_KMS" - } - } - } - } -`, - expected: athena.Workgroup{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("example", iacTypes.NewTestMetadata()), - Encryption: athena.EncryptionConfiguration{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String(athena.EncryptionTypeSSEKMS, iacTypes.NewTestMetadata()), - }, - EnforceConfiguration: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "configuration not enforced", - terraform: ` - resource "aws_athena_workgroup" "my_wg" { - name = "example" - - configuration { - enforce_workgroup_configuration = false - - result_configuration { - encryption_configuration { - encryption_option = "SSE_KMS" - } - } - } - } -`, - expected: athena.Workgroup{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("example", iacTypes.NewTestMetadata()), - Encryption: athena.EncryptionConfiguration{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String(athena.EncryptionTypeSSEKMS, iacTypes.NewTestMetadata()), - }, - EnforceConfiguration: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "enforce configuration defaults to true", - terraform: ` - resource "aws_athena_workgroup" "my_wg" { - name = "example" - - configuration { - result_configuration { - encryption_configuration { - encryption_option = "" - } - } - } - } -`, - expected: athena.Workgroup{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("example", iacTypes.NewTestMetadata()), - Encryption: athena.EncryptionConfiguration{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String(athena.EncryptionTypeNone, iacTypes.NewTestMetadata()), - }, - EnforceConfiguration: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "missing configuration block", - terraform: ` - resource "aws_athena_workgroup" "my_wg" { - name = "example" - } -`, - expected: athena.Workgroup{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("example", iacTypes.NewTestMetadata()), - Encryption: athena.EncryptionConfiguration{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String(athena.EncryptionTypeNone, iacTypes.NewTestMetadata()), - }, - EnforceConfiguration: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptWorkgroup(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_athena_database" "good_example" { - name = "database_name" - bucket = aws_s3_bucket.hoge.bucket - - encryption_configuration { - encryption_option = "SSE_KMS" - kms_key_arn = aws_kms_key.example.arn - } - } - - resource "aws_athena_workgroup" "good_example" { - name = "example" - - configuration { - enforce_workgroup_configuration = true - publish_cloudwatch_metrics_enabled = true - - result_configuration { - output_location = "s3://${aws_s3_bucket.example.bucket}/output/" - - encryption_configuration { - encryption_option = "SSE_KMS" - kms_key_arn = aws_kms_key.example.arn - } - } - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Databases, 1) - require.Len(t, adapted.Workgroups, 1) - - assert.Equal(t, 7, adapted.Databases[0].Encryption.Type.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, adapted.Databases[0].Encryption.Type.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 16, adapted.Workgroups[0].EnforceConfiguration.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 16, adapted.Workgroups[0].EnforceConfiguration.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, adapted.Workgroups[0].Encryption.Type.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 23, adapted.Workgroups[0].Encryption.Type.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/cloudfront/adapt.go b/pkg/iac/adapters/terraform/aws/cloudfront/adapt.go deleted file mode 100644 index 721ecf991d55..000000000000 --- a/pkg/iac/adapters/terraform/aws/cloudfront/adapt.go +++ /dev/null @@ -1,79 +0,0 @@ -package cloudfront - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) cloudfront.Cloudfront { - return cloudfront.Cloudfront{ - Distributions: adaptDistributions(modules), - } -} - -func adaptDistributions(modules terraform.Modules) []cloudfront.Distribution { - var distributions []cloudfront.Distribution - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_cloudfront_distribution") { - distributions = append(distributions, adaptDistribution(resource)) - } - } - return distributions -} - -func adaptDistribution(resource *terraform.Block) cloudfront.Distribution { - - distribution := cloudfront.Distribution{ - Metadata: resource.GetMetadata(), - WAFID: types.StringDefault("", resource.GetMetadata()), - Logging: cloudfront.Logging{ - Metadata: resource.GetMetadata(), - Bucket: types.StringDefault("", resource.GetMetadata()), - }, - DefaultCacheBehaviour: cloudfront.CacheBehaviour{ - Metadata: resource.GetMetadata(), - ViewerProtocolPolicy: types.StringDefault("", resource.GetMetadata()), - }, - OrdererCacheBehaviours: nil, - ViewerCertificate: cloudfront.ViewerCertificate{ - Metadata: resource.GetMetadata(), - MinimumProtocolVersion: types.StringDefault("TLSv1", resource.GetMetadata()), - }, - } - - distribution.WAFID = resource.GetAttribute("web_acl_id").AsStringValueOrDefault("", resource) - - if loggingBlock := resource.GetBlock("logging_config"); loggingBlock.IsNotNil() { - distribution.Logging.Metadata = loggingBlock.GetMetadata() - bucketAttr := loggingBlock.GetAttribute("bucket") - distribution.Logging.Bucket = bucketAttr.AsStringValueOrDefault("", loggingBlock) - } - - if defaultCacheBlock := resource.GetBlock("default_cache_behavior"); defaultCacheBlock.IsNotNil() { - distribution.DefaultCacheBehaviour.Metadata = defaultCacheBlock.GetMetadata() - viewerProtocolPolicyAttr := defaultCacheBlock.GetAttribute("viewer_protocol_policy") - distribution.DefaultCacheBehaviour.ViewerProtocolPolicy = viewerProtocolPolicyAttr.AsStringValueOrDefault("", defaultCacheBlock) - } - - orderedCacheBlocks := resource.GetBlocks("ordered_cache_behavior") - for _, orderedCacheBlock := range orderedCacheBlocks { - viewerProtocolPolicyAttr := orderedCacheBlock.GetAttribute("viewer_protocol_policy") - viewerProtocolPolicyVal := viewerProtocolPolicyAttr.AsStringValueOrDefault("", orderedCacheBlock) - distribution.OrdererCacheBehaviours = append(distribution.OrdererCacheBehaviours, cloudfront.CacheBehaviour{ - Metadata: orderedCacheBlock.GetMetadata(), - ViewerProtocolPolicy: viewerProtocolPolicyVal, - }) - } - - if viewerCertBlock := resource.GetBlock("viewer_certificate"); viewerCertBlock.IsNotNil() { - distribution.ViewerCertificate = cloudfront.ViewerCertificate{ - Metadata: viewerCertBlock.GetMetadata(), - MinimumProtocolVersion: viewerCertBlock.GetAttribute("minimum_protocol_version").AsStringValueOrDefault("TLSv1", viewerCertBlock), - SSLSupportMethod: viewerCertBlock.GetAttribute("ssl_support_method").AsStringValueOrDefault("", viewerCertBlock), - CloudfrontDefaultCertificate: viewerCertBlock.GetAttribute("cloudfront_default_certificate").AsBoolValueOrDefault(false, viewerCertBlock), - } - } - - return distribution -} diff --git a/pkg/iac/adapters/terraform/aws/cloudfront/adapt_test.go b/pkg/iac/adapters/terraform/aws/cloudfront/adapt_test.go deleted file mode 100644 index fd1bf65e9cc5..000000000000 --- a/pkg/iac/adapters/terraform/aws/cloudfront/adapt_test.go +++ /dev/null @@ -1,161 +0,0 @@ -package cloudfront - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudfront" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptDistribution(t *testing.T) { - tests := []struct { - name string - terraform string - expected cloudfront.Distribution - }{ - { - name: "configured", - terraform: ` - resource "aws_cloudfront_distribution" "example" { - logging_config { - bucket = "mylogs.s3.amazonaws.com" - } - - web_acl_id = "waf_id" - - default_cache_behavior { - viewer_protocol_policy = "redirect-to-https" - } - - ordered_cache_behavior { - viewer_protocol_policy = "redirect-to-https" - } - - viewer_certificate { - cloudfront_default_certificate = true - minimum_protocol_version = "TLSv1.2_2021" - ssl_support_method = "sni-only" - } - } -`, - expected: cloudfront.Distribution{ - Metadata: iacTypes.NewTestMetadata(), - WAFID: iacTypes.String("waf_id", iacTypes.NewTestMetadata()), - Logging: cloudfront.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Bucket: iacTypes.String("mylogs.s3.amazonaws.com", iacTypes.NewTestMetadata()), - }, - DefaultCacheBehaviour: cloudfront.CacheBehaviour{ - Metadata: iacTypes.NewTestMetadata(), - ViewerProtocolPolicy: iacTypes.String("redirect-to-https", iacTypes.NewTestMetadata()), - }, - OrdererCacheBehaviours: []cloudfront.CacheBehaviour{ - { - Metadata: iacTypes.NewTestMetadata(), - ViewerProtocolPolicy: iacTypes.String("redirect-to-https", iacTypes.NewTestMetadata()), - }, - }, - ViewerCertificate: cloudfront.ViewerCertificate{ - Metadata: iacTypes.NewTestMetadata(), - MinimumProtocolVersion: iacTypes.String("TLSv1.2_2021", iacTypes.NewTestMetadata()), - CloudfrontDefaultCertificate: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - SSLSupportMethod: iacTypes.String("sni-only", iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_cloudfront_distribution" "example" { - } -`, - expected: cloudfront.Distribution{ - Metadata: iacTypes.NewTestMetadata(), - WAFID: iacTypes.String("", iacTypes.NewTestMetadata()), - Logging: cloudfront.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Bucket: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - DefaultCacheBehaviour: cloudfront.CacheBehaviour{ - Metadata: iacTypes.NewTestMetadata(), - ViewerProtocolPolicy: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - - ViewerCertificate: cloudfront.ViewerCertificate{ - Metadata: iacTypes.NewTestMetadata(), - MinimumProtocolVersion: iacTypes.String("TLSv1", iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptDistribution(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_cloudfront_distribution" "example" { - logging_config { - bucket = "mylogs.s3.amazonaws.com" - } - - web_acl_id = "waf_id" - - default_cache_behavior { - viewer_protocol_policy = "redirect-to-https" - } - - ordered_cache_behavior { - viewer_protocol_policy = "redirect-to-https" - } - - viewer_certificate { - cloudfront_default_certificate = true - minimum_protocol_version = "TLSv1.2_2021" - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Distributions, 1) - distribution := adapted.Distributions[0] - - assert.Equal(t, 2, distribution.Metadata.Range().GetStartLine()) - assert.Equal(t, 21, distribution.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, distribution.Logging.Metadata.Range().GetStartLine()) - assert.Equal(t, 5, distribution.Logging.Metadata.Range().GetEndLine()) - - assert.Equal(t, 7, distribution.WAFID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, distribution.WAFID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 9, distribution.DefaultCacheBehaviour.Metadata.Range().GetStartLine()) - assert.Equal(t, 11, distribution.DefaultCacheBehaviour.Metadata.Range().GetEndLine()) - - assert.Equal(t, 10, distribution.DefaultCacheBehaviour.ViewerProtocolPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, distribution.DefaultCacheBehaviour.ViewerProtocolPolicy.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, distribution.OrdererCacheBehaviours[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 15, distribution.OrdererCacheBehaviours[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 14, distribution.OrdererCacheBehaviours[0].ViewerProtocolPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, distribution.OrdererCacheBehaviours[0].ViewerProtocolPolicy.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, distribution.ViewerCertificate.Metadata.Range().GetStartLine()) - assert.Equal(t, 20, distribution.ViewerCertificate.Metadata.Range().GetEndLine()) - - assert.Equal(t, 19, distribution.ViewerCertificate.MinimumProtocolVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 19, distribution.ViewerCertificate.MinimumProtocolVersion.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/cloudtrail/adapt.go b/pkg/iac/adapters/terraform/aws/cloudtrail/adapt.go deleted file mode 100644 index 22464d85f80c..000000000000 --- a/pkg/iac/adapters/terraform/aws/cloudtrail/adapt.go +++ /dev/null @@ -1,67 +0,0 @@ -package cloudtrail - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) cloudtrail.CloudTrail { - return cloudtrail.CloudTrail{ - Trails: adaptTrails(modules), - } -} - -func adaptTrails(modules terraform.Modules) []cloudtrail.Trail { - var trails []cloudtrail.Trail - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_cloudtrail") { - trails = append(trails, adaptTrail(resource)) - } - } - return trails -} - -func adaptTrail(resource *terraform.Block) cloudtrail.Trail { - nameAttr := resource.GetAttribute("name") - nameVal := nameAttr.AsStringValueOrDefault("", resource) - - enableLogFileValidationAttr := resource.GetAttribute("enable_log_file_validation") - enableLogFileValidationVal := enableLogFileValidationAttr.AsBoolValueOrDefault(false, resource) - - isMultiRegionAttr := resource.GetAttribute("is_multi_region_trail") - isMultiRegionVal := isMultiRegionAttr.AsBoolValueOrDefault(false, resource) - - KMSKeyIDAttr := resource.GetAttribute("kms_key_id") - KMSKeyIDVal := KMSKeyIDAttr.AsStringValueOrDefault("", resource) - - var selectors []cloudtrail.EventSelector - for _, selBlock := range resource.GetBlocks("event_selector") { - var resources []cloudtrail.DataResource - for _, resBlock := range selBlock.GetBlocks("data_resource") { - resources = append(resources, cloudtrail.DataResource{ - Metadata: resBlock.GetMetadata(), - Type: resBlock.GetAttribute("type").AsStringValueOrDefault("", resBlock), - Values: resBlock.GetAttribute("values").AsStringValues(), - }) - } - selector := cloudtrail.EventSelector{ - Metadata: selBlock.GetMetadata(), - DataResources: resources, - ReadWriteType: selBlock.GetAttribute("read_write_type").AsStringValueOrDefault("All", selBlock), - } - selectors = append(selectors, selector) - } - - return cloudtrail.Trail{ - Metadata: resource.GetMetadata(), - Name: nameVal, - EnableLogFileValidation: enableLogFileValidationVal, - IsMultiRegion: isMultiRegionVal, - KMSKeyID: KMSKeyIDVal, - CloudWatchLogsLogGroupArn: resource.GetAttribute("cloud_watch_logs_group_arn").AsStringValueOrDefault("", resource), - IsLogging: resource.GetAttribute("enable_logging").AsBoolValueOrDefault(true, resource), - BucketName: resource.GetAttribute("s3_bucket_name").AsStringValueOrDefault("", resource), - EventSelectors: selectors, - } -} diff --git a/pkg/iac/adapters/terraform/aws/cloudtrail/adapt_test.go b/pkg/iac/adapters/terraform/aws/cloudtrail/adapt_test.go deleted file mode 100644 index f79664d0b7de..000000000000 --- a/pkg/iac/adapters/terraform/aws/cloudtrail/adapt_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package cloudtrail - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudtrail" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptTrail(t *testing.T) { - tests := []struct { - name string - terraform string - expected cloudtrail.Trail - }{ - { - name: "configured", - terraform: ` - resource "aws_cloudtrail" "example" { - name = "example" - is_multi_region_trail = true - - enable_log_file_validation = true - kms_key_id = "kms-key" - s3_bucket_name = "abcdefgh" - cloud_watch_logs_group_arn = "abc" - enable_logging = false - } -`, - expected: cloudtrail.Trail{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("example", iacTypes.NewTestMetadata()), - EnableLogFileValidation: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - IsMultiRegion: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("kms-key", iacTypes.NewTestMetadata()), - CloudWatchLogsLogGroupArn: iacTypes.String("abc", iacTypes.NewTestMetadata()), - IsLogging: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - BucketName: iacTypes.String("abcdefgh", iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_cloudtrail" "example" { - } -`, - expected: cloudtrail.Trail{ - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("", iacTypes.NewTestMetadata()), - EnableLogFileValidation: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - IsMultiRegion: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - BucketName: iacTypes.String("", iacTypes.NewTestMetadata()), - CloudWatchLogsLogGroupArn: iacTypes.String("", iacTypes.NewTestMetadata()), - IsLogging: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptTrail(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_cloudtrail" "example" { - name = "example" - is_multi_region_trail = true - - enable_log_file_validation = true - kms_key_id = "kms-key" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Trails, 1) - trail := adapted.Trails[0] - - assert.Equal(t, 2, trail.Metadata.Range().GetStartLine()) - assert.Equal(t, 8, trail.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, trail.Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, trail.Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, trail.IsMultiRegion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, trail.IsMultiRegion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, trail.EnableLogFileValidation.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, trail.EnableLogFileValidation.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 7, trail.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, trail.KMSKeyID.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/cloudwatch/adapt.go b/pkg/iac/adapters/terraform/aws/cloudwatch/adapt.go deleted file mode 100644 index c0b47b7f8d3e..000000000000 --- a/pkg/iac/adapters/terraform/aws/cloudwatch/adapt.go +++ /dev/null @@ -1,47 +0,0 @@ -package cloudwatch - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) cloudwatch.CloudWatch { - return cloudwatch.CloudWatch{ - LogGroups: adaptLogGroups(modules), - } -} - -func adaptLogGroups(modules terraform.Modules) []cloudwatch.LogGroup { - var logGroups []cloudwatch.LogGroup - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_cloudwatch_log_group") { - logGroups = append(logGroups, adaptLogGroup(resource, module)) - } - } - return logGroups -} - -func adaptLogGroup(resource *terraform.Block, module *terraform.Module) cloudwatch.LogGroup { - nameAttr := resource.GetAttribute("name") - nameVal := nameAttr.AsStringValueOrDefault("", resource) - - KMSKeyIDAttr := resource.GetAttribute("kms_key_id") - KMSKeyIDVal := KMSKeyIDAttr.AsStringValueOrDefault("", resource) - - if keyBlock, err := module.GetReferencedBlock(KMSKeyIDAttr, resource); err == nil { - KMSKeyIDVal = types.String(keyBlock.FullName(), keyBlock.GetMetadata()) - } - - retentionInDaysAttr := resource.GetAttribute("retention_in_days") - retentionInDaysVal := retentionInDaysAttr.AsIntValueOrDefault(0, resource) - - return cloudwatch.LogGroup{ - Metadata: resource.GetMetadata(), - Arn: types.StringDefault("", resource.GetMetadata()), - Name: nameVal, - KMSKeyID: KMSKeyIDVal, - RetentionInDays: retentionInDaysVal, - MetricFilters: nil, - } -} diff --git a/pkg/iac/adapters/terraform/aws/cloudwatch/adapt_test.go b/pkg/iac/adapters/terraform/aws/cloudwatch/adapt_test.go deleted file mode 100644 index 86a484aecce6..000000000000 --- a/pkg/iac/adapters/terraform/aws/cloudwatch/adapt_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package cloudwatch - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudwatch" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptLogGroups(t *testing.T) { - tests := []struct { - name string - terraform string - expected []cloudwatch.LogGroup - }{ - { - name: "key referencing block", - terraform: ` - resource "aws_cloudwatch_log_group" "my-group" { - name = "my-group" - kms_key_id = aws_kms_key.log_key.arn - } - - resource "aws_kms_key" "log_key" { - } -`, - expected: []cloudwatch.LogGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Arn: iacTypes.String("", iacTypes.NewTestMetadata()), - Name: iacTypes.String("my-group", iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("aws_kms_key.log_key", iacTypes.NewTestMetadata()), - RetentionInDays: iacTypes.Int(0, iacTypes.NewTestMetadata()), - MetricFilters: nil, - }, - }, - }, - { - name: "key as string", - terraform: ` - resource "aws_cloudwatch_log_group" "my-group" { - name = "my-group" - kms_key_id = "key-as-string" - } -`, - expected: []cloudwatch.LogGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Arn: iacTypes.String("", iacTypes.NewTestMetadata()), - Name: iacTypes.String("my-group", iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("key-as-string", iacTypes.NewTestMetadata()), - RetentionInDays: iacTypes.Int(0, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "missing key", - terraform: ` - resource "aws_cloudwatch_log_group" "my-group" { - name = "my-group" - retention_in_days = 3 - } -`, - expected: []cloudwatch.LogGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Arn: iacTypes.String("", iacTypes.NewTestMetadata()), - Name: iacTypes.String("my-group", iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - RetentionInDays: iacTypes.Int(3, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptLogGroups(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_cloudwatch_log_group" "my-group" { - name = "my-group" - kms_key_id = aws_kms_key.log_key.arn - retention_in_days = 3 - - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - require.Len(t, adapted.LogGroups, 1) - logGroup := adapted.LogGroups[0] - - assert.Equal(t, 3, logGroup.Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, logGroup.Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, logGroup.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, logGroup.KMSKeyID.GetMetadata().Range().GetStartLine()) - - assert.Equal(t, 5, logGroup.RetentionInDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, logGroup.RetentionInDays.GetMetadata().Range().GetStartLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/codebuild/adapt.go b/pkg/iac/adapters/terraform/aws/codebuild/adapt.go deleted file mode 100644 index f7fa4b4f35b5..000000000000 --- a/pkg/iac/adapters/terraform/aws/codebuild/adapt.go +++ /dev/null @@ -1,66 +0,0 @@ -package codebuild - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) codebuild.CodeBuild { - return codebuild.CodeBuild{ - Projects: adaptProjects(modules), - } -} - -func adaptProjects(modules terraform.Modules) []codebuild.Project { - var projects []codebuild.Project - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_codebuild_project") { - projects = append(projects, adaptProject(resource)) - } - } - return projects -} - -func adaptProject(resource *terraform.Block) codebuild.Project { - - project := codebuild.Project{ - Metadata: resource.GetMetadata(), - ArtifactSettings: codebuild.ArtifactSettings{ - Metadata: resource.GetMetadata(), - EncryptionEnabled: types.BoolDefault(true, resource.GetMetadata()), - }, - SecondaryArtifactSettings: nil, - } - - var hasArtifacts bool - - if artifactsBlock := resource.GetBlock("artifacts"); artifactsBlock.IsNotNil() { - project.ArtifactSettings.Metadata = artifactsBlock.GetMetadata() - typeAttr := artifactsBlock.GetAttribute("type") - encryptionDisabledAttr := artifactsBlock.GetAttribute("encryption_disabled") - hasArtifacts = typeAttr.NotEqual("NO_ARTIFACTS") - if encryptionDisabledAttr.IsTrue() && hasArtifacts { - project.ArtifactSettings.EncryptionEnabled = types.Bool(false, artifactsBlock.GetMetadata()) - } else { - project.ArtifactSettings.EncryptionEnabled = types.Bool(true, artifactsBlock.GetMetadata()) - } - } - - secondaryArtifactBlocks := resource.GetBlocks("secondary_artifacts") - for _, secondaryArtifactBlock := range secondaryArtifactBlocks { - - secondaryEncryptionEnabled := types.BoolDefault(true, secondaryArtifactBlock.GetMetadata()) - secondaryEncryptionDisabledAttr := secondaryArtifactBlock.GetAttribute("encryption_disabled") - if secondaryEncryptionDisabledAttr.IsTrue() && hasArtifacts { - secondaryEncryptionEnabled = types.Bool(false, secondaryArtifactBlock.GetMetadata()) - } - - project.SecondaryArtifactSettings = append(project.SecondaryArtifactSettings, codebuild.ArtifactSettings{ - Metadata: secondaryArtifactBlock.GetMetadata(), - EncryptionEnabled: secondaryEncryptionEnabled, - }) - } - - return project -} diff --git a/pkg/iac/adapters/terraform/aws/codebuild/adapt_test.go b/pkg/iac/adapters/terraform/aws/codebuild/adapt_test.go deleted file mode 100644 index c53a509606dc..000000000000 --- a/pkg/iac/adapters/terraform/aws/codebuild/adapt_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package codebuild - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/codebuild" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptProject(t *testing.T) { - tests := []struct { - name string - terraform string - expected codebuild.Project - }{ - { - name: "configured", - terraform: ` - resource "aws_codebuild_project" "codebuild" { - - artifacts { - encryption_disabled = false - } - - secondary_artifacts { - encryption_disabled = false - } - secondary_artifacts { - encryption_disabled = true - } - } -`, - expected: codebuild.Project{ - Metadata: iacTypes.NewTestMetadata(), - ArtifactSettings: codebuild.ArtifactSettings{ - Metadata: iacTypes.NewTestMetadata(), - EncryptionEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - SecondaryArtifactSettings: []codebuild.ArtifactSettings{ - { - Metadata: iacTypes.NewTestMetadata(), - EncryptionEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - { - Metadata: iacTypes.NewTestMetadata(), - EncryptionEnabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - { - name: "defaults - encryption enabled", - terraform: ` - resource "aws_codebuild_project" "codebuild" { - } -`, - expected: codebuild.Project{ - Metadata: iacTypes.NewTestMetadata(), - ArtifactSettings: codebuild.ArtifactSettings{ - Metadata: iacTypes.NewTestMetadata(), - EncryptionEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptProject(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_codebuild_project" "codebuild" { - artifacts { - encryption_disabled = false - } - - secondary_artifacts { - encryption_disabled = false - } - - secondary_artifacts { - encryption_disabled = true - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Projects, 1) - project := adapted.Projects[0] - - assert.Equal(t, 2, project.Metadata.Range().GetStartLine()) - assert.Equal(t, 14, project.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, project.ArtifactSettings.Metadata.Range().GetStartLine()) - assert.Equal(t, 5, project.ArtifactSettings.Metadata.Range().GetEndLine()) - - assert.Equal(t, 7, project.SecondaryArtifactSettings[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 9, project.SecondaryArtifactSettings[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 11, project.SecondaryArtifactSettings[1].Metadata.Range().GetStartLine()) - assert.Equal(t, 13, project.SecondaryArtifactSettings[1].Metadata.Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/config/adapt.go b/pkg/iac/adapters/terraform/aws/config/adapt.go deleted file mode 100644 index 643fbd86720c..000000000000 --- a/pkg/iac/adapters/terraform/aws/config/adapt.go +++ /dev/null @@ -1,33 +0,0 @@ -package config - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) config.Config { - return config.Config{ - ConfigurationAggregrator: adaptConfigurationAggregrator(modules), - } -} - -func adaptConfigurationAggregrator(modules terraform.Modules) config.ConfigurationAggregrator { - configurationAggregrator := config.ConfigurationAggregrator{ - Metadata: iacTypes.NewUnmanagedMetadata(), - SourceAllRegions: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - } - - for _, resource := range modules.GetResourcesByType("aws_config_configuration_aggregator") { - configurationAggregrator.Metadata = resource.GetMetadata() - aggregationBlock := resource.GetFirstMatchingBlock("account_aggregation_source", "organization_aggregation_source") - if aggregationBlock.IsNil() { - configurationAggregrator.SourceAllRegions = iacTypes.Bool(false, resource.GetMetadata()) - } else { - allRegionsAttr := aggregationBlock.GetAttribute("all_regions") - allRegionsVal := allRegionsAttr.AsBoolValueOrDefault(false, aggregationBlock) - configurationAggregrator.SourceAllRegions = allRegionsVal - } - } - return configurationAggregrator -} diff --git a/pkg/iac/adapters/terraform/aws/config/adapt_test.go b/pkg/iac/adapters/terraform/aws/config/adapt_test.go deleted file mode 100644 index 74cf720caa10..000000000000 --- a/pkg/iac/adapters/terraform/aws/config/adapt_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package config - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/config" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptConfigurationAggregrator(t *testing.T) { - tests := []struct { - name string - terraform string - expected config.ConfigurationAggregrator - }{ - { - name: "configured", - terraform: ` - resource "aws_config_configuration_aggregator" "example" { - name = "example" - - account_aggregation_source { - account_ids = ["123456789012"] - all_regions = true - } - } -`, - expected: config.ConfigurationAggregrator{ - Metadata: iacTypes.NewTestMetadata(), - SourceAllRegions: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_config_configuration_aggregator" "example" { - } -`, - expected: config.ConfigurationAggregrator{ - Metadata: iacTypes.NewTestMetadata(), - SourceAllRegions: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptConfigurationAggregrator(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_config_configuration_aggregator" "example" { - name = "example" - - account_aggregation_source { - account_ids = ["123456789012"] - all_regions = true - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - aggregator := adapted.ConfigurationAggregrator - - assert.Equal(t, 2, aggregator.Metadata.Range().GetStartLine()) - assert.Equal(t, 9, aggregator.Metadata.Range().GetEndLine()) - - assert.Equal(t, 7, aggregator.SourceAllRegions.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, aggregator.SourceAllRegions.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/documentdb/adapt.go b/pkg/iac/adapters/terraform/aws/documentdb/adapt.go deleted file mode 100644 index 5c10b5195c97..000000000000 --- a/pkg/iac/adapters/terraform/aws/documentdb/adapt.go +++ /dev/null @@ -1,63 +0,0 @@ -package documentdb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) documentdb.DocumentDB { - return documentdb.DocumentDB{ - Clusters: adaptClusters(modules), - } -} - -func adaptClusters(modules terraform.Modules) []documentdb.Cluster { - var clusters []documentdb.Cluster - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_docdb_cluster") { - clusters = append(clusters, adaptCluster(resource, module)) - } - } - return clusters -} - -func adaptCluster(resource *terraform.Block, module *terraform.Module) documentdb.Cluster { - identifierAttr := resource.GetAttribute("cluster_identifier") - identifierVal := identifierAttr.AsStringValueOrDefault("", resource) - - var enabledLogExports []types.StringValue - var instances []documentdb.Instance - - enabledLogExportsAttr := resource.GetAttribute("enabled_cloudwatch_logs_exports") - for _, logExport := range enabledLogExportsAttr.AsStringValues() { - enabledLogExports = append(enabledLogExports, logExport) - } - - instancesRes := module.GetReferencingResources(resource, "aws_docdb_cluster_instance", "cluster_identifier") - for _, instanceRes := range instancesRes { - keyIDAttr := instanceRes.GetAttribute("kms_key_id") - keyIDVal := keyIDAttr.AsStringValueOrDefault("", instanceRes) - - instances = append(instances, documentdb.Instance{ - Metadata: instanceRes.GetMetadata(), - KMSKeyID: keyIDVal, - }) - } - - storageEncryptedAttr := resource.GetAttribute("storage_encrypted") - storageEncryptedVal := storageEncryptedAttr.AsBoolValueOrDefault(false, resource) - - KMSKeyIDAttr := resource.GetAttribute("kms_key_id") - KMSKeyIDVal := KMSKeyIDAttr.AsStringValueOrDefault("", resource) - - return documentdb.Cluster{ - Metadata: resource.GetMetadata(), - Identifier: identifierVal, - EnabledLogExports: enabledLogExports, - BackupRetentionPeriod: resource.GetAttribute("backup_retention_period").AsIntValueOrDefault(0, resource), - Instances: instances, - StorageEncrypted: storageEncryptedVal, - KMSKeyID: KMSKeyIDVal, - } -} diff --git a/pkg/iac/adapters/terraform/aws/documentdb/adapt_test.go b/pkg/iac/adapters/terraform/aws/documentdb/adapt_test.go deleted file mode 100644 index 76ea0a8e3103..000000000000 --- a/pkg/iac/adapters/terraform/aws/documentdb/adapt_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package documentdb - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/documentdb" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected documentdb.Cluster - }{ - { - name: "configured", - terraform: ` - resource "aws_docdb_cluster" "docdb" { - cluster_identifier = "my-docdb-cluster" - kms_key_id = "kms-key" - enabled_cloudwatch_logs_exports = "audit" - storage_encrypted = true - } - - resource "aws_docdb_cluster_instance" "cluster_instances" { - count = 1 - identifier = "my-docdb-cluster" - cluster_identifier = aws_docdb_cluster.docdb.id - kms_key_id = "kms-key#1" - } -`, - expected: documentdb.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - Identifier: iacTypes.String("my-docdb-cluster", iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("kms-key", iacTypes.NewTestMetadata()), - EnabledLogExports: []iacTypes.StringValue{ - iacTypes.String("audit", iacTypes.NewTestMetadata()), - }, - Instances: []documentdb.Instance{ - { - Metadata: iacTypes.NewTestMetadata(), - KMSKeyID: iacTypes.String("kms-key#1", iacTypes.NewTestMetadata()), - }, - }, - StorageEncrypted: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_docdb_cluster" "docdb" { - } -`, - expected: documentdb.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - Identifier: iacTypes.String("", iacTypes.NewTestMetadata()), - StorageEncrypted: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptCluster(modules.GetBlocks()[0], modules[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_docdb_cluster" "docdb" { - cluster_identifier = "my-docdb-cluster" - kms_key_id = "kms-key" - enabled_cloudwatch_logs_exports = "audit" - storage_encrypted = true - } - - resource "aws_docdb_cluster_instance" "cluster_instances" { - count = 1 - identifier = "my-docdb-cluster" - cluster_identifier = aws_docdb_cluster.docdb.id - kms_key_id = "kms-key" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Clusters, 1) - require.Len(t, adapted.Clusters[0].Instances, 1) - - cluster := adapted.Clusters[0] - instance := cluster.Instances[0] - - assert.Equal(t, 2, cluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 7, cluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, cluster.Identifier.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, cluster.Identifier.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, cluster.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, cluster.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, cluster.EnabledLogExports[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, cluster.EnabledLogExports[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, cluster.StorageEncrypted.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, cluster.StorageEncrypted.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 9, instance.Metadata.Range().GetStartLine()) - assert.Equal(t, 14, instance.Metadata.Range().GetEndLine()) - - assert.Equal(t, 13, instance.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, instance.KMSKeyID.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/dynamodb/adapt.go b/pkg/iac/adapters/terraform/aws/dynamodb/adapt.go deleted file mode 100644 index c4c1582c807b..000000000000 --- a/pkg/iac/adapters/terraform/aws/dynamodb/adapt.go +++ /dev/null @@ -1,94 +0,0 @@ -package dynamodb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) dynamodb.DynamoDB { - return dynamodb.DynamoDB{ - DAXClusters: adaptClusters(modules), - Tables: adaptTables(modules), - } -} - -func adaptClusters(modules terraform.Modules) []dynamodb.DAXCluster { - var clusters []dynamodb.DAXCluster - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_dax_cluster") { - clusters = append(clusters, adaptCluster(resource, module)) - } - } - return clusters -} - -func adaptTables(modules terraform.Modules) []dynamodb.Table { - var tables []dynamodb.Table - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_dynamodb_table") { - tables = append(tables, adaptTable(resource, module)) - } - } - return tables -} - -func adaptCluster(resource *terraform.Block, module *terraform.Module) dynamodb.DAXCluster { - - cluster := dynamodb.DAXCluster{ - Metadata: resource.GetMetadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - KMSKeyID: iacTypes.StringDefault("", resource.GetMetadata()), - }, - PointInTimeRecovery: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - if ssEncryptionBlock := resource.GetBlock("server_side_encryption"); ssEncryptionBlock.IsNotNil() { - cluster.ServerSideEncryption.Metadata = ssEncryptionBlock.GetMetadata() - enabledAttr := ssEncryptionBlock.GetAttribute("enabled") - cluster.ServerSideEncryption.Enabled = enabledAttr.AsBoolValueOrDefault(false, ssEncryptionBlock) - } - - if recoveryBlock := resource.GetBlock("point_in_time_recovery"); recoveryBlock.IsNotNil() { - recoveryEnabledAttr := recoveryBlock.GetAttribute("enabled") - cluster.PointInTimeRecovery = recoveryEnabledAttr.AsBoolValueOrDefault(false, recoveryBlock) - } - - return cluster -} - -func adaptTable(resource *terraform.Block, module *terraform.Module) dynamodb.Table { - - table := dynamodb.Table{ - Metadata: resource.GetMetadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - KMSKeyID: iacTypes.StringDefault("", resource.GetMetadata()), - }, - PointInTimeRecovery: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - if ssEncryptionBlock := resource.GetBlock("server_side_encryption"); ssEncryptionBlock.IsNotNil() { - table.ServerSideEncryption.Metadata = ssEncryptionBlock.GetMetadata() - enabledAttr := ssEncryptionBlock.GetAttribute("enabled") - table.ServerSideEncryption.Enabled = enabledAttr.AsBoolValueOrDefault(false, ssEncryptionBlock) - - kmsKeyIdAttr := ssEncryptionBlock.GetAttribute("kms_key_arn") - table.ServerSideEncryption.KMSKeyID = kmsKeyIdAttr.AsStringValueOrDefault("alias/aws/dynamodb", ssEncryptionBlock) - - kmsBlock, err := module.GetReferencedBlock(kmsKeyIdAttr, resource) - if err == nil && kmsBlock.IsNotNil() { - table.ServerSideEncryption.KMSKeyID = iacTypes.String(kmsBlock.FullName(), kmsBlock.GetMetadata()) - } - } - - if recoveryBlock := resource.GetBlock("point_in_time_recovery"); recoveryBlock.IsNotNil() { - recoveryEnabledAttr := recoveryBlock.GetAttribute("enabled") - table.PointInTimeRecovery = recoveryEnabledAttr.AsBoolValueOrDefault(false, recoveryBlock) - } - - return table -} diff --git a/pkg/iac/adapters/terraform/aws/dynamodb/adapt_test.go b/pkg/iac/adapters/terraform/aws/dynamodb/adapt_test.go deleted file mode 100644 index 72f6e8ed4ac2..000000000000 --- a/pkg/iac/adapters/terraform/aws/dynamodb/adapt_test.go +++ /dev/null @@ -1,174 +0,0 @@ -package dynamodb - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/dynamodb" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected dynamodb.DAXCluster - }{ - { - name: "cluster", - terraform: ` - resource "aws_dax_cluster" "example" { - server_side_encryption { - enabled = true - } - } -`, - expected: dynamodb.DAXCluster{ - Metadata: iacTypes.NewTestMetadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - PointInTimeRecovery: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptCluster(modules.GetBlocks()[0], modules[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptTable(t *testing.T) { - tests := []struct { - name string - terraform string - expected dynamodb.Table - }{ - { - name: "table", - terraform: ` - resource "aws_dynamodb_table" "example" { - name = "example" - - server_side_encryption { - enabled = true - kms_key_arn = "key-string" - } - - point_in_time_recovery { - enabled = true - } - } -`, - expected: dynamodb.Table{ - Metadata: iacTypes.NewTestMetadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("key-string", iacTypes.NewTestMetadata()), - }, - PointInTimeRecovery: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "table no kms", - terraform: ` - resource "aws_dax_cluster" "example" { - server_side_encryption { - enabled = true - } - } -`, - expected: dynamodb.Table{ - Metadata: iacTypes.NewTestMetadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("alias/aws/dynamodb", iacTypes.NewTestMetadata()), - }, - PointInTimeRecovery: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "reference key", - terraform: ` - resource "aws_dynamodb_table" "example" { - name = "example" - - server_side_encryption { - enabled = true - kms_key_arn = aws_kms_key.a.arn - } - } - - resource "aws_kms_key" "a" { - } -`, - expected: dynamodb.Table{ - Metadata: iacTypes.NewTestMetadata(), - ServerSideEncryption: dynamodb.ServerSideEncryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("aws_kms_key.a", iacTypes.NewTestMetadata()), - }, - PointInTimeRecovery: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptTable(modules.GetBlocks()[0], modules[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_dynamodb_table" "example" { - name = "example" - - server_side_encryption { - enabled = true - kms_key_arn = "key-string" - } - - point_in_time_recovery { - enabled = true - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Empty(t, adapted.DAXClusters) - require.Len(t, adapted.Tables, 1) - table := adapted.Tables[0] - - assert.Equal(t, 2, table.Metadata.Range().GetStartLine()) - assert.Equal(t, 13, table.Metadata.Range().GetEndLine()) - - assert.Equal(t, 5, table.ServerSideEncryption.Metadata.Range().GetStartLine()) - assert.Equal(t, 8, table.ServerSideEncryption.Metadata.Range().GetEndLine()) - - assert.Equal(t, 6, table.ServerSideEncryption.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, table.ServerSideEncryption.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 7, table.ServerSideEncryption.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, table.ServerSideEncryption.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, table.PointInTimeRecovery.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, table.PointInTimeRecovery.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/ec2/adapt.go b/pkg/iac/adapters/terraform/aws/ec2/adapt.go deleted file mode 100644 index 6b02431f3772..000000000000 --- a/pkg/iac/adapters/terraform/aws/ec2/adapt.go +++ /dev/null @@ -1,102 +0,0 @@ -package ec2 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) ec2.EC2 { - - naclAdapter := naclAdapter{naclRuleIDs: modules.GetChildResourceIDMapByType("aws_network_acl_rule")} - sgAdapter := sgAdapter{sgRuleIDs: modules.GetChildResourceIDMapByType("aws_security_group_rule")} - - return ec2.EC2{ - Instances: getInstances(modules), - VPCs: adaptVPCs(modules), - SecurityGroups: sgAdapter.adaptSecurityGroups(modules), - Subnets: adaptSubnets(modules), - NetworkACLs: naclAdapter.adaptNetworkACLs(modules), - LaunchConfigurations: adaptLaunchConfigurations(modules), - LaunchTemplates: adaptLaunchTemplates(modules), - Volumes: adaptVolumes(modules), - } -} - -func getInstances(modules terraform.Modules) []ec2.Instance { - var instances []ec2.Instance - - blocks := modules.GetResourcesByType("aws_instance") - - for _, b := range blocks { - instance := ec2.Instance{ - Metadata: b.GetMetadata(), - MetadataOptions: getMetadataOptions(b), - UserData: b.GetAttribute("user_data").AsStringValueOrDefault("", b), - } - - if launchTemplate := findRelatedLaunchTemplate(modules, b); launchTemplate != nil { - instance = launchTemplate.Instance - } - - if instance.RootBlockDevice == nil { - instance.RootBlockDevice = &ec2.BlockDevice{ - Metadata: b.GetMetadata(), - Encrypted: types.BoolDefault(false, b.GetMetadata()), - } - } - - if rootBlockDevice := b.GetBlock("root_block_device"); rootBlockDevice.IsNotNil() { - instance.RootBlockDevice = &ec2.BlockDevice{ - Metadata: rootBlockDevice.GetMetadata(), - Encrypted: rootBlockDevice.GetAttribute("encrypted").AsBoolValueOrDefault(false, b), - } - } - - for _, ebsBlock := range b.GetBlocks("ebs_block_device") { - instance.EBSBlockDevices = append(instance.EBSBlockDevices, &ec2.BlockDevice{ - Metadata: ebsBlock.GetMetadata(), - Encrypted: ebsBlock.GetAttribute("encrypted").AsBoolValueOrDefault(false, b), - }) - } - - for _, resource := range modules.GetResourcesByType("aws_ebs_encryption_by_default") { - if resource.GetAttribute("enabled").NotEqual(false) { - instance.RootBlockDevice.Encrypted = types.BoolDefault(true, resource.GetMetadata()) - for i := 0; i < len(instance.EBSBlockDevices); i++ { - ebs := instance.EBSBlockDevices[i] - ebs.Encrypted = types.BoolDefault(true, resource.GetMetadata()) - } - } - } - - instances = append(instances, instance) - } - - return instances -} - -func findRelatedLaunchTemplate(modules terraform.Modules, instanceBlock *terraform.Block) *ec2.LaunchTemplate { - launchTemplateBlock := instanceBlock.GetBlock("launch_template") - if launchTemplateBlock.IsNil() { - return nil - } - - templateRef := launchTemplateBlock.GetAttribute("name") - - if !templateRef.IsResolvable() { - templateRef = launchTemplateBlock.GetAttribute("id") - } - - if templateRef.IsString() { - for _, r := range modules.GetResourcesByType("aws_launch_template") { - templateName := r.GetAttribute("name").AsStringValueOrDefault("", r).Value() - if templateRef.Equals(r.ID()) || templateRef.Equals(templateName) { - launchTemplate := adaptLaunchTemplate(r) - return &launchTemplate - } - } - } - - return nil -} diff --git a/pkg/iac/adapters/terraform/aws/ec2/adapt_test.go b/pkg/iac/adapters/terraform/aws/ec2/adapt_test.go deleted file mode 100644 index 665852321db4..000000000000 --- a/pkg/iac/adapters/terraform/aws/ec2/adapt_test.go +++ /dev/null @@ -1,253 +0,0 @@ -package ec2 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected ec2.EC2 - }{ - { - name: "configured", - terraform: ` - resource "aws_instance" "example" { - ami = "ami-7f89a64f" - instance_type = "t1.micro" - - root_block_device { - encrypted = true - } - - metadata_options { - http_tokens = "required" - http_endpoint = "disabled" - } - - ebs_block_device { - encrypted = true - } - - user_data = < 0 { - orphanage := ec2.SecurityGroup{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Description: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - IngressRules: nil, - EgressRules: nil, - IsDefault: iacTypes.BoolUnresolvable(iacTypes.NewUnmanagedMetadata()), - VPCID: iacTypes.StringUnresolvable(iacTypes.NewUnmanagedMetadata()), - } - for _, sgRule := range orphanResources { - if sgRule.GetAttribute("type").Equals("ingress") { - orphanage.IngressRules = append(orphanage.IngressRules, adaptSGRule(sgRule)) - } else if sgRule.GetAttribute("type").Equals("egress") { - orphanage.EgressRules = append(orphanage.EgressRules, adaptSGRule(sgRule)) - } - } - securityGroups = append(securityGroups, orphanage) - } - - return securityGroups -} - -func (a *naclAdapter) adaptNetworkACLs(modules terraform.Modules) []ec2.NetworkACL { - var networkACLs []ec2.NetworkACL - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_network_acl") { - networkACLs = append(networkACLs, a.adaptNetworkACL(resource, module)) - } - } - - orphanResources := modules.GetResourceByIDs(a.naclRuleIDs.Orphans()...) - if len(orphanResources) > 0 { - orphanage := ec2.NetworkACL{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Rules: nil, - IsDefaultRule: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - } - for _, naclRule := range orphanResources { - orphanage.Rules = append(orphanage.Rules, adaptNetworkACLRule(naclRule)) - } - networkACLs = append(networkACLs, orphanage) - } - - return networkACLs -} - -func (a *sgAdapter) adaptSecurityGroup(resource *terraform.Block, module terraform.Modules) ec2.SecurityGroup { - var ingressRules []ec2.SecurityGroupRule - var egressRules []ec2.SecurityGroupRule - - descriptionAttr := resource.GetAttribute("description") - descriptionVal := descriptionAttr.AsStringValueOrDefault("Managed by Terraform", resource) - - ingressBlocks := resource.GetBlocks("ingress") - for _, ingressBlock := range ingressBlocks { - ingressRules = append(ingressRules, adaptSGRule(ingressBlock)) - } - - egressBlocks := resource.GetBlocks("egress") - for _, egressBlock := range egressBlocks { - egressRules = append(egressRules, adaptSGRule(egressBlock)) - } - - rulesBlocks := module.GetReferencingResources(resource, "aws_security_group_rule", "security_group_id") - for _, ruleBlock := range rulesBlocks { - a.sgRuleIDs.Resolve(ruleBlock.ID()) - if ruleBlock.GetAttribute("type").Equals("ingress") { - ingressRules = append(ingressRules, adaptSGRule(ruleBlock)) - } else if ruleBlock.GetAttribute("type").Equals("egress") { - egressRules = append(egressRules, adaptSGRule(ruleBlock)) - } - } - - for _, r := range module.GetReferencingResources(resource, "aws_vpc_security_group_ingress_rule", "security_group_id") { - a.sgRuleIDs.Resolve(r.ID()) - ingressRules = append(ingressRules, adaptSingleSGRule(r)) - } - - for _, r := range module.GetReferencingResources(resource, "aws_vpc_security_group_egress_rule", "security_group_id") { - a.sgRuleIDs.Resolve(r.ID()) - egressRules = append(egressRules, adaptSingleSGRule(r)) - } - - return ec2.SecurityGroup{ - Metadata: resource.GetMetadata(), - Description: descriptionVal, - IngressRules: ingressRules, - EgressRules: egressRules, - IsDefault: iacTypes.Bool(false, iacTypes.NewUnmanagedMetadata()), - VPCID: resource.GetAttribute("vpc_id").AsStringValueOrDefault("", resource), - } -} - -func adaptSGRule(resource *terraform.Block) ec2.SecurityGroupRule { - ruleDescAttr := resource.GetAttribute("description") - ruleDescVal := ruleDescAttr.AsStringValueOrDefault("", resource) - - var cidrs []iacTypes.StringValue - - cidrBlocks := resource.GetAttribute("cidr_blocks") - ipv6cidrBlocks := resource.GetAttribute("ipv6_cidr_blocks") - - if cidrBlocks.IsNotNil() { - cidrs = cidrBlocks.AsStringValues() - } - - if ipv6cidrBlocks.IsNotNil() { - cidrs = append(cidrs, ipv6cidrBlocks.AsStringValues()...) - } - - return ec2.SecurityGroupRule{ - Metadata: resource.GetMetadata(), - Description: ruleDescVal, - CIDRs: cidrs, - FromPort: resource.GetAttribute("from_port").AsIntValueOrDefault(-1, resource), - ToPort: resource.GetAttribute("to_port").AsIntValueOrDefault(-1, resource), - Protocol: resource.GetAttribute("protocol").AsStringValueOrDefault("", resource), - } -} - -func adaptSingleSGRule(resource *terraform.Block) ec2.SecurityGroupRule { - description := resource.GetAttribute("description").AsStringValueOrDefault("", resource) - - var cidrs []iacTypes.StringValue - if ipv4 := resource.GetAttribute("cidr_ipv4"); ipv4.IsNotNil() { - cidrs = append(cidrs, ipv4.AsStringValueOrDefault("", resource)) - } - if ipv6 := resource.GetAttribute("cidr_ipv6"); ipv6.IsNotNil() { - cidrs = append(cidrs, ipv6.AsStringValueOrDefault("", resource)) - } - - return ec2.SecurityGroupRule{ - Metadata: resource.GetMetadata(), - Description: description, - CIDRs: cidrs, - FromPort: resource.GetAttribute("from_port").AsIntValueOrDefault(-1, resource), - ToPort: resource.GetAttribute("to_port").AsIntValueOrDefault(-1, resource), - Protocol: resource.GetAttribute("ip_protocol").AsStringValueOrDefault("", resource), - } -} - -func (a *naclAdapter) adaptNetworkACL(resource *terraform.Block, module *terraform.Module) ec2.NetworkACL { - var networkRules []ec2.NetworkACLRule - rulesBlocks := module.GetReferencingResources(resource, "aws_network_acl_rule", "network_acl_id") - for _, ruleBlock := range rulesBlocks { - a.naclRuleIDs.Resolve(ruleBlock.ID()) - networkRules = append(networkRules, adaptNetworkACLRule(ruleBlock)) - } - return ec2.NetworkACL{ - Metadata: resource.GetMetadata(), - Rules: networkRules, - IsDefaultRule: iacTypes.BoolDefault(false, resource.GetMetadata()), - } -} - -func adaptNetworkACLRule(resource *terraform.Block) ec2.NetworkACLRule { - var cidrs []iacTypes.StringValue - - typeVal := iacTypes.StringDefault("ingress", resource.GetMetadata()) - - egressAtrr := resource.GetAttribute("egress") - if egressAtrr.IsTrue() { - typeVal = iacTypes.String("egress", egressAtrr.GetMetadata()) - } else if egressAtrr.IsNotNil() { - typeVal = iacTypes.String("ingress", egressAtrr.GetMetadata()) - } - - actionAttr := resource.GetAttribute("rule_action") - actionVal := actionAttr.AsStringValueOrDefault("", resource) - - protocolAtrr := resource.GetAttribute("protocol") - protocolVal := protocolAtrr.AsStringValueOrDefault("", resource) - - cidrAttr := resource.GetAttribute("cidr_block") - if cidrAttr.IsNotNil() { - cidrs = append(cidrs, cidrAttr.AsStringValueOrDefault("", resource)) - } - ipv4cidrAttr := resource.GetAttribute("ipv6_cidr_block") - if ipv4cidrAttr.IsNotNil() { - cidrs = append(cidrs, ipv4cidrAttr.AsStringValueOrDefault("", resource)) - } - - return ec2.NetworkACLRule{ - Metadata: resource.GetMetadata(), - Type: typeVal, - Action: actionVal, - Protocol: protocolVal, - CIDRs: cidrs, - FromPort: resource.GetAttribute("from_port").AsIntValueOrDefault(-1, resource), - ToPort: resource.GetAttribute("to_port").AsIntValueOrDefault(-1, resource), - } -} diff --git a/pkg/iac/adapters/terraform/aws/ec2/vpc_test.go b/pkg/iac/adapters/terraform/aws/ec2/vpc_test.go deleted file mode 100644 index 1acee8146d3e..000000000000 --- a/pkg/iac/adapters/terraform/aws/ec2/vpc_test.go +++ /dev/null @@ -1,403 +0,0 @@ -package ec2 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_AdaptVPC(t *testing.T) { - tests := []struct { - name string - terraform string - expected ec2.EC2 - }{ - { - name: "defined", - terraform: ` - resource "aws_flow_log" "this" { - vpc_id = aws_vpc.main.id - } - resource "aws_default_vpc" "default" { - tags = { - Name = "Default VPC" - } - } - - resource "aws_vpc" "main" { - cidr_block = "4.5.6.7/32" - } - - resource "aws_security_group" "example" { - name = "http" - description = "Allow inbound HTTP traffic" - - ingress { - description = "Rule #1" - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = [aws_vpc.main.cidr_block] - } - - egress { - cidr_blocks = ["1.2.3.4/32"] - } - } - - resource "aws_network_acl_rule" "example" { - egress = false - protocol = "tcp" - from_port = 22 - to_port = 22 - rule_action = "allow" - cidr_block = "10.0.0.0/16" - } - - resource "aws_security_group_rule" "example" { - type = "ingress" - description = "Rule #2" - security_group_id = aws_security_group.example.id - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = [ - "1.2.3.4/32", - "4.5.6.7/32", - ] - } -`, - expected: ec2.EC2{ - VPCs: []ec2.VPC{ - { - Metadata: iacTypes.NewTestMetadata(), - IsDefault: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - ID: iacTypes.String("", iacTypes.NewTestMetadata()), - FlowLogsEnabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - { - Metadata: iacTypes.NewTestMetadata(), - IsDefault: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - ID: iacTypes.String("", iacTypes.NewTestMetadata()), - FlowLogsEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - SecurityGroups: []ec2.SecurityGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("Allow inbound HTTP traffic", iacTypes.NewTestMetadata()), - IsDefault: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - VPCID: iacTypes.String("", iacTypes.NewTestMetadata()), - IngressRules: []ec2.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - - Description: iacTypes.String("Rule #1", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("4.5.6.7/32", iacTypes.NewTestMetadata()), - }, - FromPort: iacTypes.IntTest(80), - ToPort: iacTypes.IntTest(80), - Protocol: iacTypes.StringTest("tcp"), - }, - { - Metadata: iacTypes.NewTestMetadata(), - - Description: iacTypes.String("Rule #2", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("1.2.3.4/32", iacTypes.NewTestMetadata()), - iacTypes.String("4.5.6.7/32", iacTypes.NewTestMetadata()), - }, - FromPort: iacTypes.IntTest(22), - ToPort: iacTypes.IntTest(22), - Protocol: iacTypes.StringTest("tcp"), - }, - }, - - EgressRules: []ec2.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("1.2.3.4/32", iacTypes.NewTestMetadata()), - }, - FromPort: iacTypes.IntTest(-1), - ToPort: iacTypes.IntTest(-1), - }, - }, - }, - }, - NetworkACLs: []ec2.NetworkACL{ - { - Metadata: iacTypes.NewTestMetadata(), - Rules: []ec2.NetworkACLRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("ingress", iacTypes.NewTestMetadata()), - Action: iacTypes.String("allow", iacTypes.NewTestMetadata()), - Protocol: iacTypes.String("tcp", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("10.0.0.0/16", iacTypes.NewTestMetadata()), - }, - FromPort: iacTypes.IntTest(22), - ToPort: iacTypes.IntTest(22), - }, - }, - IsDefaultRule: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_security_group" "example" { - ingress { - } - - egress { - } - } - - resource "aws_network_acl_rule" "example" { - } -`, - expected: ec2.EC2{ - SecurityGroups: []ec2.SecurityGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("Managed by Terraform", iacTypes.NewTestMetadata()), - IsDefault: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - VPCID: iacTypes.String("", iacTypes.NewTestMetadata()), - IngressRules: []ec2.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - FromPort: iacTypes.IntTest(-1), - ToPort: iacTypes.IntTest(-1), - }, - }, - - EgressRules: []ec2.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - FromPort: iacTypes.IntTest(-1), - ToPort: iacTypes.IntTest(-1), - }, - }, - }, - }, - NetworkACLs: []ec2.NetworkACL{ - { - Metadata: iacTypes.NewTestMetadata(), - Rules: []ec2.NetworkACLRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("ingress", iacTypes.NewTestMetadata()), - Action: iacTypes.String("", iacTypes.NewTestMetadata()), - Protocol: iacTypes.String("", iacTypes.NewTestMetadata()), - FromPort: iacTypes.IntTest(-1), - ToPort: iacTypes.IntTest(-1), - }, - }, - IsDefaultRule: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - { - name: "aws_flow_log refer to locals", - terraform: ` -locals { - vpc_id = try(aws_vpc.this.id, "") -} - -resource "aws_vpc" "this" { -} - -resource "aws_flow_log" "this" { - vpc_id = local.vpc_id -} -`, - expected: ec2.EC2{ - VPCs: []ec2.VPC{ - { - Metadata: iacTypes.NewTestMetadata(), - IsDefault: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - ID: iacTypes.String("", iacTypes.NewTestMetadata()), - FlowLogsEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - { - name: "ingress and egress rules", - terraform: ` -resource "aws_security_group" "example" { - name = "example" - description = "example" -} - -resource "aws_vpc_security_group_egress_rule" "test" { - security_group_id = aws_security_group.example.id - cidr_ipv4 = "0.0.0.0/0" - ip_protocol = "-1" # semantically equivalent to all ports -} - -resource "aws_vpc_security_group_ingress_rule" "test" { - security_group_id = aws_security_group.example.id - cidr_ipv4 = "0.0.0.0/0" - from_port = "22" - to_port = "22" - ip_protocol = "tcp" -} -`, - expected: ec2.EC2{ - SecurityGroups: []ec2.SecurityGroup{ - { - Description: iacTypes.StringTest("example"), - IngressRules: []ec2.SecurityGroupRule{ - { - CIDRs: []iacTypes.StringValue{ - iacTypes.StringTest("0.0.0.0/0"), - }, - Protocol: iacTypes.StringTest("tcp"), - FromPort: iacTypes.IntTest(22), - ToPort: iacTypes.IntTest(22), - }, - }, - EgressRules: []ec2.SecurityGroupRule{ - { - CIDRs: []iacTypes.StringValue{ - iacTypes.StringTest("0.0.0.0/0"), - }, - Protocol: iacTypes.StringTest("-1"), - FromPort: iacTypes.IntTest(-1), - ToPort: iacTypes.IntTest(-1), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestVPCLines(t *testing.T) { - src := ` - resource "aws_default_vpc" "default" { - } - - resource "aws_vpc" "main" { - cidr_block = "4.5.6.7/32" - } - - resource "aws_security_group" "example" { - name = "http" - description = "Allow inbound HTTP traffic" - - ingress { - description = "HTTP from VPC" - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = [aws_vpc.main.cidr_block] - } - - egress { - cidr_blocks = ["1.2.3.4/32"] - } - } - - resource "aws_security_group_rule" "example" { - type = "ingress" - security_group_id = aws_security_group.example.id - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = [ - "1.2.3.4/32", - "4.5.6.7/32", - ] - } - - resource "aws_network_acl_rule" "example" { - egress = false - protocol = "tcp" - from_port = 22 - to_port = 22 - rule_action = "allow" - cidr_block = "10.0.0.0/16" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.VPCs, 2) - require.Len(t, adapted.SecurityGroups, 1) - require.Len(t, adapted.NetworkACLs, 1) - - defaultVPC := adapted.VPCs[0] - securityGroup := adapted.SecurityGroups[0] - networkACL := adapted.NetworkACLs[0] - - assert.Equal(t, 2, defaultVPC.Metadata.Range().GetStartLine()) - assert.Equal(t, 3, defaultVPC.Metadata.Range().GetEndLine()) - - assert.Equal(t, 9, securityGroup.Metadata.Range().GetStartLine()) - assert.Equal(t, 24, securityGroup.Metadata.Range().GetEndLine()) - - assert.Equal(t, 11, securityGroup.Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, securityGroup.Description.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, securityGroup.IngressRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 19, securityGroup.IngressRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 14, securityGroup.IngressRules[0].Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, securityGroup.IngressRules[0].Description.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 18, securityGroup.IngressRules[0].CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 18, securityGroup.IngressRules[0].CIDRs[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 26, securityGroup.IngressRules[1].Metadata.Range().GetStartLine()) - assert.Equal(t, 36, securityGroup.IngressRules[1].Metadata.Range().GetEndLine()) - - assert.Equal(t, 32, securityGroup.IngressRules[1].CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 35, securityGroup.IngressRules[1].CIDRs[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 21, securityGroup.EgressRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 23, securityGroup.EgressRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 22, securityGroup.EgressRules[0].CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 22, securityGroup.EgressRules[0].CIDRs[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 38, networkACL.Rules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 45, networkACL.Rules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 39, networkACL.Rules[0].Type.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 39, networkACL.Rules[0].Type.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 40, networkACL.Rules[0].Protocol.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 40, networkACL.Rules[0].Protocol.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 43, networkACL.Rules[0].Action.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 43, networkACL.Rules[0].Action.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 44, networkACL.Rules[0].CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 44, networkACL.Rules[0].CIDRs[0].GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/ecr/adapt.go b/pkg/iac/adapters/terraform/aws/ecr/adapt.go deleted file mode 100644 index 90a66ba098e1..000000000000 --- a/pkg/iac/adapters/terraform/aws/ecr/adapt.go +++ /dev/null @@ -1,113 +0,0 @@ -package ecr - -import ( - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - iamp "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) ecr.ECR { - return ecr.ECR{ - Repositories: adaptRepositories(modules), - } -} - -func adaptRepositories(modules terraform.Modules) []ecr.Repository { - var repositories []ecr.Repository - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_ecr_repository") { - repositories = append(repositories, adaptRepository(resource, module, modules)) - } - } - return repositories -} - -func adaptRepository(resource *terraform.Block, module *terraform.Module, modules terraform.Modules) ecr.Repository { - repo := ecr.Repository{ - Metadata: resource.GetMetadata(), - ImageScanning: ecr.ImageScanning{ - Metadata: resource.GetMetadata(), - ScanOnPush: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - ImageTagsImmutable: iacTypes.BoolDefault(false, resource.GetMetadata()), - Policies: nil, - Encryption: ecr.Encryption{ - Metadata: resource.GetMetadata(), - Type: iacTypes.StringDefault("AES256", resource.GetMetadata()), - KMSKeyID: iacTypes.StringDefault("", resource.GetMetadata()), - }, - } - - if imageScanningBlock := resource.GetBlock("image_scanning_configuration"); imageScanningBlock.IsNotNil() { - repo.ImageScanning.Metadata = imageScanningBlock.GetMetadata() - scanOnPushAttr := imageScanningBlock.GetAttribute("scan_on_push") - repo.ImageScanning.ScanOnPush = scanOnPushAttr.AsBoolValueOrDefault(false, imageScanningBlock) - } - - mutabilityAttr := resource.GetAttribute("image_tag_mutability") - if mutabilityAttr.Equals("IMMUTABLE") { - repo.ImageTagsImmutable = iacTypes.Bool(true, mutabilityAttr.GetMetadata()) - } else if mutabilityAttr.Equals("MUTABLE") { - repo.ImageTagsImmutable = iacTypes.Bool(false, mutabilityAttr.GetMetadata()) - } - - policyBlocks := module.GetReferencingResources(resource, "aws_ecr_repository_policy", "repository") - for _, policyRes := range policyBlocks { - if policyAttr := policyRes.GetAttribute("policy"); policyAttr.IsString() { - - dataBlock, err := module.GetBlockByID(policyAttr.Value().AsString()) - if err != nil { - - parsed, err := iamgo.ParseString(policyAttr.Value().AsString()) - if err != nil { - continue - } - - policy := iamp.Policy{ - Metadata: policyRes.GetMetadata(), - Name: iacTypes.StringDefault("", policyRes.GetMetadata()), - Document: iamp.Document{ - Parsed: *parsed, - Metadata: policyAttr.GetMetadata(), - }, - Builtin: iacTypes.Bool(false, policyRes.GetMetadata()), - } - - repo.Policies = append(repo.Policies, policy) - } else if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" { - if doc, err := iam.ConvertTerraformDocument(modules, dataBlock); err == nil { - policy := iamp.Policy{ - Metadata: policyRes.GetMetadata(), - Name: iacTypes.StringDefault("", policyRes.GetMetadata()), - Document: iamp.Document{ - Parsed: doc.Document, - Metadata: doc.Source.GetMetadata(), - IsOffset: true, - }, - Builtin: iacTypes.Bool(false, policyRes.GetMetadata()), - } - repo.Policies = append(repo.Policies, policy) - } - } - } - } - - if encryptBlock := resource.GetBlock("encryption_configuration"); encryptBlock.IsNotNil() { - repo.Encryption.Metadata = encryptBlock.GetMetadata() - encryptionTypeAttr := encryptBlock.GetAttribute("encryption_type") - repo.Encryption.Type = encryptionTypeAttr.AsStringValueOrDefault("AES256", encryptBlock) - - kmsKeyAttr := encryptBlock.GetAttribute("kms_key") - repo.Encryption.KMSKeyID = kmsKeyAttr.AsStringValueOrDefault("", encryptBlock) - if kmsKeyAttr.IsResourceBlockReference("aws_kms_key") { - if keyBlock, err := module.GetReferencedBlock(kmsKeyAttr, encryptBlock); err == nil { - repo.Encryption.KMSKeyID = iacTypes.String(keyBlock.FullName(), keyBlock.GetMetadata()) - } - } - } - - return repo -} diff --git a/pkg/iac/adapters/terraform/aws/ecr/adapt_test.go b/pkg/iac/adapters/terraform/aws/ecr/adapt_test.go deleted file mode 100644 index 71904d8932e4..000000000000 --- a/pkg/iac/adapters/terraform/aws/ecr/adapt_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package ecr - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptRepository(t *testing.T) { - tests := []struct { - name string - terraform string - expected ecr.Repository - }{ - { - name: "configured", - terraform: ` - resource "aws_kms_key" "ecr_kms" { - enable_key_rotation = true - } - - resource "aws_ecr_repository" "foo" { - name = "bar" - image_tag_mutability = "MUTABLE" - - image_scanning_configuration { - scan_on_push = true - } - - encryption_configuration { - encryption_type = "KMS" - kms_key = aws_kms_key.ecr_kms.key_id - } - } - - resource "aws_ecr_repository_policy" "foopolicy" { - repository = aws_ecr_repository.foo.name - - policy = < 0 { - var volumes []ecs.Volume - for _, volumeBlock := range volumeBlocks { - volumes = append(volumes, ecs.Volume{ - Metadata: volumeBlock.GetMetadata(), - EFSVolumeConfiguration: adaptEFSVolumeConfiguration(volumeBlock), - }) - } - return volumes - } - - return []ecs.Volume{} -} - -func adaptEFSVolumeConfiguration(volumeBlock *terraform.Block) ecs.EFSVolumeConfiguration { - EFSVolumeConfiguration := ecs.EFSVolumeConfiguration{ - Metadata: volumeBlock.GetMetadata(), - TransitEncryptionEnabled: types.BoolDefault(true, volumeBlock.GetMetadata()), - } - - if EFSConfigBlock := volumeBlock.GetBlock("efs_volume_configuration"); EFSConfigBlock.IsNotNil() { - EFSVolumeConfiguration.Metadata = EFSConfigBlock.GetMetadata() - transitEncryptionAttr := EFSConfigBlock.GetAttribute("transit_encryption") - EFSVolumeConfiguration.TransitEncryptionEnabled = types.Bool(transitEncryptionAttr.Equals("ENABLED"), EFSConfigBlock.GetMetadata()) - if transitEncryptionAttr.IsNotNil() { - EFSVolumeConfiguration.TransitEncryptionEnabled = types.Bool(transitEncryptionAttr.Equals("ENABLED"), transitEncryptionAttr.GetMetadata()) - } - } - - return EFSVolumeConfiguration -} diff --git a/pkg/iac/adapters/terraform/aws/ecs/adapt_test.go b/pkg/iac/adapters/terraform/aws/ecs/adapt_test.go deleted file mode 100644 index 72facc1c1b63..000000000000 --- a/pkg/iac/adapters/terraform/aws/ecs/adapt_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package ecs - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecs" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptClusterSettings(t *testing.T) { - tests := []struct { - name string - terraform string - expected ecs.ClusterSettings - }{ - { - name: "container insights enabled", - terraform: ` - resource "aws_ecs_cluster" "example" { - name = "services-cluster" - - setting { - name = "containerInsights" - value = "enabled" - } - } -`, - expected: ecs.ClusterSettings{ - Metadata: iacTypes.NewTestMetadata(), - ContainerInsightsEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "container insights enhanced", - terraform: ` - resource "aws_ecs_cluster" "example" { - name = "services-cluster" - - setting { - name = "containerInsights" - value = "enhanced" - } - } -`, - expected: ecs.ClusterSettings{ - Metadata: iacTypes.NewTestMetadata(), - ContainerInsightsEnabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "invalid name", - terraform: ` - resource "aws_ecs_cluster" "example" { - name = "services-cluster" - - setting { - name = "invalidName" - value = "enabled" - } - } -`, - expected: ecs.ClusterSettings{ - Metadata: iacTypes.NewTestMetadata(), - ContainerInsightsEnabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_ecs_cluster" "example" { - } -`, - expected: ecs.ClusterSettings{ - Metadata: iacTypes.NewTestMetadata(), - ContainerInsightsEnabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptClusterSettings(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptTaskDefinitionResource(t *testing.T) { - tests := []struct { - name string - terraform string - expected ecs.TaskDefinition - }{ - { - name: "configured", - terraform: ` - resource "aws_ecs_task_definition" "example" { - family = "service" - container_definitions = < 0 { - orphanage := elb.LoadBalancer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Type: iacTypes.StringDefault(elb.TypeApplication, iacTypes.NewUnmanagedMetadata()), - DropInvalidHeaderFields: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Internal: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Listeners: nil, - } - for _, listenerResource := range orphanResources { - orphanage.Listeners = append(orphanage.Listeners, adaptListener(listenerResource, "application")) - } - loadBalancers = append(loadBalancers, orphanage) - } - - return loadBalancers -} - -func (a *adapter) adaptLoadBalancer(resource *terraform.Block, module terraform.Modules) elb.LoadBalancer { - var listeners []elb.Listener - - typeAttr := resource.GetAttribute("load_balancer_type") - typeVal := typeAttr.AsStringValueOrDefault("application", resource) - - dropInvalidHeadersAttr := resource.GetAttribute("drop_invalid_header_fields") - dropInvalidHeadersVal := dropInvalidHeadersAttr.AsBoolValueOrDefault(false, resource) - - internalAttr := resource.GetAttribute("internal") - internalVal := internalAttr.AsBoolValueOrDefault(false, resource) - - listenerBlocks := module.GetReferencingResources(resource, "aws_lb_listener", "load_balancer_arn") - listenerBlocks = append(listenerBlocks, module.GetReferencingResources(resource, "aws_alb_listener", "load_balancer_arn")...) - - for _, listenerBlock := range listenerBlocks { - a.listenerIDs.Resolve(listenerBlock.ID()) - listeners = append(listeners, adaptListener(listenerBlock, typeVal.Value())) - } - return elb.LoadBalancer{ - Metadata: resource.GetMetadata(), - Type: typeVal, - DropInvalidHeaderFields: dropInvalidHeadersVal, - Internal: internalVal, - Listeners: listeners, - } -} - -func (a *adapter) adaptClassicLoadBalancer(resource *terraform.Block, module terraform.Modules) elb.LoadBalancer { - internalAttr := resource.GetAttribute("internal") - internalVal := internalAttr.AsBoolValueOrDefault(false, resource) - - return elb.LoadBalancer{ - Metadata: resource.GetMetadata(), - Type: iacTypes.String("classic", resource.GetMetadata()), - DropInvalidHeaderFields: iacTypes.BoolDefault(false, resource.GetMetadata()), - Internal: internalVal, - Listeners: nil, - } -} - -func adaptListener(listenerBlock *terraform.Block, typeVal string) elb.Listener { - listener := elb.Listener{ - Metadata: listenerBlock.GetMetadata(), - Protocol: iacTypes.StringDefault("", listenerBlock.GetMetadata()), - TLSPolicy: iacTypes.StringDefault("", listenerBlock.GetMetadata()), - DefaultActions: nil, - } - - protocolAttr := listenerBlock.GetAttribute("protocol") - if typeVal == "application" { - listener.Protocol = protocolAttr.AsStringValueOrDefault("HTTP", listenerBlock) - } - - sslPolicyAttr := listenerBlock.GetAttribute("ssl_policy") - listener.TLSPolicy = sslPolicyAttr.AsStringValueOrDefault("", listenerBlock) - - for _, defaultActionBlock := range listenerBlock.GetBlocks("default_action") { - action := elb.Action{ - Metadata: defaultActionBlock.GetMetadata(), - Type: defaultActionBlock.GetAttribute("type").AsStringValueOrDefault("", defaultActionBlock), - } - listener.DefaultActions = append(listener.DefaultActions, action) - } - - return listener -} diff --git a/pkg/iac/adapters/terraform/aws/elb/adapt_test.go b/pkg/iac/adapters/terraform/aws/elb/adapt_test.go deleted file mode 100644 index f06682d402c7..000000000000 --- a/pkg/iac/adapters/terraform/aws/elb/adapt_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package elb - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elb" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected elb.ELB - }{ - { - name: "configured", - terraform: ` - resource "aws_alb" "example" { - name = "good_alb" - internal = true - load_balancer_type = "application" - - access_logs { - bucket = aws_s3_bucket.lb_logs.bucket - prefix = "test-lb" - enabled = true - } - - drop_invalid_header_fields = true - } - - resource "aws_alb_listener" "example" { - load_balancer_arn = aws_alb.example.arn - protocol = "HTTPS" - ssl_policy = "ELBSecurityPolicy-TLS-1-1-2017-01" - - default_action { - type = "forward" - } - } -`, - expected: elb.ELB{ - LoadBalancers: []elb.LoadBalancer{ - { - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("application", iacTypes.NewTestMetadata()), - DropInvalidHeaderFields: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Internal: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Listeners: []elb.Listener{ - { - Metadata: iacTypes.NewTestMetadata(), - Protocol: iacTypes.String("HTTPS", iacTypes.NewTestMetadata()), - TLSPolicy: iacTypes.String("ELBSecurityPolicy-TLS-1-1-2017-01", iacTypes.NewTestMetadata()), - DefaultActions: []elb.Action{ - { - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("forward", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_alb" "example" { - } -`, - expected: elb.ELB{ - LoadBalancers: []elb.LoadBalancer{ - { - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("application", iacTypes.NewTestMetadata()), - DropInvalidHeaderFields: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Internal: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Listeners: nil, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_alb" "example" { - name = "good_alb" - internal = true - load_balancer_type = "application" - drop_invalid_header_fields = true - - access_logs { - bucket = aws_s3_bucket.lb_logs.bucket - prefix = "test-lb" - enabled = true - } - } - - resource "aws_alb_listener" "example" { - load_balancer_arn = aws_alb.example.arn - protocol = "HTTPS" - ssl_policy = "ELBSecurityPolicy-TLS-1-1-2017-01" - - default_action { - type = "forward" - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.LoadBalancers, 1) - loadBalancer := adapted.LoadBalancers[0] - - assert.Equal(t, 2, loadBalancer.Metadata.Range().GetStartLine()) - assert.Equal(t, 13, loadBalancer.Metadata.Range().GetEndLine()) - - assert.Equal(t, 4, loadBalancer.Internal.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, loadBalancer.Internal.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, loadBalancer.Type.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, loadBalancer.Type.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, loadBalancer.DropInvalidHeaderFields.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, loadBalancer.DropInvalidHeaderFields.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 15, loadBalancer.Listeners[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 23, loadBalancer.Listeners[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 17, loadBalancer.Listeners[0].Protocol.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, loadBalancer.Listeners[0].Protocol.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 18, loadBalancer.Listeners[0].TLSPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 18, loadBalancer.Listeners[0].TLSPolicy.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 20, loadBalancer.Listeners[0].DefaultActions[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 22, loadBalancer.Listeners[0].DefaultActions[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 21, loadBalancer.Listeners[0].DefaultActions[0].Type.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 21, loadBalancer.Listeners[0].DefaultActions[0].Type.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/aws/emr/adapt.go b/pkg/iac/adapters/terraform/aws/emr/adapt.go deleted file mode 100644 index 74bbc08516f4..000000000000 --- a/pkg/iac/adapters/terraform/aws/emr/adapt.go +++ /dev/null @@ -1,49 +0,0 @@ -package emr - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/emr" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) emr.EMR { - return emr.EMR{ - Clusters: adaptClusters(modules), - SecurityConfiguration: adaptSecurityConfigurations(modules), - } -} -func adaptClusters(modules terraform.Modules) []emr.Cluster { - var clusters []emr.Cluster - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_emr_cluster") { - clusters = append(clusters, adaptCluster(resource)) - } - } - return clusters -} - -func adaptCluster(resource *terraform.Block) emr.Cluster { - - return emr.Cluster{ - Metadata: resource.GetMetadata(), - } -} - -func adaptSecurityConfigurations(modules terraform.Modules) []emr.SecurityConfiguration { - var securityConfiguration []emr.SecurityConfiguration - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_emr_security_configuration") { - securityConfiguration = append(securityConfiguration, adaptSecurityConfiguration(resource)) - } - } - return securityConfiguration -} - -func adaptSecurityConfiguration(resource *terraform.Block) emr.SecurityConfiguration { - - return emr.SecurityConfiguration{ - Metadata: resource.GetMetadata(), - Name: resource.GetAttribute("name").AsStringValueOrDefault("", resource), - Configuration: resource.GetAttribute("configuration").AsStringValueOrDefault("", resource), - } - -} diff --git a/pkg/iac/adapters/terraform/aws/emr/adapt_test.go b/pkg/iac/adapters/terraform/aws/emr/adapt_test.go deleted file mode 100644 index 726c8862e0fb..000000000000 --- a/pkg/iac/adapters/terraform/aws/emr/adapt_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package emr - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/emr" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptSecurityConfiguration(t *testing.T) { - tests := []struct { - name string - terraform string - expected emr.SecurityConfiguration - }{ - { - name: "test", - terraform: ` - resource "aws_emr_security_configuration" "foo" { - name = "emrsc_test" - configuration = < 0, - }, nil - } - - if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" { - if doc, err := ConvertTerraformDocument(modules, dataBlock); err == nil { - return &iam.Document{ - Metadata: dataBlock.GetMetadata(), - Parsed: doc.Document, - IsOffset: true, - HasRefs: false, - }, nil - } - } - } - - return &iam.Document{ - Metadata: owner.GetMetadata(), - }, nil -} - -func unescapeVars(input string) string { - return strings.ReplaceAll(input, "&{", "${") -} - -// ConvertTerraformDocument converts a terraform data policy into an iamgo policy https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document -func ConvertTerraformDocument(modules terraform.Modules, block *terraform.Block) (*wrappedDocument, error) { - - builder := iamgo.NewPolicyBuilder() - - if sourceAttr := block.GetAttribute("source_json"); sourceAttr.IsString() { - doc, err := iamgo.ParseString(sourceAttr.Value().AsString()) - if err != nil { - return nil, err - } - builder = iamgo.PolicyBuilderFromDocument(*doc) - } - - if sourceDocumentsAttr := block.GetAttribute("source_policy_documents"); sourceDocumentsAttr.IsIterable() { - docs := findAllPolicies(modules, sourceDocumentsAttr) - for _, doc := range docs { - statements, _ := doc.Document.Statements() - for _, statement := range statements { - builder.WithStatement(statement) - } - } - } - - if idAttr := block.GetAttribute("policy_id"); idAttr.IsString() { - r := idAttr.GetMetadata().Range() - builder.WithId(idAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine()) - } - - if versionAttr := block.GetAttribute("version"); versionAttr.IsString() { - r := versionAttr.GetMetadata().Range() - builder.WithVersion(versionAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine()) - } - - for _, statementBlock := range block.GetBlocks("statement") { - statement := parseStatement(statementBlock) - builder.WithStatement(statement, statement.Range().StartLine, statement.Range().EndLine) - } - - if overrideDocumentsAttr := block.GetAttribute("override_policy_documents"); overrideDocumentsAttr.IsIterable() { - docs := findAllPolicies(modules, overrideDocumentsAttr) - for _, doc := range docs { - statements, _ := doc.Document.Statements() - for _, statement := range statements { - builder.WithStatement(statement, statement.Range().StartLine, statement.Range().EndLine) - } - } - } - - return &wrappedDocument{Document: builder.Build(), Source: block}, nil -} - -// nolint -func parseStatement(statementBlock *terraform.Block) iamgo.Statement { - - metadata := statementBlock.GetMetadata() - - builder := iamgo.NewStatementBuilder() - builder.WithRange(metadata.Range().GetStartLine(), metadata.Range().GetEndLine()) - - if sidAttr := statementBlock.GetAttribute("sid"); sidAttr.IsString() { - r := sidAttr.GetMetadata().Range() - builder.WithSid(sidAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine()) - } - if actionsAttr := statementBlock.GetAttribute("actions"); actionsAttr.IsIterable() { - r := actionsAttr.GetMetadata().Range() - values := actionsAttr.AsStringValues().AsStrings() - builder.WithActions(values, r.GetStartLine(), r.GetEndLine()) - } - if notActionsAttr := statementBlock.GetAttribute("not_actions"); notActionsAttr.IsIterable() { - r := notActionsAttr.GetMetadata().Range() - values := notActionsAttr.AsStringValues().AsStrings() - builder.WithNotActions(values, r.GetStartLine(), r.GetEndLine()) - } - if resourcesAttr := statementBlock.GetAttribute("resources"); resourcesAttr.IsIterable() { - r := resourcesAttr.GetMetadata().Range() - values := resourcesAttr.AsStringValues().AsStrings() - builder.WithResources(values, r.GetStartLine(), r.GetEndLine()) - } - if notResourcesAttr := statementBlock.GetAttribute("not_resources"); notResourcesAttr.IsIterable() { - r := notResourcesAttr.GetMetadata().Range() - values := notResourcesAttr.AsStringValues().AsStrings() - builder.WithNotResources(values, r.GetStartLine(), r.GetEndLine()) - } - if effectAttr := statementBlock.GetAttribute("effect"); effectAttr.IsString() { - r := effectAttr.GetMetadata().Range() - builder.WithEffect(effectAttr.Value().AsString(), r.GetStartLine(), r.GetEndLine()) - } else { - builder.WithEffect(iamgo.EffectAllow) - } - - for _, principalBlock := range statementBlock.GetBlocks("principals") { - typeAttr := principalBlock.GetAttribute("type") - if !typeAttr.IsString() { - continue - } - identifiersAttr := principalBlock.GetAttribute("identifiers") - if !identifiersAttr.IsIterable() { - continue - } - r := principalBlock.GetMetadata().Range() - switch typeAttr.Value().AsString() { - case "*": - builder.WithAllPrincipals(true, r.GetStartLine(), r.GetEndLine()) - case "AWS": - values := identifiersAttr.AsStringValues().AsStrings() - builder.WithAWSPrincipals(values, r.GetStartLine(), r.GetEndLine()) - case "Federated": - values := identifiersAttr.AsStringValues().AsStrings() - builder.WithFederatedPrincipals(values, r.GetStartLine(), r.GetEndLine()) - case "Service": - values := identifiersAttr.AsStringValues().AsStrings() - builder.WithServicePrincipals(values, r.GetStartLine(), r.GetEndLine()) - case "CanonicalUser": - values := identifiersAttr.AsStringValues().AsStrings() - builder.WithCanonicalUsersPrincipals(values, r.GetStartLine(), r.GetEndLine()) - } - } - - for _, conditionBlock := range statementBlock.GetBlocks("condition") { - testAttr := conditionBlock.GetAttribute("test") - if !testAttr.IsString() { - continue - } - variableAttr := conditionBlock.GetAttribute("variable") - if !variableAttr.IsString() { - continue - } - valuesAttr := conditionBlock.GetAttribute("values") - values := valuesAttr.AsStringValues().AsStrings() - if valuesAttr.IsNil() || len(values) == 0 { - continue - } - - r := conditionBlock.GetMetadata().Range() - - builder.WithCondition( - testAttr.Value().AsString(), - variableAttr.Value().AsString(), - values, - r.GetStartLine(), - r.GetEndLine(), - ) - - } - return builder.Build() -} - -func findAllPolicies(modules terraform.Modules, attr *terraform.Attribute) []wrappedDocument { - var documents []wrappedDocument - - if !attr.IsIterable() { - return documents - } - - policyDocIDs := attr.AsStringValues().AsStrings() - for _, policyDocID := range policyDocIDs { - if policyDoc, err := modules.GetBlockById(policyDocID); err == nil { - if document, err := ConvertTerraformDocument(modules, policyDoc); err == nil { - documents = append(documents, *document) - } - } else if parsed, err := iamgo.Parse([]byte(unescapeVars(policyDocID))); err == nil { - documents = append(documents, wrappedDocument{ - Document: *parsed, - Source: attr, - }) - } - } - return documents -} diff --git a/pkg/iac/adapters/terraform/aws/iam/groups.go b/pkg/iac/adapters/terraform/aws/iam/groups.go deleted file mode 100644 index 9053bf83be78..000000000000 --- a/pkg/iac/adapters/terraform/aws/iam/groups.go +++ /dev/null @@ -1,32 +0,0 @@ -package iam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptGroups(modules terraform.Modules) []iam.Group { - var groups []iam.Group - - for _, groupBlock := range modules.GetResourcesByType("aws_iam_group") { - group := iam.Group{ - Metadata: groupBlock.GetMetadata(), - Name: groupBlock.GetAttribute("name").AsStringValueOrDefault("", groupBlock), - } - - if policy, ok := applyForDependentResource( - modules, groupBlock.ID(), "name", "aws_iam_group_policy", "group", findPolicy(modules), - ); ok && policy != nil { - group.Policies = append(group.Policies, *policy) - } - - if policy, ok := applyForDependentResource( - modules, groupBlock.ID(), "name", "aws_iam_group_policy_attachment", "group", findAttachmentPolicy(modules), - ); ok && policy != nil { - group.Policies = append(group.Policies, *policy) - } - - groups = append(groups, group) - } - return groups -} diff --git a/pkg/iac/adapters/terraform/aws/iam/groups_test.go b/pkg/iac/adapters/terraform/aws/iam/groups_test.go deleted file mode 100644 index 9ac6be72c46d..000000000000 --- a/pkg/iac/adapters/terraform/aws/iam/groups_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package iam - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptGroups(t *testing.T) { - tests := []struct { - name string - terraform string - expected []iam.Group - }{ - { - name: "policy", - terraform: ` - resource "aws_iam_group_policy" "my_developer_policy" { - name = "my_developer_policy" - group = aws_iam_group.my_developers.name - - policy = < 0 { - orphanage := lambda.Function{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Tracing: lambda.Tracing{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Mode: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - Permissions: nil, - } - for _, permission := range orphanResources { - orphanage.Permissions = append(orphanage.Permissions, a.adaptPermission(permission)) - } - functions = append(functions, orphanage) - } - - return functions -} - -func (a *adapter) adaptFunction(function *terraform.Block, modules terraform.Modules, orphans terraform.ResourceIDResolutions) lambda.Function { - var permissions []lambda.Permission - for _, module := range modules { - for _, p := range module.GetResourcesByType("aws_lambda_permission") { - if referencedBlock, err := module.GetReferencedBlock(p.GetAttribute("function_name"), p); err == nil && referencedBlock == function { - permissions = append(permissions, a.adaptPermission(p)) - delete(orphans, p.ID()) - } - } - } - - return lambda.Function{ - Metadata: function.GetMetadata(), - Tracing: a.adaptTracing(function), - Permissions: permissions, - } -} - -func (a *adapter) adaptTracing(function *terraform.Block) lambda.Tracing { - if tracingConfig := function.GetBlock("tracing_config"); tracingConfig.IsNotNil() { - return lambda.Tracing{ - Metadata: tracingConfig.GetMetadata(), - Mode: tracingConfig.GetAttribute("mode").AsStringValueOrDefault("", tracingConfig), - } - } - - return lambda.Tracing{ - Metadata: function.GetMetadata(), - Mode: iacTypes.StringDefault("", function.GetMetadata()), - } -} - -func (a *adapter) adaptPermission(permission *terraform.Block) lambda.Permission { - sourceARNAttr := permission.GetAttribute("source_arn") - sourceARN := sourceARNAttr.AsStringValueOrDefault("", permission) - - if len(sourceARNAttr.AllReferences()) > 0 { - sourceARN = iacTypes.String(sourceARNAttr.AllReferences()[0].NameLabel(), sourceARNAttr.GetMetadata()) - } - - return lambda.Permission{ - Metadata: permission.GetMetadata(), - Principal: permission.GetAttribute("principal").AsStringValueOrDefault("", permission), - SourceARN: sourceARN, - } -} diff --git a/pkg/iac/adapters/terraform/aws/lambda/adapt_test.go b/pkg/iac/adapters/terraform/aws/lambda/adapt_test.go deleted file mode 100644 index c0e58104819d..000000000000 --- a/pkg/iac/adapters/terraform/aws/lambda/adapt_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package lambda - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/lambda" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected lambda.Lambda - }{ - { - name: "reference arn", - terraform: ` - resource "aws_lambda_function" "example" { - filename = "lambda_function_payload.zip" - function_name = "lambda_function_name" - role = aws_iam_role.iam_for_lambda.arn - runtime = "nodejs12.x" - - tracing_config { - mode = "Passthrough" - } - } - - resource "aws_lambda_permission" "example" { - statement_id = "AllowExecutionFromSNS" - action = "lambda:InvokeFunction" - function_name = aws_lambda_function.example.function_name - principal = "sns.amazonaws.com" - source_arn = aws_sns_topic.default.arn - } -`, - expected: lambda.Lambda{ - Functions: []lambda.Function{ - { - Metadata: iacTypes.NewTestMetadata(), - Tracing: lambda.Tracing{ - Metadata: iacTypes.NewTestMetadata(), - Mode: iacTypes.String("Passthrough", iacTypes.NewTestMetadata()), - }, - Permissions: []lambda.Permission{ - { - Metadata: iacTypes.NewTestMetadata(), - Principal: iacTypes.String("sns.amazonaws.com", iacTypes.NewTestMetadata()), - SourceARN: iacTypes.String("default", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - { - name: "defaults (with an orphan)", - terraform: ` - resource "aws_lambda_function" "example" { - tracing_config { - } - } - - resource "aws_lambda_permission" "example" { - } -`, - expected: lambda.Lambda{ - Functions: []lambda.Function{ - { - Metadata: iacTypes.NewTestMetadata(), - Tracing: lambda.Tracing{ - Metadata: iacTypes.NewTestMetadata(), - Mode: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - { - Metadata: iacTypes.NewTestMetadata(), - Tracing: lambda.Tracing{ - Metadata: iacTypes.NewTestMetadata(), - Mode: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - Permissions: []lambda.Permission{ - { - Metadata: iacTypes.NewTestMetadata(), - Principal: iacTypes.String("", iacTypes.NewTestMetadata()), - SourceARN: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_lambda_function" "example" { - filename = "lambda_function_payload.zip" - function_name = "lambda_function_name" - role = aws_iam_role.iam_for_lambda.arn - runtime = "nodejs12.x" - - tracing_config { - mode = "Passthrough" - } - } - - resource "aws_lambda_permission" "example" { - statement_id = "AllowExecutionFromSNS" - action = "lambda:InvokeFunction" - function_name = aws_lambda_function.example.function_name - principal = "sns.amazonaws.com" - source_arn = "string arn" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Functions, 1) - function := adapted.Functions[0] - - assert.Equal(t, 2, function.Metadata.Range().GetStartLine()) - assert.Equal(t, 11, function.Metadata.Range().GetEndLine()) - - assert.Equal(t, 8, function.Tracing.Metadata.Range().GetStartLine()) - assert.Equal(t, 10, function.Tracing.Metadata.Range().GetEndLine()) - - assert.Equal(t, 9, function.Tracing.Mode.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 9, function.Tracing.Mode.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, function.Permissions[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 19, function.Permissions[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 17, function.Permissions[0].Principal.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, function.Permissions[0].Principal.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 18, function.Permissions[0].SourceARN.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 18, function.Permissions[0].SourceARN.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/mq/adapt.go b/pkg/iac/adapters/terraform/aws/mq/adapt.go deleted file mode 100644 index d0fed28d5b83..000000000000 --- a/pkg/iac/adapters/terraform/aws/mq/adapt.go +++ /dev/null @@ -1,48 +0,0 @@ -package mq - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) mq.MQ { - return mq.MQ{ - Brokers: adaptBrokers(modules), - } -} - -func adaptBrokers(modules terraform.Modules) []mq.Broker { - var brokers []mq.Broker - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_mq_broker") { - brokers = append(brokers, adaptBroker(resource)) - } - } - return brokers -} - -func adaptBroker(resource *terraform.Block) mq.Broker { - - broker := mq.Broker{ - Metadata: resource.GetMetadata(), - PublicAccess: types.BoolDefault(false, resource.GetMetadata()), - Logging: mq.Logging{ - Metadata: resource.GetMetadata(), - General: types.BoolDefault(false, resource.GetMetadata()), - Audit: types.BoolDefault(false, resource.GetMetadata()), - }, - } - - publicAccessAttr := resource.GetAttribute("publicly_accessible") - broker.PublicAccess = publicAccessAttr.AsBoolValueOrDefault(false, resource) - if logsBlock := resource.GetBlock("logs"); logsBlock.IsNotNil() { - broker.Logging.Metadata = logsBlock.GetMetadata() - auditAttr := logsBlock.GetAttribute("audit") - broker.Logging.Audit = auditAttr.AsBoolValueOrDefault(false, logsBlock) - generalAttr := logsBlock.GetAttribute("general") - broker.Logging.General = generalAttr.AsBoolValueOrDefault(false, logsBlock) - } - - return broker -} diff --git a/pkg/iac/adapters/terraform/aws/mq/adapt_test.go b/pkg/iac/adapters/terraform/aws/mq/adapt_test.go deleted file mode 100644 index e581cf43be24..000000000000 --- a/pkg/iac/adapters/terraform/aws/mq/adapt_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package mq - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/mq" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptBroker(t *testing.T) { - tests := []struct { - name string - terraform string - expected mq.Broker - }{ - { - name: "audit logs", - terraform: ` - resource "aws_mq_broker" "example" { - logs { - audit = true - } - - publicly_accessible = false - } -`, - expected: mq.Broker{ - Metadata: iacTypes.NewTestMetadata(), - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Logging: mq.Logging{ - Metadata: iacTypes.NewTestMetadata(), - General: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Audit: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "general logs", - terraform: ` - resource "aws_mq_broker" "example" { - logs { - general = true - } - - publicly_accessible = true - } -`, - expected: mq.Broker{ - Metadata: iacTypes.NewTestMetadata(), - PublicAccess: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Logging: mq.Logging{ - Metadata: iacTypes.NewTestMetadata(), - General: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Audit: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_mq_broker" "example" { - } -`, - expected: mq.Broker{ - Metadata: iacTypes.NewTestMetadata(), - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Logging: mq.Logging{ - Metadata: iacTypes.NewTestMetadata(), - General: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Audit: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptBroker(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_mq_broker" "example" { - logs { - general = true - } - - publicly_accessible = true - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Brokers, 1) - broker := adapted.Brokers[0] - - assert.Equal(t, 2, broker.Metadata.Range().GetStartLine()) - assert.Equal(t, 8, broker.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, broker.Logging.Metadata.Range().GetStartLine()) - assert.Equal(t, 5, broker.Logging.Metadata.Range().GetEndLine()) - - assert.Equal(t, 4, broker.Logging.General.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, broker.Logging.General.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 7, broker.PublicAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, broker.PublicAccess.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/msk/adapt.go b/pkg/iac/adapters/terraform/aws/msk/adapt.go deleted file mode 100644 index 520b68b2a06c..000000000000 --- a/pkg/iac/adapters/terraform/aws/msk/adapt.go +++ /dev/null @@ -1,97 +0,0 @@ -package msk - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) msk.MSK { - return msk.MSK{ - Clusters: adaptClusters(modules), - } -} - -func adaptClusters(modules terraform.Modules) []msk.Cluster { - var clusters []msk.Cluster - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_msk_cluster") { - clusters = append(clusters, adaptCluster(resource)) - } - } - return clusters -} - -func adaptCluster(resource *terraform.Block) msk.Cluster { - cluster := msk.Cluster{ - Metadata: resource.GetMetadata(), - EncryptionInTransit: msk.EncryptionInTransit{ - Metadata: resource.GetMetadata(), - ClientBroker: iacTypes.StringDefault("TLS_PLAINTEXT", resource.GetMetadata()), - }, - EncryptionAtRest: msk.EncryptionAtRest{ - Metadata: resource.GetMetadata(), - KMSKeyARN: iacTypes.StringDefault("", resource.GetMetadata()), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - Logging: msk.Logging{ - Metadata: resource.GetMetadata(), - Broker: msk.BrokerLogging{ - Metadata: resource.GetMetadata(), - S3: msk.S3Logging{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - Cloudwatch: msk.CloudwatchLogging{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - Firehose: msk.FirehoseLogging{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - }, - }, - } - - if encryptBlock := resource.GetBlock("encryption_info"); encryptBlock.IsNotNil() { - if encryptionInTransitBlock := encryptBlock.GetBlock("encryption_in_transit"); encryptionInTransitBlock.IsNotNil() { - cluster.EncryptionInTransit.Metadata = encryptionInTransitBlock.GetMetadata() - if clientBrokerAttr := encryptionInTransitBlock.GetAttribute("client_broker"); clientBrokerAttr.IsNotNil() { - cluster.EncryptionInTransit.ClientBroker = clientBrokerAttr.AsStringValueOrDefault("TLS", encryptionInTransitBlock) - } - } - - if encryptionAtRestAttr := encryptBlock.GetAttribute("encryption_at_rest_kms_key_arn"); encryptionAtRestAttr.IsNotNil() { - cluster.EncryptionAtRest.Metadata = encryptionAtRestAttr.GetMetadata() - cluster.EncryptionAtRest.KMSKeyARN = encryptionAtRestAttr.AsStringValueOrDefault("", encryptBlock) - cluster.EncryptionAtRest.Enabled = iacTypes.Bool(true, encryptionAtRestAttr.GetMetadata()) - } - } - - if logBlock := resource.GetBlock("logging_info"); logBlock.IsNotNil() { - cluster.Logging.Metadata = logBlock.GetMetadata() - if brokerLogsBlock := logBlock.GetBlock("broker_logs"); brokerLogsBlock.IsNotNil() { - cluster.Logging.Broker.Metadata = brokerLogsBlock.GetMetadata() - if brokerLogsBlock.HasChild("s3") { - if s3Block := brokerLogsBlock.GetBlock("s3"); s3Block.IsNotNil() { - s3enabledAttr := s3Block.GetAttribute("enabled") - cluster.Logging.Broker.S3.Metadata = s3Block.GetMetadata() - cluster.Logging.Broker.S3.Enabled = s3enabledAttr.AsBoolValueOrDefault(false, s3Block) - } - } - if cloudwatchBlock := brokerLogsBlock.GetBlock("cloudwatch_logs"); cloudwatchBlock.IsNotNil() { - cwEnabledAttr := cloudwatchBlock.GetAttribute("enabled") - cluster.Logging.Broker.Cloudwatch.Metadata = cloudwatchBlock.GetMetadata() - cluster.Logging.Broker.Cloudwatch.Enabled = cwEnabledAttr.AsBoolValueOrDefault(false, cloudwatchBlock) - } - if firehoseBlock := brokerLogsBlock.GetBlock("firehose"); firehoseBlock.IsNotNil() { - firehoseEnabledAttr := firehoseBlock.GetAttribute("enabled") - cluster.Logging.Broker.Firehose.Metadata = firehoseBlock.GetMetadata() - cluster.Logging.Broker.Firehose.Enabled = firehoseEnabledAttr.AsBoolValueOrDefault(false, firehoseBlock) - } - } - } - - return cluster -} diff --git a/pkg/iac/adapters/terraform/aws/msk/adapt_test.go b/pkg/iac/adapters/terraform/aws/msk/adapt_test.go deleted file mode 100644 index 6157655dbfed..000000000000 --- a/pkg/iac/adapters/terraform/aws/msk/adapt_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package msk - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/msk" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected msk.Cluster - }{ - { - name: "configured", - terraform: ` - resource "aws_msk_cluster" "example" { - cluster_name = "example" - - encryption_info { - encryption_in_transit { - client_broker = "TLS" - in_cluster = true - } - encryption_at_rest_kms_key_arn = "foo-bar-key" - } - - logging_info { - broker_logs { - cloudwatch_logs { - enabled = true - log_group = aws_cloudwatch_log_group.test.name - } - firehose { - enabled = true - delivery_stream = aws_kinesis_firehose_delivery_stream.test_stream.name - } - s3 { - enabled = true - bucket = aws_s3_bucket.bucket.id - prefix = "logs/msk-" - } - } - } - } -`, - expected: msk.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - EncryptionInTransit: msk.EncryptionInTransit{ - Metadata: iacTypes.NewTestMetadata(), - ClientBroker: iacTypes.String("TLS", iacTypes.NewTestMetadata()), - }, - EncryptionAtRest: msk.EncryptionAtRest{ - Metadata: iacTypes.NewTestMetadata(), - KMSKeyARN: iacTypes.String("foo-bar-key", iacTypes.NewTestMetadata()), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Logging: msk.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Broker: msk.BrokerLogging{ - Metadata: iacTypes.NewTestMetadata(), - S3: msk.S3Logging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Cloudwatch: msk.CloudwatchLogging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Firehose: msk.FirehoseLogging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_msk_cluster" "example" { - } -`, - expected: msk.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - EncryptionInTransit: msk.EncryptionInTransit{ - Metadata: iacTypes.NewTestMetadata(), - ClientBroker: iacTypes.String("TLS_PLAINTEXT", iacTypes.NewTestMetadata()), - }, - Logging: msk.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Broker: msk.BrokerLogging{ - Metadata: iacTypes.NewTestMetadata(), - S3: msk.S3Logging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - Cloudwatch: msk.CloudwatchLogging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - Firehose: msk.FirehoseLogging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptCluster(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_msk_cluster" "example" { - cluster_name = "example" - - encryption_info { - encryption_in_transit { - client_broker = "TLS" - in_cluster = true - } - encryption_at_rest_kms_key_arn = "foo-bar-key" - } - - logging_info { - broker_logs { - cloudwatch_logs { - enabled = true - log_group = aws_cloudwatch_log_group.test.name - } - firehose { - enabled = true - delivery_stream = aws_kinesis_firehose_delivery_stream.test_stream.name - } - s3 { - enabled = true - bucket = aws_s3_bucket.bucket.id - prefix = "logs/msk-" - } - } - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Clusters, 1) - cluster := adapted.Clusters[0] - - assert.Equal(t, 2, cluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 30, cluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 6, cluster.EncryptionInTransit.Metadata.Range().GetStartLine()) - assert.Equal(t, 9, cluster.EncryptionInTransit.Metadata.Range().GetEndLine()) - - assert.Equal(t, 10, cluster.EncryptionAtRest.Metadata.Range().GetStartLine()) - assert.Equal(t, 10, cluster.EncryptionAtRest.Metadata.Range().GetEndLine()) - - assert.Equal(t, 13, cluster.Logging.Metadata.Range().GetStartLine()) - assert.Equal(t, 29, cluster.Logging.Metadata.Range().GetEndLine()) - - assert.Equal(t, 14, cluster.Logging.Broker.Metadata.Range().GetStartLine()) - assert.Equal(t, 28, cluster.Logging.Broker.Metadata.Range().GetEndLine()) - - assert.Equal(t, 15, cluster.Logging.Broker.Cloudwatch.Metadata.Range().GetStartLine()) - assert.Equal(t, 18, cluster.Logging.Broker.Cloudwatch.Metadata.Range().GetEndLine()) - - assert.Equal(t, 16, cluster.Logging.Broker.Cloudwatch.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 16, cluster.Logging.Broker.Cloudwatch.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 19, cluster.Logging.Broker.Firehose.Metadata.Range().GetStartLine()) - assert.Equal(t, 22, cluster.Logging.Broker.Firehose.Metadata.Range().GetEndLine()) - - assert.Equal(t, 20, cluster.Logging.Broker.Firehose.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 20, cluster.Logging.Broker.Firehose.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, cluster.Logging.Broker.S3.Metadata.Range().GetStartLine()) - assert.Equal(t, 27, cluster.Logging.Broker.S3.Metadata.Range().GetEndLine()) - - assert.Equal(t, 24, cluster.Logging.Broker.S3.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 24, cluster.Logging.Broker.S3.Enabled.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/neptune/adapt.go b/pkg/iac/adapters/terraform/aws/neptune/adapt.go deleted file mode 100644 index 2dc3e344b110..000000000000 --- a/pkg/iac/adapters/terraform/aws/neptune/adapt.go +++ /dev/null @@ -1,50 +0,0 @@ -package neptune - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) neptune.Neptune { - return neptune.Neptune{ - Clusters: adaptClusters(modules), - } -} - -func adaptClusters(modules terraform.Modules) []neptune.Cluster { - var clusters []neptune.Cluster - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_neptune_cluster") { - clusters = append(clusters, adaptCluster(resource)) - } - } - return clusters -} - -func adaptCluster(resource *terraform.Block) neptune.Cluster { - cluster := neptune.Cluster{ - Metadata: resource.GetMetadata(), - Logging: neptune.Logging{ - Metadata: resource.GetMetadata(), - Audit: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - StorageEncrypted: iacTypes.BoolDefault(false, resource.GetMetadata()), - KMSKeyID: iacTypes.StringDefault("", resource.GetMetadata()), - } - - if enableLogExportsAttr := resource.GetAttribute("enable_cloudwatch_logs_exports"); enableLogExportsAttr.IsNotNil() { - cluster.Logging.Metadata = enableLogExportsAttr.GetMetadata() - if enableLogExportsAttr.Contains("audit") { - cluster.Logging.Audit = iacTypes.Bool(true, enableLogExportsAttr.GetMetadata()) - } - } - - storageEncryptedAttr := resource.GetAttribute("storage_encrypted") - cluster.StorageEncrypted = storageEncryptedAttr.AsBoolValueOrDefault(false, resource) - - KMSKeyAttr := resource.GetAttribute("kms_key_arn") - cluster.KMSKeyID = KMSKeyAttr.AsStringValueOrDefault("", resource) - - return cluster -} diff --git a/pkg/iac/adapters/terraform/aws/neptune/adapt_test.go b/pkg/iac/adapters/terraform/aws/neptune/adapt_test.go deleted file mode 100644 index 7e67e51e394c..000000000000 --- a/pkg/iac/adapters/terraform/aws/neptune/adapt_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package neptune - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/neptune" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected neptune.Cluster - }{ - { - name: "configured", - terraform: ` - resource "aws_neptune_cluster" "example" { - enable_cloudwatch_logs_exports = ["audit"] - storage_encrypted = true - kms_key_arn = "kms-key" - } -`, - expected: neptune.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - Logging: neptune.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Audit: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - StorageEncrypted: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("kms-key", iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_neptune_cluster" "example" { - } -`, - expected: neptune.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - Logging: neptune.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Audit: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - StorageEncrypted: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptCluster(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_neptune_cluster" "example" { - enable_cloudwatch_logs_exports = ["audit"] - storage_encrypted = true - kms_key_arn = "kms-key" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Clusters, 1) - cluster := adapted.Clusters[0] - - assert.Equal(t, 2, cluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 6, cluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, cluster.Logging.Metadata.Range().GetStartLine()) - assert.Equal(t, 3, cluster.Logging.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, cluster.Logging.Audit.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, cluster.Logging.Audit.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, cluster.StorageEncrypted.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, cluster.StorageEncrypted.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, cluster.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, cluster.KMSKeyID.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/provider/adapt.go b/pkg/iac/adapters/terraform/aws/provider/adapt.go deleted file mode 100644 index 2641a8299840..000000000000 --- a/pkg/iac/adapters/terraform/aws/provider/adapt.go +++ /dev/null @@ -1,166 +0,0 @@ -package provider - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -const ( - defaultMaxRetires = 25 - defaultSharedConfigFile = "~/.aws/config" - //#nosec G101 -- False positive - defaultSharedCredentialsFile = "~/.aws/credentials" -) - -func Adapt(modules terraform.Modules) []aws.TerraformProvider { - return adaptProviders(modules) -} - -func adaptProviders(modules terraform.Modules) []aws.TerraformProvider { - var providers []aws.TerraformProvider - for _, providerBlock := range modules.GetBlocks().OfType("provider") { - if providerBlock.Label() == "aws" { - providers = append(providers, adaptProvider(providerBlock)) - } - } - - return providers -} - -func adaptProvider(b *terraform.Block) aws.TerraformProvider { - return aws.TerraformProvider{ - Metadata: b.GetMetadata(), - Alias: getStringAttrValue("alias", b), - Version: getStringAttrValue("version", b), - AccessKey: getStringAttrValue("access_key", b), - AllowedAccountsIDs: b.GetAttribute("allowed_account_ids").AsStringValueSliceOrEmpty(), - AssumeRole: adaptAssumeRole(b), - AssumeRoleWithWebIdentity: adaptAssumeRoleWithWebIdentity(b), - CustomCABundle: getStringAttrValue("custom_ca_bundle", b), - DefaultTags: adaptDefaultTags(b), - EC2MetadataServiceEndpoint: getStringAttrValue("ec2_metadata_service_endpoint", b), - EC2MetadataServiceEndpointMode: getStringAttrValue("ec2_metadata_service_endpoint_mode", b), - Endpoints: adaptEndpoints(b), - ForbiddenAccountIDs: b.GetAttribute("forbidden_account_ids").AsStringValueSliceOrEmpty(), - HttpProxy: getStringAttrValue("http_proxy", b), - IgnoreTags: adaptIgnoreTags(b), - Insecure: b.GetAttribute("insecure").AsBoolValueOrDefault(false, b), - MaxRetries: b.GetAttribute("max_retries").AsIntValueOrDefault(defaultMaxRetires, b), - Profile: getStringAttrValue("profile", b), - Region: getStringAttrValue("region", b), - RetryMode: getStringAttrValue("retry_mode", b), - S3UsePathStyle: b.GetAttribute("s3_use_path_style").AsBoolValueOrDefault(false, b), - S3USEast1RegionalEndpoint: getStringAttrValue("s3_us_east_1_regional_endpoint", b), - SecretKey: getStringAttrValue("secret_key", b), - SharedConfigFiles: b.GetAttribute("shared_config_files").AsStringValuesOrDefault(b, defaultSharedConfigFile), - SharedCredentialsFiles: b.GetAttribute("shared_credentials_files").AsStringValuesOrDefault(b, defaultSharedCredentialsFile), - SkipCredentialsValidation: b.GetAttribute("skip_credentials_validation").AsBoolValueOrDefault(false, b), - SkipMetadataAPICheck: b.GetAttribute("skip_metadata_api_check").AsBoolValueOrDefault(false, b), - SkipRegionValidation: b.GetAttribute("skip_region_validation").AsBoolValueOrDefault(false, b), - SkipRequestingAccountID: b.GetAttribute("skip_requesting_account_id").AsBoolValueOrDefault(false, b), - STSRegion: getStringAttrValue("sts_region", b), - Token: getStringAttrValue("token", b), - UseDualstackEndpoint: b.GetAttribute("use_dualstack_endpoint").AsBoolValueOrDefault(false, b), - UseFIPSEndpoint: b.GetAttribute("use_fips_endpoint").AsBoolValueOrDefault(false, b), - } -} - -func adaptAssumeRole(p *terraform.Block) aws.AssumeRole { - assumeRoleBlock := p.GetBlock("assume_role") - - if assumeRoleBlock.IsNil() { - return aws.AssumeRole{ - Metadata: p.GetMetadata(), - Duration: types.StringDefault("", p.GetMetadata()), - ExternalID: types.StringDefault("", p.GetMetadata()), - Policy: types.StringDefault("", p.GetMetadata()), - RoleARN: types.StringDefault("", p.GetMetadata()), - SessionName: types.StringDefault("", p.GetMetadata()), - SourceIdentity: types.StringDefault("", p.GetMetadata()), - } - } - - return aws.AssumeRole{ - Metadata: assumeRoleBlock.GetMetadata(), - Duration: getStringAttrValue("duration", p), - ExternalID: getStringAttrValue("external_id", p), - Policy: getStringAttrValue("policy", p), - PolicyARNs: p.GetAttribute("policy_arns").AsStringValueSliceOrEmpty(), - RoleARN: getStringAttrValue("role_arn", p), - SessionName: getStringAttrValue("session_name", p), - SourceIdentity: getStringAttrValue("source_identity", p), - Tags: p.GetAttribute("tags").AsMapValue(), - TransitiveTagKeys: p.GetAttribute("transitive_tag_keys").AsStringValueSliceOrEmpty(), - } -} - -func adaptAssumeRoleWithWebIdentity(p *terraform.Block) aws.AssumeRoleWithWebIdentity { - block := p.GetBlock("assume_role_with_web_identity") - if block.IsNil() { - return aws.AssumeRoleWithWebIdentity{ - Metadata: p.GetMetadata(), - Duration: types.StringDefault("", p.GetMetadata()), - Policy: types.StringDefault("", p.GetMetadata()), - RoleARN: types.StringDefault("", p.GetMetadata()), - SessionName: types.StringDefault("", p.GetMetadata()), - WebIdentityToken: types.StringDefault("", p.GetMetadata()), - WebIdentityTokenFile: types.StringDefault("", p.GetMetadata()), - } - } - - return aws.AssumeRoleWithWebIdentity{ - Metadata: block.GetMetadata(), - Duration: getStringAttrValue("duration", p), - Policy: getStringAttrValue("policy", p), - PolicyARNs: p.GetAttribute("policy_arns").AsStringValueSliceOrEmpty(), - RoleARN: getStringAttrValue("role_arn", p), - SessionName: getStringAttrValue("session_name", p), - WebIdentityToken: getStringAttrValue("web_identity_token", p), - WebIdentityTokenFile: getStringAttrValue("web_identity_token_file", p), - } -} - -func adaptEndpoints(p *terraform.Block) types.MapValue { - block := p.GetBlock("endpoints") - if block.IsNil() { - return types.MapDefault(make(map[string]string), p.GetMetadata()) - } - - values := make(map[string]string) - - for name, attr := range block.Attributes() { - values[name] = attr.AsStringValueOrDefault("", block).Value() - } - - return types.Map(values, block.GetMetadata()) -} - -func adaptDefaultTags(p *terraform.Block) aws.DefaultTags { - attr, _ := p.GetNestedAttribute("default_tags.tags") - if attr.IsNil() { - return aws.DefaultTags{} - } - - return aws.DefaultTags{ - Metadata: attr.GetMetadata(), - Tags: attr.AsMapValue(), - } -} - -func adaptIgnoreTags(p *terraform.Block) aws.IgnoreTags { - block := p.GetBlock("ignore_tags") - if block.IsNil() { - return aws.IgnoreTags{} - } - - return aws.IgnoreTags{ - Metadata: block.GetMetadata(), - Keys: block.GetAttribute("keys").AsStringValueSliceOrEmpty(), - KeyPrefixes: block.GetAttribute("key_prefixes").AsStringValueSliceOrEmpty(), - } -} - -func getStringAttrValue(name string, parent *terraform.Block) types.StringValue { - return parent.GetAttribute(name).AsStringValueOrDefault("", parent) -} diff --git a/pkg/iac/adapters/terraform/aws/provider/adapt_test.go b/pkg/iac/adapters/terraform/aws/provider/adapt_test.go deleted file mode 100644 index 9cbcc767e3b3..000000000000 --- a/pkg/iac/adapters/terraform/aws/provider/adapt_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package provider - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestAdapt(t *testing.T) { - tests := []struct { - name string - source string - expected []aws.TerraformProvider - }{ - { - name: "happy", - source: ` -variable "s3_use_path_style" { - default = true -} - -provider "aws" { - version = "~> 5.0" - region = "us-east-1" - profile = "localstack" - - access_key = "fake" - secret_key = "fake" - skip_credentials_validation = true - skip_metadata_api_check = true - skip_requesting_account_id = true - s3_use_path_style = var.s3_use_path_style - - endpoints { - dynamodb = "http://localhost:4566" - s3 = "http://localhost:4566" - } - - default_tags { - tags = { - Environment = "Local" - Name = "LocalStack" - } - } -}`, - expected: []aws.TerraformProvider{ - { - Version: types.String("~> 5.0", types.NewTestMetadata()), - Region: types.String("us-east-1", types.NewTestMetadata()), - DefaultTags: aws.DefaultTags{ - Metadata: types.NewTestMetadata(), - Tags: types.Map(map[string]string{ - "Environment": "Local", - "Name": "LocalStack", - }, types.NewTestMetadata()), - }, - Endpoints: types.Map(map[string]string{ - "dynamodb": "http://localhost:4566", - "s3": "http://localhost:4566", - }, types.NewTestMetadata()), - Profile: types.String("localstack", types.NewTestMetadata()), - AccessKey: types.String("fake", types.NewTestMetadata()), - SecretKey: types.String("fake", types.NewTestMetadata()), - SkipCredentialsValidation: types.Bool(true, types.NewTestMetadata()), - SkipMetadataAPICheck: types.Bool(true, types.NewTestMetadata()), - SkipRequestingAccountID: types.Bool(true, types.NewTestMetadata()), - S3UsePathStyle: types.Bool(true, types.NewTestMetadata()), - MaxRetries: types.IntDefault(defaultMaxRetires, types.NewTestMetadata()), - SharedConfigFiles: types.StringValueList{ - types.StringDefault(defaultSharedConfigFile, types.NewTestMetadata()), - }, - SharedCredentialsFiles: types.StringValueList{ - types.StringDefault(defaultSharedCredentialsFile, types.NewTestMetadata()), - }, - }, - }, - }, - { - name: "multiply provider configurations", - source: ` - -provider "aws" { - region = "us-east-1" -} - -provider "aws" { - alias = "west" - region = "us-west-2" -} -`, - expected: []aws.TerraformProvider{ - { - Region: types.String("us-east-1", types.NewTestMetadata()), - Endpoints: types.Map(make(map[string]string), types.NewTestMetadata()), - MaxRetries: types.IntDefault(defaultMaxRetires, types.NewTestMetadata()), - SharedConfigFiles: types.StringValueList{ - types.StringDefault(defaultSharedConfigFile, types.NewTestMetadata()), - }, - SharedCredentialsFiles: types.StringValueList{ - types.StringDefault(defaultSharedCredentialsFile, types.NewTestMetadata()), - }, - }, - { - Alias: types.String("west", types.NewTestMetadata()), - Region: types.String("us-west-2", types.NewTestMetadata()), - Endpoints: types.Map(make(map[string]string), types.NewTestMetadata()), - MaxRetries: types.IntDefault(defaultMaxRetires, types.NewTestMetadata()), - SharedConfigFiles: types.StringValueList{ - types.StringDefault(defaultSharedConfigFile, types.NewTestMetadata()), - }, - SharedCredentialsFiles: types.StringValueList{ - types.StringDefault(defaultSharedCredentialsFile, types.NewTestMetadata()), - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.source, ".tf") - testutil.AssertDefsecEqual(t, test.expected, Adapt(modules)) - }) - } -} diff --git a/pkg/iac/adapters/terraform/aws/rds/adapt.go b/pkg/iac/adapters/terraform/aws/rds/adapt.go deleted file mode 100644 index d99821c23950..000000000000 --- a/pkg/iac/adapters/terraform/aws/rds/adapt.go +++ /dev/null @@ -1,256 +0,0 @@ -package rds - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) rds.RDS { - return rds.RDS{ - Instances: getInstances(modules), - Clusters: getClusters(modules), - Classic: getClassic(modules), - Snapshots: getSnapshots(modules), - ParameterGroups: getParameterGroups(modules), - } -} - -func getInstances(modules terraform.Modules) (instances []rds.Instance) { - for _, resource := range modules.GetResourcesByType("aws_db_instance") { - instances = append(instances, adaptInstance(resource, modules)) - } - - return instances -} - -func getParameterGroups(modules terraform.Modules) (parametergroups []rds.ParameterGroups) { - for _, resource := range modules.GetResourcesByType("aws_db_parameter_group") { - parametergroups = append(parametergroups, adaptDBParameterGroups(resource, modules)) - } - - return parametergroups -} - -func getSnapshots(modules terraform.Modules) (snapshots []rds.Snapshots) { - for _, resource := range modules.GetResourcesByType("aws_db_snapshot") { - snapshots = append(snapshots, adaptDBSnapshots(resource, modules)) - } - - return snapshots -} - -func getClusters(modules terraform.Modules) (clusters []rds.Cluster) { - - rdsInstanceMaps := modules.GetChildResourceIDMapByType("aws_rds_cluster_instance") - for _, resource := range modules.GetResourcesByType("aws_rds_cluster") { - cluster, instanceIDs := adaptCluster(resource, modules) - for _, id := range instanceIDs { - rdsInstanceMaps.Resolve(id) - } - clusters = append(clusters, cluster) - } - - orphanResources := modules.GetResourceByIDs(rdsInstanceMaps.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := rds.Cluster{ - Metadata: iacTypes.NewUnmanagedMetadata(), - BackupRetentionPeriodDays: iacTypes.IntDefault(1, iacTypes.NewUnmanagedMetadata()), - ReplicationSourceARN: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Enabled: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - KMSKeyID: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - Instances: nil, - Encryption: rds.Encryption{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EncryptStorage: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - KMSKeyID: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - PublicAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Engine: iacTypes.StringUnresolvable(iacTypes.NewUnmanagedMetadata()), - LatestRestorableTime: iacTypes.TimeUnresolvable(iacTypes.NewUnmanagedMetadata()), - DeletionProtection: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - } - for _, orphan := range orphanResources { - orphanage.Instances = append(orphanage.Instances, adaptClusterInstance(orphan, modules)) - } - clusters = append(clusters, orphanage) - } - - return clusters -} - -func getClassic(modules terraform.Modules) rds.Classic { - classic := rds.Classic{ - DBSecurityGroups: nil, - } - for _, resource := range modules.GetResourcesByType("aws_db_security_group", "aws_redshift_security_group", "aws_elasticache_security_group") { - classic.DBSecurityGroups = append(classic.DBSecurityGroups, adaptClassicDBSecurityGroup(resource)) - } - return classic -} - -func adaptClusterInstance(resource *terraform.Block, modules terraform.Modules) rds.ClusterInstance { - clusterIdAttr := resource.GetAttribute("cluster_identifier") - clusterId := clusterIdAttr.AsStringValueOrDefault("", resource) - - if clusterIdAttr.IsResourceBlockReference("aws_rds_cluster") { - if referenced, err := modules.GetReferencedBlock(clusterIdAttr, resource); err == nil { - clusterId = iacTypes.String(referenced.FullName(), referenced.GetMetadata()) - } - } - - return rds.ClusterInstance{ - ClusterIdentifier: clusterId, - Instance: adaptInstance(resource, modules), - } -} - -func adaptClassicDBSecurityGroup(resource *terraform.Block) rds.DBSecurityGroup { - return rds.DBSecurityGroup{ - Metadata: resource.GetMetadata(), - } -} - -func adaptInstance(resource *terraform.Block, modules terraform.Modules) rds.Instance { - - var ReadReplicaDBInstanceIdentifiers []iacTypes.StringValue - rrdiAttr := resource.GetAttribute("replicate_source_db") - for _, rrdi := range rrdiAttr.AsStringValues() { - ReadReplicaDBInstanceIdentifiers = append(ReadReplicaDBInstanceIdentifiers, rrdi) - } - - var TagList []rds.TagList - tagres := resource.GetBlocks("tags") - for _, tagres := range tagres { - - TagList = append(TagList, rds.TagList{ - Metadata: tagres.GetMetadata(), - }) - } - - var EnabledCloudwatchLogsExports []iacTypes.StringValue - ecweAttr := resource.GetAttribute("enabled_cloudwatch_logs_exports") - for _, ecwe := range ecweAttr.AsStringValues() { - EnabledCloudwatchLogsExports = append(EnabledCloudwatchLogsExports, ecwe) - } - - replicaSource := resource.GetAttribute("replicate_source_db") - replicaSourceValue := "" - if replicaSource.IsNotNil() { - if referenced, err := modules.GetReferencedBlock(replicaSource, resource); err == nil { - replicaSourceValue = referenced.ID() - } - } - return rds.Instance{ - Metadata: resource.GetMetadata(), - BackupRetentionPeriodDays: resource.GetAttribute("backup_retention_period").AsIntValueOrDefault(0, resource), - ReplicationSourceARN: iacTypes.StringExplicit(replicaSourceValue, resource.GetMetadata()), - PerformanceInsights: adaptPerformanceInsights(resource), - Encryption: adaptEncryption(resource), - PublicAccess: resource.GetAttribute("publicly_accessible").AsBoolValueOrDefault(false, resource), - Engine: resource.GetAttribute("engine").AsStringValueOrDefault(rds.EngineAurora, resource), - IAMAuthEnabled: resource.GetAttribute("iam_database_authentication_enabled").AsBoolValueOrDefault(false, resource), - DeletionProtection: resource.GetAttribute("deletion_protection").AsBoolValueOrDefault(false, resource), - DBInstanceArn: resource.GetAttribute("arn").AsStringValueOrDefault("", resource), - StorageEncrypted: resource.GetAttribute("storage_encrypted").AsBoolValueOrDefault(true, resource), - DBInstanceIdentifier: resource.GetAttribute("identifier").AsStringValueOrDefault("", resource), - EngineVersion: resource.GetAttribute("engine_version").AsStringValueOrDefault("", resource), - AutoMinorVersionUpgrade: resource.GetAttribute("auto_minor_version_upgrade").AsBoolValueOrDefault(false, resource), - MultiAZ: resource.GetAttribute("multi_az").AsBoolValueOrDefault(false, resource), - PubliclyAccessible: resource.GetAttribute("publicly_accessible").AsBoolValueOrDefault(false, resource), - LatestRestorableTime: iacTypes.TimeUnresolvable(resource.GetMetadata()), - ReadReplicaDBInstanceIdentifiers: ReadReplicaDBInstanceIdentifiers, - TagList: TagList, - EnabledCloudwatchLogsExports: EnabledCloudwatchLogsExports, - } -} - -func adaptDBParameterGroups(resource *terraform.Block, modules terraform.Modules) rds.ParameterGroups { - - var Parameters []rds.Parameters - paramres := resource.GetBlocks("parameter") - for _, paramres := range paramres { - - Parameters = append(Parameters, rds.Parameters{ - Metadata: paramres.GetMetadata(), - ParameterName: iacTypes.StringDefault("", paramres.GetMetadata()), - ParameterValue: iacTypes.StringDefault("", paramres.GetMetadata()), - }) - } - - return rds.ParameterGroups{ - Metadata: resource.GetMetadata(), - DBParameterGroupName: resource.GetAttribute("name").AsStringValueOrDefault("", resource), - DBParameterGroupFamily: resource.GetAttribute("family").AsStringValueOrDefault("", resource), - Parameters: Parameters, - } -} - -func adaptDBSnapshots(resource *terraform.Block, modules terraform.Modules) rds.Snapshots { - - return rds.Snapshots{ - Metadata: resource.GetMetadata(), - DBSnapshotIdentifier: resource.GetAttribute("db_snapshot_identifier").AsStringValueOrDefault("", resource), - DBSnapshotArn: resource.GetAttribute("db_snapshot_arn").AsStringValueOrDefault("", resource), - Encrypted: resource.GetAttribute("encrypted").AsBoolValueOrDefault(true, resource), - KmsKeyId: resource.GetAttribute("kms_key_id").AsStringValueOrDefault("", resource), - SnapshotAttributes: nil, - } -} - -func adaptCluster(resource *terraform.Block, modules terraform.Modules) (rds.Cluster, []string) { - - clusterInstances, ids := getClusterInstances(resource, modules) - - var public bool - for _, instance := range clusterInstances { - if instance.PublicAccess.IsTrue() { - public = true - break - } - } - - return rds.Cluster{ - Metadata: resource.GetMetadata(), - BackupRetentionPeriodDays: resource.GetAttribute("backup_retention_period").AsIntValueOrDefault(1, resource), - ReplicationSourceARN: resource.GetAttribute("replication_source_identifier").AsStringValueOrDefault("", resource), - PerformanceInsights: adaptPerformanceInsights(resource), - Instances: clusterInstances, - Encryption: adaptEncryption(resource), - PublicAccess: iacTypes.Bool(public, resource.GetMetadata()), - Engine: resource.GetAttribute("engine").AsStringValueOrDefault(rds.EngineAurora, resource), - LatestRestorableTime: iacTypes.TimeUnresolvable(resource.GetMetadata()), - AvailabilityZones: resource.GetAttribute("availability_zones").AsStringValueSliceOrEmpty(), - DeletionProtection: resource.GetAttribute("deletion_protection").AsBoolValueOrDefault(false, resource), - }, ids -} - -func getClusterInstances(resource *terraform.Block, modules terraform.Modules) (clusterInstances []rds.ClusterInstance, instanceIDs []string) { - clusterInstanceResources := modules.GetReferencingResources(resource, "aws_rds_cluster_instance", "cluster_identifier") - - for _, ciResource := range clusterInstanceResources { - instanceIDs = append(instanceIDs, ciResource.ID()) - clusterInstances = append(clusterInstances, adaptClusterInstance(ciResource, modules)) - } - return clusterInstances, instanceIDs -} - -func adaptPerformanceInsights(resource *terraform.Block) rds.PerformanceInsights { - return rds.PerformanceInsights{ - Metadata: resource.GetMetadata(), - Enabled: resource.GetAttribute("performance_insights_enabled").AsBoolValueOrDefault(false, resource), - KMSKeyID: resource.GetAttribute("performance_insights_kms_key_id").AsStringValueOrDefault("", resource), - } -} - -func adaptEncryption(resource *terraform.Block) rds.Encryption { - return rds.Encryption{ - Metadata: resource.GetMetadata(), - EncryptStorage: resource.GetAttribute("storage_encrypted").AsBoolValueOrDefault(false, resource), - KMSKeyID: resource.GetAttribute("kms_key_id").AsStringValueOrDefault("", resource), - } -} diff --git a/pkg/iac/adapters/terraform/aws/rds/adapt_test.go b/pkg/iac/adapters/terraform/aws/rds/adapt_test.go deleted file mode 100644 index 76f045faf288..000000000000 --- a/pkg/iac/adapters/terraform/aws/rds/adapt_test.go +++ /dev/null @@ -1,331 +0,0 @@ -package rds - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected rds.RDS - }{ - { - name: "defined", - terraform: ` - - resource "aws_rds_cluster" "example" { - engine = "aurora-mysql" - availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"] - backup_retention_period = 7 - kms_key_id = "kms_key_1" - storage_encrypted = true - replication_source_identifier = "arn-of-a-source-db-cluster" - deletion_protection = true - } - - resource "aws_rds_cluster_instance" "example" { - cluster_identifier = aws_rds_cluster.example.id - name = "bar" - performance_insights_enabled = true - performance_insights_kms_key_id = "performance_key_0" - kms_key_id = "kms_key_0" - storage_encrypted = true - } - - resource "aws_db_security_group" "example" { - # ... - } - - resource "aws_db_instance" "example" { - publicly_accessible = false - backup_retention_period = 5 - skip_final_snapshot = true - performance_insights_enabled = true - performance_insights_kms_key_id = "performance_key_1" - storage_encrypted = true - kms_key_id = "kms_key_2" - } -`, - expected: rds.RDS{ - Instances: []rds.Instance{ - { - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(5, iacTypes.NewTestMetadata()), - ReplicationSourceARN: iacTypes.String("", iacTypes.NewTestMetadata()), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("performance_key_1", iacTypes.NewTestMetadata()), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - EncryptStorage: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("kms_key_2", iacTypes.NewTestMetadata()), - }, - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Engine: iacTypes.String(rds.EngineAurora, iacTypes.NewTestMetadata()), - StorageEncrypted: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - Clusters: []rds.Cluster{ - { - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(7, iacTypes.NewTestMetadata()), - ReplicationSourceARN: iacTypes.String("arn-of-a-source-db-cluster", iacTypes.NewTestMetadata()), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - EncryptStorage: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("kms_key_1", iacTypes.NewTestMetadata()), - }, - Instances: []rds.ClusterInstance{ - { - Instance: rds.Instance{ - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(0, iacTypes.NewTestMetadata()), - ReplicationSourceARN: iacTypes.String("", iacTypes.NewTestMetadata()), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("performance_key_0", iacTypes.NewTestMetadata()), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - EncryptStorage: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("kms_key_0", iacTypes.NewTestMetadata()), - }, - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Engine: iacTypes.String(rds.EngineAurora, iacTypes.NewTestMetadata()), - StorageEncrypted: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - ClusterIdentifier: iacTypes.String("aws_rds_cluster.example", iacTypes.NewTestMetadata()), - }, - }, - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Engine: iacTypes.String(rds.EngineAuroraMysql, iacTypes.NewTestMetadata()), - AvailabilityZones: iacTypes.StringValueList{ - iacTypes.String("us-west-2a", iacTypes.NewTestMetadata()), - iacTypes.String("us-west-2b", iacTypes.NewTestMetadata()), - iacTypes.String("us-west-2c", iacTypes.NewTestMetadata()), - }, - DeletionProtection: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - Classic: rds.Classic{ - DBSecurityGroups: []rds.DBSecurityGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptInstance(t *testing.T) { - tests := []struct { - name string - terraform string - expected rds.Instance - }{ - { - name: "instance defaults", - terraform: ` - resource "aws_db_instance" "example" { - } -`, - expected: rds.Instance{ - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(0, iacTypes.NewTestMetadata()), - ReplicationSourceARN: iacTypes.String("", iacTypes.NewTestMetadata()), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - EncryptStorage: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Engine: iacTypes.String(rds.EngineAurora, iacTypes.NewTestMetadata()), - StorageEncrypted: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - IAMAuthEnabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptInstance(modules.GetBlocks()[0], modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected rds.Cluster - }{ - { - name: "cluster defaults", - terraform: ` - resource "aws_rds_cluster" "example" { - } -`, - expected: rds.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.NewTestMetadata()), - ReplicationSourceARN: iacTypes.String("", iacTypes.NewTestMetadata()), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - EncryptStorage: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Engine: iacTypes.String(rds.EngineAurora, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted, _ := adaptCluster(modules.GetBlocks()[0], modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_rds_cluster" "example" { - backup_retention_period = 7 - kms_key_id = "kms_key_1" - storage_encrypted = true - replication_source_identifier = "arn-of-a-source-db-cluster" - } - - resource "aws_rds_cluster_instance" "example" { - cluster_identifier = aws_rds_cluster.example.id - backup_retention_period = 7 - performance_insights_enabled = true - performance_insights_kms_key_id = "performance_key" - storage_encrypted = true - kms_key_id = "kms_key_0" - } - - resource "aws_db_security_group" "example" { - } - - resource "aws_db_instance" "example" { - publicly_accessible = false - backup_retention_period = 7 - performance_insights_enabled = true - performance_insights_kms_key_id = "performance_key" - storage_encrypted = true - kms_key_id = "kms_key_0" - } -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Clusters, 1) - require.Len(t, adapted.Instances, 1) - - cluster := adapted.Clusters[0] - instance := adapted.Instances[0] - classic := adapted.Classic - - assert.Equal(t, 2, cluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 7, cluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, cluster.BackupRetentionPeriodDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, cluster.BackupRetentionPeriodDays.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, cluster.Encryption.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, cluster.Encryption.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, cluster.Encryption.EncryptStorage.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, cluster.Encryption.EncryptStorage.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, cluster.ReplicationSourceARN.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, cluster.ReplicationSourceARN.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 9, cluster.Instances[0].Instance.Metadata.Range().GetStartLine()) - assert.Equal(t, 16, cluster.Instances[0].Instance.Metadata.Range().GetEndLine()) - - assert.Equal(t, 2, cluster.Instances[0].ClusterIdentifier.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, cluster.Instances[0].ClusterIdentifier.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, cluster.Instances[0].Instance.BackupRetentionPeriodDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, cluster.Instances[0].Instance.BackupRetentionPeriodDays.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 12, cluster.Instances[0].Instance.PerformanceInsights.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 12, cluster.Instances[0].Instance.PerformanceInsights.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, cluster.Instances[0].Instance.PerformanceInsights.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, cluster.Instances[0].Instance.PerformanceInsights.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 14, cluster.Instances[0].Instance.Encryption.EncryptStorage.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, cluster.Instances[0].Instance.Encryption.EncryptStorage.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 15, cluster.Instances[0].Instance.Encryption.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 15, cluster.Instances[0].Instance.Encryption.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 18, classic.DBSecurityGroups[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 19, classic.DBSecurityGroups[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 21, instance.Metadata.Range().GetStartLine()) - assert.Equal(t, 28, instance.Metadata.Range().GetEndLine()) - - assert.Equal(t, 22, instance.PublicAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 22, instance.PublicAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, instance.BackupRetentionPeriodDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 23, instance.BackupRetentionPeriodDays.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 24, instance.PerformanceInsights.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 24, instance.PerformanceInsights.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 25, instance.PerformanceInsights.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 25, instance.PerformanceInsights.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 26, instance.Encryption.EncryptStorage.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 26, instance.Encryption.EncryptStorage.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 27, instance.Encryption.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 27, instance.Encryption.KMSKeyID.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/redshift/adapt.go b/pkg/iac/adapters/terraform/aws/redshift/adapt.go deleted file mode 100644 index 37ede1a821ab..000000000000 --- a/pkg/iac/adapters/terraform/aws/redshift/adapt.go +++ /dev/null @@ -1,117 +0,0 @@ -package redshift - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) redshift.Redshift { - return redshift.Redshift{ - Clusters: adaptClusters(modules), - SecurityGroups: adaptSecurityGroups(modules), - ClusterParameters: adaptParameters(modules), - ReservedNodes: nil, - } -} - -func adaptClusters(modules terraform.Modules) []redshift.Cluster { - var clusters []redshift.Cluster - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_redshift_cluster") { - clusters = append(clusters, adaptCluster(resource, module)) - } - } - return clusters -} - -func adaptSecurityGroups(modules terraform.Modules) []redshift.SecurityGroup { - var securityGroups []redshift.SecurityGroup - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_redshift_security_group") { - securityGroups = append(securityGroups, adaptSecurityGroup(resource)) - } - } - return securityGroups -} - -func adaptParameters(modules terraform.Modules) []redshift.ClusterParameter { - var Parameters []redshift.ClusterParameter - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_redshift_parameter_group") { - for _, r := range resource.GetBlocks("parameter") { - Parameters = append(Parameters, adaptParameter(r)) - } - } - } - return Parameters -} - -func adaptCluster(resource *terraform.Block, module *terraform.Module) redshift.Cluster { - cluster := redshift.Cluster{ - Metadata: resource.GetMetadata(), - ClusterIdentifier: resource.GetAttribute("cluster_identifier").AsStringValueOrDefault("", resource), - NodeType: resource.GetAttribute("node_type").AsStringValueOrDefault("", resource), - MasterUsername: resource.GetAttribute("master_username").AsStringValueOrDefault("", resource), - NumberOfNodes: resource.GetAttribute("number_of_nodes").AsIntValueOrDefault(1, resource), - PubliclyAccessible: resource.GetAttribute("publicly_accessible").AsBoolValueOrDefault(true, resource), - LoggingEnabled: iacTypes.Bool(false, resource.GetMetadata()), - AutomatedSnapshotRetentionPeriod: iacTypes.Int(0, resource.GetMetadata()), - AllowVersionUpgrade: resource.GetAttribute("allow_version_upgrade").AsBoolValueOrDefault(true, resource), - VpcId: iacTypes.String("", resource.GetMetadata()), - Encryption: redshift.Encryption{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - KMSKeyID: iacTypes.StringDefault("", resource.GetMetadata()), - }, - EndPoint: redshift.EndPoint{ - Metadata: resource.GetMetadata(), - Port: resource.GetAttribute("port").AsIntValueOrDefault(5439, resource), - }, - SubnetGroupName: iacTypes.StringDefault("", resource.GetMetadata()), - } - - encryptedAttr := resource.GetAttribute("encrypted") - cluster.Encryption.Enabled = encryptedAttr.AsBoolValueOrDefault(false, resource) - - if logBlock := resource.GetBlock("logging"); logBlock.IsNotNil() { - cluster.LoggingEnabled = logBlock.GetAttribute("enable").AsBoolValueOrDefault(false, logBlock) - } - - if snapBlock := resource.GetBlock("snapshot_copy"); snapBlock.IsNotNil() { - snapAttr := snapBlock.GetAttribute("retention_period") - cluster.AutomatedSnapshotRetentionPeriod = snapAttr.AsIntValueOrDefault(7, snapBlock) - } - - KMSKeyIDAttr := resource.GetAttribute("kms_key_id") - cluster.Encryption.KMSKeyID = KMSKeyIDAttr.AsStringValueOrDefault("", resource) - if KMSKeyIDAttr.IsResourceBlockReference("aws_kms_key") { - if kmsKeyBlock, err := module.GetReferencedBlock(KMSKeyIDAttr, resource); err == nil { - cluster.Encryption.KMSKeyID = iacTypes.String(kmsKeyBlock.FullName(), kmsKeyBlock.GetMetadata()) - } - } - - subnetGroupNameAttr := resource.GetAttribute("cluster_subnet_group_name") - cluster.SubnetGroupName = subnetGroupNameAttr.AsStringValueOrDefault("", resource) - - return cluster -} - -func adaptSecurityGroup(resource *terraform.Block) redshift.SecurityGroup { - descriptionAttr := resource.GetAttribute("description") - descriptionVal := descriptionAttr.AsStringValueOrDefault("Managed by Terraform", resource) - - return redshift.SecurityGroup{ - Metadata: resource.GetMetadata(), - Description: descriptionVal, - } -} - -func adaptParameter(resource *terraform.Block) redshift.ClusterParameter { - - return redshift.ClusterParameter{ - Metadata: resource.GetMetadata(), - ParameterName: resource.GetAttribute("name").AsStringValueOrDefault("", resource), - ParameterValue: resource.GetAttribute("value").AsStringValueOrDefault("", resource), - } -} diff --git a/pkg/iac/adapters/terraform/aws/redshift/adapt_test.go b/pkg/iac/adapters/terraform/aws/redshift/adapt_test.go deleted file mode 100644 index 3670038eb96d..000000000000 --- a/pkg/iac/adapters/terraform/aws/redshift/adapt_test.go +++ /dev/null @@ -1,227 +0,0 @@ -package redshift - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected redshift.Redshift - }{ - { - name: "reference key id", - terraform: ` - resource "aws_kms_key" "redshift" { - enable_key_rotation = true - } - - resource "aws_redshift_cluster" "example" { - cluster_identifier = "tf-redshift-cluster" - publicly_accessible = false - number_of_nodes = 1 - allow_version_upgrade = false - port = 5440 - encrypted = true - kms_key_id = aws_kms_key.redshift.key_id - cluster_subnet_group_name = "redshift_subnet" - } - - resource "aws_redshift_security_group" "default" { - name = "redshift-sg" - description = "some description" - } -`, - expected: redshift.Redshift{ - Clusters: []redshift.Cluster{ - { - Metadata: iacTypes.NewTestMetadata(), - ClusterIdentifier: iacTypes.String("tf-redshift-cluster", iacTypes.NewTestMetadata()), - PubliclyAccessible: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - NumberOfNodes: iacTypes.Int(1, iacTypes.NewTestMetadata()), - AllowVersionUpgrade: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - EndPoint: redshift.EndPoint{ - Metadata: iacTypes.NewTestMetadata(), - Port: iacTypes.Int(5440, iacTypes.NewTestMetadata()), - }, - Encryption: redshift.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("aws_kms_key.redshift", iacTypes.NewTestMetadata()), - }, - SubnetGroupName: iacTypes.String("redshift_subnet", iacTypes.NewTestMetadata()), - }, - }, - SecurityGroups: []redshift.SecurityGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("some description", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected redshift.Cluster - }{ - { - name: "key as string", - terraform: ` - resource "aws_redshift_cluster" "example" { - cluster_identifier = "tf-redshift-cluster" - publicly_accessible = false - number_of_nodes = 1 - allow_version_upgrade = false - port = 5440 - encrypted = true - kms_key_id = "key-id" - cluster_subnet_group_name = "redshift_subnet" - } -`, - expected: redshift.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - ClusterIdentifier: iacTypes.String("tf-redshift-cluster", iacTypes.NewTestMetadata()), - PubliclyAccessible: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - NumberOfNodes: iacTypes.Int(1, iacTypes.NewTestMetadata()), - AllowVersionUpgrade: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - EndPoint: redshift.EndPoint{ - Metadata: iacTypes.NewTestMetadata(), - Port: iacTypes.Int(5440, iacTypes.NewTestMetadata()), - }, - Encryption: redshift.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("key-id", iacTypes.NewTestMetadata()), - }, - SubnetGroupName: iacTypes.String("redshift_subnet", iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "aws_redshift_cluster" "example" { - } -`, - expected: redshift.Cluster{ - Metadata: iacTypes.NewTestMetadata(), - ClusterIdentifier: iacTypes.String("", iacTypes.NewTestMetadata()), - PubliclyAccessible: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - NumberOfNodes: iacTypes.Int(1, iacTypes.NewTestMetadata()), - AllowVersionUpgrade: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - EndPoint: redshift.EndPoint{ - Metadata: iacTypes.NewTestMetadata(), - Port: iacTypes.Int(5439, iacTypes.NewTestMetadata()), - }, - Encryption: redshift.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - SubnetGroupName: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptCluster(modules.GetBlocks()[0], modules[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptSecurityGroup(t *testing.T) { - tests := []struct { - name string - terraform string - expected redshift.SecurityGroup - }{ - { - name: "defaults", - terraform: ` -resource "" "example" { -} -`, - expected: redshift.SecurityGroup{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("Managed by Terraform", iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptSecurityGroup(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_kms_key" "redshift" { - enable_key_rotation = true - } - - resource "aws_redshift_cluster" "example" { - cluster_identifier = "tf-redshift-cluster" - encrypted = true - kms_key_id = aws_kms_key.redshift.key_id - cluster_subnet_group_name = "subnet name" - } - - resource "aws_redshift_security_group" "default" { - name = "redshift-sg" - description = "some description" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Clusters, 1) - require.Len(t, adapted.SecurityGroups, 1) - cluster := adapted.Clusters[0] - securityGroup := adapted.SecurityGroups[0] - - assert.Equal(t, 6, cluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 11, cluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 8, cluster.Encryption.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 8, cluster.Encryption.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 2, cluster.Encryption.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, cluster.Encryption.KMSKeyID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 10, cluster.SubnetGroupName.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, cluster.SubnetGroupName.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, securityGroup.Metadata.Range().GetStartLine()) - assert.Equal(t, 16, securityGroup.Metadata.Range().GetEndLine()) - - assert.Equal(t, 15, securityGroup.Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 15, securityGroup.Description.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/s3/adapt.go b/pkg/iac/adapters/terraform/aws/s3/adapt.go deleted file mode 100644 index ef9c3052eec5..000000000000 --- a/pkg/iac/adapters/terraform/aws/s3/adapt.go +++ /dev/null @@ -1,18 +0,0 @@ -package s3 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) s3.S3 { - - a := &adapter{ - modules: modules, - bucketMap: make(map[string]*s3.Bucket), - } - - return s3.S3{ - Buckets: a.adaptBuckets(), - } -} diff --git a/pkg/iac/adapters/terraform/aws/s3/adapt_test.go b/pkg/iac/adapters/terraform/aws/s3/adapt_test.go deleted file mode 100644 index b64b1e8d43a4..000000000000 --- a/pkg/iac/adapters/terraform/aws/s3/adapt_test.go +++ /dev/null @@ -1,466 +0,0 @@ -package s3 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_PublicAccessBlock(t *testing.T) { - testCases := []struct { - desc string - source string - expectedBuckets int - hasPublicAccess bool - }{ - { - desc: "public access block is found when using the bucket name as the lookup", - source: ` -resource "aws_s3_bucket" "example" { - bucket = "bucketname" -} - -resource "aws_s3_bucket_public_access_block" "example_access_block"{ - bucket = "bucketname" -} -`, - expectedBuckets: 1, - hasPublicAccess: true, - }, - { - desc: "public access block is found when using the bucket id as the lookup", - source: ` -resource "aws_s3_bucket" "example" { - bucket = "bucketname" -} - -resource "aws_s3_bucket_public_access_block" "example_access_block"{ - bucket = aws_s3_bucket.example.id -} -`, - expectedBuckets: 1, - hasPublicAccess: true, - }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - modules := tftestutil.CreateModulesFromSource(t, tC.source, ".tf") - s3Ctx := Adapt(modules) - - assert.Len(t, s3Ctx.Buckets, tC.expectedBuckets) - - for _, bucket := range s3Ctx.Buckets { - if tC.hasPublicAccess { - assert.NotNil(t, bucket.PublicAccessBlock) - } else { - assert.Nil(t, bucket.PublicAccessBlock) - } - } - - bucket := s3Ctx.Buckets[0] - assert.NotNil(t, bucket.PublicAccessBlock) - - }) - } - -} - -func Test_PublicAccessDoesNotReference(t *testing.T) { - testCases := []struct { - desc string - source string - }{ - { - desc: "just a bucket, no public access block", - source: ` -resource "aws_s3_bucket" "example" { - bucket = "bucketname" -} - `, - }, - { - desc: "bucket with unrelated public access block", - source: ` -resource "aws_s3_bucket" "example" { - bucket = "bucketname" -} - -resource "aws_s3_bucket_public_access_block" "example_access_block"{ - bucket = aws_s3_bucket.other.id -} - `, - }, - { - desc: "bucket with unrelated public access block via name", - source: ` -resource "aws_s3_bucket" "example" { - bucket = "bucketname" -} - -resource "aws_s3_bucket_public_access_block" "example_access_block"{ - bucket = "something" -} - `, - }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, tC.source, ".tf") - s3Ctx := Adapt(modules) - require.Len(t, s3Ctx.Buckets, 1) - assert.Nil(t, s3Ctx.Buckets[0].PublicAccessBlock) - - }) - } -} - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected s3.S3 - }{ - { - name: "basic", - terraform: ` - resource "aws_s3_bucket" "example" { - bucket = "bucket" - } - - resource "aws_s3_bucket_public_access_block" "example" { - bucket = aws_s3_bucket.example.id - - restrict_public_buckets = true - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - - } - - resource "aws_s3_bucket_acl" "example" { - bucket = aws_s3_bucket.example.id - acl = "private" - access_control_policy { - grant { - grantee { - type = "Group" - uri = "http://acs.amazonaws.com/groups/s3/LogDelivery" - } - permission = "READ_ACP" - } - } - } - - resource "aws_s3_bucket_server_side_encryption_configuration" "example" { - bucket = aws_s3_bucket.example.bucket - - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = "string-key" - sse_algorithm = "aws:kms" - } - } - } - - resource "aws_s3_bucket_logging" "example" { - bucket = aws_s3_bucket.example.id - - target_bucket = aws_s3_bucket.example.id - target_prefix = "log/" - } - - resource "aws_s3_bucket_versioning" "versioning_example" { - bucket = aws_s3_bucket.example.id - versioning_configuration { - status = "Enabled" - mfa_delete = "Enabled" - } - } - - resource "aws_s3_bucket_policy" "allow_access_from_another_account" { - bucket = aws_s3_bucket.example.bucket - policy = data.aws_iam_policy_document.allow_access_from_another_account.json - } - - data "aws_iam_policy_document" "allow_access_from_another_account" { - statement { - - actions = [ - "s3:GetObject", - "s3:ListBucket", - ] - - resources = [ - "arn:aws:s3:::*", - ] - } - } - `, - expected: s3.S3{ - Buckets: []s3.Bucket{ - { - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("bucket", iacTypes.NewTestMetadata()), - PublicAccessBlock: &s3.PublicAccessBlock{ - Metadata: iacTypes.NewTestMetadata(), - BlockPublicACLs: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - BlockPublicPolicy: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - IgnorePublicACLs: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - RestrictPublicBuckets: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - BucketPolicies: []iam.Policy{ - { - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("", iacTypes.NewTestMetadata()), - Document: func() iam.Document { - - builder := iamgo.NewPolicyBuilder() - - sb := iamgo.NewStatementBuilder() - sb.WithEffect(iamgo.EffectAllow) - sb.WithActions([]string{"s3:GetObject", "s3:ListBucket"}) - sb.WithResources([]string{"arn:aws:s3:::*"}) - - builder.WithStatement(sb.Build()) - - return iam.Document{ - Parsed: builder.Build(), - Metadata: iacTypes.NewTestMetadata(), - IsOffset: true, - HasRefs: false, - } - }(), - Builtin: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Encryption: s3.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Algorithm: iacTypes.String("aws:kms", iacTypes.NewTestMetadata()), - KMSKeyId: iacTypes.String("string-key", iacTypes.NewTestMetadata()), - }, - Versioning: s3.Versioning{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MFADelete: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Logging: s3.Logging{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - TargetBucket: iacTypes.String("aws_s3_bucket.example", iacTypes.NewTestMetadata()), - }, - ACL: iacTypes.String("private", iacTypes.NewTestMetadata()), - Grants: []s3.Grant{ - { - Metadata: iacTypes.NewTestMetadata(), - Grantee: s3.Grantee{ - Type: iacTypes.StringTest("Group"), - URI: iacTypes.StringTest("http://acs.amazonaws.com/groups/s3/LogDelivery"), - }, - Permissions: iacTypes.StringValueList{ - iacTypes.StringTest("READ_ACP"), - }, - }, - }, - }, - }, - }, - }, - { - name: "bucket with grants", - terraform: ` -resource "aws_s3_bucket" "this" { - bucket = "test" - - grant { - type = "Group" - uri = "http://acs.amazonaws.com/groups/s3/LogDelivery" - permissions = ["FULL_CONTROL"] - } -} -`, - expected: s3.S3{ - Buckets: []s3.Bucket{ - { - Name: iacTypes.StringTest("test"), - ACL: iacTypes.StringTest("private"), - Grants: []s3.Grant{ - { - Grantee: s3.Grantee{ - Type: iacTypes.StringTest("Group"), - URI: iacTypes.StringTest("http://acs.amazonaws.com/groups/s3/LogDelivery"), - }, - Permissions: iacTypes.StringValueList{ - iacTypes.StringTest("FULL_CONTROL"), - }, - }, - }, - }, - }, - }, - }, - { - name: "non-valid SSE algorithm", - terraform: ` -resource "aws_s3_bucket" "this" { - bucket = "test" -} - -resource "aws_s3_bucket_server_side_encryption_configuration" "this" { - bucket = aws_s3_bucket.this.id - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "" - } - } -}`, - expected: s3.S3{ - Buckets: []s3.Bucket{ - { - Name: iacTypes.String("test", iacTypes.NewTestMetadata()), - Encryption: s3.Encryption{ - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - ACL: iacTypes.String("private", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_s3_bucket" "example" { - bucket = "bucket" - } - - resource "aws_s3_bucket_public_access_block" "example" { - bucket = aws_s3_bucket.example.id - - restrict_public_buckets = true - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - } - - resource "aws_s3_bucket_acl" "example" { - bucket = aws_s3_bucket.example.id - acl = "private" - } - - resource "aws_s3_bucket_server_side_encryption_configuration" "example" { - bucket = aws_s3_bucket.example.bucket - - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = "string-key" - sse_algorithm = "aws:kms" - } - } - } - - resource "aws_s3_bucket_logging" "example" { - bucket = aws_s3_bucket.example.id - - target_bucket = aws_s3_bucket.example.id - target_prefix = "log/" - } - - resource "aws_s3_bucket_versioning" "versioning_example" { - bucket = aws_s3_bucket.example.id - versioning_configuration { - status = "Enabled" - } - } - - resource "aws_s3_bucket_policy" "allow_access_from_another_account" { - bucket = aws_s3_bucket.example.bucket - policy = data.aws_iam_policy_document.allow_access_from_another_account.json - } - - data "aws_iam_policy_document" "allow_access_from_another_account" { - statement { - - actions = [ - "s3:GetObject", - "s3:ListBucket", - ] - - resources = [ - "arn:aws:s3:::*", - ] - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Buckets, 1) - bucket := adapted.Buckets[0] - - assert.Equal(t, 2, bucket.Metadata.Range().GetStartLine()) - assert.Equal(t, 4, bucket.Metadata.Range().GetEndLine()) - - assert.Equal(t, 6, bucket.PublicAccessBlock.Metadata.Range().GetStartLine()) - assert.Equal(t, 13, bucket.PublicAccessBlock.Metadata.Range().GetEndLine()) - - assert.Equal(t, 9, bucket.PublicAccessBlock.RestrictPublicBuckets.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 9, bucket.PublicAccessBlock.RestrictPublicBuckets.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 10, bucket.PublicAccessBlock.BlockPublicACLs.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, bucket.PublicAccessBlock.BlockPublicACLs.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, bucket.PublicAccessBlock.BlockPublicPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, bucket.PublicAccessBlock.BlockPublicPolicy.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 12, bucket.PublicAccessBlock.IgnorePublicACLs.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 12, bucket.PublicAccessBlock.IgnorePublicACLs.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, bucket.ACL.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, bucket.ACL.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 20, bucket.Encryption.Metadata.Range().GetStartLine()) - assert.Equal(t, 29, bucket.Encryption.Metadata.Range().GetEndLine()) - - assert.Equal(t, 25, bucket.Encryption.KMSKeyId.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 25, bucket.Encryption.KMSKeyId.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 26, bucket.Encryption.Algorithm.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 26, bucket.Encryption.Algorithm.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 31, bucket.Logging.Metadata.Range().GetStartLine()) - assert.Equal(t, 36, bucket.Logging.Metadata.Range().GetEndLine()) - - assert.Equal(t, 34, bucket.Logging.TargetBucket.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, bucket.Logging.TargetBucket.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 38, bucket.Versioning.Metadata.Range().GetStartLine()) - assert.Equal(t, 43, bucket.Versioning.Metadata.Range().GetEndLine()) - - assert.Equal(t, 41, bucket.Versioning.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 41, bucket.Versioning.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 47, bucket.BucketPolicies[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 47, bucket.BucketPolicies[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 50, bucket.BucketPolicies[0].Document.Metadata.Range().GetStartLine()) - assert.Equal(t, 62, bucket.BucketPolicies[0].Document.Metadata.Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/s3/bucket.go b/pkg/iac/adapters/terraform/aws/s3/bucket.go deleted file mode 100644 index 206b0cbdabdf..000000000000 --- a/pkg/iac/adapters/terraform/aws/s3/bucket.go +++ /dev/null @@ -1,339 +0,0 @@ -package s3 - -import ( - "slices" - - s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type adapter struct { - modules terraform.Modules - bucketMap map[string]*s3.Bucket -} - -func (a *adapter) adaptBuckets() []s3.Bucket { - for _, block := range a.modules.GetResourcesByType("aws_s3_bucket") { - bucket := &s3.Bucket{ - Metadata: block.GetMetadata(), - Name: block.GetAttribute("bucket").AsStringValueOrDefault("", block), - PublicAccessBlock: nil, - BucketPolicies: nil, - Encryption: getEncryption(block, a), - Versioning: getVersioning(block, a), - Logging: getLogging(block, a), - ACL: getBucketAcl(block, a), - Grants: getGrants(block, a), - AccelerateConfigurationStatus: getAccelerateStatus(block, a), - BucketLocation: block.GetAttribute("region").AsStringValueOrDefault("", block), - LifecycleConfiguration: getLifecycle(block, a), - Website: getWebsite(block, a), - Objects: getObject(block, a), - } - a.bucketMap[block.ID()] = bucket - } - - a.adaptBucketPolicies() - a.adaptPublicAccessBlocks() - - var buckets []s3.Bucket - for _, bucket := range a.bucketMap { - buckets = append(buckets, *bucket) - } - - return buckets -} - -func getEncryption(block *terraform.Block, a *adapter) s3.Encryption { - if sseConfgihuration := block.GetBlock("server_side_encryption_configuration"); sseConfgihuration != nil { - return newS3Encryption(block, sseConfgihuration) - } - if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_server_side_encryption_configuration", func(resource *terraform.Block) s3.Encryption { - return newS3Encryption(resource, resource) - }); ok { - return val - } - return s3.Encryption{ - Metadata: block.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, block.GetMetadata()), - KMSKeyId: iacTypes.StringDefault("", block.GetMetadata()), - Algorithm: iacTypes.StringDefault("", block.GetMetadata()), - } -} - -func newS3Encryption(root, sseConfgihuration *terraform.Block) s3.Encryption { - return s3.Encryption{ - Metadata: root.GetMetadata(), - Enabled: isEncrypted(sseConfgihuration), - Algorithm: terraform.MapNestedAttribute( - sseConfgihuration, - "rule.apply_server_side_encryption_by_default.sse_algorithm", - func(attr *terraform.Attribute, parent *terraform.Block) iacTypes.StringValue { - return attr.AsStringValueOrDefault("", parent) - }, - ), - KMSKeyId: terraform.MapNestedAttribute( - sseConfgihuration, - "rule.apply_server_side_encryption_by_default.kms_master_key_id", - func(attr *terraform.Attribute, parent *terraform.Block) iacTypes.StringValue { - return attr.AsStringValueOrDefault("", parent) - }, - ), - } -} - -func getVersioning(block *terraform.Block, a *adapter) s3.Versioning { - versioning := s3.Versioning{ - Metadata: block.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, block.GetMetadata()), - MFADelete: iacTypes.BoolDefault(false, block.GetMetadata()), - } - if lockBlock := block.GetBlock("object_lock_configuration"); lockBlock != nil { - if enabled := isObjeckLockEnabled(lockBlock); enabled != nil { - versioning.Enabled = *enabled - } - } - if vBlock := block.GetBlock("versioning"); vBlock != nil { - versioning.Enabled = vBlock.GetAttribute("enabled").AsBoolValueOrDefault(true, vBlock) - versioning.MFADelete = vBlock.GetAttribute("mfa_delete").AsBoolValueOrDefault(false, vBlock) - } - - if enabled, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_object_lock_configuration", func(resource *terraform.Block) *iacTypes.BoolValue { - if block.GetAttribute("object_lock_enabled").IsTrue() { - return isObjeckLockEnabled(resource) - } - return nil - }); ok && enabled != nil { - versioning.Enabled = *enabled - } - - if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_versioning", getVersioningFromResource); ok { - return val - } - return versioning -} - -func isObjeckLockEnabled(resource *terraform.Block) *iacTypes.BoolValue { - var val iacTypes.BoolValue - attr := resource.GetAttribute("object_lock_enabled") - switch { - case attr.IsNil(): // enabled by default - val = iacTypes.BoolDefault(true, resource.GetMetadata()) - case attr.Equals("Enabled"): - val = iacTypes.Bool(true, attr.GetMetadata()) - } - return &val -} - -// from aws_s3_bucket_versioning -func getVersioningFromResource(block *terraform.Block) s3.Versioning { - versioning := s3.Versioning{ - Metadata: block.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, block.GetMetadata()), - MFADelete: iacTypes.BoolDefault(false, block.GetMetadata()), - } - if config := block.GetBlock("versioning_configuration"); config != nil { - if status := config.GetAttribute("status"); status.IsNotNil() { - versioning.Enabled = iacTypes.Bool(status.Equals("Enabled", terraform.IgnoreCase), status.GetMetadata()) - } - if mfa := config.GetAttribute("mfa_delete"); mfa.IsNotNil() { - versioning.MFADelete = iacTypes.Bool(mfa.Equals("Enabled", terraform.IgnoreCase), mfa.GetMetadata()) - } - } - return versioning -} - -func getLogging(block *terraform.Block, a *adapter) s3.Logging { - if loggingBlock := block.GetBlock("logging"); loggingBlock.IsNotNil() { - targetBucket := loggingBlock.GetAttribute("target_bucket").AsStringValueOrDefault("", loggingBlock) - if referencedBlock, err := a.modules.GetReferencedBlock(loggingBlock.GetAttribute("target_bucket"), loggingBlock); err == nil { - targetBucket = iacTypes.String(referencedBlock.FullName(), loggingBlock.GetAttribute("target_bucket").GetMetadata()) - } - return s3.Logging{ - Metadata: loggingBlock.GetMetadata(), - Enabled: iacTypes.Bool(true, loggingBlock.GetMetadata()), - TargetBucket: targetBucket, - } - } - - if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_logging", func(resource *terraform.Block) s3.Logging { - targetBucket := resource.GetAttribute("target-bucket").AsStringValueOrDefault("", resource) - if referencedBlock, err := a.modules.GetReferencedBlock(resource.GetAttribute("target_bucket"), resource); err == nil { - targetBucket = iacTypes.String(referencedBlock.FullName(), resource.GetAttribute("target_bucket").GetMetadata()) - } - return s3.Logging{ - Metadata: resource.GetMetadata(), - Enabled: hasLogging(resource), - TargetBucket: targetBucket, - } - }); ok { - return val - } - - return s3.Logging{ - Metadata: block.GetMetadata(), - Enabled: iacTypes.Bool(false, block.GetMetadata()), - TargetBucket: iacTypes.StringDefault("", block.GetMetadata()), - } -} - -func getBucketAcl(block *terraform.Block, a *adapter) iacTypes.StringValue { - aclAttr := block.GetAttribute("acl") - if aclAttr.IsString() { - return aclAttr.AsStringValueOrDefault("private", block) - } - - if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_acl", func(resource *terraform.Block) iacTypes.StringValue { - return resource.GetAttribute("acl").AsStringValueOrDefault("private", resource) - }); ok { - return val - } - return iacTypes.StringDefault("private", block.GetMetadata()) -} - -func getGrants(block *terraform.Block, a *adapter) []s3.Grant { - if val, ok := applyForBucketRelatedResource(a, block, "aws_s3_bucket_acl", func(resource *terraform.Block) []s3.Grant { - var grants []s3.Grant - - if acessControlPolicy := resource.GetBlock("access_control_policy"); acessControlPolicy.IsNotNil() { - for _, grantBlock := range acessControlPolicy.GetBlocks("grant") { - grant := s3.Grant{ - Metadata: grantBlock.GetMetadata(), - Permissions: iacTypes.StringValueList{ - grantBlock.GetAttribute("permission").AsStringValueOrDefault("", grantBlock), - }, - } - - if granteeBlock := grantBlock.GetBlock("grantee"); granteeBlock.IsNotNil() { - grant.Grantee = s3.Grantee{ - Metadata: granteeBlock.GetMetadata(), - Type: granteeBlock.GetAttribute("type").AsStringValueOrDefault("", granteeBlock), - URI: granteeBlock.GetAttribute("uri").AsStringValueOrDefault("", granteeBlock), - } - } - - grants = append(grants, grant) - } - } - - return grants - - }); ok { - return val - } - - var grants []s3.Grant - for _, grantBlock := range block.GetBlocks("grant") { - grant := s3.Grant{ - Metadata: grantBlock.GetMetadata(), - Permissions: grantBlock.GetAttribute("permissions").AsStringValueSliceOrEmpty(), - Grantee: s3.Grantee{ - Metadata: grantBlock.GetMetadata(), - Type: grantBlock.GetAttribute("type").AsStringValueOrDefault("", grantBlock), - URI: grantBlock.GetAttribute("uri").AsStringValueOrDefault("", grantBlock), - }, - } - - grants = append(grants, grant) - } - - return grants -} - -func isEncrypted(sseConfgihuration *terraform.Block) iacTypes.BoolValue { - return terraform.MapNestedAttribute( - sseConfgihuration, - "rule.apply_server_side_encryption_by_default.sse_algorithm", - func(attr *terraform.Attribute, parent *terraform.Block) iacTypes.BoolValue { - if attr.IsNil() || !attr.IsString() { - return iacTypes.BoolDefault(false, parent.GetMetadata()) - } - algoVal := attr.Value().AsString() - isValidAlgo := slices.Contains(s3types.ServerSideEncryption("").Values(), s3types.ServerSideEncryption(algoVal)) - return iacTypes.Bool( - isValidAlgo, - attr.GetMetadata(), - ) - }, - ) -} - -func hasLogging(b *terraform.Block) iacTypes.BoolValue { - if loggingBlock := b.GetBlock("logging"); loggingBlock.IsNotNil() { - if targetAttr := loggingBlock.GetAttribute("target_bucket"); targetAttr.IsNotNil() && targetAttr.IsNotEmpty() { - return iacTypes.Bool(true, targetAttr.GetMetadata()) - } - return iacTypes.BoolDefault(false, loggingBlock.GetMetadata()) - } - if targetBucket := b.GetAttribute("target_bucket"); targetBucket.IsNotNil() { - return iacTypes.Bool(true, targetBucket.GetMetadata()) - } - return iacTypes.BoolDefault(false, b.GetMetadata()) -} - -func getLifecycle(b *terraform.Block, a *adapter) []s3.Rules { - - var rules []s3.Rules - for _, r := range a.modules.GetReferencingResources(b, "aws_s3_bucket_lifecycle_configuration", "bucket") { - ruleblock := r.GetBlocks("rule") - for _, rule := range ruleblock { - rules = append(rules, s3.Rules{ - Metadata: rule.GetMetadata(), - Status: rule.GetAttribute("status").AsStringValueOrDefault("Enabled", rule), - }) - } - } - return rules -} - -func getWebsite(b *terraform.Block, a *adapter) (website *s3.Website) { - for _, r := range a.modules.GetReferencingResources(b, "aws_s3_bucket_website_configuration", "bucket") { - website = &s3.Website{ - Metadata: r.GetMetadata(), - } - } - return website -} - -func getObject(b *terraform.Block, a *adapter) []s3.Contents { - var object []s3.Contents - for _, r := range a.modules.GetReferencingResources(b, "aws_s3_object", "bucket") { - object = append(object, s3.Contents{ - Metadata: r.GetMetadata(), - }) - } - return object -} - -func getAccelerateStatus(b *terraform.Block, a *adapter) iacTypes.StringValue { - var status iacTypes.StringValue - for _, r := range a.modules.GetReferencingResources(b, " aws_s3_bucket_accelerate_configuration", "bucket") { - status = r.GetAttribute("status").AsStringValueOrDefault("Enabled", r) - } - return status -} - -func applyForBucketRelatedResource[T any](a *adapter, block *terraform.Block, resType string, fn func(resource *terraform.Block) T) (T, bool) { - for _, resource := range a.modules.GetResourcesByType(resType) { - bucketAttr := resource.GetAttribute("bucket") - if bucketAttr.IsNotNil() { - if bucketAttr.IsString() { - actualBucketName := block.GetAttribute("bucket").AsStringValueOrDefault("", block).Value() - if bucketAttr.Equals(block.ID()) || bucketAttr.Equals(actualBucketName) { - return fn(resource), true - } - } - if referencedBlock, err := a.modules.GetReferencedBlock(bucketAttr, resource); err == nil { - if referencedBlock.ID() == block.ID() { - return fn(resource), true - } - } - } - - } - var res T - return res, false -} diff --git a/pkg/iac/adapters/terraform/aws/s3/bucket_test.go b/pkg/iac/adapters/terraform/aws/s3/bucket_test.go deleted file mode 100644 index aeaca3d5a4c9..000000000000 --- a/pkg/iac/adapters/terraform/aws/s3/bucket_test.go +++ /dev/null @@ -1,330 +0,0 @@ -package s3 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func Test_GetBuckets(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "bucket1" { - - -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - -} - -func Test_BucketGetACL(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - acl = "authenticated-read" - - # ... other configuration ... -}` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.Equal(t, "authenticated-read", s3.Buckets[0].ACL.Value()) - -} - -func Test_V4BucketGetACL(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" -} - -resource "aws_s3_bucket_acl" "example" { - bucket = aws_s3_bucket.example.id - acl = "authenticated-read" -}` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.Equal(t, "authenticated-read", s3.Buckets[0].ACL.Value()) - -} - -func Test_BucketGetLogging(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... - logging { - target_bucket = aws_s3_bucket.log_bucket.id - target_prefix = "log/" - } -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Logging.Enabled.Value()) - -} - -func Test_V4BucketGetLogging(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "log_bucket" { - bucket = "example-log-bucket" - - # ... other configuration ... -} - -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... -} - -resource "aws_s3_bucket_logging" "example" { - bucket = aws_s3_bucket.example.id - target_bucket = aws_s3_bucket.log_bucket.id - target_prefix = "log/" -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 2) - for _, bucket := range s3.Buckets { - switch bucket.Name.Value() { - case "yournamehere": - assert.True(t, bucket.Logging.Enabled.Value()) - case "example-log-bucket": - assert.False(t, bucket.Logging.Enabled.Value()) - } - } -} - -func Test_BucketGetVersioning(t *testing.T) { - source := ` -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... - versioning { - enabled = true - } -}` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Versioning.Enabled.Value()) -} - -func Test_V4BucketGetVersioning(t *testing.T) { - source := ` -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... -} - -resource "aws_s3_bucket_versioning" "example" { - bucket = aws_s3_bucket.example.id - versioning_configuration { - status = "Enabled" - } -}` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Versioning.Enabled.Value()) -} - -func Test_BucketGetVersioningWithLockDeprecated(t *testing.T) { - source := ` -resource "aws_s3_bucket" "example" { - bucket = "mybucket" - object_lock_configuration { - object_lock_enabled = "Enabled" - } -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Versioning.Enabled.Value()) - -} - -func Test_BucketGetVersioningWithLockForNewBucket(t *testing.T) { - source := ` -resource "aws_s3_bucket" "example" { - bucket = "mybucket" - object_lock_enabled = true -} - -resource "aws_s3_bucket_object_lock_configuration" "example" { - bucket = aws_s3_bucket.example.id -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Versioning.Enabled.Value()) - -} - -func Test_BucketGetVersioningWhenLockDisabledButVersioningEnabled(t *testing.T) { - source := ` -resource "aws_s3_bucket" "example" { - bucket = "mybucket" -} - -resource "aws_s3_bucket_object_lock_configuration" "example" { - bucket = aws_s3_bucket.example.id -} - -resource "aws_s3_bucket_versioning" "example" { - bucket = aws_s3_bucket.example.id - versioning_configuration { - status = "Enabled" - } -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Versioning.Enabled.Value()) - -} - -func Test_BucketGetEncryption(t *testing.T) { - - source := ` - resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... - server_side_encryption_configuration { - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = aws_kms_key.mykey.arn - sse_algorithm = "aws:kms" - } - } - } -}` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Encryption.Enabled.Value()) -} - -func Test_V4BucketGetEncryption(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... -} - -resource "aws_s3_bucket_server_side_encryption_configuration" "example" { - bucket = aws_s3_bucket.example.id - - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = aws_kms_key.mykey.arn - sse_algorithm = "aws:kms" - } - } -} -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - assert.Len(t, s3.Buckets, 1) - assert.True(t, s3.Buckets[0].Encryption.Enabled.Value()) -} - -func Test_BucketWithPolicy(t *testing.T) { - - source := ` -resource "aws_s3_bucket" "bucket1" { - bucket = "lol" -} - -resource "aws_s3_bucket_policy" "allow_access_from_another_account" { - bucket = aws_s3_bucket.bucket1.id - policy = data.aws_iam_policy_document.allow_access_from_another_account.json -} - -data "aws_iam_policy_document" "allow_access_from_another_account" { - statement { - principals { - type = "AWS" - identifiers = ["123456789012"] - } - - actions = [ - "s3:GetObject", - "s3:ListBucket", - ] - - resources = [ - aws_s3_bucket.bucket1.arn, - ] - } -} - -` - modules := tftestutil.CreateModulesFromSource(t, source, ".tf") - - s3 := Adapt(modules) - - require.Len(t, s3.Buckets, 1) - require.Len(t, s3.Buckets[0].BucketPolicies, 1) - - policy := s3.Buckets[0].BucketPolicies[0] - - statements, _ := policy.Document.Parsed.Statements() - require.Len(t, statements, 1) - - principals, _ := statements[0].Principals() - actions, _ := statements[0].Actions() - - awsPrincipals, _ := principals.AWS() - require.Len(t, awsPrincipals, 1) - require.Len(t, actions, 2) - -} diff --git a/pkg/iac/adapters/terraform/aws/s3/policies.go b/pkg/iac/adapters/terraform/aws/s3/policies.go deleted file mode 100644 index 3c8804957d0c..000000000000 --- a/pkg/iac/adapters/terraform/aws/s3/policies.go +++ /dev/null @@ -1,53 +0,0 @@ -package s3 - -import ( - iamAdapter "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (a *adapter) adaptBucketPolicies() { - - for _, b := range a.modules.GetResourcesByType("aws_s3_bucket_policy") { - - policyAttr := b.GetAttribute("policy") - if policyAttr.IsNil() { - continue - } - doc, err := iamAdapter.ParsePolicyFromAttr(policyAttr, b, a.modules) - if err != nil { - continue - } - - policy := iam.Policy{ - Metadata: policyAttr.GetMetadata(), - Name: iacTypes.StringDefault("", b.GetMetadata()), - Document: *doc, - Builtin: iacTypes.Bool(false, b.GetMetadata()), - } - - var bucketName string - bucketAttr := b.GetAttribute("bucket") - - if bucketAttr.IsNotNil() { - if referencedBlock, err := a.modules.GetReferencedBlock(bucketAttr, b); err == nil { - if bucket, ok := a.bucketMap[referencedBlock.ID()]; ok { - bucket.BucketPolicies = append(bucket.BucketPolicies, policy) - a.bucketMap[referencedBlock.ID()] = bucket - continue - } - } - } - - if bucketAttr.IsString() { - bucketName = bucketAttr.Value().AsString() - for id, bucket := range a.bucketMap { - if bucket.Name.EqualTo(bucketName) { - bucket.BucketPolicies = append(bucket.BucketPolicies, policy) - a.bucketMap[id] = bucket - break - } - } - } - } -} diff --git a/pkg/iac/adapters/terraform/aws/s3/public_access_block.go b/pkg/iac/adapters/terraform/aws/s3/public_access_block.go deleted file mode 100644 index eff4a71b4005..000000000000 --- a/pkg/iac/adapters/terraform/aws/s3/public_access_block.go +++ /dev/null @@ -1,41 +0,0 @@ -package s3 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" -) - -func (a *adapter) adaptPublicAccessBlocks() { - - for _, b := range a.modules.GetResourcesByType("aws_s3_bucket_public_access_block") { - - pba := s3.PublicAccessBlock{ - Metadata: b.GetMetadata(), - BlockPublicACLs: b.GetAttribute("block_public_acls").AsBoolValueOrDefault(false, b), - BlockPublicPolicy: b.GetAttribute("block_public_policy").AsBoolValueOrDefault(false, b), - IgnorePublicACLs: b.GetAttribute("ignore_public_acls").AsBoolValueOrDefault(false, b), - RestrictPublicBuckets: b.GetAttribute("restrict_public_buckets").AsBoolValueOrDefault(false, b), - } - - var bucketName string - bucketAttr := b.GetAttribute("bucket") - if bucketAttr.IsNotNil() { - if referencedBlock, err := a.modules.GetReferencedBlock(bucketAttr, b); err == nil { - if bucket, ok := a.bucketMap[referencedBlock.ID()]; ok { - bucket.PublicAccessBlock = &pba - a.bucketMap[referencedBlock.ID()] = bucket - continue - } - } - } - if bucketAttr.IsString() { - bucketName = bucketAttr.Value().AsString() - for id, bucket := range a.bucketMap { - if bucketAttr.Equals(id) || bucket.Name.EqualTo(bucketName) { - bucket.PublicAccessBlock = &pba - a.bucketMap[id] = bucket - continue - } - } - } - } -} diff --git a/pkg/iac/adapters/terraform/aws/sns/adapt.go b/pkg/iac/adapters/terraform/aws/sns/adapt.go deleted file mode 100644 index 761bb281a804..000000000000 --- a/pkg/iac/adapters/terraform/aws/sns/adapt.go +++ /dev/null @@ -1,38 +0,0 @@ -package sns - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) sns.SNS { - return sns.SNS{ - Topics: adaptTopics(modules), - } -} - -func adaptTopics(modules terraform.Modules) []sns.Topic { - var topics []sns.Topic - for _, module := range modules { - for _, resource := range module.GetResourcesByType("aws_sns_topic") { - topics = append(topics, adaptTopic(resource)) - } - } - return topics -} - -func adaptTopic(resourceBlock *terraform.Block) sns.Topic { - return sns.Topic{ - Metadata: resourceBlock.GetMetadata(), - ARN: types.StringDefault("", resourceBlock.GetMetadata()), - Encryption: adaptEncryption(resourceBlock), - } -} - -func adaptEncryption(resourceBlock *terraform.Block) sns.Encryption { - return sns.Encryption{ - Metadata: resourceBlock.GetMetadata(), - KMSKeyID: resourceBlock.GetAttribute("kms_master_key_id").AsStringValueOrDefault("", resourceBlock), - } -} diff --git a/pkg/iac/adapters/terraform/aws/sns/adapt_test.go b/pkg/iac/adapters/terraform/aws/sns/adapt_test.go deleted file mode 100644 index 377a918b67ae..000000000000 --- a/pkg/iac/adapters/terraform/aws/sns/adapt_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package sns - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sns" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptTopic(t *testing.T) { - tests := []struct { - name string - terraform string - expected sns.Topic - }{ - { - name: "defined", - terraform: ` - resource "aws_sns_topic" "good_example" { - kms_master_key_id = "/blah" - } -`, - expected: sns.Topic{ - Metadata: iacTypes.NewTestMetadata(), - ARN: iacTypes.String("", iacTypes.NewTestMetadata()), - Encryption: sns.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - KMSKeyID: iacTypes.String("/blah", iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "default", - terraform: ` - resource "aws_sns_topic" "good_example" { - } -`, - expected: sns.Topic{ - Metadata: iacTypes.NewTestMetadata(), - ARN: iacTypes.String("", iacTypes.NewTestMetadata()), - Encryption: sns.Encryption{ - Metadata: iacTypes.NewTestMetadata(), - KMSKeyID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptTopic(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "aws_sns_topic" "good_example" { - kms_master_key_id = "/blah" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Topics, 1) - topic := adapted.Topics[0] - - assert.Equal(t, 2, topic.Metadata.Range().GetStartLine()) - assert.Equal(t, 4, topic.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, topic.Encryption.KMSKeyID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, topic.Encryption.KMSKeyID.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/aws/sqs/adapt.go b/pkg/iac/adapters/terraform/aws/sqs/adapt.go deleted file mode 100644 index 1e40138b47bc..000000000000 --- a/pkg/iac/adapters/terraform/aws/sqs/adapt.go +++ /dev/null @@ -1,167 +0,0 @@ -package sqs - -import ( - "github.com/google/uuid" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/aws/iam" - iamp "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) sqs.SQS { - return sqs.SQS{ - Queues: (&adapter{ - modules: modules, - queues: make(map[string]sqs.Queue), - }).adaptQueues(), - } -} - -type adapter struct { - modules terraform.Modules - queues map[string]sqs.Queue -} - -func (a *adapter) adaptQueues() []sqs.Queue { - for _, resource := range a.modules.GetResourcesByType("aws_sqs_queue") { - a.adaptQueue(resource) - } - - for _, policyBlock := range a.modules.GetResourcesByType("aws_sqs_queue_policy") { - - policy := iamp.Policy{ - Metadata: policyBlock.GetMetadata(), - Name: iacTypes.StringDefault("", policyBlock.GetMetadata()), - Document: iamp.Document{ - Metadata: policyBlock.GetMetadata(), - }, - Builtin: iacTypes.Bool(false, policyBlock.GetMetadata()), - } - if attr := policyBlock.GetAttribute("policy"); attr.IsString() { - dataBlock, err := a.modules.GetBlockById(attr.Value().AsString()) - if err != nil { - parsed, err := iamgo.ParseString(attr.Value().AsString()) - if err != nil { - continue - } - policy.Document.Parsed = *parsed - policy.Document.Metadata = attr.GetMetadata() - } else if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" { // nolint: goconst - if doc, err := iam.ConvertTerraformDocument(a.modules, dataBlock); err == nil { - policy.Document.Parsed = doc.Document - policy.Document.Metadata = doc.Source.GetMetadata() - policy.Document.IsOffset = true - } - } - } else if refBlock, err := a.modules.GetReferencedBlock(attr, policyBlock); err == nil { - if refBlock.Type() == "data" && refBlock.TypeLabel() == "aws_iam_policy_document" { // nolint: goconst - if doc, err := iam.ConvertTerraformDocument(a.modules, refBlock); err == nil { - policy.Document.Parsed = doc.Document - policy.Document.Metadata = doc.Source.GetMetadata() - } - } - } - - if urlAttr := policyBlock.GetAttribute("queue_url"); urlAttr.IsNotNil() { - if refBlock, err := a.modules.GetReferencedBlock(urlAttr, policyBlock); err == nil { - if queue, ok := a.queues[refBlock.ID()]; ok { - queue.Policies = append(queue.Policies, policy) - a.queues[refBlock.ID()] = queue - continue - } - } - } - - a.queues[uuid.NewString()] = sqs.Queue{ - Metadata: iacTypes.NewUnmanagedMetadata(), - QueueURL: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - Encryption: sqs.Encryption{ - Metadata: iacTypes.NewUnmanagedMetadata(), - ManagedEncryption: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - KMSKeyID: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - Policies: []iamp.Policy{policy}, - } - } - - var queues []sqs.Queue - for _, queue := range a.queues { - queues = append(queues, queue) - } - return queues -} - -func (a *adapter) adaptQueue(resource *terraform.Block) { - - kmsKeyIdAttr := resource.GetAttribute("kms_master_key_id") - kmsKeyIdVal := kmsKeyIdAttr.AsStringValueOrDefault("", resource) - managedEncryption := resource.GetAttribute("sqs_managed_sse_enabled") - - var policies []iamp.Policy - if attr := resource.GetAttribute("policy"); attr.IsString() { - - dataBlock, err := a.modules.GetBlockById(attr.Value().AsString()) - if err != nil { - policy := iamp.Policy{ - Metadata: attr.GetMetadata(), - Name: iacTypes.StringDefault("", attr.GetMetadata()), - Document: iamp.Document{ - Metadata: attr.GetMetadata(), - }, - Builtin: iacTypes.Bool(false, attr.GetMetadata()), - } - parsed, err := iamgo.ParseString(attr.Value().AsString()) - if err == nil { - policy.Document.Parsed = *parsed - policy.Document.Metadata = attr.GetMetadata() - policy.Metadata = attr.GetMetadata() - policies = append(policies, policy) - } - } else if dataBlock.Type() == "data" && dataBlock.TypeLabel() == "aws_iam_policy_document" { - if doc, err := iam.ConvertTerraformDocument(a.modules, dataBlock); err == nil { - policy := iamp.Policy{ - Metadata: attr.GetMetadata(), - Name: iacTypes.StringDefault("", attr.GetMetadata()), - Document: iamp.Document{ - Metadata: doc.Source.GetMetadata(), - Parsed: doc.Document, - IsOffset: true, - HasRefs: false, - }, - Builtin: iacTypes.Bool(false, attr.GetMetadata()), - } - policies = append(policies, policy) - } - } - - } else if refBlock, err := a.modules.GetReferencedBlock(attr, resource); err == nil { - if refBlock.Type() == "data" && refBlock.TypeLabel() == "aws_iam_policy_document" { - if doc, err := iam.ConvertTerraformDocument(a.modules, refBlock); err == nil { - policy := iamp.Policy{ - Metadata: doc.Source.GetMetadata(), - Name: iacTypes.StringDefault("", doc.Source.GetMetadata()), - Document: iamp.Document{ - Metadata: doc.Source.GetMetadata(), - Parsed: doc.Document, - }, - Builtin: iacTypes.Bool(false, refBlock.GetMetadata()), - } - policies = append(policies, policy) - } - } - } - - a.queues[resource.ID()] = sqs.Queue{ - Metadata: resource.GetMetadata(), - QueueURL: iacTypes.StringDefault("", resource.GetMetadata()), - Encryption: sqs.Encryption{ - Metadata: resource.GetMetadata(), - ManagedEncryption: managedEncryption.AsBoolValueOrDefault(false, resource), - KMSKeyID: kmsKeyIdVal, - }, - Policies: policies, - } -} diff --git a/pkg/iac/adapters/terraform/aws/sqs/adapt_test.go b/pkg/iac/adapters/terraform/aws/sqs/adapt_test.go deleted file mode 100644 index c9e08a1df887..000000000000 --- a/pkg/iac/adapters/terraform/aws/sqs/adapt_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package sqs - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/iamgo" - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sqs" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected sqs.SQS - }{ - { - name: "np kms key", - terraform: ` - resource "aws_sqs_queue" "good_example" { - - policy = <= azurerm 2.97.0 - if omsAgentBlock := resource.GetBlock("oms_agent"); omsAgentBlock.IsNotNil() { - cluster.AddonProfile.OMSAgent.Metadata = omsAgentBlock.GetMetadata() - cluster.AddonProfile.OMSAgent.Enabled = iacTypes.Bool(true, omsAgentBlock.GetMetadata()) - } - - // azurerm < 2.99.0 - if resource.HasChild("role_based_access_control") { - roleBasedAccessControlBlock := resource.GetBlock("role_based_access_control") - rbEnabledAttr := roleBasedAccessControlBlock.GetAttribute("enabled") - cluster.RoleBasedAccessControl.Metadata = roleBasedAccessControlBlock.GetMetadata() - cluster.RoleBasedAccessControl.Enabled = rbEnabledAttr.AsBoolValueOrDefault(false, roleBasedAccessControlBlock) - } - if resource.HasChild("role_based_access_control_enabled") { - // azurerm >= 2.99.0 - roleBasedAccessControlEnabledAttr := resource.GetAttribute("role_based_access_control_enabled") - cluster.RoleBasedAccessControl.Metadata = roleBasedAccessControlEnabledAttr.GetMetadata() - cluster.RoleBasedAccessControl.Enabled = roleBasedAccessControlEnabledAttr.AsBoolValueOrDefault(false, resource) - } - - if resource.HasChild("azure_active_directory_role_based_access_control") { - azureRoleBasedAccessControl := resource.GetBlock("azure_active_directory_role_based_access_control") - if azureRoleBasedAccessControl.IsNotNil() { - enabledAttr := azureRoleBasedAccessControl.GetAttribute("azure_rbac_enabled") - if !cluster.RoleBasedAccessControl.Enabled.IsTrue() { - cluster.RoleBasedAccessControl.Metadata = azureRoleBasedAccessControl.GetMetadata() - cluster.RoleBasedAccessControl.Enabled = enabledAttr.AsBoolValueOrDefault(false, azureRoleBasedAccessControl) - } - } - } - return cluster -} diff --git a/pkg/iac/adapters/terraform/azure/container/adapt_test.go b/pkg/iac/adapters/terraform/azure/container/adapt_test.go deleted file mode 100644 index 0bd5070c8395..000000000000 --- a/pkg/iac/adapters/terraform/azure/container/adapt_test.go +++ /dev/null @@ -1,260 +0,0 @@ -package container - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptCluster(t *testing.T) { - tests := []struct { - name string - terraform string - expected container.KubernetesCluster - }{ - { - name: "defined", - terraform: ` - resource "azurerm_kubernetes_cluster" "example" { - private_cluster_enabled = true - - network_profile { - network_policy = "calico" - } - - api_server_access_profile { - - authorized_ip_ranges = [ - "1.2.3.4/32" - ] - - } - - addon_profile { - oms_agent { - enabled = true - } - } - - role_based_access_control { - enabled = true - } - } -`, - expected: container.KubernetesCluster{ - Metadata: iacTypes.NewTestMetadata(), - NetworkProfile: container.NetworkProfile{ - Metadata: iacTypes.NewTestMetadata(), - NetworkPolicy: iacTypes.String("calico", iacTypes.NewTestMetadata()), - }, - EnablePrivateCluster: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - APIServerAuthorizedIPRanges: []iacTypes.StringValue{ - iacTypes.String("1.2.3.4/32", iacTypes.NewTestMetadata()), - }, - AddonProfile: container.AddonProfile{ - Metadata: iacTypes.NewTestMetadata(), - OMSAgent: container.OMSAgent{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - RoleBasedAccessControl: container.RoleBasedAccessControl{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "rbac with a new syntax", - terraform: ` - resource "azurerm_kubernetes_cluster" "example" { - role_based_access_control_enabled = true - } -`, - expected: container.KubernetesCluster{ - Metadata: iacTypes.NewTestMetadata(), - NetworkProfile: container.NetworkProfile{ - Metadata: iacTypes.NewTestMetadata(), - NetworkPolicy: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - EnablePrivateCluster: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - AddonProfile: container.AddonProfile{ - Metadata: iacTypes.NewTestMetadata(), - OMSAgent: container.OMSAgent{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - RoleBasedAccessControl: container.RoleBasedAccessControl{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "azurerm_kubernetes_cluster" "example" { - } -`, - expected: container.KubernetesCluster{ - Metadata: iacTypes.NewTestMetadata(), - NetworkProfile: container.NetworkProfile{ - Metadata: iacTypes.NewTestMetadata(), - NetworkPolicy: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - EnablePrivateCluster: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - AddonProfile: container.AddonProfile{ - Metadata: iacTypes.NewTestMetadata(), - OMSAgent: container.OMSAgent{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - RoleBasedAccessControl: container.RoleBasedAccessControl{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "rbac off with k8s rbac on", - terraform: ` -resource "azurerm_kubernetes_cluster" "misreporting_example" { - role_based_access_control_enabled = true # Enable k8s RBAC - azure_active_directory_role_based_access_control { - managed = true # Enable AKS-managed Azure AAD integration - azure_rbac_enabled = false # Explicitly disable Azure RBAC for Kubernetes Authorization - } - } -`, - expected: container.KubernetesCluster{ - Metadata: iacTypes.NewTestMetadata(), - NetworkProfile: container.NetworkProfile{ - Metadata: iacTypes.NewTestMetadata(), - NetworkPolicy: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - EnablePrivateCluster: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - AddonProfile: container.AddonProfile{ - Metadata: iacTypes.NewTestMetadata(), - OMSAgent: container.OMSAgent{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - RoleBasedAccessControl: container.RoleBasedAccessControl{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptCluster(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_kubernetes_cluster" "example" { - private_cluster_enabled = true - - network_profile { - network_policy = "calico" - } - - api_server_access_profile { - - authorized_ip_ranges = [ - "1.2.3.4/32" - ] - - } - - addon_profile { - oms_agent { - enabled = true - } - } - - role_based_access_control { - enabled = true - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.KubernetesClusters, 1) - cluster := adapted.KubernetesClusters[0] - - assert.Equal(t, 3, cluster.EnablePrivateCluster.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, cluster.EnablePrivateCluster.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, cluster.NetworkProfile.Metadata.Range().GetStartLine()) - assert.Equal(t, 7, cluster.NetworkProfile.Metadata.Range().GetEndLine()) - - assert.Equal(t, 6, cluster.NetworkProfile.NetworkPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, cluster.NetworkProfile.NetworkPolicy.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, cluster.APIServerAuthorizedIPRanges[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, cluster.APIServerAuthorizedIPRanges[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, cluster.AddonProfile.Metadata.Range().GetStartLine()) - assert.Equal(t, 21, cluster.AddonProfile.Metadata.Range().GetEndLine()) - - assert.Equal(t, 18, cluster.AddonProfile.OMSAgent.Metadata.Range().GetStartLine()) - assert.Equal(t, 20, cluster.AddonProfile.OMSAgent.Metadata.Range().GetEndLine()) - - assert.Equal(t, 19, cluster.AddonProfile.OMSAgent.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 19, cluster.AddonProfile.OMSAgent.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, cluster.RoleBasedAccessControl.Metadata.Range().GetStartLine()) - assert.Equal(t, 25, cluster.RoleBasedAccessControl.Metadata.Range().GetEndLine()) - - assert.Equal(t, 24, cluster.RoleBasedAccessControl.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 24, cluster.RoleBasedAccessControl.Enabled.GetMetadata().Range().GetEndLine()) -} - -func TestWithLocals(t *testing.T) { - src := ` - variable "ip_whitelist" { - description = "IP Ranges with allowed access." - type = list(string) - default = ["1.2.3.4"] -} - -locals { - ip_whitelist = concat(var.ip_whitelist, split(",", data.azurerm_public_ip.build_agents.ip_address)) -} - -resource "azurerm_kubernetes_cluster" "aks" { - # not working - api_server_access_profile { - authorized_ip_ranges = local.ip_whitelist - } - # working - api_server_access_profile { - authorized_ip_ranges = concat(var.ip_whitelist, split(",", data.azurerm_public_ip.example.ip_address)) - } -}` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.KubernetesClusters, 1) - cluster := adapted.KubernetesClusters[0] - require.Len(t, cluster.APIServerAuthorizedIPRanges, 1) - assert.False(t, cluster.APIServerAuthorizedIPRanges[0].GetMetadata().IsResolvable()) -} diff --git a/pkg/iac/adapters/terraform/azure/database/adapt.go b/pkg/iac/adapters/terraform/azure/database/adapt.go deleted file mode 100644 index ea39949dff72..000000000000 --- a/pkg/iac/adapters/terraform/azure/database/adapt.go +++ /dev/null @@ -1,439 +0,0 @@ -package database - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) database.Database { - - mssqlAdapter := mssqlAdapter{ - alertPolicyIDs: modules.GetChildResourceIDMapByType("azurerm_mssql_server_security_alert_policy"), - auditingPolicyIDs: modules.GetChildResourceIDMapByType("azurerm_mssql_server_extended_auditing_policy", "azurerm_mssql_database_extended_auditing_policy"), - firewallIDs: modules.GetChildResourceIDMapByType("azurerm_sql_firewall_rule", "azurerm_mssql_firewall_rule"), - } - - mysqlAdapter := mysqlAdapter{ - firewallIDs: modules.GetChildResourceIDMapByType("azurerm_mysql_firewall_rule"), - } - - mariaDBAdapter := mariaDBAdapter{ - firewallIDs: modules.GetChildResourceIDMapByType("azurerm_mariadb_firewall_rule"), - } - - postgresqlAdapter := postgresqlAdapter{ - firewallIDs: modules.GetChildResourceIDMapByType("azurerm_postgresql_firewall_rule"), - } - - return database.Database{ - MSSQLServers: mssqlAdapter.adaptMSSQLServers(modules), - MariaDBServers: mariaDBAdapter.adaptMariaDBServers(modules), - MySQLServers: mysqlAdapter.adaptMySQLServers(modules), - PostgreSQLServers: postgresqlAdapter.adaptPostgreSQLServers(modules), - } -} - -type mssqlAdapter struct { - alertPolicyIDs terraform.ResourceIDResolutions - auditingPolicyIDs terraform.ResourceIDResolutions - firewallIDs terraform.ResourceIDResolutions -} - -type mysqlAdapter struct { - firewallIDs terraform.ResourceIDResolutions -} - -type mariaDBAdapter struct { - firewallIDs terraform.ResourceIDResolutions -} - -type postgresqlAdapter struct { - firewallIDs terraform.ResourceIDResolutions -} - -func (a *mssqlAdapter) adaptMSSQLServers(modules terraform.Modules) []database.MSSQLServer { - var mssqlServers []database.MSSQLServer - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_sql_server") { - mssqlServers = append(mssqlServers, a.adaptMSSQLServer(resource, module)) - } - for _, resource := range module.GetResourcesByType("azurerm_mssql_server") { - mssqlServers = append(mssqlServers, a.adaptMSSQLServer(resource, module)) - } - } - - orphanResources := modules.GetResourceByIDs(a.alertPolicyIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := database.MSSQLServer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableSSLEnforcement: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnablePublicNetworkAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - FirewallRules: nil, - }, - ExtendedAuditingPolicies: nil, - SecurityAlertPolicies: nil, - } - for _, policy := range orphanResources { - orphanage.SecurityAlertPolicies = append(orphanage.SecurityAlertPolicies, adaptMSSQLSecurityAlertPolicy(policy)) - } - mssqlServers = append(mssqlServers, orphanage) - - } - - orphanResources = modules.GetResourceByIDs(a.auditingPolicyIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := database.MSSQLServer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableSSLEnforcement: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnablePublicNetworkAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - FirewallRules: nil, - }, - } - for _, policy := range orphanResources { - orphanage.ExtendedAuditingPolicies = append(orphanage.ExtendedAuditingPolicies, adaptMSSQLExtendedAuditingPolicy(policy)) - } - mssqlServers = append(mssqlServers, orphanage) - - } - - orphanResources = modules.GetResourceByIDs(a.firewallIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := database.MSSQLServer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - } - for _, policy := range orphanResources { - orphanage.FirewallRules = append(orphanage.FirewallRules, adaptFirewallRule(policy)) - } - mssqlServers = append(mssqlServers, orphanage) - - } - - return mssqlServers -} -func (a *mysqlAdapter) adaptMySQLServers(modules terraform.Modules) []database.MySQLServer { - var mySQLServers []database.MySQLServer - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_mysql_server") { - mySQLServers = append(mySQLServers, a.adaptMySQLServer(resource, module)) - } - } - - orphanResources := modules.GetResourceByIDs(a.firewallIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := database.MySQLServer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableSSLEnforcement: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnablePublicNetworkAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - FirewallRules: nil, - }, - } - for _, policy := range orphanResources { - orphanage.FirewallRules = append(orphanage.FirewallRules, adaptFirewallRule(policy)) - } - mySQLServers = append(mySQLServers, orphanage) - - } - - return mySQLServers -} - -func (a *mariaDBAdapter) adaptMariaDBServers(modules terraform.Modules) []database.MariaDBServer { - var mariaDBServers []database.MariaDBServer - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_mariadb_server") { - mariaDBServers = append(mariaDBServers, a.adaptMariaDBServer(resource, module)) - } - } - - orphanResources := modules.GetResourceByIDs(a.firewallIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := database.MariaDBServer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableSSLEnforcement: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnablePublicNetworkAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - FirewallRules: nil, - }, - } - for _, policy := range orphanResources { - orphanage.FirewallRules = append(orphanage.FirewallRules, adaptFirewallRule(policy)) - } - mariaDBServers = append(mariaDBServers, orphanage) - - } - - return mariaDBServers -} - -func (a *postgresqlAdapter) adaptPostgreSQLServers(modules terraform.Modules) []database.PostgreSQLServer { - var postgreSQLServers []database.PostgreSQLServer - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_postgresql_server") { - postgreSQLServers = append(postgreSQLServers, a.adaptPostgreSQLServer(resource, module)) - } - } - - orphanResources := modules.GetResourceByIDs(a.firewallIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := database.PostgreSQLServer{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableSSLEnforcement: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnablePublicNetworkAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - FirewallRules: nil, - }, - Config: database.PostgresSQLConfig{ - Metadata: iacTypes.NewUnmanagedMetadata(), - LogCheckpoints: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - ConnectionThrottling: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - LogConnections: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - } - for _, policy := range orphanResources { - orphanage.FirewallRules = append(orphanage.FirewallRules, adaptFirewallRule(policy)) - } - postgreSQLServers = append(postgreSQLServers, orphanage) - - } - - return postgreSQLServers -} - -func (a *mssqlAdapter) adaptMSSQLServer(resource *terraform.Block, module *terraform.Module) database.MSSQLServer { - minTLSVersionVal := iacTypes.StringDefault("", resource.GetMetadata()) - publicAccessVal := iacTypes.BoolDefault(true, resource.GetMetadata()) - enableSSLEnforcementVal := iacTypes.BoolDefault(false, resource.GetMetadata()) - - var auditingPolicies []database.ExtendedAuditingPolicy - var alertPolicies []database.SecurityAlertPolicy - var firewallRules []database.FirewallRule - - if resource.TypeLabel() == "azurerm_mssql_server" { - minTLSVersionAttr := resource.GetAttribute("minimum_tls_version") - minTLSVersionVal = minTLSVersionAttr.AsStringValueOrDefault("", resource) - - publicAccessAttr := resource.GetAttribute("public_network_access_enabled") - publicAccessVal = publicAccessAttr.AsBoolValueOrDefault(true, resource) - - } - - alertPolicyBlocks := module.GetReferencingResources(resource, "azurerm_mssql_server_security_alert_policy", "server_name") - for _, alertBlock := range alertPolicyBlocks { - a.alertPolicyIDs.Resolve(alertBlock.ID()) - alertPolicies = append(alertPolicies, adaptMSSQLSecurityAlertPolicy(alertBlock)) - } - - auditingPoliciesBlocks := module.GetReferencingResources(resource, "azurerm_mssql_server_extended_auditing_policy", "server_id") - if resource.HasChild("extended_auditing_policy") { - auditingPoliciesBlocks = append(auditingPoliciesBlocks, resource.GetBlocks("extended_auditing_policy")...) - } - - databasesRes := module.GetReferencingResources(resource, "azurerm_mssql_database", "server_id") - for _, databaseRes := range databasesRes { - dbAuditingBlocks := module.GetReferencingResources(databaseRes, "azurerm_mssql_database_extended_auditing_policy", "database_id") - auditingPoliciesBlocks = append(auditingPoliciesBlocks, dbAuditingBlocks...) - } - - for _, auditBlock := range auditingPoliciesBlocks { - a.auditingPolicyIDs.Resolve(auditBlock.ID()) - auditingPolicies = append(auditingPolicies, adaptMSSQLExtendedAuditingPolicy(auditBlock)) - } - - firewallRuleBlocks := module.GetReferencingResources(resource, "azurerm_sql_firewall_rule", "server_name") - firewallRuleBlocks = append(firewallRuleBlocks, module.GetReferencingResources(resource, "azurerm_mssql_firewall_rule", "server_id")...) - for _, firewallBlock := range firewallRuleBlocks { - a.firewallIDs.Resolve(firewallBlock.ID()) - firewallRules = append(firewallRules, adaptFirewallRule(firewallBlock)) - } - - return database.MSSQLServer{ - Metadata: resource.GetMetadata(), - Server: database.Server{ - Metadata: resource.GetMetadata(), - EnableSSLEnforcement: enableSSLEnforcementVal, - MinimumTLSVersion: minTLSVersionVal, - EnablePublicNetworkAccess: publicAccessVal, - FirewallRules: firewallRules, - }, - ExtendedAuditingPolicies: auditingPolicies, - SecurityAlertPolicies: alertPolicies, - } -} - -func (a *mysqlAdapter) adaptMySQLServer(resource *terraform.Block, module *terraform.Module) database.MySQLServer { - var firewallRules []database.FirewallRule - - enableSSLEnforcementAttr := resource.GetAttribute("ssl_enforcement_enabled") - enableSSLEnforcementVal := enableSSLEnforcementAttr.AsBoolValueOrDefault(false, resource) - - minTLSVersionAttr := resource.GetAttribute("ssl_minimal_tls_version_enforced") - minTLSVersionVal := minTLSVersionAttr.AsStringValueOrDefault("TLSEnforcementDisabled", resource) - - publicAccessAttr := resource.GetAttribute("public_network_access_enabled") - publicAccessVal := publicAccessAttr.AsBoolValueOrDefault(true, resource) - - firewallRuleBlocks := module.GetReferencingResources(resource, "azurerm_mysql_firewall_rule", "server_name") - for _, firewallBlock := range firewallRuleBlocks { - a.firewallIDs.Resolve(firewallBlock.ID()) - firewallRules = append(firewallRules, adaptFirewallRule(firewallBlock)) - } - - return database.MySQLServer{ - Metadata: resource.GetMetadata(), - Server: database.Server{ - Metadata: resource.GetMetadata(), - EnableSSLEnforcement: enableSSLEnforcementVal, - MinimumTLSVersion: minTLSVersionVal, - EnablePublicNetworkAccess: publicAccessVal, - FirewallRules: firewallRules, - }, - } -} - -func (a *mariaDBAdapter) adaptMariaDBServer(resource *terraform.Block, module *terraform.Module) database.MariaDBServer { - var firewallRules []database.FirewallRule - - enableSSLEnforcementAttr := resource.GetAttribute("ssl_enforcement_enabled") - enableSSLEnforcementVal := enableSSLEnforcementAttr.AsBoolValueOrDefault(false, resource) - - publicAccessAttr := resource.GetAttribute("public_network_access_enabled") - publicAccessVal := publicAccessAttr.AsBoolValueOrDefault(true, resource) - - firewallRuleBlocks := module.GetReferencingResources(resource, "azurerm_mariadb_firewall_rule", "server_name") - for _, firewallBlock := range firewallRuleBlocks { - a.firewallIDs.Resolve(firewallBlock.ID()) - firewallRules = append(firewallRules, adaptFirewallRule(firewallBlock)) - } - - return database.MariaDBServer{ - Metadata: resource.GetMetadata(), - Server: database.Server{ - Metadata: resource.GetMetadata(), - EnableSSLEnforcement: enableSSLEnforcementVal, - MinimumTLSVersion: iacTypes.StringDefault("", resource.GetMetadata()), - EnablePublicNetworkAccess: publicAccessVal, - FirewallRules: firewallRules, - }, - } -} - -func (a *postgresqlAdapter) adaptPostgreSQLServer(resource *terraform.Block, module *terraform.Module) database.PostgreSQLServer { - var firewallRules []database.FirewallRule - - enableSSLEnforcementAttr := resource.GetAttribute("ssl_enforcement_enabled") - enableSSLEnforcementVal := enableSSLEnforcementAttr.AsBoolValueOrDefault(false, resource) - - minTLSVersionAttr := resource.GetAttribute("ssl_minimal_tls_version_enforced") - minTLSVersionVal := minTLSVersionAttr.AsStringValueOrDefault("TLSEnforcementDisabled", resource) - - publicAccessAttr := resource.GetAttribute("public_network_access_enabled") - publicAccessVal := publicAccessAttr.AsBoolValueOrDefault(true, resource) - - firewallRuleBlocks := module.GetReferencingResources(resource, "azurerm_postgresql_firewall_rule", "server_name") - for _, firewallBlock := range firewallRuleBlocks { - a.firewallIDs.Resolve(firewallBlock.ID()) - firewallRules = append(firewallRules, adaptFirewallRule(firewallBlock)) - } - - configBlocks := module.GetReferencingResources(resource, "azurerm_postgresql_configuration", "server_name") - config := adaptPostgreSQLConfig(resource, configBlocks) - - return database.PostgreSQLServer{ - Metadata: resource.GetMetadata(), - Server: database.Server{ - Metadata: resource.GetMetadata(), - EnableSSLEnforcement: enableSSLEnforcementVal, - MinimumTLSVersion: minTLSVersionVal, - EnablePublicNetworkAccess: publicAccessVal, - FirewallRules: firewallRules, - }, - Config: config, - } -} - -func adaptPostgreSQLConfig(resource *terraform.Block, configBlocks []*terraform.Block) database.PostgresSQLConfig { - config := database.PostgresSQLConfig{ - Metadata: resource.GetMetadata(), - LogCheckpoints: iacTypes.BoolDefault(false, resource.GetMetadata()), - ConnectionThrottling: iacTypes.BoolDefault(false, resource.GetMetadata()), - LogConnections: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - for _, configBlock := range configBlocks { - - nameAttr := configBlock.GetAttribute("name") - valAttr := configBlock.GetAttribute("value") - - if nameAttr.Equals("log_checkpoints") { - config.LogCheckpoints = iacTypes.Bool(valAttr.Equals("on"), valAttr.GetMetadata()) - } - if nameAttr.Equals("connection_throttling") { - config.ConnectionThrottling = iacTypes.Bool(valAttr.Equals("on"), valAttr.GetMetadata()) - } - if nameAttr.Equals("log_connections") { - config.LogConnections = iacTypes.Bool(valAttr.Equals("on"), valAttr.GetMetadata()) - } - } - - return config -} - -func adaptMSSQLSecurityAlertPolicy(resource *terraform.Block) database.SecurityAlertPolicy { - - emailAddressesAttr := resource.GetAttribute("email_addresses") - disabledAlertsAttr := resource.GetAttribute("disabled_alerts") - - emailAccountAdminsAttr := resource.GetAttribute("email_account_admins") - emailAccountAdminsVal := emailAccountAdminsAttr.AsBoolValueOrDefault(false, resource) - - return database.SecurityAlertPolicy{ - Metadata: resource.GetMetadata(), - EmailAddresses: emailAddressesAttr.AsStringValues(), - DisabledAlerts: disabledAlertsAttr.AsStringValues(), - EmailAccountAdmins: emailAccountAdminsVal, - } -} - -func adaptFirewallRule(resource *terraform.Block) database.FirewallRule { - startIPAttr := resource.GetAttribute("start_ip_address") - startIPVal := startIPAttr.AsStringValueOrDefault("", resource) - - endIPAttr := resource.GetAttribute("end_ip_address") - endIPVal := endIPAttr.AsStringValueOrDefault("", resource) - - return database.FirewallRule{ - Metadata: resource.GetMetadata(), - StartIP: startIPVal, - EndIP: endIPVal, - } -} - -func adaptMSSQLExtendedAuditingPolicy(resource *terraform.Block) database.ExtendedAuditingPolicy { - retentionInDaysAttr := resource.GetAttribute("retention_in_days") - retentionInDaysVal := retentionInDaysAttr.AsIntValueOrDefault(0, resource) - - return database.ExtendedAuditingPolicy{ - Metadata: resource.GetMetadata(), - RetentionInDays: retentionInDaysVal, - } -} diff --git a/pkg/iac/adapters/terraform/azure/database/adapt_test.go b/pkg/iac/adapters/terraform/azure/database/adapt_test.go deleted file mode 100644 index 05dc61173d8e..000000000000 --- a/pkg/iac/adapters/terraform/azure/database/adapt_test.go +++ /dev/null @@ -1,454 +0,0 @@ -package database - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected database.Database - }{ - { - name: "postgresql", - terraform: ` - resource "azurerm_postgresql_server" "example" { - name = "example" - - public_network_access_enabled = true - ssl_enforcement_enabled = true - ssl_minimal_tls_version_enforced = "TLS1_2" - } - - resource "azurerm_postgresql_configuration" "example" { - name = "log_connections" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_postgresql_server.example.name - value = "on" - } - - resource "azurerm_postgresql_configuration" "example" { - name = "log_checkpoints" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_postgresql_server.example.name - value = "on" - } - - resource "azurerm_postgresql_configuration" "example" { - name = "connection_throttling" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_postgresql_server.example.name - value = "on" - } - - resource "azurerm_postgresql_firewall_rule" "example" { - name = "office" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_postgresql_server.example.name - start_ip_address = "40.112.8.12" - end_ip_address = "40.112.8.12" - } -`, - expected: database.Database{ - PostgreSQLServers: []database.PostgreSQLServer{ - { - Metadata: iacTypes.NewTestMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewTestMetadata(), - EnableSSLEnforcement: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), - EnablePublicNetworkAccess: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - FirewallRules: []database.FirewallRule{ - { - Metadata: iacTypes.NewTestMetadata(), - StartIP: iacTypes.String("40.112.8.12", iacTypes.NewTestMetadata()), - EndIP: iacTypes.String("40.112.8.12", iacTypes.NewTestMetadata()), - }, - }, - }, - Config: database.PostgresSQLConfig{ - Metadata: iacTypes.NewTestMetadata(), - LogConnections: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - LogCheckpoints: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - ConnectionThrottling: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "mariadb", - terraform: ` - resource "azurerm_mariadb_server" "example" { - name = "example-mariadb-server" - location = azurerm_resource_group.example.location - resource_group_name = azurerm_resource_group.example.name - - public_network_access_enabled = false - ssl_enforcement_enabled = true - } - - resource "azurerm_mariadb_firewall_rule" "example" { - name = "test-rule" - server_name = azurerm_mariadb_server.example.name - start_ip_address = "40.112.0.0" - end_ip_address = "40.112.255.255" - } -`, - expected: database.Database{ - MariaDBServers: []database.MariaDBServer{ - { - Metadata: iacTypes.NewTestMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewTestMetadata(), - EnableSSLEnforcement: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MinimumTLSVersion: iacTypes.String("", iacTypes.NewTestMetadata()), - EnablePublicNetworkAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - FirewallRules: []database.FirewallRule{ - { - Metadata: iacTypes.NewTestMetadata(), - StartIP: iacTypes.String("40.112.0.0", iacTypes.NewTestMetadata()), - EndIP: iacTypes.String("40.112.255.255", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - { - name: "mysql", - terraform: ` - resource "azurerm_mysql_server" "example" { - public_network_access_enabled = true - ssl_enforcement_enabled = true - ssl_minimal_tls_version_enforced = "TLS1_2" - } - - resource "azurerm_mysql_firewall_rule" "example" { - server_name = azurerm_mysql_server.example.name - start_ip_address = "40.112.8.12" - end_ip_address = "40.112.8.12" - } - `, - expected: database.Database{ - MySQLServers: []database.MySQLServer{ - { - Metadata: iacTypes.NewTestMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewTestMetadata(), - EnableSSLEnforcement: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), - EnablePublicNetworkAccess: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - FirewallRules: []database.FirewallRule{ - { - Metadata: iacTypes.NewTestMetadata(), - StartIP: iacTypes.String("40.112.8.12", iacTypes.NewTestMetadata()), - EndIP: iacTypes.String("40.112.8.12", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - { - name: "ms sql", - terraform: ` - resource "azurerm_mssql_server" "example" { - name = "mssqlserver" - minimum_tls_version = "1.2" - public_network_access_enabled = false - } - - resource "azurerm_mssql_firewall_rule" "example" { - name = "FirewallRule1" - server_id = azurerm_mssql_server.example.id - start_ip_address = "10.0.17.62" - end_ip_address = "10.0.17.62" - } - - resource "azurerm_mssql_server_security_alert_policy" "example" { - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_mssql_server.example.name - disabled_alerts = [ - "Sql_Injection", - "Data_Exfiltration" - ] - email_account_admins = true - email_addresses = [ - "example@example.com" - ] - } - - resource "azurerm_mssql_server_extended_auditing_policy" "example" { - server_id = azurerm_mssql_server.example.id - retention_in_days = 6 - } - `, - expected: database.Database{ - MSSQLServers: []database.MSSQLServer{ - { - Metadata: iacTypes.NewTestMetadata(), - Server: database.Server{ - Metadata: iacTypes.NewTestMetadata(), - MinimumTLSVersion: iacTypes.String("1.2", iacTypes.NewTestMetadata()), - EnablePublicNetworkAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - EnableSSLEnforcement: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - FirewallRules: []database.FirewallRule{ - { - Metadata: iacTypes.NewTestMetadata(), - StartIP: iacTypes.String("10.0.17.62", iacTypes.NewTestMetadata()), - EndIP: iacTypes.String("10.0.17.62", iacTypes.NewTestMetadata()), - }, - }, - }, - ExtendedAuditingPolicies: []database.ExtendedAuditingPolicy{ - { - Metadata: iacTypes.NewTestMetadata(), - RetentionInDays: iacTypes.Int(6, iacTypes.NewTestMetadata()), - }, - }, - SecurityAlertPolicies: []database.SecurityAlertPolicy{ - { - Metadata: iacTypes.NewTestMetadata(), - EmailAddresses: []iacTypes.StringValue{ - iacTypes.String("example@example.com", iacTypes.NewTestMetadata()), - }, - DisabledAlerts: []iacTypes.StringValue{ - iacTypes.String("Sql_Injection", iacTypes.NewTestMetadata()), - iacTypes.String("Data_Exfiltration", iacTypes.NewTestMetadata()), - }, - EmailAccountAdmins: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_postgresql_server" "example" { - public_network_access_enabled = true - ssl_enforcement_enabled = true - ssl_minimal_tls_version_enforced = "TLS1_2" - } - - resource "azurerm_postgresql_configuration" "example" { - name = "log_connections" - server_name = azurerm_postgresql_server.example.name - value = "on" - } - - resource "azurerm_postgresql_configuration" "example" { - name = "log_checkpoints" - server_name = azurerm_postgresql_server.example.name - value = "on" - } - - resource "azurerm_postgresql_configuration" "example" { - name = "connection_throttling" - server_name = azurerm_postgresql_server.example.name - value = "on" - } - - resource "azurerm_postgresql_firewall_rule" "example" { - name = "office" - server_name = azurerm_postgresql_server.example.name - start_ip_address = "40.112.8.12" - end_ip_address = "40.112.8.12" - } - - resource "azurerm_mariadb_server" "example" { - public_network_access_enabled = false - ssl_enforcement_enabled = true - } - - resource "azurerm_mariadb_firewall_rule" "example" { - name = "test-rule" - server_name = azurerm_mariadb_server.example.name - start_ip_address = "40.112.0.0" - end_ip_address = "40.112.255.255" - } - - resource "azurerm_mysql_server" "example" { - public_network_access_enabled = true - ssl_enforcement_enabled = true - ssl_minimal_tls_version_enforced = "TLS1_2" - } - - resource "azurerm_mysql_firewall_rule" "example" { - server_name = azurerm_mysql_server.example.name - start_ip_address = "40.112.8.12" - end_ip_address = "40.112.8.12" - } - - resource "azurerm_mssql_server" "example" { - name = "mssqlserver" - public_network_access_enabled = false - minimum_tls_version = "1.2" - } - - resource "azurerm_mssql_firewall_rule" "example" { - name = "FirewallRule1" - server_id = azurerm_mssql_server.example.id - start_ip_address = "10.0.17.62" - end_ip_address = "10.0.17.62" - } - - resource "azurerm_mssql_server_security_alert_policy" "example" { - server_name = azurerm_mssql_server.example.name - disabled_alerts = [ - "Sql_Injection", - "Data_Exfiltration" - ] - email_account_admins = true - email_addresses = [ - "example@example.com" - ] - } - - resource "azurerm_mssql_server_extended_auditing_policy" "example" { - server_id = azurerm_mssql_server.example.id - retention_in_days = 6 - } - ` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.PostgreSQLServers, 1) - require.Len(t, adapted.MariaDBServers, 1) - require.Len(t, adapted.MySQLServers, 1) - require.Len(t, adapted.MSSQLServers, 1) - - postgres := adapted.PostgreSQLServers[0] - mariadb := adapted.MariaDBServers[0] - mysql := adapted.MySQLServers[0] - mssql := adapted.MSSQLServers[0] - - assert.Equal(t, 2, postgres.Metadata.Range().GetStartLine()) - assert.Equal(t, 6, postgres.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, postgres.EnablePublicNetworkAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, postgres.EnablePublicNetworkAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, postgres.EnableSSLEnforcement.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, postgres.EnableSSLEnforcement.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, postgres.MinimumTLSVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, postgres.MinimumTLSVersion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, postgres.Config.LogConnections.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, postgres.Config.LogConnections.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, postgres.Config.LogCheckpoints.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, postgres.Config.LogCheckpoints.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, postgres.Config.ConnectionThrottling.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 23, postgres.Config.ConnectionThrottling.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 26, postgres.FirewallRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 31, postgres.FirewallRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 29, postgres.FirewallRules[0].StartIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 29, postgres.FirewallRules[0].StartIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 30, postgres.FirewallRules[0].EndIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 30, postgres.FirewallRules[0].EndIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 33, mariadb.Metadata.Range().GetStartLine()) - assert.Equal(t, 36, mariadb.Metadata.Range().GetEndLine()) - - assert.Equal(t, 34, mariadb.EnablePublicNetworkAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, mariadb.EnablePublicNetworkAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 35, mariadb.EnableSSLEnforcement.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 35, mariadb.EnableSSLEnforcement.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 38, mariadb.FirewallRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 43, mariadb.FirewallRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 41, mariadb.FirewallRules[0].StartIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 41, mariadb.FirewallRules[0].StartIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 42, mariadb.FirewallRules[0].EndIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 42, mariadb.FirewallRules[0].EndIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 45, mysql.Metadata.Range().GetStartLine()) - assert.Equal(t, 49, mysql.Metadata.Range().GetEndLine()) - - assert.Equal(t, 46, mysql.EnablePublicNetworkAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 46, mysql.EnablePublicNetworkAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 47, mysql.EnableSSLEnforcement.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 47, mysql.EnableSSLEnforcement.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 48, mysql.MinimumTLSVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 48, mysql.MinimumTLSVersion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 51, mysql.FirewallRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 55, mysql.FirewallRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 53, mysql.FirewallRules[0].StartIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 53, mysql.FirewallRules[0].StartIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 54, mysql.FirewallRules[0].EndIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 54, mysql.FirewallRules[0].EndIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 57, mssql.Metadata.Range().GetStartLine()) - assert.Equal(t, 61, mssql.Metadata.Range().GetEndLine()) - - assert.Equal(t, 59, mssql.EnablePublicNetworkAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 59, mssql.EnablePublicNetworkAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 60, mssql.MinimumTLSVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 60, mssql.MinimumTLSVersion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 63, mssql.FirewallRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 68, mssql.FirewallRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 66, mssql.FirewallRules[0].StartIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 66, mssql.FirewallRules[0].StartIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 67, mssql.FirewallRules[0].EndIP.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 67, mssql.FirewallRules[0].EndIP.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 70, mssql.SecurityAlertPolicies[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 80, mssql.SecurityAlertPolicies[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 72, mssql.SecurityAlertPolicies[0].DisabledAlerts[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 75, mssql.SecurityAlertPolicies[0].DisabledAlerts[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 76, mssql.SecurityAlertPolicies[0].EmailAccountAdmins.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 76, mssql.SecurityAlertPolicies[0].EmailAccountAdmins.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 77, mssql.SecurityAlertPolicies[0].EmailAddresses[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 79, mssql.SecurityAlertPolicies[0].EmailAddresses[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 82, mssql.ExtendedAuditingPolicies[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 85, mssql.ExtendedAuditingPolicies[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 84, mssql.ExtendedAuditingPolicies[0].RetentionInDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 84, mssql.ExtendedAuditingPolicies[0].RetentionInDays.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/azure/datafactory/adapt.go b/pkg/iac/adapters/terraform/azure/datafactory/adapt.go deleted file mode 100644 index 2dfc108ee3e9..000000000000 --- a/pkg/iac/adapters/terraform/azure/datafactory/adapt.go +++ /dev/null @@ -1,33 +0,0 @@ -package datafactory - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datafactory" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) datafactory.DataFactory { - return datafactory.DataFactory{ - DataFactories: adaptFactories(modules), - } -} - -func adaptFactories(modules terraform.Modules) []datafactory.Factory { - var factories []datafactory.Factory - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_data_factory") { - factories = append(factories, adaptFactory(resource)) - } - } - return factories -} - -func adaptFactory(resource *terraform.Block) datafactory.Factory { - enablePublicNetworkAttr := resource.GetAttribute("public_network_enabled") - enablePublicNetworkVal := enablePublicNetworkAttr.AsBoolValueOrDefault(true, resource) - - return datafactory.Factory{ - Metadata: resource.GetMetadata(), - EnablePublicNetwork: enablePublicNetworkVal, - } -} diff --git a/pkg/iac/adapters/terraform/azure/datafactory/adapt_test.go b/pkg/iac/adapters/terraform/azure/datafactory/adapt_test.go deleted file mode 100644 index 5a98977002d9..000000000000 --- a/pkg/iac/adapters/terraform/azure/datafactory/adapt_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package datafactory - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datafactory" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptFactory(t *testing.T) { - tests := []struct { - name string - terraform string - expected datafactory.Factory - }{ - { - name: "defined", - terraform: ` - resource "azurerm_data_factory" "example" { - name = "example" - location = azurerm_resource_group.example.location - resource_group_name = azurerm_resource_group.example.name - public_network_enabled = false - } -`, - expected: datafactory.Factory{ - Metadata: iacTypes.NewTestMetadata(), - EnablePublicNetwork: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "default", - terraform: ` - resource "azurerm_data_factory" "example" { - name = "example" - } -`, - expected: datafactory.Factory{ - Metadata: iacTypes.NewTestMetadata(), - EnablePublicNetwork: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptFactory(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_data_factory" "example" { - name = "example" - location = azurerm_resource_group.example.location - resource_group_name = azurerm_resource_group.example.name - public_network_enabled = false - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.DataFactories, 1) - dataFactory := adapted.DataFactories[0] - - assert.Equal(t, 6, dataFactory.EnablePublicNetwork.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, dataFactory.EnablePublicNetwork.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/azure/datalake/adapt.go b/pkg/iac/adapters/terraform/azure/datalake/adapt.go deleted file mode 100644 index 3f81d8925fd8..000000000000 --- a/pkg/iac/adapters/terraform/azure/datalake/adapt.go +++ /dev/null @@ -1,38 +0,0 @@ -package datalake - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datalake" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) datalake.DataLake { - return datalake.DataLake{ - Stores: adaptStores(modules), - } -} - -func adaptStores(modules terraform.Modules) []datalake.Store { - var stores []datalake.Store - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_data_lake_store") { - stores = append(stores, adaptStore(resource)) - } - } - return stores -} - -func adaptStore(resource *terraform.Block) datalake.Store { - store := datalake.Store{ - Metadata: resource.GetMetadata(), - EnableEncryption: types.BoolDefault(true, resource.GetMetadata()), - } - encryptionStateAttr := resource.GetAttribute("encryption_state") - if encryptionStateAttr.Equals("Disabled") { - store.EnableEncryption = types.Bool(false, encryptionStateAttr.GetMetadata()) - } else if encryptionStateAttr.Equals("Enabled") { - store.EnableEncryption = types.Bool(true, encryptionStateAttr.GetMetadata()) - } - return store -} diff --git a/pkg/iac/adapters/terraform/azure/datalake/adapt_test.go b/pkg/iac/adapters/terraform/azure/datalake/adapt_test.go deleted file mode 100644 index c902787eb790..000000000000 --- a/pkg/iac/adapters/terraform/azure/datalake/adapt_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package datalake - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datalake" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptStore(t *testing.T) { - tests := []struct { - name string - terraform string - expected datalake.Store - }{ - { - name: "enabled", - terraform: ` - resource "azurerm_data_lake_store" "good_example" { - encryption_state = "Enabled" - } -`, - expected: datalake.Store{ - Metadata: iacTypes.NewTestMetadata(), - EnableEncryption: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "disabled", - terraform: ` - resource "azurerm_data_lake_store" "good_example" { - encryption_state = "Disabled" - } -`, - expected: datalake.Store{ - Metadata: iacTypes.NewTestMetadata(), - EnableEncryption: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "enabled by default", - terraform: ` - resource "azurerm_data_lake_store" "good_example" { - } -`, - expected: datalake.Store{ - Metadata: iacTypes.NewTestMetadata(), - EnableEncryption: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptStore(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_data_lake_store" "good_example" { - encryption_state = "Disabled" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Stores, 1) - store := adapted.Stores[0] - - assert.Equal(t, 3, store.EnableEncryption.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, store.EnableEncryption.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/azure/keyvault/adapt.go b/pkg/iac/adapters/terraform/azure/keyvault/adapt.go deleted file mode 100644 index 87461bfbeedc..000000000000 --- a/pkg/iac/adapters/terraform/azure/keyvault/adapt.go +++ /dev/null @@ -1,157 +0,0 @@ -package keyvault - -import ( - "time" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/keyvault" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) keyvault.KeyVault { - adapter := adapter{ - vaultSecretIDs: modules.GetChildResourceIDMapByType("azurerm_key_vault_secret"), - vaultKeyIDs: modules.GetChildResourceIDMapByType("azurerm_key_vault_key"), - } - - return keyvault.KeyVault{ - Vaults: adapter.adaptVaults(modules), - } -} - -type adapter struct { - vaultSecretIDs terraform.ResourceIDResolutions - vaultKeyIDs terraform.ResourceIDResolutions -} - -func (a *adapter) adaptVaults(modules terraform.Modules) []keyvault.Vault { - - var vaults []keyvault.Vault - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_key_vault") { - vaults = append(vaults, a.adaptVault(resource, module)) - - } - } - - orphanResources := modules.GetResourceByIDs(a.vaultSecretIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := keyvault.Vault{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Secrets: nil, - Keys: nil, - EnablePurgeProtection: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - SoftDeleteRetentionDays: iacTypes.IntDefault(0, iacTypes.NewUnmanagedMetadata()), - NetworkACLs: keyvault.NetworkACLs{ - Metadata: iacTypes.NewUnmanagedMetadata(), - DefaultAction: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - } - for _, secretResource := range orphanResources { - orphanage.Secrets = append(orphanage.Secrets, adaptSecret(secretResource)) - } - vaults = append(vaults, orphanage) - } - - orphanResources = modules.GetResourceByIDs(a.vaultKeyIDs.Orphans()...) - - if len(orphanResources) > 0 { - orphanage := keyvault.Vault{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Secrets: nil, - Keys: nil, - EnablePurgeProtection: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - SoftDeleteRetentionDays: iacTypes.IntDefault(0, iacTypes.NewUnmanagedMetadata()), - NetworkACLs: keyvault.NetworkACLs{ - Metadata: iacTypes.NewUnmanagedMetadata(), - DefaultAction: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - } - for _, secretResource := range orphanResources { - orphanage.Keys = append(orphanage.Keys, adaptKey(secretResource)) - } - vaults = append(vaults, orphanage) - } - - return vaults -} - -func (a *adapter) adaptVault(resource *terraform.Block, module *terraform.Module) keyvault.Vault { - var keys []keyvault.Key - var secrets []keyvault.Secret - - defaultActionVal := iacTypes.StringDefault("", resource.GetMetadata()) - - secretBlocks := module.GetReferencingResources(resource, "azurerm_key_vault_secret", "key_vault_id") - for _, secretBlock := range secretBlocks { - a.vaultSecretIDs.Resolve(secretBlock.ID()) - secrets = append(secrets, adaptSecret(secretBlock)) - } - - keyBlocks := module.GetReferencingResources(resource, "azurerm_key_vault_key", "key_vault_id") - for _, keyBlock := range keyBlocks { - a.vaultKeyIDs.Resolve(keyBlock.ID()) - keys = append(keys, adaptKey(keyBlock)) - } - - purgeProtectionAttr := resource.GetAttribute("purge_protection_enabled") - purgeProtectionVal := purgeProtectionAttr.AsBoolValueOrDefault(false, resource) - - softDeleteRetentionDaysAttr := resource.GetAttribute("soft_delete_retention_days") - softDeleteRetentionDaysVal := softDeleteRetentionDaysAttr.AsIntValueOrDefault(0, resource) - - aclMetadata := iacTypes.NewUnmanagedMetadata() - if aclBlock := resource.GetBlock("network_acls"); aclBlock.IsNotNil() { - aclMetadata = aclBlock.GetMetadata() - defaultActionAttr := aclBlock.GetAttribute("default_action") - defaultActionVal = defaultActionAttr.AsStringValueOrDefault("", resource.GetBlock("network_acls")) - } - - return keyvault.Vault{ - Metadata: resource.GetMetadata(), - Secrets: secrets, - Keys: keys, - EnablePurgeProtection: purgeProtectionVal, - SoftDeleteRetentionDays: softDeleteRetentionDaysVal, - NetworkACLs: keyvault.NetworkACLs{ - Metadata: aclMetadata, - DefaultAction: defaultActionVal, - }, - } -} - -func adaptSecret(resource *terraform.Block) keyvault.Secret { - contentTypeAttr := resource.GetAttribute("content_type") - contentTypeVal := contentTypeAttr.AsStringValueOrDefault("", resource) - - return keyvault.Secret{ - Metadata: resource.GetMetadata(), - ContentType: contentTypeVal, - ExpiryDate: resolveExpiryDate(resource), - } -} - -func adaptKey(resource *terraform.Block) keyvault.Key { - - return keyvault.Key{ - Metadata: resource.GetMetadata(), - ExpiryDate: resolveExpiryDate(resource), - } -} - -func resolveExpiryDate(resource *terraform.Block) iacTypes.TimeValue { - expiryDateAttr := resource.GetAttribute("expiration_date") - expiryDateVal := iacTypes.TimeDefault(time.Time{}, resource.GetMetadata()) - - if expiryDateAttr.IsString() { - expiryDateString := expiryDateAttr.Value().AsString() - if expiryDate, err := time.Parse(time.RFC3339, expiryDateString); err == nil { - expiryDateVal = iacTypes.Time(expiryDate, expiryDateAttr.GetMetadata()) - } - } else if expiryDateAttr.IsNotNil() { - expiryDateVal = iacTypes.TimeUnresolvable(expiryDateAttr.GetMetadata()) - } - - return expiryDateVal -} diff --git a/pkg/iac/adapters/terraform/azure/keyvault/adapt_test.go b/pkg/iac/adapters/terraform/azure/keyvault/adapt_test.go deleted file mode 100644 index 9c8f1ace0320..000000000000 --- a/pkg/iac/adapters/terraform/azure/keyvault/adapt_test.go +++ /dev/null @@ -1,269 +0,0 @@ -package keyvault - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/keyvault" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected keyvault.KeyVault - }{ - { - name: "defined", - terraform: ` - resource "azurerm_key_vault" "example" { - name = "examplekeyvault" - enabled_for_disk_encryption = true - soft_delete_retention_days = 7 - purge_protection_enabled = true - - network_acls { - bypass = "AzureServices" - default_action = "Deny" - } - } -`, - expected: keyvault.KeyVault{ - Vaults: []keyvault.Vault{ - { - Metadata: iacTypes.NewTestMetadata(), - EnablePurgeProtection: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - SoftDeleteRetentionDays: iacTypes.Int(7, iacTypes.NewTestMetadata()), - NetworkACLs: keyvault.NetworkACLs{ - Metadata: iacTypes.NewTestMetadata(), - DefaultAction: iacTypes.String("Deny", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "azurerm_key_vault" "example" { - } -`, - expected: keyvault.KeyVault{ - Vaults: []keyvault.Vault{ - { - Metadata: iacTypes.NewTestMetadata(), - EnablePurgeProtection: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - SoftDeleteRetentionDays: iacTypes.Int(0, iacTypes.NewTestMetadata()), - NetworkACLs: keyvault.NetworkACLs{ - Metadata: iacTypes.NewTestMetadata(), - DefaultAction: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptSecret(t *testing.T) { - tests := []struct { - name string - terraform string - expected keyvault.Secret - }{ - { - name: "defaults", - terraform: ` - resource "azurerm_key_vault_secret" "example" { - } -`, - expected: keyvault.Secret{ - Metadata: iacTypes.NewTestMetadata(), - ContentType: iacTypes.String("", iacTypes.NewTestMetadata()), - ExpiryDate: iacTypes.Time(time.Time{}, iacTypes.NewTestMetadata()), - }, - }, - { - name: "defined", - terraform: ` - resource "azurerm_key_vault_secret" "example" { - content_type = "password" - expiration_date = "1982-12-31T00:00:00Z" - } -`, - expected: keyvault.Secret{ - Metadata: iacTypes.NewTestMetadata(), - ContentType: iacTypes.String("password", iacTypes.NewTestMetadata()), - ExpiryDate: iacTypes.Time(func(timeVal string) time.Time { - parsed, _ := time.Parse(time.RFC3339, timeVal) - return parsed - }("1982-12-31T00:00:00Z"), iacTypes.NewTestMetadata())}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptSecret(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptKey(t *testing.T) { - tests := []struct { - name string - terraform string - expected keyvault.Key - }{ - { - name: "defined", - terraform: ` - resource "azurerm_key_vault_key" "example" { - name = "generated-certificate" - expiration_date = "1982-12-31T00:00:00Z" - } -`, - expected: keyvault.Key{ - Metadata: iacTypes.NewTestMetadata(), - ExpiryDate: iacTypes.Time(func(timeVal string) time.Time { - parsed, _ := time.Parse(time.RFC3339, timeVal) - return parsed - }("1982-12-31T00:00:00Z"), iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "azurerm_key_vault_key" "example" { - } -`, - expected: keyvault.Key{ - Metadata: iacTypes.NewTestMetadata(), - ExpiryDate: iacTypes.Time(time.Time{}, iacTypes.NewTestMetadata()), - }, - }, - { - name: "expiration date refers to the resource", - terraform: ` -terraform { - required_version = ">=1.3.0" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = ">=3.0.0" - } - time = { - source = "hashicorp/time" - version = ">=0.9.0" - } - } -} - -resource "azurerm_key_vault" "this" { - name = "keyvault" - location = "us-west" - resource_group_name = "resource-group" - tenant_id = "tenant-id" - sku_name = "Standard" -} - -resource "time_offset" "expiry" { - offset_years = 1 - base_rfc3339 = "YYYY-MM-DDTHH:MM:SSZ" -} - -resource "azurerm_key_vault_key" "this" { - name = "key" - key_vault_id = azurerm_key_vault.this.id - key_type = "RSA" - key_size = 2048 - key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] - expiration_date = time_offset.expiry.rfc3339 -} -`, - expected: keyvault.Key{ - Metadata: iacTypes.NewTestMetadata(), - ExpiryDate: iacTypes.TimeUnresolvable(iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptKey(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_key_vault" "example" { - name = "examplekeyvault" - enabled_for_disk_encryption = true - soft_delete_retention_days = 7 - purge_protection_enabled = true - - network_acls { - bypass = "AzureServices" - default_action = "Deny" - } - } - - resource "azurerm_key_vault_key" "example" { - key_vault_id = azurerm_key_vault.example.id - name = "generated-certificate" - expiration_date = "1982-12-31T00:00:00Z" - } - - resource "azurerm_key_vault_secret" "example" { - key_vault_id = azurerm_key_vault.example.id - content_type = "password" - expiration_date = "1982-12-31T00:00:00Z" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Vaults, 1) - require.Len(t, adapted.Vaults[0].Keys, 1) - require.Len(t, adapted.Vaults[0].Secrets, 1) - - vault := adapted.Vaults[0] - key := vault.Keys[0] - secret := vault.Secrets[0] - - assert.Equal(t, 5, vault.SoftDeleteRetentionDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, vault.SoftDeleteRetentionDays.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, vault.EnablePurgeProtection.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, vault.EnablePurgeProtection.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 10, vault.NetworkACLs.DefaultAction.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, vault.NetworkACLs.DefaultAction.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, key.ExpiryDate.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, key.ExpiryDate.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 22, secret.ContentType.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 22, secret.ContentType.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, secret.ExpiryDate.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 23, secret.ExpiryDate.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/azure/monitor/adapt.go b/pkg/iac/adapters/terraform/azure/monitor/adapt.go deleted file mode 100644 index a622bcdeaf41..000000000000 --- a/pkg/iac/adapters/terraform/azure/monitor/adapt.go +++ /dev/null @@ -1,56 +0,0 @@ -package monitor - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/monitor" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) monitor.Monitor { - return monitor.Monitor{ - LogProfiles: adaptLogProfiles(modules), - } -} - -func adaptLogProfiles(modules terraform.Modules) []monitor.LogProfile { - var logProfiles []monitor.LogProfile - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_monitor_log_profile") { - logProfiles = append(logProfiles, adaptLogProfile(resource)) - } - } - return logProfiles -} - -func adaptLogProfile(resource *terraform.Block) monitor.LogProfile { - - logProfile := monitor.LogProfile{ - Metadata: resource.GetMetadata(), - RetentionPolicy: monitor.RetentionPolicy{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - Days: iacTypes.IntDefault(0, resource.GetMetadata()), - }, - Categories: nil, - Locations: nil, - } - - if retentionPolicyBlock := resource.GetBlock("retention_policy"); retentionPolicyBlock.IsNotNil() { - logProfile.RetentionPolicy.Metadata = retentionPolicyBlock.GetMetadata() - enabledAttr := retentionPolicyBlock.GetAttribute("enabled") - logProfile.RetentionPolicy.Enabled = enabledAttr.AsBoolValueOrDefault(false, resource) - daysAttr := retentionPolicyBlock.GetAttribute("days") - logProfile.RetentionPolicy.Days = daysAttr.AsIntValueOrDefault(0, resource) - } - - if categoriesAttr := resource.GetAttribute("categories"); categoriesAttr.IsNotNil() { - logProfile.Categories = categoriesAttr.AsStringValues() - } - - if locationsAttr := resource.GetAttribute("locations"); locationsAttr.IsNotNil() { - logProfile.Locations = locationsAttr.AsStringValues() - } - - return logProfile -} diff --git a/pkg/iac/adapters/terraform/azure/monitor/adapt_test.go b/pkg/iac/adapters/terraform/azure/monitor/adapt_test.go deleted file mode 100644 index d8b075a0d09c..000000000000 --- a/pkg/iac/adapters/terraform/azure/monitor/adapt_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package monitor - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/monitor" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptLogProfile(t *testing.T) { - tests := []struct { - name string - terraform string - expected monitor.LogProfile - }{ - { - name: "defined", - terraform: ` - resource "azurerm_monitor_log_profile" "example" { - categories = [ - "Action", - "Delete", - "Write", - ] - - retention_policy { - enabled = true - days = 365 - } - - locations = [ - "eastus", - "eastus2", - "southcentralus" - ] - } -`, - expected: monitor.LogProfile{ - Metadata: iacTypes.NewTestMetadata(), - Categories: []iacTypes.StringValue{ - iacTypes.String("Action", iacTypes.NewTestMetadata()), - iacTypes.String("Delete", iacTypes.NewTestMetadata()), - iacTypes.String("Write", iacTypes.NewTestMetadata()), - }, - RetentionPolicy: monitor.RetentionPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Days: iacTypes.Int(365, iacTypes.NewTestMetadata()), - }, - Locations: []iacTypes.StringValue{ - iacTypes.String("eastus", iacTypes.NewTestMetadata()), - iacTypes.String("eastus2", iacTypes.NewTestMetadata()), - iacTypes.String("southcentralus", iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "default", - terraform: ` - resource "azurerm_monitor_log_profile" "example" { - } -`, - expected: monitor.LogProfile{ - Metadata: iacTypes.NewTestMetadata(), - RetentionPolicy: monitor.RetentionPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Days: iacTypes.Int(0, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptLogProfile(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_monitor_log_profile" "example" { - categories = [ - "Action", - "Delete", - "Write", - ] - - retention_policy { - enabled = true - days = 365 - } - - locations = [ - "eastus", - "eastus2", - "southcentralus" - ] - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.LogProfiles, 1) - logProfile := adapted.LogProfiles[0] - - assert.Equal(t, 3, logProfile.Categories[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, logProfile.Categories[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 10, logProfile.RetentionPolicy.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, logProfile.RetentionPolicy.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, logProfile.RetentionPolicy.Days.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, logProfile.RetentionPolicy.Days.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 14, logProfile.Locations[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 18, logProfile.Locations[0].GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/azure/network/adapt.go b/pkg/iac/adapters/terraform/azure/network/adapt.go deleted file mode 100644 index 4bbcca6c5fd2..000000000000 --- a/pkg/iac/adapters/terraform/azure/network/adapt.go +++ /dev/null @@ -1,218 +0,0 @@ -package network - -import ( - "strconv" - "strings" - - "github.com/google/uuid" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/network" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) network.Network { - return network.Network{ - SecurityGroups: (&adapter{ - modules: modules, - groups: make(map[string]network.SecurityGroup), - }).adaptSecurityGroups(), - NetworkWatcherFlowLogs: adaptWatcherLogs(modules), - } -} - -type adapter struct { - modules terraform.Modules - groups map[string]network.SecurityGroup -} - -func (a *adapter) adaptSecurityGroups() []network.SecurityGroup { - - for _, module := range a.modules { - for _, resource := range module.GetResourcesByType("azurerm_network_security_group") { - a.adaptSecurityGroup(resource) - } - } - - for _, ruleBlock := range a.modules.GetResourcesByType("azurerm_network_security_rule") { - rule := a.adaptSGRule(ruleBlock) - - groupAttr := ruleBlock.GetAttribute("network_security_group_name") - if groupAttr.IsNotNil() { - if referencedBlock, err := a.modules.GetReferencedBlock(groupAttr, ruleBlock); err == nil { - if group, ok := a.groups[referencedBlock.ID()]; ok { - group.Rules = append(group.Rules, rule) - a.groups[referencedBlock.ID()] = group - continue - } - } - - } - - a.groups[uuid.NewString()] = network.SecurityGroup{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Rules: []network.SecurityGroupRule{rule}, - } - } - - var securityGroups []network.SecurityGroup - for _, group := range a.groups { - securityGroups = append(securityGroups, group) - } - - return securityGroups -} - -func adaptWatcherLogs(modules terraform.Modules) []network.NetworkWatcherFlowLog { - var watcherLogs []network.NetworkWatcherFlowLog - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_network_watcher_flow_log") { - watcherLogs = append(watcherLogs, adaptWatcherLog(resource)) - } - } - return watcherLogs -} - -func (a *adapter) adaptSecurityGroup(resource *terraform.Block) { - var rules []network.SecurityGroupRule - for _, ruleBlock := range resource.GetBlocks("security_rule") { - rules = append(rules, a.adaptSGRule(ruleBlock)) - } - a.groups[resource.ID()] = network.SecurityGroup{ - Metadata: resource.GetMetadata(), - Rules: rules, - } -} - -func (a *adapter) adaptSGRule(ruleBlock *terraform.Block) network.SecurityGroupRule { - - rule := network.SecurityGroupRule{ - Metadata: ruleBlock.GetMetadata(), - Outbound: iacTypes.BoolDefault(false, ruleBlock.GetMetadata()), - Allow: iacTypes.BoolDefault(true, ruleBlock.GetMetadata()), - SourceAddresses: nil, - SourcePorts: nil, - DestinationAddresses: nil, - DestinationPorts: nil, - Protocol: ruleBlock.GetAttribute("protocol").AsStringValueOrDefault("", ruleBlock), - } - - accessAttr := ruleBlock.GetAttribute("access") - if accessAttr.Equals("Allow") { - rule.Allow = iacTypes.Bool(true, accessAttr.GetMetadata()) - } else if accessAttr.Equals("Deny") { - rule.Allow = iacTypes.Bool(false, accessAttr.GetMetadata()) - } - - directionAttr := ruleBlock.GetAttribute("direction") - if directionAttr.Equals("Inbound") { - rule.Outbound = iacTypes.Bool(false, directionAttr.GetMetadata()) - } else if directionAttr.Equals("Outbound") { - rule.Outbound = iacTypes.Bool(true, directionAttr.GetMetadata()) - } - - a.adaptSource(ruleBlock, &rule) - a.adaptDestination(ruleBlock, &rule) - - return rule -} - -func (a *adapter) adaptSource(ruleBlock *terraform.Block, rule *network.SecurityGroupRule) { - if sourceAddressAttr := ruleBlock.GetAttribute("source_address_prefix"); sourceAddressAttr.IsString() { - rule.SourceAddresses = append(rule.SourceAddresses, sourceAddressAttr.AsStringValueOrDefault("", ruleBlock)) - } else if sourceAddressPrefixesAttr := ruleBlock.GetAttribute("source_address_prefixes"); sourceAddressPrefixesAttr.IsNotNil() { - rule.SourceAddresses = append(rule.SourceAddresses, sourceAddressPrefixesAttr.AsStringValues()...) - } - - if sourcePortRangesAttr := ruleBlock.GetAttribute("source_port_ranges"); sourcePortRangesAttr.IsNotNil() { - ports := sourcePortRangesAttr.AsStringValues() - for _, value := range ports { - rule.SourcePorts = append(rule.SourcePorts, expandRange(value.Value(), value.GetMetadata())) - } - } else if sourcePortRangeAttr := ruleBlock.GetAttribute("source_port_range"); sourcePortRangeAttr.IsString() { - rule.SourcePorts = append(rule.SourcePorts, expandRange(sourcePortRangeAttr.Value().AsString(), sourcePortRangeAttr.GetMetadata())) - } else if sourcePortRangeAttr := ruleBlock.GetAttribute("source_port_range"); sourcePortRangeAttr.IsNumber() { - f := sourcePortRangeAttr.AsNumber() - rule.SourcePorts = append(rule.SourcePorts, network.PortRange{ - Metadata: sourcePortRangeAttr.GetMetadata(), - Start: iacTypes.Int(int(f), sourcePortRangeAttr.GetMetadata()), - End: iacTypes.Int(int(f), sourcePortRangeAttr.GetMetadata()), - }) - } -} - -func (a *adapter) adaptDestination(ruleBlock *terraform.Block, rule *network.SecurityGroupRule) { - if destAddressAttr := ruleBlock.GetAttribute("destination_address_prefix"); destAddressAttr.IsString() { - rule.DestinationAddresses = append(rule.DestinationAddresses, destAddressAttr.AsStringValueOrDefault("", ruleBlock)) - } else if destAddressPrefixesAttr := ruleBlock.GetAttribute("destination_address_prefixes"); destAddressPrefixesAttr.IsNotNil() { - rule.DestinationAddresses = append(rule.DestinationAddresses, destAddressPrefixesAttr.AsStringValues()...) - } - - if destPortRangesAttr := ruleBlock.GetAttribute("destination_port_ranges"); destPortRangesAttr.IsNotNil() { - ports := destPortRangesAttr.AsStringValues() - for _, value := range ports { - rule.DestinationPorts = append(rule.DestinationPorts, expandRange(value.Value(), destPortRangesAttr.GetMetadata())) - } - } else if destPortRangeAttr := ruleBlock.GetAttribute("destination_port_range"); destPortRangeAttr.IsString() { - rule.DestinationPorts = append(rule.DestinationPorts, expandRange(destPortRangeAttr.Value().AsString(), destPortRangeAttr.GetMetadata())) - } else if destPortRangeAttr := ruleBlock.GetAttribute("destination_port_range"); destPortRangeAttr.IsNumber() { - f := destPortRangeAttr.AsNumber() - rule.DestinationPorts = append(rule.DestinationPorts, network.PortRange{ - Metadata: destPortRangeAttr.GetMetadata(), - Start: iacTypes.Int(int(f), destPortRangeAttr.GetMetadata()), - End: iacTypes.Int(int(f), destPortRangeAttr.GetMetadata()), - }) - } -} - -func expandRange(r string, m iacTypes.Metadata) network.PortRange { - start := 0 - end := 65535 - switch { - case r == "*": - case strings.Contains(r, "-"): - if parts := strings.Split(r, "-"); len(parts) == 2 { - if p1, err := strconv.ParseInt(parts[0], 10, 32); err == nil { - start = int(p1) - } - if p2, err := strconv.ParseInt(parts[1], 10, 32); err == nil { - end = int(p2) - } - } - default: - if val, err := strconv.ParseInt(r, 10, 32); err == nil { - start = int(val) - end = int(val) - } - } - - return network.PortRange{ - Metadata: m, - Start: iacTypes.Int(start, m), - End: iacTypes.Int(end, m), - } -} - -func adaptWatcherLog(resource *terraform.Block) network.NetworkWatcherFlowLog { - flowLog := network.NetworkWatcherFlowLog{ - Metadata: resource.GetMetadata(), - RetentionPolicy: network.RetentionPolicy{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - Days: iacTypes.IntDefault(0, resource.GetMetadata()), - }, - } - - if retentionPolicyBlock := resource.GetBlock("retention_policy"); retentionPolicyBlock.IsNotNil() { - flowLog.RetentionPolicy.Metadata = retentionPolicyBlock.GetMetadata() - - enabledAttr := retentionPolicyBlock.GetAttribute("enabled") - flowLog.RetentionPolicy.Enabled = enabledAttr.AsBoolValueOrDefault(false, retentionPolicyBlock) - - daysAttr := retentionPolicyBlock.GetAttribute("days") - flowLog.RetentionPolicy.Days = daysAttr.AsIntValueOrDefault(0, retentionPolicyBlock) - } - - return flowLog -} diff --git a/pkg/iac/adapters/terraform/azure/network/adapt_test.go b/pkg/iac/adapters/terraform/azure/network/adapt_test.go deleted file mode 100644 index 99931b6b2d3e..000000000000 --- a/pkg/iac/adapters/terraform/azure/network/adapt_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/network" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected network.Network - }{ - { - name: "defined", - terraform: ` - resource "azurerm_network_security_rule" "example" { - name = "example_security_rule" - network_security_group_name = azurerm_network_security_group.example.name - direction = "Inbound" - access = "Allow" - protocol = "TCP" - source_port_range = "*" - destination_port_ranges = ["3389"] - source_address_prefix = "4.53.160.75" - destination_address_prefix = "*" - } - - resource "azurerm_network_security_group" "example" { - name = "tf-appsecuritygroup" - } - - resource "azurerm_network_watcher_flow_log" "example" { - resource_group_name = azurerm_resource_group.example.name - name = "example-log" - - retention_policy { - enabled = true - days = 7 - } - } -`, - expected: network.Network{ - SecurityGroups: []network.SecurityGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Rules: []network.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Outbound: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Allow: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - SourceAddresses: []iacTypes.StringValue{ - iacTypes.String("4.53.160.75", iacTypes.NewTestMetadata()), - }, - DestinationAddresses: []iacTypes.StringValue{ - iacTypes.String("*", iacTypes.NewTestMetadata()), - }, - SourcePorts: []network.PortRange{ - { - Metadata: iacTypes.NewTestMetadata(), - Start: iacTypes.IntTest(0), - End: iacTypes.IntTest(65535), - }, - }, - DestinationPorts: []network.PortRange{ - { - Metadata: iacTypes.NewTestMetadata(), - Start: iacTypes.IntTest(3389), - End: iacTypes.IntTest(3389), - }, - }, - Protocol: iacTypes.String("TCP", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - NetworkWatcherFlowLogs: []network.NetworkWatcherFlowLog{ - { - Metadata: iacTypes.NewTestMetadata(), - RetentionPolicy: network.RetentionPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Days: iacTypes.Int(7, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "azurerm_network_security_group" "example" { - name = "tf-appsecuritygroup" - security_rule { - } - } -`, - expected: network.Network{ - SecurityGroups: []network.SecurityGroup{ - { - Metadata: iacTypes.NewTestMetadata(), - Rules: []network.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Outbound: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Allow: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Protocol: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptWatcherLog(t *testing.T) { - tests := []struct { - name string - terraform string - expected network.NetworkWatcherFlowLog - }{ - { - name: "defined", - terraform: ` - resource "azurerm_network_watcher_flow_log" "watcher" { - retention_policy { - enabled = true - days = 90 - } - } -`, - expected: network.NetworkWatcherFlowLog{ - Metadata: iacTypes.NewTestMetadata(), - RetentionPolicy: network.RetentionPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Days: iacTypes.Int(90, iacTypes.NewTestMetadata()), - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "azurerm_network_watcher_flow_log" "watcher" { - retention_policy { - } - } -`, - expected: network.NetworkWatcherFlowLog{ - Metadata: iacTypes.NewTestMetadata(), - RetentionPolicy: network.RetentionPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Days: iacTypes.Int(0, iacTypes.NewTestMetadata()), - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptWatcherLog(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_network_security_group" "example" { - name = "tf-appsecuritygroup" - } - - resource "azurerm_network_security_rule" "example" { - name = "example_security_rule" - network_security_group_name = azurerm_network_security_group.example.name - direction = "Inbound" - access = "Allow" - protocol = "TCP" - source_port_range = "*" - destination_port_ranges = ["3389"] - source_address_prefix = "4.53.160.75" - destination_address_prefix = "*" - } - - resource "azurerm_network_watcher_flow_log" "example" { - resource_group_name = azurerm_resource_group.example.name - name = "example-log" - - retention_policy { - enabled = true - days = 7 - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.SecurityGroups, 1) - require.Len(t, adapted.NetworkWatcherFlowLogs, 1) - - securityGroup := adapted.SecurityGroups[0] - rule := securityGroup.Rules[0] - watcher := adapted.NetworkWatcherFlowLogs[0] - - assert.Equal(t, 2, securityGroup.Metadata.Range().GetStartLine()) - assert.Equal(t, 4, securityGroup.Metadata.Range().GetEndLine()) - - assert.Equal(t, 6, rule.Metadata.Range().GetStartLine()) - assert.Equal(t, 16, rule.Metadata.Range().GetEndLine()) - - assert.Equal(t, 9, rule.Outbound.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 9, rule.Outbound.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 10, rule.Allow.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, rule.Allow.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, rule.Protocol.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, rule.Protocol.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 12, rule.SourcePorts[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 12, rule.SourcePorts[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 13, rule.DestinationPorts[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 13, rule.DestinationPorts[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 14, rule.SourceAddresses[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, rule.SourceAddresses[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 15, rule.DestinationAddresses[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 15, rule.DestinationAddresses[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 18, watcher.Metadata.Range().GetStartLine()) - assert.Equal(t, 26, watcher.Metadata.Range().GetEndLine()) - - assert.Equal(t, 22, watcher.RetentionPolicy.Metadata.Range().GetStartLine()) - assert.Equal(t, 25, watcher.RetentionPolicy.Metadata.Range().GetEndLine()) - - assert.Equal(t, 23, watcher.RetentionPolicy.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 23, watcher.RetentionPolicy.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 24, watcher.RetentionPolicy.Days.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 24, watcher.RetentionPolicy.Days.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/azure/securitycenter/adapt.go b/pkg/iac/adapters/terraform/azure/securitycenter/adapt.go deleted file mode 100644 index 2053e8e9e587..000000000000 --- a/pkg/iac/adapters/terraform/azure/securitycenter/adapt.go +++ /dev/null @@ -1,59 +0,0 @@ -package securitycenter - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/securitycenter" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) securitycenter.SecurityCenter { - return securitycenter.SecurityCenter{ - Contacts: adaptContacts(modules), - Subscriptions: adaptSubscriptions(modules), - } -} - -func adaptContacts(modules terraform.Modules) []securitycenter.Contact { - var contacts []securitycenter.Contact - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_security_center_contact") { - contacts = append(contacts, adaptContact(resource)) - } - } - return contacts -} - -func adaptSubscriptions(modules terraform.Modules) []securitycenter.SubscriptionPricing { - var subscriptions []securitycenter.SubscriptionPricing - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_security_center_subscription_pricing") { - subscriptions = append(subscriptions, adaptSubscription(resource)) - } - } - return subscriptions -} - -func adaptContact(resource *terraform.Block) securitycenter.Contact { - enableAlertNotifAttr := resource.GetAttribute("alert_notifications") - enableAlertNotifVal := enableAlertNotifAttr.AsBoolValueOrDefault(false, resource) - - phoneAttr := resource.GetAttribute("phone") - phoneVal := phoneAttr.AsStringValueOrDefault("", resource) - - return securitycenter.Contact{ - Metadata: resource.GetMetadata(), - EnableAlertNotifications: enableAlertNotifVal, - Phone: phoneVal, - } -} - -func adaptSubscription(resource *terraform.Block) securitycenter.SubscriptionPricing { - tierAttr := resource.GetAttribute("tier") - tierVal := tierAttr.AsStringValueOrDefault("Free", resource) - - return securitycenter.SubscriptionPricing{ - Metadata: resource.GetMetadata(), - Tier: tierVal, - } -} diff --git a/pkg/iac/adapters/terraform/azure/securitycenter/adapt_test.go b/pkg/iac/adapters/terraform/azure/securitycenter/adapt_test.go deleted file mode 100644 index acd3760952c2..000000000000 --- a/pkg/iac/adapters/terraform/azure/securitycenter/adapt_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package securitycenter - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/securitycenter" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptContact(t *testing.T) { - tests := []struct { - name string - terraform string - expected securitycenter.Contact - }{ - { - name: "defined", - terraform: ` - resource "azurerm_security_center_contact" "example" { - phone = "+1-555-555-5555" - alert_notifications = true - } -`, - expected: securitycenter.Contact{ - Metadata: iacTypes.NewTestMetadata(), - EnableAlertNotifications: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Phone: iacTypes.String("+1-555-555-5555", iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "azurerm_security_center_contact" "example" { - } -`, - expected: securitycenter.Contact{ - Metadata: iacTypes.NewTestMetadata(), - EnableAlertNotifications: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Phone: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptContact(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptSubscription(t *testing.T) { - tests := []struct { - name string - terraform string - expected securitycenter.SubscriptionPricing - }{ - { - name: "free tier", - terraform: ` - resource "azurerm_security_center_subscription_pricing" "example" { - tier = "Free" - }`, - expected: securitycenter.SubscriptionPricing{ - Metadata: iacTypes.NewTestMetadata(), - Tier: iacTypes.String("Free", iacTypes.NewTestMetadata()), - }, - }, - { - name: "default - free tier", - terraform: ` - resource "azurerm_security_center_subscription_pricing" "example" { - }`, - expected: securitycenter.SubscriptionPricing{ - Metadata: iacTypes.NewTestMetadata(), - Tier: iacTypes.String("Free", iacTypes.NewTestMetadata()), - }, - }, - { - name: "standard tier", - terraform: ` - resource "azurerm_security_center_subscription_pricing" "example" { - tier = "Standard" - }`, - expected: securitycenter.SubscriptionPricing{ - Metadata: iacTypes.NewTestMetadata(), - Tier: iacTypes.String("Standard", iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptSubscription(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_security_center_contact" "example" { - phone = "+1-555-555-5555" - alert_notifications = true - } - - resource "azurerm_security_center_subscription_pricing" "example" { - tier = "Standard" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Contacts, 1) - require.Len(t, adapted.Subscriptions, 1) - - contact := adapted.Contacts[0] - sub := adapted.Subscriptions[0] - - assert.Equal(t, 3, contact.Phone.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, contact.Phone.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, contact.EnableAlertNotifications.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, contact.EnableAlertNotifications.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 8, sub.Tier.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 8, sub.Tier.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/azure/storage/adapt.go b/pkg/iac/adapters/terraform/azure/storage/adapt.go deleted file mode 100644 index 3f125c3c828f..000000000000 --- a/pkg/iac/adapters/terraform/azure/storage/adapt.go +++ /dev/null @@ -1,175 +0,0 @@ -package storage - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -const minimumTlsVersionOneTwo = "TLS1_2" - -func Adapt(modules terraform.Modules) storage.Storage { - accounts, containers, networkRules := adaptAccounts(modules) - - orphanAccount := storage.Account{ - Metadata: iacTypes.NewUnmanagedMetadata(), - NetworkRules: adaptOrphanNetworkRules(modules, networkRules), - EnforceHTTPS: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Containers: adaptOrphanContainers(modules, containers), - QueueProperties: storage.QueueProperties{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableLogging: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - } - - accounts = append(accounts, orphanAccount) - - return storage.Storage{ - Accounts: accounts, - } -} - -func adaptOrphanContainers(modules terraform.Modules, containers []string) (orphans []storage.Container) { - accountedFor := make(map[string]bool) - for _, container := range containers { - accountedFor[container] = true - } - for _, module := range modules { - for _, containerResource := range module.GetResourcesByType("azurerm_storage_container") { - if _, ok := accountedFor[containerResource.ID()]; ok { - continue - } - orphans = append(orphans, adaptContainer(containerResource)) - } - } - - return orphans -} - -func adaptOrphanNetworkRules(modules terraform.Modules, networkRules []string) (orphans []storage.NetworkRule) { - accountedFor := make(map[string]bool) - for _, networkRule := range networkRules { - accountedFor[networkRule] = true - } - - for _, module := range modules { - for _, networkRuleResource := range module.GetResourcesByType("azurerm_storage_account_network_rules") { - if _, ok := accountedFor[networkRuleResource.ID()]; ok { - continue - } - - orphans = append(orphans, adaptNetworkRule(networkRuleResource)) - } - } - - return orphans -} - -func adaptAccounts(modules terraform.Modules) ([]storage.Account, []string, []string) { - var accounts []storage.Account - var accountedForContainers []string - var accountedForNetworkRules []string - - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_storage_account") { - account := adaptAccount(resource) - containerResource := module.GetReferencingResources(resource, "azurerm_storage_container", "storage_account_name") - for _, containerBlock := range containerResource { - accountedForContainers = append(accountedForContainers, containerBlock.ID()) - account.Containers = append(account.Containers, adaptContainer(containerBlock)) - } - networkRulesResource := module.GetReferencingResources(resource, "azurerm_storage_account_network_rules", "storage_account_name") - for _, networkRuleBlock := range networkRulesResource { - accountedForNetworkRules = append(accountedForNetworkRules, networkRuleBlock.ID()) - account.NetworkRules = append(account.NetworkRules, adaptNetworkRule(networkRuleBlock)) - } - for _, queueBlock := range module.GetReferencingResources(resource, "azurerm_storage_queue", "storage_account_name") { - queue := storage.Queue{ - Metadata: queueBlock.GetMetadata(), - Name: queueBlock.GetAttribute("name").AsStringValueOrDefault("", queueBlock), - } - account.Queues = append(account.Queues, queue) - } - accounts = append(accounts, account) - } - } - - return accounts, accountedForContainers, accountedForNetworkRules -} - -func adaptAccount(resource *terraform.Block) storage.Account { - account := storage.Account{ - Metadata: resource.GetMetadata(), - NetworkRules: nil, - EnforceHTTPS: iacTypes.BoolDefault(true, resource.GetMetadata()), - Containers: nil, - QueueProperties: storage.QueueProperties{ - Metadata: resource.GetMetadata(), - EnableLogging: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - MinimumTLSVersion: iacTypes.StringDefault(minimumTlsVersionOneTwo, resource.GetMetadata()), - PublicNetworkAccess: resource.GetAttribute("public_network_access_enabled").AsBoolValueOrDefault(true, resource), - } - - networkRulesBlocks := resource.GetBlocks("network_rules") - for _, networkBlock := range networkRulesBlocks { - account.NetworkRules = append(account.NetworkRules, adaptNetworkRule(networkBlock)) - } - - httpsOnlyAttr := resource.GetAttribute("enable_https_traffic_only") - account.EnforceHTTPS = httpsOnlyAttr.AsBoolValueOrDefault(true, resource) - - queuePropertiesBlock := resource.GetBlock("queue_properties") - if queuePropertiesBlock.IsNotNil() { - account.QueueProperties.Metadata = queuePropertiesBlock.GetMetadata() - loggingBlock := queuePropertiesBlock.GetBlock("logging") - if loggingBlock.IsNotNil() { - account.QueueProperties.EnableLogging = iacTypes.Bool(true, loggingBlock.GetMetadata()) - } - } - - minTLSVersionAttr := resource.GetAttribute("min_tls_version") - account.MinimumTLSVersion = minTLSVersionAttr.AsStringValueOrDefault(minimumTlsVersionOneTwo, resource) - return account -} - -func adaptContainer(resource *terraform.Block) storage.Container { - accessTypeAttr := resource.GetAttribute("container_access_type") - publicAccess := iacTypes.StringDefault(storage.PublicAccessOff, resource.GetMetadata()) - - if accessTypeAttr.Equals("blob") { - publicAccess = iacTypes.String(storage.PublicAccessBlob, accessTypeAttr.GetMetadata()) - } else if accessTypeAttr.Equals("container") { - publicAccess = iacTypes.String(storage.PublicAccessContainer, accessTypeAttr.GetMetadata()) - } - - return storage.Container{ - Metadata: resource.GetMetadata(), - PublicAccess: publicAccess, - } -} - -func adaptNetworkRule(resource *terraform.Block) storage.NetworkRule { - var allowByDefault iacTypes.BoolValue - var bypass []iacTypes.StringValue - - defaultActionAttr := resource.GetAttribute("default_action") - - if defaultActionAttr.IsNotNil() { - allowByDefault = iacTypes.Bool(defaultActionAttr.Equals("Allow", terraform.IgnoreCase), defaultActionAttr.GetMetadata()) - } else { - allowByDefault = iacTypes.BoolDefault(false, resource.GetMetadata()) - } - - if resource.HasChild("bypass") { - bypassAttr := resource.GetAttribute("bypass") - bypass = bypassAttr.AsStringValues() - } - - return storage.NetworkRule{ - Metadata: resource.GetMetadata(), - Bypass: bypass, - AllowByDefault: allowByDefault, - } -} diff --git a/pkg/iac/adapters/terraform/azure/storage/adapt_test.go b/pkg/iac/adapters/terraform/azure/storage/adapt_test.go deleted file mode 100644 index 5f576731d432..000000000000 --- a/pkg/iac/adapters/terraform/azure/storage/adapt_test.go +++ /dev/null @@ -1,267 +0,0 @@ -package storage - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected storage.Storage - }{ - { - name: "default", - terraform: `resource "azurerm_storage_account" "example" {}`, - expected: storage.Storage{ - Accounts: []storage.Account{ - { - PublicNetworkAccess: iacTypes.BoolTest(true), - MinimumTLSVersion: iacTypes.StringTest(minimumTlsVersionOneTwo), - EnforceHTTPS: iacTypes.BoolTest(true), - }, - {}, - }, - }, - }, - { - name: "defined", - terraform: ` - resource "azurerm_resource_group" "example" { - name = "example" - } - - resource "azurerm_storage_account" "example" { - name = "storageaccountname" - resource_group_name = azurerm_resource_group.example.name - - network_rules { - default_action = "Deny" - bypass = ["Metrics", "AzureServices"] - } - - enable_https_traffic_only = true - queue_properties { - logging { - delete = true - read = true - write = true - version = "1.0" - retention_policy_days = 10 - } - } - min_tls_version = "TLS1_2" - public_network_access_enabled = false - } - - resource "azurerm_storage_account_network_rules" "test" { - resource_group_name = azurerm_resource_group.example.name - storage_account_name = azurerm_storage_account.example.name - - default_action = "Allow" - bypass = ["Metrics"] - } - - resource "azurerm_storage_container" "example" { - storage_account_name = azurerm_storage_account.example.name - resource_group_name = azurerm_resource_group.example.name - container_access_type = "blob" - } -`, - expected: storage.Storage{ - Accounts: []storage.Account{ - - { - Metadata: iacTypes.NewTestMetadata(), - EnforceHTTPS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), - PublicNetworkAccess: iacTypes.BoolTest(false), - NetworkRules: []storage.NetworkRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Bypass: []iacTypes.StringValue{ - iacTypes.String("Metrics", iacTypes.NewTestMetadata()), - iacTypes.String("AzureServices", iacTypes.NewTestMetadata()), - }, - AllowByDefault: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - { - Metadata: iacTypes.NewTestMetadata(), - Bypass: []iacTypes.StringValue{ - iacTypes.String("Metrics", iacTypes.NewTestMetadata()), - }, - AllowByDefault: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - QueueProperties: storage.QueueProperties{ - Metadata: iacTypes.NewTestMetadata(), - EnableLogging: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Containers: []storage.Container{ - { - Metadata: iacTypes.NewTestMetadata(), - PublicAccess: iacTypes.String("blob", iacTypes.NewTestMetadata()), - }, - }, - }, - { - Metadata: iacTypes.NewUnmanagedMetadata(), - EnforceHTTPS: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - QueueProperties: storage.QueueProperties{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableLogging: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - }, - }, - }, - { - name: "orphans", - terraform: ` - resource "azurerm_storage_account_network_rules" "test" { - default_action = "Allow" - bypass = ["Metrics"] - } - - resource "azurerm_storage_container" "example" { - container_access_type = "blob" - } -`, - expected: storage.Storage{ - Accounts: []storage.Account{ - { - Metadata: iacTypes.NewUnmanagedMetadata(), - EnforceHTTPS: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - NetworkRules: []storage.NetworkRule{ - { - Metadata: iacTypes.NewTestMetadata(), - Bypass: []iacTypes.StringValue{ - iacTypes.String("Metrics", iacTypes.NewTestMetadata()), - }, - AllowByDefault: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - QueueProperties: storage.QueueProperties{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnableLogging: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - MinimumTLSVersion: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - Containers: []storage.Container{ - { - Metadata: iacTypes.NewTestMetadata(), - PublicAccess: iacTypes.String("blob", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_resource_group" "example" { - name = "example" - location = "West Europe" - } - - resource "azurerm_storage_account" "example" { - resource_group_name = azurerm_resource_group.example.name - - enable_https_traffic_only = true - min_tls_version = "TLS1_2" - - queue_properties { - logging { - delete = true - read = true - write = true - version = "1.0" - retention_policy_days = 10 - } - } - - network_rules { - default_action = "Deny" - bypass = ["Metrics", "AzureServices"] - } - } - - resource "azurerm_storage_account_network_rules" "test" { - resource_group_name = azurerm_resource_group.example.name - storage_account_name = azurerm_storage_account.example.name - - default_action = "Allow" - bypass = ["Metrics"] - } - - resource "azurerm_storage_container" "example" { - storage_account_name = azurerm_storage_account.example.name - resource_group_name = azurerm_resource_group.example.name - container_access_type = "blob" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Accounts, 2) //+orphans holder - account := adapted.Accounts[0] - - assert.Equal(t, 7, account.Metadata.Range().GetStartLine()) - assert.Equal(t, 27, account.Metadata.Range().GetEndLine()) - - assert.Equal(t, 10, account.EnforceHTTPS.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, account.EnforceHTTPS.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, account.MinimumTLSVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, account.MinimumTLSVersion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, account.QueueProperties.Metadata.Range().GetStartLine()) - assert.Equal(t, 21, account.QueueProperties.Metadata.Range().GetEndLine()) - - assert.Equal(t, 14, account.QueueProperties.EnableLogging.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 20, account.QueueProperties.EnableLogging.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 23, account.NetworkRules[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 26, account.NetworkRules[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 24, account.NetworkRules[0].AllowByDefault.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 24, account.NetworkRules[0].AllowByDefault.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 25, account.NetworkRules[0].Bypass[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 25, account.NetworkRules[0].Bypass[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 29, account.NetworkRules[1].Metadata.Range().GetStartLine()) - assert.Equal(t, 35, account.NetworkRules[1].Metadata.Range().GetEndLine()) - - assert.Equal(t, 33, account.NetworkRules[1].AllowByDefault.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 33, account.NetworkRules[1].AllowByDefault.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 34, account.NetworkRules[1].Bypass[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, account.NetworkRules[1].Bypass[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 37, account.Containers[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 41, account.Containers[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 40, account.Containers[0].PublicAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 40, account.Containers[0].PublicAccess.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/azure/synapse/adapt.go b/pkg/iac/adapters/terraform/azure/synapse/adapt.go deleted file mode 100644 index 37efcb596a9d..000000000000 --- a/pkg/iac/adapters/terraform/azure/synapse/adapt.go +++ /dev/null @@ -1,32 +0,0 @@ -package synapse - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/synapse" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) synapse.Synapse { - return synapse.Synapse{ - Workspaces: adaptWorkspaces(modules), - } -} - -func adaptWorkspaces(modules terraform.Modules) []synapse.Workspace { - var workspaces []synapse.Workspace - for _, module := range modules { - for _, resource := range module.GetResourcesByType("azurerm_synapse_workspace") { - workspaces = append(workspaces, adaptWorkspace(resource)) - } - } - return workspaces -} - -func adaptWorkspace(resource *terraform.Block) synapse.Workspace { - enableManagedVNAttr := resource.GetAttribute("managed_virtual_network_enabled") - enableManagedVNVal := enableManagedVNAttr.AsBoolValueOrDefault(false, resource) - - return synapse.Workspace{ - Metadata: resource.GetMetadata(), - EnableManagedVirtualNetwork: enableManagedVNVal, - } -} diff --git a/pkg/iac/adapters/terraform/azure/synapse/adapt_test.go b/pkg/iac/adapters/terraform/azure/synapse/adapt_test.go deleted file mode 100644 index 0b4cb3c0ea9e..000000000000 --- a/pkg/iac/adapters/terraform/azure/synapse/adapt_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package synapse - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/synapse" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptWorkspace(t *testing.T) { - tests := []struct { - name string - terraform string - expected synapse.Workspace - }{ - { - name: "enabled", - terraform: ` - resource "azurerm_synapse_workspace" "example" { - managed_virtual_network_enabled = true - } -`, - expected: synapse.Workspace{ - Metadata: iacTypes.NewTestMetadata(), - EnableManagedVirtualNetwork: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - { - name: "disabled", - terraform: ` - resource "azurerm_synapse_workspace" "example" { - managed_virtual_network_enabled = false - } -`, - expected: synapse.Workspace{ - Metadata: iacTypes.NewTestMetadata(), - EnableManagedVirtualNetwork: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "default", - terraform: ` - resource "azurerm_synapse_workspace" "example" { - } -`, - expected: synapse.Workspace{ - Metadata: iacTypes.NewTestMetadata(), - EnableManagedVirtualNetwork: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptWorkspace(modules.GetBlocks()[0]) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "azurerm_synapse_workspace" "example" { - managed_virtual_network_enabled = true - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Workspaces, 1) - workspace := adapted.Workspaces[0] - - assert.Equal(t, 3, workspace.EnableManagedVirtualNetwork.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, workspace.EnableManagedVirtualNetwork.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/cloudstack/adapt.go b/pkg/iac/adapters/terraform/cloudstack/adapt.go deleted file mode 100644 index d19a05bdf3a4..000000000000 --- a/pkg/iac/adapters/terraform/cloudstack/adapt.go +++ /dev/null @@ -1,13 +0,0 @@ -package cloudstack - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/cloudstack/compute" - "github.com/aquasecurity/trivy/pkg/iac/providers/cloudstack" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) cloudstack.CloudStack { - return cloudstack.CloudStack{ - Compute: compute.Adapt(modules), - } -} diff --git a/pkg/iac/adapters/terraform/cloudstack/compute/adapt.go b/pkg/iac/adapters/terraform/cloudstack/compute/adapt.go deleted file mode 100644 index 4908f0325158..000000000000 --- a/pkg/iac/adapters/terraform/cloudstack/compute/adapt.go +++ /dev/null @@ -1,47 +0,0 @@ -package compute - -import ( - "encoding/base64" - - "github.com/aquasecurity/trivy/pkg/iac/providers/cloudstack/compute" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) compute.Compute { - return compute.Compute{ - Instances: adaptInstances(modules), - } -} - -func adaptInstances(modules terraform.Modules) []compute.Instance { - var instances []compute.Instance - for _, module := range modules { - for _, resource := range module.GetResourcesByType("cloudstack_instance") { - instances = append(instances, adaptInstance(resource)) - } - } - return instances -} - -func adaptInstance(resource *terraform.Block) compute.Instance { - userDataAttr := resource.GetAttribute("user_data") - var encoded []byte - var err error - - if userDataAttr.IsNotNil() && userDataAttr.IsString() { - encoded, err = base64.StdEncoding.DecodeString(userDataAttr.Value().AsString()) - if err != nil { - encoded = []byte(userDataAttr.Value().AsString()) - } - return compute.Instance{ - Metadata: resource.GetMetadata(), - UserData: types.String(string(encoded), userDataAttr.GetMetadata()), - } - } - - return compute.Instance{ - Metadata: resource.GetMetadata(), - UserData: types.StringDefault("", resource.GetMetadata()), - } -} diff --git a/pkg/iac/adapters/terraform/cloudstack/compute/adapt_test.go b/pkg/iac/adapters/terraform/cloudstack/compute/adapt_test.go deleted file mode 100644 index d869947197ee..000000000000 --- a/pkg/iac/adapters/terraform/cloudstack/compute/adapt_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package compute - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/cloudstack/compute" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptInstance(t *testing.T) { - tests := []struct { - name string - terraform string - expected compute.Instance - }{ - { - name: "sensitive user data", - terraform: ` - resource "cloudstack_instance" "web" { - name = "server-1" - user_data = < 0 { - cluster.NodeConfig = cluster.NodePools[0].NodeConfig - a.clusterMap[id] = cluster - } - } - - var clusters []gke.Cluster - for _, cluster := range a.clusterMap { - clusters = append(clusters, cluster) - } - return clusters -} - -func (a *adapter) adaptCluster(resource *terraform.Block, module *terraform.Module) { - - cluster := gke.Cluster{ - Metadata: resource.GetMetadata(), - NodePools: nil, - IPAllocationPolicy: gke.IPAllocationPolicy{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - MasterAuthorizedNetworks: gke.MasterAuthorizedNetworks{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - CIDRs: []iacTypes.StringValue{}, - }, - NetworkPolicy: gke.NetworkPolicy{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - DatapathProvider: resource.GetAttribute("datapath_provider"). - AsStringValueOrDefault("DATAPATH_PROVIDER_UNSPECIFIED", resource), - PrivateCluster: gke.PrivateCluster{ - Metadata: resource.GetMetadata(), - EnablePrivateNodes: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - LoggingService: iacTypes.StringDefault("logging.googleapis.com/kubernetes", resource.GetMetadata()), - MonitoringService: iacTypes.StringDefault("monitoring.googleapis.com/kubernetes", resource.GetMetadata()), - MasterAuth: gke.MasterAuth{ - Metadata: resource.GetMetadata(), - ClientCertificate: gke.ClientCertificate{ - Metadata: resource.GetMetadata(), - IssueCertificate: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - Username: iacTypes.StringDefault("", resource.GetMetadata()), - Password: iacTypes.StringDefault("", resource.GetMetadata()), - }, - NodeConfig: gke.NodeConfig{ - Metadata: resource.GetMetadata(), - ImageType: iacTypes.StringDefault("", resource.GetMetadata()), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: resource.GetMetadata(), - NodeMetadata: iacTypes.StringDefault("", resource.GetMetadata()), - }, - ServiceAccount: iacTypes.StringDefault("", resource.GetMetadata()), - EnableLegacyEndpoints: iacTypes.BoolDefault(true, resource.GetMetadata()), - }, - EnableShieldedNodes: iacTypes.BoolDefault(true, resource.GetMetadata()), - EnableLegacyABAC: iacTypes.BoolDefault(false, resource.GetMetadata()), - ResourceLabels: iacTypes.MapDefault(make(map[string]string), resource.GetMetadata()), - RemoveDefaultNodePool: iacTypes.BoolDefault(false, resource.GetMetadata()), - EnableAutpilot: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - if allocBlock := resource.GetBlock("ip_allocation_policy"); allocBlock.IsNotNil() { - cluster.IPAllocationPolicy.Metadata = allocBlock.GetMetadata() - cluster.IPAllocationPolicy.Enabled = iacTypes.Bool(true, allocBlock.GetMetadata()) - } - - if blocks := resource.GetBlocks("master_authorized_networks_config"); len(blocks) > 0 { - cluster.MasterAuthorizedNetworks = adaptMasterAuthNetworksAsBlocks(resource, blocks) - } - - if policyBlock := resource.GetBlock("network_policy"); policyBlock.IsNotNil() { - enabledAttr := policyBlock.GetAttribute("enabled") - cluster.NetworkPolicy.Metadata = policyBlock.GetMetadata() - cluster.NetworkPolicy.Enabled = enabledAttr.AsBoolValueOrDefault(false, policyBlock) - } - - if privBlock := resource.GetBlock("private_cluster_config"); privBlock.IsNotNil() { - privateNodesEnabledAttr := privBlock.GetAttribute("enable_private_nodes") - cluster.PrivateCluster.Metadata = privBlock.GetMetadata() - cluster.PrivateCluster.EnablePrivateNodes = privateNodesEnabledAttr.AsBoolValueOrDefault(false, privBlock) - } - - loggingAttr := resource.GetAttribute("logging_service") - cluster.LoggingService = loggingAttr.AsStringValueOrDefault("logging.googleapis.com/kubernetes", resource) - monitoringServiceAttr := resource.GetAttribute("monitoring_service") - cluster.MonitoringService = monitoringServiceAttr.AsStringValueOrDefault("monitoring.googleapis.com/kubernetes", resource) - - if masterBlock := resource.GetBlock("master_auth"); masterBlock.IsNotNil() { - cluster.MasterAuth = adaptMasterAuth(masterBlock) - } - - if configBlock := resource.GetBlock("node_config"); configBlock.IsNotNil() { - if configBlock.GetBlock("metadata").IsNotNil() { - cluster.NodeConfig.Metadata = configBlock.GetBlock("metadata").GetMetadata() - } - cluster.NodeConfig = adaptNodeConfig(configBlock) - } - - cluster.EnableShieldedNodes = resource.GetAttribute("enable_shielded_nodes").AsBoolValueOrDefault(true, resource) - - enableLegacyABACAttr := resource.GetAttribute("enable_legacy_abac") - cluster.EnableLegacyABAC = enableLegacyABACAttr.AsBoolValueOrDefault(false, resource) - - cluster.EnableAutpilot = resource.GetAttribute("enable_autopilot").AsBoolValueOrDefault(false, resource) - - resourceLabelsAttr := resource.GetAttribute("resource_labels") - if resourceLabelsAttr.IsNotNil() { - cluster.ResourceLabels = resourceLabelsAttr.AsMapValue() - } - - cluster.RemoveDefaultNodePool = resource.GetAttribute("remove_default_node_pool").AsBoolValueOrDefault(false, resource) - - a.clusterMap[resource.ID()] = cluster -} - -func (a *adapter) adaptNodePools() { - for _, nodePoolBlock := range a.modules.GetResourcesByType("google_container_node_pool") { - a.adaptNodePool(nodePoolBlock) - } -} - -func (a *adapter) adaptNodePool(resource *terraform.Block) { - nodeConfig := gke.NodeConfig{ - Metadata: resource.GetMetadata(), - ImageType: iacTypes.StringDefault("", resource.GetMetadata()), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: resource.GetMetadata(), - NodeMetadata: iacTypes.StringDefault("", resource.GetMetadata()), - }, - ServiceAccount: iacTypes.StringDefault("", resource.GetMetadata()), - EnableLegacyEndpoints: iacTypes.BoolDefault(true, resource.GetMetadata()), - } - - management := gke.Management{ - Metadata: resource.GetMetadata(), - EnableAutoRepair: iacTypes.BoolDefault(false, resource.GetMetadata()), - EnableAutoUpgrade: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - if resource.HasChild("management") { - management.Metadata = resource.GetBlock("management").GetMetadata() - - autoRepairAttr := resource.GetBlock("management").GetAttribute("auto_repair") - management.EnableAutoRepair = autoRepairAttr.AsBoolValueOrDefault(false, resource.GetBlock("management")) - - autoUpgradeAttr := resource.GetBlock("management").GetAttribute("auto_upgrade") - management.EnableAutoUpgrade = autoUpgradeAttr.AsBoolValueOrDefault(false, resource.GetBlock("management")) - } - - if resource.HasChild("node_config") { - nodeConfig = adaptNodeConfig(resource.GetBlock("node_config")) - } - - nodePool := gke.NodePool{ - Metadata: resource.GetMetadata(), - Management: management, - NodeConfig: nodeConfig, - } - - clusterAttr := resource.GetAttribute("cluster") - if referencedCluster, err := a.modules.GetReferencedBlock(clusterAttr, resource); err == nil { - if referencedCluster.TypeLabel() == "google_container_cluster" { - if cluster, ok := a.clusterMap[referencedCluster.ID()]; ok { - cluster.NodePools = append(cluster.NodePools, nodePool) - a.clusterMap[referencedCluster.ID()] = cluster - return - } - } - } - - // we didn't find a cluster to put the nodepool in, so create a placeholder - a.clusterMap[uuid.NewString()] = gke.Cluster{ - Metadata: iacTypes.NewUnmanagedMetadata(), - NodePools: []gke.NodePool{nodePool}, - IPAllocationPolicy: gke.IPAllocationPolicy{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Enabled: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - MasterAuthorizedNetworks: gke.MasterAuthorizedNetworks{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Enabled: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - CIDRs: nil, - }, - NetworkPolicy: gke.NetworkPolicy{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Enabled: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - PrivateCluster: gke.PrivateCluster{ - Metadata: iacTypes.NewUnmanagedMetadata(), - EnablePrivateNodes: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - LoggingService: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - MonitoringService: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - MasterAuth: gke.MasterAuth{ - Metadata: iacTypes.NewUnmanagedMetadata(), - ClientCertificate: gke.ClientCertificate{ - Metadata: iacTypes.NewUnmanagedMetadata(), - IssueCertificate: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - Username: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - Password: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - NodeConfig: gke.NodeConfig{ - Metadata: iacTypes.NewUnmanagedMetadata(), - ImageType: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: iacTypes.NewUnmanagedMetadata(), - NodeMetadata: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - }, - ServiceAccount: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnableLegacyEndpoints: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - }, - EnableShieldedNodes: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - EnableLegacyABAC: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - ResourceLabels: iacTypes.MapDefault(nil, iacTypes.NewUnmanagedMetadata()), - RemoveDefaultNodePool: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - EnableAutpilot: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - } -} - -func adaptNodeConfig(resource *terraform.Block) gke.NodeConfig { - - config := gke.NodeConfig{ - Metadata: resource.GetMetadata(), - ImageType: resource.GetAttribute("image_type").AsStringValueOrDefault("", resource), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: resource.GetMetadata(), - NodeMetadata: iacTypes.StringDefault("UNSPECIFIED", resource.GetMetadata()), - }, - ServiceAccount: resource.GetAttribute("service_account").AsStringValueOrDefault("", resource), - EnableLegacyEndpoints: iacTypes.BoolDefault(true, resource.GetMetadata()), - } - - if metadata := resource.GetAttribute("metadata"); metadata.IsNotNil() { - legacyMetadata := metadata.MapValue("disable-legacy-endpoints") - if legacyMetadata.IsWhollyKnown() && legacyMetadata.Type() == cty.Bool { - config.EnableLegacyEndpoints = iacTypes.Bool(legacyMetadata.False(), metadata.GetMetadata()) - } - } - - workloadBlock := resource.GetBlock("workload_metadata_config") - if workloadBlock.IsNotNil() { - config.WorkloadMetadataConfig.Metadata = workloadBlock.GetMetadata() - modeAttr := workloadBlock.GetAttribute("node_metadata") - if modeAttr.IsNil() { - modeAttr = workloadBlock.GetAttribute("mode") // try newest version - } - config.WorkloadMetadataConfig.NodeMetadata = modeAttr.AsStringValueOrDefault("UNSPECIFIED", workloadBlock) - } - - return config -} - -func adaptMasterAuth(resource *terraform.Block) gke.MasterAuth { - clientCert := gke.ClientCertificate{ - Metadata: resource.GetMetadata(), - IssueCertificate: iacTypes.BoolDefault(false, resource.GetMetadata()), - } - - if resource.HasChild("client_certificate_config") { - clientCertAttr := resource.GetBlock("client_certificate_config").GetAttribute("issue_client_certificate") - clientCert.IssueCertificate = clientCertAttr.AsBoolValueOrDefault(false, resource.GetBlock("client_certificate_config")) - clientCert.Metadata = resource.GetBlock("client_certificate_config").GetMetadata() - } - - username := resource.GetAttribute("username").AsStringValueOrDefault("", resource) - password := resource.GetAttribute("password").AsStringValueOrDefault("", resource) - - return gke.MasterAuth{ - Metadata: resource.GetMetadata(), - ClientCertificate: clientCert, - Username: username, - Password: password, - } -} - -func adaptMasterAuthNetworksAsBlocks(parent *terraform.Block, blocks terraform.Blocks) gke.MasterAuthorizedNetworks { - var cidrs []iacTypes.StringValue - for _, block := range blocks { - for _, cidrBlock := range block.GetBlocks("cidr_blocks") { - if cidrAttr := cidrBlock.GetAttribute("cidr_block"); cidrAttr.IsNotNil() { - cidrs = append(cidrs, cidrAttr.AsStringValues()...) - } - } - } - enabled := iacTypes.Bool(true, blocks[0].GetMetadata()) - return gke.MasterAuthorizedNetworks{ - Metadata: blocks[0].GetMetadata(), - Enabled: enabled, - CIDRs: cidrs, - } -} diff --git a/pkg/iac/adapters/terraform/google/gke/adapt_test.go b/pkg/iac/adapters/terraform/google/gke/adapt_test.go deleted file mode 100644 index 461a5169e7be..000000000000 --- a/pkg/iac/adapters/terraform/google/gke/adapt_test.go +++ /dev/null @@ -1,415 +0,0 @@ -package gke - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/gke" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected gke.GKE - }{ - { - name: "separately defined pool", - terraform: ` -resource "google_service_account" "default" { - account_id = "service-account-id" - display_name = "Service Account" -} - -resource "google_container_cluster" "example" { - name = "my-gke-cluster" - - node_config { - metadata = { - disable-legacy-endpoints = true - } - } - - pod_security_policy_config { - enabled = "true" - } - - enable_legacy_abac = "true" - enable_shielded_nodes = "true" - - remove_default_node_pool = true - initial_node_count = 1 - monitoring_service = "monitoring.googleapis.com/kubernetes" - logging_service = "logging.googleapis.com/kubernetes" - - master_auth { - client_certificate_config { - issue_client_certificate = true - } - } - - master_authorized_networks_config { - cidr_blocks { - cidr_block = "10.10.128.0/24" - display_name = "internal" - } - } - - resource_labels = { - "env" = "staging" - } - - private_cluster_config { - enable_private_nodes = true - } - - network_policy { - enabled = true - } - - ip_allocation_policy {} - - enable_autopilot = true - - datapath_provider = "ADVANCED_DATAPATH" -} - -resource "google_container_node_pool" "primary_preemptible_nodes" { - cluster = google_container_cluster.example.name - node_count = 1 - - node_config { - service_account = google_service_account.default.email - metadata = { - disable-legacy-endpoints = true - } - image_type = "COS_CONTAINERD" - workload_metadata_config { - mode = "GCE_METADATA" - } - } - management { - auto_repair = true - auto_upgrade = true - } -} -`, - expected: gke.GKE{ - Clusters: []gke.Cluster{ - { - Metadata: iacTypes.NewTestMetadata(), - NodeConfig: gke.NodeConfig{ - Metadata: iacTypes.NewTestMetadata(), - ImageType: iacTypes.String("COS_CONTAINERD", iacTypes.NewTestMetadata()), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: iacTypes.NewTestMetadata(), - NodeMetadata: iacTypes.String("GCE_METADATA", iacTypes.NewTestMetadata()), - }, - ServiceAccount: iacTypes.String("", iacTypes.NewTestMetadata()), - EnableLegacyEndpoints: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - NodePools: []gke.NodePool{ - { - Metadata: iacTypes.NewTestMetadata(), - Management: gke.Management{ - Metadata: iacTypes.NewTestMetadata(), - EnableAutoRepair: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - EnableAutoUpgrade: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - NodeConfig: gke.NodeConfig{ - Metadata: iacTypes.NewTestMetadata(), - ImageType: iacTypes.String("COS_CONTAINERD", iacTypes.NewTestMetadata()), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: iacTypes.NewTestMetadata(), - NodeMetadata: iacTypes.String("GCE_METADATA", iacTypes.NewTestMetadata()), - }, - ServiceAccount: iacTypes.String("", iacTypes.NewTestMetadata()), - EnableLegacyEndpoints: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - IPAllocationPolicy: gke.IPAllocationPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - MasterAuthorizedNetworks: gke.MasterAuthorizedNetworks{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("10.10.128.0/24", iacTypes.NewTestMetadata()), - }, - }, - NetworkPolicy: gke.NetworkPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - DatapathProvider: iacTypes.String("ADVANCED_DATAPATH", iacTypes.NewTestMetadata()), - PrivateCluster: gke.PrivateCluster{ - Metadata: iacTypes.NewTestMetadata(), - EnablePrivateNodes: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - LoggingService: iacTypes.String("logging.googleapis.com/kubernetes", iacTypes.NewTestMetadata()), - MonitoringService: iacTypes.String("monitoring.googleapis.com/kubernetes", iacTypes.NewTestMetadata()), - MasterAuth: gke.MasterAuth{ - Metadata: iacTypes.NewTestMetadata(), - ClientCertificate: gke.ClientCertificate{ - Metadata: iacTypes.NewTestMetadata(), - IssueCertificate: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Username: iacTypes.String("", iacTypes.NewTestMetadata()), - Password: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - EnableShieldedNodes: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - EnableLegacyABAC: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - ResourceLabels: iacTypes.Map(map[string]string{ - "env": "staging", - }, iacTypes.NewTestMetadata()), - RemoveDefaultNodePool: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - EnableAutpilot: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - { - name: "default node pool", - terraform: ` -resource "google_container_cluster" "example" { - node_config { - service_account = "service-account" - metadata = { - disable-legacy-endpoints = true - } - image_type = "COS" - workload_metadata_config { - mode = "GCE_METADATA" - } - } -} -`, - expected: gke.GKE{ - Clusters: []gke.Cluster{ - { - Metadata: iacTypes.NewTestMetadata(), - NodeConfig: gke.NodeConfig{ - Metadata: iacTypes.NewTestMetadata(), - ImageType: iacTypes.String("COS", iacTypes.NewTestMetadata()), - WorkloadMetadataConfig: gke.WorkloadMetadataConfig{ - Metadata: iacTypes.NewTestMetadata(), - NodeMetadata: iacTypes.String("GCE_METADATA", iacTypes.NewTestMetadata()), - }, - ServiceAccount: iacTypes.String("service-account", iacTypes.NewTestMetadata()), - EnableLegacyEndpoints: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - - IPAllocationPolicy: gke.IPAllocationPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - MasterAuthorizedNetworks: gke.MasterAuthorizedNetworks{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{}, - }, - NetworkPolicy: gke.NetworkPolicy{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - DatapathProvider: iacTypes.StringDefault("DATAPATH_PROVIDER_UNSPECIFIED", iacTypes.NewTestMetadata()), - PrivateCluster: gke.PrivateCluster{ - Metadata: iacTypes.NewTestMetadata(), - EnablePrivateNodes: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - LoggingService: iacTypes.String("logging.googleapis.com/kubernetes", iacTypes.NewTestMetadata()), - MonitoringService: iacTypes.String("monitoring.googleapis.com/kubernetes", iacTypes.NewTestMetadata()), - MasterAuth: gke.MasterAuth{ - Metadata: iacTypes.NewTestMetadata(), - ClientCertificate: gke.ClientCertificate{ - Metadata: iacTypes.NewTestMetadata(), - IssueCertificate: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - Username: iacTypes.String("", iacTypes.NewTestMetadata()), - Password: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - EnableShieldedNodes: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - EnableLegacyABAC: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - ResourceLabels: iacTypes.Map(make(map[string]string), iacTypes.NewTestMetadata()), - RemoveDefaultNodePool: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` -resource "google_container_cluster" "example" { - - node_config { - metadata = { - disable-legacy-endpoints = true - } - } - pod_security_policy_config { - enabled = "true" - } - - enable_legacy_abac = "true" - enable_shielded_nodes = "true" - - remove_default_node_pool = true - monitoring_service = "monitoring.googleapis.com/kubernetes" - logging_service = "logging.googleapis.com/kubernetes" - - master_auth { - client_certificate_config { - issue_client_certificate = true - } - } - - master_authorized_networks_config { - cidr_blocks { - cidr_block = "10.10.128.0/24" - } - } - - resource_labels = { - "env" = "staging" - } - - private_cluster_config { - enable_private_nodes = true - } - - network_policy { - enabled = true - } - ip_allocation_policy {} -} - -resource "google_container_node_pool" "primary_preemptible_nodes" { - cluster = google_container_cluster.example.name - - node_config { - metadata = { - disable-legacy-endpoints = true - } - service_account = google_service_account.default.email - image_type = "COS_CONTAINERD" - - workload_metadata_config { - mode = "GCE_METADATA" - } - } - management { - auto_repair = true - auto_upgrade = true - } -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Clusters, 1) - cluster := adapted.Clusters[0] - nodePool := cluster.NodePools[0] - - assert.Equal(t, 2, cluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 44, cluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 49, cluster.NodeConfig.Metadata.Range().GetStartLine()) - assert.Equal(t, 59, cluster.NodeConfig.Metadata.Range().GetEndLine()) - - assert.Equal(t, 50, cluster.NodeConfig.EnableLegacyEndpoints.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 52, cluster.NodeConfig.EnableLegacyEndpoints.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, cluster.EnableLegacyABAC.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, cluster.EnableLegacyABAC.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 14, cluster.EnableShieldedNodes.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, cluster.EnableShieldedNodes.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 16, cluster.RemoveDefaultNodePool.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 16, cluster.RemoveDefaultNodePool.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, cluster.MonitoringService.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, cluster.MonitoringService.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 18, cluster.LoggingService.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 18, cluster.LoggingService.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 20, cluster.MasterAuth.Metadata.Range().GetStartLine()) - assert.Equal(t, 24, cluster.MasterAuth.Metadata.Range().GetEndLine()) - - assert.Equal(t, 21, cluster.MasterAuth.ClientCertificate.Metadata.Range().GetStartLine()) - assert.Equal(t, 23, cluster.MasterAuth.ClientCertificate.Metadata.Range().GetEndLine()) - - assert.Equal(t, 22, cluster.MasterAuth.ClientCertificate.IssueCertificate.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 22, cluster.MasterAuth.ClientCertificate.IssueCertificate.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 26, cluster.MasterAuthorizedNetworks.Metadata.Range().GetStartLine()) - assert.Equal(t, 30, cluster.MasterAuthorizedNetworks.Metadata.Range().GetEndLine()) - - assert.Equal(t, 28, cluster.MasterAuthorizedNetworks.CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 28, cluster.MasterAuthorizedNetworks.CIDRs[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 32, cluster.ResourceLabels.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, cluster.ResourceLabels.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 36, cluster.PrivateCluster.Metadata.Range().GetStartLine()) - assert.Equal(t, 38, cluster.PrivateCluster.Metadata.Range().GetEndLine()) - - assert.Equal(t, 37, cluster.PrivateCluster.EnablePrivateNodes.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 37, cluster.PrivateCluster.EnablePrivateNodes.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 40, cluster.NetworkPolicy.Metadata.Range().GetStartLine()) - assert.Equal(t, 42, cluster.NetworkPolicy.Metadata.Range().GetEndLine()) - - assert.Equal(t, 41, cluster.NetworkPolicy.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 41, cluster.NetworkPolicy.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 43, cluster.IPAllocationPolicy.Metadata.Range().GetStartLine()) - assert.Equal(t, 43, cluster.IPAllocationPolicy.Metadata.Range().GetEndLine()) - - assert.Equal(t, 46, nodePool.Metadata.Range().GetStartLine()) - assert.Equal(t, 64, nodePool.Metadata.Range().GetEndLine()) - - assert.Equal(t, 49, nodePool.NodeConfig.Metadata.Range().GetStartLine()) - assert.Equal(t, 59, nodePool.NodeConfig.Metadata.Range().GetEndLine()) - - assert.Equal(t, 53, nodePool.NodeConfig.ServiceAccount.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 53, nodePool.NodeConfig.ServiceAccount.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 54, nodePool.NodeConfig.ImageType.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 54, nodePool.NodeConfig.ImageType.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 56, nodePool.NodeConfig.WorkloadMetadataConfig.Metadata.Range().GetStartLine()) - assert.Equal(t, 58, nodePool.NodeConfig.WorkloadMetadataConfig.Metadata.Range().GetEndLine()) - - assert.Equal(t, 57, nodePool.NodeConfig.WorkloadMetadataConfig.NodeMetadata.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 57, nodePool.NodeConfig.WorkloadMetadataConfig.NodeMetadata.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 60, nodePool.Management.Metadata.Range().GetStartLine()) - assert.Equal(t, 63, nodePool.Management.Metadata.Range().GetEndLine()) - - assert.Equal(t, 61, nodePool.Management.EnableAutoRepair.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 61, nodePool.Management.EnableAutoRepair.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 62, nodePool.Management.EnableAutoUpgrade.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 62, nodePool.Management.EnableAutoUpgrade.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/google/iam/adapt.go b/pkg/iac/adapters/terraform/google/iam/adapt.go deleted file mode 100644 index 1dcc8a4cec68..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/adapt.go +++ /dev/null @@ -1,59 +0,0 @@ -package iam - -import ( - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) iam.IAM { - return (&adapter{ - orgs: make(map[string]*iam.Organization), - projects: make(map[string]*iam.Project), - projectsByID: make(map[string]string), // projectID -> blockID - folders: make(map[string]*iam.Folder), - modules: modules, - }).Adapt() -} - -type adapter struct { - modules terraform.Modules - orgs map[string]*iam.Organization - folders map[string]*iam.Folder - projects map[string]*iam.Project - projectsByID map[string]string - workloadIdentityPoolProviders []iam.WorkloadIdentityPoolProvider -} - -func (a *adapter) Adapt() iam.IAM { - a.adaptOrganizationIAM() - a.adaptFolderIAM() - a.adaptProjectIAM() - a.adaptWorkloadIdentityPoolProviders() - return a.buildIAMOutput() -} - -func (a *adapter) buildIAMOutput() iam.IAM { - return iam.IAM{ - Organizations: fromPtrSlice(lo.Values(a.orgs)), - Folders: fromPtrSlice(lo.Values(a.folders)), - Projects: fromPtrSlice(lo.Values(a.projects)), - WorkloadIdentityPoolProviders: a.workloadIdentityPoolProviders, - } -} - -func fromPtrSlice[T any](collection []*T) []T { - if len(collection) == 0 { - return nil - } - - result := make([]T, 0, len(collection)) - for _, item := range collection { - if item == nil { - continue - } - result = append(result, *item) - } - return result -} diff --git a/pkg/iac/adapters/terraform/google/iam/adapt_test.go b/pkg/iac/adapters/terraform/google/iam/adapt_test.go deleted file mode 100644 index 6d6b6cb38b00..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/adapt_test.go +++ /dev/null @@ -1,427 +0,0 @@ -package iam - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected iam.IAM - }{ - { - name: "basic", - terraform: ` -data "google_organization" "org" { - domain = "example.com" -} - -resource "google_project" "my_project" { - name = "My Project" - project_id = "your-project-id" - org_id = data.google_organization.org.org_id - auto_create_network = true -} - -resource "google_folder" "department1" { - display_name = "Department 1" - parent = data.google_organization.org.org_id -} - -resource "google_folder_iam_member" "admin" { - folder = google_folder.department1.name - role = "roles/editor" - member = "user:alice@gmail.com" -} - -resource "google_folder_iam_binding" "folder-123" { - folder = google_folder.department1.name - role = "roles/nothing" - members = [ - "user:not-alice@gmail.com", - ] -} - -resource "google_organization_iam_member" "org-123" { - org_id = data.google_organization.org.org_id - role = "roles/whatever" - member = "user:member@gmail.com" -} - -resource "google_organization_iam_binding" "binding" { - org_id = data.google_organization.org.org_id - role = "roles/browser" - - members = [ - "user:member_2@gmail.com", - ] -} - -resource "google_iam_workload_identity_pool_provider" "example" { - workload_identity_pool_id = "example-pool" - workload_identity_pool_provider_id = "example-provider" - attribute_condition = "assertion.repository_owner=='your-github-organization'" -} -`, - expected: iam.IAM{ - Projects: []iam.Project{ - { - Metadata: iacTypes.NewTestMetadata(), - AutoCreateNetwork: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - Folders: []iam.Folder{ - { - Metadata: iacTypes.NewTestMetadata(), - Members: []iam.Member{ - { - Metadata: iacTypes.NewTestMetadata(), - Member: iacTypes.String("user:alice@gmail.com", iacTypes.NewTestMetadata()), - Role: iacTypes.String("roles/editor", iacTypes.NewTestMetadata()), - DefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Members: []iacTypes.StringValue{ - iacTypes.String("user:not-alice@gmail.com", iacTypes.NewTestMetadata()), - }, - Role: iacTypes.String("roles/nothing", iacTypes.NewTestMetadata()), - IncludesDefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - Organizations: []iam.Organization{ - { - Metadata: iacTypes.NewTestMetadata(), - Members: []iam.Member{ - { - Metadata: iacTypes.NewTestMetadata(), - Member: iacTypes.String("user:member@gmail.com", iacTypes.NewTestMetadata()), - Role: iacTypes.String("roles/whatever", iacTypes.NewTestMetadata()), - DefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Members: []iacTypes.StringValue{ - iacTypes.String("user:member_2@gmail.com", iacTypes.NewTestMetadata())}, - Role: iacTypes.String("roles/browser", iacTypes.NewTestMetadata()), - IncludesDefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - WorkloadIdentityPoolProviders: []iam.WorkloadIdentityPoolProvider{ - { - Metadata: iacTypes.NewTestMetadata(), - - WorkloadIdentityPoolId: iacTypes.String("example-pool", iacTypes.NewTestMetadata()), - WorkloadIdentityPoolProviderId: iacTypes.String("example-provider", iacTypes.NewTestMetadata()), - AttributeCondition: iacTypes.String("assertion.repository_owner=='your-github-organization'", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - { - name: "iam policies", - terraform: ` -resource "google_folder" "test" { - display_name = "Department 1" -} - -resource "google_folder_iam_policy" "folder" { - folder = google_folder.test.folder_id - policy_data = data.google_iam_policy.folder_admin.policy_data -} - -data "google_iam_policy" "folder_admin" { - binding { - role = "roles/editor" - - members = [ - "user:jane@example.com", - ] - } -} - -data "google_organization" "test" { - domain = "example.com" -} - -resource "google_organization_iam_policy" "organization" { - org_id = data.google_organization.test.name - policy_data = data.google_iam_policy.org_admin.policy_data -} - -data "google_iam_policy" "org_admin" { - binding { - role = "roles/editor" - - members = [ - "user:jane2@example.com", - ] - } -} - -resource "google_project" "test" { - name = "My Project2" -} - -resource "google_project_iam_policy" "project" { - project = google_project.test.id - policy_data = data.google_iam_policy.project_admin.policy_data -} - -data "google_iam_policy" "project_admin" { - binding { - role = "roles/editor" - - members = [ - "user:jane3@example.com", - ] - } -} -`, - expected: iam.IAM{ - Folders: []iam.Folder{ - { - Metadata: iacTypes.NewTestMetadata(), - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.StringTest("roles/editor"), - Members: []iacTypes.StringValue{ - iacTypes.StringTest("user:jane@example.com"), - }, - }, - }, - }, - }, - Organizations: []iam.Organization{ - { - Metadata: iacTypes.NewTestMetadata(), - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.StringTest("roles/editor"), - Members: []iacTypes.StringValue{ - iacTypes.StringTest("user:jane2@example.com"), - }, - }, - }, - }, - }, - Projects: []iam.Project{ - { - AutoCreateNetwork: iacTypes.BoolTest(true), - Metadata: iacTypes.NewTestMetadata(), - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.StringTest("roles/editor"), - Members: []iacTypes.StringValue{ - iacTypes.StringTest("user:jane3@example.com"), - }, - }, - }, - }, - }, - }, - }, - { - name: "google_project_iam ref by value", - terraform: ` -resource "google_project" "my_project" { - name = "My Project" - project_id = "your-project-id" - org_id = "1234567" -} - - -resource "google_project_iam_member" "project" { - project = "your-project-id" - role = "roles/editor" - member = "user:jane@example.com" -} -`, - expected: iam.IAM{ - Projects: []iam.Project{ - { - Metadata: iacTypes.NewTestMetadata(), - AutoCreateNetwork: iacTypes.BoolTest(true), - Members: []iam.Member{ - { - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.StringTest("roles/editor"), - Member: iacTypes.StringTest("user:jane@example.com"), - }, - }, - }, - }, - }, - }, - { - name: "only google_project_iam", - terraform: ` -resource "google_project_iam_member" "project" { - project = "your-project-id" - role = "roles/editor" - member = "user:jane@example.com" -} -`, - expected: iam.IAM{ - Projects: []iam.Project{ - { - Metadata: iacTypes.NewTestMetadata(), - AutoCreateNetwork: iacTypes.BoolTest(false), - Members: []iam.Member{ - { - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.StringTest("roles/editor"), - Member: iacTypes.StringTest("user:jane@example.com"), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - data "google_organization" "org" { - domain = "example.com" - } - - resource "google_project" "my_project" { - name = "My Project" - project_id = "your-project-id" - org_id = data.google_organization.org.id - auto_create_network = true - } - - resource "google_folder" "department1" { - display_name = "Department 1" - parent = data.google_organization.org.id - } - - resource "google_folder_iam_binding" "folder-123" { - folder = google_folder.department1.name - role = "roles/nothing" - members = [ - "user:not-alice@gmail.com", - ] - } - - resource "google_folder_iam_member" "admin" { - folder = google_folder.department1.name - role = "roles/editor" - member = "user:alice@gmail.com" - } - - resource "google_organization_iam_member" "org-123" { - org_id = data.google_organization.org.id - role = "roles/whatever" - member = "user:member@gmail.com" - } - - resource "google_organization_iam_binding" "binding" { - org_id = data.google_organization.org.id - role = "roles/browser" - - members = [ - "user:member_2@gmail.com", - ] - } - - resource "google_iam_workload_identity_pool_provider" "example" { - workload_identity_pool_id = "example-pool" - workload_identity_pool_provider_id = "example-provider" - attribute_condition = "assertion.repository_owner=='your-github-organization'" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Organizations, 1) - require.Len(t, adapted.Projects, 1) - require.Len(t, adapted.Folders, 1) - require.Len(t, adapted.Organizations[0].Bindings, 1) - require.Len(t, adapted.Organizations[0].Members, 1) - require.Len(t, adapted.WorkloadIdentityPoolProviders, 1) - - project := adapted.Projects[0] - folder := adapted.Folders[0] - binding := adapted.Organizations[0].Bindings[0] - member := adapted.Organizations[0].Members[0] - pool := adapted.WorkloadIdentityPoolProviders[0] - - assert.Equal(t, 6, project.Metadata.Range().GetStartLine()) - assert.Equal(t, 11, project.Metadata.Range().GetEndLine()) - - assert.Equal(t, 10, project.AutoCreateNetwork.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, project.AutoCreateNetwork.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, folder.Metadata.Range().GetStartLine()) - assert.Equal(t, 16, folder.Metadata.Range().GetEndLine()) - - assert.Equal(t, 18, folder.Bindings[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 24, folder.Bindings[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 20, folder.Bindings[0].Role.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 20, folder.Bindings[0].Role.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 21, folder.Bindings[0].Members[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 23, folder.Bindings[0].Members[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 26, folder.Members[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 30, folder.Members[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 29, folder.Members[0].Member.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 29, folder.Members[0].Member.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 28, folder.Members[0].Role.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 28, folder.Members[0].Role.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 32, member.Metadata.Range().GetStartLine()) - assert.Equal(t, 36, member.Metadata.Range().GetEndLine()) - - assert.Equal(t, 34, member.Role.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, member.Role.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 35, member.Member.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 35, member.Member.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 38, binding.Metadata.Range().GetStartLine()) - assert.Equal(t, 45, binding.Metadata.Range().GetEndLine()) - - assert.Equal(t, 40, binding.Role.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 40, binding.Role.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 42, binding.Members[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 44, binding.Members[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 51, pool.Metadata.Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/google/iam/convert.go b/pkg/iac/adapters/terraform/google/iam/convert.go deleted file mode 100644 index 0bf31b41b223..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/convert.go +++ /dev/null @@ -1,26 +0,0 @@ -package iam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func ParsePolicyBlock(block *terraform.Block) []iam.Binding { - var bindings []iam.Binding - for _, bindingBlock := range block.GetBlocks("binding") { - binding := iam.Binding{ - Metadata: bindingBlock.GetMetadata(), - Members: nil, - Role: bindingBlock.GetAttribute("role").AsStringValueOrDefault("", bindingBlock), - IncludesDefaultServiceAccount: iacTypes.BoolDefault(false, bindingBlock.GetMetadata()), - } - membersAttr := bindingBlock.GetAttribute("members") - members := membersAttr.AsStringValues().AsStrings() - for _, member := range members { - binding.Members = append(binding.Members, iacTypes.String(member, membersAttr.GetMetadata())) - } - bindings = append(bindings, binding) - } - return bindings -} diff --git a/pkg/iac/adapters/terraform/google/iam/folder_iam.go b/pkg/iac/adapters/terraform/google/iam/folder_iam.go deleted file mode 100644 index 7b572b2bc634..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/folder_iam.go +++ /dev/null @@ -1,99 +0,0 @@ -package iam - -import ( - "github.com/google/uuid" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -// see https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_folder_iam - -func (a *adapter) adaptFolderIAM() { - a.adaptFolders() - a.adaptFolderMembers() - a.adaptFolderBindings() -} - -const googleFolder = "google_folder" - -func (a *adapter) adaptFolders() { - for _, folderBlock := range a.modules.GetResourcesByType(googleFolder) { - a.folders[folderBlock.ID()] = &iam.Folder{ - Metadata: folderBlock.GetMetadata(), - } - } -} - -func (a *adapter) adaptFolderMembers() { - for _, iamBlock := range a.modules.GetResourcesByType("google_folder_iam_member") { - member := a.adaptMember(iamBlock) - - if folder := a.findFolder(iamBlock); folder != nil { - folder.Members = append(folder.Members, member) - } else { - // we didn't find the folder - add an unmanaged one - a.folders[uuid.NewString()] = &iam.Folder{ - Metadata: types.NewUnmanagedMetadata(), - Members: []iam.Member{member}, - } - } - } -} - -func (a *adapter) adaptFolderBindings() { - - for _, iamBlock := range a.modules.GetResourcesByType("google_folder_iam_policy") { - - policyAttr := iamBlock.GetAttribute("policy_data") - if policyAttr.IsNil() { - continue - } - - policyBlock, err := a.modules.GetReferencedBlock(policyAttr, iamBlock) - if err != nil { - continue - } - - bindings := ParsePolicyBlock(policyBlock) - - if folder := a.findFolder(iamBlock); folder != nil { - folder.Bindings = append(folder.Bindings, bindings...) - } else { - // we didn't find the folder - add an unmanaged one - a.folders[uuid.NewString()] = &iam.Folder{ - Metadata: types.NewUnmanagedMetadata(), - Bindings: bindings, - } - } - } - - for _, iamBlock := range a.modules.GetResourcesByType("google_folder_iam_binding") { - binding := a.adaptBinding(iamBlock) - - if folder := a.findFolder(iamBlock); folder != nil { - folder.Bindings = append(folder.Bindings, binding) - } else { - // we didn't find the folder - add an unmanaged one - a.folders[uuid.NewString()] = &iam.Folder{ - Metadata: types.NewUnmanagedMetadata(), - Bindings: []iam.Binding{binding}, - } - } - } -} - -func (a *adapter) findFolder(iamBlock *terraform.Block) *iam.Folder { - folderAttr := iamBlock.GetAttribute("folder") - refBlock, err := a.modules.GetReferencedBlock(folderAttr, iamBlock) - if err != nil { - return nil - } - - if folder, exists := a.folders[refBlock.ID()]; exists { - return folder - } - - return nil -} diff --git a/pkg/iac/adapters/terraform/google/iam/org_iam.go b/pkg/iac/adapters/terraform/google/iam/org_iam.go deleted file mode 100644 index fe6ac9917fbe..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/org_iam.go +++ /dev/null @@ -1,102 +0,0 @@ -package iam - -import ( - "github.com/google/uuid" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -// see https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_organization_iam - -func (a *adapter) adaptOrganizationIAM() { - a.adaptOrganizations() - a.adaptOrganizationMembers() - a.adaptOrganizationBindings() -} - -func (a *adapter) adaptOrganizations() { - for _, orgBlock := range a.modules.GetDatasByType("google_organization") { - a.orgs[orgBlock.ID()] = &iam.Organization{ - Metadata: orgBlock.GetMetadata(), - } - } -} - -func (a *adapter) adaptOrganizationMembers() { - for _, iamBlock := range a.modules.GetResourcesByType("google_organization_iam_member") { - - member := a.adaptMember(iamBlock) - - if org := a.findOrganization(iamBlock); org != nil { - org.Members = append(org.Members, member) - } else { - // we didn't find the org - add an unmanaged one - a.orgs[uuid.NewString()] = &iam.Organization{ - Metadata: types.NewUnmanagedMetadata(), - Members: []iam.Member{member}, - } - } - } -} - -func (a *adapter) adaptOrganizationBindings() { - - for _, iamBlock := range a.modules.GetResourcesByType("google_organization_iam_policy") { - - policyAttr := iamBlock.GetAttribute("policy_data") - if policyAttr.IsNil() { - continue - } - - policyBlock, err := a.modules.GetReferencedBlock(policyAttr, iamBlock) - if err != nil { - continue - } - - bindings := ParsePolicyBlock(policyBlock) - - if org := a.findOrganization(iamBlock); org != nil { - org.Bindings = append(org.Bindings, bindings...) - } else { - // we didn't find the org - add an unmanaged one - a.orgs[uuid.NewString()] = &iam.Organization{ - Metadata: types.NewUnmanagedMetadata(), - Bindings: bindings, - } - } - } - - for _, iamBlock := range a.modules.GetResourcesByType("google_organization_iam_binding") { - - binding := a.adaptBinding(iamBlock) - - if org := a.findOrganization(iamBlock); org != nil { - org.Bindings = append(org.Bindings, binding) - } else { - // we didn't find the org - add an unmanaged one - a.orgs[uuid.NewString()] = &iam.Organization{ - Metadata: types.NewUnmanagedMetadata(), - Bindings: []iam.Binding{binding}, - } - } - } -} - -func (a *adapter) findOrganization(iamBlock *terraform.Block) *iam.Organization { - orgAttr := iamBlock.GetAttribute("organization") - if orgAttr.IsNil() { - orgAttr = iamBlock.GetAttribute("org_id") - } - refBlock, err := a.modules.GetReferencedBlock(orgAttr, iamBlock) - if err != nil { - return nil - } - - if org, exists := a.orgs[refBlock.ID()]; exists { - return org - } - - return nil -} diff --git a/pkg/iac/adapters/terraform/google/iam/project_iam.go b/pkg/iac/adapters/terraform/google/iam/project_iam.go deleted file mode 100644 index fc7f539ded12..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/project_iam.go +++ /dev/null @@ -1,214 +0,0 @@ -package iam - -import ( - "strings" - - "github.com/google/uuid" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -// see https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam - -const GoogleProject = "google_project" - -func (a *adapter) adaptProjectIAM() { - a.adaptProjects() - a.adaptProjectMembers() - a.adaptProjectBindings() -} - -func (a *adapter) adaptProjects() { - for _, projectBlock := range a.modules.GetResourcesByType(GoogleProject) { - idAttr := projectBlock.GetAttribute("project_id") - if idAttr.IsString() { - a.projectsByID[idAttr.Value().AsString()] = projectBlock.ID() - } - - a.projects[projectBlock.ID()] = &iam.Project{ - Metadata: projectBlock.GetMetadata().Root(), - AutoCreateNetwork: projectBlock.GetAttribute("auto_create_network").AsBoolValueOrDefault(true, projectBlock), - } - } -} - -func (a *adapter) adaptMember(iamBlock *terraform.Block) iam.Member { - return AdaptMember(iamBlock, a.modules) -} - -func AdaptMember(iamBlock *terraform.Block, modules terraform.Modules) iam.Member { - member := iam.Member{ - Metadata: iamBlock.GetMetadata(), - Member: iamBlock.GetAttribute("member").AsStringValueOrDefault("", iamBlock), - Role: iamBlock.GetAttribute("role").AsStringValueOrDefault("", iamBlock), - DefaultServiceAccount: iacTypes.BoolDefault(false, iamBlock.GetMetadata()), - } - - memberAttr := iamBlock.GetAttribute("member") - if referencedBlock, err := modules.GetReferencedBlock(memberAttr, iamBlock); err == nil { - if strings.HasSuffix(referencedBlock.TypeLabel(), "_default_service_account") { - member.DefaultServiceAccount = iacTypes.Bool(true, memberAttr.GetMetadata()) - } - } - - return member -} - -// TODO(nikita): add new resources -var projectMemberResources = []string{ - "google_project_iam_member", - "google_cloud_run_service_iam_member", - "google_compute_instance_iam_member", - "google_compute_subnetwork_iam_member", - "google_data_catalog_entry_group_iam_member", - "google_pubsub_subscription_iam_member", - "google_pubsub_topic_iam_member", - "google_sourcerepo_repository_iam_member", - "google_spanner_database_iam_member", - "google_spanner_instance_iam_member", - "google_storage_bucket_iam_member", -} - -func (a *adapter) adaptProjectMembers() { - - for _, memberType := range projectMemberResources { - for _, iamBlock := range a.modules.GetResourcesByType(memberType) { - - member := a.adaptMember(iamBlock) - - if project := a.findProject(iamBlock); project != nil { - project.Members = append(project.Members, member) - } else { - // we didn't find the folder - add an unmanaged one - a.projects[uuid.NewString()] = &iam.Project{ - Metadata: iacTypes.NewUnmanagedMetadata(), - AutoCreateNetwork: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Members: []iam.Member{member}, - } - } - } - } -} - -func (a *adapter) adaptBinding(iamBlock *terraform.Block) iam.Binding { - return AdaptBinding(iamBlock, a.modules) -} - -func AdaptBinding(iamBlock *terraform.Block, modules terraform.Modules) iam.Binding { - binding := iam.Binding{ - Metadata: iamBlock.GetMetadata(), - Members: nil, - Role: iamBlock.GetAttribute("role").AsStringValueOrDefault("", iamBlock), - IncludesDefaultServiceAccount: iacTypes.BoolDefault(false, iamBlock.GetMetadata()), - } - membersAttr := iamBlock.GetAttribute("members") - members := membersAttr.AsStringValues().AsStrings() - for _, member := range members { - binding.Members = append(binding.Members, iacTypes.String(member, membersAttr.GetMetadata())) - } - if referencedBlock, err := modules.GetReferencedBlock(membersAttr, iamBlock); err == nil { - if strings.HasSuffix(referencedBlock.TypeLabel(), "_default_service_account") { - binding.IncludesDefaultServiceAccount = iacTypes.Bool(true, membersAttr.GetMetadata()) - } - } - return binding -} - -// TODO(nikita): add new resources -var projectBindingResources = []string{ - "google_project_iam_binding", - "google_cloud_run_service_iam_binding", - "google_compute_instance_iam_binding", - "google_compute_subnetwork_iam_binding", - "google_data_catalog_entry_group_iam_binding", - "google_pubsub_subscription_iam_binding", - "google_pubsub_topic_iam_binding", - "google_sourcerepo_repository_iam_binding", - "google_spanner_database_iam_binding", - "google_spanner_instance_iam_binding", - "google_storage_bucket_iam_binding", -} - -func (a *adapter) adaptProjectDataBindings() { - for _, iamBlock := range a.modules.GetResourcesByType("google_project_iam_policy") { - - policyAttr := iamBlock.GetAttribute("policy_data") - if policyAttr.IsNil() { - continue - } - - policyBlock, err := a.modules.GetReferencedBlock(policyAttr, iamBlock) - if err != nil { - continue - } - - bindings := ParsePolicyBlock(policyBlock) - - if project := a.findProject(iamBlock); project != nil { - project.Bindings = append(project.Bindings, bindings...) - } else { - // we didn't find the folder - add an unmanaged one - a.projects[uuid.NewString()] = &iam.Project{ - Metadata: iacTypes.NewUnmanagedMetadata(), - AutoCreateNetwork: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Bindings: bindings, - } - } - } - -} - -func (a *adapter) adaptProjectBindings() { - - a.adaptProjectDataBindings() - - for _, bindingType := range projectBindingResources { - for _, iamBlock := range a.modules.GetResourcesByType(bindingType) { - - binding := a.adaptBinding(iamBlock) - - if project := a.findProject(iamBlock); project != nil { - project.Bindings = append(project.Bindings, binding) - } else { - // we didn't find the folder - add an unmanaged one - a.projects[uuid.NewString()] = &iam.Project{ - Metadata: iacTypes.NewUnmanagedMetadata(), - AutoCreateNetwork: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Bindings: []iam.Binding{binding}, - } - } - } - } -} - -func (a *adapter) resolveProjectBlockID(projectAttr *terraform.Attribute, iamBlock *terraform.Block) string { - - if projectAttr.IsString() { - projectID := projectAttr.Value().AsString() - if blockID, exists := a.projectsByID[projectID]; exists { - return blockID - } - } - - refBlock, err := a.modules.GetReferencedBlock(projectAttr, iamBlock) - if err != nil { - return "" - } - return refBlock.ID() -} - -func (a *adapter) findProject(iamBlock *terraform.Block) *iam.Project { - projectAttr := iamBlock.GetAttribute("project") - blockID := a.resolveProjectBlockID(projectAttr, iamBlock) - if blockID == "" { - return nil - } - - if project, exists := a.projects[blockID]; exists { - return project - } - - return nil -} diff --git a/pkg/iac/adapters/terraform/google/iam/project_iam_test.go b/pkg/iac/adapters/terraform/google/iam/project_iam_test.go deleted file mode 100644 index 2ddeb9bbf3ca..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/project_iam_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package iam - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_AdaptBinding(t *testing.T) { - tests := []struct { - name string - terraform string - expected iam.Binding - }{ - { - name: "defined", - terraform: ` - resource "google_organization_iam_binding" "binding" { - org_id = data.google_organization.org.id - role = "roles/browser" - - members = [ - "user:alice@gmail.com", - ] - }`, - expected: iam.Binding{ - Metadata: iacTypes.NewTestMetadata(), - Members: []iacTypes.StringValue{ - iacTypes.String("user:alice@gmail.com", iacTypes.NewTestMetadata())}, - Role: iacTypes.String("roles/browser", iacTypes.NewTestMetadata()), - IncludesDefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - { - name: "defaults", - terraform: ` - resource "google_organization_iam_binding" "binding" { - }`, - expected: iam.Binding{ - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.String("", iacTypes.NewTestMetadata()), - IncludesDefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := AdaptBinding(modules.GetBlocks()[0], modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/google/iam/workload_identity_pool_providers.go b/pkg/iac/adapters/terraform/google/iam/workload_identity_pool_providers.go deleted file mode 100644 index 242c3a09b907..000000000000 --- a/pkg/iac/adapters/terraform/google/iam/workload_identity_pool_providers.go +++ /dev/null @@ -1,18 +0,0 @@ -package iam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" -) - -// See https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iam_workload_identity_pool_provider - -func (a *adapter) adaptWorkloadIdentityPoolProviders() { - for _, resource := range a.modules.GetResourcesByType("google_iam_workload_identity_pool_provider") { - a.workloadIdentityPoolProviders = append(a.workloadIdentityPoolProviders, iam.WorkloadIdentityPoolProvider{ - Metadata: resource.GetMetadata(), - WorkloadIdentityPoolId: resource.GetAttribute("workload_identity_pool_id").AsStringValueOrDefault("", resource), - WorkloadIdentityPoolProviderId: resource.GetAttribute("workload_identity_pool_provider_id").AsStringValueOrDefault("", resource), - AttributeCondition: resource.GetAttribute("attribute_condition").AsStringValueOrDefault("", resource), - }) - } -} diff --git a/pkg/iac/adapters/terraform/google/kms/adapt.go b/pkg/iac/adapters/terraform/google/kms/adapt.go deleted file mode 100644 index 564fcd6cb365..000000000000 --- a/pkg/iac/adapters/terraform/google/kms/adapt.go +++ /dev/null @@ -1,58 +0,0 @@ -package kms - -import ( - "strconv" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/kms" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) kms.KMS { - return kms.KMS{ - KeyRings: adaptKeyRings(modules), - } -} - -func adaptKeyRings(modules terraform.Modules) []kms.KeyRing { - var keyRings []kms.KeyRing - for _, module := range modules { - for _, resource := range module.GetResourcesByType("google_kms_key_ring") { - var keys []kms.Key - - keyBlocks := module.GetReferencingResources(resource, "google_kms_crypto_key", "key_ring") - for _, keyBlock := range keyBlocks { - keys = append(keys, adaptKey(keyBlock)) - } - keyRings = append(keyRings, kms.KeyRing{ - Metadata: resource.GetMetadata(), - Keys: keys, - }) - } - } - return keyRings -} - -func adaptKey(resource *terraform.Block) kms.Key { - - key := kms.Key{ - Metadata: resource.GetMetadata(), - RotationPeriodSeconds: types.IntDefault(-1, resource.GetMetadata()), - } - - rotationPeriodAttr := resource.GetAttribute("rotation_period") - if !rotationPeriodAttr.IsString() { - return key - } - rotationStr := rotationPeriodAttr.Value().AsString() - if rotationStr[len(rotationStr)-1:] != "s" { - return key - } - seconds, err := strconv.Atoi(rotationStr[:len(rotationStr)-1]) - if err != nil { - return key - } - - key.RotationPeriodSeconds = types.Int(seconds, rotationPeriodAttr.GetMetadata()) - return key -} diff --git a/pkg/iac/adapters/terraform/google/kms/adapt_test.go b/pkg/iac/adapters/terraform/google/kms/adapt_test.go deleted file mode 100644 index 7bc9a9646914..000000000000 --- a/pkg/iac/adapters/terraform/google/kms/adapt_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package kms - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/kms" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptKeyRings(t *testing.T) { - tests := []struct { - name string - terraform string - expected []kms.KeyRing - }{ - { - name: "configured", - terraform: ` - resource "google_kms_key_ring" "keyring" { - name = "keyring-example" - } - - resource "google_kms_crypto_key" "example-key" { - name = "crypto-key-example" - key_ring = google_kms_key_ring.keyring.id - rotation_period = "7776000s" - } -`, - expected: []kms.KeyRing{ - { - Metadata: iacTypes.NewTestMetadata(), - Keys: []kms.Key{ - { - Metadata: iacTypes.NewTestMetadata(), - RotationPeriodSeconds: iacTypes.Int(7776000, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "no keys", - terraform: ` - resource "google_kms_key_ring" "keyring" { - name = "keyring-example" - } - -`, - expected: []kms.KeyRing{ - { - Metadata: iacTypes.NewTestMetadata(), - }, - }, - }, - { - name: "default rotation period", - terraform: ` - resource "google_kms_key_ring" "keyring" { - name = "keyring-example" - } - - resource "google_kms_crypto_key" "example-key" { - name = "crypto-key-example" - key_ring = google_kms_key_ring.keyring.id - } -`, - expected: []kms.KeyRing{ - { - Metadata: iacTypes.NewTestMetadata(), - Keys: []kms.Key{ - { - Metadata: iacTypes.NewTestMetadata(), - RotationPeriodSeconds: iacTypes.Int(-1, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptKeyRings(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "google_kms_key_ring" "keyring" { - name = "keyring-example" - } - - resource "google_kms_crypto_key" "example-key" { - name = "crypto-key-example" - key_ring = google_kms_key_ring.keyring.id - rotation_period = "7776000s" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.KeyRings, 1) - require.Len(t, adapted.KeyRings[0].Keys, 1) - - key := adapted.KeyRings[0].Keys[0] - - assert.Equal(t, 2, adapted.KeyRings[0].Metadata.Range().GetStartLine()) - assert.Equal(t, 4, adapted.KeyRings[0].Metadata.Range().GetEndLine()) - - assert.Equal(t, 6, key.Metadata.Range().GetStartLine()) - assert.Equal(t, 10, key.Metadata.Range().GetEndLine()) - - assert.Equal(t, 9, key.RotationPeriodSeconds.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 9, key.RotationPeriodSeconds.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/google/sql/adapt.go b/pkg/iac/adapters/terraform/google/sql/adapt.go deleted file mode 100644 index 6d68795bcfac..000000000000 --- a/pkg/iac/adapters/terraform/google/sql/adapt.go +++ /dev/null @@ -1,150 +0,0 @@ -package sql - -import ( - "strconv" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/sql" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) sql.SQL { - return sql.SQL{ - Instances: adaptInstances(modules), - } -} - -func adaptInstances(modules terraform.Modules) []sql.DatabaseInstance { - var instances []sql.DatabaseInstance - for _, module := range modules { - for _, resource := range module.GetResourcesByType("google_sql_database_instance") { - instances = append(instances, adaptInstance(resource)) - } - } - return instances -} - -func adaptInstance(resource *terraform.Block) sql.DatabaseInstance { - - instance := sql.DatabaseInstance{ - Metadata: resource.GetMetadata(), - DatabaseVersion: resource.GetAttribute("database_version").AsStringValueOrDefault("", resource), - IsReplica: iacTypes.BoolDefault(false, resource.GetMetadata()), - Settings: sql.Settings{ - Metadata: resource.GetMetadata(), - Flags: sql.Flags{ - Metadata: resource.GetMetadata(), - LogTempFileSize: iacTypes.IntDefault(-1, resource.GetMetadata()), - LocalInFile: iacTypes.BoolDefault(false, resource.GetMetadata()), - ContainedDatabaseAuthentication: iacTypes.BoolDefault(true, resource.GetMetadata()), - CrossDBOwnershipChaining: iacTypes.BoolDefault(true, resource.GetMetadata()), - LogCheckpoints: iacTypes.BoolDefault(false, resource.GetMetadata()), - LogConnections: iacTypes.BoolDefault(false, resource.GetMetadata()), - LogDisconnections: iacTypes.BoolDefault(false, resource.GetMetadata()), - LogLockWaits: iacTypes.BoolDefault(false, resource.GetMetadata()), - LogMinMessages: iacTypes.StringDefault("", resource.GetMetadata()), - LogMinDurationStatement: iacTypes.IntDefault(-1, resource.GetMetadata()), - }, - Backups: sql.Backups{ - Metadata: resource.GetMetadata(), - Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()), - }, - IPConfiguration: sql.IPConfiguration{ - Metadata: resource.GetMetadata(), - RequireTLS: iacTypes.BoolDefault(false, resource.GetMetadata()), - SSLMode: iacTypes.String("", resource.GetMetadata()), - EnableIPv4: iacTypes.BoolDefault(true, resource.GetMetadata()), - AuthorizedNetworks: nil, - }, - }, - } - - if attr := resource.GetAttribute("master_instance_name"); attr.IsNotNil() { - instance.IsReplica = iacTypes.Bool(true, attr.GetMetadata()) - } - - if settingsBlock := resource.GetBlock("settings"); settingsBlock.IsNotNil() { - instance.Settings.Metadata = settingsBlock.GetMetadata() - if blocks := settingsBlock.GetBlocks("database_flags"); len(blocks) > 0 { - adaptFlags(blocks, &instance.Settings.Flags) - } - if backupBlock := settingsBlock.GetBlock("backup_configuration"); backupBlock.IsNotNil() { - instance.Settings.Backups.Metadata = backupBlock.GetMetadata() - backupConfigEnabledAttr := backupBlock.GetAttribute("enabled") - instance.Settings.Backups.Enabled = backupConfigEnabledAttr.AsBoolValueOrDefault(false, backupBlock) - } - if settingsBlock.HasChild("ip_configuration") { - instance.Settings.IPConfiguration = adaptIPConfig(settingsBlock.GetBlock("ip_configuration")) - } - } - return instance -} - -// nolint -func adaptFlags(resources terraform.Blocks, flags *sql.Flags) { - for _, resource := range resources { - - nameAttr := resource.GetAttribute("name") - valueAttr := resource.GetAttribute("value") - - if !nameAttr.IsString() || valueAttr.IsNil() { - continue - } - - switch nameAttr.Value().AsString() { - case "log_temp_files": - if logTempInt, err := strconv.Atoi(valueAttr.Value().AsString()); err == nil { - flags.LogTempFileSize = iacTypes.Int(logTempInt, nameAttr.GetMetadata()) - } - case "log_min_messages": - flags.LogMinMessages = valueAttr.AsStringValueOrDefault("", resource) - case "log_min_duration_statement": - if logMinDS, err := strconv.Atoi(valueAttr.Value().AsString()); err == nil { - flags.LogMinDurationStatement = iacTypes.Int(logMinDS, nameAttr.GetMetadata()) - } - case "local_infile": - flags.LocalInFile = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - case "log_checkpoints": - flags.LogCheckpoints = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - case "log_connections": - flags.LogConnections = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - case "log_disconnections": - flags.LogDisconnections = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - case "log_lock_waits": - flags.LogLockWaits = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - case "contained database authentication": - flags.ContainedDatabaseAuthentication = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - case "cross db ownership chaining": - flags.CrossDBOwnershipChaining = iacTypes.Bool(valueAttr.Equals("on"), valueAttr.GetMetadata()) - } - } -} - -func adaptIPConfig(resource *terraform.Block) sql.IPConfiguration { - var authorizedNetworks []struct { - Name iacTypes.StringValue - CIDR iacTypes.StringValue - } - - authNetworksBlocks := resource.GetBlocks("authorized_networks") - for _, authBlock := range authNetworksBlocks { - nameVal := authBlock.GetAttribute("name").AsStringValueOrDefault("", authBlock) - cidrVal := authBlock.GetAttribute("value").AsStringValueOrDefault("", authBlock) - - authorizedNetworks = append(authorizedNetworks, struct { - Name iacTypes.StringValue - CIDR iacTypes.StringValue - }{ - Name: nameVal, - CIDR: cidrVal, - }) - } - - return sql.IPConfiguration{ - Metadata: resource.GetMetadata(), - RequireTLS: resource.GetAttribute("require_ssl").AsBoolValueOrDefault(false, resource), - SSLMode: resource.GetAttribute("ssl_mode").AsStringValueOrDefault("", resource), - EnableIPv4: resource.GetAttribute("ipv4_enabled").AsBoolValueOrDefault(true, resource), - AuthorizedNetworks: authorizedNetworks, - } -} diff --git a/pkg/iac/adapters/terraform/google/sql/adapt_test.go b/pkg/iac/adapters/terraform/google/sql/adapt_test.go deleted file mode 100644 index fd3207ed3547..000000000000 --- a/pkg/iac/adapters/terraform/google/sql/adapt_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package sql - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/sql" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected sql.SQL - }{ - { - name: "default flags", - terraform: ` - resource "google_sql_database_instance" "db" { - database_version = "POSTGRES_12" - settings { - backup_configuration { - enabled = true - } - ip_configuration { - ipv4_enabled = false - authorized_networks { - value = "108.12.12.0/24" - name = "internal" - } - require_ssl = true - ssl_mode = "TRUSTED_CLIENT_CERTIFICATE_REQUIRED" - } - } - } -`, - expected: sql.SQL{ - Instances: []sql.DatabaseInstance{ - { - Metadata: iacTypes.NewTestMetadata(), - IsReplica: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - DatabaseVersion: iacTypes.String("POSTGRES_12", iacTypes.NewTestMetadata()), - Settings: sql.Settings{ - Metadata: iacTypes.NewTestMetadata(), - Backups: sql.Backups{ - Metadata: iacTypes.NewTestMetadata(), - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Flags: sql.Flags{ - Metadata: iacTypes.NewTestMetadata(), - LogMinDurationStatement: iacTypes.Int(-1, iacTypes.NewTestMetadata()), - ContainedDatabaseAuthentication: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - CrossDBOwnershipChaining: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - LocalInFile: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - LogCheckpoints: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - LogConnections: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - LogDisconnections: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - LogLockWaits: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - LogMinMessages: iacTypes.String("", iacTypes.NewTestMetadata()), - LogTempFileSize: iacTypes.Int(-1, iacTypes.NewTestMetadata()), - }, - IPConfiguration: sql.IPConfiguration{ - Metadata: iacTypes.NewTestMetadata(), - RequireTLS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - EnableIPv4: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - SSLMode: iacTypes.StringTest("TRUSTED_CLIENT_CERTIFICATE_REQUIRED"), - AuthorizedNetworks: []struct { - Name iacTypes.StringValue - CIDR iacTypes.StringValue - }{ - { - Name: iacTypes.String("internal", iacTypes.NewTestMetadata()), - CIDR: iacTypes.String("108.12.12.0/24", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func Test_adaptInstances(t *testing.T) { - tests := []struct { - name string - terraform string - expected []sql.DatabaseInstance - }{ - { - name: "all flags", - terraform: ` -resource "google_sql_database_instance" "backup_source_instance" { - name = "test-instance" - database_version = "POSTGRES_11" - - project = "test-project" - region = "europe-west6" - deletion_protection = false - settings { - tier = "db-f1-micro" - backup_configuration { - enabled = true - } - ip_configuration { - ipv4_enabled = false - private_network = "test-network" - require_ssl = true - } - database_flags { - name = "log_connections" - value = "on" - } - database_flags { - name = "log_temp_files" - value = "0" - } - database_flags { - name = "log_checkpoints" - value = "on" - } - database_flags { - name = "log_disconnections" - value = "on" - } - database_flags { - name = "log_lock_waits" - value = "on" - } - } -} - `, - expected: []sql.DatabaseInstance{ - { - Metadata: iacTypes.NewTestMetadata(), - DatabaseVersion: iacTypes.String("POSTGRES_11", iacTypes.NewTestMetadata()), - IsReplica: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Settings: sql.Settings{ - Backups: sql.Backups{ - Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - Flags: sql.Flags{ - LogConnections: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - LogTempFileSize: iacTypes.Int(0, iacTypes.NewTestMetadata()), - LogCheckpoints: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - LogDisconnections: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - LogLockWaits: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - ContainedDatabaseAuthentication: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - CrossDBOwnershipChaining: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - LocalInFile: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - LogMinDurationStatement: iacTypes.Int(-1, iacTypes.NewTestMetadata()), - LogMinMessages: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - IPConfiguration: sql.IPConfiguration{ - EnableIPv4: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - RequireTLS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptInstances(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "google_sql_database_instance" "backup_source_instance" { - name = "test-instance" - database_version = "POSTGRES_11" - - settings { - backup_configuration { - enabled = true - } - - ip_configuration { - ipv4_enabled = false - require_ssl = true - authorized_networks { - name = "internal" - value = "108.12.12.0/24" - } - } - - database_flags { - name = "log_connections" - value = "on" - } - database_flags { - name = "log_temp_files" - value = "0" - } - database_flags { - name = "log_checkpoints" - value = "on" - } - database_flags { - name = "log_disconnections" - value = "on" - } - database_flags { - name = "log_lock_waits" - value = "on" - } - } - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Instances, 1) - instance := adapted.Instances[0] - - assert.Equal(t, 2, instance.Metadata.Range().GetStartLine()) - assert.Equal(t, 41, instance.Metadata.Range().GetEndLine()) - - assert.Equal(t, 4, instance.DatabaseVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, instance.DatabaseVersion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, instance.Settings.Metadata.Range().GetStartLine()) - assert.Equal(t, 40, instance.Settings.Metadata.Range().GetEndLine()) - - assert.Equal(t, 7, instance.Settings.Backups.Metadata.Range().GetStartLine()) - assert.Equal(t, 9, instance.Settings.Backups.Metadata.Range().GetEndLine()) - - assert.Equal(t, 8, instance.Settings.Backups.Enabled.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 8, instance.Settings.Backups.Enabled.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, instance.Settings.IPConfiguration.Metadata.Range().GetStartLine()) - assert.Equal(t, 18, instance.Settings.IPConfiguration.Metadata.Range().GetEndLine()) - - assert.Equal(t, 12, instance.Settings.IPConfiguration.EnableIPv4.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 12, instance.Settings.IPConfiguration.EnableIPv4.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, instance.Settings.IPConfiguration.RequireTLS.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, instance.Settings.IPConfiguration.RequireTLS.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 15, instance.Settings.IPConfiguration.AuthorizedNetworks[0].Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 15, instance.Settings.IPConfiguration.AuthorizedNetworks[0].Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 16, instance.Settings.IPConfiguration.AuthorizedNetworks[0].CIDR.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 16, instance.Settings.IPConfiguration.AuthorizedNetworks[0].CIDR.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 22, instance.Settings.Flags.LogConnections.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 22, instance.Settings.Flags.LogConnections.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 25, instance.Settings.Flags.LogTempFileSize.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 25, instance.Settings.Flags.LogTempFileSize.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 34, instance.Settings.Flags.LogDisconnections.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 34, instance.Settings.Flags.LogDisconnections.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 38, instance.Settings.Flags.LogLockWaits.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 38, instance.Settings.Flags.LogLockWaits.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/google/storage/adapt.go b/pkg/iac/adapters/terraform/google/storage/adapt.go deleted file mode 100644 index 9fe918030151..000000000000 --- a/pkg/iac/adapters/terraform/google/storage/adapt.go +++ /dev/null @@ -1,129 +0,0 @@ -package storage - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/google/storage" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Adapt(modules terraform.Modules) storage.Storage { - return storage.Storage{ - Buckets: (&adapter{modules: modules}).adaptBuckets(), - } -} - -type adapter struct { - modules terraform.Modules - bindings []parentedBinding - members []parentedMember - bindingMap terraform.ResourceIDResolutions - memberMap terraform.ResourceIDResolutions -} - -func (a *adapter) adaptBuckets() []storage.Bucket { - - a.bindingMap = a.modules.GetChildResourceIDMapByType("google_storage_bucket_iam_binding", "google_storage_bucket_iam_policy") - a.memberMap = a.modules.GetChildResourceIDMapByType("google_storage_bucket_iam_member") - - a.adaptMembers() - a.adaptBindings() - - var buckets []storage.Bucket - for _, module := range a.modules { - for _, resource := range module.GetResourcesByType(GoogleStorageBucket) { - buckets = append(buckets, a.adaptBucketResource(resource)) - } - } - - orphanage := storage.Bucket{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Name: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - Location: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - EnableUniformBucketLevelAccess: iacTypes.BoolDefault(false, iacTypes.NewUnmanagedMetadata()), - Members: nil, - Bindings: nil, - } - for _, orphanedBindingID := range a.bindingMap.Orphans() { - for _, binding := range a.bindings { - if binding.blockID == orphanedBindingID { - orphanage.Bindings = append(orphanage.Bindings, binding.bindings...) - break - } - } - } - for _, orphanedMemberID := range a.memberMap.Orphans() { - for _, member := range a.members { - if member.blockID == orphanedMemberID { - orphanage.Members = append(orphanage.Members, member.member) - break - } - } - } - if len(orphanage.Bindings) > 0 || len(orphanage.Members) > 0 { - buckets = append(buckets, orphanage) - } - - return buckets -} - -func (a *adapter) adaptBucketResource(resourceBlock *terraform.Block) storage.Bucket { - - nameAttr := resourceBlock.GetAttribute("name") - nameValue := nameAttr.AsStringValueOrDefault("", resourceBlock) - - locationAttr := resourceBlock.GetAttribute("location") - locationValue := locationAttr.AsStringValueOrDefault("", resourceBlock) - - // See https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket#uniform_bucket_level_access - ublaAttr := resourceBlock.GetAttribute("uniform_bucket_level_access") - ublaValue := ublaAttr.AsBoolValueOrDefault(false, resourceBlock) - - bucket := storage.Bucket{ - Metadata: resourceBlock.GetMetadata(), - Name: nameValue, - Location: locationValue, - EnableUniformBucketLevelAccess: ublaValue, - Members: nil, - Bindings: nil, - Encryption: storage.BucketEncryption{ - Metadata: resourceBlock.GetMetadata(), - DefaultKMSKeyName: iacTypes.StringDefault("", resourceBlock.GetMetadata()), - }, - } - - if encBlock := resourceBlock.GetBlock("encryption"); encBlock.IsNotNil() { - bucket.Encryption.Metadata = encBlock.GetMetadata() - kmsKeyNameAttr := encBlock.GetAttribute("default_kms_key_name") - bucket.Encryption.DefaultKMSKeyName = kmsKeyNameAttr.AsStringValueOrDefault("", encBlock) - } - - var name string - if nameAttr.IsString() { - name = nameAttr.Value().AsString() - } - - for _, member := range a.members { - if member.bucketBlockID == resourceBlock.ID() { - bucket.Members = append(bucket.Members, member.member) - a.memberMap.Resolve(member.blockID) - continue - } - if name != "" && name == member.bucketID { - bucket.Members = append(bucket.Members, member.member) - a.memberMap.Resolve(member.blockID) - } - } - for _, binding := range a.bindings { - if binding.bucketBlockID == resourceBlock.ID() { - bucket.Bindings = append(bucket.Bindings, binding.bindings...) - a.bindingMap.Resolve(binding.blockID) - continue - } - if name != "" && name == binding.bucketID { - bucket.Bindings = append(bucket.Bindings, binding.bindings...) - a.bindingMap.Resolve(binding.blockID) - } - } - - return bucket -} diff --git a/pkg/iac/adapters/terraform/google/storage/adapt_test.go b/pkg/iac/adapters/terraform/google/storage/adapt_test.go deleted file mode 100644 index 0af0a99cb10a..000000000000 --- a/pkg/iac/adapters/terraform/google/storage/adapt_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package storage - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/storage" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Adapt(t *testing.T) { - tests := []struct { - name string - terraform string - expected storage.Storage - }{ - { - name: "defined", - terraform: ` - resource "google_storage_bucket" "static-site" { - name = "image-store.com" - location = "EU" - uniform_bucket_level_access = true - - encryption { - default_kms_key_name = "default-kms-key-name" - } - } - - resource "google_storage_bucket_iam_binding" "binding" { - bucket = google_storage_bucket.static-site.name - role = "roles/storage.admin #1" - members = [ - "group:test@example.com", - ] - } - - resource "google_storage_bucket_iam_member" "example" { - member = "serviceAccount:test@example.com" - bucket = google_storage_bucket.static-site.name - role = "roles/storage.admin #2" - }`, - expected: storage.Storage{ - Buckets: []storage.Bucket{ - { - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("image-store.com", iacTypes.NewTestMetadata()), - Location: iacTypes.String("EU", iacTypes.NewTestMetadata()), - EnableUniformBucketLevelAccess: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Members: []iacTypes.StringValue{ - iacTypes.String("group:test@example.com", iacTypes.NewTestMetadata()), - }, - Role: iacTypes.String("roles/storage.admin #1", iacTypes.NewTestMetadata()), - IncludesDefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Members: []iam.Member{ - { - Metadata: iacTypes.NewTestMetadata(), - Member: iacTypes.String("serviceAccount:test@example.com", iacTypes.NewTestMetadata()), - Role: iacTypes.String("roles/storage.admin #2", iacTypes.NewTestMetadata()), - DefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Encryption: storage.BucketEncryption{ - Metadata: iacTypes.NewTestMetadata(), - DefaultKMSKeyName: iacTypes.String("default-kms-key-name", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - { - name: "defaults", - terraform: ` - resource "google_storage_bucket" "static-site" { - } - - resource "google_storage_bucket_iam_binding" "binding" { - bucket = google_storage_bucket.static-site.name - } - - resource "google_storage_bucket_iam_member" "example" { - bucket = google_storage_bucket.static-site.name - }`, - expected: storage.Storage{ - Buckets: []storage.Bucket{ - { - Metadata: iacTypes.NewTestMetadata(), - Name: iacTypes.String("", iacTypes.NewTestMetadata()), - Location: iacTypes.String("", iacTypes.NewTestMetadata()), - EnableUniformBucketLevelAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - Bindings: []iam.Binding{ - { - Metadata: iacTypes.NewTestMetadata(), - Role: iacTypes.String("", iacTypes.NewTestMetadata()), - IncludesDefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Members: []iam.Member{ - { - Metadata: iacTypes.NewTestMetadata(), - Member: iacTypes.String("", iacTypes.NewTestMetadata()), - Role: iacTypes.String("", iacTypes.NewTestMetadata()), - DefaultServiceAccount: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Encryption: storage.BucketEncryption{ - Metadata: iacTypes.NewTestMetadata(), - DefaultKMSKeyName: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := Adapt(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} - -func TestLines(t *testing.T) { - src := ` - resource "google_storage_bucket" "static-site" { - name = "image-store.com" - location = "EU" - uniform_bucket_level_access = true - } - - resource "google_storage_bucket_iam_binding" "binding" { - bucket = google_storage_bucket.static-site.name - role = "roles/storage.admin #1" - members = [ - "group:test@example.com", - ] - } - - resource "google_storage_bucket_iam_member" "example" { - member = "serviceAccount:test@example.com" - bucket = google_storage_bucket.static-site.name - role = "roles/storage.admin #2" - }` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Buckets, 1) - require.Len(t, adapted.Buckets[0].Bindings, 1) - require.Len(t, adapted.Buckets[0].Members, 1) - - bucket := adapted.Buckets[0] - binding := adapted.Buckets[0].Bindings[0] - member := adapted.Buckets[0].Members[0] - - assert.Equal(t, 2, bucket.Metadata.Range().GetStartLine()) - assert.Equal(t, 6, bucket.Metadata.Range().GetEndLine()) - - assert.Equal(t, 3, bucket.Name.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, bucket.Name.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, bucket.Location.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, bucket.Location.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, bucket.EnableUniformBucketLevelAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, bucket.EnableUniformBucketLevelAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 8, binding.Metadata.Range().GetStartLine()) - assert.Equal(t, 14, binding.Metadata.Range().GetEndLine()) - - assert.Equal(t, 10, binding.Role.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, binding.Role.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, binding.Members[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, binding.Members[0].GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 16, member.Metadata.Range().GetStartLine()) - assert.Equal(t, 20, member.Metadata.Range().GetEndLine()) - - assert.Equal(t, 17, member.Member.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, member.Member.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 19, member.Role.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 19, member.Role.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/google/storage/iam.go b/pkg/iac/adapters/terraform/google/storage/iam.go deleted file mode 100644 index 6f6cdd000c94..000000000000 --- a/pkg/iac/adapters/terraform/google/storage/iam.go +++ /dev/null @@ -1,98 +0,0 @@ -package storage - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/google/iam" - iamTypes "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" -) - -type parentedBinding struct { - blockID string - bucketID string - bucketBlockID string - bindings []iamTypes.Binding -} - -const GoogleStorageBucket = "google_storage_bucket" - -type parentedMember struct { - blockID string - bucketID string - bucketBlockID string - member iamTypes.Member -} - -func (a *adapter) adaptBindings() { - - for _, iamBlock := range a.modules.GetResourcesByType("google_storage_bucket_iam_policy") { - var parented parentedBinding - parented.blockID = iamBlock.ID() - - bucketAttr := iamBlock.GetAttribute("bucket") - if bucketAttr.IsString() { - parented.bucketID = bucketAttr.Value().AsString() - } - - if refBlock, err := a.modules.GetReferencedBlock(bucketAttr, iamBlock); err == nil { - if refBlock.TypeLabel() == GoogleStorageBucket { - parented.bucketBlockID = refBlock.ID() - } - } - - policyAttr := iamBlock.GetAttribute("policy_data") - if policyAttr.IsNil() { - continue - } - - policyBlock, err := a.modules.GetReferencedBlock(policyAttr, iamBlock) - if err != nil { - continue - } - - parented.bindings = iam.ParsePolicyBlock(policyBlock) - a.bindings = append(a.bindings, parented) - } - - for _, iamBlock := range a.modules.GetResourcesByType("google_storage_bucket_iam_binding") { - - var parented parentedBinding - parented.blockID = iamBlock.ID() - parented.bindings = []iamTypes.Binding{iam.AdaptBinding(iamBlock, a.modules)} - - bucketAttr := iamBlock.GetAttribute("bucket") - if bucketAttr.IsString() { - parented.bucketID = bucketAttr.Value().AsString() - } - - if refBlock, err := a.modules.GetReferencedBlock(bucketAttr, iamBlock); err == nil { - if refBlock.TypeLabel() == GoogleStorageBucket { - parented.bucketBlockID = refBlock.ID() - } - } - - a.bindings = append(a.bindings, parented) - } -} - -func (a *adapter) adaptMembers() { - - for _, iamBlock := range a.modules.GetResourcesByType("google_storage_bucket_iam_member") { - - var parented parentedMember - parented.blockID = iamBlock.ID() - parented.member = iam.AdaptMember(iamBlock, a.modules) - - bucketAttr := iamBlock.GetAttribute("bucket") - if bucketAttr.IsString() { - parented.bucketID = bucketAttr.Value().AsString() - } - - if refBlock, err := a.modules.GetReferencedBlock(bucketAttr, iamBlock); err == nil { - if refBlock.TypeLabel() == GoogleStorageBucket { - parented.bucketBlockID = refBlock.ID() - } - } - - a.members = append(a.members, parented) - } - -} diff --git a/pkg/iac/adapters/terraform/kubernetes/adapt.go b/pkg/iac/adapters/terraform/kubernetes/adapt.go deleted file mode 100644 index 52eed9db4052..000000000000 --- a/pkg/iac/adapters/terraform/kubernetes/adapt.go +++ /dev/null @@ -1,123 +0,0 @@ -package kubernetes - -import ( - "regexp" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/providers/kubernetes" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -var versionRegex = regexp.MustCompile(`^v\d+(beta\d+)?$`) - -func Adapt(modules terraform.Modules) kubernetes.Kubernetes { - return kubernetes.Kubernetes{ - NetworkPolicies: adaptNetworkPolicies(modules), - } -} - -func adaptNetworkPolicies(modules terraform.Modules) []kubernetes.NetworkPolicy { - var networkPolicies []kubernetes.NetworkPolicy - for _, module := range modules { - for _, resource := range getBlocksIgnoreVersion(module, "resource", "kubernetes_network_policy") { - networkPolicies = append(networkPolicies, adaptNetworkPolicy(resource)) - } - } - return networkPolicies -} - -func adaptNetworkPolicy(resourceBlock *terraform.Block) kubernetes.NetworkPolicy { - - policy := kubernetes.NetworkPolicy{ - Metadata: resourceBlock.GetMetadata(), - Spec: kubernetes.NetworkPolicySpec{ - Metadata: resourceBlock.GetMetadata(), - Egress: kubernetes.Egress{ - Metadata: resourceBlock.GetMetadata(), - Ports: nil, - DestinationCIDRs: nil, - }, - Ingress: kubernetes.Ingress{ - Metadata: resourceBlock.GetMetadata(), - Ports: nil, - SourceCIDRs: nil, - }, - }, - } - - if specBlock := resourceBlock.GetBlock("spec"); specBlock.IsNotNil() { - if egressBlock := specBlock.GetBlock("egress"); egressBlock.IsNotNil() { - policy.Spec.Egress.Metadata = egressBlock.GetMetadata() - for _, port := range egressBlock.GetBlocks("ports") { - numberAttr := port.GetAttribute("number") - numberVal := numberAttr.AsStringValueOrDefault("", port) - - protocolAttr := port.GetAttribute("protocol") - protocolVal := protocolAttr.AsStringValueOrDefault("", port) - - policy.Spec.Egress.Ports = append(policy.Spec.Egress.Ports, kubernetes.Port{ - Metadata: port.GetMetadata(), - Number: numberVal, - Protocol: protocolVal, - }) - } - - for _, to := range egressBlock.GetBlocks("to") { - cidrAtrr := to.GetBlock("ip_block").GetAttribute("cidr") - cidrVal := cidrAtrr.AsStringValueOrDefault("", to) - - policy.Spec.Egress.DestinationCIDRs = append(policy.Spec.Egress.DestinationCIDRs, cidrVal) - } - } - - if ingressBlock := specBlock.GetBlock("ingress"); ingressBlock.IsNotNil() { - policy.Spec.Ingress.Metadata = ingressBlock.GetMetadata() - for _, port := range ingressBlock.GetBlocks("ports") { - numberAttr := port.GetAttribute("number") - numberVal := numberAttr.AsStringValueOrDefault("", port) - - protocolAttr := port.GetAttribute("protocol") - protocolVal := protocolAttr.AsStringValueOrDefault("", port) - - policy.Spec.Ingress.Ports = append(policy.Spec.Ingress.Ports, kubernetes.Port{ - Metadata: port.GetMetadata(), - Number: numberVal, - Protocol: protocolVal, - }) - } - - for _, from := range ingressBlock.GetBlocks("from") { - cidrAtrr := from.GetBlock("ip_block").GetAttribute("cidr") - cidrVal := cidrAtrr.AsStringValueOrDefault("", from) - - policy.Spec.Ingress.SourceCIDRs = append(policy.Spec.Ingress.SourceCIDRs, cidrVal) - } - } - } - - return policy -} - -// https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/guides/versioned-resources -func getBlocksIgnoreVersion(module *terraform.Module, blockType, resourceType string) terraform.Blocks { - var res terraform.Blocks - for _, block := range module.GetBlocks().OfType(blockType) { - if isMatchingTypeLabel(block.TypeLabel(), resourceType) { - res = append(res, block) - } - } - return res -} - -func isMatchingTypeLabel(typeLabel, resourceType string) bool { - if typeLabel == resourceType { - return true - } - - versionPart, found := strings.CutPrefix(typeLabel, resourceType+"_") - if !found { - return false - } - - return versionRegex.MatchString(versionPart) -} diff --git a/pkg/iac/adapters/terraform/kubernetes/adapt_test.go b/pkg/iac/adapters/terraform/kubernetes/adapt_test.go deleted file mode 100644 index eea390bd2e01..000000000000 --- a/pkg/iac/adapters/terraform/kubernetes/adapt_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package kubernetes - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIsMatchingTypeLabel(t *testing.T) { - tests := []struct { - name string - typeLabel string - resourceType string - expected bool - }{ - { - name: "without version", - typeLabel: "kubernetes_network_policy", - resourceType: "kubernetes_network_policy", - expected: true, - }, - { - name: "v1", - typeLabel: "kubernetes_network_policy_v1", - resourceType: "kubernetes_network_policy", - expected: true, - }, - { - name: "beta version", - typeLabel: "kubernetes_horizontal_pod_autoscaler_v2beta2", - resourceType: "kubernetes_horizontal_pod_autoscaler", - expected: true, - }, - { - name: "another type of resource", - typeLabel: "kubernetes_network_policy", - resourceType: "kubernetes_horizontal_pod_autoscaler", - expected: false, - }, - { - name: "similar resource type", - typeLabel: "kubernetes_network_policy_test_v1", - resourceType: "kubernetes_network_policy", - expected: false, - }, - { - name: "empty resource type", - typeLabel: "kubernetes_network_policy_test_v1", - resourceType: "", - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := isMatchingTypeLabel(tt.typeLabel, tt.resourceType) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/computing/adapt.go b/pkg/iac/adapters/terraform/nifcloud/computing/adapt.go deleted file mode 100644 index 399f492ba4e7..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/computing/adapt.go +++ /dev/null @@ -1,16 +0,0 @@ -package computing - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/computing" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) computing.Computing { - - sgAdapter := sgAdapter{sgRuleIDs: modules.GetChildResourceIDMapByType("nifcloud_security_group_rule")} - - return computing.Computing{ - SecurityGroups: sgAdapter.adaptSecurityGroups(modules), - Instances: adaptInstances(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/computing/adapt_test.go b/pkg/iac/adapters/terraform/nifcloud/computing/adapt_test.go deleted file mode 100644 index 88af58c58674..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/computing/adapt_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package computing - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func TestLines(t *testing.T) { - src := ` -resource "nifcloud_instance" "example" { - security_group = nifcloud_security_group.example.group_name - - network_interface { - network_id = "net-COMMON_PRIVATE" - } -} - -resource "nifcloud_security_group" "example" { - group_name = "example" - description = "memo" -} - -resource "nifcloud_security_group_rule" "example" { - type = "IN" - security_group_names = [nifcloud_security_group.example.group_name] - from_port = 22 - to_port = 22 - protocol = "TCP" - description = "memo" - cidr_ip = "1.2.3.4/32" -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Instances, 1) - require.Len(t, adapted.SecurityGroups, 1) - - instance := adapted.Instances[0] - sg := adapted.SecurityGroups[0] - - assert.Equal(t, 3, instance.SecurityGroup.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, instance.SecurityGroup.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, instance.NetworkInterfaces[0].NetworkID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, instance.NetworkInterfaces[0].NetworkID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 12, sg.Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 12, sg.Description.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 21, sg.IngressRules[0].Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 21, sg.IngressRules[0].Description.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 22, sg.IngressRules[0].CIDR.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 22, sg.IngressRules[0].CIDR.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/nifcloud/computing/instance.go b/pkg/iac/adapters/terraform/nifcloud/computing/instance.go deleted file mode 100644 index ebbce94439dd..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/computing/instance.go +++ /dev/null @@ -1,35 +0,0 @@ -package computing - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/computing" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptInstances(modules terraform.Modules) []computing.Instance { - var instances []computing.Instance - - for _, resource := range modules.GetResourcesByType("nifcloud_instance") { - instances = append(instances, adaptInstance(resource)) - } - return instances -} - -func adaptInstance(resource *terraform.Block) computing.Instance { - var networkInterfaces []computing.NetworkInterface - networkInterfaceBlocks := resource.GetBlocks("network_interface") - for _, networkInterfaceBlock := range networkInterfaceBlocks { - networkInterfaces = append( - networkInterfaces, - computing.NetworkInterface{ - Metadata: networkInterfaceBlock.GetMetadata(), - NetworkID: networkInterfaceBlock.GetAttribute("network_id").AsStringValueOrDefault("", resource), - }, - ) - } - - return computing.Instance{ - Metadata: resource.GetMetadata(), - SecurityGroup: resource.GetAttribute("security_group").AsStringValueOrDefault("", resource), - NetworkInterfaces: networkInterfaces, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/computing/instance_test.go b/pkg/iac/adapters/terraform/nifcloud/computing/instance_test.go deleted file mode 100644 index 81d996cc7e62..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/computing/instance_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package computing - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/computing" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptInstances(t *testing.T) { - tests := []struct { - name string - terraform string - expected []computing.Instance - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_instance" "my_example" { - security_group = "example-security-group" - network_interface { - network_id = "net-COMMON_PRIVATE" - } - } -`, - expected: []computing.Instance{{ - Metadata: iacTypes.NewTestMetadata(), - SecurityGroup: iacTypes.String("example-security-group", iacTypes.NewTestMetadata()), - NetworkInterfaces: []computing.NetworkInterface{ - { - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("net-COMMON_PRIVATE", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_instance" "my_example" { - network_interface { - } - } -`, - - expected: []computing.Instance{{ - Metadata: iacTypes.NewTestMetadata(), - SecurityGroup: iacTypes.String("", iacTypes.NewTestMetadata()), - NetworkInterfaces: []computing.NetworkInterface{ - { - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptInstances(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/computing/security_group.go b/pkg/iac/adapters/terraform/nifcloud/computing/security_group.go deleted file mode 100644 index 38699a6f2555..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/computing/security_group.go +++ /dev/null @@ -1,76 +0,0 @@ -package computing - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/computing" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type sgAdapter struct { - sgRuleIDs terraform.ResourceIDResolutions -} - -func (a *sgAdapter) adaptSecurityGroups(modules terraform.Modules) []computing.SecurityGroup { - var securityGroups []computing.SecurityGroup - for _, resource := range modules.GetResourcesByType("nifcloud_security_group") { - securityGroups = append(securityGroups, a.adaptSecurityGroup(resource, modules)) - } - orphanResources := modules.GetResourceByIDs(a.sgRuleIDs.Orphans()...) - if len(orphanResources) > 0 { - orphanage := computing.SecurityGroup{ - Metadata: iacTypes.NewUnmanagedMetadata(), - Description: iacTypes.StringDefault("", iacTypes.NewUnmanagedMetadata()), - IngressRules: nil, - } - for _, sgRule := range orphanResources { - if sgRule.GetAttribute("type").Equals("IN") { - orphanage.IngressRules = append(orphanage.IngressRules, adaptSGRule(sgRule, modules)) - } - if sgRule.GetAttribute("type").Equals("OUT") { - orphanage.EgressRules = append(orphanage.EgressRules, adaptSGRule(sgRule, modules)) - } - } - securityGroups = append(securityGroups, orphanage) - } - - return securityGroups -} - -func (a *sgAdapter) adaptSecurityGroup(resource *terraform.Block, module terraform.Modules) computing.SecurityGroup { - var ingressRules, egressRules []computing.SecurityGroupRule - - descriptionAttr := resource.GetAttribute("description") - descriptionVal := descriptionAttr.AsStringValueOrDefault("", resource) - - rulesBlocks := module.GetReferencingResources(resource, "nifcloud_security_group_rule", "security_group_names") - for _, ruleBlock := range rulesBlocks { - a.sgRuleIDs.Resolve(ruleBlock.ID()) - if ruleBlock.GetAttribute("type").Equals("IN") { - ingressRules = append(ingressRules, adaptSGRule(ruleBlock, module)) - } - if ruleBlock.GetAttribute("type").Equals("OUT") { - egressRules = append(egressRules, adaptSGRule(ruleBlock, module)) - } - } - - return computing.SecurityGroup{ - Metadata: resource.GetMetadata(), - Description: descriptionVal, - IngressRules: ingressRules, - EgressRules: egressRules, - } -} - -func adaptSGRule(resource *terraform.Block, modules terraform.Modules) computing.SecurityGroupRule { - ruleDescAttr := resource.GetAttribute("description") - ruleDescVal := ruleDescAttr.AsStringValueOrDefault("", resource) - - cidrAttr := resource.GetAttribute("cidr_ip") - cidrVal := cidrAttr.AsStringValueOrDefault("", resource) - - return computing.SecurityGroupRule{ - Metadata: resource.GetMetadata(), - Description: ruleDescVal, - CIDR: cidrVal, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/computing/security_group_test.go b/pkg/iac/adapters/terraform/nifcloud/computing/security_group_test.go deleted file mode 100644 index d7e514831667..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/computing/security_group_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package computing - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/computing" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptSecurityGroups(t *testing.T) { - tests := []struct { - name string - terraform string - expected []computing.SecurityGroup - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_security_group" "example" { - group_name = "example" - description = "memo" - } - - resource "nifcloud_security_group_rule" "example" { - type = "IN" - security_group_names = [nifcloud_security_group.example.group_name] - from_port = 22 - to_port = 22 - protocol = "TCP" - description = "memo" - cidr_ip = "1.2.3.4/32" - } -`, - expected: []computing.SecurityGroup{{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("memo", iacTypes.NewTestMetadata()), - IngressRules: []computing.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - CIDR: iacTypes.String("1.2.3.4/32", iacTypes.NewTestMetadata()), - Description: iacTypes.String("memo", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_security_group" "example" { - } - - resource "nifcloud_security_group_rule" "example" { - type = "IN" - security_group_names = [nifcloud_security_group.example.group_name] - } - -`, - - expected: []computing.SecurityGroup{{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - IngressRules: []computing.SecurityGroupRule{ - { - Metadata: iacTypes.NewTestMetadata(), - CIDR: iacTypes.String("", iacTypes.NewTestMetadata()), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - sgAdapter := sgAdapter{sgRuleIDs: modules.GetChildResourceIDMapByType("nifcloud_security_group_rule")} - adapted := sgAdapter.adaptSecurityGroups(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/dns/adapt.go b/pkg/iac/adapters/terraform/nifcloud/dns/adapt.go deleted file mode 100644 index c1118e98ef23..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/dns/adapt.go +++ /dev/null @@ -1,12 +0,0 @@ -package dns - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/dns" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) dns.DNS { - return dns.DNS{ - Records: adaptRecords(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/dns/adapt_test.go b/pkg/iac/adapters/terraform/nifcloud/dns/adapt_test.go deleted file mode 100644 index b3c0f535667b..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/dns/adapt_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package dns - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func TestLines(t *testing.T) { - src := ` -resource "nifcloud_dns_record" "example" { - type = "A" - record = "example-record" -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.Records, 1) - - record := adapted.Records[0] - - assert.Equal(t, 3, record.Type.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, record.Type.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, record.Record.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, record.Record.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/nifcloud/dns/record.go b/pkg/iac/adapters/terraform/nifcloud/dns/record.go deleted file mode 100644 index 84e61889cac0..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/dns/record.go +++ /dev/null @@ -1,23 +0,0 @@ -package dns - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/dns" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptRecords(modules terraform.Modules) []dns.Record { - var records []dns.Record - - for _, resource := range modules.GetResourcesByType("nifcloud_dns_record") { - records = append(records, adaptRecord(resource)) - } - return records -} - -func adaptRecord(resource *terraform.Block) dns.Record { - return dns.Record{ - Metadata: resource.GetMetadata(), - Record: resource.GetAttribute("record").AsStringValueOrDefault("", resource), - Type: resource.GetAttribute("type").AsStringValueOrDefault("", resource), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/dns/record_test.go b/pkg/iac/adapters/terraform/nifcloud/dns/record_test.go deleted file mode 100644 index 75fd1c8d06bc..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/dns/record_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package dns - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/dns" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptRecords(t *testing.T) { - tests := []struct { - name string - terraform string - expected []dns.Record - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_dns_record" "example" { - type = "A" - record = "example-record" - } -`, - expected: []dns.Record{{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("A", iacTypes.NewTestMetadata()), - Record: iacTypes.String("example-record", iacTypes.NewTestMetadata()), - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_dns_record" "example" { - } -`, - - expected: []dns.Record{{ - Metadata: iacTypes.NewTestMetadata(), - Type: iacTypes.String("", iacTypes.NewTestMetadata()), - Record: iacTypes.String("", iacTypes.NewTestMetadata()), - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptRecords(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nas/adapt.go b/pkg/iac/adapters/terraform/nifcloud/nas/adapt.go deleted file mode 100644 index c1b60fc551e2..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nas/adapt.go +++ /dev/null @@ -1,13 +0,0 @@ -package nas - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/nas" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) nas.NAS { - return nas.NAS{ - NASSecurityGroups: adaptNASSecurityGroups(modules), - NASInstances: adaptNASInstances(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nas/adapt_test.go b/pkg/iac/adapters/terraform/nifcloud/nas/adapt_test.go deleted file mode 100644 index bbd18e71a7d2..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nas/adapt_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package nas - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func TestLines(t *testing.T) { - src := ` -resource "nifcloud_nas_instance" "example" { - network_id = "example-network" -} - -resource "nifcloud_nas_security_group" "example" { - description = "memo" - - rule { - cidr_ip = "0.0.0.0/0" - } -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.NASInstances, 1) - require.Len(t, adapted.NASSecurityGroups, 1) - - nasInstance := adapted.NASInstances[0] - nasSecurityGroup := adapted.NASSecurityGroups[0] - - assert.Equal(t, 3, nasInstance.NetworkID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, nasInstance.NetworkID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 7, nasSecurityGroup.Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, nasSecurityGroup.Description.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 10, nasSecurityGroup.CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 10, nasSecurityGroup.CIDRs[0].GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nas/nas_instance.go b/pkg/iac/adapters/terraform/nifcloud/nas/nas_instance.go deleted file mode 100644 index e04024f0c4dd..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nas/nas_instance.go +++ /dev/null @@ -1,22 +0,0 @@ -package nas - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/nas" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptNASInstances(modules terraform.Modules) []nas.NASInstance { - var nasInstances []nas.NASInstance - - for _, resource := range modules.GetResourcesByType("nifcloud_nas_instance") { - nasInstances = append(nasInstances, adaptNASInstance(resource)) - } - return nasInstances -} - -func adaptNASInstance(resource *terraform.Block) nas.NASInstance { - return nas.NASInstance{ - Metadata: resource.GetMetadata(), - NetworkID: resource.GetAttribute("network_id").AsStringValueOrDefault("net-COMMON_PRIVATE", resource), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nas/nas_instance_test.go b/pkg/iac/adapters/terraform/nifcloud/nas/nas_instance_test.go deleted file mode 100644 index f7a4965b1703..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nas/nas_instance_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package nas - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/nas" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptNASInstances(t *testing.T) { - tests := []struct { - name string - terraform string - expected []nas.NASInstance - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_nas_instance" "example" { - network_id = "example-network" - } -`, - expected: []nas.NASInstance{{ - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("example-network", iacTypes.NewTestMetadata()), - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_nas_instance" "example" { - } -`, - - expected: []nas.NASInstance{{ - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("net-COMMON_PRIVATE", iacTypes.NewTestMetadata()), - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptNASInstances(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nas/nas_security_group.go b/pkg/iac/adapters/terraform/nifcloud/nas/nas_security_group.go deleted file mode 100644 index 468cbddce309..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nas/nas_security_group.go +++ /dev/null @@ -1,30 +0,0 @@ -package nas - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/nas" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptNASSecurityGroups(modules terraform.Modules) []nas.NASSecurityGroup { - var nasSecurityGroups []nas.NASSecurityGroup - - for _, resource := range modules.GetResourcesByType("nifcloud_nas_security_group") { - nasSecurityGroups = append(nasSecurityGroups, adaptNASSecurityGroup(resource)) - } - return nasSecurityGroups -} - -func adaptNASSecurityGroup(resource *terraform.Block) nas.NASSecurityGroup { - var cidrs []iacTypes.StringValue - - for _, rule := range resource.GetBlocks("rule") { - cidrs = append(cidrs, rule.GetAttribute("cidr_ip").AsStringValueOrDefault("", resource)) - } - - return nas.NASSecurityGroup{ - Metadata: resource.GetMetadata(), - Description: resource.GetAttribute("description").AsStringValueOrDefault("", resource), - CIDRs: cidrs, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nas/nas_security_group_test.go b/pkg/iac/adapters/terraform/nifcloud/nas/nas_security_group_test.go deleted file mode 100644 index a83ac31024ac..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nas/nas_security_group_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package nas - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/nas" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptNASSecurityGroups(t *testing.T) { - tests := []struct { - name string - terraform string - expected []nas.NASSecurityGroup - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_nas_security_group" "example" { - description = "memo" - - rule { - cidr_ip = "0.0.0.0/0" - } - } -`, - expected: []nas.NASSecurityGroup{{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("memo", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("0.0.0.0/0", iacTypes.NewTestMetadata()), - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_nas_security_group" "example" { - rule { - } - } -`, - - expected: []nas.NASSecurityGroup{{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptNASSecurityGroups(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/adapt.go b/pkg/iac/adapters/terraform/nifcloud/network/adapt.go deleted file mode 100644 index b3006abcd1d8..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/adapt.go +++ /dev/null @@ -1,16 +0,0 @@ -package network - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) network.Network { - - return network.Network{ - ElasticLoadBalancers: adaptElasticLoadBalancers(modules), - LoadBalancers: adaptLoadBalancers(modules), - Routers: adaptRouters(modules), - VpnGateways: adaptVpnGateways(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/adapt_test.go b/pkg/iac/adapters/terraform/nifcloud/network/adapt_test.go deleted file mode 100644 index 44af9f204343..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/adapt_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func TestLines(t *testing.T) { - src := ` -resource "nifcloud_elb" "example" { - protocol = "HTTP" - - network_interface { - network_id = "net-COMMON_PRIVATE" - is_vip_network = false - } -} - -resource "nifcloud_load_balancer" "example" { - ssl_policy_id = "example-ssl-policy-id" - load_balancer_port = 8080 -} - -resource "nifcloud_router" "example" { - security_group = nifcloud_security_group.example.group_name - - network_interface { - network_id = "net-COMMON_PRIVATE" - } -} - -resource "nifcloud_security_group" "example" { - group_name = "example" - description = "memo" -} - -resource "nifcloud_vpn_gateway" "example" { - security_group = nifcloud_security_group.example.group_name -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.ElasticLoadBalancers, 1) - require.Len(t, adapted.LoadBalancers, 1) - require.Len(t, adapted.Routers, 1) - require.Len(t, adapted.VpnGateways, 1) - - elb := adapted.ElasticLoadBalancers[0] - lb := adapted.LoadBalancers[0] - router := adapted.Routers[0] - vpngw := adapted.VpnGateways[0] - - assert.Equal(t, 3, elb.Listeners[0].Protocol.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, elb.Listeners[0].Protocol.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, elb.NetworkInterfaces[0].NetworkID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, elb.NetworkInterfaces[0].NetworkID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 7, elb.NetworkInterfaces[0].IsVipNetwork.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, elb.NetworkInterfaces[0].IsVipNetwork.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 12, lb.Listeners[0].TLSPolicy.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 12, lb.Listeners[0].TLSPolicy.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 13, lb.Listeners[0].Protocol.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 13, lb.Listeners[0].Protocol.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 17, router.SecurityGroup.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 17, router.SecurityGroup.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 20, router.NetworkInterfaces[0].NetworkID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 20, router.NetworkInterfaces[0].NetworkID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 30, vpngw.SecurityGroup.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 30, vpngw.SecurityGroup.GetMetadata().Range().GetEndLine()) - -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/elastic_load_balancer.go b/pkg/iac/adapters/terraform/nifcloud/network/elastic_load_balancer.go deleted file mode 100644 index 89478aadea6e..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/elastic_load_balancer.go +++ /dev/null @@ -1,50 +0,0 @@ -package network - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptElasticLoadBalancers(modules terraform.Modules) []network.ElasticLoadBalancer { - var elasticLoadBalancers []network.ElasticLoadBalancer - - for _, resource := range modules.GetResourcesByType("nifcloud_elb") { - elasticLoadBalancers = append(elasticLoadBalancers, adaptElasticLoadBalancer(resource, modules)) - } - return elasticLoadBalancers -} - -func adaptElasticLoadBalancer(resource *terraform.Block, modules terraform.Modules) network.ElasticLoadBalancer { - var listeners []network.ElasticLoadBalancerListener - var networkInterfaces []network.NetworkInterface - - networkInterfaceBlocks := resource.GetBlocks("network_interface") - for _, networkInterfaceBlock := range networkInterfaceBlocks { - networkInterfaces = append( - networkInterfaces, - network.NetworkInterface{ - Metadata: networkInterfaceBlock.GetMetadata(), - NetworkID: networkInterfaceBlock.GetAttribute("network_id").AsStringValueOrDefault("", resource), - IsVipNetwork: networkInterfaceBlock.GetAttribute("is_vip_network").AsBoolValueOrDefault(true, resource), - }, - ) - } - - listeners = append(listeners, adaptElasticLoadBalancerListener(resource)) - for _, listenerBlock := range modules.GetReferencingResources(resource, "nifcloud_elb_listener", "elb_id") { - listeners = append(listeners, adaptElasticLoadBalancerListener(listenerBlock)) - } - - return network.ElasticLoadBalancer{ - Metadata: resource.GetMetadata(), - NetworkInterfaces: networkInterfaces, - Listeners: listeners, - } -} - -func adaptElasticLoadBalancerListener(resource *terraform.Block) network.ElasticLoadBalancerListener { - return network.ElasticLoadBalancerListener{ - Metadata: resource.GetMetadata(), - Protocol: resource.GetAttribute("protocol").AsStringValueOrDefault("", resource), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/elastic_load_balancer_test.go b/pkg/iac/adapters/terraform/nifcloud/network/elastic_load_balancer_test.go deleted file mode 100644 index 252396e387e0..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/elastic_load_balancer_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package network - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptElasticLoadBalancers(t *testing.T) { - tests := []struct { - name string - terraform string - expected []network.ElasticLoadBalancer - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_elb" "example" { - protocol = "HTTP" - - network_interface { - network_id = "net-COMMON_PRIVATE" - is_vip_network = false - } - } - - resource "nifcloud_elb_listener" "example" { - elb_id = nifcloud_elb.example.id - protocol = "HTTPS" - } -`, - expected: []network.ElasticLoadBalancer{{ - Metadata: iacTypes.NewTestMetadata(), - NetworkInterfaces: []network.NetworkInterface{ - { - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("net-COMMON_PRIVATE", iacTypes.NewTestMetadata()), - IsVipNetwork: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }, - }, - Listeners: []network.ElasticLoadBalancerListener{ - { - Metadata: iacTypes.NewTestMetadata(), - Protocol: iacTypes.String("HTTP", iacTypes.NewTestMetadata()), - }, - { - Metadata: iacTypes.NewTestMetadata(), - Protocol: iacTypes.String("HTTPS", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_elb" "example" { - network_interface { - } - } -`, - - expected: []network.ElasticLoadBalancer{{ - Metadata: iacTypes.NewTestMetadata(), - NetworkInterfaces: []network.NetworkInterface{ - { - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("", iacTypes.NewTestMetadata()), - IsVipNetwork: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }, - }, - Listeners: []network.ElasticLoadBalancerListener{{ - Metadata: iacTypes.NewTestMetadata(), - }}, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptElasticLoadBalancers(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/load_balancer.go b/pkg/iac/adapters/terraform/nifcloud/network/load_balancer.go deleted file mode 100644 index d137f799756d..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/load_balancer.go +++ /dev/null @@ -1,67 +0,0 @@ -package network - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptLoadBalancers(modules terraform.Modules) []network.LoadBalancer { - var loadBalancers []network.LoadBalancer - - for _, resource := range modules.GetResourcesByType("nifcloud_load_balancer") { - loadBalancers = append(loadBalancers, adaptLoadBalancer(resource, modules)) - } - - return loadBalancers -} - -func adaptLoadBalancer(resource *terraform.Block, modules terraform.Modules) network.LoadBalancer { - var listeners []network.LoadBalancerListener - - listeners = append(listeners, adaptListener(resource)) - for _, listenerBlock := range modules.GetReferencingResources(resource, "nifcloud_load_balancer_listener", "load_balancer_name") { - listeners = append(listeners, adaptListener(listenerBlock)) - } - - return network.LoadBalancer{ - Metadata: resource.GetMetadata(), - Listeners: listeners, - } -} - -func adaptListener(resource *terraform.Block) network.LoadBalancerListener { - protocolVal := iacTypes.String("", resource.GetMetadata()) - policyVal := iacTypes.String("", resource.GetMetadata()) - - portAttr := resource.GetAttribute("load_balancer_port") - if portAttr.IsNotNil() && portAttr.IsNumber() { - port := portAttr.AsNumber() - switch port { - case 21: - protocolVal = iacTypes.String("FTP", portAttr.GetMetadata()) - case 80: - protocolVal = iacTypes.String("HTTP", portAttr.GetMetadata()) - case 443: - protocolVal = iacTypes.String("HTTPS", portAttr.GetMetadata()) - default: - protocolVal = iacTypes.String("custom", portAttr.GetMetadata()) - } - } - - policyIDAttr := resource.GetAttribute("ssl_policy_id") - if policyIDAttr.IsNotNil() && policyIDAttr.IsString() { - policyVal = policyIDAttr.AsStringValueOrDefault("", resource) - } - - policyNameAttr := resource.GetAttribute("ssl_policy_name") - if policyNameAttr.IsNotNil() && policyNameAttr.IsString() { - policyVal = policyNameAttr.AsStringValueOrDefault("", resource) - } - - return network.LoadBalancerListener{ - Metadata: resource.GetMetadata(), - Protocol: protocolVal, - TLSPolicy: policyVal, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/load_balancer_test.go b/pkg/iac/adapters/terraform/nifcloud/network/load_balancer_test.go deleted file mode 100644 index b94b0e43353d..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/load_balancer_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package network - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptLoadBalancers(t *testing.T) { - tests := []struct { - name string - terraform string - expected []network.LoadBalancer - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_load_balancer" "example" { - load_balancer_name = "example" - load_balancer_port = 80 - ssl_policy_id = "example-ssl-policy-id" - } - - resource "nifcloud_load_balancer_listener" "example" { - load_balancer_name = nifcloud_load_balancer.example.load_balancer_name - load_balancer_port = 443 - ssl_policy_name = "example-ssl-policy-name" - } - -`, - expected: []network.LoadBalancer{{ - Metadata: iacTypes.NewTestMetadata(), - Listeners: []network.LoadBalancerListener{ - { - Metadata: iacTypes.NewTestMetadata(), - TLSPolicy: iacTypes.String("example-ssl-policy-id", iacTypes.NewTestMetadata()), - Protocol: iacTypes.String("HTTP", iacTypes.NewTestMetadata()), - }, - { - Metadata: iacTypes.NewTestMetadata(), - TLSPolicy: iacTypes.String("example-ssl-policy-name", iacTypes.NewTestMetadata()), - Protocol: iacTypes.String("HTTPS", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_load_balancer" "example" { - } -`, - - expected: []network.LoadBalancer{{ - Metadata: iacTypes.NewTestMetadata(), - Listeners: []network.LoadBalancerListener{{ - Metadata: iacTypes.NewTestMetadata(), - }}, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptLoadBalancers(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/router.go b/pkg/iac/adapters/terraform/nifcloud/network/router.go deleted file mode 100644 index d75595279d7d..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/router.go +++ /dev/null @@ -1,37 +0,0 @@ -package network - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptRouters(modules terraform.Modules) []network.Router { - var routers []network.Router - - for _, resource := range modules.GetResourcesByType("nifcloud_router") { - routers = append(routers, adaptRouter(resource)) - } - return routers -} - -func adaptRouter(resource *terraform.Block) network.Router { - var networkInterfaces []network.NetworkInterface - networkInterfaceBlocks := resource.GetBlocks("network_interface") - for _, networkInterfaceBlock := range networkInterfaceBlocks { - networkInterfaces = append( - networkInterfaces, - network.NetworkInterface{ - Metadata: networkInterfaceBlock.GetMetadata(), - NetworkID: networkInterfaceBlock.GetAttribute("network_id").AsStringValueOrDefault("", resource), - IsVipNetwork: types.Bool(false, networkInterfaceBlock.GetMetadata()), - }, - ) - } - - return network.Router{ - Metadata: resource.GetMetadata(), - SecurityGroup: resource.GetAttribute("security_group").AsStringValueOrDefault("", resource), - NetworkInterfaces: networkInterfaces, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/router_test.go b/pkg/iac/adapters/terraform/nifcloud/network/router_test.go deleted file mode 100644 index e60c554917f6..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/router_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package network - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptRouters(t *testing.T) { - tests := []struct { - name string - terraform string - expected []network.Router - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_router" "example" { - security_group = "example-security-group" - network_interface { - network_id = "net-COMMON_PRIVATE" - } - } -`, - expected: []network.Router{{ - Metadata: iacTypes.NewTestMetadata(), - SecurityGroup: iacTypes.String("example-security-group", iacTypes.NewTestMetadata()), - NetworkInterfaces: []network.NetworkInterface{ - { - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("net-COMMON_PRIVATE", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_router" "example" { - network_interface { - } - } -`, - - expected: []network.Router{{ - Metadata: iacTypes.NewTestMetadata(), - SecurityGroup: iacTypes.String("", iacTypes.NewTestMetadata()), - NetworkInterfaces: []network.NetworkInterface{ - { - Metadata: iacTypes.NewTestMetadata(), - NetworkID: iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptRouters(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/vpn_gateway.go b/pkg/iac/adapters/terraform/nifcloud/network/vpn_gateway.go deleted file mode 100644 index dcb6812a1ef7..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/vpn_gateway.go +++ /dev/null @@ -1,22 +0,0 @@ -package network - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptVpnGateways(modules terraform.Modules) []network.VpnGateway { - var vpnGateways []network.VpnGateway - - for _, resource := range modules.GetResourcesByType("nifcloud_vpn_gateway") { - vpnGateways = append(vpnGateways, adaptVpnGateway(resource)) - } - return vpnGateways -} - -func adaptVpnGateway(resource *terraform.Block) network.VpnGateway { - return network.VpnGateway{ - Metadata: resource.GetMetadata(), - SecurityGroup: resource.GetAttribute("security_group").AsStringValueOrDefault("", resource), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/network/vpn_gateway_test.go b/pkg/iac/adapters/terraform/nifcloud/network/vpn_gateway_test.go deleted file mode 100644 index b9499c078518..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/network/vpn_gateway_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package network - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptVpnGateways(t *testing.T) { - tests := []struct { - name string - terraform string - expected []network.VpnGateway - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_vpn_gateway" "example" { - security_group = "example-security-group" - } -`, - expected: []network.VpnGateway{{ - Metadata: iacTypes.NewTestMetadata(), - SecurityGroup: iacTypes.String("example-security-group", iacTypes.NewTestMetadata()), - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_vpn_gateway" "example" { - } -`, - - expected: []network.VpnGateway{{ - Metadata: iacTypes.NewTestMetadata(), - SecurityGroup: iacTypes.String("", iacTypes.NewTestMetadata()), - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptVpnGateways(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/nifcloud.go b/pkg/iac/adapters/terraform/nifcloud/nifcloud.go deleted file mode 100644 index 5f17fe6b235c..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/nifcloud.go +++ /dev/null @@ -1,23 +0,0 @@ -package nifcloud - -import ( - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud/computing" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud/dns" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud/nas" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud/rdb" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/nifcloud/sslcertificate" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) nifcloud.Nifcloud { - return nifcloud.Nifcloud{ - Computing: computing.Adapt(modules), - DNS: dns.Adapt(modules), - NAS: nas.Adapt(modules), - Network: network.Adapt(modules), - RDB: rdb.Adapt(modules), - SSLCertificate: sslcertificate.Adapt(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/rdb/adapt.go b/pkg/iac/adapters/terraform/nifcloud/rdb/adapt.go deleted file mode 100644 index fd35b2236187..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/rdb/adapt.go +++ /dev/null @@ -1,13 +0,0 @@ -package rdb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/rdb" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) rdb.RDB { - return rdb.RDB{ - DBSecurityGroups: adaptDBSecurityGroups(modules), - DBInstances: adaptDBInstances(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/rdb/adapt_test.go b/pkg/iac/adapters/terraform/nifcloud/rdb/adapt_test.go deleted file mode 100644 index 4aeb48ce5b53..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/rdb/adapt_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package rdb - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func TestLines(t *testing.T) { - src := ` -resource "nifcloud_db_instance" "example" { - publicly_accessible = false - engine = "MySQL" - engine_version = "5.7.15" - backup_retention_period = 2 - network_id = "example-network" -} - -resource "nifcloud_db_security_group" "example" { - description = "memo" - - rule { - cidr_ip = "0.0.0.0/0" - } -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.DBInstances, 1) - require.Len(t, adapted.DBSecurityGroups, 1) - - dbInstance := adapted.DBInstances[0] - dbSecurityGroup := adapted.DBSecurityGroups[0] - - assert.Equal(t, 3, dbInstance.PublicAccess.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, dbInstance.PublicAccess.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 4, dbInstance.Engine.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 4, dbInstance.Engine.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 5, dbInstance.EngineVersion.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 5, dbInstance.EngineVersion.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 6, dbInstance.BackupRetentionPeriodDays.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 6, dbInstance.BackupRetentionPeriodDays.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 7, dbInstance.NetworkID.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 7, dbInstance.NetworkID.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 11, dbSecurityGroup.Description.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 11, dbSecurityGroup.Description.GetMetadata().Range().GetEndLine()) - - assert.Equal(t, 14, dbSecurityGroup.CIDRs[0].GetMetadata().Range().GetStartLine()) - assert.Equal(t, 14, dbSecurityGroup.CIDRs[0].GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/nifcloud/rdb/db_instance.go b/pkg/iac/adapters/terraform/nifcloud/rdb/db_instance.go deleted file mode 100644 index 69757e41e9a2..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/rdb/db_instance.go +++ /dev/null @@ -1,26 +0,0 @@ -package rdb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/rdb" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func adaptDBInstances(modules terraform.Modules) []rdb.DBInstance { - var dbInstances []rdb.DBInstance - - for _, resource := range modules.GetResourcesByType("nifcloud_db_instance") { - dbInstances = append(dbInstances, adaptDBInstance(resource)) - } - return dbInstances -} - -func adaptDBInstance(resource *terraform.Block) rdb.DBInstance { - return rdb.DBInstance{ - Metadata: resource.GetMetadata(), - BackupRetentionPeriodDays: resource.GetAttribute("backup_retention_period").AsIntValueOrDefault(0, resource), - Engine: resource.GetAttribute("engine").AsStringValueOrDefault("", resource), - EngineVersion: resource.GetAttribute("engine_version").AsStringValueOrDefault("", resource), - NetworkID: resource.GetAttribute("network_id").AsStringValueOrDefault("net-COMMON_PRIVATE", resource), - PublicAccess: resource.GetAttribute("publicly_accessible").AsBoolValueOrDefault(true, resource), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/rdb/db_instance_test.go b/pkg/iac/adapters/terraform/nifcloud/rdb/db_instance_test.go deleted file mode 100644 index b6ffd7985a5b..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/rdb/db_instance_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package rdb - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/rdb" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptDBInstances(t *testing.T) { - tests := []struct { - name string - terraform string - expected []rdb.DBInstance - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_db_instance" "example" { - backup_retention_period = 2 - engine = "MySQL" - engine_version = "5.7.15" - publicly_accessible = false - network_id = "example-network" - } -`, - expected: []rdb.DBInstance{{ - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(2, iacTypes.NewTestMetadata()), - Engine: iacTypes.String("MySQL", iacTypes.NewTestMetadata()), - EngineVersion: iacTypes.String("5.7.15", iacTypes.NewTestMetadata()), - NetworkID: iacTypes.String("example-network", iacTypes.NewTestMetadata()), - PublicAccess: iacTypes.Bool(false, iacTypes.NewTestMetadata()), - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_db_instance" "example" { - } -`, - - expected: []rdb.DBInstance{{ - Metadata: iacTypes.NewTestMetadata(), - BackupRetentionPeriodDays: iacTypes.Int(0, iacTypes.NewTestMetadata()), - Engine: iacTypes.String("", iacTypes.NewTestMetadata()), - EngineVersion: iacTypes.String("", iacTypes.NewTestMetadata()), - NetworkID: iacTypes.String("net-COMMON_PRIVATE", iacTypes.NewTestMetadata()), - PublicAccess: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptDBInstances(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/rdb/db_security_group.go b/pkg/iac/adapters/terraform/nifcloud/rdb/db_security_group.go deleted file mode 100644 index e78d95ea0e3f..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/rdb/db_security_group.go +++ /dev/null @@ -1,30 +0,0 @@ -package rdb - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/rdb" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptDBSecurityGroups(modules terraform.Modules) []rdb.DBSecurityGroup { - var dbSecurityGroups []rdb.DBSecurityGroup - - for _, resource := range modules.GetResourcesByType("nifcloud_db_security_group") { - dbSecurityGroups = append(dbSecurityGroups, adaptDBSecurityGroup(resource)) - } - return dbSecurityGroups -} - -func adaptDBSecurityGroup(resource *terraform.Block) rdb.DBSecurityGroup { - var cidrs []iacTypes.StringValue - - for _, rule := range resource.GetBlocks("rule") { - cidrs = append(cidrs, rule.GetAttribute("cidr_ip").AsStringValueOrDefault("", resource)) - } - - return rdb.DBSecurityGroup{ - Metadata: resource.GetMetadata(), - Description: resource.GetAttribute("description").AsStringValueOrDefault("", resource), - CIDRs: cidrs, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/rdb/db_security_group_test.go b/pkg/iac/adapters/terraform/nifcloud/rdb/db_security_group_test.go deleted file mode 100644 index 3833e8241101..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/rdb/db_security_group_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package rdb - -import ( - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/rdb" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_adaptDBSecurityGroups(t *testing.T) { - tests := []struct { - name string - terraform string - expected []rdb.DBSecurityGroup - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_db_security_group" "example" { - description = "memo" - - rule { - cidr_ip = "0.0.0.0/0" - } - } -`, - expected: []rdb.DBSecurityGroup{{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("memo", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("0.0.0.0/0", iacTypes.NewTestMetadata()), - }, - }}, - }, - { - name: "defaults", - terraform: ` - resource "nifcloud_db_security_group" "example" { - rule { - } - } -`, - - expected: []rdb.DBSecurityGroup{{ - Metadata: iacTypes.NewTestMetadata(), - Description: iacTypes.String("", iacTypes.NewTestMetadata()), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("", iacTypes.NewTestMetadata()), - }, - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := tftestutil.CreateModulesFromSource(t, test.terraform, ".tf") - adapted := adaptDBSecurityGroups(modules) - testutil.AssertDefsecEqual(t, test.expected, adapted) - }) - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/adapt.go b/pkg/iac/adapters/terraform/nifcloud/sslcertificate/adapt.go deleted file mode 100644 index 1f1391397a12..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/adapt.go +++ /dev/null @@ -1,12 +0,0 @@ -package sslcertificate - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/sslcertificate" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Adapt(modules terraform.Modules) sslcertificate.SSLCertificate { - return sslcertificate.SSLCertificate{ - ServerCertificates: adaptServerCertificates(modules), - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/adapt_test.go b/pkg/iac/adapters/terraform/nifcloud/sslcertificate/adapt_test.go deleted file mode 100644 index 0f045f1c2c52..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/adapt_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package sslcertificate - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" -) - -func TestLines(t *testing.T) { - src := ` -resource "nifcloud_ssl_certificate" "example" { - certificate = "generated-certificate" -} -` - - modules := tftestutil.CreateModulesFromSource(t, src, ".tf") - adapted := Adapt(modules) - - require.Len(t, adapted.ServerCertificates, 1) - - serverCertificate := adapted.ServerCertificates[0] - - assert.Equal(t, 3, serverCertificate.Expiration.GetMetadata().Range().GetStartLine()) - assert.Equal(t, 3, serverCertificate.Expiration.GetMetadata().Range().GetEndLine()) -} diff --git a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/server_certificate.go b/pkg/iac/adapters/terraform/nifcloud/sslcertificate/server_certificate.go deleted file mode 100644 index 7c08d0d78ca3..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/server_certificate.go +++ /dev/null @@ -1,41 +0,0 @@ -package sslcertificate - -import ( - "crypto/x509" - "encoding/pem" - - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/sslcertificate" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func adaptServerCertificates(modules terraform.Modules) []sslcertificate.ServerCertificate { - var serverCertificates []sslcertificate.ServerCertificate - - for _, resource := range modules.GetResourcesByType("nifcloud_ssl_certificate") { - serverCertificates = append(serverCertificates, adaptServerCertificate(resource)) - } - return serverCertificates -} - -func adaptServerCertificate(resource *terraform.Block) sslcertificate.ServerCertificate { - certificateAttr := resource.GetAttribute("certificate") - expiryDateVal := iacTypes.TimeUnresolvable(resource.GetMetadata()) - - if certificateAttr.IsNotNil() { - expiryDateVal = iacTypes.TimeUnresolvable(certificateAttr.GetMetadata()) - if certificateAttr.IsString() { - certificateString := certificateAttr.Value().AsString() - if block, _ := pem.Decode([]byte(certificateString)); block != nil { - if cert, err := x509.ParseCertificate(block.Bytes); err == nil { - expiryDateVal = iacTypes.Time(cert.NotAfter, certificateAttr.GetMetadata()) - } - } - } - } - - return sslcertificate.ServerCertificate{ - Metadata: resource.GetMetadata(), - Expiration: expiryDateVal, - } -} diff --git a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/server_certificate_test.go b/pkg/iac/adapters/terraform/nifcloud/sslcertificate/server_certificate_test.go deleted file mode 100644 index 1b9006034ce2..000000000000 --- a/pkg/iac/adapters/terraform/nifcloud/sslcertificate/server_certificate_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package sslcertificate - -import ( - "testing" - "time" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform/tftestutil" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/sslcertificate" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -const certificate = ` ------BEGIN CERTIFICATE----- -MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ -hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa -rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv -zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF -MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW -r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V ------END CERTIFICATE----- -` - -func Test_adaptServerCertificates(t *testing.T) { - tests := []struct { - name string - terraform string - expected []sslcertificate.ServerCertificate - }{ - { - name: "configured", - terraform: ` - resource "nifcloud_ssl_certificate" "example" { - certificate = < 0 || len(sniff.Resources) > 0 - } - - matchers[FileTypeDockerfile] = func(name string, _ io.ReadSeeker) bool { - requiredFiles := []string{"Dockerfile", "Containerfile"} - for _, requiredFile := range requiredFiles { - base := filepath.Base(name) - ext := filepath.Ext(base) - if strings.TrimSuffix(base, ext) == requiredFile { - return true - } - if strings.EqualFold(ext, "."+requiredFile) { - return true - } - } - return false - } - - matchers[FileTypeHelm] = func(name string, r io.ReadSeeker) bool { - helmFiles := []string{"Chart.yaml", ".helmignore", "values.schema.json", "NOTES.txt"} - for _, expected := range helmFiles { - if strings.HasSuffix(name, expected) { - return true - } - } - helmFileExtensions := []string{".yaml", ".tpl"} - ext := filepath.Ext(filepath.Base(name)) - for _, expected := range helmFileExtensions { - if strings.EqualFold(ext, expected) { - return true - } - } - return IsHelmChartArchive(name, r) - } - - matchers[FileTypeKubernetes] = func(name string, r io.ReadSeeker) bool { - - if !IsType(name, r, FileTypeYAML) && !IsType(name, r, FileTypeJSON) { - return false - } - if resetReader(r) == nil { - return false - } - - expectedProperties := []string{"apiVersion", "kind", "metadata"} - - if IsType(name, r, FileTypeJSON) { - if resetReader(r) == nil { - return false - } - - var result map[string]any - if err := json.NewDecoder(r).Decode(&result); err != nil { - return false - } - - for _, expected := range expectedProperties { - if _, ok := result[expected]; !ok { - return false - } - } - return true - } - - // at this point, we need to inspect bytes - var buf bytes.Buffer - if _, err := io.Copy(&buf, r); err != nil { - return false - } - data := buf.Bytes() - - marker := []byte("\n---\n") - altMarker := []byte("\r\n---\r\n") - if bytes.Contains(data, altMarker) { - marker = altMarker - } - - for _, partial := range bytes.Split(data, marker) { - var result map[string]any - if err := yaml.Unmarshal(partial, &result); err != nil { - continue - } - match := true - for _, expected := range expectedProperties { - if _, ok := result[expected]; !ok { - match = false - break - } - } - if match { - return true - } - } - - return false - } -} - -func IsTerraformFile(path string) bool { - if strings.HasSuffix(path, filepath.ToSlash(".terraform/modules/modules.json")) { - return true - } - - for _, ext := range []string{".tf", ".tf.json", ".tfvars"} { - if strings.HasSuffix(path, ext) { - return true - } - } - - return false -} - -func IsType(name string, r io.ReadSeeker, t FileType) bool { - r = ensureSeeker(r) - f, ok := matchers[t] - if !ok { - return false - } - return f(name, r) -} - -func GetTypes(name string, r io.ReadSeeker) []FileType { - var matched []FileType - r = ensureSeeker(r) - for check, f := range matchers { - if f(name, r) { - matched = append(matched, check) - } - resetReader(r) - } - return matched -} - -func ensureSeeker(r io.Reader) io.ReadSeeker { - if r == nil { - return nil - } - if seeker, ok := r.(io.ReadSeeker); ok { - return seeker - } - - var buf bytes.Buffer - if _, err := io.Copy(&buf, r); err == nil { - return bytes.NewReader(buf.Bytes()) - } - - return nil -} - -func resetReader(r io.Reader) io.ReadSeeker { - if r == nil { - return nil - } - if seeker, ok := r.(io.ReadSeeker); ok { - _, _ = seeker.Seek(0, 0) - return seeker - } - return ensureSeeker(r) -} - -func isJSON(name string) bool { - ext := filepath.Ext(name) - return strings.EqualFold(ext, ".json") -} -func isYAML(name string) bool { - ext := filepath.Ext(name) - return strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml") -} - -func IsFileMatchesSchemas(schemas map[string]*gojsonschema.Schema, typ FileType, name string, r io.ReadSeeker) bool { - defer resetReader(r) - - var l gojsonschema.JSONLoader - switch { - case typ == FileTypeJSON && isJSON(name): - b, err := io.ReadAll(r) - if err != nil { - return false - } - l = gojsonschema.NewBytesLoader(b) - case typ == FileTypeYAML && isYAML(name): - var content any - if err := yaml.NewDecoder(r).Decode(&content); err != nil { - return false - } - l = gojsonschema.NewGoLoader(content) - default: - return false - } - - for schemaPath, schema := range schemas { - if res, err := schema.Validate(l); err == nil && res.Valid() { - log.Debug("File matched schema", log.FilePath(name), log.String("schema_path", schemaPath)) - return true - } - } - return false -} diff --git a/pkg/iac/detection/detect_stub.go b/pkg/iac/detection/detect_stub.go new file mode 100644 index 000000000000..95dce689007e --- /dev/null +++ b/pkg/iac/detection/detect_stub.go @@ -0,0 +1,37 @@ +package detection + +import ( + "path/filepath" + "strings" +) + +type FileType string + +const ( + FileTypeCloudFormation FileType = "cloudformation" + FileTypeTerraform FileType = "terraform" + FileTypeTerraformPlanJSON FileType = "terraformplan-json" + FileTypeTerraformPlanSnapshot FileType = "terraformplan-snapshot" + FileTypeDockerfile FileType = "dockerfile" + FileTypeKubernetes FileType = "kubernetes" + FileTypeRbac FileType = "rbac" + FileTypeYAML FileType = "yaml" + FileTypeTOML FileType = "toml" + FileTypeJSON FileType = "json" + FileTypeHelm FileType = "helm" + FileTypeAzureARM FileType = "azure-arm" +) + +func IsTerraformFile(path string) bool { + if strings.HasSuffix(path, filepath.ToSlash(".terraform/modules/modules.json")) { + return true + } + + for _, ext := range []string{".tf", ".tf.json", ".tfvars"} { + if strings.HasSuffix(path, ext) { + return true + } + } + + return false +} diff --git a/pkg/iac/detection/detect_test.go b/pkg/iac/detection/detect_test.go deleted file mode 100644 index f082220f2f00..000000000000 --- a/pkg/iac/detection/detect_test.go +++ /dev/null @@ -1,613 +0,0 @@ -package detection - -import ( - "bytes" - "fmt" - "io" - "os" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/xeipuuv/gojsonschema" -) - -func Test_Detection(t *testing.T) { - tests := []struct { - name string - path string - r io.ReadSeeker - expected []FileType - }{ - { - name: "text file, no reader", - path: "something.txt", - expected: nil, - }, - { - name: "text file, with reader", - path: "something.txt", - r: strings.NewReader("some file content"), - expected: nil, - }, - { - name: "terraform, no reader", - path: "main.tf", - expected: []FileType{ - FileTypeTerraform, - }, - }, - { - name: "terraform, with reader", - path: "main.tf", - r: strings.NewReader("some file content"), - expected: []FileType{ - FileTypeTerraform, - }, - }, - { - name: "terraform json, no reader", - path: "main.tf.json", - expected: []FileType{ - FileTypeTerraform, - FileTypeJSON, - }, - }, - { - name: "terraform json, with reader", - path: "main.tf.json", - r: strings.NewReader(` -{ - "variable": { - "example": { - "default": "hello" - } - } -} -`), - expected: []FileType{ - FileTypeTerraform, - FileTypeJSON, - }, - }, - { - name: "terraform vars, no reader", - path: "main.tfvars", - expected: []FileType{ - FileTypeTerraform, - }, - }, - { - name: "terraform vars, with reader", - path: "main.tfvars", - r: strings.NewReader("some_var = \"some value\""), - expected: []FileType{ - FileTypeTerraform, - }, - }, - { - name: "cloudformation, no reader", - path: "main.yaml", - expected: []FileType{ - FileTypeYAML, - FileTypeHelm, - }, - }, - { - name: "terraform plan, with reader", - path: "plan.json", - r: strings.NewReader(`{ - "format_version": "0.2", - "terraform_version": "1.0.3", - "variables": { - "bucket_name": { - "value": "tfsec-plan-testing" - } - }, - "planned_values": {}, - "resource_changes": [], - "prior_state": {}, - "configuration": {} - }`), - expected: []FileType{ - FileTypeTerraformPlanJSON, - FileTypeJSON, - }, - }, - { - name: "cloudformation, with reader", - path: "main.yaml", - r: strings.NewReader(`--- -AWSTemplateFormatVersion: 2010-09-09 - -Description: CodePipeline for continuous integration and continuous deployment - -Parameters: - RepositoryName: - Type: String - Description: Name of the CodeCommit repository - BuildDockerImage: - Type: String - Default: aws/codebuild/ubuntu-base:14.04 - Description: Docker image to use for the build phase - DeployDockerImage: - Type: String - Default: aws/codebuild/ubuntu-base:14.04 - Description: Docker image to use for the deployment phase - -Resources: - PipelineS3Bucket: - Type: AWS::S3::Bucket -`), - expected: []FileType{ - FileTypeCloudFormation, - FileTypeYAML, - FileTypeHelm, - }, - }, - { - name: "JSON with Resources, not cloudformation", - path: "whatever.json", - r: strings.NewReader(`{ - "Resources": ["something"] -}`), - expected: []FileType{ - FileTypeJSON, - }, - }, - { - name: "Dockerfile, no reader", - path: "Dockerfile", - r: nil, - expected: []FileType{ - FileTypeDockerfile, - }, - }, - { - name: "Containerfile, no reader", - path: "Containerfile", - r: nil, - expected: []FileType{ - FileTypeDockerfile, - }, - }, - { - name: "Dockerfile, reader", - path: "Dockerfile", - r: strings.NewReader("FROM ubuntu\n"), - expected: []FileType{ - FileTypeDockerfile, - }, - }, - { - name: "Dockerfile extension", - path: "lol.Dockerfile", - r: nil, - expected: []FileType{ - FileTypeDockerfile, - }, - }, - { - name: "kubernetes, no reader", - path: "k8s.yml", - r: nil, - expected: []FileType{ - FileTypeYAML, - }, - }, - { - name: "kubernetes, reader", - path: "k8s.yml", - r: strings.NewReader(`apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 3 - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx:1.14.2 - ports: - - containerPort: 80`), - expected: []FileType{ - FileTypeKubernetes, - FileTypeYAML, - }, - }, - { - name: "kubernetes, reader, JSON", - path: "k8s.json", - r: strings.NewReader(`{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "nginx-deployment", - "labels": { - "app": "nginx" - } - }, - "spec": { - "replicas": 3, - "selector": { - "matchLabels": { - "app": "nginx" - } - }, - "template": { - "metadata": { - "labels": { - "app": "nginx" - } - }, - "spec": { - "containers": [ - { - "name": "nginx", - "image": "nginx:1.14.2", - "ports": [ - { - "containerPort": 80 - } - ] - } - ] - } - } - } -}`), - expected: []FileType{ - FileTypeKubernetes, - FileTypeJSON, - }, - }, - { - name: "YAML, no reader", - path: "file.yaml", - r: nil, - expected: []FileType{ - FileTypeYAML, - FileTypeHelm, - }, - }, - { - name: "YML, no reader", - path: "file.yml", - r: nil, - expected: []FileType{ - FileTypeYAML, - }, - }, - { - name: "YML uppercase", - path: "file.YML", - r: nil, - expected: []FileType{ - FileTypeYAML, - }, - }, - { - name: "TOML, no reader", - path: "file.toml", - r: nil, - expected: []FileType{ - FileTypeTOML, - }, - }, - { - name: "JSON, no reader", - path: "file.json", - r: nil, - expected: []FileType{ - FileTypeJSON, - }, - }, - { - name: "kubernetes, configmap", - path: "k8s.yml", - r: strings.NewReader(`apiVersion: v1 -kind: ConfigMap -metadata: - name: test - namespace: default -data: - AWS_ACCESS_KEY_ID: "XXX" - AWS_SECRET_ACCESS_KEY: "XXX"`), - expected: []FileType{ - FileTypeKubernetes, - FileTypeYAML, - }, - }, - { - name: "kubernetes, clusterRole", - path: "k8s.yml", - r: strings.NewReader(`apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - annotations: - rbac.authorization.kubernetes.io/autoupdate: "true" - labels: - kubernetes.io/bootstrapping: rbac-defaults - rbac.authorization.k8s.io/aggregate-to-edit: "true" - name: view -rules: -- apiGroups: - - networking.k8s.io - resources: - - ingresses - - ingresses/status - - networkpolicies - verbs: - - get - - list - - watch`), - expected: []FileType{ - FileTypeKubernetes, - FileTypeYAML, - }, - }, - { - name: "Azure ARM template with resources", - path: "test.json", - r: strings.NewReader(` -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2021-09-01", - "name": "{provide-unique-name}", - "location": "eastus", - "sku": { - "name": "Standard_LRS" - }, - "kind": "StorageV2", - "properties": { - "supportsHttpsTrafficOnly": true - } - } - ] -} -`), - expected: []FileType{ - FileTypeJSON, - FileTypeAzureARM, - }, - }, - { - name: "Azure ARM template with parameters", - path: "test.json", - r: strings.NewReader(` -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "storageName": { - "type": "string", - "minLength": 3, - "maxLength": 24 - } - } -} -`), - expected: []FileType{ - FileTypeJSON, - FileTypeAzureARM, - }, - }, - { - name: "empty Azure ARM template", - path: "test.json", - r: strings.NewReader(` -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [] -} -`), - expected: []FileType{ - FileTypeJSON, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Run("GetTypes", func(t *testing.T) { - actualDetections := GetTypes(test.path, test.r) - assert.Equal(t, len(test.expected), len(actualDetections)) - for _, expected := range test.expected { - resetReader(test.r) - var found bool - for _, actual := range actualDetections { - if actual == expected { - found = true - break - } - } - assert.True(t, found, "%s should be detected", expected) - } - }) - for _, expected := range test.expected { - resetReader(test.r) - t.Run(fmt.Sprintf("IsType_%s", expected), func(t *testing.T) { - assert.True(t, IsType(test.path, test.r, expected)) - }) - } - t.Run("IsType_invalid", func(t *testing.T) { - resetReader(test.r) - assert.False(t, IsType(test.path, test.r, "invalid")) - }) - }) - } -} - -func BenchmarkIsType_SmallFile(b *testing.B) { - data, err := os.ReadFile(fmt.Sprintf("./testdata/%s", "small.file")) - require.NoError(b, err) - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = IsType(fmt.Sprintf("./testdata/%s", "small.file"), bytes.NewReader(data), FileTypeAzureARM) - } -} - -func BenchmarkIsType_BigFile(b *testing.B) { - data, err := os.ReadFile(fmt.Sprintf("./testdata/%s", "big.file")) - require.NoError(b, err) - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = IsType(fmt.Sprintf("./testdata/%s", "big.file"), bytes.NewReader(data), FileTypeAzureARM) - } -} - -func Test_IsFileMatchesSchemas(t *testing.T) { - - schema := `{ - "$id": "https://example.com/test.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "service": { "type": "string" } - }, - "required": ["service"] -}` - - schema2 := `{ - "$id": "https://example.com/test.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "provider": { "type": "string" } - }, - "required": ["provider"] - }` - - type args struct { - schemas []string - fileType FileType - fileName string - fileContent string - } - tests := []struct { - name string - args args - matches bool - }{ - { - name: "json file matches", - args: args{ - schemas: []string{schema}, - fileType: FileTypeJSON, - fileName: "test.json", - fileContent: `{ - "service": "test" -}`, - }, - matches: true, - }, - { - name: "json file dost not matches", - args: args{ - schemas: []string{schema}, - fileType: FileTypeJSON, - fileName: "test.json", - fileContent: `{ - "somefield": "test", -}`, - }, - matches: false, - }, - { - name: "json file matches, but file type is yaml", - args: args{ - schemas: []string{schema}, - fileType: FileTypeYAML, - fileName: "test.json", - fileContent: `{ - "service": "test" -}`, - }, - matches: false, - }, - { - name: "broken json file", - args: args{ - schemas: []string{schema}, - fileType: FileTypeJSON, - fileName: "test.json", - fileContent: `{ - "service": "test",, -}`, - }, - matches: false, - }, - { - name: "yaml file matches", - args: args{ - schemas: []string{schema}, - fileType: FileTypeYAML, - fileName: "test.yml", - fileContent: `service: test`, - }, - matches: true, - }, - { - name: "yaml file does not matches", - args: args{ - schemas: []string{schema}, - fileType: FileTypeYAML, - fileName: "test.yaml", - fileContent: `somefield: test`, - }, - matches: false, - }, - { - name: "broken yaml file", - args: args{ - schemas: []string{schema}, - fileType: FileTypeYAML, - fileName: "test.yaml", - fileContent: `text foobar -number: 2`, - }, - matches: false, - }, - { - name: "multiple schemas", - args: args{ - schemas: []string{schema, schema2}, - fileType: FileTypeYAML, - fileName: "test.yaml", - fileContent: `provider: test`, - }, - matches: true, - }, - } - for _, tt := range tests { - schemas := make(map[string]*gojsonschema.Schema) - for i, content := range tt.args.schemas { - l := gojsonschema.NewStringLoader(content) - s, err := gojsonschema.NewSchema(l) - require.NoError(t, err) - schemas[fmt.Sprintf("schema-%d.json", i)] = s - } - rs := strings.NewReader(tt.args.fileContent) - got := IsFileMatchesSchemas(schemas, tt.args.fileType, tt.args.fileName, rs) - assert.Equal(t, tt.matches, got) - } -} diff --git a/pkg/iac/detection/peek.go b/pkg/iac/detection/peek.go deleted file mode 100644 index 0e76115d9bd8..000000000000 --- a/pkg/iac/detection/peek.go +++ /dev/null @@ -1,53 +0,0 @@ -package detection - -import ( - "archive/tar" - "compress/gzip" - "errors" - "io" - "strings" -) - -func IsHelmChartArchive(path string, file io.Reader) bool { - - if !IsArchive(path) { - return false - } - - var err error - var fr = file - - if IsZip(path) { - if fr, err = gzip.NewReader(file); err != nil { - return false - } - } - tr := tar.NewReader(fr) - - if tr == nil { - return false - } - - for { - header, err := tr.Next() - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return false - } - - if header.Typeflag == tar.TypeReg && strings.HasSuffix(header.Name, "Chart.yaml") { - return true - } - } - return false -} - -func IsArchive(path string) bool { - return strings.HasSuffix(path, ".tar") || IsZip(path) -} - -func IsZip(path string) bool { - return strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") -} diff --git a/pkg/iac/detection/testdata/big.file b/pkg/iac/detection/testdata/big.file deleted file mode 100644 index e7f3c2d40ecc..000000000000 Binary files a/pkg/iac/detection/testdata/big.file and /dev/null differ diff --git a/pkg/iac/detection/testdata/small.file b/pkg/iac/detection/testdata/small.file deleted file mode 100644 index d8ae428a4800..000000000000 --- a/pkg/iac/detection/testdata/small.file +++ /dev/null @@ -1,3 +0,0 @@ -{ - "content": "foo bar baz" -} \ No newline at end of file diff --git a/pkg/iac/framework/frameworks.go b/pkg/iac/framework/frameworks.go deleted file mode 100644 index 82f43947d568..000000000000 --- a/pkg/iac/framework/frameworks.go +++ /dev/null @@ -1,11 +0,0 @@ -package framework - -type Framework string - -const ( - Default Framework = "default" - Experimental Framework = "experimental" - CIS_AWS_1_2 Framework = "cis-aws-1.2" - CIS_AWS_1_4 Framework = "cis-aws-1.4" - ALL Framework = "all" -) diff --git a/pkg/iac/ignore/parse.go b/pkg/iac/ignore/parse.go deleted file mode 100644 index 8a7a940c6b5c..000000000000 --- a/pkg/iac/ignore/parse.go +++ /dev/null @@ -1,181 +0,0 @@ -package ignore - -import ( - "errors" - "strings" - "time" - - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -// RuleSectionParser defines the interface for parsing ignore rules. -type RuleSectionParser interface { - Key() string - Parse(string) bool - Param() any -} - -// Parse parses the configuration file and returns the Rules -func Parse(src, path, sourcePrefix string, parsers ...RuleSectionParser) Rules { - var rules Rules - for i, line := range strings.Split(src, "\n") { - line = strings.TrimSpace(line) - rng := types.NewRange(path, i+1, i+1, sourcePrefix, nil) - lineIgnores := parseLine(line, rng, parsers) - for _, lineIgnore := range lineIgnores { - rules = append(rules, lineIgnore) - } - } - - rules.shift() - - return rules -} - -func parseLine(line string, rng types.Range, parsers []RuleSectionParser) []Rule { - var rules []Rule - - parts := strings.Split(strings.TrimSpace(line), " ") - parts = lo.FilterMap(parts, func(part string, _ int) (string, bool) { - part = strings.TrimSpace(part) - part = strings.TrimLeftFunc(part, func(r rune) bool { - return r == '#' || r == '/' || r == '*' - }) - - return part, part != "" - }) - - for i, part := range parts { - part, exists := hasIgnoreRulePrefix(part) - if !exists { - continue - } - - sections, err := parseRuleSections(part, rng, parsers) - if err != nil { - log.Debug("Failed to parse rule", log.String("range", rng.String()), log.Err(err)) - continue - } - - rule := Rule{ - rng: rng, - isStartLine: i == 0 || (len(rules) > 0 && rules[0].isStartLine), - sections: sections, - } - - rules = append(rules, rule) - } - - return rules -} - -func hasIgnoreRulePrefix(s string) (string, bool) { - for _, prefix := range []string{ - "tfsec:", - "trivy:", - } { - if after, found := strings.CutPrefix(s, prefix); found { - return after, true - } - } - - return "", false -} - -func parseRuleSections(input string, rng types.Range, parsers []RuleSectionParser) (map[string]any, error) { - sections := make(map[string]any) - - parsers = append(parsers, &expiryDateParser{ - rng: rng, - }) - - segments := strings.Split(input, ":") - - for i := 0; i < len(segments)-1; i += 2 { - key := segments[i] - val := segments[i+1] - if key == "ignore" { - // special case, because id and parameters are in the same section - idParser := &checkIDParser{ - StringMatchParser{SectionKey: "id"}, - } - if idParser.Parse(val) { - sections[idParser.Key()] = idParser.Param() - } - } - - for _, parser := range parsers { - if parser.Key() != key { - continue - } - - if parser.Parse(val) { - sections[parser.Key()] = parser.Param() - } - } - } - - if _, exists := sections["id"]; !exists { - return nil, errors.New("rule section with the `ignore` key is required") - } - - return sections, nil -} - -type StringMatchParser struct { - SectionKey string - param string -} - -func (s *StringMatchParser) Key() string { - return s.SectionKey -} - -func (s *StringMatchParser) Parse(str string) bool { - s.param = str - return str != "" -} - -func (s *StringMatchParser) Param() any { - return s.param -} - -type checkIDParser struct { - StringMatchParser -} - -func (s *checkIDParser) Parse(str string) bool { - if idx := strings.Index(str, "["); idx != -1 { - str = str[:idx] - } - return s.StringMatchParser.Parse(str) -} - -type expiryDateParser struct { - rng types.Range - expiry time.Time -} - -func (s *expiryDateParser) Key() string { - return "exp" -} - -func (s *expiryDateParser) Parse(str string) bool { - parsed, err := time.Parse("2006-01-02", str) - if err != nil { - log.Debug("Incorrect time to ignore is specified", log.String("time", str)) - parsed = time.Time{} - } else if time.Now().After(parsed) { - log.Debug("Ignore rule time has expired for location", log.String("range", s.rng.String())) - } - - s.expiry = parsed - return true -} - -func (s *expiryDateParser) Param() any { - return s.expiry -} diff --git a/pkg/iac/ignore/rule.go b/pkg/iac/ignore/rule.go deleted file mode 100644 index 86e24e8dd120..000000000000 --- a/pkg/iac/ignore/rule.go +++ /dev/null @@ -1,137 +0,0 @@ -package ignore - -import ( - "regexp" - "slices" - "strings" - "time" - - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -// Ignorer represents a function that checks if the rule should be ignored. -type Ignorer func(resultMeta types.Metadata, ignoredParam any) bool - -type Rules []Rule - -// Ignore checks if the rule should be ignored based on provided metadata, IDs, and ignorer functions. -func (r Rules) Ignore(m types.Metadata, ids []string, ignorers map[string]Ignorer) bool { - return slices.ContainsFunc(r, func(r Rule) bool { - return r.ignore(m, ids, ignorers) - }) -} - -func (r Rules) shift() { - var ( - currentRange *types.Range - offset int - ) - - for i := len(r) - 1; i > 0; i-- { - currentRule, prevRule := r[i], r[i-1] - - if !prevRule.isStartLine { - continue - } - - if currentRange == nil { - currentRange = ¤tRule.rng - } - if prevRule.rng.GetStartLine()+1+offset == currentRule.rng.GetStartLine() { - r[i-1].rng = *currentRange - offset++ - } else { - currentRange = nil - offset = 0 - } - } -} - -// Rule represents a rule for ignoring vulnerabilities. -type Rule struct { - rng types.Range - isStartLine bool - sections map[string]any -} - -func (r Rule) ignore(m types.Metadata, ids []string, ignorers map[string]Ignorer) bool { - matchMeta, ok := r.matchRange(&m) - if !ok { - return false - } - - ignorers = lo.Assign(defaultIgnorers(ids), ignorers) - - for ignoreID, ignore := range ignorers { - if param, exists := r.sections[ignoreID]; exists { - if !ignore(*matchMeta, param) { - return false - } - } - } - - return true -} - -func (r Rule) matchRange(m *types.Metadata) (*types.Metadata, bool) { - metaHierarchy := m - for metaHierarchy != nil { - if r.rng.GetFilename() != metaHierarchy.Range().GetFilename() { - metaHierarchy = metaHierarchy.Parent() - continue - } - if metaHierarchy.Range().GetStartLine() == r.rng.GetStartLine()+1 || - metaHierarchy.Range().GetStartLine() == r.rng.GetStartLine() { - return metaHierarchy, true - } - metaHierarchy = metaHierarchy.Parent() - } - - return nil, false -} - -func defaultIgnorers(ids []string) map[string]Ignorer { - return map[string]Ignorer{ - "id": func(_ types.Metadata, param any) bool { - id, ok := param.(string) - if !ok { - return false - } - if id == "*" || len(ids) == 0 { - return true - } - - return slices.ContainsFunc(ids, func(s string) bool { - return MatchPattern(s, id) - }) - }, - "exp": func(_ types.Metadata, param any) bool { - expiry, ok := param.(time.Time) - return ok && time.Now().Before(expiry) - }, - } -} - -// MatchPattern checks if the pattern string matches the input pattern. -// The wildcard '*' in the pattern matches any sequence of characters. -func MatchPattern(input, pattern string) bool { - matched, err := regexp.MatchString(regexpFromPattern(pattern), input) - return err == nil && matched -} - -func regexpFromPattern(pattern string) string { - parts := strings.Split(pattern, "*") - if len(parts) == 1 { - return "^" + pattern + "$" - } - var sb strings.Builder - for i, literal := range parts { - if i > 0 { - sb.WriteString(".*") - } - sb.WriteString(regexp.QuoteMeta(literal)) - } - return "^" + sb.String() + "$" -} diff --git a/pkg/iac/ignore/rule_test.go b/pkg/iac/ignore/rule_test.go deleted file mode 100644 index 619d251eb750..000000000000 --- a/pkg/iac/ignore/rule_test.go +++ /dev/null @@ -1,359 +0,0 @@ -package ignore_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func metadataWithLine(path string, line int) types.Metadata { - return types.NewMetadata(types.NewRange(path, line, line, "", nil), "") -} - -func TestRules_Ignore(t *testing.T) { - - const filename = "test" - - type args struct { - metadata types.Metadata - ids []string - } - - tests := []struct { - name string - src string - args args - shouldIgnore bool - }{ - { - name: "no ignore", - src: `#test`, - shouldIgnore: false, - }, - { - name: "one ignore rule", - src: `#trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "blank line between rule and finding", - src: `#trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine(filename, 3), - ids: []string{"rule-1"}, - }, - shouldIgnore: false, - }, - { - name: "blank line between rules", - src: `#trivy:ignore:rule-1 - -#trivy:ignore:rule-2 -`, - args: args{ - metadata: metadataWithLine(filename, 4), - ids: []string{"rule-1"}, - }, - shouldIgnore: false, - }, - { - name: "rule and a finding on the same line", - src: `#trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine(filename, 1), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "rule and a finding on the same line", - src: `test #trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine(filename, 1), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "multiple rules on one line", - src: `test #trivy:ignore:rule-1 #trivy:ignore:rule-2`, - args: args{ - metadata: metadataWithLine(filename, 1), - ids: []string{"rule-2"}, - }, - shouldIgnore: true, - }, - { - name: "rule and find from different files", - src: `test #trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine("another-file", 1), - ids: []string{"rule-2"}, - }, - shouldIgnore: false, - }, - { - name: "multiple ignore rule", - src: `#trivy:ignore:rule-1 -#trivy:ignore:rule-2 -`, - args: args{ - metadata: metadataWithLine(filename, 3), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "ignore section with params", - src: `#trivy:ignore:rule-1[param1=1]`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "id's don't match", - src: `#trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-2"}, - }, - shouldIgnore: false, - }, - { - name: "without ignore section", - src: `#trivy:exp:2022-01-01`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-2"}, - }, - shouldIgnore: false, - }, - { - name: "non valid ignore section", - src: `#trivy:ignore`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-2"}, - }, - shouldIgnore: false, - }, - { - name: "ignore rule with expiry date passed", - src: `#trivy:ignore:rule-1:exp:2022-01-01`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - }, - shouldIgnore: false, - }, - { - name: "ignore rule with expiry date not passed", - src: `#trivy:ignore:rule-1:exp:2026-01-01`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "ignore rule with invalid expiry date", - src: `#trivy:ignore:rule-1:exp:2026-99-01`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - }, - shouldIgnore: false, - }, - { - name: "with valid wildcard", - src: `#trivy:ignore:rule-*`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "with non-valid wildcard", - src: `#trivy:ignore:rule-1-*d`, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1-abc"}, - }, - shouldIgnore: false, - }, - { - name: "multiple ignore rules on the same line", - src: `test #trivy:ignore:rule-1 -test #trivy:ignore:rule-2 - `, - args: args{ - metadata: metadataWithLine(filename, 1), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "multiple ignore rules on the same line", - src: `# trivy:ignore:rule-1 -# trivy:ignore:rule-2 -test #trivy:ignore:rule-3 -`, - args: args{ - metadata: metadataWithLine(filename, 3), - ids: []string{"rule-1"}, - }, - shouldIgnore: true, - }, - { - name: "multiple ignore rules on the same line", - src: `# trivy:ignore:rule-1 # trivy:ignore:rule-2 -# trivy:ignore:rule-3 -test #trivy:ignore:rule-4 -`, - args: args{ - metadata: metadataWithLine(filename, 3), - ids: []string{"rule-2"}, - }, - shouldIgnore: true, - }, - { - name: "multiple ids", - src: `# trivy:ignore:rule-1`, - args: args{ - metadata: metadataWithLine(filename, 1), - ids: []string{"rule-1", "rule-2"}, - }, - shouldIgnore: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rules := ignore.Parse(tt.src, "", filename) - got := rules.Ignore(tt.args.metadata, tt.args.ids, nil) - assert.Equal(t, tt.shouldIgnore, got) - }) - } -} - -func TestRules_IgnoreWithCustomIgnorer(t *testing.T) { - const filename = "test" - - type args struct { - metadata types.Metadata - ids []string - ignorers map[string]ignore.Ignorer - } - - tests := []struct { - name string - src string - parser ignore.RuleSectionParser - args args - shouldIgnore bool - }{ - { - name: "happy", - src: `#trivy:ignore:rule-1:ws:dev`, - parser: &ignore.StringMatchParser{ - SectionKey: "ws", - }, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - ignorers: map[string]ignore.Ignorer{ - "ws": func(_ types.Metadata, param any) bool { - ws, ok := param.(string) - if !ok { - return false - } - return ws == "dev" - }, - }, - }, - shouldIgnore: true, - }, - { - name: "with wildcard", - src: `#trivy:ignore:rule-1:ws:dev-*`, - parser: &ignore.StringMatchParser{ - SectionKey: "ws", - }, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - ignorers: map[string]ignore.Ignorer{ - "ws": func(_ types.Metadata, param any) bool { - ws, ok := param.(string) - if !ok { - return false - } - return ignore.MatchPattern("dev-stage1", ws) - }, - }, - }, - shouldIgnore: true, - }, - { - name: "bad", - src: `#trivy:ignore:rule-1:ws:prod`, - parser: &ignore.StringMatchParser{ - SectionKey: "ws", - }, - args: args{ - metadata: metadataWithLine(filename, 2), - ids: []string{"rule-1"}, - ignorers: map[string]ignore.Ignorer{ - "ws": func(_ types.Metadata, param any) bool { - ws, ok := param.(string) - if !ok { - return false - } - return ws == "dev" - }, - }, - }, - shouldIgnore: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rules := ignore.Parse(tt.src, filename, "", tt.parser) - got := rules.Ignore(tt.args.metadata, tt.args.ids, tt.args.ignorers) - assert.Equal(t, tt.shouldIgnore, got) - }) - } -} - -func TestMatchPattern(t *testing.T) { - tests := []struct { - input string - pattern string - expected bool - }{ - {"foo-test-bar", "*-test-*", true}, - {"foo-test-bar", "*-example-*", false}, - {"test", "*test", true}, - {"example", "test", false}, - {"example-test", "*-test*", true}, - {"example-test", "*example-*", true}, - } - - for _, tc := range tests { - t.Run(tc.input+":"+tc.pattern, func(t *testing.T) { - got := ignore.MatchPattern(tc.input, tc.pattern) - assert.Equal(t, tc.expected, got) - }) - } -} diff --git a/pkg/iac/providers/aws/accessanalyzer/aa.go b/pkg/iac/providers/aws/accessanalyzer/aa.go deleted file mode 100644 index 66be31561d04..000000000000 --- a/pkg/iac/providers/aws/accessanalyzer/aa.go +++ /dev/null @@ -1,19 +0,0 @@ -package accessanalyzer - -import "github.com/aquasecurity/trivy/pkg/iac/types" - -type AccessAnalyzer struct { - Analyzers []Analyzer -} - -type Analyzer struct { - Metadata types.Metadata - ARN types.StringValue - Name types.StringValue - Active types.BoolValue - Findings []Findings -} - -type Findings struct { - Metadata types.Metadata -} diff --git a/pkg/iac/providers/aws/apigateway/ag.go b/pkg/iac/providers/aws/apigateway/ag.go deleted file mode 100644 index d87a9b364ee8..000000000000 --- a/pkg/iac/providers/aws/apigateway/ag.go +++ /dev/null @@ -1,11 +0,0 @@ -package apigateway - -import ( - v1 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v1" - v2 "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway/v2" -) - -type APIGateway struct { - V1 v1.APIGateway - V2 v2.APIGateway -} diff --git a/pkg/iac/providers/aws/apigateway/v1/apigateway.go b/pkg/iac/providers/aws/apigateway/v1/apigateway.go deleted file mode 100755 index 626d91d513b7..000000000000 --- a/pkg/iac/providers/aws/apigateway/v1/apigateway.go +++ /dev/null @@ -1,62 +0,0 @@ -package v1 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type APIGateway struct { - APIs []API - DomainNames []DomainName -} - -type API struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Stages []Stage - Resources []Resource -} - -type Stage struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - AccessLogging AccessLogging - XRayTracingEnabled iacTypes.BoolValue - RESTMethodSettings []RESTMethodSettings -} - -type Resource struct { - Metadata iacTypes.Metadata - Methods []Method -} - -type AccessLogging struct { - Metadata iacTypes.Metadata - CloudwatchLogGroupARN iacTypes.StringValue -} - -type RESTMethodSettings struct { - Metadata iacTypes.Metadata - Method iacTypes.StringValue - CacheDataEncrypted iacTypes.BoolValue - CacheEnabled iacTypes.BoolValue -} - -const ( - AuthorizationNone = "NONE" - AuthorizationCustom = "CUSTOM" - AuthorizationIAM = "AWS_IAM" - AuthorizationCognitoUserPools = "COGNITO_USER_POOLS" -) - -type Method struct { - Metadata iacTypes.Metadata - HTTPMethod iacTypes.StringValue - AuthorizationType iacTypes.StringValue - APIKeyRequired iacTypes.BoolValue -} - -type DomainName struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - SecurityPolicy iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/apigateway/v2/apigateway.go b/pkg/iac/providers/aws/apigateway/v2/apigateway.go deleted file mode 100755 index 5a87e8ffbca7..000000000000 --- a/pkg/iac/providers/aws/apigateway/v2/apigateway.go +++ /dev/null @@ -1,41 +0,0 @@ -package v2 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type APIGateway struct { - APIs []API - DomainNames []DomainName -} - -const ( - ProtocolTypeUnknown string = "" - ProtocolTypeREST string = "REST" - ProtocolTypeHTTP string = "HTTP" - ProtocolTypeWebsocket string = "WEBSOCKET" -) - -type API struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - ProtocolType iacTypes.StringValue - Stages []Stage -} - -type Stage struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - AccessLogging AccessLogging -} - -type AccessLogging struct { - Metadata iacTypes.Metadata - CloudwatchLogGroupARN iacTypes.StringValue -} - -type DomainName struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - SecurityPolicy iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/athena/athena.go b/pkg/iac/providers/aws/athena/athena.go deleted file mode 100755 index 537a4b63d6b0..000000000000 --- a/pkg/iac/providers/aws/athena/athena.go +++ /dev/null @@ -1,35 +0,0 @@ -package athena - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Athena struct { - Databases []Database - Workgroups []Workgroup -} - -type Database struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Encryption EncryptionConfiguration -} - -type Workgroup struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Encryption EncryptionConfiguration - EnforceConfiguration iacTypes.BoolValue -} - -const ( - EncryptionTypeNone = "" - EncryptionTypeSSES3 = "SSE_S3" - EncryptionTypeSSEKMS = "SSE_KMS" - EncryptionTypeCSEKMS = "CSE_KMS" -) - -type EncryptionConfiguration struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/aws.go b/pkg/iac/providers/aws/aws.go deleted file mode 100755 index 15c80a84afa7..000000000000 --- a/pkg/iac/providers/aws/aws.go +++ /dev/null @@ -1,80 +0,0 @@ -package aws - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/accessanalyzer" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/apigateway" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/athena" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudfront" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudtrail" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/cloudwatch" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/codebuild" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/config" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/documentdb" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/dynamodb" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecs" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticache" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elasticsearch" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/elb" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/emr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/kinesis" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/kms" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/lambda" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/mq" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/msk" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/neptune" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/redshift" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sam" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sns" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/sqs" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ssm" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/workspaces" -) - -type AWS struct { - Meta Meta - AccessAnalyzer accessanalyzer.AccessAnalyzer - APIGateway apigateway.APIGateway - Athena athena.Athena - Cloudfront cloudfront.Cloudfront - CloudTrail cloudtrail.CloudTrail - CloudWatch cloudwatch.CloudWatch - CodeBuild codebuild.CodeBuild - Config config.Config - DocumentDB documentdb.DocumentDB - DynamoDB dynamodb.DynamoDB - EC2 ec2.EC2 - ECR ecr.ECR - ECS ecs.ECS - EFS efs.EFS - EKS eks.EKS - ElastiCache elasticache.ElastiCache - Elasticsearch elasticsearch.Elasticsearch - ELB elb.ELB - EMR emr.EMR - IAM iam.IAM - Kinesis kinesis.Kinesis - KMS kms.KMS - Lambda lambda.Lambda - MQ mq.MQ - MSK msk.MSK - Neptune neptune.Neptune - RDS rds.RDS - Redshift redshift.Redshift - SAM sam.SAM - S3 s3.S3 - SNS sns.SNS - SQS sqs.SQS - SSM ssm.SSM - WorkSpaces workspaces.WorkSpaces -} - -type Meta struct { - TFProviders []TerraformProvider -} diff --git a/pkg/iac/providers/aws/cloudfront/cloudfront.go b/pkg/iac/providers/aws/cloudfront/cloudfront.go deleted file mode 100755 index 0c4914e4c3bb..000000000000 --- a/pkg/iac/providers/aws/cloudfront/cloudfront.go +++ /dev/null @@ -1,45 +0,0 @@ -package cloudfront - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Cloudfront struct { - Distributions []Distribution -} - -type Distribution struct { - Metadata iacTypes.Metadata - WAFID iacTypes.StringValue - Logging Logging - DefaultCacheBehaviour CacheBehaviour - OrdererCacheBehaviours []CacheBehaviour - ViewerCertificate ViewerCertificate -} - -type Logging struct { - Metadata iacTypes.Metadata - Bucket iacTypes.StringValue -} - -type CacheBehaviour struct { - Metadata iacTypes.Metadata - ViewerProtocolPolicy iacTypes.StringValue -} - -const ( - ViewerPolicyProtocolAllowAll = "allow-all" - ViewerPolicyProtocolHTTPSOnly = "https-only" - ViewerPolicyProtocolRedirectToHTTPS = "redirect-to-https" -) - -const ( - ProtocolVersionTLS1_2 = "TLSv1.2_2021" -) - -type ViewerCertificate struct { - Metadata iacTypes.Metadata - CloudfrontDefaultCertificate iacTypes.BoolValue - SSLSupportMethod iacTypes.StringValue - MinimumProtocolVersion iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/cloudtrail/cloudtrail.go b/pkg/iac/providers/aws/cloudtrail/cloudtrail.go deleted file mode 100755 index 8e5da87e7c89..000000000000 --- a/pkg/iac/providers/aws/cloudtrail/cloudtrail.go +++ /dev/null @@ -1,42 +0,0 @@ -package cloudtrail - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type CloudTrail struct { - Trails []Trail -} - -func (c CloudTrail) MultiRegionTrails() (multiRegionTrails []Trail) { - for _, trail := range c.Trails { - if trail.IsMultiRegion.IsTrue() { - multiRegionTrails = append(multiRegionTrails, trail) - } - } - return multiRegionTrails -} - -type Trail struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - EnableLogFileValidation iacTypes.BoolValue - IsMultiRegion iacTypes.BoolValue - KMSKeyID iacTypes.StringValue - CloudWatchLogsLogGroupArn iacTypes.StringValue - IsLogging iacTypes.BoolValue - BucketName iacTypes.StringValue - EventSelectors []EventSelector -} - -type EventSelector struct { - Metadata iacTypes.Metadata - DataResources []DataResource - ReadWriteType iacTypes.StringValue // ReadOnly, WriteOnly, All. Default value is All for TF. -} - -type DataResource struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue // You can specify only the following value: "AWS::S3::Object", "AWS::Lambda::Function" and "AWS::DynamoDB::Table". - Values []iacTypes.StringValue // List of ARNs/partial ARNs - e.g. arn:aws:s3:::/ for all objects in a bucket, arn:aws:s3:::/key for specific objects -} diff --git a/pkg/iac/providers/aws/cloudwatch/cloudwatch.go b/pkg/iac/providers/aws/cloudwatch/cloudwatch.go deleted file mode 100755 index 630ed84e64ef..000000000000 --- a/pkg/iac/providers/aws/cloudwatch/cloudwatch.go +++ /dev/null @@ -1,63 +0,0 @@ -package cloudwatch - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type CloudWatch struct { - LogGroups []LogGroup - Alarms []Alarm -} - -func (w CloudWatch) GetLogGroupByArn(arn string) (logGroup *LogGroup) { - for _, logGroup := range w.LogGroups { - if logGroup.Arn.EqualTo(arn) { - return &logGroup - } - } - return nil -} - -func (w CloudWatch) GetAlarmByMetricName(metricName string) (alarm *Alarm) { - for _, alarm := range w.Alarms { - if alarm.MetricName.EqualTo(metricName) { - return &alarm - } - } - return nil -} - -type Alarm struct { - Metadata iacTypes.Metadata - AlarmName iacTypes.StringValue - MetricName iacTypes.StringValue - Dimensions []AlarmDimension - Metrics []MetricDataQuery -} - -type AlarmDimension struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Value iacTypes.StringValue -} - -type MetricFilter struct { - Metadata iacTypes.Metadata - FilterName iacTypes.StringValue - FilterPattern iacTypes.StringValue -} - -type MetricDataQuery struct { - Metadata iacTypes.Metadata - Expression iacTypes.StringValue - ID iacTypes.StringValue -} - -type LogGroup struct { - Metadata iacTypes.Metadata - Arn iacTypes.StringValue - Name iacTypes.StringValue - KMSKeyID iacTypes.StringValue - RetentionInDays iacTypes.IntValue - MetricFilters []MetricFilter -} diff --git a/pkg/iac/providers/aws/codebuild/codebuild.go b/pkg/iac/providers/aws/codebuild/codebuild.go deleted file mode 100755 index 34115ce40cd5..000000000000 --- a/pkg/iac/providers/aws/codebuild/codebuild.go +++ /dev/null @@ -1,20 +0,0 @@ -package codebuild - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type CodeBuild struct { - Projects []Project -} - -type Project struct { - Metadata iacTypes.Metadata - ArtifactSettings ArtifactSettings - SecondaryArtifactSettings []ArtifactSettings -} - -type ArtifactSettings struct { - Metadata iacTypes.Metadata - EncryptionEnabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/config/config.go b/pkg/iac/providers/aws/config/config.go deleted file mode 100755 index ef18539f3bfa..000000000000 --- a/pkg/iac/providers/aws/config/config.go +++ /dev/null @@ -1,14 +0,0 @@ -package config - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Config struct { - ConfigurationAggregrator ConfigurationAggregrator -} - -type ConfigurationAggregrator struct { - Metadata iacTypes.Metadata - SourceAllRegions iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/documentdb/documentdb.go b/pkg/iac/providers/aws/documentdb/documentdb.go deleted file mode 100755 index c1bdc0a73854..000000000000 --- a/pkg/iac/providers/aws/documentdb/documentdb.go +++ /dev/null @@ -1,29 +0,0 @@ -package documentdb - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DocumentDB struct { - Clusters []Cluster -} - -const ( - LogExportAudit = "audit" - LogExportProfiler = "profiler" -) - -type Cluster struct { - Metadata iacTypes.Metadata - Identifier iacTypes.StringValue - EnabledLogExports []iacTypes.StringValue - BackupRetentionPeriod iacTypes.IntValue - Instances []Instance - StorageEncrypted iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} - -type Instance struct { - Metadata iacTypes.Metadata - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/dynamodb/dynamodb.go b/pkg/iac/providers/aws/dynamodb/dynamodb.go deleted file mode 100755 index eef0d5d532c0..000000000000 --- a/pkg/iac/providers/aws/dynamodb/dynamodb.go +++ /dev/null @@ -1,30 +0,0 @@ -package dynamodb - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DynamoDB struct { - DAXClusters []DAXCluster - Tables []Table -} - -type DAXCluster struct { - Metadata iacTypes.Metadata - ServerSideEncryption ServerSideEncryption - PointInTimeRecovery iacTypes.BoolValue -} - -type Table struct { - Metadata iacTypes.Metadata - ServerSideEncryption ServerSideEncryption - PointInTimeRecovery iacTypes.BoolValue -} - -type ServerSideEncryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} - -const DefaultKMSKeyID = "alias/aws/dynamodb" diff --git a/pkg/iac/providers/aws/ec2/ec2.go b/pkg/iac/providers/aws/ec2/ec2.go deleted file mode 100755 index 726e312f65aa..000000000000 --- a/pkg/iac/providers/aws/ec2/ec2.go +++ /dev/null @@ -1,12 +0,0 @@ -package ec2 - -type EC2 struct { - Instances []Instance - LaunchConfigurations []LaunchConfiguration - LaunchTemplates []LaunchTemplate - VPCs []VPC - SecurityGroups []SecurityGroup - NetworkACLs []NetworkACL - Subnets []Subnet - Volumes []Volume -} diff --git a/pkg/iac/providers/aws/ec2/instance.go b/pkg/iac/providers/aws/ec2/instance.go deleted file mode 100644 index 09ecede5592b..000000000000 --- a/pkg/iac/providers/aws/ec2/instance.go +++ /dev/null @@ -1,55 +0,0 @@ -package ec2 - -import ( - "github.com/owenrumney/squealer/pkg/squealer" - - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Instance struct { - Metadata iacTypes.Metadata - MetadataOptions MetadataOptions - UserData iacTypes.StringValue - SecurityGroups []SecurityGroup - RootBlockDevice *BlockDevice - EBSBlockDevices []*BlockDevice -} - -type BlockDevice struct { - Metadata iacTypes.Metadata - Encrypted iacTypes.BoolValue -} - -type MetadataOptions struct { - Metadata iacTypes.Metadata - HttpTokens iacTypes.StringValue - HttpEndpoint iacTypes.StringValue -} - -func NewInstance(metadata iacTypes.Metadata) *Instance { - return &Instance{ - Metadata: metadata, - MetadataOptions: MetadataOptions{ - Metadata: metadata, - HttpTokens: iacTypes.StringDefault("optional", metadata), - HttpEndpoint: iacTypes.StringDefault("enabled", metadata), - }, - UserData: iacTypes.StringDefault("", metadata), - SecurityGroups: []SecurityGroup{}, - RootBlockDevice: nil, - EBSBlockDevices: nil, - } -} - -func (i *Instance) RequiresIMDSToken() bool { - return i.MetadataOptions.HttpTokens.EqualTo("required") -} - -func (i *Instance) HasHTTPEndpointDisabled() bool { - return i.MetadataOptions.HttpEndpoint.EqualTo("disabled") -} - -func (i *Instance) HasSensitiveInformationInUserData() bool { - scanner := squealer.NewStringScanner() - return scanner.Scan(i.UserData.Value()).TransgressionFound -} diff --git a/pkg/iac/providers/aws/ec2/launch.go b/pkg/iac/providers/aws/ec2/launch.go deleted file mode 100644 index 9b7c4c711e01..000000000000 --- a/pkg/iac/providers/aws/ec2/launch.go +++ /dev/null @@ -1,29 +0,0 @@ -package ec2 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type LaunchConfiguration struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - AssociatePublicIP iacTypes.BoolValue - RootBlockDevice *BlockDevice - EBSBlockDevices []*BlockDevice - MetadataOptions MetadataOptions - UserData iacTypes.StringValue -} - -type LaunchTemplate struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Instance -} - -func (i *LaunchConfiguration) RequiresIMDSToken() bool { - return i.MetadataOptions.HttpTokens.EqualTo("required") -} - -func (i *LaunchConfiguration) HasHTTPEndpointDisabled() bool { - return i.MetadataOptions.HttpEndpoint.EqualTo("disabled") -} diff --git a/pkg/iac/providers/aws/ec2/subnet.go b/pkg/iac/providers/aws/ec2/subnet.go deleted file mode 100644 index 18f605011e07..000000000000 --- a/pkg/iac/providers/aws/ec2/subnet.go +++ /dev/null @@ -1,10 +0,0 @@ -package ec2 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Subnet struct { - Metadata iacTypes.Metadata - MapPublicIpOnLaunch iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/ec2/volume.go b/pkg/iac/providers/aws/ec2/volume.go deleted file mode 100644 index 3258a3ee6c5a..000000000000 --- a/pkg/iac/providers/aws/ec2/volume.go +++ /dev/null @@ -1,16 +0,0 @@ -package ec2 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Volume struct { - Metadata iacTypes.Metadata - Encryption Encryption -} - -type Encryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/ec2/vpc.go b/pkg/iac/providers/aws/ec2/vpc.go deleted file mode 100644 index d6bff4fe7025..000000000000 --- a/pkg/iac/providers/aws/ec2/vpc.go +++ /dev/null @@ -1,57 +0,0 @@ -package ec2 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type NetworkACL struct { - Metadata iacTypes.Metadata - Rules []NetworkACLRule - IsDefaultRule iacTypes.BoolValue -} - -type SecurityGroup struct { - Metadata iacTypes.Metadata - IsDefault iacTypes.BoolValue - Description iacTypes.StringValue - IngressRules []SecurityGroupRule - EgressRules []SecurityGroupRule - VPCID iacTypes.StringValue -} - -type SecurityGroupRule struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue - CIDRs []iacTypes.StringValue - Protocol iacTypes.StringValue - FromPort iacTypes.IntValue - ToPort iacTypes.IntValue -} - -type VPC struct { - Metadata iacTypes.Metadata - ID iacTypes.StringValue - IsDefault iacTypes.BoolValue - SecurityGroups []SecurityGroup - FlowLogsEnabled iacTypes.BoolValue -} - -const ( - TypeIngress = "ingress" - TypeEgress = "egress" -) - -const ( - ActionAllow = "allow" - ActionDeny = "deny" -) - -type NetworkACLRule struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue - Action iacTypes.StringValue - Protocol iacTypes.StringValue - CIDRs []iacTypes.StringValue - FromPort iacTypes.IntValue - ToPort iacTypes.IntValue -} diff --git a/pkg/iac/providers/aws/ecr/ecr.go b/pkg/iac/providers/aws/ecr/ecr.go deleted file mode 100755 index 053b7f13dace..000000000000 --- a/pkg/iac/providers/aws/ecr/ecr.go +++ /dev/null @@ -1,34 +0,0 @@ -package ecr - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ECR struct { - Repositories []Repository -} - -type Repository struct { - Metadata iacTypes.Metadata - ImageScanning ImageScanning - ImageTagsImmutable iacTypes.BoolValue - Policies []iam.Policy - Encryption Encryption -} - -type ImageScanning struct { - Metadata iacTypes.Metadata - ScanOnPush iacTypes.BoolValue -} - -const ( - EncryptionTypeKMS = "KMS" - EncryptionTypeAES256 = "AES256" -) - -type Encryption struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/ecs/ecs.go b/pkg/iac/providers/aws/ecs/ecs.go deleted file mode 100755 index 661d2233b2da..000000000000 --- a/pkg/iac/providers/aws/ecs/ecs.go +++ /dev/null @@ -1,124 +0,0 @@ -package ecs - -import ( - "encoding/json" - - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ECS struct { - Clusters []Cluster - TaskDefinitions []TaskDefinition -} - -type Cluster struct { - Metadata iacTypes.Metadata - Settings ClusterSettings -} - -type ClusterSettings struct { - Metadata iacTypes.Metadata - ContainerInsightsEnabled iacTypes.BoolValue -} - -type TaskDefinition struct { - Metadata iacTypes.Metadata - Volumes []Volume - ContainerDefinitions []ContainerDefinition -} - -func CreateDefinitionsFromString(metadata iacTypes.Metadata, str string) ([]ContainerDefinition, error) { - var containerDefinitionsJSON []containerDefinitionJSON - if err := json.Unmarshal([]byte(str), &containerDefinitionsJSON); err != nil { - return nil, err - } - var definitions []ContainerDefinition - for _, j := range containerDefinitionsJSON { - definitions = append(definitions, j.convert(metadata)) - } - return definitions, nil -} - -// see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html -type containerDefinitionJSON struct { - Name string `json:"name"` - Image string `json:"image"` - CPU string `json:"cpu"` - Memory string `json:"memory"` - Essential bool `json:"essential"` - PortMappings []portMappingJSON `json:"portMappings"` - EnvVars []envVarJSON `json:"environment"` - Privileged bool `json:"privileged"` -} - -type envVarJSON struct { - Name string `json:"name"` - Value string `json:"value"` -} - -type portMappingJSON struct { - ContainerPort int `json:"containerPort"` - HostPort int `json:"hostPort"` -} - -func (j containerDefinitionJSON) convert(metadata iacTypes.Metadata) ContainerDefinition { - var mappings []PortMapping - for _, jMapping := range j.PortMappings { - mappings = append(mappings, PortMapping{ - ContainerPort: iacTypes.Int(jMapping.ContainerPort, metadata), - HostPort: iacTypes.Int(jMapping.HostPort, metadata), - }) - } - - var envVars []EnvVar - for _, env := range j.EnvVars { - envVars = append(envVars, EnvVar{ - Name: iacTypes.String(env.Name, metadata), - Value: iacTypes.String(env.Value, metadata), - }) - } - - return ContainerDefinition{ - Metadata: metadata, - Name: iacTypes.String(j.Name, metadata), - Image: iacTypes.String(j.Image, metadata), - CPU: iacTypes.String(j.CPU, metadata), - Memory: iacTypes.String(j.Memory, metadata), - Essential: iacTypes.Bool(j.Essential, metadata), - PortMappings: mappings, - Environment: envVars, - Privileged: iacTypes.Bool(j.Privileged, metadata), - } -} - -type ContainerDefinition struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Image iacTypes.StringValue - CPU iacTypes.StringValue - Memory iacTypes.StringValue - Essential iacTypes.BoolValue - PortMappings []PortMapping - Environment []EnvVar - Privileged iacTypes.BoolValue -} - -type EnvVar struct { - Name iacTypes.StringValue - Value iacTypes.StringValue -} - -type PortMapping struct { - ContainerPort iacTypes.IntValue - HostPort iacTypes.IntValue -} - -type Volume struct { - Metadata iacTypes.Metadata - EFSVolumeConfiguration EFSVolumeConfiguration -} - -type EFSVolumeConfiguration struct { - Metadata iacTypes.Metadata - TransitEncryptionEnabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/efs/efs.go b/pkg/iac/providers/aws/efs/efs.go deleted file mode 100755 index 4e9765b7d500..000000000000 --- a/pkg/iac/providers/aws/efs/efs.go +++ /dev/null @@ -1,14 +0,0 @@ -package efs - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type EFS struct { - FileSystems []FileSystem -} - -type FileSystem struct { - Metadata iacTypes.Metadata - Encrypted iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/eks/eks.go b/pkg/iac/providers/aws/eks/eks.go deleted file mode 100755 index 9eab8b563f65..000000000000 --- a/pkg/iac/providers/aws/eks/eks.go +++ /dev/null @@ -1,32 +0,0 @@ -package eks - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type EKS struct { - Clusters []Cluster -} - -type Cluster struct { - Metadata iacTypes.Metadata - Logging Logging - Encryption Encryption - PublicAccessEnabled iacTypes.BoolValue - PublicAccessCIDRs []iacTypes.StringValue -} - -type Logging struct { - Metadata iacTypes.Metadata - API iacTypes.BoolValue - Audit iacTypes.BoolValue - Authenticator iacTypes.BoolValue - ControllerManager iacTypes.BoolValue - Scheduler iacTypes.BoolValue -} - -type Encryption struct { - Metadata iacTypes.Metadata - Secrets iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/elasticache/elasticache.go b/pkg/iac/providers/aws/elasticache/elasticache.go deleted file mode 100755 index 4069601b3193..000000000000 --- a/pkg/iac/providers/aws/elasticache/elasticache.go +++ /dev/null @@ -1,29 +0,0 @@ -package elasticache - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ElastiCache struct { - Clusters []Cluster - ReplicationGroups []ReplicationGroup - SecurityGroups []SecurityGroup -} - -type Cluster struct { - Metadata iacTypes.Metadata - Engine iacTypes.StringValue - NodeType iacTypes.StringValue - SnapshotRetentionLimit iacTypes.IntValue // days -} - -type ReplicationGroup struct { - Metadata iacTypes.Metadata - TransitEncryptionEnabled iacTypes.BoolValue - AtRestEncryptionEnabled iacTypes.BoolValue -} - -type SecurityGroup struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/elasticsearch/elasticsearch.go b/pkg/iac/providers/aws/elasticsearch/elasticsearch.go deleted file mode 100755 index 4f3cb0583681..000000000000 --- a/pkg/iac/providers/aws/elasticsearch/elasticsearch.go +++ /dev/null @@ -1,53 +0,0 @@ -package elasticsearch - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Elasticsearch struct { - Domains []Domain -} - -type Domain struct { - Metadata iacTypes.Metadata - DomainName iacTypes.StringValue - AccessPolicies iacTypes.StringValue - DedicatedMasterEnabled iacTypes.BoolValue - VpcId iacTypes.StringValue - LogPublishing LogPublishing - TransitEncryption TransitEncryption - AtRestEncryption AtRestEncryption - ServiceSoftwareOptions ServiceSoftwareOptions - Endpoint Endpoint -} - -type ServiceSoftwareOptions struct { - Metadata iacTypes.Metadata - CurrentVersion iacTypes.StringValue - NewVersion iacTypes.StringValue - UpdateAvailable iacTypes.BoolValue - UpdateStatus iacTypes.StringValue -} - -type Endpoint struct { - Metadata iacTypes.Metadata - EnforceHTTPS iacTypes.BoolValue - TLSPolicy iacTypes.StringValue -} - -type LogPublishing struct { - Metadata iacTypes.Metadata - AuditEnabled iacTypes.BoolValue - CloudWatchLogGroupArn iacTypes.StringValue -} - -type TransitEncryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type AtRestEncryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - KmsKeyId iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/elb/elb.go b/pkg/iac/providers/aws/elb/elb.go deleted file mode 100755 index 04803fd26380..000000000000 --- a/pkg/iac/providers/aws/elb/elb.go +++ /dev/null @@ -1,36 +0,0 @@ -package elb - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ELB struct { - LoadBalancers []LoadBalancer -} - -const ( - TypeApplication = "application" - TypeGateway = "gateway" - TypeNetwork = "network" - TypeClassic = "classic" -) - -type LoadBalancer struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue - DropInvalidHeaderFields iacTypes.BoolValue - Internal iacTypes.BoolValue - Listeners []Listener -} - -type Listener struct { - Metadata iacTypes.Metadata - Protocol iacTypes.StringValue - TLSPolicy iacTypes.StringValue - DefaultActions []Action -} - -type Action struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/emr/emr.go b/pkg/iac/providers/aws/emr/emr.go deleted file mode 100644 index 1d00618a0345..000000000000 --- a/pkg/iac/providers/aws/emr/emr.go +++ /dev/null @@ -1,28 +0,0 @@ -package emr - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type EMR struct { - Clusters []Cluster - SecurityConfiguration []SecurityConfiguration -} - -type Cluster struct { - Metadata iacTypes.Metadata - Settings ClusterSettings -} - -type ClusterSettings struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - ReleaseLabel iacTypes.StringValue - ServiceRole iacTypes.StringValue -} - -type SecurityConfiguration struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Configuration iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/iam/iam.go b/pkg/iac/providers/aws/iam/iam.go deleted file mode 100644 index 9a90a857c9d7..000000000000 --- a/pkg/iac/providers/aws/iam/iam.go +++ /dev/null @@ -1,117 +0,0 @@ -package iam - -import ( - "github.com/aquasecurity/iamgo" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type IAM struct { - PasswordPolicy PasswordPolicy - Policies []Policy - Groups []Group - Users []User - Roles []Role - ServerCertificates []ServerCertificate -} - -type ServerCertificate struct { - Metadata iacTypes.Metadata - Expiration iacTypes.TimeValue -} - -type Policy struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Document Document - Builtin iacTypes.BoolValue -} - -type Document struct { - Metadata iacTypes.Metadata - Parsed iamgo.Document - IsOffset bool - HasRefs bool -} - -func (d Document) ToRego() any { - m := d.Metadata - doc, _ := d.Parsed.MarshalJSON() - input := map[string]any{ - "filepath": m.Range().GetFilename(), - "startline": m.Range().GetStartLine(), - "endline": m.Range().GetEndLine(), - "managed": m.IsManaged(), - "explicit": m.IsExplicit(), - "value": string(doc), - "sourceprefix": m.Range().GetSourcePrefix(), - "fskey": iacTypes.CreateFSKey(m.Range().GetFS()), - "resource": m.Reference(), - } - - if m.Parent() != nil { - input["parent"] = m.Parent().ToRego() - } - - return input -} - -type Group struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Policies []Policy -} - -type User struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Policies []Policy - AccessKeys []AccessKey - MFADevices []MFADevice - LastAccess iacTypes.TimeValue -} - -func (u *User) HasLoggedIn() bool { - return u.LastAccess.GetMetadata().IsResolvable() && !u.LastAccess.IsNever() -} - -type MFADevice struct { - Metadata iacTypes.Metadata - IsVirtual iacTypes.BoolValue -} - -type AccessKey struct { - Metadata iacTypes.Metadata - AccessKeyId iacTypes.StringValue - Active iacTypes.BoolValue - CreationDate iacTypes.TimeValue - LastAccess iacTypes.TimeValue -} - -type Role struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Policies []Policy -} - -func (d Document) MetadataFromIamGo(r ...iamgo.Range) iacTypes.Metadata { - m := d.Metadata - if d.HasRefs { - return m - } - newRange := m.Range() - var start int - if !d.IsOffset { - start = newRange.GetStartLine() - } - for _, rng := range r { - newRange := iacTypes.NewRange( - newRange.GetLocalFilename(), - start+rng.StartLine, - start+rng.EndLine, - newRange.GetSourcePrefix(), - newRange.GetFS(), - ) - m = iacTypes.NewMetadata(newRange, m.Reference()).WithParent(m) - } - return m -} diff --git a/pkg/iac/providers/aws/iam/passwords.go b/pkg/iac/providers/aws/iam/passwords.go deleted file mode 100755 index 09632ba029e1..000000000000 --- a/pkg/iac/providers/aws/iam/passwords.go +++ /dev/null @@ -1,16 +0,0 @@ -package iam - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type PasswordPolicy struct { - Metadata iacTypes.Metadata - ReusePreventionCount iacTypes.IntValue - RequireLowercase iacTypes.BoolValue - RequireUppercase iacTypes.BoolValue - RequireNumbers iacTypes.BoolValue - RequireSymbols iacTypes.BoolValue - MaxAgeDays iacTypes.IntValue - MinimumLength iacTypes.IntValue -} diff --git a/pkg/iac/providers/aws/kinesis/kinesis.go b/pkg/iac/providers/aws/kinesis/kinesis.go deleted file mode 100755 index 53e6c9d75f02..000000000000 --- a/pkg/iac/providers/aws/kinesis/kinesis.go +++ /dev/null @@ -1,24 +0,0 @@ -package kinesis - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Kinesis struct { - Streams []Stream -} - -type Stream struct { - Metadata iacTypes.Metadata - Encryption Encryption -} - -const ( - EncryptionTypeKMS = "KMS" -) - -type Encryption struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/kms/kms.go b/pkg/iac/providers/aws/kms/kms.go deleted file mode 100755 index a0ef986fd086..000000000000 --- a/pkg/iac/providers/aws/kms/kms.go +++ /dev/null @@ -1,19 +0,0 @@ -package kms - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type KMS struct { - Keys []Key -} - -const ( - KeyUsageSignAndVerify = "SIGN_VERIFY" -) - -type Key struct { - Metadata iacTypes.Metadata - Usage iacTypes.StringValue - RotationEnabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/lambda/lambda.go b/pkg/iac/providers/aws/lambda/lambda.go deleted file mode 100755 index 1d4bb483747d..000000000000 --- a/pkg/iac/providers/aws/lambda/lambda.go +++ /dev/null @@ -1,31 +0,0 @@ -package lambda - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Lambda struct { - Functions []Function -} - -type Function struct { - Metadata iacTypes.Metadata - Tracing Tracing - Permissions []Permission -} - -const ( - TracingModePassThrough = "PassThrough" - TracingModeActive = "Active" -) - -type Tracing struct { - Metadata iacTypes.Metadata - Mode iacTypes.StringValue -} - -type Permission struct { - Metadata iacTypes.Metadata - Principal iacTypes.StringValue - SourceARN iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/mq/mq.go b/pkg/iac/providers/aws/mq/mq.go deleted file mode 100755 index a0e383de6dc4..000000000000 --- a/pkg/iac/providers/aws/mq/mq.go +++ /dev/null @@ -1,21 +0,0 @@ -package mq - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type MQ struct { - Brokers []Broker -} - -type Broker struct { - Metadata iacTypes.Metadata - PublicAccess iacTypes.BoolValue - Logging Logging -} - -type Logging struct { - Metadata iacTypes.Metadata - General iacTypes.BoolValue - Audit iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/msk/msk.go b/pkg/iac/providers/aws/msk/msk.go deleted file mode 100755 index 25d398167ef3..000000000000 --- a/pkg/iac/providers/aws/msk/msk.go +++ /dev/null @@ -1,60 +0,0 @@ -package msk - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type MSK struct { - Clusters []Cluster -} - -type Cluster struct { - Metadata iacTypes.Metadata - EncryptionInTransit EncryptionInTransit - EncryptionAtRest EncryptionAtRest - Logging Logging -} - -const ( - ClientBrokerEncryptionTLS = "TLS" - ClientBrokerEncryptionPlaintext = "PLAINTEXT" - ClientBrokerEncryptionTLSOrPlaintext = "TLS_PLAINTEXT" -) - -type EncryptionInTransit struct { - Metadata iacTypes.Metadata - ClientBroker iacTypes.StringValue -} - -type EncryptionAtRest struct { - Metadata iacTypes.Metadata - KMSKeyARN iacTypes.StringValue - Enabled iacTypes.BoolValue -} - -type Logging struct { - Metadata iacTypes.Metadata - Broker BrokerLogging -} - -type BrokerLogging struct { - Metadata iacTypes.Metadata - S3 S3Logging - Cloudwatch CloudwatchLogging - Firehose FirehoseLogging -} - -type S3Logging struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type CloudwatchLogging struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type FirehoseLogging struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/neptune/neptune.go b/pkg/iac/providers/aws/neptune/neptune.go deleted file mode 100755 index 15966afe4d47..000000000000 --- a/pkg/iac/providers/aws/neptune/neptune.go +++ /dev/null @@ -1,21 +0,0 @@ -package neptune - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Neptune struct { - Clusters []Cluster -} - -type Cluster struct { - Metadata iacTypes.Metadata - Logging Logging - StorageEncrypted iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} - -type Logging struct { - Metadata iacTypes.Metadata - Audit iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/provider.go b/pkg/iac/providers/aws/provider.go deleted file mode 100644 index 475d1bd0691e..000000000000 --- a/pkg/iac/providers/aws/provider.go +++ /dev/null @@ -1,77 +0,0 @@ -package aws - -import "github.com/aquasecurity/trivy/pkg/iac/types" - -type TerraformProvider struct { - Metadata types.Metadata - // generic fields - Alias types.StringValue - Version types.StringValue - - // provider specific fields - AccessKey types.StringValue - AllowedAccountsIDs types.StringValueList - AssumeRole AssumeRole - AssumeRoleWithWebIdentity AssumeRoleWithWebIdentity - CustomCABundle types.StringValue - DefaultTags DefaultTags - EC2MetadataServiceEndpoint types.StringValue - EC2MetadataServiceEndpointMode types.StringValue - Endpoints types.MapValue - ForbiddenAccountIDs types.StringValueList - HttpProxy types.StringValue - IgnoreTags IgnoreTags - Insecure types.BoolValue - MaxRetries types.IntValue - Profile types.StringValue - Region types.StringValue - RetryMode types.StringValue - S3UsePathStyle types.BoolValue - S3USEast1RegionalEndpoint types.StringValue - SecretKey types.StringValue - SharedConfigFiles types.StringValueList - SharedCredentialsFiles types.StringValueList - SkipCredentialsValidation types.BoolValue - SkipMetadataAPICheck types.BoolValue - SkipRegionValidation types.BoolValue - SkipRequestingAccountID types.BoolValue - STSRegion types.StringValue - Token types.StringValue - UseDualstackEndpoint types.BoolValue - UseFIPSEndpoint types.BoolValue -} - -type AssumeRole struct { - Metadata types.Metadata - Duration types.StringValue - ExternalID types.StringValue - Policy types.StringValue - PolicyARNs types.StringValueList - RoleARN types.StringValue - SessionName types.StringValue - SourceIdentity types.StringValue - Tags types.MapValue - TransitiveTagKeys types.StringValueList -} - -type AssumeRoleWithWebIdentity struct { - Metadata types.Metadata - Duration types.StringValue - Policy types.StringValue - PolicyARNs types.StringValueList - RoleARN types.StringValue - SessionName types.StringValue - WebIdentityToken types.StringValue - WebIdentityTokenFile types.StringValue -} - -type IgnoreTags struct { - Metadata types.Metadata - Keys types.StringValueList - KeyPrefixes types.StringValueList -} - -type DefaultTags struct { - Metadata types.Metadata - Tags types.MapValue -} diff --git a/pkg/iac/providers/aws/rds/classic.go b/pkg/iac/providers/aws/rds/classic.go deleted file mode 100755 index 41be09b743ef..000000000000 --- a/pkg/iac/providers/aws/rds/classic.go +++ /dev/null @@ -1,13 +0,0 @@ -package rds - -import ( - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Classic struct { - DBSecurityGroups []DBSecurityGroup -} - -type DBSecurityGroup struct { - Metadata types.Metadata -} diff --git a/pkg/iac/providers/aws/rds/rds.go b/pkg/iac/providers/aws/rds/rds.go deleted file mode 100755 index d5a55b188e63..000000000000 --- a/pkg/iac/providers/aws/rds/rds.go +++ /dev/null @@ -1,127 +0,0 @@ -package rds - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type RDS struct { - Instances []Instance - Clusters []Cluster - Classic Classic - Snapshots []Snapshots - ParameterGroups []ParameterGroups -} - -type Instance struct { - Metadata iacTypes.Metadata - BackupRetentionPeriodDays iacTypes.IntValue - ReplicationSourceARN iacTypes.StringValue - PerformanceInsights PerformanceInsights - Encryption Encryption - PublicAccess iacTypes.BoolValue - Engine iacTypes.StringValue - IAMAuthEnabled iacTypes.BoolValue - DeletionProtection iacTypes.BoolValue - DBInstanceArn iacTypes.StringValue - StorageEncrypted iacTypes.BoolValue - DBInstanceIdentifier iacTypes.StringValue - DBParameterGroups []DBParameterGroupsList - TagList []TagList - EnabledCloudwatchLogsExports []iacTypes.StringValue - EngineVersion iacTypes.StringValue - AutoMinorVersionUpgrade iacTypes.BoolValue - MultiAZ iacTypes.BoolValue - PubliclyAccessible iacTypes.BoolValue - LatestRestorableTime iacTypes.TimeValue - ReadReplicaDBInstanceIdentifiers []iacTypes.StringValue -} - -type Cluster struct { - Metadata iacTypes.Metadata - BackupRetentionPeriodDays iacTypes.IntValue - ReplicationSourceARN iacTypes.StringValue - PerformanceInsights PerformanceInsights - Instances []ClusterInstance - Encryption Encryption - PublicAccess iacTypes.BoolValue - Engine iacTypes.StringValue - LatestRestorableTime iacTypes.TimeValue - AvailabilityZones []iacTypes.StringValue - DeletionProtection iacTypes.BoolValue - SkipFinalSnapshot iacTypes.BoolValue -} - -type Snapshots struct { - Metadata iacTypes.Metadata - DBSnapshotIdentifier iacTypes.StringValue - DBSnapshotArn iacTypes.StringValue - Encrypted iacTypes.BoolValue - KmsKeyId iacTypes.StringValue - SnapshotAttributes []DBSnapshotAttributes -} - -type Parameters struct { - Metadata iacTypes.Metadata - ParameterName iacTypes.StringValue - ParameterValue iacTypes.StringValue -} - -type ParameterGroups struct { - Metadata iacTypes.Metadata - DBParameterGroupName iacTypes.StringValue - DBParameterGroupFamily iacTypes.StringValue - Parameters []Parameters -} - -type DBSnapshotAttributes struct { - Metadata iacTypes.Metadata - AttributeValues []iacTypes.StringValue -} - -const ( - EngineAurora = "aurora" - EngineAuroraMysql = "aurora-mysql" - EngineAuroraPostgresql = "aurora-postgresql" - EngineMySQL = "mysql" - EnginePostgres = "postgres" - EngineCustomOracleEE = "custom-oracle-ee" - EngineOracleEE = "oracle-ee" - EngineOracleEECDB = "oracle-ee-cdb" - EngineOracleSE2 = "oracle-se2" - EngineOracleSE2CDB = "oracle-se2-cdb" - EngineSQLServerEE = "sqlserver-ee" - EngineSQLServerSE = "sqlserver-se" - EngineSQLServerEX = "sqlserver-ex" - EngineSQLServerWEB = "sqlserver-web" - EngineMariaDB = "mariadb" - EngineCustomSQLServerEE = "custom-sqlserver-ee" - EngineCustomSQLServerSE = "custom-sqlserver-se" - EngineCustomSQLServerWEB = "custom-sqlserver-web" -) - -type Encryption struct { - Metadata iacTypes.Metadata - EncryptStorage iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} - -type ClusterInstance struct { - Instance - ClusterIdentifier iacTypes.StringValue -} - -type PerformanceInsights struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} - -type DBParameterGroupsList struct { - Metadata iacTypes.Metadata - DBParameterGroupName iacTypes.StringValue - KMSKeyID iacTypes.StringValue -} - -type TagList struct { - Metadata iacTypes.Metadata -} diff --git a/pkg/iac/providers/aws/redshift/redshift.go b/pkg/iac/providers/aws/redshift/redshift.go deleted file mode 100755 index cdafaa290525..000000000000 --- a/pkg/iac/providers/aws/redshift/redshift.go +++ /dev/null @@ -1,55 +0,0 @@ -package redshift - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Redshift struct { - Clusters []Cluster - ReservedNodes []ReservedNode - ClusterParameters []ClusterParameter - SecurityGroups []SecurityGroup -} - -type SecurityGroup struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue -} - -type ReservedNode struct { - Metadata iacTypes.Metadata - NodeType iacTypes.StringValue -} - -type ClusterParameter struct { - Metadata iacTypes.Metadata - ParameterName iacTypes.StringValue - ParameterValue iacTypes.StringValue -} - -type Cluster struct { - Metadata iacTypes.Metadata - ClusterIdentifier iacTypes.StringValue - NodeType iacTypes.StringValue - VpcId iacTypes.StringValue - NumberOfNodes iacTypes.IntValue - PubliclyAccessible iacTypes.BoolValue - AllowVersionUpgrade iacTypes.BoolValue - MasterUsername iacTypes.StringValue - AutomatedSnapshotRetentionPeriod iacTypes.IntValue - LoggingEnabled iacTypes.BoolValue - EndPoint EndPoint - Encryption Encryption - SubnetGroupName iacTypes.StringValue -} - -type EndPoint struct { - Metadata iacTypes.Metadata - Port iacTypes.IntValue -} - -type Encryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/s3/bucket.go b/pkg/iac/providers/aws/s3/bucket.go deleted file mode 100755 index ccabe6974c99..000000000000 --- a/pkg/iac/providers/aws/s3/bucket.go +++ /dev/null @@ -1,80 +0,0 @@ -package s3 - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Bucket struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - PublicAccessBlock *PublicAccessBlock - BucketPolicies []iam.Policy - Encryption Encryption - Versioning Versioning - Logging Logging - ACL iacTypes.StringValue - Grants []Grant - BucketLocation iacTypes.StringValue - AccelerateConfigurationStatus iacTypes.StringValue - LifecycleConfiguration []Rules - Objects []Contents - Website *Website -} - -func (b *Bucket) HasPublicExposureACL() bool { - for _, publicACL := range []string{"public-read", "public-read-write", "website", "authenticated-read"} { - if b.ACL.EqualTo(publicACL) { - // if there is a public access block, check the public ACL blocks - if b.PublicAccessBlock != nil && b.PublicAccessBlock.Metadata.IsManaged() { - return b.PublicAccessBlock.IgnorePublicACLs.IsFalse() && b.PublicAccessBlock.BlockPublicACLs.IsFalse() - } - return true - } - } - return false -} - -type Logging struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - TargetBucket iacTypes.StringValue -} - -type Versioning struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - MFADelete iacTypes.BoolValue -} - -type Encryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - Algorithm iacTypes.StringValue - KMSKeyId iacTypes.StringValue -} - -type Rules struct { - Metadata iacTypes.Metadata - Status iacTypes.StringValue -} - -type Contents struct { - Metadata iacTypes.Metadata -} - -type Website struct { - Metadata iacTypes.Metadata -} - -type Grant struct { - Metadata iacTypes.Metadata - Grantee Grantee - Permissions iacTypes.StringValueList -} - -type Grantee struct { - Metadata iacTypes.Metadata - URI iacTypes.StringValue - Type iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/s3/bucket_public_access_block.go b/pkg/iac/providers/aws/s3/bucket_public_access_block.go deleted file mode 100755 index 573cf6a1c0f7..000000000000 --- a/pkg/iac/providers/aws/s3/bucket_public_access_block.go +++ /dev/null @@ -1,23 +0,0 @@ -package s3 - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type PublicAccessBlock struct { - Metadata iacTypes.Metadata - BlockPublicACLs iacTypes.BoolValue - BlockPublicPolicy iacTypes.BoolValue - IgnorePublicACLs iacTypes.BoolValue - RestrictPublicBuckets iacTypes.BoolValue -} - -func NewPublicAccessBlock(metadata iacTypes.Metadata) PublicAccessBlock { - return PublicAccessBlock{ - Metadata: metadata, - BlockPublicPolicy: iacTypes.BoolDefault(false, metadata), - BlockPublicACLs: iacTypes.BoolDefault(false, metadata), - IgnorePublicACLs: iacTypes.BoolDefault(false, metadata), - RestrictPublicBuckets: iacTypes.BoolDefault(false, metadata), - } -} diff --git a/pkg/iac/providers/aws/s3/s3.go b/pkg/iac/providers/aws/s3/s3.go deleted file mode 100755 index 230269a9e660..000000000000 --- a/pkg/iac/providers/aws/s3/s3.go +++ /dev/null @@ -1,5 +0,0 @@ -package s3 - -type S3 struct { - Buckets []Bucket -} diff --git a/pkg/iac/providers/aws/sam/api.go b/pkg/iac/providers/aws/sam/api.go deleted file mode 100644 index ba716f3e4d92..000000000000 --- a/pkg/iac/providers/aws/sam/api.go +++ /dev/null @@ -1,38 +0,0 @@ -package sam - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type API struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - TracingEnabled iacTypes.BoolValue - DomainConfiguration DomainConfiguration - AccessLogging AccessLogging - RESTMethodSettings RESTMethodSettings -} - -type ApiAuth struct { - Metadata iacTypes.Metadata - ApiKeyRequired iacTypes.BoolValue -} - -type AccessLogging struct { - Metadata iacTypes.Metadata - CloudwatchLogGroupARN iacTypes.StringValue -} - -type DomainConfiguration struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - SecurityPolicy iacTypes.StringValue -} - -type RESTMethodSettings struct { - Metadata iacTypes.Metadata - CacheDataEncrypted iacTypes.BoolValue - LoggingEnabled iacTypes.BoolValue - DataTraceEnabled iacTypes.BoolValue - MetricsEnabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/sam/application.go b/pkg/iac/providers/aws/sam/application.go deleted file mode 100644 index 99fb7b7cac5b..000000000000 --- a/pkg/iac/providers/aws/sam/application.go +++ /dev/null @@ -1,17 +0,0 @@ -package sam - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Application struct { - Metadata iacTypes.Metadata - LocationPath iacTypes.StringValue - Location Location -} - -type Location struct { - Metadata iacTypes.Metadata - ApplicationID iacTypes.StringValue - SemanticVersion iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/sam/function.go b/pkg/iac/providers/aws/sam/function.go deleted file mode 100644 index 5fb47afd2b66..000000000000 --- a/pkg/iac/providers/aws/sam/function.go +++ /dev/null @@ -1,25 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Function struct { - Metadata iacTypes.Metadata - FunctionName iacTypes.StringValue - Tracing iacTypes.StringValue - ManagedPolicies []iacTypes.StringValue - Policies []iam.Policy -} - -const ( - TracingModePassThrough = "PassThrough" - TracingModeActive = "Active" -) - -type Permission struct { - Metadata iacTypes.Metadata - Principal iacTypes.StringValue - SourceARN iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/sam/http_api.go b/pkg/iac/providers/aws/sam/http_api.go deleted file mode 100644 index eb1b488bc1bb..000000000000 --- a/pkg/iac/providers/aws/sam/http_api.go +++ /dev/null @@ -1,20 +0,0 @@ -package sam - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type HttpAPI struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - AccessLogging AccessLogging - DefaultRouteSettings RouteSettings - DomainConfiguration DomainConfiguration -} - -type RouteSettings struct { - Metadata iacTypes.Metadata - LoggingEnabled iacTypes.BoolValue - DataTraceEnabled iacTypes.BoolValue - DetailedMetricsEnabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/sam/sam.go b/pkg/iac/providers/aws/sam/sam.go deleted file mode 100644 index ed75777d8053..000000000000 --- a/pkg/iac/providers/aws/sam/sam.go +++ /dev/null @@ -1,10 +0,0 @@ -package sam - -type SAM struct { - APIs []API - Applications []Application - Functions []Function - HttpAPIs []HttpAPI - SimpleTables []SimpleTable - StateMachines []StateMachine -} diff --git a/pkg/iac/providers/aws/sam/state_machine.go b/pkg/iac/providers/aws/sam/state_machine.go deleted file mode 100644 index caee979436de..000000000000 --- a/pkg/iac/providers/aws/sam/state_machine.go +++ /dev/null @@ -1,25 +0,0 @@ -package sam - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type StateMachine struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - LoggingConfiguration LoggingConfiguration - ManagedPolicies []iacTypes.StringValue - Policies []iam.Policy - Tracing TracingConfiguration -} - -type LoggingConfiguration struct { - Metadata iacTypes.Metadata - LoggingEnabled iacTypes.BoolValue -} - -type TracingConfiguration struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/sam/table.go b/pkg/iac/providers/aws/sam/table.go deleted file mode 100644 index 33179771ee66..000000000000 --- a/pkg/iac/providers/aws/sam/table.go +++ /dev/null @@ -1,18 +0,0 @@ -package sam - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SimpleTable struct { - Metadata iacTypes.Metadata - TableName iacTypes.StringValue - SSESpecification SSESpecification -} - -type SSESpecification struct { - Metadata iacTypes.Metadata - - Enabled iacTypes.BoolValue - KMSMasterKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/sns/sns.go b/pkg/iac/providers/aws/sns/sns.go deleted file mode 100755 index 49c2d370d56f..000000000000 --- a/pkg/iac/providers/aws/sns/sns.go +++ /dev/null @@ -1,31 +0,0 @@ -package sns - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SNS struct { - Topics []Topic -} - -func NewTopic(arn string, metadata iacTypes.Metadata) *Topic { - return &Topic{ - Metadata: metadata, - ARN: iacTypes.String(arn, metadata), - Encryption: Encryption{ - Metadata: metadata, - KMSKeyID: iacTypes.StringDefault("", metadata), - }, - } -} - -type Topic struct { - Metadata iacTypes.Metadata - ARN iacTypes.StringValue - Encryption Encryption -} - -type Encryption struct { - Metadata iacTypes.Metadata - KMSKeyID iacTypes.StringValue -} diff --git a/pkg/iac/providers/aws/sqs/sqs.go b/pkg/iac/providers/aws/sqs/sqs.go deleted file mode 100755 index edafcb888a22..000000000000 --- a/pkg/iac/providers/aws/sqs/sqs.go +++ /dev/null @@ -1,23 +0,0 @@ -package sqs - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SQS struct { - Queues []Queue -} - -type Queue struct { - Metadata iacTypes.Metadata - QueueURL iacTypes.StringValue - Encryption Encryption - Policies []iam.Policy -} - -type Encryption struct { - Metadata iacTypes.Metadata - KMSKeyID iacTypes.StringValue - ManagedEncryption iacTypes.BoolValue -} diff --git a/pkg/iac/providers/aws/ssm/ssm.go b/pkg/iac/providers/aws/ssm/ssm.go deleted file mode 100755 index 725e099f2b8d..000000000000 --- a/pkg/iac/providers/aws/ssm/ssm.go +++ /dev/null @@ -1,16 +0,0 @@ -package ssm - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SSM struct { - Secrets []Secret -} - -type Secret struct { - Metadata iacTypes.Metadata - KMSKeyID iacTypes.StringValue -} - -const DefaultKMSKeyID = "alias/aws/secretsmanager" diff --git a/pkg/iac/providers/aws/workspaces/workspaces.go b/pkg/iac/providers/aws/workspaces/workspaces.go deleted file mode 100755 index beaf56eef645..000000000000 --- a/pkg/iac/providers/aws/workspaces/workspaces.go +++ /dev/null @@ -1,25 +0,0 @@ -package workspaces - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type WorkSpaces struct { - WorkSpaces []WorkSpace -} - -type WorkSpace struct { - Metadata iacTypes.Metadata - RootVolume Volume - UserVolume Volume -} - -type Volume struct { - Metadata iacTypes.Metadata - Encryption Encryption -} - -type Encryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/azure/appservice/appservice.go b/pkg/iac/providers/azure/appservice/appservice.go deleted file mode 100755 index 990adf98183a..000000000000 --- a/pkg/iac/providers/azure/appservice/appservice.go +++ /dev/null @@ -1,30 +0,0 @@ -package appservice - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type AppService struct { - Services []Service - FunctionApps []FunctionApp -} - -type Service struct { - Metadata iacTypes.Metadata - EnableClientCert iacTypes.BoolValue - Identity struct { - Type iacTypes.StringValue - } - Authentication struct { - Enabled iacTypes.BoolValue - } - Site struct { - EnableHTTP2 iacTypes.BoolValue - MinimumTLSVersion iacTypes.StringValue - } -} - -type FunctionApp struct { - Metadata iacTypes.Metadata - HTTPSOnly iacTypes.BoolValue -} diff --git a/pkg/iac/providers/azure/authorization/authorization.go b/pkg/iac/providers/azure/authorization/authorization.go deleted file mode 100755 index fee924724d3c..000000000000 --- a/pkg/iac/providers/azure/authorization/authorization.go +++ /dev/null @@ -1,20 +0,0 @@ -package authorization - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Authorization struct { - RoleDefinitions []RoleDefinition -} - -type RoleDefinition struct { - Metadata iacTypes.Metadata - Permissions []Permission - AssignableScopes []iacTypes.StringValue -} - -type Permission struct { - Metadata iacTypes.Metadata - Actions []iacTypes.StringValue -} diff --git a/pkg/iac/providers/azure/azure.go b/pkg/iac/providers/azure/azure.go deleted file mode 100755 index 07774647a454..000000000000 --- a/pkg/iac/providers/azure/azure.go +++ /dev/null @@ -1,33 +0,0 @@ -package azure - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/authorization" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/compute" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/database" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datafactory" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/datalake" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/keyvault" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/monitor" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/network" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/securitycenter" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/synapse" -) - -type Azure struct { - AppService appservice.AppService - Authorization authorization.Authorization - Compute compute.Compute - Container container.Container - Database database.Database - DataFactory datafactory.DataFactory - DataLake datalake.DataLake - KeyVault keyvault.KeyVault - Monitor monitor.Monitor - Network network.Network - SecurityCenter securitycenter.SecurityCenter - Storage storage.Storage - Synapse synapse.Synapse -} diff --git a/pkg/iac/providers/azure/compute/compute.go b/pkg/iac/providers/azure/compute/compute.go deleted file mode 100755 index d45105985afa..000000000000 --- a/pkg/iac/providers/azure/compute/compute.go +++ /dev/null @@ -1,42 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Compute struct { - LinuxVirtualMachines []LinuxVirtualMachine - WindowsVirtualMachines []WindowsVirtualMachine - ManagedDisks []ManagedDisk -} - -type VirtualMachine struct { - Metadata iacTypes.Metadata - CustomData iacTypes.StringValue // NOT base64 encoded -} - -type LinuxVirtualMachine struct { - Metadata iacTypes.Metadata - VirtualMachine - OSProfileLinuxConfig OSProfileLinuxConfig -} - -type WindowsVirtualMachine struct { - Metadata iacTypes.Metadata - VirtualMachine -} - -type OSProfileLinuxConfig struct { - Metadata iacTypes.Metadata - DisablePasswordAuthentication iacTypes.BoolValue -} - -type ManagedDisk struct { - Metadata iacTypes.Metadata - Encryption Encryption -} - -type Encryption struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/azure/container/container.go b/pkg/iac/providers/azure/container/container.go deleted file mode 100755 index 0af940d4ebf7..000000000000 --- a/pkg/iac/providers/azure/container/container.go +++ /dev/null @@ -1,38 +0,0 @@ -package container - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Container struct { - KubernetesClusters []KubernetesCluster -} - -type KubernetesCluster struct { - Metadata iacTypes.Metadata - NetworkProfile NetworkProfile - EnablePrivateCluster iacTypes.BoolValue - APIServerAuthorizedIPRanges []iacTypes.StringValue - AddonProfile AddonProfile - RoleBasedAccessControl RoleBasedAccessControl -} - -type RoleBasedAccessControl struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type AddonProfile struct { - Metadata iacTypes.Metadata - OMSAgent OMSAgent -} - -type OMSAgent struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type NetworkProfile struct { - Metadata iacTypes.Metadata - NetworkPolicy iacTypes.StringValue // "", "calico", "azure" -} diff --git a/pkg/iac/providers/azure/database/database.go b/pkg/iac/providers/azure/database/database.go deleted file mode 100755 index 34ac63d8f129..000000000000 --- a/pkg/iac/providers/azure/database/database.go +++ /dev/null @@ -1,68 +0,0 @@ -package database - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Database struct { - MSSQLServers []MSSQLServer - MariaDBServers []MariaDBServer - MySQLServers []MySQLServer - PostgreSQLServers []PostgreSQLServer -} - -type MariaDBServer struct { - Metadata iacTypes.Metadata - Server -} - -type MySQLServer struct { - Metadata iacTypes.Metadata - Server -} - -type PostgreSQLServer struct { - Metadata iacTypes.Metadata - Server - Config PostgresSQLConfig -} - -type PostgresSQLConfig struct { - Metadata iacTypes.Metadata - LogCheckpoints iacTypes.BoolValue - ConnectionThrottling iacTypes.BoolValue - LogConnections iacTypes.BoolValue -} - -type Server struct { - Metadata iacTypes.Metadata - EnableSSLEnforcement iacTypes.BoolValue - MinimumTLSVersion iacTypes.StringValue - EnablePublicNetworkAccess iacTypes.BoolValue - FirewallRules []FirewallRule -} - -type MSSQLServer struct { - Metadata iacTypes.Metadata - Server - ExtendedAuditingPolicies []ExtendedAuditingPolicy - SecurityAlertPolicies []SecurityAlertPolicy -} - -type SecurityAlertPolicy struct { - Metadata iacTypes.Metadata - EmailAddresses []iacTypes.StringValue - DisabledAlerts []iacTypes.StringValue - EmailAccountAdmins iacTypes.BoolValue -} - -type ExtendedAuditingPolicy struct { - Metadata iacTypes.Metadata - RetentionInDays iacTypes.IntValue -} - -type FirewallRule struct { - Metadata iacTypes.Metadata - StartIP iacTypes.StringValue - EndIP iacTypes.StringValue -} diff --git a/pkg/iac/providers/azure/datafactory/datafactory.go b/pkg/iac/providers/azure/datafactory/datafactory.go deleted file mode 100755 index c8f0e91d010b..000000000000 --- a/pkg/iac/providers/azure/datafactory/datafactory.go +++ /dev/null @@ -1,14 +0,0 @@ -package datafactory - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DataFactory struct { - DataFactories []Factory -} - -type Factory struct { - Metadata iacTypes.Metadata - EnablePublicNetwork iacTypes.BoolValue -} diff --git a/pkg/iac/providers/azure/datalake/datalake.go b/pkg/iac/providers/azure/datalake/datalake.go deleted file mode 100755 index a36f9a12c94a..000000000000 --- a/pkg/iac/providers/azure/datalake/datalake.go +++ /dev/null @@ -1,14 +0,0 @@ -package datalake - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DataLake struct { - Stores []Store -} - -type Store struct { - Metadata iacTypes.Metadata - EnableEncryption iacTypes.BoolValue -} diff --git a/pkg/iac/providers/azure/keyvault/keyvault.go b/pkg/iac/providers/azure/keyvault/keyvault.go deleted file mode 100755 index 8c263fa413f2..000000000000 --- a/pkg/iac/providers/azure/keyvault/keyvault.go +++ /dev/null @@ -1,34 +0,0 @@ -package keyvault - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type KeyVault struct { - Vaults []Vault -} - -type Vault struct { - Metadata iacTypes.Metadata - Secrets []Secret - Keys []Key - EnablePurgeProtection iacTypes.BoolValue - SoftDeleteRetentionDays iacTypes.IntValue - NetworkACLs NetworkACLs -} - -type NetworkACLs struct { - Metadata iacTypes.Metadata - DefaultAction iacTypes.StringValue -} - -type Key struct { - Metadata iacTypes.Metadata - ExpiryDate iacTypes.TimeValue -} - -type Secret struct { - Metadata iacTypes.Metadata - ContentType iacTypes.StringValue - ExpiryDate iacTypes.TimeValue -} diff --git a/pkg/iac/providers/azure/monitor/monitor.go b/pkg/iac/providers/azure/monitor/monitor.go deleted file mode 100755 index e8ba51e40338..000000000000 --- a/pkg/iac/providers/azure/monitor/monitor.go +++ /dev/null @@ -1,22 +0,0 @@ -package monitor - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Monitor struct { - LogProfiles []LogProfile -} - -type LogProfile struct { - Metadata iacTypes.Metadata - RetentionPolicy RetentionPolicy - Categories []iacTypes.StringValue - Locations []iacTypes.StringValue -} - -type RetentionPolicy struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - Days iacTypes.IntValue -} diff --git a/pkg/iac/providers/azure/network/network.go b/pkg/iac/providers/azure/network/network.go deleted file mode 100755 index 4fdc56e44e86..000000000000 --- a/pkg/iac/providers/azure/network/network.go +++ /dev/null @@ -1,47 +0,0 @@ -package network - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Network struct { - SecurityGroups []SecurityGroup - NetworkWatcherFlowLogs []NetworkWatcherFlowLog -} - -type SecurityGroup struct { - Metadata iacTypes.Metadata - Rules []SecurityGroupRule -} - -type SecurityGroupRule struct { - Metadata iacTypes.Metadata - Outbound iacTypes.BoolValue - Allow iacTypes.BoolValue - SourceAddresses []iacTypes.StringValue - SourcePorts []PortRange - DestinationAddresses []iacTypes.StringValue - DestinationPorts []PortRange - Protocol iacTypes.StringValue -} - -type PortRange struct { - Metadata iacTypes.Metadata - Start iacTypes.IntValue - End iacTypes.IntValue -} - -func (r PortRange) Includes(port int) bool { - return port >= r.Start.Value() && port <= r.End.Value() -} - -type NetworkWatcherFlowLog struct { - Metadata iacTypes.Metadata - RetentionPolicy RetentionPolicy -} - -type RetentionPolicy struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - Days iacTypes.IntValue -} diff --git a/pkg/iac/providers/azure/securitycenter/securitycenter.go b/pkg/iac/providers/azure/securitycenter/securitycenter.go deleted file mode 100755 index 49dcd95d1ef0..000000000000 --- a/pkg/iac/providers/azure/securitycenter/securitycenter.go +++ /dev/null @@ -1,26 +0,0 @@ -package securitycenter - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SecurityCenter struct { - Contacts []Contact - Subscriptions []SubscriptionPricing -} - -type Contact struct { - Metadata iacTypes.Metadata - EnableAlertNotifications iacTypes.BoolValue - Phone iacTypes.StringValue -} - -const ( - TierFree = "Free" - TierStandard = "Standard" -) - -type SubscriptionPricing struct { - Metadata iacTypes.Metadata - Tier iacTypes.StringValue -} diff --git a/pkg/iac/providers/azure/storage/storage.go b/pkg/iac/providers/azure/storage/storage.go deleted file mode 100755 index ce86ec8698cc..000000000000 --- a/pkg/iac/providers/azure/storage/storage.go +++ /dev/null @@ -1,47 +0,0 @@ -package storage - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Storage struct { - Accounts []Account -} - -type Account struct { - Metadata iacTypes.Metadata - NetworkRules []NetworkRule - EnforceHTTPS iacTypes.BoolValue - Containers []Container - QueueProperties QueueProperties - MinimumTLSVersion iacTypes.StringValue - Queues []Queue - PublicNetworkAccess iacTypes.BoolValue -} - -type Queue struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue -} - -type QueueProperties struct { - Metadata iacTypes.Metadata - EnableLogging iacTypes.BoolValue -} - -type NetworkRule struct { - Metadata iacTypes.Metadata - Bypass []iacTypes.StringValue - AllowByDefault iacTypes.BoolValue -} - -const ( - PublicAccessOff = "off" - PublicAccessBlob = "blob" - PublicAccessContainer = "container" -) - -type Container struct { - Metadata iacTypes.Metadata - PublicAccess iacTypes.StringValue -} diff --git a/pkg/iac/providers/azure/synapse/synapse.go b/pkg/iac/providers/azure/synapse/synapse.go deleted file mode 100755 index dc72175d7106..000000000000 --- a/pkg/iac/providers/azure/synapse/synapse.go +++ /dev/null @@ -1,14 +0,0 @@ -package synapse - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Synapse struct { - Workspaces []Workspace -} - -type Workspace struct { - Metadata iacTypes.Metadata - EnableManagedVirtualNetwork iacTypes.BoolValue -} diff --git a/pkg/iac/providers/cloudstack/cloudstack.go b/pkg/iac/providers/cloudstack/cloudstack.go deleted file mode 100755 index d514eae513a3..000000000000 --- a/pkg/iac/providers/cloudstack/cloudstack.go +++ /dev/null @@ -1,9 +0,0 @@ -package cloudstack - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/cloudstack/compute" -) - -type CloudStack struct { - Compute compute.Compute -} diff --git a/pkg/iac/providers/cloudstack/compute/compute.go b/pkg/iac/providers/cloudstack/compute/compute.go deleted file mode 100755 index cbfa55ba63d4..000000000000 --- a/pkg/iac/providers/cloudstack/compute/compute.go +++ /dev/null @@ -1,14 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Compute struct { - Instances []Instance -} - -type Instance struct { - Metadata iacTypes.Metadata - UserData iacTypes.StringValue // not b64 encoded pls -} diff --git a/pkg/iac/providers/digitalocean/compute/compute.go b/pkg/iac/providers/digitalocean/compute/compute.go deleted file mode 100755 index a0b0fe24761a..000000000000 --- a/pkg/iac/providers/digitalocean/compute/compute.go +++ /dev/null @@ -1,50 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Compute struct { - Firewalls []Firewall - LoadBalancers []LoadBalancer - Droplets []Droplet - KubernetesClusters []KubernetesCluster -} - -type Firewall struct { - Metadata iacTypes.Metadata - OutboundRules []OutboundFirewallRule - InboundRules []InboundFirewallRule -} - -type KubernetesCluster struct { - Metadata iacTypes.Metadata - SurgeUpgrade iacTypes.BoolValue - AutoUpgrade iacTypes.BoolValue -} - -type LoadBalancer struct { - Metadata iacTypes.Metadata - ForwardingRules []ForwardingRule - RedirectHttpToHttps iacTypes.BoolValue -} - -type ForwardingRule struct { - Metadata iacTypes.Metadata - EntryProtocol iacTypes.StringValue -} - -type OutboundFirewallRule struct { - Metadata iacTypes.Metadata - DestinationAddresses []iacTypes.StringValue -} - -type InboundFirewallRule struct { - Metadata iacTypes.Metadata - SourceAddresses []iacTypes.StringValue -} - -type Droplet struct { - Metadata iacTypes.Metadata - SSHKeys []iacTypes.StringValue -} diff --git a/pkg/iac/providers/digitalocean/digitalocean.go b/pkg/iac/providers/digitalocean/digitalocean.go deleted file mode 100755 index d56240646279..000000000000 --- a/pkg/iac/providers/digitalocean/digitalocean.go +++ /dev/null @@ -1,11 +0,0 @@ -package digitalocean - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/digitalocean/compute" - "github.com/aquasecurity/trivy/pkg/iac/providers/digitalocean/spaces" -) - -type DigitalOcean struct { - Compute compute.Compute - Spaces spaces.Spaces -} diff --git a/pkg/iac/providers/digitalocean/spaces/spaces.go b/pkg/iac/providers/digitalocean/spaces/spaces.go deleted file mode 100755 index 2936e469da12..000000000000 --- a/pkg/iac/providers/digitalocean/spaces/spaces.go +++ /dev/null @@ -1,28 +0,0 @@ -package spaces - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Spaces struct { - Buckets []Bucket -} - -type Bucket struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Objects []Object - ACL iacTypes.StringValue - ForceDestroy iacTypes.BoolValue - Versioning Versioning -} - -type Versioning struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type Object struct { - Metadata iacTypes.Metadata - ACL iacTypes.StringValue -} diff --git a/pkg/iac/providers/dockerfile/dockerfile.go b/pkg/iac/providers/dockerfile/dockerfile.go deleted file mode 100644 index 84b10c42fb7e..000000000000 --- a/pkg/iac/providers/dockerfile/dockerfile.go +++ /dev/null @@ -1,61 +0,0 @@ -package dockerfile - -import ( - "reflect" - - "github.com/aquasecurity/trivy/pkg/iac/rego/convert" -) - -// NOTE: we are currently preserving mixed case json here for backward compatibility - -// Dockerfile represents a parsed Dockerfile -type Dockerfile struct { - Stages []Stage -} - -type Stage struct { - Name string - Commands []Command -} - -func (d Dockerfile) ToRego() any { - return map[string]any{ - "Stages": convert.SliceToRego(reflect.ValueOf(d.Stages)), - } -} - -func (s Stage) ToRego() any { - return map[string]any{ - "Name": s.Name, - "Commands": convert.SliceToRego(reflect.ValueOf(s.Commands)), - } -} - -// Command is the struct for each dockerfile command -type Command struct { - Cmd string - SubCmd string - Flags []string - Value []string - Original string - JSON bool - Stage int - Path string - StartLine int - EndLine int -} - -func (c Command) ToRego() any { - return map[string]any{ - "Cmd": c.Cmd, - "SubCmd": c.SubCmd, - "Flags": c.Flags, - "Value": c.Value, - "Original": c.Original, - "JSON": c.JSON, - "Stage": c.Stage, - "Path": c.Path, - "StartLine": c.StartLine, - "EndLine": c.EndLine, - } -} diff --git a/pkg/iac/providers/github/actions.go b/pkg/iac/providers/github/actions.go deleted file mode 100644 index 0b28269e0c80..000000000000 --- a/pkg/iac/providers/github/actions.go +++ /dev/null @@ -1,19 +0,0 @@ -package github - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Action struct { - Metadata iacTypes.Metadata - EnvironmentSecrets []EnvironmentSecret -} - -type EnvironmentSecret struct { - Metadata iacTypes.Metadata - Repository iacTypes.StringValue - Environment iacTypes.StringValue - SecretName iacTypes.StringValue - PlainTextValue iacTypes.StringValue - EncryptedValue iacTypes.StringValue -} diff --git a/pkg/iac/providers/github/branch_protections.go b/pkg/iac/providers/github/branch_protections.go deleted file mode 100755 index 5d9f59599457..000000000000 --- a/pkg/iac/providers/github/branch_protections.go +++ /dev/null @@ -1,14 +0,0 @@ -package github - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type BranchProtection struct { - Metadata iacTypes.Metadata - RequireSignedCommits iacTypes.BoolValue -} - -func (b BranchProtection) RequiresSignedCommits() bool { - return b.RequireSignedCommits.IsTrue() -} diff --git a/pkg/iac/providers/github/github.go b/pkg/iac/providers/github/github.go deleted file mode 100755 index 449f94cecc30..000000000000 --- a/pkg/iac/providers/github/github.go +++ /dev/null @@ -1,7 +0,0 @@ -package github - -type GitHub struct { - Repositories []Repository - EnvironmentSecrets []EnvironmentSecret - BranchProtections []BranchProtection -} diff --git a/pkg/iac/providers/github/repositories.go b/pkg/iac/providers/github/repositories.go deleted file mode 100755 index 0fc4c96080d2..000000000000 --- a/pkg/iac/providers/github/repositories.go +++ /dev/null @@ -1,16 +0,0 @@ -package github - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Repository struct { - Metadata iacTypes.Metadata - Public iacTypes.BoolValue - VulnerabilityAlerts iacTypes.BoolValue - Archived iacTypes.BoolValue -} - -func (r Repository) IsArchived() bool { - return r.Archived.IsTrue() -} diff --git a/pkg/iac/providers/google/bigquery/bigquery.go b/pkg/iac/providers/google/bigquery/bigquery.go deleted file mode 100755 index 9a9bfb054dde..000000000000 --- a/pkg/iac/providers/google/bigquery/bigquery.go +++ /dev/null @@ -1,26 +0,0 @@ -package bigquery - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type BigQuery struct { - Datasets []Dataset -} - -type Dataset struct { - Metadata iacTypes.Metadata - ID iacTypes.StringValue - AccessGrants []AccessGrant -} - -const ( - SpecialGroupAllAuthenticatedUsers = "allAuthenticatedUsers" -) - -type AccessGrant struct { - Metadata iacTypes.Metadata - Role iacTypes.StringValue - Domain iacTypes.StringValue - SpecialGroup iacTypes.StringValue -} diff --git a/pkg/iac/providers/google/compute/compute.go b/pkg/iac/providers/google/compute/compute.go deleted file mode 100755 index ffa9db257bad..000000000000 --- a/pkg/iac/providers/google/compute/compute.go +++ /dev/null @@ -1,9 +0,0 @@ -package compute - -type Compute struct { - Disks []Disk - Networks []Network - SSLPolicies []SSLPolicy - ProjectMetadata ProjectMetadata - Instances []Instance -} diff --git a/pkg/iac/providers/google/compute/disk.go b/pkg/iac/providers/google/compute/disk.go deleted file mode 100755 index b636cb135651..000000000000 --- a/pkg/iac/providers/google/compute/disk.go +++ /dev/null @@ -1,17 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Disk struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Encryption DiskEncryption -} - -type DiskEncryption struct { - Metadata iacTypes.Metadata - RawKey iacTypes.BytesValue - KMSKeyLink iacTypes.StringValue -} diff --git a/pkg/iac/providers/google/compute/firewall.go b/pkg/iac/providers/google/compute/firewall.go deleted file mode 100755 index 19f4044cdb72..000000000000 --- a/pkg/iac/providers/google/compute/firewall.go +++ /dev/null @@ -1,40 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Firewall struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - IngressRules []IngressRule - EgressRules []EgressRule - SourceTags []iacTypes.StringValue - TargetTags []iacTypes.StringValue -} - -type FirewallRule struct { - Metadata iacTypes.Metadata - Enforced iacTypes.BoolValue - IsAllow iacTypes.BoolValue - Protocol iacTypes.StringValue - Ports []PortRange -} - -type PortRange struct { - Metadata iacTypes.Metadata - Start iacTypes.IntValue - End iacTypes.IntValue -} - -type IngressRule struct { - Metadata iacTypes.Metadata - FirewallRule - SourceRanges []iacTypes.StringValue -} - -type EgressRule struct { - Metadata iacTypes.Metadata - FirewallRule - DestinationRanges []iacTypes.StringValue -} diff --git a/pkg/iac/providers/google/compute/instance.go b/pkg/iac/providers/google/compute/instance.go deleted file mode 100755 index d5189945f3b6..000000000000 --- a/pkg/iac/providers/google/compute/instance.go +++ /dev/null @@ -1,41 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Instance struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - NetworkInterfaces []NetworkInterface - ShieldedVM ShieldedVMConfig - ServiceAccount ServiceAccount - CanIPForward iacTypes.BoolValue - OSLoginEnabled iacTypes.BoolValue - EnableProjectSSHKeyBlocking iacTypes.BoolValue - EnableSerialPort iacTypes.BoolValue - BootDisks []Disk - AttachedDisks []Disk -} - -type ServiceAccount struct { - Metadata iacTypes.Metadata - Email iacTypes.StringValue - IsDefault iacTypes.BoolValue - Scopes []iacTypes.StringValue -} - -type NetworkInterface struct { - Metadata iacTypes.Metadata - Network *Network - SubNetwork *SubNetwork - HasPublicIP iacTypes.BoolValue - NATIP iacTypes.StringValue -} - -type ShieldedVMConfig struct { - Metadata iacTypes.Metadata - SecureBootEnabled iacTypes.BoolValue - IntegrityMonitoringEnabled iacTypes.BoolValue - VTPMEnabled iacTypes.BoolValue -} diff --git a/pkg/iac/providers/google/compute/metadata.go b/pkg/iac/providers/google/compute/metadata.go deleted file mode 100755 index 9083854ba2af..000000000000 --- a/pkg/iac/providers/google/compute/metadata.go +++ /dev/null @@ -1,10 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ProjectMetadata struct { - Metadata iacTypes.Metadata - EnableOSLogin iacTypes.BoolValue -} diff --git a/pkg/iac/providers/google/compute/network.go b/pkg/iac/providers/google/compute/network.go deleted file mode 100755 index c8e7d0b3d56c..000000000000 --- a/pkg/iac/providers/google/compute/network.go +++ /dev/null @@ -1,11 +0,0 @@ -package compute - -import ( - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Network struct { - Metadata types.Metadata - Firewall *Firewall - Subnetworks []SubNetwork -} diff --git a/pkg/iac/providers/google/compute/ssl_policy.go b/pkg/iac/providers/google/compute/ssl_policy.go deleted file mode 100755 index 38ee07c047f4..000000000000 --- a/pkg/iac/providers/google/compute/ssl_policy.go +++ /dev/null @@ -1,12 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SSLPolicy struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Profile iacTypes.StringValue - MinimumTLSVersion iacTypes.StringValue -} diff --git a/pkg/iac/providers/google/compute/subnetwork.go b/pkg/iac/providers/google/compute/subnetwork.go deleted file mode 100755 index a0bd03d2a71c..000000000000 --- a/pkg/iac/providers/google/compute/subnetwork.go +++ /dev/null @@ -1,12 +0,0 @@ -package compute - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SubNetwork struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Purpose iacTypes.StringValue - EnableFlowLogs iacTypes.BoolValue -} diff --git a/pkg/iac/providers/google/dns/dns.go b/pkg/iac/providers/google/dns/dns.go deleted file mode 100755 index 93bc6fbb02c2..000000000000 --- a/pkg/iac/providers/google/dns/dns.go +++ /dev/null @@ -1,31 +0,0 @@ -package dns - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DNS struct { - ManagedZones []ManagedZone -} - -type ManagedZone struct { - Metadata iacTypes.Metadata - DNSSec DNSSec - Visibility iacTypes.StringValue -} - -func (m ManagedZone) IsPrivate() bool { - return m.Visibility.EqualTo("private", iacTypes.IgnoreCase) -} - -type DNSSec struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - DefaultKeySpecs []KeySpecs -} - -type KeySpecs struct { - Metadata iacTypes.Metadata - Algorithm iacTypes.StringValue - KeyType iacTypes.StringValue -} diff --git a/pkg/iac/providers/google/gke/gke.go b/pkg/iac/providers/google/gke/gke.go deleted file mode 100755 index 3fe346f82f6e..000000000000 --- a/pkg/iac/providers/google/gke/gke.go +++ /dev/null @@ -1,86 +0,0 @@ -package gke - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type GKE struct { - Clusters []Cluster -} - -type Cluster struct { - Metadata iacTypes.Metadata - NodePools []NodePool - IPAllocationPolicy IPAllocationPolicy - MasterAuthorizedNetworks MasterAuthorizedNetworks - NetworkPolicy NetworkPolicy - PrivateCluster PrivateCluster - LoggingService iacTypes.StringValue - MonitoringService iacTypes.StringValue - MasterAuth MasterAuth - NodeConfig NodeConfig - EnableShieldedNodes iacTypes.BoolValue - EnableLegacyABAC iacTypes.BoolValue - ResourceLabels iacTypes.MapValue - RemoveDefaultNodePool iacTypes.BoolValue - EnableAutpilot iacTypes.BoolValue - DatapathProvider iacTypes.StringValue -} - -type NodeConfig struct { - Metadata iacTypes.Metadata - ImageType iacTypes.StringValue - WorkloadMetadataConfig WorkloadMetadataConfig - ServiceAccount iacTypes.StringValue - EnableLegacyEndpoints iacTypes.BoolValue -} - -type WorkloadMetadataConfig struct { - Metadata iacTypes.Metadata - NodeMetadata iacTypes.StringValue -} - -type MasterAuth struct { - Metadata iacTypes.Metadata - ClientCertificate ClientCertificate - Username iacTypes.StringValue - Password iacTypes.StringValue -} - -type ClientCertificate struct { - Metadata iacTypes.Metadata - IssueCertificate iacTypes.BoolValue -} - -type PrivateCluster struct { - Metadata iacTypes.Metadata - EnablePrivateNodes iacTypes.BoolValue -} - -type NetworkPolicy struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type MasterAuthorizedNetworks struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue - CIDRs []iacTypes.StringValue -} - -type IPAllocationPolicy struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type NodePool struct { - Metadata iacTypes.Metadata - Management Management - NodeConfig NodeConfig -} - -type Management struct { - Metadata iacTypes.Metadata - EnableAutoRepair iacTypes.BoolValue - EnableAutoUpgrade iacTypes.BoolValue -} diff --git a/pkg/iac/providers/google/google.go b/pkg/iac/providers/google/google.go deleted file mode 100755 index 4616c9f0089d..000000000000 --- a/pkg/iac/providers/google/google.go +++ /dev/null @@ -1,23 +0,0 @@ -package google - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/google/bigquery" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/dns" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/gke" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/kms" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/sql" - "github.com/aquasecurity/trivy/pkg/iac/providers/google/storage" -) - -type Google struct { - BigQuery bigquery.BigQuery - Compute compute.Compute - DNS dns.DNS - GKE gke.GKE - KMS kms.KMS - IAM iam.IAM - SQL sql.SQL - Storage storage.Storage -} diff --git a/pkg/iac/providers/google/iam/iam.go b/pkg/iac/providers/google/iam/iam.go deleted file mode 100755 index 1711f8609bf0..000000000000 --- a/pkg/iac/providers/google/iam/iam.go +++ /dev/null @@ -1,60 +0,0 @@ -package iam - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type IAM struct { - Organizations []Organization - WorkloadIdentityPoolProviders []WorkloadIdentityPoolProvider - Projects []Project - Folders []Folder -} - -type Organization struct { - Metadata iacTypes.Metadata - Members []Member - Bindings []Binding -} - -type Folder struct { - Metadata iacTypes.Metadata - Members []Member - Bindings []Binding -} - -type Project struct { - Metadata iacTypes.Metadata - AutoCreateNetwork iacTypes.BoolValue - Members []Member - Bindings []Binding -} - -type Binding struct { - Metadata iacTypes.Metadata - Members []iacTypes.StringValue - Role iacTypes.StringValue - IncludesDefaultServiceAccount iacTypes.BoolValue -} - -type Member struct { - Metadata iacTypes.Metadata - Member iacTypes.StringValue - Role iacTypes.StringValue - DefaultServiceAccount iacTypes.BoolValue -} - -type WorkloadIdentityPoolProvider struct { - Metadata iacTypes.Metadata - WorkloadIdentityPoolId iacTypes.StringValue - WorkloadIdentityPoolProviderId iacTypes.StringValue - AttributeCondition iacTypes.StringValue -} - -func (p *IAM) AllProjects() []Project { - return p.Projects -} - -func (p *IAM) AllFolders() []Folder { - return p.Folders -} diff --git a/pkg/iac/providers/google/kms/kms.go b/pkg/iac/providers/google/kms/kms.go deleted file mode 100755 index 4247db119e13..000000000000 --- a/pkg/iac/providers/google/kms/kms.go +++ /dev/null @@ -1,19 +0,0 @@ -package kms - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type KMS struct { - KeyRings []KeyRing -} - -type KeyRing struct { - Metadata iacTypes.Metadata - Keys []Key -} - -type Key struct { - Metadata iacTypes.Metadata - RotationPeriodSeconds iacTypes.IntValue -} diff --git a/pkg/iac/providers/google/sql/sql.go b/pkg/iac/providers/google/sql/sql.go deleted file mode 100755 index 672e78a42fbd..000000000000 --- a/pkg/iac/providers/google/sql/sql.go +++ /dev/null @@ -1,79 +0,0 @@ -package sql - -import ( - "strings" - - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SQL struct { - Instances []DatabaseInstance -} - -const ( - DatabaseFamilyMySQL = "MYSQL" - DatabaseFamilyPostgres = "POSTGRES" - DatabaseFamilySQLServer = "SQLSERVER" -) - -const ( - DatabaseVersionMySQL_5_6 = "MYSQL_5_6" - DatabaseVersionMySQL_5_7 = "MYSQL_5_7" - DatabaseVersionMySQL_8_0 = "MYSQL_8_0" - DatabaseVersionPostgres_9_6 = "POSTGRES_9_6" - DatabaseVersionPostgres_10 = "POSTGRES_10" - DatabaseVersionPostgres_11 = "POSTGRES_11" - DatabaseVersionPostgres_12 = "POSTGRES_12" - DatabaseVersionPostgres_13 = "POSTGRES_13" - DatabaseVersionSQLServer_2017_STANDARD = "SQLSERVER_2017_STANDARD" - DatabaseVersionSQLServer_2017_ENTERPRISE = "SQLSERVER_2017_ENTERPRISE" - DatabaseVersionSQLServer_2017_EXPRESS = "SQLSERVER_2017_EXPRESS" - DatabaseVersionSQLServer_2017_WEB = "SQLSERVER_2017_WEB" -) - -type DatabaseInstance struct { - Metadata iacTypes.Metadata - DatabaseVersion iacTypes.StringValue - Settings Settings - IsReplica iacTypes.BoolValue -} - -type Settings struct { - Metadata iacTypes.Metadata - Flags Flags - Backups Backups - IPConfiguration IPConfiguration -} -type Flags struct { - Metadata iacTypes.Metadata - LogTempFileSize iacTypes.IntValue - LocalInFile iacTypes.BoolValue - ContainedDatabaseAuthentication iacTypes.BoolValue - CrossDBOwnershipChaining iacTypes.BoolValue - LogCheckpoints iacTypes.BoolValue - LogConnections iacTypes.BoolValue - LogDisconnections iacTypes.BoolValue - LogLockWaits iacTypes.BoolValue - LogMinMessages iacTypes.StringValue // FATAL, PANIC, LOG, ERROR, WARN - LogMinDurationStatement iacTypes.IntValue -} - -type Backups struct { - Metadata iacTypes.Metadata - Enabled iacTypes.BoolValue -} - -type IPConfiguration struct { - Metadata iacTypes.Metadata - RequireTLS iacTypes.BoolValue - SSLMode iacTypes.StringValue - EnableIPv4 iacTypes.BoolValue - AuthorizedNetworks []struct { - Name iacTypes.StringValue - CIDR iacTypes.StringValue - } -} - -func (i *DatabaseInstance) DatabaseFamily() string { - return strings.Split(i.DatabaseVersion.Value(), "_")[0] -} diff --git a/pkg/iac/providers/google/storage/storage.go b/pkg/iac/providers/google/storage/storage.go deleted file mode 100755 index 7650474b8640..000000000000 --- a/pkg/iac/providers/google/storage/storage.go +++ /dev/null @@ -1,25 +0,0 @@ -package storage - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/google/iam" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Storage struct { - Buckets []Bucket -} - -type Bucket struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Location iacTypes.StringValue - EnableUniformBucketLevelAccess iacTypes.BoolValue - Members []iam.Member - Bindings []iam.Binding - Encryption BucketEncryption -} - -type BucketEncryption struct { - Metadata iacTypes.Metadata - DefaultKMSKeyName iacTypes.StringValue -} diff --git a/pkg/iac/providers/kubernetes/kubernetes.go b/pkg/iac/providers/kubernetes/kubernetes.go deleted file mode 100755 index cf71291161a1..000000000000 --- a/pkg/iac/providers/kubernetes/kubernetes.go +++ /dev/null @@ -1,38 +0,0 @@ -package kubernetes - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Kubernetes struct { - NetworkPolicies []NetworkPolicy -} - -type NetworkPolicy struct { - Metadata iacTypes.Metadata - Spec NetworkPolicySpec -} - -type NetworkPolicySpec struct { - Metadata iacTypes.Metadata - Egress Egress - Ingress Ingress -} - -type Egress struct { - Metadata iacTypes.Metadata - Ports []Port - DestinationCIDRs []iacTypes.StringValue -} - -type Ingress struct { - Metadata iacTypes.Metadata - Ports []Port - SourceCIDRs []iacTypes.StringValue -} - -type Port struct { - Metadata iacTypes.Metadata - Number iacTypes.StringValue // e.g. "http" or "80" - Protocol iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/computing/computing.go b/pkg/iac/providers/nifcloud/computing/computing.go deleted file mode 100755 index aaef2361bf98..000000000000 --- a/pkg/iac/providers/nifcloud/computing/computing.go +++ /dev/null @@ -1,6 +0,0 @@ -package computing - -type Computing struct { - SecurityGroups []SecurityGroup - Instances []Instance -} diff --git a/pkg/iac/providers/nifcloud/computing/instance.go b/pkg/iac/providers/nifcloud/computing/instance.go deleted file mode 100644 index 2b729920f37f..000000000000 --- a/pkg/iac/providers/nifcloud/computing/instance.go +++ /dev/null @@ -1,16 +0,0 @@ -package computing - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Instance struct { - Metadata iacTypes.Metadata - SecurityGroup iacTypes.StringValue - NetworkInterfaces []NetworkInterface -} - -type NetworkInterface struct { - Metadata iacTypes.Metadata - NetworkID iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/computing/security_group.go b/pkg/iac/providers/nifcloud/computing/security_group.go deleted file mode 100644 index f707d8a2825a..000000000000 --- a/pkg/iac/providers/nifcloud/computing/security_group.go +++ /dev/null @@ -1,18 +0,0 @@ -package computing - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SecurityGroup struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue - IngressRules []SecurityGroupRule - EgressRules []SecurityGroupRule -} - -type SecurityGroupRule struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue - CIDR iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/dns/dns.go b/pkg/iac/providers/nifcloud/dns/dns.go deleted file mode 100755 index 7351506d7f6f..000000000000 --- a/pkg/iac/providers/nifcloud/dns/dns.go +++ /dev/null @@ -1,5 +0,0 @@ -package dns - -type DNS struct { - Records []Record -} diff --git a/pkg/iac/providers/nifcloud/dns/record.go b/pkg/iac/providers/nifcloud/dns/record.go deleted file mode 100644 index 44275dfb27d0..000000000000 --- a/pkg/iac/providers/nifcloud/dns/record.go +++ /dev/null @@ -1,13 +0,0 @@ -package dns - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -const ZoneRegistrationAuthTxt = "nifty-dns-verify=" - -type Record struct { - Metadata iacTypes.Metadata - Type iacTypes.StringValue - Record iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/nas/nas.go b/pkg/iac/providers/nifcloud/nas/nas.go deleted file mode 100755 index e73a9c9efd70..000000000000 --- a/pkg/iac/providers/nifcloud/nas/nas.go +++ /dev/null @@ -1,6 +0,0 @@ -package nas - -type NAS struct { - NASSecurityGroups []NASSecurityGroup - NASInstances []NASInstance -} diff --git a/pkg/iac/providers/nifcloud/nas/nas_instance.go b/pkg/iac/providers/nifcloud/nas/nas_instance.go deleted file mode 100644 index 90567629ba09..000000000000 --- a/pkg/iac/providers/nifcloud/nas/nas_instance.go +++ /dev/null @@ -1,10 +0,0 @@ -package nas - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type NASInstance struct { - Metadata iacTypes.Metadata - NetworkID iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/nas/nas_security_group.go b/pkg/iac/providers/nifcloud/nas/nas_security_group.go deleted file mode 100644 index f5351f7ab150..000000000000 --- a/pkg/iac/providers/nifcloud/nas/nas_security_group.go +++ /dev/null @@ -1,11 +0,0 @@ -package nas - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type NASSecurityGroup struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue - CIDRs []iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/network/elastic_load_balancer.go b/pkg/iac/providers/nifcloud/network/elastic_load_balancer.go deleted file mode 100644 index 82d977fcf8fa..000000000000 --- a/pkg/iac/providers/nifcloud/network/elastic_load_balancer.go +++ /dev/null @@ -1,16 +0,0 @@ -package network - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ElasticLoadBalancer struct { - Metadata iacTypes.Metadata - NetworkInterfaces []NetworkInterface - Listeners []ElasticLoadBalancerListener -} - -type ElasticLoadBalancerListener struct { - Metadata iacTypes.Metadata - Protocol iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/network/load_balancer.go b/pkg/iac/providers/nifcloud/network/load_balancer.go deleted file mode 100644 index 860a50614f8a..000000000000 --- a/pkg/iac/providers/nifcloud/network/load_balancer.go +++ /dev/null @@ -1,16 +0,0 @@ -package network - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type LoadBalancer struct { - Metadata iacTypes.Metadata - Listeners []LoadBalancerListener -} - -type LoadBalancerListener struct { - Metadata iacTypes.Metadata - Protocol iacTypes.StringValue - TLSPolicy iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/network/network.go b/pkg/iac/providers/nifcloud/network/network.go deleted file mode 100755 index dc337e0a2470..000000000000 --- a/pkg/iac/providers/nifcloud/network/network.go +++ /dev/null @@ -1,16 +0,0 @@ -package network - -import iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" - -type Network struct { - ElasticLoadBalancers []ElasticLoadBalancer - LoadBalancers []LoadBalancer - Routers []Router - VpnGateways []VpnGateway -} - -type NetworkInterface struct { - Metadata iacTypes.Metadata - NetworkID iacTypes.StringValue - IsVipNetwork iacTypes.BoolValue -} diff --git a/pkg/iac/providers/nifcloud/network/router.go b/pkg/iac/providers/nifcloud/network/router.go deleted file mode 100644 index c26aa9115729..000000000000 --- a/pkg/iac/providers/nifcloud/network/router.go +++ /dev/null @@ -1,11 +0,0 @@ -package network - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Router struct { - Metadata iacTypes.Metadata - SecurityGroup iacTypes.StringValue - NetworkInterfaces []NetworkInterface -} diff --git a/pkg/iac/providers/nifcloud/network/vpn_gateway.go b/pkg/iac/providers/nifcloud/network/vpn_gateway.go deleted file mode 100644 index 54ff74aab3d4..000000000000 --- a/pkg/iac/providers/nifcloud/network/vpn_gateway.go +++ /dev/null @@ -1,10 +0,0 @@ -package network - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type VpnGateway struct { - Metadata iacTypes.Metadata - SecurityGroup iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/nifcloud.go b/pkg/iac/providers/nifcloud/nifcloud.go deleted file mode 100755 index 4126407925c6..000000000000 --- a/pkg/iac/providers/nifcloud/nifcloud.go +++ /dev/null @@ -1,19 +0,0 @@ -package nifcloud - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/computing" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/dns" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/nas" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/network" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/rdb" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud/sslcertificate" -) - -type Nifcloud struct { - Computing computing.Computing - DNS dns.DNS - NAS nas.NAS - Network network.Network - RDB rdb.RDB - SSLCertificate sslcertificate.SSLCertificate -} diff --git a/pkg/iac/providers/nifcloud/rdb/db_instance.go b/pkg/iac/providers/nifcloud/rdb/db_instance.go deleted file mode 100644 index be888d09210e..000000000000 --- a/pkg/iac/providers/nifcloud/rdb/db_instance.go +++ /dev/null @@ -1,14 +0,0 @@ -package rdb - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DBInstance struct { - Metadata iacTypes.Metadata - BackupRetentionPeriodDays iacTypes.IntValue - Engine iacTypes.StringValue - EngineVersion iacTypes.StringValue - NetworkID iacTypes.StringValue - PublicAccess iacTypes.BoolValue -} diff --git a/pkg/iac/providers/nifcloud/rdb/db_security_group.go b/pkg/iac/providers/nifcloud/rdb/db_security_group.go deleted file mode 100644 index 3c9e350e5cad..000000000000 --- a/pkg/iac/providers/nifcloud/rdb/db_security_group.go +++ /dev/null @@ -1,11 +0,0 @@ -package rdb - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type DBSecurityGroup struct { - Metadata iacTypes.Metadata - Description iacTypes.StringValue - CIDRs []iacTypes.StringValue -} diff --git a/pkg/iac/providers/nifcloud/rdb/rdb.go b/pkg/iac/providers/nifcloud/rdb/rdb.go deleted file mode 100755 index 4aea31980708..000000000000 --- a/pkg/iac/providers/nifcloud/rdb/rdb.go +++ /dev/null @@ -1,6 +0,0 @@ -package rdb - -type RDB struct { - DBSecurityGroups []DBSecurityGroup - DBInstances []DBInstance -} diff --git a/pkg/iac/providers/nifcloud/sslcertificate/server_certificate.go b/pkg/iac/providers/nifcloud/sslcertificate/server_certificate.go deleted file mode 100644 index 86dc479851a3..000000000000 --- a/pkg/iac/providers/nifcloud/sslcertificate/server_certificate.go +++ /dev/null @@ -1,10 +0,0 @@ -package sslcertificate - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type ServerCertificate struct { - Metadata iacTypes.Metadata - Expiration iacTypes.TimeValue -} diff --git a/pkg/iac/providers/nifcloud/sslcertificate/ssl_certificate.go b/pkg/iac/providers/nifcloud/sslcertificate/ssl_certificate.go deleted file mode 100755 index 7ab46d870b16..000000000000 --- a/pkg/iac/providers/nifcloud/sslcertificate/ssl_certificate.go +++ /dev/null @@ -1,5 +0,0 @@ -package sslcertificate - -type SSLCertificate struct { - ServerCertificates []ServerCertificate -} diff --git a/pkg/iac/providers/openstack/networking.go b/pkg/iac/providers/openstack/networking.go deleted file mode 100644 index 944bc3fe9253..000000000000 --- a/pkg/iac/providers/openstack/networking.go +++ /dev/null @@ -1,27 +0,0 @@ -package openstack - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Networking struct { - SecurityGroups []SecurityGroup -} - -type SecurityGroup struct { - Metadata iacTypes.Metadata - Name iacTypes.StringValue - Description iacTypes.StringValue - Rules []SecurityGroupRule -} - -// SecurityGroupRule describes https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_secgroup_rule_v2 -type SecurityGroupRule struct { - Metadata iacTypes.Metadata - IsIngress iacTypes.BoolValue - EtherType iacTypes.IntValue // 4 or 6 for ipv4/ipv6 - Protocol iacTypes.StringValue // e.g. tcp - PortMin iacTypes.IntValue - PortMax iacTypes.IntValue - CIDR iacTypes.StringValue -} diff --git a/pkg/iac/providers/openstack/openstack.go b/pkg/iac/providers/openstack/openstack.go deleted file mode 100755 index 04a23f28fcee..000000000000 --- a/pkg/iac/providers/openstack/openstack.go +++ /dev/null @@ -1,34 +0,0 @@ -package openstack - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type OpenStack struct { - Compute Compute - Networking Networking -} - -type Compute struct { - Instances []Instance - Firewall Firewall -} - -type Firewall struct { - AllowRules []FirewallRule - DenyRules []FirewallRule -} - -type FirewallRule struct { - Metadata iacTypes.Metadata - Source iacTypes.StringValue - Destination iacTypes.StringValue - SourcePort iacTypes.StringValue - DestinationPort iacTypes.StringValue - Enabled iacTypes.BoolValue -} - -type Instance struct { - Metadata iacTypes.Metadata - AdminPassword iacTypes.StringValue -} diff --git a/pkg/iac/providers/oracle/oracle.go b/pkg/iac/providers/oracle/oracle.go deleted file mode 100755 index 6d6a3ecbdfe5..000000000000 --- a/pkg/iac/providers/oracle/oracle.go +++ /dev/null @@ -1,18 +0,0 @@ -package oracle - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Oracle struct { - Compute Compute -} - -type Compute struct { - AddressReservations []AddressReservation -} - -type AddressReservation struct { - Metadata iacTypes.Metadata - Pool iacTypes.StringValue // e.g. public-pool -} diff --git a/pkg/iac/providers/provider.go b/pkg/iac/providers/provider.go deleted file mode 100755 index 46dbf19ec43c..000000000000 --- a/pkg/iac/providers/provider.go +++ /dev/null @@ -1,58 +0,0 @@ -package providers - -import ( - "strings" - - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -// Provider is the provider that the check applies to -type Provider string - -const ( - UnknownProvider Provider = "" - AWSProvider Provider = "aws" - AzureProvider Provider = "azure" - CustomProvider Provider = "custom" - DigitalOceanProvider Provider = "digitalocean" - GeneralProvider Provider = "general" - GitHubProvider Provider = "github" - GoogleProvider Provider = "google" - KubernetesProvider Provider = "kubernetes" - OracleProvider Provider = "oracle" - OpenStackProvider Provider = "openstack" - NifcloudProvider Provider = "nifcloud" - CloudStackProvider Provider = "cloudstack" -) - -func AllProviders() []Provider { - return []Provider{ - AWSProvider, AzureProvider, DigitalOceanProvider, GitHubProvider, GoogleProvider, - KubernetesProvider, OracleProvider, OpenStackProvider, NifcloudProvider, CloudStackProvider, - } -} - -func RuleProviderToString(provider Provider) string { - return strings.ToUpper(string(provider)) -} - -func (p Provider) DisplayName() string { - switch p { - case "aws": - return strings.ToUpper(string(p)) - case "digitalocean": - return "Digital Ocean" - case "github": - return "GitHub" - case "openstack": - return "OpenStack" - case "cloudstack": - return "Cloudstack" - default: - return cases.Title(language.English).String(strings.ToLower(string(p))) - } -} -func (p Provider) ConstName() string { - return strings.ReplaceAll(p.DisplayName(), " ", "") -} diff --git a/pkg/iac/rego/build.go b/pkg/iac/rego/build.go deleted file mode 100644 index fbd7a54cc51c..000000000000 --- a/pkg/iac/rego/build.go +++ /dev/null @@ -1,99 +0,0 @@ -package rego - -import ( - "fmt" - "io/fs" - "path/filepath" - "strings" - - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/util" - - "github.com/aquasecurity/trivy/pkg/iac/rego/schemas" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func BuildSchemaSetFromPolicies(policies map[string]*ast.Module, paths []string, fsys fs.FS, customSchemas map[string][]byte) (*ast.SchemaSet, error) { - schemaSet := ast.NewSchemaSet() - schemaSet.Put(ast.MustParseRef("schema.input"), make(map[string]any)) // for backwards compat only - - for _, policy := range policies { - for _, annotation := range policy.Annotations { - for _, ss := range annotation.Schemas { - schemaName, err := ss.Schema.Ptr() - if err != nil || schemaName == "input" { // for backwards compat only - continue - } - - if schemaSet.Get(ss.Schema) != nil { - continue - } - - var schema []byte - if s, ok := schemas.SchemaMap[types.Source(schemaName)]; ok { - schema = []byte(s) - } else if s, ok := customSchemas[schemaName]; ok { - schema = s - } else { - b, err := findSchemaInFS(paths, fsys, schemaName) - if err != nil { - return nil, err - } - - if b == nil { - return nil, fmt.Errorf("could not find schema %q", schemaName) - } - - schema = b - } - - var rawSchema any - if err := util.UnmarshalJSON(schema, &rawSchema); err != nil { - return schemaSet, fmt.Errorf("could not parse schema %q: %w", schemaName, err) - } - schemaSet.Put(ss.Schema, rawSchema) - } - } - } - - return schemaSet, nil -} - -// findSchemaInFS tries to find the schema anywhere in the specified FS -func findSchemaInFS(paths []string, srcFS fs.FS, schemaName string) ([]byte, error) { - var schema []byte - for _, path := range paths { - if err := fs.WalkDir(srcFS, sanitisePath(path), func(path string, info fs.DirEntry, err error) error { - if err != nil { - return err - } - if info.IsDir() { - return nil - } - if !IsJSONFile(info.Name()) { - return nil - } - if info.Name() == schemaName+".json" { - schema, err = fs.ReadFile(srcFS, filepath.ToSlash(path)) - if err != nil { - return err - } - return nil - } - return nil - }); err != nil { - return nil, err - } - } - return schema, nil -} - -func IsJSONFile(name string) bool { - return strings.HasSuffix(name, ".json") -} - -func sanitisePath(path string) string { - vol := filepath.VolumeName(path) - path = strings.TrimPrefix(path, vol) - return strings.TrimPrefix(strings.TrimPrefix(filepath.ToSlash(path), "./"), "/") -} diff --git a/pkg/iac/rego/convert/anonymous.go b/pkg/iac/rego/convert/anonymous.go deleted file mode 100644 index 069ea0d06d88..000000000000 --- a/pkg/iac/rego/convert/anonymous.go +++ /dev/null @@ -1,47 +0,0 @@ -package convert - -import ( - "reflect" -) - -var converterInterface = reflect.TypeOf((*Converter)(nil)).Elem() - -func anonymousToRego(inputValue reflect.Value) any { - - if inputValue.IsZero() { - return nil - } - - for inputValue.Type().Kind() == reflect.Interface { - if inputValue.IsNil() { - return nil - } - inputValue = inputValue.Elem() - } - - if inputValue.Type().Implements(converterInterface) { - returns := inputValue.MethodByName("ToRego").Call(nil) - return returns[0].Interface() - } - - for inputValue.Type().Kind() == reflect.Ptr { - if inputValue.IsNil() { - return nil - } - inputValue = inputValue.Elem() - } - - if inputValue.Type().Implements(converterInterface) { - returns := inputValue.MethodByName("ToRego").Call(nil) - return returns[0].Interface() - } - - switch kind := inputValue.Type().Kind(); kind { - case reflect.Struct: - return StructToRego(inputValue) - case reflect.Slice: - return SliceToRego(inputValue) - } - - return nil -} diff --git a/pkg/iac/rego/convert/converter.go b/pkg/iac/rego/convert/converter.go deleted file mode 100644 index 880050c0eb6a..000000000000 --- a/pkg/iac/rego/convert/converter.go +++ /dev/null @@ -1,5 +0,0 @@ -package convert - -type Converter interface { - ToRego() any -} diff --git a/pkg/iac/rego/convert/slice.go b/pkg/iac/rego/convert/slice.go deleted file mode 100644 index 02c7dcff92a0..000000000000 --- a/pkg/iac/rego/convert/slice.go +++ /dev/null @@ -1,32 +0,0 @@ -package convert - -import ( - "reflect" -) - -func SliceToRego(inputValue reflect.Value) []any { - - // make sure we have a struct literal - for inputValue.Type().Kind() == reflect.Ptr { - if inputValue.IsNil() { - return nil - } - inputValue = inputValue.Elem() - } - if inputValue.Type().Kind() != reflect.Slice { - panic("not a slice") - } - - output := make([]any, inputValue.Len()) - - for i := 0; i < inputValue.Len(); i++ { - val := inputValue.Index(i) - if val.Type().Kind() == reflect.Ptr && val.IsZero() { - output[i] = nil - continue - } - output[i] = anonymousToRego(val) - } - - return output -} diff --git a/pkg/iac/rego/convert/slice_test.go b/pkg/iac/rego/convert/slice_test.go deleted file mode 100644 index ff318e2b1a03..000000000000 --- a/pkg/iac/rego/convert/slice_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package convert - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_SliceConversion(t *testing.T) { - input := []struct { - X string - Y int - Z struct { - A float64 - } - }{ - {}, - } - input[0].Z.A = 123 - converted := SliceToRego(reflect.ValueOf(input)) - assert.Equal(t, []any{map[string]any{"z": make(map[string]any)}}, converted) -} - -func Test_SliceTypesConversion(t *testing.T) { - input := []types.StringValue{ - types.String("test1", types.NewTestMetadata()), - types.String("test2", types.NewTestMetadata()), - } - converted := SliceToRego(reflect.ValueOf(input)) - assert.Equal(t, []any{ - map[string]any{ - "value": "test1", - "filepath": "test.test", - "startline": 123, - "endline": 123, - "sourceprefix": "", - "managed": true, - "unresolvable": false, - "explicit": false, - "fskey": "", - "resource": "", - }, - map[string]any{ - "value": "test2", - "filepath": "test.test", - "startline": 123, - "endline": 123, - "sourceprefix": "", - "managed": true, - "unresolvable": false, - "explicit": false, - "fskey": "", - "resource": "", - }, - }, converted) -} diff --git a/pkg/iac/rego/convert/struct.go b/pkg/iac/rego/convert/struct.go deleted file mode 100644 index 306f4d85a464..000000000000 --- a/pkg/iac/rego/convert/struct.go +++ /dev/null @@ -1,69 +0,0 @@ -package convert - -import ( - "reflect" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type metadataProvider interface { - GetMetadata() types.Metadata -} - -var metadataInterface = reflect.TypeOf((*metadataProvider)(nil)).Elem() - -func StructToRego(inputValue reflect.Value) map[string]any { - - // make sure we have a struct literal - for inputValue.Type().Kind() == reflect.Ptr || inputValue.Type().Kind() == reflect.Interface { - if inputValue.IsNil() { - return nil - } - inputValue = inputValue.Elem() - } - if inputValue.Type().Kind() != reflect.Struct { - panic("not a struct") - } - - output := make(map[string]any, inputValue.NumField()) - - for i := 0; i < inputValue.NumField(); i++ { - field := inputValue.Field(i) - typ := inputValue.Type().Field(i) - name := typ.Name - - if !typ.IsExported() || field.Interface() == nil { - continue - } - - if _, ok := field.Interface().(types.Metadata); ok && name == "Metadata" { - continue - } - - val := anonymousToRego(reflect.ValueOf(field.Interface())) - - if val == nil { - continue - } - - output[strings.ToLower(name)] = val - } - - if inputValue.Type().Implements(metadataInterface) { - returns := inputValue.MethodByName("GetMetadata").Call(nil) - if metadata, ok := returns[0].Interface().(types.Metadata); ok { - output["__defsec_metadata"] = metadata.ToRego() - } - } else { - metaVal := inputValue.FieldByName("Metadata") - if metaVal.Kind() == reflect.Struct { - if meta, ok := metaVal.Interface().(types.Metadata); ok { - output["__defsec_metadata"] = meta.ToRego() - } - } - - } - - return output -} diff --git a/pkg/iac/rego/convert/struct_test.go b/pkg/iac/rego/convert/struct_test.go deleted file mode 100644 index 0970b00c26e6..000000000000 --- a/pkg/iac/rego/convert/struct_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package convert - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_StructConversion(t *testing.T) { - tests := []struct { - name string - inp any - expected any - }{ - { - name: "struct with nested struct", - inp: struct { - X string - Y int - Z struct { - A float64 - } - }{ - X: "test", - Z: struct { - A float64 - }{ - A: 123, - }, - }, - expected: map[string]any{"z": make(map[string]any)}, - }, - { - name: "struct with metadata", - inp: struct { - X string - Metadata types.Metadata - }{ - X: "test", - Metadata: types.NewTestMetadata(), - }, - expected: map[string]any{ - "__defsec_metadata": func() any { - meta := types.NewTestMetadata().GetMetadata() - return meta.ToRego() - }(), - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - converted := StructToRego(reflect.ValueOf(tt.inp)) - assert.Equal(t, tt.expected, converted) - }) - } -} diff --git a/pkg/iac/rego/custom.go b/pkg/iac/rego/custom.go deleted file mode 100644 index 9de17beeadea..000000000000 --- a/pkg/iac/rego/custom.go +++ /dev/null @@ -1,114 +0,0 @@ -package rego - -import ( - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" - "github.com/open-policy-agent/opa/types" - - checksrego "github.com/aquasecurity/trivy-checks/pkg/rego" -) - -func init() { - - checksrego.RegisterBuiltins() - - rego.RegisterBuiltin2(®o.Function{ - Name: "result.new", - Decl: types.NewFunction(types.Args(types.S, types.A), types.A), - }, - createResult, - ) - - rego.RegisterBuiltin1(®o.Function{ - Name: "isManaged", - Decl: types.NewFunction(types.Args(types.A), types.B), - }, - func(c rego.BuiltinContext, resource *ast.Term) (*ast.Term, error) { - metadata, err := createResult(c, ast.StringTerm(""), resource) - if err != nil { - return nil, err - } - return metadata.Get(ast.StringTerm("managed")), nil - }, - ) -} - -func createResult(ctx rego.BuiltinContext, msg, cause *ast.Term) (*ast.Term, error) { - - metadata := map[string]*ast.Term{ - "startline": ast.IntNumberTerm(0), - "endline": ast.IntNumberTerm(0), - "sourceprefix": ast.StringTerm(""), - "filepath": ast.StringTerm(""), - "explicit": ast.BooleanTerm(false), - "managed": ast.BooleanTerm(true), - "fskey": ast.StringTerm(""), - "resource": ast.StringTerm(""), - "parent": ast.NullTerm(), - } - if msg != nil { - metadata["msg"] = msg - } - - // universal - input := cause.Get(ast.StringTerm("__defsec_metadata")) - if input == nil { - // docker - input = cause - } - metadata = updateMetadata(metadata, input) - - if term := input.Get(ast.StringTerm("parent")); term != nil { - var err error - metadata["parent"], err = createResult(ctx, nil, term) - if err != nil { - return nil, err - } - } - - var values [][2]*ast.Term - for key, val := range metadata { - values = append(values, [2]*ast.Term{ - ast.StringTerm(key), - val, - }) - } - return ast.ObjectTerm(values...), nil -} - -func updateMetadata(metadata map[string]*ast.Term, input *ast.Term) map[string]*ast.Term { - if term := input.Get(ast.StringTerm("startline")); term != nil { - metadata["startline"] = term - } - if term := input.Get(ast.StringTerm("StartLine")); term != nil { - metadata["startline"] = term - } - if term := input.Get(ast.StringTerm("endline")); term != nil { - metadata["endline"] = term - } - if term := input.Get(ast.StringTerm("EndLine")); term != nil { - metadata["endline"] = term - } - if term := input.Get(ast.StringTerm("filepath")); term != nil { - metadata["filepath"] = term - } - if term := input.Get(ast.StringTerm("sourceprefix")); term != nil { - metadata["sourceprefix"] = term - } - if term := input.Get(ast.StringTerm("Path")); term != nil { - metadata["filepath"] = term - } - if term := input.Get(ast.StringTerm("explicit")); term != nil { - metadata["explicit"] = term - } - if term := input.Get(ast.StringTerm("managed")); term != nil { - metadata["managed"] = term - } - if term := input.Get(ast.StringTerm("fskey")); term != nil { - metadata["fskey"] = term - } - if term := input.Get(ast.StringTerm("resource")); term != nil { - metadata["resource"] = term - } - return metadata -} diff --git a/pkg/iac/rego/embed.go b/pkg/iac/rego/embed.go deleted file mode 100644 index c2dbb6ae5fd4..000000000000 --- a/pkg/iac/rego/embed.go +++ /dev/null @@ -1,120 +0,0 @@ -package rego - -import ( - "context" - "fmt" - "io/fs" - "path/filepath" - "strings" - "sync" - - "github.com/open-policy-agent/opa/ast" - - checks "github.com/aquasecurity/trivy-checks" - "github.com/aquasecurity/trivy/pkg/iac/rules" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" -) - -var LoadAndRegister = sync.OnceFunc(func() { - modules, err := LoadEmbeddedPolicies() - if err != nil { - // we should panic as the policies were not embedded properly - panic(err) - } - loadedLibs, err := LoadEmbeddedLibraries() - if err != nil { - panic(err) - } - for name, policy := range loadedLibs { - modules[name] = policy - } - - RegisterRegoRules(modules) -}) - -func RegisterRegoRules(modules map[string]*ast.Module) { - ctx := context.TODO() - - schemaSet, _ := BuildSchemaSetFromPolicies(modules, nil, nil, make(map[string][]byte)) - - compiler := ast.NewCompiler(). - WithSchemas(schemaSet). - WithCapabilities(nil). - WithUseTypeCheckAnnotations(true) - - compiler.Compile(modules) - if compiler.Failed() { - // we should panic as the embedded rego policies are syntactically incorrect... - panic(compiler.Errors) - } - - retriever := NewMetadataRetriever(compiler) - regoCheckIDs := set.New[string]() - - for _, module := range modules { - metadata, err := retriever.RetrieveMetadata(ctx, module) - if err != nil { - log.Warn("Failed to retrieve metadata", log.String("package", module.Package.String()), log.Err(err)) - continue - } - - if metadata.AVDID == "" { - if !metadata.Library { - log.Warn("Check ID is empty", log.FilePath(module.Package.Location.File)) - } - continue - } - - if !metadata.Deprecated { - regoCheckIDs.Append(metadata.AVDID) - } - - rules.Register(metadata.ToRule()) - } -} - -func LoadEmbeddedPolicies() (map[string]*ast.Module, error) { - return LoadPoliciesFromDirs(checks.EmbeddedPolicyFileSystem, ".") -} - -func LoadEmbeddedLibraries() (map[string]*ast.Module, error) { - return LoadPoliciesFromDirs(checks.EmbeddedLibraryFileSystem, ".") -} - -func LoadPoliciesFromDirs(target fs.FS, paths ...string) (map[string]*ast.Module, error) { - modules := make(map[string]*ast.Module) - for _, path := range paths { - if err := fs.WalkDir(target, sanitisePath(path), func(path string, info fs.DirEntry, err error) error { - if err != nil { - return err - } - if info.IsDir() { - return nil - } - - if strings.HasSuffix(filepath.Dir(filepath.ToSlash(path)), filepath.Join("advanced", "optional")) { - return fs.SkipDir - } - - if !IsRegoFile(info.Name()) || IsDotFile(info.Name()) { - return nil - } - data, err := fs.ReadFile(target, filepath.ToSlash(path)) - if err != nil { - return err - } - module, err := ast.ParseModuleWithOpts(path, string(data), ast.ParserOptions{ - ProcessAnnotation: true, - }) - if err != nil { - return fmt.Errorf("failed to parse Rego module: %w", err) - } - modules[path] = module - return nil - }); err != nil { - return nil, err - } - } - return modules, nil -} diff --git a/pkg/iac/rego/embed_test.go b/pkg/iac/rego/embed_test.go deleted file mode 100644 index 90541f5e2397..000000000000 --- a/pkg/iac/rego/embed_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package rego - -import ( - "testing" - - "github.com/open-policy-agent/opa/ast" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - checks "github.com/aquasecurity/trivy-checks" - "github.com/aquasecurity/trivy/pkg/iac/rules" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_EmbeddedLoading(t *testing.T) { - LoadAndRegister() - - frameworkRules := rules.GetRegistered() - var found bool - for _, rule := range frameworkRules { - if rule.GetRule().RegoPackage != "" { - found = true - } - } - assert.True(t, found, "no embedded rego policies were registered as rules") -} - -func Test_RegisterRegoRules(t *testing.T) { - var testCases = []struct { - name string - inputPolicy string - expectedError bool - }{ - { - name: "happy path old single schema", - inputPolicy: `# METADATA -# title: "dummy title" -# description: "some description" -# scope: package -# schemas: -# - input: schema["input"] -# custom: -# input: -# selector: -# - type: dockerfile -package builtin.dockerfile.DS1234 -deny[res]{ - res := true -}`, - }, - { - name: "happy path new builtin single schema", - inputPolicy: `# METADATA -# title: "dummy title" -# description: "some description" -# scope: package -# schemas: -# - input: schema["dockerfile"] -# custom: -# input: -# selector: -# - type: dockerfile -package builtin.dockerfile.DS1234 -deny[res]{ - res := true -}`, - }, - { - name: "happy path new multiple schemas", - inputPolicy: `# METADATA -# title: "dummy title" -# description: "some description" -# scope: package -# schemas: -# - input: schema["dockerfile"] -# - input: schema["kubernetes"] -# custom: -# input: -# selector: -# - type: dockerfile -package builtin.dockerfile.DS1234 -deny[res]{ - res := true -}`, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - policies, err := LoadPoliciesFromDirs(checks.EmbeddedLibraryFileSystem, ".") - require.NoError(t, err) - newRule, err := ast.ParseModuleWithOpts("/rules/newrule.rego", tc.inputPolicy, ast.ParserOptions{ - ProcessAnnotation: true, - }) - require.NoError(t, err) - - policies["/rules/newrule.rego"] = newRule - switch { - case tc.expectedError: - assert.Panics(t, func() { - RegisterRegoRules(policies) - }, tc.name) - default: - RegisterRegoRules(policies) - } - }) - } -} - -func Test_RegisterDeprecatedRule(t *testing.T) { - var testCases = []struct { - name string - id string - inputPolicy string - expected scan.Rule - }{ - { - name: "deprecated check", - id: "AVD-DEP-0001", - inputPolicy: `# METADATA -# title: "deprecated check" -# description: "some description" -# scope: package -# schemas: -# - input: schema["dockerfile"] -# custom: -# avd_id: AVD-DEP-0001 -# input: -# selector: -# - type: dockerfile -# deprecated: true -package builtin.dockerfile.DS1234 -deny[res]{ - res := true -}`, - expected: scan.Rule{ - Deprecated: true, - }, - }, - { - name: "not a deprecated check", - id: "AVD-NOTDEP-0001", - inputPolicy: `# METADATA -# title: "not a deprecated check" -# description: "some description" -# scope: package -# schemas: -# - input: schema["dockerfile"] -# custom: -# avd_id: AVD-NOTDEP-0001 -# input: -# selector: -# - type: dockerfile -package builtin.dockerfile.DS1234 -deny[res]{ - res := true -}`, - expected: scan.Rule{ - Deprecated: false, - }, - }, - { - name: "invalid deprecation value", - id: "AVD-BADDEP-0001", - inputPolicy: `# METADATA -# title: "badly deprecated check" -# description: "some description" -# scope: package -# schemas: -# - input: schema["dockerfile"] -# custom: -# avd_id: AVD-BADDEP-0001 -# input: -# selector: -# - type: dockerfile -# deprecated: "this is bad, deprecation is a bool value not a string" -package builtin.dockerfile.DS1234 -deny[res]{ - res := true -}`, - expected: scan.Rule{ - Deprecated: false, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - policies := make(map[string]*ast.Module) - newRule, err := ast.ParseModuleWithOpts("/rules/newrule.rego", tc.inputPolicy, ast.ParserOptions{ - ProcessAnnotation: true, - }) - require.NoError(t, err) - - policies["/rules/newrule.rego"] = newRule - assert.NotPanics(t, func() { - RegisterRegoRules(policies) - }) - - for _, rule := range rules.GetRegistered() { - if rule.AVDID == tc.id { - assert.Equal(t, tc.expected.Deprecated, rule.GetRule().Deprecated, tc.name) - } - } - }) - } -} diff --git a/pkg/iac/rego/load.go b/pkg/iac/rego/load.go deleted file mode 100644 index 1b1111290840..000000000000 --- a/pkg/iac/rego/load.go +++ /dev/null @@ -1,300 +0,0 @@ -package rego - -import ( - "context" - "fmt" - "io" - "io/fs" - "strings" - - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/bundle" - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" -) - -var builtinNamespaces = set.New("builtin", "defsec", "appshield") - -func BuiltinNamespaces() []string { - return builtinNamespaces.Items() -} - -func IsBuiltinNamespace(namespace string) bool { - return lo.ContainsBy(BuiltinNamespaces(), func(ns string) bool { - return strings.HasPrefix(namespace, ns+".") - }) -} - -func IsRegoFile(name string) bool { - return strings.HasSuffix(name, bundle.RegoExt) && !strings.HasSuffix(name, "_test"+bundle.RegoExt) -} - -func IsDotFile(name string) bool { - return strings.HasPrefix(name, ".") -} - -func (s *Scanner) loadPoliciesFromReaders(readers []io.Reader) (map[string]*ast.Module, error) { - modules := make(map[string]*ast.Module) - for i, r := range readers { - moduleName := fmt.Sprintf("reader_%d", i) - data, err := io.ReadAll(r) - if err != nil { - return nil, err - } - module, err := ast.ParseModuleWithOpts(moduleName, string(data), ast.ParserOptions{ - ProcessAnnotation: true, - }) - if err != nil { - return nil, err - } - modules[moduleName] = module - } - return modules, nil -} - -func (s *Scanner) loadEmbedded() error { - loaded, err := LoadEmbeddedLibraries() - if err != nil { - return fmt.Errorf("failed to load embedded rego libraries: %w", err) - } - s.embeddedLibs = loaded - s.logger.Debug("Embedded libraries are loaded", log.Int("count", len(loaded))) - - loaded, err = LoadEmbeddedPolicies() - if err != nil { - return fmt.Errorf("failed to load embedded rego checks: %w", err) - } - s.embeddedChecks = loaded - s.logger.Debug("Embedded checks are loaded", log.Int("count", len(loaded))) - - return nil -} - -func (s *Scanner) LoadPolicies(srcFS fs.FS) error { - - if s.policies == nil { - s.policies = make(map[string]*ast.Module) - } - - if s.policyFS != nil { - s.logger.Debug("Overriding filesystem for checks") - srcFS = s.policyFS - } - - if err := s.loadEmbedded(); err != nil { - return err - } - - if s.includeEmbeddedPolicies { - s.policies = lo.Assign(s.policies, s.embeddedChecks) - } - - if s.includeEmbeddedLibraries { - s.policies = lo.Assign(s.policies, s.embeddedLibs) - } - - var err error - if len(s.policyDirs) > 0 { - loaded, err := LoadPoliciesFromDirs(srcFS, s.policyDirs...) - if err != nil { - return fmt.Errorf("failed to load rego checks from %s: %w", s.policyDirs, err) - } - for name, policy := range loaded { - s.policies[name] = policy - } - s.logger.Debug("Checks from disk are loaded", log.Int("count", len(loaded))) - } - - if len(s.policyReaders) > 0 { - loaded, err := s.loadPoliciesFromReaders(s.policyReaders) - if err != nil { - return fmt.Errorf("failed to load rego checks from reader(s): %w", err) - } - for name, policy := range loaded { - s.policies[name] = policy - } - s.logger.Debug("Checks from readers are loaded", log.Int("count", len(loaded))) - } - - // gather namespaces - uniq := set.New[string]() - for _, module := range s.policies { - namespace := getModuleNamespace(module) - uniq.Append(namespace) - } - namespaces := uniq.Items() - - dataFS := srcFS - if s.dataFS != nil { - s.logger.Debug("Overriding filesystem for data") - dataFS = s.dataFS - } - store, err := initStore(dataFS, s.dataDirs, namespaces) - if err != nil { - return fmt.Errorf("unable to load data: %w", err) - } - s.store = store - - return s.compilePolicies(srcFS, s.policyDirs) -} - -func (s *Scanner) fallbackChecks(compiler *ast.Compiler) { - - var excludedFiles []string - - for _, e := range compiler.Errors { - if e.Location == nil { - continue - } - - loc := e.Location.File - - if lo.Contains(excludedFiles, loc) { - continue - } - - badPolicy, exists := s.policies[loc] - if !exists || badPolicy == nil { - continue - } - - if !IsBuiltinNamespace(getModuleNamespace(badPolicy)) { - continue - } - - s.logger.Error( - "Error occurred while parsing. Trying to fallback to embedded check", - log.FilePath(loc), - log.Err(e), - ) - - embedded := s.findMatchedEmbeddedCheck(badPolicy) - if embedded == nil { - s.logger.Error("Failed to find embedded check, skipping", log.FilePath(loc)) - continue - } - - s.logger.Debug("Found embedded check", log.FilePath(embedded.Package.Location.File)) - delete(s.policies, loc) // remove bad check - s.policies[embedded.Package.Location.File] = embedded - delete(s.embeddedChecks, embedded.Package.Location.File) // avoid infinite loop if embedded check contains ref error - excludedFiles = append(excludedFiles, e.Location.File) - } - - compiler.Errors = lo.Filter(compiler.Errors, func(e *ast.Error, _ int) bool { - return e.Location == nil || !lo.Contains(excludedFiles, e.Location.File) - }) -} - -func (s *Scanner) findMatchedEmbeddedCheck(badPolicy *ast.Module) *ast.Module { - for _, embeddedCheck := range s.embeddedChecks { - if embeddedCheck.Package.Path.String() == badPolicy.Package.Path.String() { - return embeddedCheck - } - } - - badPolicyMeta, err := metadataFromRegoModule(badPolicy) - if err != nil { - return nil - } - - for _, embeddedCheck := range s.embeddedChecks { - meta, err := metadataFromRegoModule(embeddedCheck) - if err != nil { - continue - } - if badPolicyMeta.AVDID != "" && badPolicyMeta.AVDID == meta.AVDID { - return embeddedCheck - } - } - return nil -} - -func (s *Scanner) prunePoliciesWithError(compiler *ast.Compiler) error { - if len(compiler.Errors) > s.regoErrorLimit { - s.logger.Error("Error(s) occurred while loading checks") - return compiler.Errors - } - - for _, e := range compiler.Errors { - if e.Location == nil { - continue - } - s.logger.Error( - "Error occurred while parsing", - log.FilePath(e.Location.File), log.Err(e), - ) - delete(s.policies, e.Location.File) - } - return nil -} - -func (s *Scanner) compilePolicies(srcFS fs.FS, paths []string) error { - - schemaSet, err := BuildSchemaSetFromPolicies(s.policies, paths, srcFS, s.customSchemas) - if err != nil { - return err - } - - compiler := ast.NewCompiler(). - WithUseTypeCheckAnnotations(true). - WithCapabilities(ast.CapabilitiesForThisVersion()). - WithSchemas(schemaSet) - - compiler.Compile(s.policies) - if compiler.Failed() { - s.fallbackChecks(compiler) - if err := s.prunePoliciesWithError(compiler); err != nil { - return err - } - return s.compilePolicies(srcFS, paths) - } - retriever := NewMetadataRetriever(compiler) - - if err := s.filterModules(retriever); err != nil { - return err - } - s.compiler = compiler - s.retriever = retriever - return nil -} - -func (s *Scanner) filterModules(retriever *MetadataRetriever) error { - - filtered := make(map[string]*ast.Module) - for name, module := range s.policies { - meta, err := retriever.RetrieveMetadata(context.TODO(), module) - if err != nil { - return err - } - - if !meta.hasAnyFramework(s.frameworks) { - continue - } - - if IsBuiltinNamespace(getModuleNamespace(module)) { - if s.disabledCheckIDs.Contains(meta.ID) { // ignore builtin disabled checks - continue - } - } - - if len(meta.InputOptions.Selectors) == 0 { - if !meta.Library { - s.logger.Warn( - "Module has no input selectors - it will be loaded for all inputs!", - log.FilePath(module.Package.Location.File), - log.String("module", name), - ) - } - filtered[name] = module - continue - } - - filtered[name] = module - } - - s.policies = filtered - return nil -} diff --git a/pkg/iac/rego/load_stub.go b/pkg/iac/rego/load_stub.go new file mode 100644 index 000000000000..c85e0561ec0c --- /dev/null +++ b/pkg/iac/rego/load_stub.go @@ -0,0 +1,21 @@ +package rego + +import ( + "strings" + + "github.com/samber/lo" + + "github.com/aquasecurity/trivy/pkg/set" +) + +var builtinNamespaces = set.New("builtin", "defsec", "appshield") + +func BuiltinNamespaces() []string { + return builtinNamespaces.Items() +} + +func IsBuiltinNamespace(namespace string) bool { + return lo.ContainsBy(BuiltinNamespaces(), func(ns string) bool { + return strings.HasPrefix(namespace, ns+".") + }) +} diff --git a/pkg/iac/rego/load_test.go b/pkg/iac/rego/load_test.go deleted file mode 100644 index 746e5830a3dd..000000000000 --- a/pkg/iac/rego/load_test.go +++ /dev/null @@ -1,255 +0,0 @@ -package rego_test - -import ( - "bytes" - "embed" - "fmt" - "log/slog" - "strings" - "testing" - "testing/fstest" - - "github.com/open-policy-agent/opa/ast" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - checks "github.com/aquasecurity/trivy-checks" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/log" -) - -//go:embed all:testdata/policies -var testEmbedFS embed.FS - -//go:embed testdata/embedded -var embeddedChecksFS embed.FS - -func Test_RegoScanning_WithSomeInvalidPolicies(t *testing.T) { - t.Run("allow no errors", func(t *testing.T) { - var debugBuf bytes.Buffer - slog.SetDefault(log.New(log.NewHandler(&debugBuf, nil))) - scanner := rego.NewScanner( - rego.WithRegoErrorLimits(0), - rego.WithPolicyDirs("."), - ) - - err := scanner.LoadPolicies(testEmbedFS) - require.ErrorContains(t, err, `want (one of): ["Cmd" "EndLine" "Flags" "JSON" "Original" "Path" "Stage" "StartLine" "SubCmd" "Value"]`) - assert.Contains(t, debugBuf.String(), "Error(s) occurred while loading checks") - }) - - t.Run("allow up to max 1 error", func(t *testing.T) { - var debugBuf bytes.Buffer - slog.SetDefault(log.New(log.NewHandler(&debugBuf, nil))) - scanner := rego.NewScanner( - rego.WithRegoErrorLimits(1), - rego.WithPolicyDirs("."), - ) - - err := scanner.LoadPolicies(testEmbedFS) - require.NoError(t, err) - - assert.Contains(t, debugBuf.String(), "Error occurred while parsing\tfile_path=\"testdata/policies/invalid.rego\" err=\"testdata/policies/invalid.rego:7") - }) - - t.Run("schema does not exist", func(t *testing.T) { - check := `# METADATA -# schemas: -# - input: schema["fooschema"] -package mypackage - -deny { - input.evil == "foo bar" -}` - scanner := rego.NewScanner( - rego.WithPolicyDirs("."), - rego.WithPolicyReader(strings.NewReader(check)), - ) - - err := scanner.LoadPolicies(fstest.MapFS{}) - assert.ErrorContains(t, err, "could not find schema \"fooschema\"") - }) - - t.Run("schema is invalid", func(t *testing.T) { - check := `# METADATA -# schemas: -# - input: schema["fooschema"] -package mypackage - -deny { - input.evil == "foo bar" -}` - scanner := rego.NewScanner( - rego.WithPolicyDirs("."), - rego.WithPolicyReader(strings.NewReader(check)), - ) - - fsys := fstest.MapFS{ - "schemas/fooschema.json": &fstest.MapFile{ - Data: []byte("bad json"), - }, - } - - err := scanner.LoadPolicies(fsys) - assert.ErrorContains(t, err, "could not parse schema \"fooschema\"") - }) - - t.Run("schema is not specified", func(t *testing.T) { - check := `package mypackage - -deny { - input.evil == "foo bar" -}` - scanner := rego.NewScanner( - rego.WithPolicyDirs("."), - rego.WithPolicyReader(strings.NewReader(check)), - ) - err := scanner.LoadPolicies(fstest.MapFS{}) - require.NoError(t, err) - }) -} - -func Test_FallbackToEmbedded(t *testing.T) { - tests := []struct { - name string - files map[string]*fstest.MapFile - expectedErr string - }{ - { - name: "match by namespace", - files: map[string]*fstest.MapFile{ - "policies/my-check2.rego": { - Data: []byte(`# METADATA -# schemas: -# - input: schema["fooschema"] - -package builtin.test - -deny { - input.evil == "foo bar" -}`, - ), - }, - }, - }, - { - name: "match by check ID", - files: map[string]*fstest.MapFile{ - "policies/my-check2.rego": { - Data: []byte(`# METADATA -# schemas: -# - input: schema["fooschema"] -# custom: -# avd_id: test-001 -package builtin.test2 - -deny { - input.evil == "foo bar" -}`, - ), - }, - }, - }, - { - name: "bad embedded check", - files: map[string]*fstest.MapFile{ - "policies/my-check2.rego": { - Data: []byte(`# METADATA -# schemas: -# - input: schema["fooschema"] -package builtin.bad.test - -deny { - input.evil == "foo bar" -}`, - ), - }, - }, - expectedErr: "testdata/embedded/bad-check.rego:8: rego_type_error: undefined ref", - }, - { - name: "with non existent function", - files: map[string]*fstest.MapFile{ - "policies/my-check2.rego": { - Data: []byte(`# METADATA -# schemas: -# - input: schema["fooschema"] -package builtin.test - -deny { - input.foo == fn.is_foo("foo") -}`, - ), - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - scanner := rego.NewScanner( - rego.WithRegoErrorLimits(0), - rego.WithEmbeddedPolicies(false), - rego.WithPolicyDirs("."), - ) - - tt.files["schemas/fooschema.json"] = &fstest.MapFile{ - Data: []byte(`{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "foo": { - "type": "string" - } - } - }`), - } - - checks.EmbeddedPolicyFileSystem = embeddedChecksFS - err := scanner.LoadPolicies(fstest.MapFS(tt.files)) - - if tt.expectedErr != "" { - assert.ErrorContains(t, err, tt.expectedErr) - } else { - require.NoError(t, err) - } - }) - } -} - -func Test_FallbackErrorWithoutLocation(t *testing.T) { - fsys := fstest.MapFS{ - "schemas/fooschema.json": { - Data: []byte(`{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "foo": { - "type": "string" - } - } - }`), - }, - } - - for i := 0; i < ast.CompileErrorLimitDefault+1; i++ { - src := `# METADATA -# schemas: -# - input: schema["fooschema"] -package builtin.test%d - -deny { - input.evil == "foo bar" -}` - fsys[fmt.Sprintf("policies/my-check%d.rego", i)] = &fstest.MapFile{ - Data: []byte(fmt.Sprintf(src, i)), - } - } - - scanner := rego.NewScanner( - rego.WithEmbeddedPolicies(false), - rego.WithPolicyDirs("."), - ) - err := scanner.LoadPolicies(fsys) - require.Error(t, err) -} diff --git a/pkg/iac/rego/metadata.go b/pkg/iac/rego/metadata.go deleted file mode 100644 index 637b30ea5122..000000000000 --- a/pkg/iac/rego/metadata.go +++ /dev/null @@ -1,460 +0,0 @@ -package rego - -import ( - "context" - "errors" - "fmt" - "strings" - - "github.com/mitchellh/mapstructure" - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/providers" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/severity" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -const annotationScopePackage = "package" - -type StaticMetadata struct { - Deprecated bool - ID string - AVDID string - Title string - ShortCode string - Aliases []string - Description string - Severity string - RecommendedActions string - PrimaryURL string - References []string - InputOptions InputOptions - Package string - Frameworks map[framework.Framework][]string - Provider string - Service string - Library bool - CloudFormation *scan.EngineMetadata - Terraform *scan.EngineMetadata - Examples string -} - -func NewStaticMetadata(pkgPath string, inputOpt InputOptions) *StaticMetadata { - return &StaticMetadata{ - ID: "N/A", - Title: "N/A", - Severity: "UNKNOWN", - Description: fmt.Sprintf("Rego module: %s", pkgPath), - Package: pkgPath, - InputOptions: inputOpt, - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - } -} - -func (sm *StaticMetadata) update(meta map[string]any) error { - if sm.Frameworks == nil { - sm.Frameworks = make(map[framework.Framework][]string) - } - - upd := func(field *string, key string) { - if raw, ok := meta[key]; ok { - *field = fmt.Sprintf("%s", raw) - } - } - - upd(&sm.ID, "id") - upd(&sm.AVDID, "avd_id") - upd(&sm.Title, "title") - upd(&sm.ShortCode, "short_code") - upd(&sm.Description, "description") - upd(&sm.Service, "service") - upd(&sm.Provider, "provider") - upd(&sm.RecommendedActions, "recommended_actions") - upd(&sm.RecommendedActions, "recommended_action") - upd(&sm.Examples, "examples") - - if raw, ok := meta["deprecated"]; ok { - if dep, ok := raw.(bool); ok { - sm.Deprecated = dep - } - } - - if raw, ok := meta["severity"]; ok { - sm.Severity = strings.ToUpper(fmt.Sprintf("%s", raw)) - } - - if raw, ok := meta["library"]; ok { - if lib, ok := raw.(bool); ok { - sm.Library = lib - } - } - - if raw, ok := meta["url"]; ok { - sm.References = append(sm.References, fmt.Sprintf("%s", raw)) - } - - if raw, ok := meta["related_resources"]; ok { - switch relatedResources := raw.(type) { - case []map[string]any: - for _, relatedResource := range relatedResources { - if raw, ok := relatedResource["ref"]; ok { - sm.References = append(sm.References, fmt.Sprintf("%s", raw)) - } - } - case []string: - sm.References = append(sm.References, relatedResources...) - } - } - - if err := sm.updateFrameworks(meta); err != nil { - return fmt.Errorf("failed to update frameworks: %w", err) - } - sm.updateAliases(meta) - - var err error - if sm.CloudFormation, err = NewEngineMetadata("cloud_formation", meta); err != nil { - return err - } - - if sm.Terraform, err = NewEngineMetadata("terraform", meta); err != nil { - return err - } - - return nil -} - -func (sm *StaticMetadata) updateFrameworks(meta map[string]any) error { - raw, ok := meta["frameworks"] - if !ok { - return nil - } - - frameworks, ok := raw.(map[string]any) - if !ok { - return fmt.Errorf("frameworks metadata is not an object, got %T", raw) - } - - if len(frameworks) > 0 { - sm.Frameworks = make(map[framework.Framework][]string) - } - - for fw, rawIDs := range frameworks { - ids, ok := rawIDs.([]any) - if !ok { - return fmt.Errorf("framework ids is not an array, got %T", rawIDs) - } - fr := framework.Framework(fw) - for _, id := range ids { - if str, ok := id.(string); ok { - sm.Frameworks[fr] = append(sm.Frameworks[fr], str) - } else { - sm.Frameworks[fr] = []string{} - } - } - } - return nil -} - -func (sm *StaticMetadata) updateAliases(meta map[string]any) { - if raw, ok := meta["aliases"]; ok { - if aliases, ok := raw.([]any); ok { - for _, a := range aliases { - sm.Aliases = append(sm.Aliases, fmt.Sprintf("%s", a)) - } - } - } -} - -func (sm *StaticMetadata) FromAnnotations(annotations *ast.Annotations) error { - sm.Title = annotations.Title - sm.Description = annotations.Description - for _, resource := range annotations.RelatedResources { - if !resource.Ref.IsAbs() { - continue - } - sm.References = append(sm.References, resource.Ref.String()) - } - if custom := annotations.Custom; custom != nil { - if err := sm.update(custom); err != nil { - return err - } - } - if len(annotations.RelatedResources) > 0 { - sm.PrimaryURL = annotations.RelatedResources[0].Ref.String() - } - return nil -} - -func NewEngineMetadata(schema string, meta map[string]any) (*scan.EngineMetadata, error) { - var sMap map[string]any - if raw, ok := meta[schema]; ok { - sMap, ok = raw.(map[string]any) - if !ok { - return nil, fmt.Errorf("failed to parse %s metadata: not an object", schema) - } - } - - var em scan.EngineMetadata - if val, ok := sMap["good_examples"].(string); ok { - em.GoodExamples = []string{val} - } - if val, ok := sMap["bad_examples"].(string); ok { - em.BadExamples = []string{val} - } - switch links := sMap["links"].(type) { - case string: - em.Links = []string{links} - case []any: - for _, v := range links { - if str, ok := v.(string); ok { - em.Links = append(em.Links, str) - } - } - } - if val, ok := sMap["remediation_markdown"].(string); ok { - em.RemediationMarkdown = val - } - - return &em, nil -} - -type InputOptions struct { - Selectors []Selector -} - -type Selector struct { - Type string - Subtypes []SubType -} - -type SubType struct { - Group string - Version string - Kind string - Namespace string - Service string // only for cloud - Provider string // only for cloud -} - -func (m StaticMetadata) ToRule() scan.Rule { - - provider := "generic" - if m.Provider != "" { - provider = m.Provider - } else if len(m.InputOptions.Selectors) > 0 { - provider = m.InputOptions.Selectors[0].Type - } - service := "general" - if m.Service != "" { - service = m.Service - } - - return scan.Rule{ - Deprecated: m.Deprecated, - AVDID: m.AVDID, - Aliases: append(m.Aliases, m.ID), - ShortCode: m.ShortCode, - Summary: m.Title, - Explanation: m.Description, - Impact: "", - Resolution: m.RecommendedActions, - Provider: providers.Provider(provider), - Service: service, - Links: m.References, - Severity: severity.Severity(m.Severity), - RegoPackage: m.Package, - Frameworks: m.Frameworks, - CloudFormation: m.CloudFormation, - Terraform: m.Terraform, - Examples: m.Examples, - } -} - -type MetadataRetriever struct { - compiler *ast.Compiler -} - -func NewMetadataRetriever(compiler *ast.Compiler) *MetadataRetriever { - return &MetadataRetriever{ - compiler: compiler, - } -} - -func (m *MetadataRetriever) findPackageAnnotations(module *ast.Module) *ast.Annotations { - return lo.FindOrElse(module.Annotations, nil, func(a *ast.Annotations) bool { - return a.Scope == annotationScopePackage - }) -} - -func (m *MetadataRetriever) RetrieveMetadata(ctx context.Context, module *ast.Module, contents ...any) (*StaticMetadata, error) { - - metadata := NewStaticMetadata( - module.Package.Path.String(), - m.queryInputOptions(ctx, module), - ) - - // read metadata from official rego annotations if possible - if annotations := m.findPackageAnnotations(module); annotations != nil { - if err := metadata.FromAnnotations(annotations); err != nil { - return nil, err - } - return metadata, nil - } - - // otherwise, try to read metadata from the rego module itself - we used to do this before annotations were a thing - namespace := getModuleNamespace(module) - metadataQuery := fmt.Sprintf("data.%s.__rego_metadata__", namespace) - - options := []func(*rego.Rego){ - rego.Query(metadataQuery), - rego.Compiler(m.compiler), - rego.Capabilities(nil), - } - // support dynamic metadata fields - for _, in := range contents { - options = append(options, rego.Input(in)) - } - - instance := rego.New(options...) - set, err := instance.Eval(ctx) - if err != nil { - return nil, err - } - - // no metadata supplied - if set == nil { - return metadata, nil - } - - if len(set) != 1 { - return nil, errors.New("failed to parse metadata: unexpected set length") - } - if len(set[0].Expressions) != 1 { - return nil, errors.New("failed to parse metadata: unexpected expression length") - } - expression := set[0].Expressions[0] - meta, ok := expression.Value.(map[string]any) - if !ok { - return nil, errors.New("failed to parse metadata: not an object") - } - - if err := metadata.update(meta); err != nil { - return nil, err - } - - return metadata, nil -} - -// nolint: gocyclo -func (m *MetadataRetriever) queryInputOptions(ctx context.Context, module *ast.Module) InputOptions { - - options := InputOptions{ - Selectors: nil, - } - - var metadata map[string]any - - // read metadata from official rego annotations if possible - if annotation := m.findPackageAnnotations(module); annotation != nil && annotation.Custom != nil { - if input, ok := annotation.Custom["input"]; ok { - if mapped, ok := input.(map[string]any); ok { - metadata = mapped - } - } - } - - if metadata == nil { - - namespace := getModuleNamespace(module) - inputOptionQuery := fmt.Sprintf("data.%s.__rego_input__", namespace) - instance := rego.New( - rego.Query(inputOptionQuery), - rego.Compiler(m.compiler), - rego.Capabilities(nil), - ) - set, err := instance.Eval(ctx) - if err != nil { - return options - } - - if len(set) != 1 { - return options - } - if len(set[0].Expressions) != 1 { - return options - } - expression := set[0].Expressions[0] - meta, ok := expression.Value.(map[string]any) - if !ok { - return options - } - metadata = meta - } - - if raw, ok := metadata["selector"]; ok { - if each, ok := raw.([]any); ok { - for _, rawSelector := range each { - var selector Selector - if selectorMap, ok := rawSelector.(map[string]any); ok { - if rawType, ok := selectorMap["type"]; ok { - selector.Type = fmt.Sprintf("%s", rawType) - // handle backward compatibility for "defsec" source type which is now "cloud" - if selector.Type == string(iacTypes.SourceDefsec) { - selector.Type = string(iacTypes.SourceCloud) - } - } - if subType, ok := selectorMap["subtypes"].([]any); ok { - for _, subT := range subType { - if st, ok := subT.(map[string]any); ok { - s := SubType{} - _ = mapstructure.Decode(st, &s) - selector.Subtypes = append(selector.Subtypes, s) - } - } - } - } - options.Selectors = append(options.Selectors, selector) - } - } - } - - return options - -} - -func getModuleNamespace(module *ast.Module) string { - return strings.TrimPrefix(module.Package.Path.String(), "data.") -} - -func metadataFromRegoModule(module *ast.Module) (*StaticMetadata, error) { - meta := new(StaticMetadata) - for _, annotation := range module.Annotations { - if annotation.Scope == "package" { - if err := meta.FromAnnotations(annotation); err != nil { - return nil, err - } - break - } - } - return meta, nil -} - -func (m *StaticMetadata) hasAnyFramework(frameworks []framework.Framework) bool { - if len(frameworks) == 0 { - frameworks = []framework.Framework{framework.Default} - } - - for _, fr := range frameworks { - if _, exists := m.Frameworks[fr]; exists { - return true - } - } - - return false -} diff --git a/pkg/iac/rego/metadata_test.go b/pkg/iac/rego/metadata_test.go deleted file mode 100644 index b421df69bb30..000000000000 --- a/pkg/iac/rego/metadata_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package rego - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_UpdateStaticMetadata(t *testing.T) { - t.Run("happy", func(t *testing.T) { - sm := StaticMetadata{ - ID: "i", - AVDID: "a", - Title: "t", - ShortCode: "sc", - Aliases: []string{"a", "b", "c"}, - Description: "d", - Severity: "s", - RecommendedActions: "ra", - PrimaryURL: "pu", - References: []string{"r"}, - Package: "pkg", - Provider: "pr", - Service: "srvc", - Library: false, - } - - require.NoError(t, sm.update( - map[string]any{ - "id": "i_n", - "avd_id": "a_n", - "title": "t_n", - "short_code": "sc_n", - "aliases": []any{"a_n", "b_n", "c_n"}, - "description": "d_n", - "service": "srvc_n", - "provider": "pr_n", - "recommended_actions": "ra_n", - "severity": "s_n", - "library": true, - "url": "r_n", - "frameworks": map[string]any{ - "all": []any{"aa"}, - }, - }, - )) - - expected := StaticMetadata{ - ID: "i_n", - AVDID: "a_n", - Title: "t_n", - ShortCode: "sc_n", - Aliases: []string{"a", "b", "c", "a_n", "b_n", "c_n"}, - Description: "d_n", - Severity: "S_N", - RecommendedActions: "ra_n", - PrimaryURL: "pu", - References: []string{"r", "r_n"}, - Package: "pkg", - Provider: "pr_n", - Service: "srvc_n", - Library: true, - Frameworks: map[framework.Framework][]string{ - framework.ALL: {"aa"}, - }, - CloudFormation: &scan.EngineMetadata{}, - Terraform: &scan.EngineMetadata{}, - } - - assert.Equal(t, expected, sm) - }) - - t.Run("related resources are a map", func(t *testing.T) { - sm := StaticMetadata{ - References: []string{"r"}, - } - require.NoError(t, sm.update(map[string]any{ - "related_resources": []map[string]any{ - { - "ref": "r1_n", - }, - { - "ref": "r2_n", - }, - }, - })) - - expected := StaticMetadata{ - References: []string{"r", "r1_n", "r2_n"}, - CloudFormation: &scan.EngineMetadata{}, - Terraform: &scan.EngineMetadata{}, - Frameworks: make(map[framework.Framework][]string), - } - - assert.Equal(t, expected, sm) - }) - - t.Run("related resources are a string", func(t *testing.T) { - sm := StaticMetadata{ - References: []string{"r"}, - } - require.NoError(t, sm.update(map[string]any{ - "related_resources": []string{"r1_n", "r2_n"}, - })) - - expected := StaticMetadata{ - References: []string{"r", "r1_n", "r2_n"}, - CloudFormation: &scan.EngineMetadata{}, - Terraform: &scan.EngineMetadata{}, - Frameworks: make(map[framework.Framework][]string), - } - - assert.Equal(t, expected, sm) - }) - - t.Run("check is deprecated", func(t *testing.T) { - sm := StaticMetadata{ - Deprecated: false, - } - require.NoError(t, sm.update(map[string]any{ - "deprecated": true, - })) - - expected := StaticMetadata{ - Deprecated: true, - CloudFormation: &scan.EngineMetadata{}, - Terraform: &scan.EngineMetadata{}, - Frameworks: make(map[framework.Framework][]string), - } - - assert.Equal(t, expected, sm) - }) - - t.Run("frameworks is not initialized", func(t *testing.T) { - sm := StaticMetadata{} - err := sm.update(map[string]any{ - "frameworks": map[string]any{"all": []any{"a", "b", "c"}}, - }) - require.NoError(t, err) - }) -} - -func Test_NewEngineMetadata(t *testing.T) { - inputSchema := map[string]any{ - "terraform": map[string]any{ - "good_examples": `resource "aws_cloudtrail" "good_example" { - is_multi_region_trail = true - - event_selector { - read_write_type = "All" - include_management_events = true - - data_resource { - type = "AWS::S3::Object" - values = ["${data.aws_s3_bucket.important-bucket.arn}/"] - } - } - }`, - - "links": "https://avd.aquasec.com/avd/183", - }, - "cloud_formation": map[string]any{ - "good_examples": `--- -Resources: - GoodExample: - Type: AWS::CloudTrail::Trail - Properties: - IsLogging: true - IsMultiRegionTrail: true - S3BucketName: "CloudtrailBucket" - S3KeyPrefix: "/trailing" - TrailName: "Cloudtrail"`, - "links": []any{"https://avd.aquasec.com/avd/183"}, - }, - } - - var testCases = []struct { - schema string - want *scan.EngineMetadata - }{ - { - schema: "terraform", - want: &scan.EngineMetadata{ - GoodExamples: []string{ - `resource "aws_cloudtrail" "good_example" { - is_multi_region_trail = true - - event_selector { - read_write_type = "All" - include_management_events = true - - data_resource { - type = "AWS::S3::Object" - values = ["${data.aws_s3_bucket.important-bucket.arn}/"] - } - } - }`, - }, - Links: []string{"https://avd.aquasec.com/avd/183"}, - }, - }, - { - schema: "cloud_formation", - want: &scan.EngineMetadata{ - GoodExamples: []string{ - `--- -Resources: - GoodExample: - Type: AWS::CloudTrail::Trail - Properties: - IsLogging: true - IsMultiRegionTrail: true - S3BucketName: "CloudtrailBucket" - S3KeyPrefix: "/trailing" - TrailName: "Cloudtrail"`, - }, - Links: []string{"https://avd.aquasec.com/avd/183"}, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.schema, func(t *testing.T) { - em, err := NewEngineMetadata(tc.schema, inputSchema) - require.NoError(t, err) - assert.Equal(t, tc.want, em) - }) - } -} diff --git a/pkg/iac/rego/options.go b/pkg/iac/rego/options.go deleted file mode 100644 index 31026b2f3b57..000000000000 --- a/pkg/iac/rego/options.go +++ /dev/null @@ -1,132 +0,0 @@ -package rego - -import ( - "io" - "io/fs" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" -) - -func WithPolicyReader(readers ...io.Reader) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.policyReaders = readers - } - } -} - -func WithEmbeddedPolicies(include bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.includeEmbeddedPolicies = include - } - } -} - -func WithEmbeddedLibraries(include bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.includeEmbeddedLibraries = include - } - } -} - -// WithTrace specifies an io.Writer for trace logs (mainly rego tracing) - if not set, they are discarded -func WithTrace(w io.Writer) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.traceWriter = w - } - } -} - -func WithPerResultTracing(enabled bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.tracePerResult = enabled - } - } -} - -func WithPolicyDirs(paths ...string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.policyDirs = paths - } - } -} - -func WithDataDirs(paths ...string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.dataDirs = paths - } - } -} - -// WithPolicyNamespaces - namespaces which indicate rego policies containing enforced rules -func WithPolicyNamespaces(namespaces ...string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.ruleNamespaces.Append(namespaces...) - } - } -} - -func WithPolicyFilesystem(fsys fs.FS) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.policyFS = fsys - } - } -} - -func WithDataFilesystem(fsys fs.FS) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.dataFS = fsys - } - } -} - -func WithRegoErrorLimits(limit int) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.regoErrorLimit = limit - } - } -} - -func WithCustomSchemas(schemas map[string][]byte) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.customSchemas = schemas - } - } -} - -// WithDisabledCheckIDs disables checks by their ID (ID field in metadata) -func WithDisabledCheckIDs(ids ...string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.disabledCheckIDs.Append(ids...) - } - } -} - -func WithIncludeDeprecatedChecks(enabled bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.includeDeprecatedChecks = true - } - } -} - -func WithFrameworks(frameworks ...framework.Framework) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if ss, ok := s.(*Scanner); ok { - ss.frameworks = frameworks - } - } -} diff --git a/pkg/iac/rego/result.go b/pkg/iac/rego/result.go deleted file mode 100644 index 87723a11ee3d..000000000000 --- a/pkg/iac/rego/result.go +++ /dev/null @@ -1,167 +0,0 @@ -package rego - -import ( - "fmt" - "io/fs" - "strconv" - - "github.com/open-policy-agent/opa/rego" - - "github.com/aquasecurity/trivy/pkg/iac/scan" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type regoResult struct { - Filepath string - Resource string - StartLine int - EndLine int - SourcePrefix string - Message string - Explicit bool - Managed bool - FSKey string - FS fs.FS - Parent *regoResult -} - -func (r regoResult) GetMetadata() iacTypes.Metadata { - var m iacTypes.Metadata - if !r.Managed { - m = iacTypes.NewUnmanagedMetadata() - } else { - rng := iacTypes.NewRangeWithFSKey(r.Filepath, r.StartLine, r.EndLine, r.SourcePrefix, r.FSKey, r.FS) - if r.Explicit { - m = iacTypes.NewExplicitMetadata(rng, r.Resource) - } else { - m = iacTypes.NewMetadata(rng, r.Resource) - } - } - if r.Parent != nil { - return m.WithParent(r.Parent.GetMetadata()) - } - return m -} - -func (r regoResult) GetRawValue() any { - return nil -} - -func parseResult(raw any) *regoResult { - var result regoResult - result.Managed = true - switch val := raw.(type) { - case []any: - var msg string - for _, item := range val { - switch raw := item.(type) { - case map[string]any: - result = parseCause(raw) - case string: - msg = raw - } - } - result.Message = msg - case string: - result.Message = val - case map[string]any: - result = parseCause(val) - default: - result.Message = "Rego check resulted in DENY" - } - return &result -} - -func parseCause(cause map[string]any) regoResult { - var result regoResult - result.Managed = true - if msg, ok := cause["msg"]; ok { - result.Message = fmt.Sprintf("%s", msg) - } - if filepath, ok := cause["filepath"]; ok { - result.Filepath = fmt.Sprintf("%s", filepath) - } - if msg, ok := cause["fskey"]; ok { - result.FSKey = fmt.Sprintf("%s", msg) - } - if msg, ok := cause["resource"]; ok { - result.Resource = fmt.Sprintf("%s", msg) - } - if start, ok := cause["startline"]; ok { - result.StartLine = parseLineNumber(start) - } - if end, ok := cause["endline"]; ok { - result.EndLine = parseLineNumber(end) - } - if prefix, ok := cause["sourceprefix"]; ok { - result.SourcePrefix = fmt.Sprintf("%s", prefix) - } - if explicit, ok := cause["explicit"]; ok { - if set, ok := explicit.(bool); ok { - result.Explicit = set - } - } - if managed, ok := cause["managed"]; ok { - if set, ok := managed.(bool); ok { - result.Managed = set - } - } - if parent, ok := cause["parent"]; ok { - if m, ok := parent.(map[string]any); ok { - parentResult := parseCause(m) - result.Parent = &parentResult - } - } - return result -} - -func parseLineNumber(raw any) int { - str := fmt.Sprintf("%s", raw) - n, _ := strconv.Atoi(str) - return n -} - -func (s *Scanner) convertResults(resultSet rego.ResultSet, input Input, namespace, rule string, traces []string) scan.Results { - var results scan.Results - - offset := 0 - if input.Contents != nil { - if xx, ok := input.Contents.(map[string]any); ok { - if md, ok := xx["__defsec_metadata"]; ok { - if md2, ok := md.(map[string]any); ok { - if sl, ok := md2["offset"]; ok { - offset, _ = sl.(int) - } - } - } - } - } - for _, result := range resultSet { - for _, expression := range result.Expressions { - values, ok := expression.Value.([]any) - if !ok { - values = []any{expression.Value} - } - - for _, value := range values { - regoResult := parseResult(value) - regoResult.FS = input.FS - if regoResult.Filepath == "" && input.Path != "" { - regoResult.Filepath = input.Path - } - if regoResult.Message == "" { - regoResult.Message = fmt.Sprintf("Rego check rule: %s.%s", namespace, rule) - } - regoResult.StartLine += offset - regoResult.EndLine += offset - results.AddRego(regoResult.Message, namespace, rule, traces, regoResult) - } - } - } - return results -} - -func (s *Scanner) embellishResultsWithRuleMetadata(results scan.Results, metadata StaticMetadata) scan.Results { - results.SetRule(metadata.ToRule()) - return results -} diff --git a/pkg/iac/rego/result_test.go b/pkg/iac/rego/result_test.go deleted file mode 100644 index fe1b1df846aa..000000000000 --- a/pkg/iac/rego/result_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package rego - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_parseResult(t *testing.T) { - var testCases = []struct { - name string - input any - want regoResult - }{ - { - name: "unknown", - input: nil, - want: regoResult{ - Managed: true, - Message: "Rego check resulted in DENY", - }, - }, - { - name: "string", - input: "message", - want: regoResult{ - Managed: true, - Message: "message", - }, - }, - { - name: "strings", - input: []any{"message"}, - want: regoResult{ - Managed: true, - Message: "message", - }, - }, - { - name: "maps", - input: []any{ - "message", - map[string]any{ - "filepath": "a.out", - }, - }, - want: regoResult{ - Managed: true, - Message: "message", - Filepath: "a.out", - }, - }, - { - name: "map", - input: map[string]any{ - "msg": "message", - "filepath": "a.out", - "fskey": "abcd", - "resource": "resource", - "startline": "123", - "endline": "456", - "sourceprefix": "git", - "explicit": true, - "managed": true, - }, - want: regoResult{ - Message: "message", - Filepath: "a.out", - Resource: "resource", - StartLine: 123, - EndLine: 456, - SourcePrefix: "git", - FSKey: "abcd", - Explicit: true, - Managed: true, - }, - }, - { - name: "parent", - input: map[string]any{ - "msg": "child", - "parent": map[string]any{ - "msg": "parent", - }, - }, - want: regoResult{ - Message: "child", - Managed: true, - Parent: ®oResult{ - Message: "parent", - Managed: true, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - have := parseResult(tc.input) - assert.NotNil(t, have) - assert.Equal(t, tc.want, *have) - }) - } -} diff --git a/pkg/iac/rego/runtime.go b/pkg/iac/rego/runtime.go deleted file mode 100644 index 6e28268d9971..000000000000 --- a/pkg/iac/rego/runtime.go +++ /dev/null @@ -1,28 +0,0 @@ -package rego - -import ( - "os" - "strings" - - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/version" -) - -func addRuntimeValues() *ast.Term { - env := ast.NewObject() - for _, pair := range os.Environ() { - parts := strings.SplitN(pair, "=", 2) - if len(parts) == 1 { - env.Insert(ast.StringTerm(parts[0]), ast.NullTerm()) - } else if len(parts) > 1 { - env.Insert(ast.StringTerm(parts[0]), ast.StringTerm(parts[1])) - } - } - - obj := ast.NewObject() - obj.Insert(ast.StringTerm("env"), ast.NewTerm(env)) - obj.Insert(ast.StringTerm("version"), ast.StringTerm(version.Version)) - obj.Insert(ast.StringTerm("commit"), ast.StringTerm(version.Vcs)) - - return ast.NewTerm(obj) -} diff --git a/pkg/iac/rego/scanner.go b/pkg/iac/rego/scanner.go deleted file mode 100644 index de80bedc6883..000000000000 --- a/pkg/iac/rego/scanner.go +++ /dev/null @@ -1,339 +0,0 @@ -package rego - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "io/fs" - "strings" - - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" - "github.com/open-policy-agent/opa/storage" - "github.com/open-policy-agent/opa/util" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/providers" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" -) - -var checkTypesWithSubtype = set.New[types.Source](types.SourceCloud, types.SourceDefsec, types.SourceKubernetes) - -var supportedProviders = makeSupportedProviders() - -func makeSupportedProviders() set.Set[string] { - m := set.New[string]() - for _, p := range providers.AllProviders() { - m.Append(string(p)) - } - m.Append("kind") // kubernetes - return m -} - -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - ruleNamespaces set.Set[string] - policies map[string]*ast.Module - store storage.Store - runtimeValues *ast.Term - compiler *ast.Compiler - regoErrorLimit int - logger *log.Logger - traceWriter io.Writer - tracePerResult bool - retriever *MetadataRetriever - policyFS fs.FS - policyDirs []string - policyReaders []io.Reader - dataFS fs.FS - dataDirs []string - frameworks []framework.Framework - includeDeprecatedChecks bool - includeEmbeddedPolicies bool - includeEmbeddedLibraries bool - - embeddedLibs map[string]*ast.Module - embeddedChecks map[string]*ast.Module - customSchemas map[string][]byte - - disabledCheckIDs set.Set[string] -} - -func (s *Scanner) trace(heading string, input any) { - if s.traceWriter == nil { - return - } - data, err := json.MarshalIndent(input, "", " ") - if err != nil { - return - } - _, _ = fmt.Fprintf(s.traceWriter, "REGO %[1]s:\n%s\nEND REGO %[1]s\n\n", heading, string(data)) -} - -type DynamicMetadata struct { - Warning bool - Filepath string - Message string - StartLine int - EndLine int -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - LoadAndRegister() - - s := &Scanner{ - regoErrorLimit: ast.CompileErrorLimitDefault, - ruleNamespaces: builtinNamespaces.Clone(), - runtimeValues: addRuntimeValues(), - logger: log.WithPrefix("rego"), - customSchemas: make(map[string][]byte), - disabledCheckIDs: set.New[string](), - } - - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) runQuery(ctx context.Context, query string, input ast.Value, disableTracing bool) (rego.ResultSet, []string, error) { - - trace := (s.traceWriter != nil || s.tracePerResult) && !disableTracing - - regoOptions := []func(*rego.Rego){ - rego.Query(query), - rego.Compiler(s.compiler), - rego.Store(s.store), - rego.Runtime(s.runtimeValues), - rego.Trace(trace), - } - - if input != nil { - regoOptions = append(regoOptions, rego.ParsedInput(input)) - } - - instance := rego.New(regoOptions...) - resultSet, err := instance.Eval(ctx) - if err != nil { - return nil, nil, err - } - - // we also build a slice of trace lines for per-result tracing - primarily for fanal/trivy - var traces []string - - if trace { - if s.traceWriter != nil { - rego.PrintTrace(s.traceWriter, instance) - } - if s.tracePerResult { - traceBuffer := bytes.NewBuffer([]byte{}) - rego.PrintTrace(traceBuffer, instance) - traces = strings.Split(traceBuffer.String(), "\n") - } - } - return resultSet, traces, nil -} - -type Input struct { - Path string `json:"path"` - FS fs.FS `json:"-"` - Contents any `json:"contents"` -} - -func GetInputsContents(inputs []Input) []any { - results := make([]any, len(inputs)) - for i, c := range inputs { - results[i] = c.Contents - } - return results -} - -func (s *Scanner) ScanInput(ctx context.Context, sourceType types.Source, inputs ...Input) (scan.Results, error) { - - s.logger.Debug("Scanning inputs", "count", len(inputs)) - - var results scan.Results - - for _, module := range s.policies { - - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - namespace := getModuleNamespace(module) - topLevel := strings.Split(namespace, ".")[0] - if !s.ruleNamespaces.Contains(topLevel) { - continue - } - - staticMeta, err := s.retriever.RetrieveMetadata(ctx, module, GetInputsContents(inputs)...) - if err != nil { - s.logger.Error( - "Error occurred while retrieving metadata from check", - log.FilePath(module.Package.Location.File), - log.Err(err), - ) - continue - } - - if !s.includeDeprecatedChecks && staticMeta.Deprecated { - continue // skip deprecated checks - } - - // skip if check isn't relevant to what is being scanned - if !isPolicyApplicable(sourceType, staticMeta, inputs...) { - continue - } - - if len(inputs) == 0 { - continue - } - - usedRules := set.New[string]() - - // all rules - for _, rule := range module.Rules { - ruleName := rule.Head.Name.String() - if usedRules.Contains(ruleName) { - continue - } - usedRules.Append(ruleName) - if isEnforcedRule(ruleName) { - ruleResults, err := s.applyRule(ctx, namespace, ruleName, inputs) - if err != nil { - s.logger.Error( - "Error occurred while applying rule from check", - log.String("rule", ruleName), - log.FilePath(module.Package.Location.File), - log.Err(err), - ) - continue - } - results = append(results, s.embellishResultsWithRuleMetadata(ruleResults, *staticMeta)...) - } - } - - } - - return results, nil -} - -func isPolicyWithSubtype(sourceType types.Source) bool { - return checkTypesWithSubtype.Contains(sourceType) -} - -func checkSubtype(ii map[string]any, provider string, subTypes []SubType) bool { - if len(subTypes) == 0 { - return true - } - - for _, st := range subTypes { - switch services := ii[provider].(type) { - case map[string]any: - if st.Provider != provider { - continue - } - if _, exists := services[st.Service]; exists { - return true - } - case string: // k8s - logic can be improved - if strings.EqualFold(services, st.Group) || - strings.EqualFold(services, st.Version) || - strings.EqualFold(services, st.Kind) { - return true - } - } - } - return false -} - -func isPolicyApplicable(sourceType types.Source, staticMetadata *StaticMetadata, inputs ...Input) bool { - if len(staticMetadata.InputOptions.Selectors) == 0 { // check always applies if no selectors - return true - } - - for _, selector := range staticMetadata.InputOptions.Selectors { - if selector.Type != string(sourceType) { - return false - } - } - - if !isPolicyWithSubtype(sourceType) { - return true - } - - for _, input := range inputs { - if ii, ok := input.Contents.(map[string]any); ok { - for provider := range ii { - if !supportedProviders.Contains(provider) { - continue - } - - // check metadata for subtype - for _, s := range staticMetadata.InputOptions.Selectors { - if checkSubtype(ii, provider, s.Subtypes) { - return true - } - } - } - } - } - return false -} - -func parseRawInput(input any) (ast.Value, error) { - if err := util.RoundTrip(&input); err != nil { - return nil, err - } - - return ast.InterfaceToValue(input) -} - -func (s *Scanner) applyRule(ctx context.Context, namespace, rule string, inputs []Input) (scan.Results, error) { - var results scan.Results - qualified := fmt.Sprintf("data.%s.%s", namespace, rule) - for _, input := range inputs { - s.trace("INPUT", input) - parsedInput, err := parseRawInput(input.Contents) - if err != nil { - s.logger.Error("Error occurred while parsing input", log.Err(err)) - continue - } - - resultSet, traces, err := s.runQuery(ctx, qualified, parsedInput, false) - if err != nil { - return nil, err - } - s.trace("RESULTSET", resultSet) - ruleResults := s.convertResults(resultSet, input, namespace, rule, traces) - if len(ruleResults) == 0 { // It passed because we didn't find anything wrong (NOT because it didn't exist) - var result regoResult - result.FS = input.FS - result.Filepath = input.Path - result.Managed = true - results.AddPassedRego(namespace, rule, traces, result) - continue - } - results = append(results, ruleResults...) - } - - return results, nil -} - -// severity is now set with metadata, so deny/warn/violation now behave the same way -func isEnforcedRule(name string) bool { - switch { - case name == "deny", strings.HasPrefix(name, "deny_"): - return true - } - return false -} diff --git a/pkg/iac/rego/scanner_test.go b/pkg/iac/rego/scanner_test.go deleted file mode 100644 index 3da11ef132d7..000000000000 --- a/pkg/iac/rego/scanner_test.go +++ /dev/null @@ -1,1018 +0,0 @@ -package rego_test - -import ( - "bytes" - "context" - "io/fs" - "os" - "path/filepath" - "strings" - "testing" - "testing/fstest" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/severity" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func CreateFS(t *testing.T, files map[string]string) fs.FS { - memfs := memoryfs.New() - for name, content := range files { - name := strings.TrimPrefix(name, "/") - err := memfs.MkdirAll(filepath.Dir(name), 0o700) - require.NoError(t, err) - err = memfs.WriteFile(name, []byte(content), 0o644) - require.NoError(t, err) - } - return memfs -} - -func Test_RegoScanning_Deny(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - FS: srcFS, - }) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename()) -} - -func Test_RegoScanning_AbsolutePolicyPath_Deny(t *testing.T) { - - tmp := t.TempDir() - require.NoError(t, os.Mkdir(filepath.Join(tmp, "policies"), 0755)) - require.NoError(t, os.WriteFile(filepath.Join(tmp, "policies", "test.rego"), []byte(`package defsec.test - -deny { - input.evil -}`), 0600)) - - srcFS := os.DirFS(tmp) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - FS: srcFS, - }) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename()) -} - -func Test_RegoScanning_Allow(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": false, - }, - }) - require.NoError(t, err) - - assert.Empty(t, results.GetFailed()) - require.Len(t, results.GetPassed(), 1) - assert.Empty(t, results.GetIgnored()) - - assert.Equal(t, "/evil.lol", results.GetPassed()[0].Metadata().Range().GetFilename()) -} - -func Test_RegoScanning_WithRuntimeValues(t *testing.T) { - - t.Setenv("DEFSEC_RUNTIME_VAL", "AOK") - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny_evil { - output := opa.runtime() - output.env.DEFSEC_RUNTIME_VAL == "AOK" -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - -func Test_RegoScanning_WithDenyMessage(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny[msg] { - input.evil - msg := "oh no" -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Equal(t, "oh no", results.GetFailed()[0].Description()) - assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename()) -} - -func Test_RegoScanning_WithDenyMetadata_ImpliedPath(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny[res] { - input.evil - res := { - "msg": "oh no", - "startline": 123, - "endline": 456, - } -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Equal(t, "oh no", results.GetFailed()[0].Description()) - assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename()) - assert.Equal(t, 123, results.GetFailed()[0].Metadata().Range().GetStartLine()) - assert.Equal(t, 456, results.GetFailed()[0].Metadata().Range().GetEndLine()) - -} - -func Test_RegoScanning_WithDenyMetadata_PersistedPath(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny[res] { - input.evil - res := { - "msg": "oh no", - "startline": 123, - "endline": 456, - "filepath": "/blah.txt", - } -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Equal(t, "oh no", results.GetFailed()[0].Description()) - assert.Equal(t, "/blah.txt", results.GetFailed()[0].Metadata().Range().GetFilename()) - assert.Equal(t, 123, results.GetFailed()[0].Metadata().Range().GetStartLine()) - assert.Equal(t, 456, results.GetFailed()[0].Metadata().Range().GetEndLine()) - -} - -func Test_RegoScanning_WithStaticMetadata(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -__rego_metadata__ := { - "id": "AA001", - "avd_id": "AVD-XX-9999", - "title": "This is a title", - "short_code": "short-code", - "severity": "LOW", - "type": "Dockerfile Security Check", - "description": "This is a description", - "recommended_actions": "This is a recommendation", - "url": "https://google.com", -} - -deny[res] { - input.evil - res := { - "msg": "oh no", - "startline": 123, - "endline": 456, - "filepath": "/blah.txt", - } -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - failure := results.GetFailed()[0] - - assert.Equal(t, "oh no", failure.Description()) - assert.Equal(t, "/blah.txt", failure.Metadata().Range().GetFilename()) - assert.Equal(t, 123, failure.Metadata().Range().GetStartLine()) - assert.Equal(t, 456, failure.Metadata().Range().GetEndLine()) - assert.Equal(t, "AVD-XX-9999", failure.Rule().AVDID) - assert.True(t, failure.Rule().HasID("AA001")) - assert.Equal(t, "This is a title", failure.Rule().Summary) - assert.Equal(t, severity.Low, failure.Rule().Severity) - assert.Equal(t, "This is a recommendation", failure.Rule().Resolution) - assert.Equal(t, "https://google.com", failure.Rule().Links[0]) - -} - -func Test_RegoScanning_WithMatchingInputSelector(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -__rego_input__ := { - "selector": [{"type": "json"}], -} - -deny { - input.evil -} - -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - -func Test_RegoScanning_WithNonMatchingInputSelector(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -__rego_input__ := { - "selector": [{"type": "testing"}], -} - -deny { - input.evil -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Empty(t, results.GetFailed()) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - -func Test_RegoScanning_NoTracingByDefault(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Empty(t, results.GetFailed()[0].Traces()) -} - -func Test_RegoScanning_GlobalTracingEnabled(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - }) - - traceBuffer := bytes.NewBuffer([]byte{}) - - scanner := rego.NewScanner( - rego.WithTrace(traceBuffer), - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.Empty(t, results.GetFailed()[0].Traces()) - assert.NotEmpty(t, traceBuffer.Bytes()) -} - -func Test_RegoScanning_PerResultTracingEnabled(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPerResultTracing(true), - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) - - assert.NotEmpty(t, results.GetFailed()[0].Traces()) -} - -func Test_dynamicMetadata(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -__rego_metadata__ := { - "title" : sprintf("i am %s",[input.text]) -} - -deny { - input.text -} - -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "text": "dynamic", - }, - }) - require.NoError(t, err) - assert.Equal(t, "i am dynamic", results[0].Rule().Summary) -} - -func Test_staticMetadata(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -__rego_metadata__ := { - "title" : "i am static" -} - -deny { - input.text -} - -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "text": "test", - }, - }) - require.NoError(t, err) - assert.Equal(t, "i am static", results[0].Rule().Summary) -} - -func Test_annotationMetadata(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": `# METADATA -# title: i am a title -# description: i am a description -# related_resources: -# - https://google.com -# custom: -# id: EG123 -# avd_id: AVD-EG-0123 -# severity: LOW -# recommended_action: have a cup of tea -package defsec.test - -deny { - input.text -} - -`, - "policies/test2.rego": `# METADATA -# title: i am another title -package defsec.test2 - -deny { - input.blah -} - -`, - }) - - scanner := rego.NewScanner( - rego.WithPerResultTracing(true), - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "text": "test", - }, - }) - require.NoError(t, err) - require.Len(t, results.GetFailed(), 1) - failure := results.GetFailed()[0].Rule() - assert.Equal(t, "i am a title", failure.Summary) - assert.Equal(t, "i am a description", failure.Explanation) - require.Len(t, failure.Links, 1) - assert.Equal(t, "https://google.com", failure.Links[0]) - assert.Equal(t, "AVD-EG-0123", failure.AVDID) - assert.Equal(t, severity.Low, failure.Severity) - assert.Equal(t, "have a cup of tea", failure.Resolution) -} - -func Test_RegoScanning_WithInvalidInputSchema(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": `# METADATA -# schemas: -# - input: schema["input"] -package defsec.test - -deny { - input.evil == "lol" -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) -} - -func Test_RegoScanning_WithValidInputSchema(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": `# METADATA -# schemas: -# - input: schema["input"] -package defsec.test - -deny { - input.Stages[0].Commands[0].Cmd == "lol" -} -`, - }) - - scanner := rego.NewScanner( - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) -} - -func Test_RegoScanning_WithFilepathToSchema(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": `# METADATA -# schemas: -# - input: schema["dockerfile"] -package defsec.test - -deny { - input.evil == "lol" -} -`, - }) - - scanner := rego.NewScanner( - rego.WithRegoErrorLimits(0), - rego.WithPolicyDirs("policies"), - ) - - assert.ErrorContains( - t, - scanner.LoadPolicies(srcFS), - "undefined ref: input.evil", - ) -} - -func Test_RegoScanning_CustomData(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test -import data.settings.DS123.foo_bar_baz - -deny { - not foo_bar_baz -} -`, - }) - - dataFS := CreateFS(t, map[string]string{ - "data/data.json": `{ - "settings": { - "DS123":{ - "foo_bar_baz":false - } - } -}`, - "data/junk.txt": "this file should be ignored", - }) - - scanner := rego.NewScanner( - rego.WithDataFilesystem(dataFS), - rego.WithDataDirs("."), - rego.WithPolicyDirs("policies"), - ) - - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{}) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - -func Test_RegoScanning_InvalidFS(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test -import data.settings.DS123.foo_bar_baz - -deny { - not foo_bar_baz -} -`, - }) - - dataFS := CreateFS(t, map[string]string{ - "data/data.json": `{ - "settings": { - "DS123":{ - "foo_bar_baz":false - } - } -}`, - "data/junk.txt": "this file should be ignored", - }) - - scanner := rego.NewScanner( - rego.WithDataFilesystem(dataFS), - rego.WithDataDirs("X://"), - rego.WithPolicyDirs("policies"), - ) - - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{}) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - -func Test_NoErrorsWhenUsingBadRegoCheck(t *testing.T) { - - // this check cause eval_conflict_error - // https://www.openpolicyagent.org/docs/latest/policy-language/#functions - fsys := fstest.MapFS{ - "checks/bad.rego": { - Data: []byte(`package defsec.test - -p(x) = y { - y := x[_] -} - -deny { - p([1, 2, 3]) -} -`), - }, - } - - scanner := rego.NewScanner( - rego.WithPolicyDirs("checks"), - ) - require.NoError(t, scanner.LoadPolicies(fsys)) - _, err := scanner.ScanInput(context.TODO(), types.SourceYAML, rego.Input{}) - require.NoError(t, err) -} - -func Test_RegoScanning_WithDeprecatedCheck(t *testing.T) { - var testCases = []struct { - name string - policy string - expectedResults int - }{ - { - name: "happy path check is deprecated", - policy: `# METADATA -# title: i am a deprecated check -# description: i am a description -# related_resources: -# - https://google.com -# custom: -# id: EG123 -# avd_id: AVD-EG-0123 -# severity: LOW -# recommended_action: have a cup of tea -# deprecated: true -package defsec.test - -deny { - input.text -} - -`, - expectedResults: 0, - }, - { - name: "happy path check is not deprecated", - policy: `# METADATA -# title: i am a deprecated check -# description: i am a description -# related_resources: -# - https://google.com -# custom: -# id: EG123 -# avd_id: AVD-EG-0123 -# severity: LOW -# recommended_action: have a cup of tea -package defsec.test - -deny { - input.text -} - -`, - expectedResults: 1, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": tc.policy, - }) - - scanner := rego.NewScanner(rego.WithPolicyDirs("policies")) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceJSON, rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "text": "test", - }, - }) - require.NoError(t, err) - require.Len(t, results, tc.expectedResults, tc.name) - }) - } -} - -func Test_RegoScanner_WithCustomSchemas(t *testing.T) { - - schema := `{ - "$id": "https://example.com/test.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "service": { "type": "string" } - }, - "required": ["service"] -}` - - tests := []struct { - name string - check string - expectedResults int - }{ - { - name: "happy path", - check: `# METADATA -# title: test check -# schemas: -# - input: schema["test"] -package user.test - -deny { - input.service == "test" -} -`, - expectedResults: 1, - }, - { - name: "sad path", - check: `# METADATA -# title: test check -# schemas: -# - input: schema["test"] -package user.test - -deny { - input.other == "test" -} -`, - expectedResults: 0, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - scanner := rego.NewScanner( - rego.WithCustomSchemas(map[string][]byte{ - "test": []byte(schema), - }), - rego.WithPolicyNamespaces("user"), - rego.WithPolicyReader(strings.NewReader(tc.check)), - ) - - require.NoError(t, scanner.LoadPolicies(nil)) - - results, err := scanner.ScanInput(context.TODO(), types.SourceYAML, rego.Input{ - Path: "test.yaml", - Contents: map[string]any{"service": "test"}, - }) - require.NoError(t, err) - require.Len(t, results, tc.expectedResults, tc.name) - }) - } -} - -func Test_RegoScanner_WithDisabledCheckIDs(t *testing.T) { - - check := `# METADATA -# custom: -# id: TEST-001 -# avd_id: AVD-TEST-001 -# severity: LOW -# provider: aws -# service: s3 -# short_code: test -package builtin.test - -deny { - true -} -` - - tests := []struct { - name string - disabledChecks []string - inputCheck string - expected bool - }{ - { - name: "no disabled checks", - expected: true, - inputCheck: check, - }, - { - name: "disable check by ID", - disabledChecks: []string{"TEST-001"}, - inputCheck: check, - }, - { - name: "disabling a non-existent check", - disabledChecks: []string{"FOO"}, - expected: true, - inputCheck: check, - }, - { - name: "one of the identifiers does not exist", - disabledChecks: []string{"FOO", "TEST-001"}, - inputCheck: check, - }, - { - name: "do not disable user checks with builtin IDs", - inputCheck: `# METADATA -# custom: -# id: TEST-001 -# avd_id: AVD-TEST-001 -# severity: LOW -# provider: aws -# service: s3 -# short_code: test -package user.test - -deny { - true -} -`, - disabledChecks: []string{"TEST-001"}, - expected: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - scanner := rego.NewScanner( - rego.WithPolicyReader(strings.NewReader(tt.inputCheck)), - rego.WithDisabledCheckIDs(tt.disabledChecks...), - rego.WithPolicyNamespaces("user"), - ) - - require.NoError(t, scanner.LoadPolicies(nil)) - results, err := scanner.ScanInput(context.TODO(), types.SourceYAML, rego.Input{}) - require.NoError(t, err) - - require.Equal(t, tt.expected, len(results.GetFailed()) > 0) - }) - } -} diff --git a/pkg/iac/rego/schemas/00_schema.go b/pkg/iac/rego/schemas/00_schema.go deleted file mode 100644 index e6674912fe58..000000000000 --- a/pkg/iac/rego/schemas/00_schema.go +++ /dev/null @@ -1,22 +0,0 @@ -package schemas - -import _ "embed" - -type Schema string - -var ( - None Schema = "" - Anything Schema = `{}` - - //go:embed dockerfile.json - Dockerfile Schema - - //go:embed kubernetes.json - Kubernetes Schema - - //go:embed rbac.json - RBAC Schema - - //go:embed cloud.json - Cloud Schema -) diff --git a/pkg/iac/rego/schemas/builder.go b/pkg/iac/rego/schemas/builder.go deleted file mode 100644 index 38f3c0c13594..000000000000 --- a/pkg/iac/rego/schemas/builder.go +++ /dev/null @@ -1,275 +0,0 @@ -package schemas - -import ( - "errors" - "fmt" - "reflect" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/rego/convert" - "github.com/aquasecurity/trivy/pkg/iac/state" -) - -type RawSchema struct { - Type string `json:"type"` // object - Properties map[string]Property `json:"properties,omitempty"` - Defs map[string]*Property `json:"definitions,omitempty"` -} - -type Property struct { - Type string `json:"type,omitempty"` - Ref string `json:"$ref,omitempty"` - Properties map[string]Property `json:"properties,omitempty"` - Items *Property `json:"items,omitempty"` -} - -type builder struct { - schema RawSchema -} - -func Build() (*RawSchema, error) { - - b := newBuilder() - - inputValue := reflect.ValueOf(state.State{}) - - err := b.fromInput(inputValue) - if err != nil { - return nil, err - } - - return &b.schema, nil -} - -func newBuilder() *builder { - return &builder{ - schema: RawSchema{ - Properties: nil, - Defs: nil, - }, - } -} - -func (b *builder) fromInput(inputValue reflect.Value) error { - - prop, err := b.readProperty("", nil, inputValue.Type(), 0) - if err != nil { - return err - } - if prop == nil { - return errors.New("property is nil") - } - b.schema.Properties = prop.Properties - b.schema.Type = prop.Type - return nil -} - -func refName(name string, parent, t reflect.Type) string { - if t.Name() == "" { // inline struct - return sanitize(parent.PkgPath() + "." + parent.Name() + "." + name) - } - return sanitize(t.PkgPath() + "." + t.Name()) -} - -func sanitize(s string) string { - return strings.ReplaceAll(s, "/", ".") -} - -func (b *builder) readProperty(name string, parent, inputType reflect.Type, indent int) (*Property, error) { - if inputType.Kind() == reflect.Ptr { - inputType = inputType.Elem() - } - - switch inputType.String() { - case "types.Range", "types.Reference": - return nil, nil - } - - if b.schema.Defs != nil { - _, ok := b.schema.Defs[refName(name, parent, inputType)] - if ok { - return &Property{ - Type: "object", - Ref: "#/definitions/" + refName(name, parent, inputType), - }, nil - } - } - - fmt.Println(strings.Repeat(" ", indent) + name) - - switch kind := inputType.Kind(); kind { - case reflect.Struct: - return b.readStruct(name, parent, inputType, indent) - case reflect.Slice: - return b.readSlice(name, parent, inputType, indent) - case reflect.String: - return &Property{ - Type: "string", - }, nil - case reflect.Int: - return &Property{ - Type: "integer", - }, nil - case reflect.Bool: - return &Property{ - Type: "boolean", - }, nil - case reflect.Float32, reflect.Float64: - return &Property{ - Type: "number", - }, nil - } - - switch inputType.Name() { - case "BoolValue": - return &Property{ - Type: "object", - Properties: map[string]Property{ - "value": { - Type: "boolean", - }, - }, - }, nil - case "IntValue": - return &Property{ - Type: "object", - Properties: map[string]Property{ - "value": { - Type: "integer", - }, - }, - }, nil - case "StringValue", "TimeValue", "BytesValue": - return &Property{ - Type: "object", - Properties: map[string]Property{ - "value": { - Type: "string", - }, - }, - }, nil - case "MapValue": - return &Property{ - Type: "object", - Properties: map[string]Property{ - "value": { - Type: "object", - }, - }, - }, nil - - } - - fmt.Printf("WARNING: unsupported type: %s (%s)\n", inputType.Name(), inputType) - return nil, nil -} - -var converterInterface = reflect.TypeOf((*convert.Converter)(nil)).Elem() - -func (b *builder) readStruct(name string, parent, inputType reflect.Type, indent int) (*Property, error) { - - if b.schema.Defs == nil { - b.schema.Defs = make(map[string]*Property) - } - - def := &Property{ - Type: "object", - Properties: make(map[string]Property), - } - - if parent != nil { - b.schema.Defs[refName(name, parent, inputType)] = def - } - - if inputType.Implements(converterInterface) || - inputType.String() == "types.Metadata" { - if inputType.Kind() == reflect.Ptr { - inputType = inputType.Elem() - } - returns := reflect.New(inputType).MethodByName("ToRego").Call(nil) - if err := b.readRego(def, name, parent, returns[0].Type(), returns[0].Interface(), indent); err != nil { - return nil, err - } - } else { - - for i := 0; i < inputType.NumField(); i++ { - - field := inputType.Field(i) - prop, err := b.readProperty(field.Name, inputType, field.Type, indent+1) - if err != nil { - return nil, err - } - if prop == nil { - continue - } - key := strings.ToLower(field.Name) - - // metadata exported as "__defsec_metadata" - if key == "metadata" { - key = "__defsec_metadata" - } - - def.Properties[key] = *prop - } - } - - if parent == nil { - return def, nil - } - - return &Property{ - Type: "object", - Ref: "#/definitions/" + refName(name, parent, inputType), - }, nil -} - -func (b *builder) readSlice(name string, parent, inputType reflect.Type, indent int) (*Property, error) { - - items, err := b.readProperty(name, parent, inputType.Elem(), indent+1) - if err != nil { - return nil, err - } - - prop := &Property{ - Type: "array", - Items: items, - } - return prop, nil -} - -func (b *builder) readRego(def *Property, name string, parent, typ reflect.Type, raw any, indent int) error { - - switch cast := raw.(type) { - case map[string]any: - def.Type = "object" - for k, v := range cast { - child := &Property{ - Properties: make(map[string]Property), - } - if err := b.readRego(child, k, reflect.TypeOf(raw), reflect.TypeOf(v), v, indent+1); err != nil { - return err - } - def.Properties[k] = *child - } - case map[string]string: - def.Type = "object" - for k, v := range cast { - child := &Property{ - Properties: make(map[string]Property), - } - if err := b.readRego(child, k, reflect.TypeOf(raw), reflect.TypeOf(v), v, indent+1); err != nil { - return err - } - def.Properties[k] = *child - } - default: - prop, err := b.readProperty(name, parent, typ, indent) - if err != nil { - return err - } - *def = *prop - } - - return nil - -} diff --git a/pkg/iac/rego/schemas/cloud.json b/pkg/iac/rego/schemas/cloud.json deleted file mode 100644 index dd9c10ddeae7..000000000000 --- a/pkg/iac/rego/schemas/cloud.json +++ /dev/null @@ -1,8103 +0,0 @@ -{ - "type": "object", - "properties": { - "aws": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.AWS" - }, - "azure": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.Azure" - }, - "cloudstack": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.cloudstack.CloudStack" - }, - "digitalocean": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.DigitalOcean" - }, - "github": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.github.GitHub" - }, - "google": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.Google" - }, - "kubernetes": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Kubernetes" - }, - "nifcloud": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.Nifcloud" - }, - "openstack": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.OpenStack" - }, - "oracle": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.oracle.Oracle" - } - }, - "definitions": { - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.AWS": { - "type": "object", - "properties": { - "accessanalyzer": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.accessanalyzer.AccessAnalyzer" - }, - "apigateway": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.APIGateway" - }, - "athena": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.Athena" - }, - "cloudfront": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.Cloudfront" - }, - "cloudtrail": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.CloudTrail" - }, - "cloudwatch": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.CloudWatch" - }, - "codebuild": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.CodeBuild" - }, - "config": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.config.Config" - }, - "documentdb": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.documentdb.DocumentDB" - }, - "dynamodb": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.DynamoDB" - }, - "ec2": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.EC2" - }, - "ecr": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.ECR" - }, - "ecs": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.ECS" - }, - "efs": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.efs.EFS" - }, - "eks": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.EKS" - }, - "elasticache": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.ElastiCache" - }, - "elasticsearch": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.Elasticsearch" - }, - "elb": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.ELB" - }, - "emr": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.EMR" - }, - "iam": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.IAM" - }, - "kinesis": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.kinesis.Kinesis" - }, - "kms": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.kms.KMS" - }, - "lambda": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Lambda" - }, - "meta": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.Meta" - }, - "mq": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.mq.MQ" - }, - "msk": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.MSK" - }, - "neptune": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.neptune.Neptune" - }, - "rds": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.RDS" - }, - "redshift": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.Redshift" - }, - "s3": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.S3" - }, - "sam": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.SAM" - }, - "sns": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sns.SNS" - }, - "sqs": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sqs.SQS" - }, - "ssm": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ssm.SSM" - }, - "workspaces": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.WorkSpaces" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.AssumeRole": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "duration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "externalid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policyarns": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "rolearn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sessionname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sourceidentity": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "tags": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.MapValue" - }, - "transitivetagkeys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.AssumeRoleWithWebIdentity": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "duration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policyarns": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "rolearn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sessionname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "webidentitytoken": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "webidentitytokenfile": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.DefaultTags": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "tags": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.MapValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.IgnoreTags": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "keyprefixes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "keys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.Meta": { - "type": "object", - "properties": { - "tfproviders": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.TerraformProvider" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.TerraformProvider": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesskey": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "alias": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "allowedaccountsids": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "assumerole": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.AssumeRole" - }, - "assumerolewithwebidentity": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.AssumeRoleWithWebIdentity" - }, - "customcabundle": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "defaulttags": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.DefaultTags" - }, - "ec2metadataserviceendpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "ec2metadataserviceendpointmode": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "endpoints": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.MapValue" - }, - "forbiddenaccountids": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "httpproxy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "ignoretags": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.IgnoreTags" - }, - "insecure": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "maxretries": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "profile": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "region": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "retrymode": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "s3useast1regionalendpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "s3usepathstyle": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "secretkey": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sharedconfigfiles": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "sharedcredentialsfiles": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "skipcredentialsvalidation": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "skipmetadataapicheck": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "skipregionvalidation": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "skiprequestingaccountid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "stsregion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "token": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "usedualstackendpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "usefipsendpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "version": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.accessanalyzer.AccessAnalyzer": { - "type": "object", - "properties": { - "analyzers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.accessanalyzer.Analyzer" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.accessanalyzer.Analyzer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "active": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "arn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "findings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.accessanalyzer.Findings" - } - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.accessanalyzer.Findings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.APIGateway": { - "type": "object", - "properties": { - "v1": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.APIGateway" - }, - "v2": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.APIGateway" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.API": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "resources": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.Resource" - } - }, - "stages": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.Stage" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.APIGateway": { - "type": "object", - "properties": { - "apis": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.API" - } - }, - "domainnames": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.DomainName" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.AccessLogging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cloudwatchloggrouparn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.DomainName": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "securitypolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.Method": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "apikeyrequired": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "authorizationtype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "httpmethod": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.RESTMethodSettings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cachedataencrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "cacheenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "method": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.Resource": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "methods": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.Method" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.Stage": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesslogging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.AccessLogging" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "restmethodsettings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v1.RESTMethodSettings" - } - }, - "xraytracingenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.API": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "protocoltype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "stages": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.Stage" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.APIGateway": { - "type": "object", - "properties": { - "apis": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.API" - } - }, - "domainnames": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.DomainName" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.AccessLogging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cloudwatchloggrouparn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.DomainName": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "securitypolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.Stage": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesslogging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.apigateway.v2.AccessLogging" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.Athena": { - "type": "object", - "properties": { - "databases": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.Database" - } - }, - "workgroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.Workgroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.Database": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.EncryptionConfiguration" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.EncryptionConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.Workgroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.athena.EncryptionConfiguration" - }, - "enforceconfiguration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.CacheBehaviour": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "viewerprotocolpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.Cloudfront": { - "type": "object", - "properties": { - "distributions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.Distribution" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.Distribution": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "defaultcachebehaviour": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.CacheBehaviour" - }, - "logging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.Logging" - }, - "orderercachebehaviours": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.CacheBehaviour" - } - }, - "viewercertificate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.ViewerCertificate" - }, - "wafid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "bucket": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudfront.ViewerCertificate": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cloudfrontdefaultcertificate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "minimumprotocolversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sslsupportmethod": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.CloudTrail": { - "type": "object", - "properties": { - "trails": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.Trail" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.DataResource": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "values": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.EventSelector": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "dataresources": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.DataResource" - } - }, - "readwritetype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.Trail": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "bucketname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "cloudwatchlogsloggrouparn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "enablelogfilevalidation": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "eventselectors": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudtrail.EventSelector" - } - }, - "islogging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "ismultiregion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.Alarm": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "alarmname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "dimensions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.AlarmDimension" - } - }, - "metricname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "metrics": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.MetricDataQuery" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.AlarmDimension": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "value": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.CloudWatch": { - "type": "object", - "properties": { - "alarms": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.Alarm" - } - }, - "loggroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.LogGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.LogGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "arn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "metricfilters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.MetricFilter" - } - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "retentionindays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.MetricDataQuery": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "expression": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "id": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.cloudwatch.MetricFilter": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "filtername": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "filterpattern": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.ArtifactSettings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryptionenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.CodeBuild": { - "type": "object", - "properties": { - "projects": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.Project" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.Project": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "artifactsettings": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.ArtifactSettings" - }, - "secondaryartifactsettings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.codebuild.ArtifactSettings" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.config.Config": { - "type": "object", - "properties": { - "configurationaggregrator": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.config.ConfigurationAggregrator" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.config.ConfigurationAggregrator": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "sourceallregions": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.documentdb.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "backupretentionperiod": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "enabledlogexports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "identifier": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.documentdb.Instance" - } - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "storageencrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.documentdb.DocumentDB": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.documentdb.Cluster" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.documentdb.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.DAXCluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "pointintimerecovery": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "serversideencryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.ServerSideEncryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.DynamoDB": { - "type": "object", - "properties": { - "daxclusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.DAXCluster" - } - }, - "tables": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.Table" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.ServerSideEncryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.Table": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "pointintimerecovery": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "serversideencryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.dynamodb.ServerSideEncryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.BlockDevice": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.EC2": { - "type": "object", - "properties": { - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Instance" - } - }, - "launchconfigurations": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.LaunchConfiguration" - } - }, - "launchtemplates": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.LaunchTemplate" - } - }, - "networkacls": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.NetworkACL" - } - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroup" - } - }, - "subnets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Subnet" - } - }, - "volumes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Volume" - } - }, - "vpcs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.VPC" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "ebsblockdevices": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.BlockDevice" - } - }, - "metadataoptions": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.MetadataOptions" - }, - "rootblockdevice": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.BlockDevice" - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroup" - } - }, - "userdata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.LaunchConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "associatepublicip": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "ebsblockdevices": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.BlockDevice" - } - }, - "metadataoptions": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.MetadataOptions" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "rootblockdevice": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.BlockDevice" - }, - "userdata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.LaunchTemplate": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "instance": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Instance" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.MetadataOptions": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "httpendpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "httptokens": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.NetworkACL": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "isdefaultrule": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "rules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.NetworkACLRule" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.NetworkACLRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "action": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "cidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "fromport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "toport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "egressrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroupRule" - } - }, - "ingressrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroupRule" - } - }, - "isdefault": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "vpcid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroupRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "fromport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "toport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Subnet": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "mappubliciponlaunch": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.VPC": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "flowlogsenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "id": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "isdefault": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.SecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Volume": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ec2.Encryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.ECR": { - "type": "object", - "properties": { - "repositories": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.Repository" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.ImageScanning": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "scanonpush": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.Repository": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.Encryption" - }, - "imagescanning": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecr.ImageScanning" - }, - "imagetagsimmutable": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "settings": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.ClusterSettings" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.ClusterSettings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "containerinsightsenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.ContainerDefinition": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cpu": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "environment": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.EnvVar" - } - }, - "essential": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "image": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "memory": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "portmappings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.PortMapping" - } - }, - "privileged": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.ECS": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.Cluster" - } - }, - "taskdefinitions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.TaskDefinition" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.EFSVolumeConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "transitencryptionenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.EnvVar": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.PortMapping": { - "type": "object", - "properties": { - "containerport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "hostport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.TaskDefinition": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "containerdefinitions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.ContainerDefinition" - } - }, - "volumes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.Volume" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.Volume": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "efsvolumeconfiguration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ecs.EFSVolumeConfiguration" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.efs.EFS": { - "type": "object", - "properties": { - "filesystems": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.efs.FileSystem" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.efs.FileSystem": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.Encryption" - }, - "logging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.Logging" - }, - "publicaccesscidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "publicaccessenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.EKS": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.Cluster" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "secrets": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.eks.Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "api": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "audit": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "authenticator": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "controllermanager": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "scheduler": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "engine": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "nodetype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "snapshotretentionlimit": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.ElastiCache": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.Cluster" - } - }, - "replicationgroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.ReplicationGroup" - } - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.SecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.ReplicationGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "atrestencryptionenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "transitencryptionenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticache.SecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.AtRestEncryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.Domain": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesspolicies": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "atrestencryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.AtRestEncryption" - }, - "dedicatedmasterenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "domainname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "endpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.Endpoint" - }, - "logpublishing": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.LogPublishing" - }, - "servicesoftwareoptions": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.ServiceSoftwareOptions" - }, - "transitencryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.TransitEncryption" - }, - "vpcid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.Elasticsearch": { - "type": "object", - "properties": { - "domains": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.Domain" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.Endpoint": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enforcehttps": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "tlspolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.LogPublishing": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "auditenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "cloudwatchloggrouparn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.ServiceSoftwareOptions": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "currentversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "newversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "updateavailable": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "updatestatus": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elasticsearch.TransitEncryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.Action": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.ELB": { - "type": "object", - "properties": { - "loadbalancers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.LoadBalancer" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.Listener": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "defaultactions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.Action" - } - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "tlspolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.LoadBalancer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "dropinvalidheaderfields": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "internal": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "listeners": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.elb.Listener" - } - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "settings": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.ClusterSettings" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.ClusterSettings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "releaselabel": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "servicerole": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.EMR": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.Cluster" - } - }, - "securityconfiguration": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.SecurityConfiguration" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.emr.SecurityConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "configuration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.AccessKey": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "active": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "creationdate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - }, - "lastaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Document": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "value": { - "type": "string" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Group": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.IAM": { - "type": "object", - "properties": { - "groups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Group" - } - }, - "passwordpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.PasswordPolicy" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - }, - "roles": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Role" - } - }, - "servercertificates": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.ServerCertificate" - } - }, - "users": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.User" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.MFADevice": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "isvirtual": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.PasswordPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "maxagedays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "minimumlength": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "requirelowercase": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "requirenumbers": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "requiresymbols": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "requireuppercase": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "reusepreventioncount": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "builtin": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "document": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Document" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Role": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.ServerCertificate": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "expiration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.User": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesskeys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.AccessKey" - } - }, - "lastaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - }, - "mfadevices": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.MFADevice" - } - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.kinesis.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.kinesis.Kinesis": { - "type": "object", - "properties": { - "streams": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.kinesis.Stream" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.kinesis.Stream": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.kinesis.Encryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.kms.KMS": { - "type": "object", - "properties": { - "keys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.kms.Key" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.kms.Key": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "rotationenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "usage": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Function": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "permissions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Permission" - } - }, - "tracing": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Tracing" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Lambda": { - "type": "object", - "properties": { - "functions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Function" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Permission": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "principal": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sourcearn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.lambda.Tracing": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "mode": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.mq.Broker": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "logging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.mq.Logging" - }, - "publicaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.mq.Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "audit": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "general": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.mq.MQ": { - "type": "object", - "properties": { - "brokers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.mq.Broker" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.BrokerLogging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cloudwatch": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.CloudwatchLogging" - }, - "firehose": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.FirehoseLogging" - }, - "s3": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.S3Logging" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.CloudwatchLogging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryptionatrest": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.EncryptionAtRest" - }, - "encryptionintransit": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.EncryptionInTransit" - }, - "logging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.Logging" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.EncryptionAtRest": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyarn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.EncryptionInTransit": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "clientbroker": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.FirehoseLogging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "broker": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.BrokerLogging" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.MSK": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.Cluster" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.msk.S3Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.neptune.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "logging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.neptune.Logging" - }, - "storageencrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.neptune.Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "audit": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.neptune.Neptune": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.neptune.Cluster" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Classic": { - "type": "object", - "properties": { - "dbsecuritygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.DBSecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "availabilityzones": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "backupretentionperioddays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "deletionprotection": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Encryption" - }, - "engine": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.ClusterInstance" - } - }, - "latestrestorabletime": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - }, - "performanceinsights": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.PerformanceInsights" - }, - "publicaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "replicationsourcearn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "skipfinalsnapshot": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.ClusterInstance": { - "type": "object", - "properties": { - "clusteridentifier": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "instance": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Instance" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.DBParameterGroupsList": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "dbparametergroupname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.DBSecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.DBSnapshotAttributes": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "attributevalues": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryptstorage": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "autominorversionupgrade": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "backupretentionperioddays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "dbinstancearn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "dbinstanceidentifier": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "dbparametergroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.DBParameterGroupsList" - } - }, - "deletionprotection": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enabledcloudwatchlogsexports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Encryption" - }, - "engine": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "engineversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "iamauthenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "latestrestorabletime": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - }, - "multiaz": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "performanceinsights": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.PerformanceInsights" - }, - "publicaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "publiclyaccessible": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "readreplicadbinstanceidentifiers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "replicationsourcearn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "storageencrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "taglist": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.TagList" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.ParameterGroups": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "dbparametergroupfamily": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "dbparametergroupname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "parameters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Parameters" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Parameters": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "parametername": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "parametervalue": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.PerformanceInsights": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.RDS": { - "type": "object", - "properties": { - "classic": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Classic" - }, - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Cluster" - } - }, - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Instance" - } - }, - "parametergroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.ParameterGroups" - } - }, - "snapshots": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Snapshots" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.Snapshots": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "dbsnapshotarn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "dbsnapshotidentifier": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "encrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "snapshotattributes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.DBSnapshotAttributes" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.rds.TagList": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "allowversionupgrade": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "automatedsnapshotretentionperiod": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "clusteridentifier": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.Encryption" - }, - "endpoint": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.EndPoint" - }, - "loggingenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "masterusername": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "nodetype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "numberofnodes": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "publiclyaccessible": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "subnetgroupname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "vpcid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.ClusterParameter": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "parametername": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "parametervalue": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.EndPoint": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "port": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.Redshift": { - "type": "object", - "properties": { - "clusterparameters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.ClusterParameter" - } - }, - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.Cluster" - } - }, - "reservednodes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.ReservedNode" - } - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.SecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.ReservedNode": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "nodetype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.redshift.SecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Bucket": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accelerateconfigurationstatus": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "acl": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "bucketlocation": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "bucketpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Encryption" - }, - "grants": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grant" - } - }, - "lifecycleconfiguration": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Rules" - } - }, - "logging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Logging" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "objects": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Contents" - } - }, - "publicaccessblock": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.PublicAccessBlock" - }, - "versioning": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Versioning" - }, - "website": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Website" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Contents": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "algorithm": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grant": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "grantee": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grantee" - }, - "permissions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Grantee": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "uri": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Logging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "targetbucket": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.PublicAccessBlock": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "blockpublicacls": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "blockpublicpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "ignorepublicacls": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "restrictpublicbuckets": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Rules": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "status": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.S3": { - "type": "object", - "properties": { - "buckets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Bucket" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Versioning": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "mfadelete": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.s3.Website": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.API": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesslogging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.AccessLogging" - }, - "domainconfiguration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.DomainConfiguration" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "restmethodsettings": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.RESTMethodSettings" - }, - "tracingenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.AccessLogging": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cloudwatchloggrouparn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.Application": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "location": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.Location" - }, - "locationpath": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.DomainConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "securitypolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.Function": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "functionname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "managedpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - }, - "tracing": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.HttpAPI": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accesslogging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.AccessLogging" - }, - "defaultroutesettings": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.RouteSettings" - }, - "domainconfiguration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.DomainConfiguration" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.Location": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "applicationid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "semanticversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.LoggingConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "loggingenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.RESTMethodSettings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cachedataencrypted": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "datatraceenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "loggingenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "metricsenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.RouteSettings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "datatraceenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "detailedmetricsenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "loggingenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.SAM": { - "type": "object", - "properties": { - "apis": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.API" - } - }, - "applications": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.Application" - } - }, - "functions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.Function" - } - }, - "httpapis": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.HttpAPI" - } - }, - "simpletables": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.SimpleTable" - } - }, - "statemachines": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.StateMachine" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.SSESpecification": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "kmsmasterkeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.SimpleTable": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "ssespecification": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.SSESpecification" - }, - "tablename": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.StateMachine": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "loggingconfiguration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.LoggingConfiguration" - }, - "managedpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - }, - "tracing": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.TracingConfiguration" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sam.TracingConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sns.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sns.SNS": { - "type": "object", - "properties": { - "topics": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sns.Topic" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sns.Topic": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "arn": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sns.Encryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sqs.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "managedencryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sqs.Queue": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sqs.Encryption" - }, - "policies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.iam.Policy" - } - }, - "queueurl": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.sqs.SQS": { - "type": "object", - "properties": { - "queues": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.sqs.Queue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ssm.SSM": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.ssm.Secret" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.ssm.Secret": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeyid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.Volume": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.Encryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.WorkSpace": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "rootvolume": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.Volume" - }, - "uservolume": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.Volume" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.WorkSpaces": { - "type": "object", - "properties": { - "workspaces": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.aws.workspaces.WorkSpace" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.Azure": { - "type": "object", - "properties": { - "appservice": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.AppService" - }, - "authorization": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.authorization.Authorization" - }, - "compute": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.Compute" - }, - "container": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.Container" - }, - "database": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Database" - }, - "datafactory": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.datafactory.DataFactory" - }, - "datalake": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.datalake.DataLake" - }, - "keyvault": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.KeyVault" - }, - "monitor": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.monitor.Monitor" - }, - "network": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.Network" - }, - "securitycenter": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.securitycenter.SecurityCenter" - }, - "storage": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Storage" - }, - "synapse": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.synapse.Synapse" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.AppService": { - "type": "object", - "properties": { - "functionapps": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.FunctionApp" - } - }, - "services": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.FunctionApp": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "httpsonly": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "authentication": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service.Authentication" - }, - "enableclientcert": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "identity": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service.Identity" - }, - "site": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service.Site" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service.Authentication": { - "type": "object", - "properties": { - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service.Identity": { - "type": "object", - "properties": { - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.appservice.Service.Site": { - "type": "object", - "properties": { - "enablehttp2": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "minimumtlsversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.authorization.Authorization": { - "type": "object", - "properties": { - "roledefinitions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.authorization.RoleDefinition" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.authorization.Permission": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "actions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.authorization.RoleDefinition": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "assignablescopes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "permissions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.authorization.Permission" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.Compute": { - "type": "object", - "properties": { - "linuxvirtualmachines": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.LinuxVirtualMachine" - } - }, - "manageddisks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.ManagedDisk" - } - }, - "windowsvirtualmachines": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.WindowsVirtualMachine" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.Encryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.LinuxVirtualMachine": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "osprofilelinuxconfig": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.OSProfileLinuxConfig" - }, - "virtualmachine": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.VirtualMachine" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.ManagedDisk": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.Encryption" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.OSProfileLinuxConfig": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "disablepasswordauthentication": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.VirtualMachine": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "customdata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.WindowsVirtualMachine": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "virtualmachine": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.VirtualMachine" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.AddonProfile": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "omsagent": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.OMSAgent" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.Container": { - "type": "object", - "properties": { - "kubernetesclusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.KubernetesCluster" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.KubernetesCluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "addonprofile": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.AddonProfile" - }, - "apiserverauthorizedipranges": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "enableprivatecluster": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "networkprofile": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.NetworkProfile" - }, - "rolebasedaccesscontrol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.RoleBasedAccessControl" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.NetworkProfile": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "networkpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.OMSAgent": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.container.RoleBasedAccessControl": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Database": { - "type": "object", - "properties": { - "mariadbservers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.MariaDBServer" - } - }, - "mssqlservers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.MSSQLServer" - } - }, - "mysqlservers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.MySQLServer" - } - }, - "postgresqlservers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.PostgreSQLServer" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.ExtendedAuditingPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "retentionindays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.FirewallRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "endip": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "startip": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.MSSQLServer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "extendedauditingpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.ExtendedAuditingPolicy" - } - }, - "securityalertpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.SecurityAlertPolicy" - } - }, - "server": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Server" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.MariaDBServer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "server": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Server" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.MySQLServer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "server": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Server" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.PostgreSQLServer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "config": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.PostgresSQLConfig" - }, - "server": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Server" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.PostgresSQLConfig": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "connectionthrottling": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "logcheckpoints": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "logconnections": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.SecurityAlertPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "disabledalerts": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "emailaccountadmins": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "emailaddresses": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.Server": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablepublicnetworkaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enablesslenforcement": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "firewallrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.database.FirewallRule" - } - }, - "minimumtlsversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.datafactory.DataFactory": { - "type": "object", - "properties": { - "datafactories": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.datafactory.Factory" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.datafactory.Factory": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablepublicnetwork": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.datalake.DataLake": { - "type": "object", - "properties": { - "stores": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.datalake.Store" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.datalake.Store": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enableencryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.Key": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "expirydate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.KeyVault": { - "type": "object", - "properties": { - "vaults": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.Vault" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.NetworkACLs": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "defaultaction": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.Secret": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "contenttype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "expirydate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.Vault": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablepurgeprotection": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "keys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.Key" - } - }, - "networkacls": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.NetworkACLs" - }, - "secrets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.keyvault.Secret" - } - }, - "softdeleteretentiondays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.monitor.LogProfile": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "categories": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "locations": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "retentionpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.monitor.RetentionPolicy" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.monitor.Monitor": { - "type": "object", - "properties": { - "logprofiles": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.monitor.LogProfile" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.monitor.RetentionPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "days": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.Network": { - "type": "object", - "properties": { - "networkwatcherflowlogs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.NetworkWatcherFlowLog" - } - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.SecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.NetworkWatcherFlowLog": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "retentionpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.RetentionPolicy" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.PortRange": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "end": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "start": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.RetentionPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "days": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.SecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "rules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.SecurityGroupRule" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.SecurityGroupRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "allow": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "destinationaddresses": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "destinationports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.PortRange" - } - }, - "outbound": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sourceaddresses": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "sourceports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.PortRange" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.securitycenter.Contact": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablealertnotifications": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "phone": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.securitycenter.SecurityCenter": { - "type": "object", - "properties": { - "contacts": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.securitycenter.Contact" - } - }, - "subscriptions": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.securitycenter.SubscriptionPricing" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.securitycenter.SubscriptionPricing": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "tier": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Account": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "containers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Container" - } - }, - "enforcehttps": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "minimumtlsversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "networkrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.NetworkRule" - } - }, - "publicnetworkaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "queueproperties": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.QueueProperties" - }, - "queues": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Queue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Container": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "publicaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.NetworkRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "allowbydefault": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "bypass": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Queue": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.QueueProperties": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablelogging": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Storage": { - "type": "object", - "properties": { - "accounts": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.Account" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.synapse.Synapse": { - "type": "object", - "properties": { - "workspaces": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.synapse.Workspace" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.azure.synapse.Workspace": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablemanagedvirtualnetwork": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.cloudstack.CloudStack": { - "type": "object", - "properties": { - "compute": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.cloudstack.compute.Compute" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.cloudstack.compute.Compute": { - "type": "object", - "properties": { - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.cloudstack.compute.Instance" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.cloudstack.compute.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "userdata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.DigitalOcean": { - "type": "object", - "properties": { - "compute": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.Compute" - }, - "spaces": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Spaces" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.Compute": { - "type": "object", - "properties": { - "droplets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.Droplet" - } - }, - "firewalls": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.Firewall" - } - }, - "kubernetesclusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.KubernetesCluster" - } - }, - "loadbalancers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.LoadBalancer" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.Droplet": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "sshkeys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.Firewall": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "inboundrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.InboundFirewallRule" - } - }, - "outboundrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.OutboundFirewallRule" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.ForwardingRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "entryprotocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.InboundFirewallRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "sourceaddresses": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.KubernetesCluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "autoupgrade": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "surgeupgrade": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.LoadBalancer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "forwardingrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.ForwardingRule" - } - }, - "redirecthttptohttps": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.compute.OutboundFirewallRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "destinationaddresses": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Bucket": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "acl": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "forcedestroy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "objects": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Object" - } - }, - "versioning": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Versioning" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Object": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "acl": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Spaces": { - "type": "object", - "properties": { - "buckets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Bucket" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.digitalocean.spaces.Versioning": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.github.BranchProtection": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "requiresignedcommits": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.github.EnvironmentSecret": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryptedvalue": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "environment": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "plaintextvalue": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "repository": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "secretname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.github.GitHub": { - "type": "object", - "properties": { - "branchprotections": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.github.BranchProtection" - } - }, - "environmentsecrets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.github.EnvironmentSecret" - } - }, - "repositories": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.github.Repository" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.github.Repository": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "archived": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "public": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "vulnerabilityalerts": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.Google": { - "type": "object", - "properties": { - "bigquery": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.bigquery.BigQuery" - }, - "compute": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Compute" - }, - "dns": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.DNS" - }, - "gke": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.GKE" - }, - "iam": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.IAM" - }, - "kms": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.kms.KMS" - }, - "sql": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.SQL" - }, - "storage": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.storage.Storage" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.bigquery.AccessGrant": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "domain": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "role": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "specialgroup": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.bigquery.BigQuery": { - "type": "object", - "properties": { - "datasets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.bigquery.Dataset" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.bigquery.Dataset": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "accessgrants": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.bigquery.AccessGrant" - } - }, - "id": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Compute": { - "type": "object", - "properties": { - "disks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Disk" - } - }, - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Instance" - } - }, - "networks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Network" - } - }, - "projectmetadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.ProjectMetadata" - }, - "sslpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.SSLPolicy" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Disk": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.DiskEncryption" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.DiskEncryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "kmskeylink": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "rawkey": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BytesValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.EgressRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "destinationranges": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "firewallrule": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.FirewallRule" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Firewall": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "egressrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.EgressRule" - } - }, - "ingressrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.IngressRule" - } - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sourcetags": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "targettags": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.FirewallRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enforced": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "isallow": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "ports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.PortRange" - } - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.IngressRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "firewallrule": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.FirewallRule" - }, - "sourceranges": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "attacheddisks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Disk" - } - }, - "bootdisks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Disk" - } - }, - "canipforward": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enableprojectsshkeyblocking": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enableserialport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "networkinterfaces": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.NetworkInterface" - } - }, - "osloginenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "serviceaccount": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.ServiceAccount" - }, - "shieldedvm": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.ShieldedVMConfig" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Network": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "firewall": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Firewall" - }, - "subnetworks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.SubNetwork" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.NetworkInterface": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "haspublicip": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "natip": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "network": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.Network" - }, - "subnetwork": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.SubNetwork" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.PortRange": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "end": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "start": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.ProjectMetadata": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enableoslogin": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.SSLPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "minimumtlsversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "profile": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.ServiceAccount": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "email": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "isdefault": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "scopes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.ShieldedVMConfig": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "integritymonitoringenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "securebootenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "vtpmenabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.compute.SubNetwork": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enableflowlogs": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "purpose": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.DNS": { - "type": "object", - "properties": { - "managedzones": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.ManagedZone" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.DNSSec": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "defaultkeyspecs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.KeySpecs" - } - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.KeySpecs": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "algorithm": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "keytype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.ManagedZone": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "dnssec": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.dns.DNSSec" - }, - "visibility": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.ClientCertificate": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "issuecertificate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.Cluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "datapathprovider": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "enableautpilot": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enablelegacyabac": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enableshieldednodes": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "ipallocationpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.IPAllocationPolicy" - }, - "loggingservice": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "masterauth": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.MasterAuth" - }, - "masterauthorizednetworks": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.MasterAuthorizedNetworks" - }, - "monitoringservice": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "networkpolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NetworkPolicy" - }, - "nodeconfig": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NodeConfig" - }, - "nodepools": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NodePool" - } - }, - "privatecluster": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.PrivateCluster" - }, - "removedefaultnodepool": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "resourcelabels": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.MapValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.GKE": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.Cluster" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.IPAllocationPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.Management": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enableautorepair": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "enableautoupgrade": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.MasterAuth": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "clientcertificate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.ClientCertificate" - }, - "password": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "username": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.MasterAuthorizedNetworks": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NetworkPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NodeConfig": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enablelegacyendpoints": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "imagetype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "serviceaccount": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "workloadmetadataconfig": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.WorkloadMetadataConfig" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NodePool": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "management": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.Management" - }, - "nodeconfig": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.NodeConfig" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.PrivateCluster": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enableprivatenodes": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.gke.WorkloadMetadataConfig": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "nodemetadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Binding": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "includesdefaultserviceaccount": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "members": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "role": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Folder": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "bindings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Binding" - } - }, - "members": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Member" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.IAM": { - "type": "object", - "properties": { - "folders": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Folder" - } - }, - "organizations": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Organization" - } - }, - "projects": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Project" - } - }, - "workloadidentitypoolproviders": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.WorkloadIdentityPoolProvider" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Member": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "defaultserviceaccount": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "member": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "role": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Organization": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "bindings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Binding" - } - }, - "members": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Member" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Project": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "autocreatenetwork": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "bindings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Binding" - } - }, - "members": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Member" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.WorkloadIdentityPoolProvider": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "attributecondition": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "workloadidentitypoolid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "workloadidentitypoolproviderid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.kms.KMS": { - "type": "object", - "properties": { - "keyrings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.kms.KeyRing" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.kms.Key": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "rotationperiodseconds": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.kms.KeyRing": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "keys": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.kms.Key" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.Backups": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.DatabaseInstance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "databaseversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "isreplica": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "settings": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.Settings" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.Flags": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "containeddatabaseauthentication": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "crossdbownershipchaining": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "localinfile": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "logcheckpoints": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "logconnections": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "logdisconnections": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "loglockwaits": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "logmindurationstatement": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "logminmessages": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "logtempfilesize": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.IPConfiguration": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "authorizednetworks": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.IPConfiguration.AuthorizedNetworks" - } - }, - "enableipv4": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "requiretls": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "sslmode": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.IPConfiguration.AuthorizedNetworks": { - "type": "object", - "properties": { - "cidr": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.SQL": { - "type": "object", - "properties": { - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.DatabaseInstance" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.Settings": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "backups": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.Backups" - }, - "flags": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.Flags" - }, - "ipconfiguration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.sql.IPConfiguration" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.storage.Bucket": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "bindings": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Binding" - } - }, - "enableuniformbucketlevelaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "encryption": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.storage.BucketEncryption" - }, - "location": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "members": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.iam.Member" - } - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.storage.BucketEncryption": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "defaultkmskeyname": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.google.storage.Storage": { - "type": "object", - "properties": { - "buckets": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.google.storage.Bucket" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Egress": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "destinationcidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "ports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Port" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Ingress": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "ports": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Port" - } - }, - "sourcecidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Kubernetes": { - "type": "object", - "properties": { - "networkpolicies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.NetworkPolicy" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.NetworkPolicy": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "spec": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.NetworkPolicySpec" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.NetworkPolicySpec": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "egress": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Egress" - }, - "ingress": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Ingress" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.kubernetes.Port": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "number": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.Nifcloud": { - "type": "object", - "properties": { - "computing": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.Computing" - }, - "dns": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.dns.DNS" - }, - "nas": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.nas.NAS" - }, - "network": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.Network" - }, - "rdb": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.rdb.RDB" - }, - "sslcertificate": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.sslcertificate.SSLCertificate" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.Computing": { - "type": "object", - "properties": { - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.Instance" - } - }, - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.SecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "networkinterfaces": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.NetworkInterface" - } - }, - "securitygroup": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.NetworkInterface": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "networkid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.SecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "egressrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.SecurityGroupRule" - } - }, - "ingressrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.SecurityGroupRule" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.computing.SecurityGroupRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cidr": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.dns.DNS": { - "type": "object", - "properties": { - "records": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.dns.Record" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.dns.Record": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "record": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "type": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.nas.NAS": { - "type": "object", - "properties": { - "nasinstances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.nas.NASInstance" - } - }, - "nassecuritygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.nas.NASSecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.nas.NASInstance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "networkid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.nas.NASSecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.ElasticLoadBalancer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "listeners": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.ElasticLoadBalancerListener" - } - }, - "networkinterfaces": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.NetworkInterface" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.ElasticLoadBalancerListener": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.LoadBalancer": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "listeners": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.LoadBalancerListener" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.LoadBalancerListener": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "tlspolicy": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.Network": { - "type": "object", - "properties": { - "elasticloadbalancers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.ElasticLoadBalancer" - } - }, - "loadbalancers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.LoadBalancer" - } - }, - "routers": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.Router" - } - }, - "vpngateways": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.VpnGateway" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.NetworkInterface": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "isvipnetwork": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "networkid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.Router": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "networkinterfaces": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.NetworkInterface" - } - }, - "securitygroup": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.network.VpnGateway": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "securitygroup": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.rdb.DBInstance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "backupretentionperioddays": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "engine": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "engineversion": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "networkid": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "publicaccess": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.rdb.DBSecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cidrs": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.rdb.RDB": { - "type": "object", - "properties": { - "dbinstances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.rdb.DBInstance" - } - }, - "dbsecuritygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.rdb.DBSecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.sslcertificate.SSLCertificate": { - "type": "object", - "properties": { - "servercertificates": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.sslcertificate.ServerCertificate" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.nifcloud.sslcertificate.ServerCertificate": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "expiration": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.TimeValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Compute": { - "type": "object", - "properties": { - "firewall": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Firewall" - }, - "instances": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Instance" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Firewall": { - "type": "object", - "properties": { - "allowrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.FirewallRule" - } - }, - "denyrules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.FirewallRule" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.FirewallRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "destination": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "destinationport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "enabled": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "source": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "sourceport": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Instance": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "adminpassword": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Networking": { - "type": "object", - "properties": { - "securitygroups": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.SecurityGroup" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.OpenStack": { - "type": "object", - "properties": { - "compute": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Compute" - }, - "networking": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.Networking" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.SecurityGroup": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "description": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "name": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "rules": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.openstack.SecurityGroupRule" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.openstack.SecurityGroupRule": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "cidr": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - }, - "ethertype": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "isingress": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" - }, - "portmax": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "portmin": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.IntValue" - }, - "protocol": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.oracle.AddressReservation": { - "type": "object", - "properties": { - "__defsec_metadata": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata" - }, - "pool": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.oracle.Compute": { - "type": "object", - "properties": { - "addressreservations": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.oracle.AddressReservation" - } - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.providers.oracle.Oracle": { - "type": "object", - "properties": { - "compute": { - "type": "object", - "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.oracle.Compute" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.BoolValue": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - }, - "value": { - "type": "boolean" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.BytesValue": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.IntValue": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.MapValue": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - }, - "value": { - "type": "object" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.Metadata": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.StringValue": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - }, - "github.com.aquasecurity.trivy.pkg.iac.types.TimeValue": { - "type": "object", - "properties": { - "endline": { - "type": "integer" - }, - "explicit": { - "type": "boolean" - }, - "filepath": { - "type": "string" - }, - "fskey": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "resource": { - "type": "string" - }, - "sourceprefix": { - "type": "string" - }, - "startline": { - "type": "integer" - }, - "unresolvable": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - } - } -} \ No newline at end of file diff --git a/pkg/iac/rego/schemas/dockerfile.json b/pkg/iac/rego/schemas/dockerfile.json deleted file mode 100644 index 0805b47f702d..000000000000 --- a/pkg/iac/rego/schemas/dockerfile.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/aquasecurity/trivy/blob/main/pkg/iac/rego/schemas/dockerfile.json", - "type": "object", - "properties": { - "Stages": { - "type": "array", - "items": { - "$ref": "#/$defs/stage" - } - } - }, - "$defs": { - "stage": { - "type": "object", - "properties": { - "Name": { - "type": "string" - }, - "Commands": { - "type": "array", - "items": { - "$ref": "#/$defs/command" - } - } - } - }, - "command": { - "type": "object", - "properties": { - "Flags": { - "type": "array", - "items": { - "type": "string" - } - }, - "Value": { - "type": "array", - "items": { - "type": "string" - } - }, - "Cmd": { - "type": "string" - }, - "SubCmd": { - "type": "string" - }, - "Original": { - "type": "string" - }, - "Path": { - "type": "string" - }, - "JSON": { - "type": "boolean" - }, - "Stage": { - "type": "integer" - }, - "StartLine": { - "type": "integer" - }, - "EndLine": { - "type": "integer" - } - } - } - } -} \ No newline at end of file diff --git a/pkg/iac/rego/schemas/kubernetes.json b/pkg/iac/rego/schemas/kubernetes.json deleted file mode 100644 index 27de806d8bb5..000000000000 --- a/pkg/iac/rego/schemas/kubernetes.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/aquasecurity/trivy/blob/main/pkg/iac/rego/schemas/kubernetes.json", - "type": "object", - "properties": { - "apiVersion": { - "type": "string" - }, - "kind": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "spec": { - "type": "object" - }, - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "apiGroups": { - "type": "array", - "items": { - "type": "string" - } - }, - "resources": { - "type": "array", - "items": { - "type": "string" - } - }, - "resourceNames": { - "type": "array", - "items": { - "type": "string" - } - }, - "verbs": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/pkg/iac/rego/schemas/rbac.json b/pkg/iac/rego/schemas/rbac.json deleted file mode 100644 index 0bbc356297af..000000000000 --- a/pkg/iac/rego/schemas/rbac.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/aquasecurity/trivy/blob/main/pkg/iac/rego/schemas/rbac.json", - "type": "object", - "properties": { - "apiVersion": { - "type": "string" - }, - "kind": { - "type": "string" - }, - "metadata": { - "type": "object" - }, - "spec": { - "type": "object" - }, - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "apiGroups": { - "type": "array", - "items": { - "type": "string" - } - }, - "resources": { - "type": "array", - "items": { - "type": "string" - } - }, - "resourceNames": { - "type": "array", - "items": { - "type": "string" - } - }, - "verbs": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/pkg/iac/rego/schemas/schemas.go b/pkg/iac/rego/schemas/schemas.go deleted file mode 100644 index 73f92c10fc89..000000000000 --- a/pkg/iac/rego/schemas/schemas.go +++ /dev/null @@ -1,16 +0,0 @@ -package schemas - -import ( - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -var SchemaMap = map[types.Source]Schema{ - types.SourceDefsec: Cloud, - types.SourceCloud: Cloud, - types.SourceKubernetes: Kubernetes, - types.SourceRbac: Kubernetes, - types.SourceDockerfile: Dockerfile, - types.SourceTOML: Anything, - types.SourceYAML: Anything, - types.SourceJSON: Anything, -} diff --git a/pkg/iac/rego/store.go b/pkg/iac/rego/store.go deleted file mode 100644 index c75818d402e7..000000000000 --- a/pkg/iac/rego/store.go +++ /dev/null @@ -1,48 +0,0 @@ -package rego - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" - - "github.com/open-policy-agent/opa/loader" - "github.com/open-policy-agent/opa/storage" -) - -// initialize a store populated with OPA data files found in dataPaths -func initStore(dataFS fs.FS, dataPaths, namespaces []string) (storage.Store, error) { - // FilteredPaths will recursively find all file paths that contain a valid document - // extension from the given list of data paths. - allDocumentPaths, _ := loader.FilteredPathsFS(dataFS, dataPaths, func(abspath string, info os.FileInfo, depth int) bool { - if info.IsDir() { - return false // filter in, include - } - ext := strings.ToLower(filepath.Ext(info.Name())) - for _, filter := range []string{ - ".yaml", - ".yml", - ".json", - } { - if filter == ext { - return false // filter in, include - } - } - return true // filter out, exclude - }) - - documents, err := loader.NewFileLoader().WithFS(dataFS).All(allDocumentPaths) - if err != nil { - return nil, fmt.Errorf("load documents: %w", err) - } - - // pass all namespaces so that rego rule can refer to namespaces as data.namespaces - documents.Documents["namespaces"] = namespaces - - store, err := documents.Store() - if err != nil { - return nil, fmt.Errorf("get documents store: %w", err) - } - return store, nil -} diff --git a/pkg/iac/rego/testdata/embedded/bad-check.rego b/pkg/iac/rego/testdata/embedded/bad-check.rego deleted file mode 100644 index f30348989af4..000000000000 --- a/pkg/iac/rego/testdata/embedded/bad-check.rego +++ /dev/null @@ -1,9 +0,0 @@ -# METADATA -# schemas: -# - input: schema["fooschema"] - -package builtin.bad.test - -deny { - input.evil == "foo bar" -} \ No newline at end of file diff --git a/pkg/iac/rego/testdata/embedded/my-check1.rego b/pkg/iac/rego/testdata/embedded/my-check1.rego deleted file mode 100644 index a087e9376abc..000000000000 --- a/pkg/iac/rego/testdata/embedded/my-check1.rego +++ /dev/null @@ -1,11 +0,0 @@ -# METADATA -# schemas: -# - input: schema["fooschema"] -# custom: -# avd_id: test-001 - -package builtin.test - -deny { - input.foo == "foo bar" -} \ No newline at end of file diff --git a/pkg/iac/rego/testdata/policies/invalid.rego b/pkg/iac/rego/testdata/policies/invalid.rego deleted file mode 100644 index 6a4afdc0fcd0..000000000000 --- a/pkg/iac/rego/testdata/policies/invalid.rego +++ /dev/null @@ -1,8 +0,0 @@ -# METADATA -# schemas: -# - input: schema["dockerfile"] -package defsec.test_invalid - -deny { - input.Stages[0].Commands[0].FooBarNothingBurger == "lol" -} diff --git a/pkg/iac/rego/testdata/policies/valid.rego b/pkg/iac/rego/testdata/policies/valid.rego deleted file mode 100644 index 9af18e24b0a3..000000000000 --- a/pkg/iac/rego/testdata/policies/valid.rego +++ /dev/null @@ -1,8 +0,0 @@ -# METADATA -# schemas: -# - input: schema["dockerfile"] -package defsec.test_valid - -deny { - input.Stages[0].Commands[0].Cmd == "lol" -} diff --git a/pkg/iac/rules/providers.go b/pkg/iac/rules/providers.go deleted file mode 100644 index 7c14aa1c627a..000000000000 --- a/pkg/iac/rules/providers.go +++ /dev/null @@ -1,169 +0,0 @@ -package rules - -import ( - "encoding/json" - "strings" -) - -type Provider struct { - Name string `json:"name"` - Services []Service `json:"services"` -} - -type Service struct { - Name string `json:"name"` - Checks []Check `json:"checks"` -} - -type Check struct { - Name string `json:"name"` - Description string `json:"description"` -} - -func GetProvidersHierarchy() (providers map[string]map[string][]string) { - - registeredRules := GetRegistered() - - provs := make(map[string]map[string][]string) - - for _, rule := range registeredRules { - - cNames := make(map[string]bool) - pName := strings.ToLower(rule.GetRule().Provider.DisplayName()) - sName := strings.ToLower(rule.GetRule().Service) - cName := rule.GetRule().AVDID - - if _, ok := provs[pName]; !ok { - provs[pName] = make(map[string][]string) - } - - if _, ok := provs[pName][sName]; !ok { - provs[pName][sName] = make([]string, 0) - } - - if _, ok := cNames[cName]; !ok { - cNames[cName] = true - provs[pName][sName] = append(provs[pName][sName], cName) - } - } - - return provs -} - -func GetProviders() (providers []Provider) { - - registeredRules := GetRegistered() - - provs := make(map[string]map[string][]Check) - - for _, rule := range registeredRules { - - pName := strings.ToLower(rule.GetRule().Provider.DisplayName()) - sName := strings.ToLower(rule.GetRule().Service) - cName := rule.GetRule().AVDID - desc := rule.GetRule().Summary - - if _, ok := provs[pName]; !ok { - provs[pName] = make(map[string][]Check) - } - - if _, ok := provs[pName][sName]; !ok { - provs[pName][sName] = []Check{} - } - - provs[pName][sName] = append(provs[pName][sName], Check{ - Name: cName, - Description: desc, - }) - } - - for providerName, providerServices := range provs { - var services []Service - for serviceName, checks := range providerServices { - services = append(services, Service{ - Name: serviceName, - Checks: checks, - }) - } - - providers = append(providers, Provider{ - Name: providerName, - Services: services, - }) - } - - return providers -} - -func GetProvidersAsJson() ([]byte, error) { - - providers := GetProviders() - - return json.MarshalIndent(providers, "", " ") -} - -func GetProviderNames() []string { - - registeredRules := GetRegistered() - - providers := make(map[string]bool) - - for _, rule := range registeredRules { - - if _, ok := providers[rule.GetRule().Provider.DisplayName()]; !ok { - providers[rule.GetRule().Provider.DisplayName()] = true - } - - } - - var uniqueProviders []string - for p := range providers { - uniqueProviders = append(uniqueProviders, p) - } - - return uniqueProviders - -} - -func GetProviderServiceNames(providerName string) []string { - - registeredRules := GetRegistered() - - services := make(map[string]bool) - - for _, rule := range registeredRules { - - if !strings.EqualFold(providerName, rule.GetRule().Provider.DisplayName()) { - continue - } - - if _, ok := services[rule.GetRule().Service]; !ok { - services[rule.GetRule().Service] = true - } - - } - var uniqueServices []string - for p := range services { - uniqueServices = append(uniqueServices, p) - } - - return uniqueServices -} - -func GetProviderServiceCheckNames(providerName, serviceName string) []string { - - registeredRules := GetRegistered() - - var checks []string - - for _, rule := range registeredRules { - - if !strings.EqualFold(providerName, rule.GetRule().Provider.DisplayName()) || - !strings.EqualFold(serviceName, rule.GetRule().Service) { - continue - } - - checks = append(checks, rule.GetRule().AVDID) - } - return checks -} diff --git a/pkg/iac/rules/providers_stub.go b/pkg/iac/rules/providers_stub.go new file mode 100644 index 000000000000..c5ddecaa23ab --- /dev/null +++ b/pkg/iac/rules/providers_stub.go @@ -0,0 +1,16 @@ +package rules + +type Provider struct { + Name string `json:"name"` + Services []Service `json:"services"` +} + +type Service struct { + Name string `json:"name"` + Checks []Check `json:"checks"` +} + +type Check struct { + Name string `json:"name"` + Description string `json:"description"` +} diff --git a/pkg/iac/rules/register.go b/pkg/iac/rules/register.go deleted file mode 100755 index 207502672c51..000000000000 --- a/pkg/iac/rules/register.go +++ /dev/null @@ -1,138 +0,0 @@ -package rules - -import ( - "sync" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy-checks/pkg/specs" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/scan" - dftypes "github.com/aquasecurity/trivy/pkg/iac/types" - ruleTypes "github.com/aquasecurity/trivy/pkg/iac/types/rules" - "github.com/aquasecurity/trivy/pkg/set" -) - -type registry struct { - sync.RWMutex - index int - frameworks map[framework.Framework][]ruleTypes.RegisteredRule -} - -var coreRegistry = registry{ - frameworks: make(map[framework.Framework][]ruleTypes.RegisteredRule), -} - -func Reset() { - coreRegistry.Reset() -} - -func Register(rule scan.Rule) ruleTypes.RegisteredRule { - return coreRegistry.register(rule) -} - -func Deregister(rule ruleTypes.RegisteredRule) { - coreRegistry.deregister(rule) -} - -func (r *registry) register(rule scan.Rule) ruleTypes.RegisteredRule { - r.Lock() - defer r.Unlock() - if len(rule.Frameworks) == 0 { - rule.Frameworks = map[framework.Framework][]string{framework.Default: nil} - } - registeredRule := ruleTypes.RegisteredRule{ - Number: r.index, - Rule: rule, - } - r.index++ - for fw := range rule.Frameworks { - r.frameworks[fw] = append(r.frameworks[fw], registeredRule) - } - - r.frameworks[framework.ALL] = append(r.frameworks[framework.ALL], registeredRule) - - return registeredRule -} - -func (r *registry) deregister(rule ruleTypes.RegisteredRule) { - r.Lock() - defer r.Unlock() - for fw := range r.frameworks { - for i, registered := range r.frameworks[fw] { - if registered.Number == rule.Number { - r.frameworks[fw] = append(r.frameworks[fw][:i], r.frameworks[fw][i+1:]...) - break - } - } - } -} - -func (r *registry) getFrameworkRules(fw ...framework.Framework) []ruleTypes.RegisteredRule { - r.RLock() - defer r.RUnlock() - var registered []ruleTypes.RegisteredRule - if len(fw) == 0 { - fw = []framework.Framework{framework.Default} - } - unique := set.New[int]() - for _, f := range fw { - for _, rule := range r.frameworks[f] { - if unique.Contains(rule.Number) { - continue - } - registered = append(registered, rule) - unique.Append(rule.Number) - } - } - return registered -} - -func (r *registry) getSpecRules(spec string) []ruleTypes.RegisteredRule { - r.RLock() - defer r.RUnlock() - var specRules []ruleTypes.RegisteredRule - - var complianceSpec dftypes.ComplianceSpec - specContent := specs.GetSpec(spec) - if err := yaml.Unmarshal([]byte(specContent), &complianceSpec); err != nil { - return nil - } - - registered := r.getFrameworkRules(framework.ALL) - for _, rule := range registered { - for _, csRule := range complianceSpec.Spec.Controls { - if len(csRule.Checks) > 0 { - for _, c := range csRule.Checks { - if rule.GetRule().AVDID == c.ID { - specRules = append(specRules, rule) - } - } - } - } - } - - return specRules -} - -func (r *registry) Reset() { - r.Lock() - defer r.Unlock() - r.frameworks = make(map[framework.Framework][]ruleTypes.RegisteredRule) -} - -func GetFrameworkRules(fw ...framework.Framework) []ruleTypes.RegisteredRule { - return coreRegistry.getFrameworkRules(fw...) -} - -func GetSpecRules(spec string) []ruleTypes.RegisteredRule { - if spec != "" { - return coreRegistry.getSpecRules(spec) - } - - return GetFrameworkRules() -} - -func GetRegistered(fw ...framework.Framework) []ruleTypes.RegisteredRule { - return GetFrameworkRules(fw...) -} diff --git a/pkg/iac/rules/register_test.go b/pkg/iac/rules/register_test.go deleted file mode 100644 index 9c6ca00985c9..000000000000 --- a/pkg/iac/rules/register_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package rules - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_Reset(t *testing.T) { - Reset() - rule := scan.Rule{} - _ = Register(rule) - assert.Len(t, GetFrameworkRules(), 1) - Reset() - assert.Empty(t, GetFrameworkRules()) -} - -func Test_Registration(t *testing.T) { - var tests = []struct { - name string - registeredFrameworks map[framework.Framework][]string - inputFrameworks []framework.Framework - expected bool - }{ - { - name: "rule without framework specified should be returned when no frameworks are requested", - expected: true, - }, - { - name: "rule without framework specified should not be returned when a specific framework is requested", - inputFrameworks: []framework.Framework{framework.CIS_AWS_1_2}, - expected: false, - }, - { - name: "rule without framework specified should be returned when the default framework is requested", - inputFrameworks: []framework.Framework{framework.Default}, - expected: true, - }, - { - name: "rule with default framework specified should be returned when the default framework is requested", - registeredFrameworks: map[framework.Framework][]string{framework.Default: {"1.1"}}, - inputFrameworks: []framework.Framework{framework.Default}, - expected: true, - }, - { - name: "rule with default framework specified should not be returned when a specific framework is requested", - registeredFrameworks: map[framework.Framework][]string{framework.Default: {"1.1"}}, - inputFrameworks: []framework.Framework{framework.CIS_AWS_1_2}, - expected: false, - }, - { - name: "rule with specific framework specified should not be returned when a default framework is requested", - registeredFrameworks: map[framework.Framework][]string{framework.CIS_AWS_1_2: {"1.1"}}, - inputFrameworks: []framework.Framework{framework.Default}, - expected: false, - }, - { - name: "rule with specific framework specified should be returned when the specific framework is requested", - registeredFrameworks: map[framework.Framework][]string{framework.CIS_AWS_1_2: {"1.1"}}, - inputFrameworks: []framework.Framework{framework.CIS_AWS_1_2}, - expected: true, - }, - { - name: "rule with multiple frameworks specified should be returned when the specific framework is requested", - registeredFrameworks: map[framework.Framework][]string{framework.CIS_AWS_1_2: {"1.1"}, "blah": {"1.2"}}, - inputFrameworks: []framework.Framework{framework.CIS_AWS_1_2}, - expected: true, - }, - { - name: "rule with multiple frameworks specified should be returned only once when multiple matching frameworks are requested", - registeredFrameworks: map[framework.Framework][]string{framework.CIS_AWS_1_2: {"1.1"}, "blah": {"1.2"}, "something": {"1.3"}}, - inputFrameworks: []framework.Framework{framework.CIS_AWS_1_2, "blah", "other"}, - expected: true, - }, - } - - for i, test := range tests { - t.Run(test.name, func(t *testing.T) { - Reset() - rule := scan.Rule{ - AVDID: fmt.Sprintf("%d-%s", i, test.name), - Frameworks: test.registeredFrameworks, - } - _ = Register(rule) - var found bool - for _, matchedRule := range GetFrameworkRules(test.inputFrameworks...) { - if matchedRule.GetRule().AVDID == rule.AVDID { - assert.False(t, found, "rule should not be returned more than once") - found = true - } - } - assert.Equal(t, test.expected, found, "rule should be returned if it matches any of the input frameworks") - }) - } -} - -func Test_Deregistration(t *testing.T) { - Reset() - registrationA := Register(scan.Rule{ - AVDID: "A", - }) - registrationB := Register(scan.Rule{ - AVDID: "B", - }) - assert.Len(t, GetFrameworkRules(), 2) - Deregister(registrationA) - actual := GetFrameworkRules() - require.Len(t, actual, 1) - assert.Equal(t, "B", actual[0].GetRule().AVDID) - Deregister(registrationB) - assert.Empty(t, GetFrameworkRules()) -} - -func Test_DeregistrationMultipleFrameworks(t *testing.T) { - Reset() - registrationA := Register(scan.Rule{ - AVDID: "A", - }) - registrationB := Register(scan.Rule{ - AVDID: "B", - Frameworks: map[framework.Framework][]string{ - "a": nil, - "b": nil, - "c": nil, - framework.Default: nil, - }, - }) - assert.Len(t, GetFrameworkRules(), 2) - Deregister(registrationA) - actual := GetFrameworkRules() - require.Len(t, actual, 1) - assert.Equal(t, "B", actual[0].GetRule().AVDID) - Deregister(registrationB) - assert.Empty(t, GetFrameworkRules()) -} diff --git a/pkg/iac/scan/code.go b/pkg/iac/scan/code.go deleted file mode 100644 index e50cc5920f67..000000000000 --- a/pkg/iac/scan/code.go +++ /dev/null @@ -1,320 +0,0 @@ -package scan - -import ( - "bufio" - "errors" - "fmt" - "io/fs" - "path/filepath" - "strings" - - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Code struct { - Lines []Line -} - -func (c *Code) truncateLines(maxLines int) { - previouslyTruncated := maxLines-1 > 0 && c.Lines[maxLines-2].Truncated - if maxLines-1 > 0 && c.Lines[maxLines-1].LastCause { - c.Lines[maxLines-2].LastCause = true - } - c.Lines[maxLines-1] = Line{ - Truncated: true, - Number: c.Lines[maxLines-1].Number, - } - if previouslyTruncated { - c.Lines = c.Lines[:maxLines-1] - } else { - c.Lines = c.Lines[:maxLines] - } -} - -func (c *Code) markFirstAndLastCauses() { - var isFirst bool - var isLast bool - - for i, line := range c.Lines { - if line.IsCause && !isFirst { - c.Lines[i].FirstCause = true - isFirst = true - } - - if isFirst && !line.IsCause && i > 0 { - c.Lines[i-1].LastCause = true - isLast = true - break - } - } - - if !isLast && len(c.Lines) > 0 { - c.Lines[len(c.Lines)-1].LastCause = true - } -} - -type Line struct { - Number int `json:"Number"` - Content string `json:"Content"` - IsCause bool `json:"IsCause"` - Annotation string `json:"Annotation"` - Truncated bool `json:"Truncated"` - Highlighted string `json:"Highlighted,omitempty"` - FirstCause bool `json:"FirstCause"` - LastCause bool `json:"LastCause"` -} - -func (c *Code) IsCauseMultiline() bool { - var count int - for _, line := range c.Lines { - if line.IsCause { - count++ - if count > 1 { - return true - } - } - } - return false -} - -const ( - DarkTheme = "solarized-dark256" - LightTheme = "github" -) - -type codeSettings struct { - theme string - allowTruncation bool - maxLines int - includeHighlighted bool -} - -var defaultCodeSettings = codeSettings{ - theme: DarkTheme, - allowTruncation: true, - maxLines: 10, - includeHighlighted: true, -} - -type CodeOption func(*codeSettings) - -func OptionCodeWithTheme(theme string) CodeOption { - return func(s *codeSettings) { - s.theme = theme - } -} - -func OptionCodeWithDarkTheme() CodeOption { - return func(s *codeSettings) { - s.theme = DarkTheme - } -} - -func OptionCodeWithLightTheme() CodeOption { - return func(s *codeSettings) { - s.theme = LightTheme - } -} - -func OptionCodeWithTruncation(truncate bool) CodeOption { - return func(s *codeSettings) { - s.allowTruncation = truncate - } -} - -func OptionCodeWithMaxLines(lines int) CodeOption { - return func(s *codeSettings) { - s.maxLines = lines - } -} - -func OptionCodeWithHighlighted(include bool) CodeOption { - return func(s *codeSettings) { - s.includeHighlighted = include - } -} - -func (r *Result) GetCode(opts ...CodeOption) (*Code, error) { - settings := defaultCodeSettings - for _, opt := range opts { - opt(&settings) - } - - fsys := r.Metadata().Range().GetFS() - if fsys == nil { - return nil, errors.New("code unavailable: result was not mapped to a known filesystem") - } - - innerRange := r.metadata.Range() - if err := innerRange.Validate(); err != nil { - return nil, err - } - - if innerRange.GetStartLine() == 0 { - return nil, fmt.Errorf("inner range has invalid start line: %s", innerRange.String()) - } - - outerRange := r.getOuterRange() - if err := outerRange.Validate(); err != nil { - return nil, err - } - - filePath := strings.TrimPrefix(filepath.ToSlash(r.fsPath), "/") - rawLines, err := readLinesFromFile(fsys, filePath, outerRange.GetStartLine(), outerRange.GetEndLine()) - if err != nil { - return nil, err - } - - if outerRange.GetEndLine()-outerRange.GetStartLine() > len(rawLines) { - return nil, fmt.Errorf("invalid outer range: %s", outerRange.String()) - } - - highlightedLines := r.getHighlightedLines(outerRange, innerRange, rawLines, settings) - - var code Code - - shrink := settings.allowTruncation && outerRange.LineCount() > (innerRange.LineCount()+10) - - if shrink { - code.Lines = r.getTruncatedLines(outerRange, innerRange, rawLines, highlightedLines) - } else { - code.Lines = r.getAllLines(outerRange, innerRange, rawLines, highlightedLines) - } - - if settings.allowTruncation && len(code.Lines) > settings.maxLines && settings.maxLines > 0 { - code.truncateLines(settings.maxLines) - } - - code.markFirstAndLastCauses() - - return &code, nil -} - -func (r *Result) getHighlightedLines(outerRange, innerRange iacTypes.Range, rawLines []string, settings codeSettings) []string { - - highlightedLines := make([]string, len(rawLines)) - if !settings.includeHighlighted { - return highlightedLines - } - - content := strings.Join(rawLines, "\n") - fsKey := iacTypes.CreateFSKey(innerRange.GetFS()) - highlightedLines = highlight(fsKey, innerRange.GetLocalFilename(), - outerRange.GetStartLine(), outerRange.GetEndLine(), content, settings.theme) - - if len(highlightedLines) < len(rawLines) { - return rawLines - } - - return highlightedLines -} - -func (r *Result) getOuterRange() iacTypes.Range { - outer := r.Metadata().Range() - for parent := r.Metadata().Parent(); parent != nil && - parent.Range().GetFilename() == outer.GetFilename() && - parent.Range().GetStartLine() > 0; parent = parent.Parent() { - outer = parent.Range() - } - return outer -} - -func (r *Result) getTruncatedLines(outerRange, innerRange iacTypes.Range, rawLines, highlightedLines []string) []Line { - var lines []Line - - if outerRange.GetStartLine() < innerRange.GetStartLine() { - lines = append(lines, Line{ - Content: rawLines[0], - Highlighted: highlightedLines[0], - Number: outerRange.GetStartLine(), - }) - if outerRange.GetStartLine()+1 < innerRange.GetStartLine() { - lines = append(lines, Line{ - Truncated: true, - Number: outerRange.GetStartLine() + 1, - }) - } - } - - for lineNo := innerRange.GetStartLine() - outerRange.GetStartLine(); lineNo <= innerRange.GetEndLine()-outerRange.GetStartLine(); lineNo++ { - if lineNo >= len(rawLines) || lineNo >= len(highlightedLines) { - break - } - - line := Line{ - Number: lineNo + outerRange.GetStartLine(), - Content: strings.TrimSuffix(rawLines[lineNo], "\r"), - Highlighted: strings.TrimSuffix(highlightedLines[lineNo], "\r"), - IsCause: true, - } - - if r.Annotation() != "" && lineNo == innerRange.GetStartLine()-outerRange.GetStartLine()-1 { - line.Annotation = r.Annotation() - } - - lines = append(lines, line) - } - - if outerRange.GetEndLine() > innerRange.GetEndLine() { - if outerRange.GetEndLine() > innerRange.GetEndLine()+1 { - lines = append(lines, Line{ - Truncated: true, - Number: outerRange.GetEndLine() - 1, - }) - } - lines = append(lines, Line{ - Content: rawLines[outerRange.GetEndLine()-outerRange.GetStartLine()], - Highlighted: highlightedLines[outerRange.GetEndLine()-outerRange.GetStartLine()], - Number: outerRange.GetEndLine(), - }) - } - - return lines -} - -func (r *Result) getAllLines(outerRange, innerRange iacTypes.Range, rawLines, highlightedLines []string) []Line { - lines := make([]Line, 0, outerRange.GetEndLine()-outerRange.GetStartLine()+1) - - for lineNo := 0; lineNo <= outerRange.GetEndLine()-outerRange.GetStartLine(); lineNo++ { - line := Line{ - Number: lineNo + outerRange.GetStartLine(), - Content: strings.TrimSuffix(rawLines[lineNo], "\r"), - Highlighted: strings.TrimSuffix(highlightedLines[lineNo], "\r"), - IsCause: lineNo >= innerRange.GetStartLine()-outerRange.GetStartLine() && - lineNo <= innerRange.GetEndLine()-outerRange.GetStartLine(), - } - - if r.Annotation() != "" && lineNo == innerRange.GetStartLine()-outerRange.GetStartLine()-1 { - line.Annotation = r.Annotation() - } - - lines = append(lines, line) - } - - return lines -} - -func readLinesFromFile(fsys fs.FS, path string, from, to int) ([]string, error) { - slashedPath := strings.TrimPrefix(filepath.ToSlash(path), "/") - - file, err := fsys.Open(slashedPath) - if err != nil { - return nil, fmt.Errorf("failed to read file from result filesystem: %w", err) - } - defer file.Close() - - scanner := bufio.NewScanner(file) - rawLines := make([]string, 0, to-from+1) - - for lineNum := 0; scanner.Scan() && lineNum < to; lineNum++ { - if lineNum >= from-1 { - rawLines = append(rawLines, scanner.Text()) - } - } - - if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("failed to scan file: %w", err) - } - - return rawLines, nil -} diff --git a/pkg/iac/scan/code_test.go b/pkg/iac/scan/code_test.go deleted file mode 100644 index 559f1d9dfe69..000000000000 --- a/pkg/iac/scan/code_test.go +++ /dev/null @@ -1,402 +0,0 @@ -package scan - -import ( - "strings" - "testing" - "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func TestResult_GetCode(t *testing.T) { - const line = "If you can do a half-assed job of anything, you're a one-eyed man in a kingdom of the blind." - tests := []struct { - name string - source string - filename string - startInnerLine int - endInnerLine int - startOuterLine int - endOuterLine int - expected []Line - options []CodeOption - wantErr bool - }{ - { - name: "basic w/ defaults", - source: `1 -2 -3 -4`, - filename: "test.txt", - startInnerLine: 2, - endInnerLine: 3, - expected: []Line{ - { - Number: 2, - Content: "2", - IsCause: true, - Highlighted: "2", - FirstCause: true, - LastCause: false, - }, - { - Number: 3, - Content: "3", - IsCause: true, - Highlighted: "3", - FirstCause: false, - LastCause: true, - }, - }, - }, - { - name: "nested ranges", - source: `resource "aws_s3_bucket" "something" { - bucket = "something" -}`, - filename: "main.tf", - startInnerLine: 2, - endInnerLine: 2, - startOuterLine: 1, - endOuterLine: 3, - options: []CodeOption{OptionCodeWithHighlighted(false)}, - expected: []Line{ - { - Number: 1, - Content: `resource "aws_s3_bucket" "something" {`, - }, - { - Number: 2, - Content: ` bucket = "something"`, - IsCause: true, - FirstCause: true, - LastCause: true, - }, - { - Number: 3, - Content: "}", - }, - }, - }, - { - name: "bad filename", - source: `1 -2 -3 -4`, - filename: "", - startInnerLine: 2, - endInnerLine: 3, - wantErr: true, - }, - { - name: "no line numbers", - source: `1 -2 -3 -4`, - filename: "test.txt", - startInnerLine: 0, - endInnerLine: 0, - wantErr: true, - }, - { - name: "negative line numbers", - source: `1 -2 -3 -4`, - filename: "test.txt", - startInnerLine: -2, - endInnerLine: -1, - wantErr: true, - }, - { - name: "invalid line numbers", - source: `1 -2 -3 -4`, - filename: "test.txt", - startInnerLine: 5, - endInnerLine: 6, - wantErr: true, - }, - { - name: "syntax highlighting", - source: `FROM ubuntu`, - filename: "Dockerfile", - startInnerLine: 1, - endInnerLine: 1, - expected: []Line{ - { - Number: 1, - Content: "FROM ubuntu", - IsCause: true, - Highlighted: "\x1b[38;5;64mFROM\x1b[0m\x1b[38;5;37m ubuntu\x1b[0m", - FirstCause: true, - LastCause: true, - }, - }, - }, - { - name: "truncation", - source: strings.Repeat(line+"\n", 100), - filename: "longfile.txt", - startInnerLine: 1, - endInnerLine: 100, - expected: []Line{ - { - Number: 1, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: true, - LastCause: false, - }, - { - Number: 2, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 3, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 4, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 5, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 6, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 7, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 8, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: false, - }, - { - Number: 9, - Content: line, - IsCause: true, - Highlighted: line, - FirstCause: false, - LastCause: true, - }, - { - Number: 10, - Truncated: true, - }, - }, - }, - { - name: "invalid inner range", - source: `Test`, - filename: "test.txt", - startInnerLine: 0, - endInnerLine: 0, - wantErr: true, - }, - { - name: "invalid outer range", - source: `Test`, - filename: "test.txt", - startInnerLine: 10, - endInnerLine: 12, - startOuterLine: 5, - endOuterLine: 3, - wantErr: true, - }, - { - name: "truncate with outer range", - source: strings.Repeat(line+"\n", 100), - filename: "longfile.txt", - startOuterLine: 1, - endOuterLine: 100, - startInnerLine: 10, - endInnerLine: 12, - options: []CodeOption{OptionCodeWithTruncation(true)}, - expected: []Line{ - { - Number: 1, - Content: line, - Highlighted: line, - }, - { - Number: 2, - Truncated: true, - }, - { - Number: 10, - Content: line, - IsCause: true, - FirstCause: true, - Highlighted: line, - }, - { - Number: 11, - Content: line, - IsCause: true, - Highlighted: line, - }, - { - Number: 12, - Content: line, - IsCause: true, - LastCause: true, - Highlighted: line, - }, - { - Number: 99, - Truncated: true, - }, - { - Number: 100, - Content: line, - Highlighted: line, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - fsys := fstest.MapFS{ - test.filename: &fstest.MapFile{ - Data: []byte(test.source), - }, - } - - meta := iacTypes.NewMetadata( - iacTypes.NewRange(test.filename, test.startInnerLine, test.endInnerLine, "", fsys), - "", - ) - if test.startOuterLine > 0 { - meta = meta.WithParent(iacTypes.NewMetadata( - iacTypes.NewRange(test.filename, test.startOuterLine, test.endOuterLine, "", fsys), - "", - )) - } - result := &Result{ - metadata: meta, - fsPath: test.filename, - } - code, err := result.GetCode(test.options...) - if test.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - assert.Equal(t, test.expected, code.Lines) - }) - } - -} - -func TestCode_IsCauseMultiline(t *testing.T) { - - tests := []struct { - name string - code Code - expected bool - }{ - { - name: "no cause", - code: Code{ - Lines: []Line{ - { - Number: 1, - Content: "Test", - Highlighted: "Test", - }, - }, - }, - expected: false, - }, - { - name: "one cause", - code: Code{ - Lines: []Line{ - { - Number: 1, - Content: "Test", - IsCause: true, - Highlighted: "Test", - }, - }, - }, - expected: false, - }, - { - name: "multiple causes", - code: Code{ - Lines: []Line{ - { - Number: 1, - Content: "Test", - IsCause: true, - Highlighted: "Test", - }, - { - Number: 2, - Content: "Test", - IsCause: true, - Highlighted: "Test", - }, - { - Number: 3, - Content: "Test", - IsCause: true, - Highlighted: "Test", - }, - }, - }, - expected: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.expected, tt.code.IsCauseMultiline()) - }) - } -} diff --git a/pkg/iac/scan/flat.go b/pkg/iac/scan/flat.go deleted file mode 100755 index 7f55a5f36398..000000000000 --- a/pkg/iac/scan/flat.go +++ /dev/null @@ -1,74 +0,0 @@ -package scan - -import ( - "github.com/aquasecurity/trivy/pkg/iac/providers" - "github.com/aquasecurity/trivy/pkg/iac/severity" -) - -type FlatResult struct { - Deprecated bool `json:"deprecated,omitempty"` - RuleID string `json:"rule_id"` - LongID string `json:"long_id"` - RuleSummary string `json:"rule_description"` - RuleProvider providers.Provider `json:"rule_provider"` - RuleService string `json:"rule_service"` - Impact string `json:"impact"` - Resolution string `json:"resolution"` - Links []string `json:"links"` - Description string `json:"description"` - RangeAnnotation string `json:"-"` - Severity severity.Severity `json:"severity"` - Status Status `json:"status"` - Resource string `json:"resource"` - Occurrences []Occurrence `json:"occurrences,omitempty"` - Location FlatRange `json:"location"` - RenderedCause RenderedCause `json:"rendered_cause"` -} - -type FlatRange struct { - Filename string `json:"filename"` - StartLine int `json:"start_line"` - EndLine int `json:"end_line"` -} - -func (r Results) Flatten() []FlatResult { - var results []FlatResult - for _, original := range r { - results = append(results, original.Flatten()) - } - return results -} - -func (r *Result) Flatten() FlatResult { - rng := r.metadata.Range() - - resMetadata := r.metadata - - for resMetadata.Parent() != nil { - resMetadata = *resMetadata.Parent() - } - - return FlatResult{ - Deprecated: r.rule.Deprecated, - RuleID: r.rule.AVDID, - LongID: r.Rule().LongID(), - RuleSummary: r.rule.Summary, - RuleProvider: r.rule.Provider, - RuleService: r.rule.Service, - Impact: r.rule.Impact, - Resolution: r.rule.Resolution, - Links: r.rule.Links, - Description: r.Description(), - RangeAnnotation: r.Annotation(), - Severity: r.rule.Severity, - Status: r.status, - Resource: resMetadata.Reference(), - Occurrences: r.Occurrences(), - Location: FlatRange{ - Filename: rng.GetFilename(), - StartLine: rng.GetStartLine(), - EndLine: rng.GetEndLine(), - }, - RenderedCause: r.renderedCause, - } -} diff --git a/pkg/iac/scan/highlighting.go b/pkg/iac/scan/highlighting.go deleted file mode 100644 index dcc685bb005c..000000000000 --- a/pkg/iac/scan/highlighting.go +++ /dev/null @@ -1,129 +0,0 @@ -package scan - -import ( - "bytes" - "fmt" - "strings" - "sync" - - "github.com/alecthomas/chroma" - "github.com/alecthomas/chroma/formatters" - "github.com/alecthomas/chroma/lexers" - "github.com/alecthomas/chroma/styles" -) - -type cache struct { - sync.RWMutex - data map[string][]string -} - -func (c *cache) Get(key string) ([]string, bool) { - c.RLock() - defer c.RUnlock() - data, ok := c.data[key] - return data, ok -} - -func (c *cache) Set(key string, data []string) { - c.Lock() - defer c.Unlock() - c.data[key] = data -} - -var globalCache = &cache{ - data: make(map[string][]string), -} - -func highlight(fsKey, filename string, startLine, endLine int, input, theme string) []string { - - key := fmt.Sprintf("%s|%s|%d-%d", fsKey, filename, startLine, endLine) - if lines, ok := globalCache.Get(key); ok { - return lines - } - - highlighted, ok := Highlight(filename, input, theme) - if !ok { - return nil - } - - lines := strings.Split(highlighted, "\n") - globalCache.Set(key, lines) - return lines -} - -func Highlight(filename, input, theme string) (string, bool) { - style := styles.Get(theme) - if style == nil { - style = styles.Fallback - } - formatter := formatters.Get("terminal256") - if formatter == nil { - formatter = formatters.Fallback - } - - lexer := lexers.Match(filename) - if lexer == nil { - lexer = lexers.Fallback - } - lexer = chroma.Coalesce(lexer) - - iterator, err := lexer.Tokenise(nil, input) - if err != nil { - return "", false - } - - var buffer bytes.Buffer - if err := formatter.Format(&buffer, style, iterator); err != nil { - return "", false - } - - return string(shiftANSIOverLineEndings(buffer.Bytes())), true -} - -func shiftANSIOverLineEndings(input []byte) []byte { - var output []byte - prev := byte(0) - inCSI := false - csiShouldCarry := false - var csi []byte - var skipOutput bool - for _, r := range input { - skipOutput = false - if !inCSI { - switch { - case r == '\n': - if csiShouldCarry && len(csi) > 0 { - skipOutput = true - output = append(output, '\n') - output = append(output, csi...) - csi = nil - csiShouldCarry = false - } - case r == '[' && prev == 0x1b: - inCSI = true - csi = append(csi, 0x1b, '[') - output = output[:len(output)-1] - skipOutput = true - default: - csiShouldCarry = false - if len(csi) > 0 { - output = append(output, csi...) - csi = nil - } - } - } else { - csi = append(csi, r) - skipOutput = true - if r >= 0x40 && r <= 0x7E { - csiShouldCarry = true - inCSI = false - } - } - if !skipOutput { - output = append(output, r) - } - prev = r - } - - return append(output, csi...) -} diff --git a/pkg/iac/scan/result.go b/pkg/iac/scan/result.go deleted file mode 100644 index 69d4abdd894b..000000000000 --- a/pkg/iac/scan/result.go +++ /dev/null @@ -1,386 +0,0 @@ -package scan - -import ( - "fmt" - "io/fs" - "path/filepath" - "reflect" - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/iac/severity" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Status uint8 - -const ( - StatusFailed Status = iota - StatusPassed - StatusIgnored -) - -type Result struct { - rule Rule - description string - annotation string - status Status - metadata iacTypes.Metadata - severityOverride *severity.Severity - regoNamespace string - regoRule string - traces []string - fsPath string - renderedCause RenderedCause -} - -func (r Result) RegoNamespace() string { - return r.regoNamespace -} - -func (r Result) RegoRule() string { - return r.regoRule -} - -func (r Result) Severity() severity.Severity { - if r.severityOverride != nil { - return *r.severityOverride - } - return r.Rule().Severity -} - -func (r *Result) OverrideSeverity(s severity.Severity) { - r.severityOverride = &s -} - -func (r *Result) OverrideDescription(description string) { - r.description = description -} - -func (r *Result) OverrideMetadata(metadata iacTypes.Metadata) { - r.metadata = metadata -} - -func (r *Result) OverrideStatus(status Status) { - r.status = status -} - -func (r *Result) OverrideAnnotation(annotation string) { - r.annotation = annotation -} - -func (r *Result) SetRule(ru Rule) { - r.rule = ru -} - -func (r Result) Status() Status { - return r.status -} - -func (r Result) Rule() Rule { - return r.rule -} - -func (r Result) Description() string { - return r.description -} - -func (r Result) Annotation() string { - return r.annotation -} - -func (r Result) Metadata() iacTypes.Metadata { - return r.metadata -} - -func (r Result) Range() iacTypes.Range { - return r.metadata.Range() -} - -func (r Result) Traces() []string { - return r.traces -} - -type RenderedCause struct { - Raw string -} - -func (r *Result) WithRenderedCause(cause RenderedCause) { - r.renderedCause = cause -} - -func (r *Result) AbsolutePath(fsRoot string, metadata iacTypes.Metadata) string { - if strings.HasSuffix(fsRoot, ":") { - fsRoot += "/" - } - - if metadata.IsUnmanaged() { - return "" - } - rng := metadata.Range() - if rng.GetSourcePrefix() != "" && !strings.HasPrefix(rng.GetSourcePrefix(), ".") { - return rng.GetFilename() - } - return filepath.Join(fsRoot, rng.GetLocalFilename()) -} - -func (r *Result) RelativePathTo(fsRoot, to string, metadata iacTypes.Metadata) string { - - absolute := r.AbsolutePath(fsRoot, metadata) - - if strings.HasSuffix(fsRoot, ":") { - fsRoot += "/" - } - - if metadata.IsUnmanaged() { - return absolute - } - rng := metadata.Range() - if rng.GetSourcePrefix() != "" && !strings.HasPrefix(rng.GetSourcePrefix(), ".") { - return absolute - } - if !strings.HasPrefix(rng.GetLocalFilename(), strings.TrimSuffix(fsRoot, "/")) { - return absolute - } - relative, err := filepath.Rel(to, rng.GetLocalFilename()) - if err != nil { - return absolute - } - return relative -} - -type Results []Result - -type MetadataProvider interface { - GetMetadata() iacTypes.Metadata - GetRawValue() any -} - -func (r *Results) GetPassed() Results { - return r.filterStatus(StatusPassed) -} - -func (r *Results) GetIgnored() Results { - return r.filterStatus(StatusIgnored) -} - -func (r *Results) GetFailed() Results { - return r.filterStatus(StatusFailed) -} - -func (r *Results) filterStatus(status Status) Results { - var filtered Results - if r == nil { - return filtered - } - for _, res := range *r { - if res.Status() == status { - filtered = append(filtered, res) - } - } - return filtered -} - -func (r *Results) Add(description string, source any) { - result := Result{ - description: description, - } - result.metadata = getMetadataFromSource(source) - if result.metadata.IsExplicit() { - result.annotation = getAnnotation(source) - } - rnge := result.metadata.Range() - result.fsPath = rnge.GetLocalFilename() - *r = append(*r, result) -} - -func (r *Results) AddRego(description, namespace, rule string, traces []string, source MetadataProvider) { - result := Result{ - description: description, - regoNamespace: namespace, - regoRule: rule, - traces: traces, - } - result.metadata = getMetadataFromSource(source) - if result.metadata.IsExplicit() { - result.annotation = getAnnotation(source) - } - rnge := result.metadata.Range() - result.fsPath = rnge.GetLocalFilename() - *r = append(*r, result) -} - -func (r *Results) AddPassed(source any, descriptions ...string) { - res := Result{ - description: strings.Join(descriptions, " "), - status: StatusPassed, - } - res.metadata = getMetadataFromSource(source) - rnge := res.metadata.Range() - res.fsPath = rnge.GetLocalFilename() - *r = append(*r, res) -} - -func getMetadataFromSource(source any) iacTypes.Metadata { - if provider, ok := source.(MetadataProvider); ok { - return provider.GetMetadata() - } - - metaValue := reflect.ValueOf(source) - if metaValue.Kind() == reflect.Ptr { - metaValue = metaValue.Elem() - } - metaVal := metaValue.FieldByName("Metadata") - return metaVal.Interface().(iacTypes.Metadata) -} - -func getAnnotation(source any) string { - if provider, ok := source.(MetadataProvider); ok { - return rawToString(provider.GetRawValue()) - } - return "" -} - -func (r *Results) AddPassedRego(namespace, rule string, traces []string, source any) { - res := Result{ - status: StatusPassed, - regoNamespace: namespace, - regoRule: rule, - traces: traces, - } - res.metadata = getMetadataFromSource(source) - rnge := res.metadata.Range() - res.fsPath = rnge.GetLocalFilename() - *r = append(*r, res) -} - -func (r *Results) AddIgnored(source any, descriptions ...string) { - res := Result{ - description: strings.Join(descriptions, " "), - status: StatusIgnored, - } - res.metadata = getMetadataFromSource(source) - rnge := res.metadata.Range() - res.fsPath = rnge.GetLocalFilename() - *r = append(*r, res) -} - -func (r *Results) Ignore(ignoreRules ignore.Rules, ignores map[string]ignore.Ignorer) { - for i, result := range *r { - allIDs := []string{ - result.Rule().LongID(), - result.Rule().AVDID, - strings.ToLower(result.Rule().AVDID), - result.Rule().ShortCode, - } - allIDs = append(allIDs, result.Rule().Aliases...) - - if ignoreRules.Ignore(result.Metadata(), allIDs, ignores) { - (*r)[i].OverrideStatus(StatusIgnored) - } - } -} - -func (r *Results) SetRule(rule Rule) { - for i := range *r { - (*r)[i].rule = rule - } -} - -func (r *Results) SetSourceAndFilesystem(source string, f fs.FS, logicalSource bool) { - for i := range *r { - m := (*r)[i].Metadata() - if m.IsUnmanaged() { - continue - } - rng := m.Range() - - newrng := iacTypes.NewRange(rng.GetLocalFilename(), rng.GetStartLine(), rng.GetEndLine(), source, f) - if logicalSource { - newrng = iacTypes.NewRangeWithLogicalSource(rng.GetLocalFilename(), rng.GetStartLine(), rng.GetEndLine(), - source, f) - } - parent := m.Parent() - switch { - case m.IsExplicit(): - m = iacTypes.NewExplicitMetadata(newrng, m.Reference()) - default: - m = iacTypes.NewMetadata(newrng, m.Reference()) - } - if parent != nil { - m.SetParentPtr(parent) - } - (*r)[i].OverrideMetadata(m) - } -} - -func rawToString(raw any) string { - if raw == nil { - return "" - } - switch t := raw.(type) { - case int: - return strconv.Itoa(t) - case bool: - return strconv.FormatBool(t) - case float64: - return fmt.Sprintf("%f", t) - case string: - return fmt.Sprintf("%q", t) - case []string: - var items []string - for _, item := range t { - items = append(items, rawToString(item)) - } - return fmt.Sprintf("[%s]", strings.Join(items, ", ")) - case []int: - var items []string - for _, item := range t { - items = append(items, rawToString(item)) - } - return fmt.Sprintf("[%s]", strings.Join(items, ", ")) - case []float64: - var items []string - for _, item := range t { - items = append(items, rawToString(item)) - } - return fmt.Sprintf("[%s]", strings.Join(items, ", ")) - case []bool: - var items []string - for _, item := range t { - items = append(items, rawToString(item)) - } - return fmt.Sprintf("[%s]", strings.Join(items, ", ")) - default: - return "?" - } -} - -type Occurrence struct { - Resource string `json:"resource"` - Filename string `json:"filename"` - StartLine int `json:"start_line"` - EndLine int `json:"end_line"` -} - -func (r *Result) Occurrences() []Occurrence { - var occurrences []Occurrence - - mod := &r.metadata - - for { - mod = mod.Parent() - if mod == nil { - break - } - parentRange := mod.Range() - occurrences = append(occurrences, Occurrence{ - Resource: mod.Reference(), - Filename: parentRange.GetFilename(), - StartLine: parentRange.GetStartLine(), - EndLine: parentRange.GetEndLine(), - }) - } - return occurrences -} diff --git a/pkg/iac/scan/result_stub.go b/pkg/iac/scan/result_stub.go new file mode 100644 index 000000000000..ad97b0665015 --- /dev/null +++ b/pkg/iac/scan/result_stub.go @@ -0,0 +1,4 @@ +package scan + +type Result struct { +} diff --git a/pkg/iac/scan/result_test.go b/pkg/iac/scan/result_test.go deleted file mode 100644 index 67da0d61be86..000000000000 --- a/pkg/iac/scan/result_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package scan_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Occurrences(t *testing.T) { - tests := []struct { - name string - factory func() *scan.Result - expected []scan.Occurrence - }{ - { - name: "happy", - factory: func() *scan.Result { - r := scan.Result{} - causeResourceMeta := types.NewMetadata(types.NewRange( - "main.tf", 1, 13, "", nil, - ), "module.aws-security-groups[\"db1\"]") - - parentMeta := types.NewMetadata(types.NewRange( - "terraform-aws-modules/security-group/aws/main.tf", 191, 227, "", nil, - ), "aws_security_group_rule.ingress_with_cidr_blocks[0]").WithParent(causeResourceMeta) - - r.OverrideMetadata(types.NewMetadata(types.NewRange( - "terraform-aws-modules/security-group/aws/main.tf", 197, 204, "", nil, - ), "aws_security_group_rule.ingress_with_cidr_blocks").WithParent(parentMeta)) - return &r - }, - expected: []scan.Occurrence{ - { - Resource: "aws_security_group_rule.ingress_with_cidr_blocks[0]", - Filename: "terraform-aws-modules/security-group/aws/main.tf", - StartLine: 191, - EndLine: 227, - }, - { - Resource: "module.aws-security-groups[\"db1\"]", - Filename: "main.tf", - StartLine: 1, - EndLine: 13, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.expected, tt.factory().Occurrences()) - }) - } -} diff --git a/pkg/iac/scan/rule.go b/pkg/iac/scan/rule.go deleted file mode 100755 index 0ed48d8a6145..000000000000 --- a/pkg/iac/scan/rule.go +++ /dev/null @@ -1,159 +0,0 @@ -package scan - -import ( - "fmt" - "regexp" - "strings" - - "golang.org/x/text/cases" - "golang.org/x/text/language" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/providers" - "github.com/aquasecurity/trivy/pkg/iac/severity" - "github.com/aquasecurity/trivy/pkg/iac/state" - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -type CheckFunc func(s *state.State) (results Results) - -type EngineMetadata struct { - GoodExamples []string `json:"good_examples,omitempty"` - BadExamples []string `json:"bad_examples,omitempty"` - RemediationMarkdown string `json:"remediation_markdown,omitempty"` - Links []string `json:"links,omitempty"` -} - -type CustomChecks struct { - Terraform *TerraformCustomCheck -} - -type TerraformCustomCheck struct { - RequiredTypes []string - RequiredLabels []string - RequiredSources []string - Check func(*terraform.Block, *terraform.Module) Results -} - -type Rule struct { - Deprecated bool `json:"deprecated"` - AVDID string `json:"avd_id"` - Aliases []string `json:"aliases"` - ShortCode string `json:"short_code"` - Summary string `json:"summary"` - Explanation string `json:"explanation"` - Impact string `json:"impact"` - Resolution string `json:"resolution"` - Provider providers.Provider `json:"provider"` - Service string `json:"service"` - Links []string `json:"links"` - Severity severity.Severity `json:"severity"` - Terraform *EngineMetadata `json:"terraform,omitempty"` - CloudFormation *EngineMetadata `json:"cloud_formation,omitempty"` - Examples string `json:"-"` - CustomChecks CustomChecks `json:"-"` - RegoPackage string `json:"-"` - Frameworks map[framework.Framework][]string `json:"frameworks"` - Check CheckFunc `json:"-"` -} - -func (r Rule) IsDeprecated() bool { - return r.Deprecated -} - -func (r Rule) HasID(id string) bool { - if r.AVDID == id || r.LongID() == id { - return true - } - for _, alias := range r.Aliases { - if alias == id { - return true - } - } - return false -} - -func (r Rule) LongID() string { - return strings.ToLower(fmt.Sprintf("%s-%s-%s", r.Provider, r.Service, r.ShortCode)) -} - -func (r Rule) ServiceDisplayName() string { - return nicify(r.Service) -} - -func (r Rule) ShortCodeDisplayName() string { - return nicify(r.ShortCode) -} - -var acronyms = []string{ - "acl", - "alb", - "api", - "arn", - "aws", - "cidr", - "db", - "dns", - "ebs", - "ec2", - "ecr", - "ecs", - "efs", - "eks", - "elb", - "gke", - "http", - "http2", - "https", - "iam", - "im", - "imds", - "ip", - "ips", - "kms", - "lb", - "md5", - "mfa", - "mq", - "msk", - "rbac", - "rdp", - "rds", - "rsa", - "sam", - "sgr", - "sha1", - "sha256", - "sns", - "sql", - "sqs", - "ssh", - "ssm", - "tls", - "ubla", - "vm", - "vpc", - "vtpm", - "waf", -} - -var specials = map[string]string{ - "dynamodb": "DynamoDB", - "documentdb": "DocumentDB", - "mysql": "MySQL", - "postgresql": "PostgreSQL", - "acls": "ACLs", - "ips": "IPs", - "bigquery": "BigQuery", -} - -func nicify(input string) string { - input = strings.ToLower(input) - for replace, with := range specials { - input = regexp.MustCompile(fmt.Sprintf("\\b%s\\b", replace)).ReplaceAllString(input, with) - } - for _, acronym := range acronyms { - input = regexp.MustCompile(fmt.Sprintf("\\b%s\\b", acronym)).ReplaceAllString(input, strings.ToUpper(acronym)) - } - return cases.Title(language.English).String(strings.ReplaceAll(input, "-", " ")) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/bench_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/bench_test.go deleted file mode 100644 index 1437a9b83bb3..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/bench_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package armjson - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func BenchmarkUnmarshal_JFather(b *testing.B) { - target := make(map[string]any) - input := []byte(`{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -}`) - - for n := 0; n < b.N; n++ { - metadata := types.NewTestMetadata() - require.NoError(b, Unmarshal(input, &target, &metadata)) - } -} - -func BenchmarkUnmarshal_Traditional(b *testing.B) { - target := make(map[string]any) - input := []byte(`{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -}`) - - for n := 0; n < b.N; n++ { - require.NoError(b, json.Unmarshal(input, &target)) - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode.go deleted file mode 100644 index dcce24f3a5d9..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode.go +++ /dev/null @@ -1,67 +0,0 @@ -package armjson - -import ( - "errors" - "fmt" - "reflect" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (n *node) Decode(target any) error { - v := reflect.ValueOf(target) - return n.decodeToValue(v) -} - -func (n *node) Metadata() types.Metadata { - return *n.metadata -} - -var unmarshaller = reflect.TypeOf((*Unmarshaller)(nil)).Elem() -var receiver = reflect.TypeOf((*MetadataReceiver)(nil)).Elem() - -func (n *node) decodeToValue(v reflect.Value) error { - - if v.Type().Implements(receiver) { - rec := v - defer func() { - rec.MethodByName("SetMetadata").Call([]reflect.Value{reflect.ValueOf(n.metadata)}) - }() - } - if v.Type().Implements(unmarshaller) { - returns := v.MethodByName("UnmarshalJSONWithMetadata").Call([]reflect.Value{reflect.ValueOf(n)}) - if err := returns[0].Interface(); err != nil { - return err.(error) - } - return nil - } - - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - - if !v.CanSet() { - return errors.New("target is not settable") - } - - switch n.kind { - case KindObject: - return n.decodeObject(v) - case KindArray: - return n.decodeArray(v) - case KindString: - return n.decodeString(v) - case KindNumber: - return n.decodeNumber(v) - case KindBoolean: - return n.decodeBoolean(v) - case KindNull: - return n.decodeNull(v) - case KindComment: - return n.decodeString(v) - case KindUnknown: - return errors.New("cannot decode unknown kind") - default: - return fmt.Errorf("decoding of kind 0x%x is not supported", n.kind) - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_array.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_array.go deleted file mode 100644 index 457b78b5f0d7..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_array.go +++ /dev/null @@ -1,51 +0,0 @@ -package armjson - -import ( - "errors" - "reflect" -) - -func (n *node) decodeArray(v reflect.Value) error { - - length := len(n.content) - - var original reflect.Value - - switch v.Kind() { - case reflect.Array: - if v.Len() != length { - return errors.New("invalid length") - } - case reflect.Slice: - v.Set(reflect.MakeSlice(v.Type(), length, length)) - case reflect.Interface: - original = v - slice := reflect.ValueOf(make([]any, length)) - v = reflect.New(slice.Type()).Elem() - v.Set(slice) - default: - return errors.New("invalid target type") - } - - elementType := v.Type().Elem() - for i, nodeElement := range n.content { - node := nodeElement.(*node) - targetElement := reflect.New(elementType).Elem() - addressable := targetElement - if targetElement.Kind() == reflect.Ptr { - targetElement.Set(reflect.New(elementType.Elem())) - } else { - addressable = targetElement.Addr() - } - if err := node.decodeToValue(addressable); err != nil { - return err - } - v.Index(i).Set(targetElement) - } - - if original.IsValid() { - original.Set(v) - } - - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_boolean.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_boolean.go deleted file mode 100644 index dbdef3a3253d..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_boolean.go +++ /dev/null @@ -1,18 +0,0 @@ -package armjson - -import ( - "fmt" - "reflect" -) - -func (n *node) decodeBoolean(v reflect.Value) error { - switch v.Kind() { - case reflect.Bool: - v.SetBool(n.raw.(bool)) - case reflect.Interface: - v.Set(reflect.ValueOf(n.raw)) - default: - return fmt.Errorf("cannot decode boolean value to %s target", v.Kind()) - } - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_meta_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_meta_test.go deleted file mode 100644 index 29c1d4c5aa59..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_meta_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type TestParent struct { - Child *TestChild `json:"child"` -} - -type TestChild struct { - Name string - Line int - Column int -} - -func (t *TestChild) UnmarshalJSONWithMetadata(node Node) error { - t.Line = node.Range().Start.Line - t.Column = node.Range().Start.Column - return node.Decode(&t.Name) -} - -func Test_DecodeWithMetadata(t *testing.T) { - example := []byte(` -{ - "child": "secret" -} -`) - var parent TestParent - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &parent, &metadata)) - assert.Equal(t, 3, parent.Child.Line) - assert.Equal(t, 12, parent.Child.Column) - assert.Equal(t, "secret", parent.Child.Name) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_null.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_null.go deleted file mode 100644 index 2cc86b3c1bb7..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_null.go +++ /dev/null @@ -1,10 +0,0 @@ -package armjson - -import ( - "reflect" -) - -func (n *node) decodeNull(v reflect.Value) error { - v.Set(reflect.Zero(v.Type())) - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_number.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_number.go deleted file mode 100644 index 4b477e89ca70..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_number.go +++ /dev/null @@ -1,47 +0,0 @@ -package armjson - -import ( - "errors" - "fmt" - "reflect" -) - -func (n *node) decodeNumber(v reflect.Value) error { - - switch v.Kind() { - case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: - if i64, ok := n.raw.(int64); ok { - v.SetInt(i64) - return nil - } - if f64, ok := n.raw.(float64); ok { - v.SetInt(int64(f64)) - return nil - } - case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: - if i64, ok := n.raw.(int64); ok { - v.SetUint(uint64(i64)) - return nil - } - if f64, ok := n.raw.(float64); ok { - v.SetUint(uint64(f64)) - return nil - } - case reflect.Float32, reflect.Float64: - if i64, ok := n.raw.(int64); ok { - v.SetFloat(float64(i64)) - return nil - } - if f64, ok := n.raw.(float64); ok { - v.SetFloat(f64) - return nil - } - case reflect.Interface: - v.Set(reflect.ValueOf(n.raw)) - return nil - default: - return fmt.Errorf("cannot decode number value to %s target", v.Kind()) - } - - return errors.New("internal value is not numeric") -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_object.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_object.go deleted file mode 100644 index 5d7e15357007..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_object.go +++ /dev/null @@ -1,123 +0,0 @@ -package armjson - -import ( - "errors" - "fmt" - "reflect" - "strings" -) - -func (n *node) decodeObject(v reflect.Value) error { - switch v.Kind() { - case reflect.Struct: - return n.decodeObjectToStruct(v) - case reflect.Map: - return n.decodeObjectToMap(v) - case reflect.Interface: - target := reflect.New(reflect.TypeOf(make(map[string]any, len(n.Content())))).Elem() - if err := n.decodeObjectToMap(target); err != nil { - return err - } - v.Set(target) - return nil - default: - return fmt.Errorf("cannot set object value to target of type %s", v.Kind()) - } -} - -func (n *node) decodeObjectToMap(v reflect.Value) error { - properties, err := n.objectAsMap() - if err != nil { - return err - } - - newMap := reflect.MakeMap(v.Type()) - valueType := v.Type().Elem() - - for key, value := range properties { - target := reflect.New(valueType).Elem() - addressable := target - if target.Kind() == reflect.Ptr { - target.Set(reflect.New(valueType.Elem())) - } else { - addressable = target.Addr() - } - if err := value.(*node).decodeToValue(addressable); err != nil { - return err - } - newMap.SetMapIndex(reflect.ValueOf(key), target) - } - - v.Set(newMap) - return nil - -} - -func (n *node) objectAsMap() (map[string]Node, error) { - if n.kind != KindObject { - return nil, errors.New("not an object") - } - properties := make(map[string]Node) - contents := n.content - for i := 0; i < len(contents); i += 2 { - key := contents[i] - if key.Kind() != KindString { - return nil, errors.New("invalid object key - please report this bug") - } - keyStr := key.(*node).raw.(string) - - if i+1 >= len(contents) { - return nil, errors.New("missing object value - please report this bug") - } - properties[keyStr] = contents[i+1] - } - return properties, nil -} - -func (n *node) decodeObjectToStruct(v reflect.Value) error { - - temp := reflect.New(v.Type()).Elem() - v.Set(temp) - - properties, err := n.objectAsMap() - if err != nil { - return err - } - - t := v.Type() - for i := 0; i < t.NumField(); i++ { - fv := t.Field(i) - tags := strings.Split(fv.Tag.Get("json"), ",") - var tagName string - for _, tag := range tags { - if tag != "omitempty" && tag != "-" { - tagName = tag - } - } - if tagName == "" { - tagName = fv.Name - } - - value, ok := properties[tagName] - if !ok { - // TODO: should we zero this value? - continue - } - - subject := v.Field(i) - - // if fields are nil pointers, initialize them with values of the correct type - if subject.Kind() == reflect.Ptr { - if subject.IsNil() { - subject.Set(reflect.New(subject.Type().Elem())) - } - } else { - subject = subject.Addr() - } - - if err := value.(*node).decodeToValue(subject); err != nil { - return err - } - } - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/decode_string.go b/pkg/iac/scanners/azure/arm/parser/armjson/decode_string.go deleted file mode 100644 index c8f734b57024..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/decode_string.go +++ /dev/null @@ -1,19 +0,0 @@ -package armjson - -import ( - "fmt" - "reflect" -) - -func (n *node) decodeString(v reflect.Value) error { - - switch v.Kind() { - case reflect.String: - v.SetString(n.raw.(string)) - case reflect.Interface: - v.Set(reflect.ValueOf(n.raw)) - default: - return fmt.Errorf("cannot decode string value to non-string target: %s", v.Kind()) - } - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/kind.go b/pkg/iac/scanners/azure/arm/parser/armjson/kind.go deleted file mode 100644 index 82712cc89225..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/kind.go +++ /dev/null @@ -1,14 +0,0 @@ -package armjson - -type Kind uint8 - -const ( - KindUnknown Kind = iota - KindNull - KindNumber - KindString - KindBoolean - KindArray - KindObject - KindComment -) diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/node.go b/pkg/iac/scanners/azure/arm/parser/armjson/node.go deleted file mode 100644 index 28322a0c264d..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/node.go +++ /dev/null @@ -1,59 +0,0 @@ -package armjson - -import "github.com/aquasecurity/trivy/pkg/iac/types" - -type Node interface { - Comments() []Node - Range() Range - Decode(target any) error - Kind() Kind - Content() []Node - Metadata() types.Metadata -} - -type Range struct { - Start Position - End Position -} - -type Position struct { - Line int - Column int -} - -type node struct { - raw any - start Position - end Position - kind Kind - content []Node - comments []Node - metadata *types.Metadata - ref string -} - -func (n *node) Range() Range { - return Range{ - Start: n.start, - End: Position{ - Column: n.end.Column - 1, - Line: n.end.Line, - }, - } -} - -func (n *node) Comments() []Node { - return n.comments -} - -func (n *node) End() Position { - return n.end -} - -func (n *node) Kind() Kind { - return n.kind -} - -func (n *node) Content() []Node { - return n.content -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse.go deleted file mode 100644 index 6eaea9c57390..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse.go +++ /dev/null @@ -1,150 +0,0 @@ -package armjson - -import ( - "fmt" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type parser struct { - position Position - size int - peeker *PeekReader -} - -func newParser(p *PeekReader, pos Position) *parser { - return &parser{ - position: pos, - peeker: p, - } -} - -func (p *parser) parse(rootMetadata *types.Metadata) (Node, error) { - root, err := p.parseElement(rootMetadata) - if err != nil { - return nil, err - } - root.(*node).updateMetadata("") - return root, nil -} - -func (p *parser) parseElement(parentMetadata *types.Metadata) (Node, error) { - if err := p.parseWhitespace(); err != nil { - return nil, err - } - n, err := p.parseValue(parentMetadata) - if err != nil { - return nil, err - } - if err := p.parseWhitespace(); err != nil { - return nil, err - } - return n, nil -} - -func (p *parser) parseValue(parentMetadata *types.Metadata) (Node, error) { - c, err := p.peeker.Peek() - if err != nil { - return nil, err - } - - switch c { - case '/': - return p.parseComment(parentMetadata) - case '"': - return p.parseString(parentMetadata) - case '{': - return p.parseObject(parentMetadata) - case '[': - return p.parseArray(parentMetadata) - case 'n': - return p.parseNull(parentMetadata) - case 't', 'f': - return p.parseBoolean(parentMetadata) - default: - if c == '-' || (c >= '0' && c <= '9') { - return p.parseNumber(parentMetadata) - } - return nil, fmt.Errorf("unexpected character '%c'", c) - } -} - -func (p *parser) next() (rune, error) { - b, err := p.peeker.Next() - if err != nil { - return 0, err - } - p.position.Column++ - p.size++ - return b, nil -} - -func (p *parser) undo() error { - if err := p.peeker.Undo(); err != nil { - return err - } - p.position.Column-- - p.size-- - return nil -} - -func (p *parser) makeError(format string, args ...any) error { - return fmt.Errorf( - "error at line %d, column %d: %s", - p.position.Line, - p.position.Column, - fmt.Sprintf(format, args...), - ) -} - -func (p *parser) newNode(k Kind, parentMetadata *types.Metadata) (*node, *types.Metadata) { - n := &node{ - start: p.position, - kind: k, - } - metadata := types.NewMetadata( - types.NewRange(parentMetadata.Range().GetFilename(), n.start.Line, n.end.Line, "", parentMetadata.Range().GetFS()), - n.ref, - ) - metadata.SetParentPtr(parentMetadata) - n.metadata = &metadata - return n, n.metadata -} - -func (n *node) updateMetadata(prefix string) { - - var full string - // nolint:gocritic - if strings.HasPrefix(n.ref, "[") { - full = prefix + n.ref - } else if prefix != "" { - full = prefix + "." + n.ref - } else { - full = n.ref - } - - n.metadata.SetRange(types.NewRange(n.metadata.Range().GetFilename(), - n.start.Line, - n.end.Line, - "", - n.metadata.Range().GetFS())) - - n.metadata.SetReference(full) - - for i := range n.content { - n.content[i].(*node).updateMetadata(full) - } -} - -func (p *parser) swallowIfEqual(r rune) bool { - c, err := p.peeker.Peek() - if err != nil { - return false - } - if c != r { - return false - } - _, _ = p.next() - return true -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_array.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_array.go deleted file mode 100644 index 8d3795ee792a..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_array.go +++ /dev/null @@ -1,54 +0,0 @@ -package armjson - -import ( - "fmt" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (p *parser) parseArray(parentMetadata *types.Metadata) (Node, error) { - n, metadata := p.newNode(KindArray, parentMetadata) - - c, err := p.next() - if err != nil { - return nil, err - } - - if c != '[' { - return nil, p.makeError("expecting object delimiter") - } - if err := p.parseWhitespace(); err != nil { - return nil, err - } - // we've hit the end of the object - if p.swallowIfEqual(']') { - n.end = p.position - return n, nil - } - - // for each element - for { - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - - val, err := p.parseElement(metadata) - if err != nil { - return nil, err - } - val.(*node).ref = fmt.Sprintf("[%d]", len(n.content)) - - n.content = append(n.content, val) - - // we've hit the end of the array - if p.swallowIfEqual(']') { - n.end = p.position - return n, nil - } - - if !p.swallowIfEqual(',') { - return nil, p.makeError("unexpected character - expecting , or ]") - } - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_array_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_array_test.go deleted file mode 100644 index 0d373c6da241..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_array_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Array_Empty(t *testing.T) { - example := []byte(`[]`) - var target []int - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &target, &metadata)) - assert.Empty(t, target) -} - -func Test_Array_ToSlice(t *testing.T) { - example := []byte(`[1, 2, 3]`) - var target []int - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &target, &metadata)) - assert.Len(t, target, 3) - assert.EqualValues(t, []int{1, 2, 3}, target) -} - -func Test_Array_ToArray(t *testing.T) { - example := []byte(`[3, 2, 1]`) - target := [3]int{6, 6, 6} - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &target, &metadata)) - assert.Len(t, target, 3) - assert.EqualValues(t, [3]int{3, 2, 1}, target) -} - -func Test_Array_ToInterface(t *testing.T) { - example := []byte(`{ "List": [1, 2, 3] }`) - target := struct { - List any - }{} - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &target, &metadata)) - assert.Len(t, target.List, 3) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_boolean.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_boolean.go deleted file mode 100644 index 59a5fffb114b..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_boolean.go +++ /dev/null @@ -1,40 +0,0 @@ -package armjson - -import ( - "errors" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -var trueRunes = []rune("true") -var falseRunes = []rune("false") - -func (p *parser) parseBoolean(parentMetadata *types.Metadata) (Node, error) { - - n, _ := p.newNode(KindBoolean, parentMetadata) - - r, err := p.peeker.Peek() - if err != nil { - return nil, err - } - - if r == 't' { - for _, expected := range trueRunes { - if !p.swallowIfEqual(expected) { - return nil, errors.New("unexpected character in boolean value") - } - } - n.raw = true - n.end = p.position - return n, err - } - - for _, expected := range falseRunes { - if !p.swallowIfEqual(expected) { - return nil, errors.New("unexpected character in boolean value") - } - } - n.raw = false - n.end = p.position - return n, nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_boolean_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_boolean_test.go deleted file mode 100644 index 2e9fec7fcae6..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_boolean_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Boolean_True(t *testing.T) { - example := []byte(`true`) - var output bool - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.True(t, output) -} - -func Test_Boolean_False(t *testing.T) { - example := []byte(`false`) - var output bool - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.False(t, output) -} - -func Test_Boolean_ToNonBoolPointer(t *testing.T) { - example := []byte(`false`) - var output string - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.Error(t, err) -} - -func Test_Bool_ToUninitialisedPointer(t *testing.T) { - example := []byte(`true`) - var str *string - metadata := types.NewTestMetadata() - err := Unmarshal(example, str, &metadata) - require.Error(t, err) - assert.Nil(t, str) -} - -func Test_Bool_ToInterface(t *testing.T) { - example := []byte(`true`) - var output any - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.True(t, output.(bool)) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_comment.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_comment.go deleted file mode 100644 index 3408d330c2b3..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_comment.go +++ /dev/null @@ -1,98 +0,0 @@ -package armjson - -import ( - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (p *parser) parseComment(parentMetadata *types.Metadata) (Node, error) { - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - - _, err := p.next() - if err != nil { - return nil, err - } - - b, err := p.next() - if err != nil { - return nil, err - } - - switch b { - case '/': - return p.parseLineComment(parentMetadata) - case '*': - return p.parseBlockComment(parentMetadata) - default: - return nil, p.makeError("expecting comment delimiter") - } -} - -func (p *parser) parseLineComment(parentMetadata *types.Metadata) (Node, error) { - - n, _ := p.newNode(KindComment, parentMetadata) - - var sb strings.Builder - for { - c, err := p.next() - if err != nil { - return nil, err - } - if c == '\n' { - p.position.Column = 1 - p.position.Line++ - break - } - sb.WriteRune(c) - } - - n.raw = sb.String() - n.end = p.position - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - return n, nil -} - -func (p *parser) parseBlockComment(parentMetadata *types.Metadata) (Node, error) { - - n, _ := p.newNode(KindComment, parentMetadata) - - var sb strings.Builder - - for { - c, err := p.next() - if err != nil { - return nil, err - } - if c == '*' { - c, err := p.peeker.Peek() - if err != nil { - return nil, err - } - if c == '/' { - break - } - sb.WriteRune('*') - } else { - if c == '\n' { - p.position.Column = 1 - p.position.Line++ - } - sb.WriteRune(c) - } - } - - n.raw = sb.String() - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - - return n, nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_complex_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_complex_test.go deleted file mode 100644 index 3cc16f94046c..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_complex_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Complex(t *testing.T) { - target := make(map[string]any) - input := `{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -}` - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal([]byte(input), &target, &metadata)) -} - -type Resource struct { - Line int - inner resourceInner -} - -type resourceInner struct { - Type string `json:"Type" yaml:"Type"` - Properties map[string]*Property `json:"Properties" yaml:"Properties"` -} - -func (r *Resource) UnmarshalJSONWithMetadata(node Node) error { - r.Line = node.Range().Start.Line - return node.Decode(&r.inner) -} - -type Parameter struct { - inner parameterInner -} - -type parameterInner struct { - Type string `json:"Type" yaml:"Type"` - Default any `yaml:"Default"` -} - -func (p *Parameter) UnmarshalJSONWithMetadata(node Node) error { - return node.Decode(&p.inner) -} - -type Property struct { - Line int - inner propertyInner -} - -type CFType string - -type propertyInner struct { - Type CFType - Value any `json:"Value" yaml:"Value"` -} - -func (p *Property) UnmarshalJSONWithMetadata(node Node) error { - p.Line = node.Range().Start.Line - return node.Decode(&p.inner) -} - -type Temp struct { - BucketName *Parameter - BucketKeyEnabled *Parameter -} - -type FileContext struct { - Parameters map[string]*Parameter `json:"Parameters" yaml:"Parameters"` - Resources map[string]*Resource `json:"Resources" yaml:"Resources"` -} - -func Test_CloudFormation(t *testing.T) { - var target FileContext - input := ` -{ - "Parameters": { - "BucketName": { - "Type": "String", - "Default": "naughty" - }, - "BucketKeyEnabled": { - "Type": "Boolean", - "Default": false - } - }, - "Resources": { - "S3Bucket": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketName": { - "Ref": "BucketName" - }, - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "BucketKeyEnabled": { - "Ref": "BucketKeyEnabled" - } - } - ] - } - } - } - } -} -` - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal([]byte(input), &target, &metadata)) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_null.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_null.go deleted file mode 100644 index 0ae20a8c9edf..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_null.go +++ /dev/null @@ -1,23 +0,0 @@ -package armjson - -import ( - "errors" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -var nullRunes = []rune("null") - -func (p *parser) parseNull(parentMetadata *types.Metadata) (Node, error) { - - n, _ := p.newNode(KindNull, parentMetadata) - - for _, expected := range nullRunes { - if !p.swallowIfEqual(expected) { - return nil, errors.New("unexpected character") - } - } - n.raw = nil - n.end = p.position - return n, nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_null_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_null_test.go deleted file mode 100644 index 3455809b9874..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_null_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Null(t *testing.T) { - example := []byte(`null`) - var output string - ref := &output - metadata := types.NewTestMetadata() - err := Unmarshal(example, &ref, &metadata) - require.NoError(t, err) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_number.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_number.go deleted file mode 100644 index 586fbd3a4841..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_number.go +++ /dev/null @@ -1,163 +0,0 @@ -package armjson - -import ( - "fmt" - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (p *parser) parseNumber(parentMetadata *types.Metadata) (Node, error) { - - n, _ := p.newNode(KindNumber, parentMetadata) - - var str string - - if p.swallowIfEqual('-') { - str = "-" - } - - integral, err := p.parseIntegral() - if err != nil { - return nil, err - } - fraction, err := p.parseFraction() - if err != nil { - return nil, err - } - exponent, err := p.parseExponent() - if err != nil { - return nil, err - } - - str = fmt.Sprintf("%s%s%s%s", str, integral, fraction, exponent) - n.end = p.position - - if fraction != "" || exponent != "" { - f, err := strconv.ParseFloat(str, 64) - if err != nil { - return nil, p.makeError("%s", err) - } - n.raw = f - return n, nil - } - - i, err := strconv.ParseInt(str, 10, 64) - if err != nil { - return nil, p.makeError("%s", err) - } - n.raw = i - - return n, nil -} - -func (p *parser) parseIntegral() (string, error) { - r, err := p.next() - if err != nil { - return "", err - } - if r == '0' { - r, _ := p.peeker.Peek() - if r >= '0' && r <= '9' { - return "", p.makeError("invalid number") - } - return "0", nil - } - - var sb strings.Builder - if r < '1' || r > '9' { - return "", p.makeError("invalid number") - } - sb.WriteRune(r) - - for { - r, err := p.next() - if err != nil { - return sb.String(), nil - } - if r < '0' || r > '9' { - return sb.String(), p.undo() - } - sb.WriteRune(r) - } -} - -func (p *parser) parseFraction() (string, error) { - r, err := p.next() - if err != nil { - return "", nil - } - if r != '.' { - return "", p.undo() - } - - var sb strings.Builder - sb.WriteRune('.') - - for { - r, err := p.next() - if err != nil { - break - } - if r < '0' || r > '9' { - if err := p.undo(); err != nil { - return "", err - } - break - } - sb.WriteRune(r) - } - - str := sb.String() - if str == "." { - return "", p.makeError("invalid number - missing digits after decimal point") - } - - return str, nil -} - -func (p *parser) parseExponent() (string, error) { - r, err := p.next() - if err != nil { - return "", nil - } - if r != 'e' && r != 'E' { - return "", p.undo() - } - - var sb strings.Builder - sb.WriteRune(r) - - r, err = p.next() - if err != nil { - return "", nil - } - hasDigits := r >= '0' && r <= '9' - if r != '-' && r != '+' && !hasDigits { - return "", p.undo() - } - - sb.WriteRune(r) - - for { - r, err := p.next() - if err != nil { - break - } - if r < '0' || r > '9' { - if err := p.undo(); err != nil { - return "", err - } - break - } - hasDigits = true - sb.WriteRune(r) - } - - if !hasDigits { - return "", p.makeError("invalid number - no digits in exponent") - } - - return sb.String(), nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_number_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_number_test.go deleted file mode 100644 index d2acf3b0a8d9..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_number_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Number_IntToInt(t *testing.T) { - example := []byte(`123`) - var output int - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.Equal(t, 123, output) -} - -func Test_Number_IntToFloat(t *testing.T) { - example := []byte(`123`) - var output float64 - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.InEpsilon(t, 123.0, output, 0.0001) -} - -func Test_Number_FloatToFloat(t *testing.T) { - example := []byte(`123.456`) - var output float64 - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.InEpsilon(t, 123.456, output, 0.0001) -} - -func Test_Number_FloatToInt(t *testing.T) { - example := []byte(`123.456`) - var output int - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.InEpsilon(t, 123, output, 0.0001) -} - -func Test_Number_FloatWithExponent(t *testing.T) { - cases := []struct { - in string - out float64 - }{ - { - in: `123.456e10`, - out: 123.456e+10, - }, - { - in: `123e+1`, - out: 123e+1, - }, - { - in: `123e-2`, - out: 123e-2, - }, - } - for _, test := range cases { - t.Run(test.in, func(t *testing.T) { - example := []byte(test.in) - var output float64 - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.InEpsilon(t, test.out, output, 0.0001) - - }) - } -} - -func Test_Number_IntWithExponent(t *testing.T) { - cases := []struct { - in string - out int64 - }{ - { - in: `123e10`, - out: 123e+10, - }, - { - in: `123e+1`, - out: 123e+1, - }, - } - for _, test := range cases { - t.Run(test.in, func(t *testing.T) { - example := []byte(test.in) - var output int64 - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.Equal(t, test.out, output) - - }) - } -} - -func Test_Number_Ints(t *testing.T) { - cases := []struct { - in string - out int64 - err bool - }{ - { - in: `123e10`, - out: 123e+10, - }, - { - in: `-1`, - out: -1, - }, - { - in: `1.0123`, - out: 1, - }, - { - in: `0`, - out: 0, - }, - { - in: `01`, - err: true, - }, - { - in: ``, - err: true, - }, - { - in: `+1`, - err: true, - }, - { - in: `e`, - err: true, - }, - - { - in: `.123`, - err: true, - }, - - { - in: `.`, - err: true, - }, - - { - in: `00`, - err: true, - }, - { - in: `-`, - err: true, - }, - } - for _, test := range cases { - t.Run(test.in, func(t *testing.T) { - example := []byte(test.in) - var output int64 - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - if test.err { - require.Error(t, err) - return - } - require.NoError(t, err) - assert.Equal(t, test.out, output) - }) - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_object.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_object.go deleted file mode 100644 index 392dbbbae697..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_object.go +++ /dev/null @@ -1,143 +0,0 @@ -package armjson - -import ( - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (p *parser) parseObject(parentMetadata *types.Metadata) (Node, error) { - - n, metadata := p.newNode(KindObject, parentMetadata) - - c, err := p.next() - if err != nil { - return nil, err - } - - if c != '{' { - return nil, p.makeError("expecting object delimiter") - } - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - - // we've hit the end of the object - if p.swallowIfEqual('}') { - n.end = p.position - return n, nil - } - - var nextComments []Node - return p.iterateObject(nextComments, metadata, n) - -} - -// nolint: gocyclo -func (p *parser) iterateObject(nextComments []Node, metadata *types.Metadata, n *node) (Node, error) { - for { - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - - comments := make([]Node, len(nextComments)) - copy(comments, nextComments) - nextComments = nil - for { - peeked, err := p.peeker.Peek() - if err != nil { - return nil, err - } - if peeked != '/' { - break - } - comment, err := p.parseComment(metadata) - if err != nil { - return nil, err - } - comments = append(comments, comment) - } - - if comments != nil { - if err := p.parseWhitespace(); err != nil { - return nil, err - } - } - - key, err := p.parseString(metadata) - if err != nil { - return nil, err - } - - if err := p.parseWhitespace(); err != nil { - return nil, err - } - - if !p.swallowIfEqual(':') { - return nil, p.makeError("invalid character, expecting ':'") - } - - val, err := p.parseElement(metadata) - if err != nil { - return nil, err - } - ref := key.(*node).raw.(string) - key.(*node).ref = ref - val.(*node).ref = ref - - for { - peeked, err := p.peeker.Peek() - if err != nil { - return nil, err - } - if peeked != '/' { - break - } - comment, err := p.parseComment(metadata) - if err != nil { - return nil, err - } - comments = append(comments, comment) - } - - // we've hit the end of the object - if p.swallowIfEqual('}') { - key.(*node).comments = comments - val.(*node).comments = comments - n.content = append(n.content, key, val) - n.end = p.position - return n, nil - } - - if !p.swallowIfEqual(',') { - return nil, p.makeError("unexpected character - expecting , or }") - } - - for { - if err := p.parseWhitespace(); err != nil { - return nil, err - } - peeked, err := p.peeker.Peek() - if err != nil { - return nil, err - } - if peeked != '/' { - break - } - comment, err := p.parseComment(metadata) - if err != nil { - return nil, err - } - if comment.Range().Start.Line > val.Range().End.Line { - nextComments = append(nextComments, comment) - } else { - comments = append(comments, comment) - } - } - - key.(*node).comments = comments - val.(*node).comments = comments - n.content = append(n.content, key, val) - - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_object_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_object_test.go deleted file mode 100644 index 31ea685cef23..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_object_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Object(t *testing.T) { - example := []byte(`{ - "name": "testing", - "balance": 3.14 -}`) - target := struct { - Name string `json:"name"` - Balance float64 `json:"balance"` - }{} - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &target, &metadata)) - assert.Equal(t, "testing", target.Name) - assert.InEpsilon(t, 3.14, target.Balance, 0.0001) -} - -func Test_ObjectWithPointers(t *testing.T) { - example := []byte(`{ - "name": "testing", - "balance": 3.14 -}`) - target := struct { - Name *string `json:"name"` - Balance *float64 `json:"balance"` - }{} - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &target, &metadata)) - assert.Equal(t, "testing", *target.Name) - assert.InEpsilon(t, 3.14, *target.Balance, 0.0001) -} - -type nestedParent struct { - Child *nestedChild - Name string -} - -type nestedChild struct { - Blah string `json:"secret"` -} - -func Test_ObjectWithPointerToNestedStruct(t *testing.T) { - example := []byte(`{ - "Child": { - "secret": "password" - }, - "Name": "testing" -}`) - - var parent nestedParent - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &parent, &metadata)) - assert.Equal(t, "testing", parent.Name) - assert.Equal(t, "password", parent.Child.Blah) -} - -func Test_Object_ToMapStringInterface(t *testing.T) { - example := []byte(`{ - "Name": "testing" -}`) - - parent := make(map[string]any) - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &parent, &metadata)) - assert.Equal(t, "testing", parent["Name"]) -} - -func Test_Object_ToNestedMapStringInterfaceFromIAM(t *testing.T) { - example := []byte(` -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "", - "Effect": "Allow", - "Action": "ec2:*", - "Resource": "*", - "Condition": { - "Bool": { - "aws:MultiFactorAuthPresent": ["true"] - } - } - } - ] -}`) - - parent := make(map[string]any) - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &parent, &metadata)) -} - -func Test_Object_ToNestedMapStringInterface(t *testing.T) { - example := []byte(`{ - "Child": { - "secret": "password" - }, - "Name": "testing" -}`) - - parent := make(map[string]any) - metadata := types.NewTestMetadata() - require.NoError(t, Unmarshal(example, &parent, &metadata)) - assert.Equal(t, "testing", parent["Name"]) - child := parent["Child"].(map[string]any) - assert.Equal(t, "password", child["secret"]) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_string.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_string.go deleted file mode 100644 index 41847b914d7f..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_string.go +++ /dev/null @@ -1,91 +0,0 @@ -package armjson - -import ( - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -var escapes = map[rune]string{ - '\\': "\\", - '/': "/", - '"': "\"", - 'n': "\n", - 'r': "\r", - 'b': "\b", - 'f': "\f", - 't': "\t", -} - -// nolint: cyclop -func (p *parser) parseString(parentMetadata *types.Metadata) (Node, error) { - - n, _ := p.newNode(KindString, parentMetadata) - - b, err := p.next() - if err != nil { - return nil, err - } - - if b != '"' { - return nil, p.makeError("expecting string delimiter") - } - - var sb strings.Builder - - var inEscape bool - var inHex bool - var hex []rune - - for { - c, err := p.next() - if err != nil { - return nil, err - } - // nolint: gocritic - if inHex { - switch { - case c >= 'a' && c <= 'f', c >= 'A' && c <= 'F', c >= '0' && c <= '9': - hex = append(hex, c) - if len(hex) == 4 { - inHex = false - char, err := strconv.Unquote("\\u" + string(hex)) - if err != nil { - return nil, p.makeError("invalid unicode character '%s'", err) - } - sb.WriteString(char) - hex = nil - } - default: - return nil, p.makeError("invalid hexedecimal escape sequence '\\%s%c'", string(hex), c) - } - } else if inEscape { - inEscape = false - if c == 'u' { - inHex = true - continue - } - seq, ok := escapes[c] - if !ok { - return nil, p.makeError("invalid escape sequence '\\%c'", c) - } - sb.WriteString(seq) - } else { - switch c { - case '\\': - inEscape = true - case '"': - n.raw = sb.String() - n.end = p.position - return n, nil - default: - if c < 0x20 || c > 0x10FFFF { - return nil, p.makeError("invalid unescaped character '0x%X'", c) - } - sb.WriteRune(c) - } - } - - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_string_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_string_test.go deleted file mode 100644 index 699c1c88fffe..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_string_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package armjson - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_String(t *testing.T) { - example := []byte(`"hello"`) - var output string - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.Equal(t, "hello", output) -} - -func Test_StringToUninitialisedPointer(t *testing.T) { - example := []byte(`"hello"`) - var str *string - metadata := types.NewTestMetadata() - err := Unmarshal(example, str, &metadata) - require.Error(t, err) - assert.Nil(t, str) -} - -func Test_String_ToInterface(t *testing.T) { - example := []byte(`"hello"`) - var output any - metadata := types.NewTestMetadata() - err := Unmarshal(example, &output, &metadata) - require.NoError(t, err) - assert.Equal(t, "hello", output) -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/parse_whitespace.go b/pkg/iac/scanners/azure/arm/parser/armjson/parse_whitespace.go deleted file mode 100644 index ad5751147d3e..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/parse_whitespace.go +++ /dev/null @@ -1,29 +0,0 @@ -package armjson - -import ( - "errors" - "io" -) - -func (p *parser) parseWhitespace() error { - for { - b, err := p.peeker.Peek() - if err != nil { - if errors.Is(err, io.EOF) { - return nil - } - return err - } - switch b { - case 0x0d, 0x20, 0x09: - case 0x0a: - p.position.Column = 1 - p.position.Line++ - default: - return nil - } - if _, err := p.next(); err != nil { - return err - } - } -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/reader.go b/pkg/iac/scanners/azure/arm/parser/armjson/reader.go deleted file mode 100644 index e05769f02da9..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/reader.go +++ /dev/null @@ -1,36 +0,0 @@ -package armjson - -import ( - "bufio" - "io" -) - -type PeekReader struct { - underlying *bufio.Reader -} - -func NewPeekReader(reader io.Reader) *PeekReader { - return &PeekReader{ - underlying: bufio.NewReader(reader), - } -} - -func (r *PeekReader) Next() (rune, error) { - c, _, err := r.underlying.ReadRune() - return c, err -} - -func (r *PeekReader) Undo() error { - return r.underlying.UnreadRune() -} - -func (r *PeekReader) Peek() (rune, error) { - c, _, err := r.underlying.ReadRune() - if err != nil { - return 0, err - } - if err := r.underlying.UnreadRune(); err != nil { - return 0, err - } - return c, nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/reader_test.go b/pkg/iac/scanners/azure/arm/parser/armjson/reader_test.go deleted file mode 100644 index 8017f30f9f98..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/reader_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package armjson - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var input = `abcdefghijklmnopqrstuvwxyz` - -func Test_Peeker(t *testing.T) { - peeker := NewPeekReader(strings.NewReader(input)) - - var b rune - var err error - - for i := 0; i < 30; i++ { - b, err = peeker.Peek() - require.NoError(t, err) - assert.Equal(t, ('a'), b) - } - - b, err = peeker.Next() - require.NoError(t, err) - assert.Equal(t, ('a'), b) - - b, err = peeker.Next() - require.NoError(t, err) - assert.Equal(t, ('b'), b) - - b, err = peeker.Peek() - require.NoError(t, err) - assert.Equal(t, ('c'), b) - - for i := 0; i < 5; i++ { - b, err = peeker.Next() - require.NoError(t, err) - assert.Equal(t, []rune(input)[2+i], b) - } - - b, err = peeker.Peek() - require.NoError(t, err) - assert.Equal(t, ('h'), b) - - b, err = peeker.Next() - require.NoError(t, err) - assert.Equal(t, ('h'), b) - for i := 0; i < 18; i++ { - b, err = peeker.Next() - require.NoError(t, err) - assert.Equal(t, []rune(input)[8+i], b) - } - - _, err = peeker.Peek() - require.Error(t, err) - - _, err = peeker.Next() - require.Error(t, err) - -} diff --git a/pkg/iac/scanners/azure/arm/parser/armjson/unmarshal.go b/pkg/iac/scanners/azure/arm/parser/armjson/unmarshal.go deleted file mode 100644 index b198bb7b7073..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/armjson/unmarshal.go +++ /dev/null @@ -1,40 +0,0 @@ -package armjson - -import ( - "bytes" - "io" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Unmarshaller interface { - UnmarshalJSONWithMetadata(node Node) error -} - -type MetadataReceiver interface { - SetMetadata(m *types.Metadata) -} - -func Unmarshal(data []byte, target any, metadata *types.Metadata) error { - node, err := newParser(NewPeekReader(bytes.NewReader(data)), Position{1, 1}).parse(metadata) - if err != nil { - return err - } - if err := node.Decode(target); err != nil { - return err - } - - return nil -} - -func UnmarshalFromReader(r io.ReadSeeker, target any, metadata *types.Metadata) error { - node, err := newParser(NewPeekReader(r), Position{1, 1}).parse(metadata) - if err != nil { - return err - } - if err := node.Decode(target); err != nil { - return err - } - - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/parser.go b/pkg/iac/scanners/azure/arm/parser/parser.go deleted file mode 100644 index 53f9eb21431b..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/parser.go +++ /dev/null @@ -1,149 +0,0 @@ -package parser - -import ( - "context" - "fmt" - "io" - "io/fs" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/arm/parser/armjson" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/resolver" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - targetFS fs.FS - logger *log.Logger -} - -func New(targetFS fs.FS) *Parser { - return &Parser{ - targetFS: targetFS, - logger: log.WithPrefix("arm parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, dir string) ([]azure.Deployment, error) { - - var deployments []azure.Deployment - - if err := fs.WalkDir(p.targetFS, dir, func(path string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if entry.IsDir() { - return nil - } - - f, err := p.targetFS.Open(path) - if err != nil { - return err - } - defer f.Close() - - deployment, err := p.parseFile(f, path) - if err != nil { - return err - } - deployments = append(deployments, *deployment) - return nil - }); err != nil { - return nil, err - } - - return deployments, nil -} - -func (p *Parser) parseFile(r io.Reader, filename string) (*azure.Deployment, error) { - var template Template - data, err := io.ReadAll(r) - if err != nil { - return nil, err - } - root := types.NewMetadata( - types.NewRange(filename, 0, 0, "", p.targetFS), - "", - ).WithInternal(resolver.NewResolver()) - - if err := armjson.Unmarshal(data, &template, &root); err != nil { - return nil, fmt.Errorf("failed to parse template: %w", err) - } - return p.convertTemplate(template), nil -} - -func (p *Parser) convertTemplate(template Template) *azure.Deployment { - - deployment := azure.Deployment{ - Metadata: template.Metadata, - TargetScope: azure.ScopeResourceGroup, // TODO: override from --resource-group? - Parameters: nil, - Variables: nil, - Resources: nil, - Outputs: nil, - } - - if r, ok := template.Metadata.Internal().(resolver.Resolver); ok { - r.SetDeployment(&deployment) - } - - // TODO: the references passed here should probably not be the name - maybe params.NAME.DefaultValue? - for name, param := range template.Parameters { - deployment.Parameters = append(deployment.Parameters, azure.Parameter{ - Variable: azure.Variable{ - Name: name, - Value: param.DefaultValue, - }, - Default: param.DefaultValue, - Decorators: nil, - }) - } - - for name, variable := range template.Variables { - deployment.Variables = append(deployment.Variables, azure.Variable{ - Name: name, - Value: variable, - }) - } - - for name, output := range template.Outputs { - deployment.Outputs = append(deployment.Outputs, azure.Output{ - Name: name, - Value: output, - }) - } - - for _, resource := range template.Resources { - deployment.Resources = append(deployment.Resources, p.convertResource(resource)) - } - - return &deployment -} - -func (p *Parser) convertResource(input Resource) azure.Resource { - - var children []azure.Resource - - for _, child := range input.Resources { - children = append(children, p.convertResource(child)) - } - - resource := azure.Resource{ - Metadata: input.Metadata, - APIVersion: input.APIVersion, - Type: input.Type, - Kind: input.Kind, - Name: input.Name, - Location: input.Location, - Properties: input.Properties, - Resources: children, - } - - return resource -} diff --git a/pkg/iac/scanners/azure/arm/parser/parser_test.go b/pkg/iac/scanners/azure/arm/parser/parser_test.go deleted file mode 100644 index ff1444a60655..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/parser_test.go +++ /dev/null @@ -1,331 +0,0 @@ -package parser - -import ( - "context" - "io/fs" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/resolver" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func createMetadata(targetFS fs.FS, filename string, start, end int, ref string, parent *types.Metadata) types.Metadata { - child := types.NewMetadata(types.NewRange(filename, start, end, "", targetFS), ref) - if parent != nil { - child.SetParentPtr(parent) - } - return child -} - -func TestParser_Parse(t *testing.T) { - - filename := "example.json" - - targetFS := memoryfs.New() - - tests := []struct { - name string - input string - want func() azure2.Deployment - wantDeployment bool - }{ - { - name: "basic param", - input: `{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", // another one - "contentVersion": "1.0.0.0", - "parameters": { - "storagePrefix": { - "type": "string", - "defaultValue": "x", - "maxLength": 11, - "minLength": 3 - } - }, - "resources": [] -}`, - want: func() azure2.Deployment { - - root := createMetadata(targetFS, filename, 0, 0, "", nil).WithInternal(resolver.NewResolver()) - metadata := createMetadata(targetFS, filename, 1, 13, "", &root) - parametersMetadata := createMetadata(targetFS, filename, 4, 11, "parameters", &metadata) - storageMetadata := createMetadata(targetFS, filename, 5, 10, "parameters.storagePrefix", ¶metersMetadata) - - return azure2.Deployment{ - Metadata: metadata, - TargetScope: azure2.ScopeResourceGroup, - Parameters: []azure2.Parameter{ - { - Variable: azure2.Variable{ - Name: "storagePrefix", - Value: azure2.NewValue("x", createMetadata(targetFS, filename, 7, 7, "parameters.storagePrefix.defaultValue", &storageMetadata)), - }, - Default: azure2.NewValue("x", createMetadata(targetFS, filename, 7, 7, "parameters.storagePrefix.defaultValue", &storageMetadata)), - Decorators: nil, - }, - }, - } - }, - wantDeployment: true, - }, - { - name: "storageAccount", - input: `{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", // another one - "contentVersion": "1.0.0.0", - "parameters": {}, - "resources": [ -{ - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2022-05-01", - "name": "myResource", - "location": "string", - "tags": { - "tagName1": "tagValue1", - "tagName2": "tagValue2" - }, - "sku": { - "name": "string" - }, - "kind": "string", - "extendedLocation": { - "name": "string", - "type": "EdgeZone" - }, - "identity": { - "type": "string", - "userAssignedIdentities": {} - }, - "properties": { - "allowSharedKeyAccess":false, - "customDomain": { - "name": "string", - "useSubDomainName":false, - "number": 123 - }, - "networkAcls": [ - { - "bypass": "AzureServices1" - }, - { - "bypass": "AzureServices2" - } - ] - } -} -] -}`, - want: func() azure2.Deployment { - - rootMetadata := createMetadata(targetFS, filename, 0, 0, "", nil).WithInternal(resolver.NewResolver()) - fileMetadata := createMetadata(targetFS, filename, 1, 45, "", &rootMetadata) - resourcesMetadata := createMetadata(targetFS, filename, 5, 44, "resources", &fileMetadata) - - resourceMetadata := createMetadata(targetFS, filename, 6, 43, "resources[0]", &resourcesMetadata) - - propertiesMetadata := createMetadata(targetFS, filename, 27, 42, "resources[0].properties", &resourceMetadata) - - customDomainMetadata := createMetadata(targetFS, filename, 29, 33, "resources[0].properties.customDomain", &propertiesMetadata) - networkACLListMetadata := createMetadata(targetFS, filename, 34, 41, "resources[0].properties.networkAcls", &propertiesMetadata) - - networkACL0Metadata := createMetadata(targetFS, filename, 35, 37, "resources[0].properties.networkAcls[0]", &networkACLListMetadata) - networkACL1Metadata := createMetadata(targetFS, filename, 38, 40, "resources[0].properties.networkAcls[1]", &networkACLListMetadata) - - return azure2.Deployment{ - Metadata: fileMetadata, - TargetScope: azure2.ScopeResourceGroup, - Resources: []azure2.Resource{ - { - Metadata: resourceMetadata, - APIVersion: azure2.NewValue( - "2022-05-01", - createMetadata(targetFS, filename, 8, 8, "resources[0].apiVersion", &resourceMetadata), - ), - Type: azure2.NewValue( - "Microsoft.Storage/storageAccounts", - createMetadata(targetFS, filename, 7, 7, "resources[0].type", &resourceMetadata), - ), - Kind: azure2.NewValue( - "string", - createMetadata(targetFS, filename, 18, 18, "resources[0].kind", &resourceMetadata), - ), - Name: azure2.NewValue( - "myResource", - createMetadata(targetFS, filename, 9, 9, "resources[0].name", &resourceMetadata), - ), - Location: azure2.NewValue( - "string", - createMetadata(targetFS, filename, 10, 10, "resources[0].location", &resourceMetadata), - ), - Properties: azure2.NewValue( - map[string]azure2.Value{ - "allowSharedKeyAccess": azure2.NewValue(false, createMetadata(targetFS, filename, 28, 28, "resources[0].properties.allowSharedKeyAccess", &propertiesMetadata)), - "customDomain": azure2.NewValue( - map[string]azure2.Value{ - "name": azure2.NewValue("string", createMetadata(targetFS, filename, 30, 30, "resources[0].properties.customDomain.name", &customDomainMetadata)), - "useSubDomainName": azure2.NewValue(false, createMetadata(targetFS, filename, 31, 31, "resources[0].properties.customDomain.useSubDomainName", &customDomainMetadata)), - "number": azure2.NewValue(int64(123), createMetadata(targetFS, filename, 32, 32, "resources[0].properties.customDomain.number", &customDomainMetadata)), - }, customDomainMetadata), - "networkAcls": azure2.NewValue( - []azure2.Value{ - azure2.NewValue( - map[string]azure2.Value{ - "bypass": azure2.NewValue("AzureServices1", createMetadata(targetFS, filename, 36, 36, "resources[0].properties.networkAcls[0].bypass", &networkACL0Metadata)), - }, - networkACL0Metadata, - ), - azure2.NewValue( - map[string]azure2.Value{ - "bypass": azure2.NewValue("AzureServices2", createMetadata(targetFS, filename, 39, 39, "resources[0].properties.networkAcls[1].bypass", &networkACL1Metadata)), - }, - networkACL1Metadata, - ), - }, networkACLListMetadata), - }, - propertiesMetadata, - ), - }, - }, - } - }, - - wantDeployment: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - require.NoError(t, targetFS.WriteFile(filename, []byte(tt.input), 0644)) - - p := New(targetFS) - got, err := p.ParseFS(context.Background(), ".") - require.NoError(t, err) - - if !tt.wantDeployment { - assert.Empty(t, got) - return - } - - require.Len(t, got, 1) - want := tt.want() - g := got[0] - - require.Equal(t, want, g) - }) - } -} - -func Test_NestedResourceParsing(t *testing.T) { - - input := ` -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "environment": { - "type": "string", - "allowedValues": [ - "dev", - "test", - "prod" - ] - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Location for all resources." - } - }, - "storageAccountSkuName": { - "type": "string", - "defaultValue": "Standard_LRS" - }, - "storageAccountSkuTier": { - "type": "string", - "defaultValue": "Standard" - } - }, - "variables": { - "uniquePart": "[take(uniqueString(resourceGroup().id), 4)]", - "storageAccountName": "[concat('mystorageaccount', variables('uniquePart'), parameters('environment'))]", - "queueName": "myqueue" - }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts", - "name": "[variables('storageAccountName')]", - "location": "[parameters('location')]", - "apiVersion": "2019-06-01", - "sku": { - "name": "[parameters('storageAccountSkuName')]", - "tier": "[parameters('storageAccountSkuTier')]" - }, - "kind": "StorageV2", - "properties": {}, - "resources": [ - { - "name": "[concat('default/', variables('queueName'))]", - "type": "queueServices/queues", - "apiVersion": "2019-06-01", - "dependsOn": [ - "[variables('storageAccountName')]" - ], - "properties": { - "metadata": {} - } - } - ] - } - ] -} -` - - targetFS := memoryfs.New() - - require.NoError(t, targetFS.WriteFile("nested.json", []byte(input), 0644)) - - p := New(targetFS) - got, err := p.ParseFS(context.Background(), ".") - require.NoError(t, err) - require.Len(t, got, 1) - - deployment := got[0] - - require.Len(t, deployment.Resources, 1) - - storageAccountResource := deployment.Resources[0] - - require.Len(t, storageAccountResource.Resources, 1) - - queue := storageAccountResource.Resources[0] - - assert.Equal(t, "queueServices/queues", queue.Type.AsString()) -} - -// -// func Test_JsonFile(t *testing.T) { -// -// input, err := os.ReadFile("testdata/postgres.json") -// require.NoError(t, err) -// -// targetFS := memoryfs.New() -// -// require.NoError(t, targetFS.WriteFile("postgres.json", input, 0644)) -// -// p := New(targetFS, options.ParserWithDebug(os.Stderr)) -// got, err := p.ParseFS(context.Background(), ".") -// require.NoError(t, err) -// -// got[0].Resources[3].Name.Resolve() -// -// name := got[0].Resources[3].Name.AsString() -// assert.Equal(t, "myserver", name) -// -// } diff --git a/pkg/iac/scanners/azure/arm/parser/template.go b/pkg/iac/scanners/azure/arm/parser/template.go deleted file mode 100644 index ed7b3fa30238..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/template.go +++ /dev/null @@ -1,78 +0,0 @@ -package parser - -import ( - types2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/arm/parser/armjson" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Template struct { - Metadata types.Metadata `json:"-"` - Schema types2.Value `json:"$schema"` - ContentVersion types2.Value `json:"contentVersion"` - APIProfile types2.Value `json:"apiProfile"` - Parameters map[string]Parameter `json:"parameters"` - Variables map[string]types2.Value `json:"variables"` - Functions []Function `json:"functions"` - Resources []Resource `json:"resources"` - Outputs map[string]types2.Value `json:"outputs"` -} - -type Parameter struct { - Metadata types.Metadata - Type types2.Value `json:"type"` - DefaultValue types2.Value `json:"defaultValue"` - MaxLength types2.Value `json:"maxLength"` - MinLength types2.Value `json:"minLength"` -} - -type Function struct{} - -type Resource struct { - Metadata types.Metadata `json:"-"` - innerResource -} - -func (t *Template) SetMetadata(m *types.Metadata) { - t.Metadata = *m -} - -func (r *Resource) SetMetadata(m *types.Metadata) { - r.Metadata = *m -} - -func (p *Parameter) SetMetadata(m *types.Metadata) { - p.Metadata = *m -} - -type innerResource struct { - APIVersion types2.Value `json:"apiVersion"` - Type types2.Value `json:"type"` - Kind types2.Value `json:"kind"` - Name types2.Value `json:"name"` - Location types2.Value `json:"location"` - Tags types2.Value `json:"tags"` - Sku types2.Value `json:"sku"` - Properties types2.Value `json:"properties"` - Resources []Resource `json:"resources"` -} - -func (v *Resource) UnmarshalJSONWithMetadata(node armjson.Node) error { - - if err := node.Decode(&v.innerResource); err != nil { - return err - } - - v.Metadata = node.Metadata() - - for _, comment := range node.Comments() { - var str string - if err := comment.Decode(&str); err != nil { - return err - } - // TODO(someone): add support for metadata comments - // v.Metadata.Comments = append(v.Metadata.Comments, str) - } - - return nil -} diff --git a/pkg/iac/scanners/azure/arm/parser/template_test.go b/pkg/iac/scanners/azure/arm/parser/template_test.go deleted file mode 100644 index 493cee263582..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/template_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package parser - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - types2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/arm/parser/armjson" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_JSONUnmarshal(t *testing.T) { - data, err := os.ReadFile(filepath.Join("testdata", "example.json")) - require.NoError(t, err) - var target Template - metadata := types.NewTestMetadata() - require.NoError(t, armjson.Unmarshal(data, &target, &metadata)) - assert.Equal(t, - "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - target.Schema.AsString(), - ) - require.Len(t, target.Schema.Comments, 2) - assert.Equal(t, " wow this is a comment", target.Schema.Comments[0]) - assert.Equal(t, " another one", target.Schema.Comments[1]) - - assert.Equal(t, "1.0.0.0", target.ContentVersion.Raw()) - require.Len(t, target.ContentVersion.Comments, 1) - assert.Equal(t, " this version is great", target.ContentVersion.Comments[0]) - - require.Contains(t, target.Parameters, "storagePrefix") - prefix := target.Parameters["storagePrefix"] - /* - "type": "string", - "defaultValue": "x", - "maxLength": 11, - "minLength": 3 - */ - assert.Equal(t, "string", prefix.Type.Raw()) - assert.Equal(t, types2.KindString, prefix.Type.Kind) - assert.Equal(t, 8, prefix.Type.Metadata.Range().GetStartLine()) - assert.Equal(t, 8, prefix.Type.Metadata.Range().GetEndLine()) - - assert.Equal(t, "x", prefix.DefaultValue.Raw()) - assert.Equal(t, types2.KindString, prefix.DefaultValue.Kind) - assert.Equal(t, 9, prefix.DefaultValue.Metadata.Range().GetStartLine()) - assert.Equal(t, 9, prefix.DefaultValue.Metadata.Range().GetEndLine()) - - assert.Equal(t, int64(11), prefix.MaxLength.Raw()) - assert.Equal(t, types2.KindNumber, prefix.MaxLength.Kind) - assert.Equal(t, 10, prefix.MaxLength.Metadata.Range().GetStartLine()) - assert.Equal(t, 10, prefix.MaxLength.Metadata.Range().GetEndLine()) - - assert.Equal(t, int64(3), prefix.MinLength.Raw()) - assert.Equal(t, types2.KindNumber, prefix.MinLength.Kind) - assert.Equal(t, 11, prefix.MinLength.Metadata.Range().GetStartLine()) - assert.Equal(t, 11, prefix.MinLength.Metadata.Range().GetEndLine()) -} diff --git a/pkg/iac/scanners/azure/arm/parser/testdata/example.json b/pkg/iac/scanners/azure/arm/parser/testdata/example.json deleted file mode 100644 index 9698ed1a0583..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/testdata/example.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // wow this is a comment - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", // another one - // this version is great - "contentVersion": "1.0.0.0", - "parameters": { - "storagePrefix": { - "type": "string", - "defaultValue": "x", - "maxLength": 11, - "minLength": 3 - } - }, - "resources": [] -} \ No newline at end of file diff --git a/pkg/iac/scanners/azure/arm/parser/testdata/postgres.json b/pkg/iac/scanners/azure/arm/parser/testdata/postgres.json deleted file mode 100644 index 670733fdd308..000000000000 --- a/pkg/iac/scanners/azure/arm/parser/testdata/postgres.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.10.61.36676", - "templateHash": "8074447630975889785" - } - }, - "resources": [ - { - "type": "Microsoft.DBforPostgreSQL/servers", - "apiVersion": "2017-12-01", - "name": "myPostgreSQLServer", - "location": "westus", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "administratorLogin": "myadmin", - "administratorLoginPassword": "myadminpassword", - "version": "9.6", - "sslEnforcement": "Enabled", - "storageProfile": { - "storageMB": 5120 - }, - "createMode": "Default", - "minimalTlsVersion": "1.2", - "publicNetworkAccess": "Enabled", - "FirewallRules": [ - { - "name": "AllowAllAzureIps", - "startIpAddress": "0.0.0.0/0" - } - ] - } - }, - { - "type": "Microsoft.DBforPostgreSQL/servers/configurations", - "apiVersion": "2017-12-01", - "name": "[format('{0}/{1}', 'myPostgreSQLServer', 'connection_throttling')]", - "properties": { - "value": "OFF" - }, - "dependsOn": [ - "[resourceId('Microsoft.DBforPostgreSQL/servers', 'myPostgreSQLServer')]" - ] - }, - { - "type": "Microsoft.DBforPostgreSQL/servers/configurations", - "apiVersion": "2017-12-01", - "name": "[format('{0}/{1}', 'myPostgreSQLServer', 'log_checkpoints')]", - "properties": { - "value": "OFF" - }, - "dependsOn": [ - "[resourceId('Microsoft.DBforPostgreSQL/servers', 'myPostgreSQLServer')]" - ] - }, - { - "type": "Microsoft.DBforPostgreSQL/servers/configurations", - "apiVersion": "2017-12-01", - "name": "[format('{0}/{1}', 'myPostgreSQLServer', 'log_connections')]", - "properties": { - "value": "OFF" - }, - "dependsOn": [ - "[resourceId('Microsoft.DBforPostgreSQL/servers', 'myPostgreSQLServer')]" - ] - } - ] -} \ No newline at end of file diff --git a/pkg/iac/scanners/azure/arm/scanner.go b/pkg/iac/scanners/azure/arm/scanner.go deleted file mode 100644 index fe8b3e8a33d9..000000000000 --- a/pkg/iac/scanners/azure/arm/scanner.go +++ /dev/null @@ -1,106 +0,0 @@ -package arm - -import ( - "context" - "fmt" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/adapters/arm" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/arm/parser" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/state" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - scannerOptions []options.ScannerOption - logger *log.Logger - regoScanner *rego.Scanner -} - -func New(opts ...options.ScannerOption) *Scanner { - scanner := &Scanner{ - scannerOptions: opts, - logger: log.WithPrefix("azure-arm"), - } - for _, opt := range opts { - opt(scanner) - } - return scanner -} - -func (s *Scanner) Name() string { - return "Azure ARM" -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) error { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return nil - } - regoScanner := rego.NewScanner(s.scannerOptions...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return err - } - s.regoScanner = regoScanner - return nil -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (scan.Results, error) { - p := parser.New(fsys) - deployments, err := p.ParseFS(ctx, dir) - if err != nil { - return nil, err - } - if err := s.initRegoScanner(fsys); err != nil { - return nil, err - } - - return s.scanDeployments(ctx, deployments, fsys) -} - -func (s *Scanner) scanDeployments(ctx context.Context, deployments []azure.Deployment, f fs.FS) (scan.Results, error) { - - var results scan.Results - - for _, deployment := range deployments { - - result, err := s.scanDeployment(ctx, deployment, f) - if err != nil { - return nil, err - } - results = append(results, result...) - } - - return results, nil -} - -func (s *Scanner) scanDeployment(ctx context.Context, deployment azure.Deployment, fsys fs.FS) (scan.Results, error) { - deploymentState := s.adaptDeployment(ctx, deployment) - - results, err := s.regoScanner.ScanInput(ctx, types.SourceCloud, rego.Input{ - Path: deployment.Metadata.Range().GetFilename(), - FS: fsys, - Contents: deploymentState.ToRego(), - }) - if err != nil { - return nil, fmt.Errorf("rego scan error: %w", err) - } - - return results, nil -} - -func (s *Scanner) adaptDeployment(ctx context.Context, deployment azure.Deployment) *state.State { - return arm.Adapt(ctx, deployment) -} diff --git a/pkg/iac/scanners/azure/deployment.go b/pkg/iac/scanners/azure/deployment.go deleted file mode 100644 index 9932f1538db3..000000000000 --- a/pkg/iac/scanners/azure/deployment.go +++ /dev/null @@ -1,179 +0,0 @@ -package azure - -import ( - "os" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Deployment struct { - Metadata types.Metadata - TargetScope Scope - Parameters []Parameter - Variables []Variable - Resources []Resource - Outputs []Output -} - -type Parameter struct { - Variable - Default Value - Decorators []Decorator -} - -type Variable struct { - Name string - Value Value -} - -type Output Variable - -type Resource struct { - Metadata types.Metadata - APIVersion Value - Type Value - Kind Value - Name Value - Location Value - Tags Value - Sku Value - Properties Value - Resources []Resource -} - -type PropertyBag struct { - Metadata types.Metadata - Data map[string]Value -} - -type Decorator struct { - Name string - Args []Value -} - -type Scope string - -const ( - ScopeResourceGroup Scope = "resourceGroup" -) - -func (d *Deployment) GetResourcesByType(t string) []Resource { - var resources []Resource - for _, r := range d.Resources { - if r.Type.AsString() == t { - resources = append(resources, r) - } - } - return resources -} - -func (r *Resource) GetResourcesByType(t string) []Resource { - var resources []Resource - for _, res := range r.Resources { - if res.Type.AsString() == t { - resources = append(resources, res) - } - } - return resources -} - -func (d *Deployment) GetParameter(parameterName string) any { - - for _, parameter := range d.Parameters { - if parameter.Name == parameterName { - return parameter.Value.Raw() - } - } - return nil -} - -func (d *Deployment) GetVariable(variableName string) any { - - for _, variable := range d.Variables { - if variable.Name == variableName { - return variable.Value.Raw() - } - } - return nil -} - -func (d *Deployment) GetEnvVariable(envVariableName string) any { - - if envVariable, exists := os.LookupEnv(envVariableName); exists { - return envVariable - } - return nil -} - -func (d *Deployment) GetOutput(outputName string) any { - - for _, output := range d.Outputs { - if output.Name == outputName { - return output.Value.Raw() - } - } - return nil -} - -func (d *Deployment) GetDeployment() any { - - type template struct { - Schema string `json:"$schema"` - ContentVersion string `json:"contentVersion"` - Parameters map[string]any `json:"parameters"` - Variables map[string]any `json:"variables"` - Resources []any `json:"resources"` - Outputs map[string]any `json:"outputs"` - } - - type templateLink struct { - URI string `json:"uri"` - } - - type properties struct { - TemplateLink templateLink `json:"templateLink"` - Template template `json:"template"` - TemplateHash string `json:"templateHash"` - Parameters map[string]any `json:"parameters"` - Mode string `json:"mode"` - ProvisioningState string `json:"provisioningState"` - } - - deploymentShell := struct { - Name string `json:"name"` - Properties properties `json:"properties"` - }{ - Name: "Placeholder Deployment", - Properties: properties{ - TemplateLink: templateLink{ - URI: "https://placeholder.com", - }, - Template: template{ - Schema: "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - ContentVersion: "", - Parameters: make(map[string]any), - Variables: make(map[string]any), - Resources: make([]any, 0), - Outputs: make(map[string]any), - }, - }, - } - - for _, parameter := range d.Parameters { - deploymentShell.Properties.Template.Parameters[parameter.Name] = parameter.Value.Raw() - } - - for _, variable := range d.Variables { - deploymentShell.Properties.Template.Variables[variable.Name] = variable.Value.Raw() - } - - for _, resource := range d.Resources { - deploymentShell.Properties.Template.Resources = append(deploymentShell.Properties.Template.Resources, resource) - } - - for _, output := range d.Outputs { - deploymentShell.Properties.Template.Outputs[output.Name] = output.Value.Raw() - } - - return deploymentShell -} diff --git a/pkg/iac/scanners/azure/expressions/lex.go b/pkg/iac/scanners/azure/expressions/lex.go deleted file mode 100644 index 6bbb4717e745..000000000000 --- a/pkg/iac/scanners/azure/expressions/lex.go +++ /dev/null @@ -1,204 +0,0 @@ -package expressions - -import ( - "bufio" - "errors" - "fmt" - "strconv" - "strings" -) - -type TokenType uint16 - -const ( - TokenName TokenType = iota - TokenOpenParen - TokenCloseParen - TokenComma - TokenDot - TokenLiteralString - TokenLiteralInteger - TokenLiteralFloat - TokenNewLine -) - -type Token struct { - Type TokenType - Data any -} - -type lexer struct { - reader *bufio.Reader -} - -func lex(expression string) ([]Token, error) { - lexer := &lexer{ - reader: bufio.NewReader(strings.NewReader(expression)), - } - return lexer.Lex() -} - -func (l *lexer) unread() { - _ = l.reader.UnreadRune() -} - -func (l *lexer) read() (rune, error) { - r, _, err := l.reader.ReadRune() - return r, err -} - -func (l *lexer) Lex() ([]Token, error) { - var tokens []Token - - for { - r, err := l.read() - if err != nil { - break - } - - switch r { - case ' ', '\t', '\r': - continue - case '\n': - tokens = append(tokens, Token{Type: TokenNewLine}) - case '(': - tokens = append(tokens, Token{Type: TokenOpenParen}) - case ')': - tokens = append(tokens, Token{Type: TokenCloseParen}) - case ',': - tokens = append(tokens, Token{Type: TokenComma}) - case '.': - tokens = append(tokens, Token{Type: TokenDot}) - case '"', '\'': - token, err := l.lexString(r) - if err != nil { - return nil, fmt.Errorf("string parse error: %w", err) - } - tokens = append(tokens, token) - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - l.unread() - token, err := l.lexNumber() - if err != nil { - return nil, fmt.Errorf("number parse error: %w", err) - } - tokens = append(tokens, token) - default: - l.unread() - tokens = append(tokens, l.lexKeyword()) - } - } - - return tokens, nil -} - -func (l *lexer) lexString(terminator rune) (Token, error) { - var sb strings.Builder - for { - r, err := l.read() - if err != nil { - break - } - if r == '\\' { - r, err := l.readEscapedChar() - if err != nil { - return Token{}, fmt.Errorf("bad escape: %w", err) - } - sb.WriteRune(r) - continue - } - if r == terminator { - break - } - sb.WriteRune(r) - } - return Token{ - Type: TokenLiteralString, - Data: sb.String(), - }, nil -} - -func (l *lexer) readEscapedChar() (rune, error) { - r, err := l.read() - if err != nil { - return 0, errors.New("unexpected EOF") - } - switch r { - case 'n': - return '\n', nil - case 'r': - return '\r', nil - case 't': - return '\t', nil - case '"', '\'': - return r, nil - default: - return 0, fmt.Errorf("'%c' is not a supported escape sequence", r) - } -} - -func (l *lexer) lexNumber() (Token, error) { - - var sb strings.Builder - var decimal bool - -LOOP: - for { - r, err := l.read() - if err != nil { - break - } - switch r { - case '.': - decimal = true - sb.WriteRune('.') - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - sb.WriteRune(r) - default: - l.unread() - break LOOP - } - } - - raw := sb.String() - if decimal { - fl, err := strconv.ParseFloat(raw, 64) - if err != nil { - return Token{}, err - } - return Token{ - Type: TokenLiteralFloat, - Data: fl, - }, nil - } - - i, err := strconv.ParseInt(raw, 10, 64) - if err != nil { - return Token{}, err - } - return Token{ - Type: TokenLiteralInteger, - Data: i, - }, nil -} - -func (l *lexer) lexKeyword() Token { - var sb strings.Builder -LOOP: - for { - r, err := l.read() - if err != nil { - break - } - switch { - case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '_': - sb.WriteRune(r) - default: - l.unread() - break LOOP - } - } - return Token{ - Type: TokenName, - Data: sb.String(), - } -} diff --git a/pkg/iac/scanners/azure/expressions/node.go b/pkg/iac/scanners/azure/expressions/node.go deleted file mode 100644 index 8c274b500a6c..000000000000 --- a/pkg/iac/scanners/azure/expressions/node.go +++ /dev/null @@ -1,75 +0,0 @@ -package expressions - -import ( - functions2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/functions" -) - -type Node interface { - Evaluate(deploymentProvider functions2.DeploymentData) any -} - -type expressionValue struct { - val any -} - -func (e expressionValue) Evaluate(deploymentProvider functions2.DeploymentData) any { - if f, ok := e.val.(expression); ok { - return f.Evaluate(deploymentProvider) - } - return e.val -} - -type expression struct { - name string - args []Node -} - -func (f expression) Evaluate(deploymentProvider functions2.DeploymentData) any { - args := make([]any, len(f.args)) - for i, arg := range f.args { - args[i] = arg.Evaluate(deploymentProvider) - } - - return functions2.Evaluate(deploymentProvider, f.name, args...) -} - -func NewExpressionTree(code string) (Node, error) { - tokens, err := lex(code) - if err != nil { - return nil, err - } - - // create a walker for the nodes - tw := newTokenWalker(tokens) - - // generate the root function - return newFunctionNode(tw), nil -} - -func newFunctionNode(tw *tokenWalker) Node { - funcNode := &expression{ - name: tw.pop().Data.(string), - } - - for tw.hasNext() { - token := tw.pop() - if token == nil { - break - } - - switch token.Type { - case TokenCloseParen: - return funcNode - case TokenName: - if tw.peek().Type == TokenOpenParen { - // this is a function, unwind 1 - tw.unPop() - funcNode.args = append(funcNode.args, newFunctionNode(tw)) - } - case TokenLiteralString, TokenLiteralInteger, TokenLiteralFloat: - funcNode.args = append(funcNode.args, expressionValue{token.Data}) - } - - } - return funcNode -} diff --git a/pkg/iac/scanners/azure/expressions/token_walker.go b/pkg/iac/scanners/azure/expressions/token_walker.go deleted file mode 100644 index d07a238d1bd9..000000000000 --- a/pkg/iac/scanners/azure/expressions/token_walker.go +++ /dev/null @@ -1,40 +0,0 @@ -package expressions - -type tokenWalker struct { - tokens []Token - currentPosition int -} - -func newTokenWalker(tokens []Token) *tokenWalker { - return &tokenWalker{ - tokens: tokens, - currentPosition: 0, - } -} - -func (t *tokenWalker) peek() Token { - if t.currentPosition >= len(t.tokens) { - return Token{} - } - return t.tokens[t.currentPosition] -} - -func (t *tokenWalker) hasNext() bool { - return t.currentPosition+1 < len(t.tokens) -} - -func (t *tokenWalker) unPop() { - if t.currentPosition > 0 { - t.currentPosition-- - } -} - -func (t *tokenWalker) pop() *Token { - if !t.hasNext() { - return nil - } - - token := t.tokens[t.currentPosition] - t.currentPosition++ - return &token -} diff --git a/pkg/iac/scanners/azure/functions/add.go b/pkg/iac/scanners/azure/functions/add.go deleted file mode 100644 index 21b1281574ad..000000000000 --- a/pkg/iac/scanners/azure/functions/add.go +++ /dev/null @@ -1,15 +0,0 @@ -package functions - -func Add(args ...any) any { - - if len(args) != 2 { - return nil - } - - if a, ok := args[0].(int); ok { - if b, ok := args[1].(int); ok { - return a + b - } - } - return nil -} diff --git a/pkg/iac/scanners/azure/functions/add_test.go b/pkg/iac/scanners/azure/functions/add_test.go deleted file mode 100644 index 51ae2bab044a..000000000000 --- a/pkg/iac/scanners/azure/functions/add_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Add(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "Add with 1 and 2", - args: []any{1, 2}, - expected: 3, - }, - { - name: "Add with 2 and 3", - args: []any{2, 3}, - expected: 5, - }, - { - name: "Add with 3 and -4", - args: []any{3, -4}, - expected: -1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Add(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/and.go b/pkg/iac/scanners/azure/functions/and.go deleted file mode 100644 index 4b8b10b7cb31..000000000000 --- a/pkg/iac/scanners/azure/functions/and.go +++ /dev/null @@ -1,27 +0,0 @@ -package functions - -func And(args ...any) any { - - if len(args) <= 1 { - return false - } - - arg0, ok := args[0].(bool) - if !ok { - return false - } - - benchmark := arg0 - - for _, arg := range args[1:] { - arg1, ok := arg.(bool) - if !ok { - return false - } - if benchmark != arg1 { - return false - } - - } - return true -} diff --git a/pkg/iac/scanners/azure/functions/and_test.go b/pkg/iac/scanners/azure/functions/and_test.go deleted file mode 100644 index 8124ee262b1f..000000000000 --- a/pkg/iac/scanners/azure/functions/and_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_And(t *testing.T) { - - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "And with same 2 bools", - args: []any{true, true}, - expected: true, - }, - { - name: "And with same 3 bools", - args: []any{true, true, true}, - expected: true, - }, - { - name: "And with different 4 bools", - args: []any{true, true, false, true}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := And(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/array.go b/pkg/iac/scanners/azure/functions/array.go deleted file mode 100644 index 813a133b30a0..000000000000 --- a/pkg/iac/scanners/azure/functions/array.go +++ /dev/null @@ -1,29 +0,0 @@ -package functions - -func Array(args ...any) any { - - if len(args) != 1 { - return "" - } - - switch ctype := args[0].(type) { - case int: - return []int{ctype} - case string: - return []string{ctype} - case map[string]any: - var result []any - for k, v := range ctype { - result = append(result, k, v) - } - return result - case any: - switch ctype := ctype.(type) { - case []string: - return ctype - case []any: - return ctype - } - } - return []any{} -} diff --git a/pkg/iac/scanners/azure/functions/array_test.go b/pkg/iac/scanners/azure/functions/array_test.go deleted file mode 100644 index f4396fc4093a..000000000000 --- a/pkg/iac/scanners/azure/functions/array_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Array(t *testing.T) { - test := []struct { - name string - input []any - expected any - }{ - { - name: "array from an int", - input: []any{1}, - expected: []int{1}, - }, - { - name: "array from a string", - input: []any{"hello"}, - expected: []string{"hello"}, - }, - { - name: "array from a map", - input: []any{map[string]any{"hello": "world"}}, - expected: []any{"hello", "world"}, - }, - { - name: "array from an slice", - input: []any{ - []string{"hello", "world"}, - }, - expected: []string{"hello", "world"}, - }, - } - for _, tt := range test { - t.Run(tt.name, func(t *testing.T) { - actual := Array(tt.input...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/base64.go b/pkg/iac/scanners/azure/functions/base64.go deleted file mode 100644 index 46b784bb0f96..000000000000 --- a/pkg/iac/scanners/azure/functions/base64.go +++ /dev/null @@ -1,52 +0,0 @@ -package functions - -import ( - "encoding/base64" - "encoding/json" -) - -func Base64(args ...any) any { - - if len(args) == 0 { - return nil - } - - input := args[0].(string) - - return base64.StdEncoding.EncodeToString([]byte(input)) -} - -func Base64ToString(args ...any) any { - if len(args) == 0 { - return nil - } - - input := args[0].(string) - - result, err := base64.StdEncoding.DecodeString(input) - if err != nil { - return "" - } - return string(result) -} - -func Base64ToJson(args ...any) any { - - if len(args) == 0 { - return nil - } - - input := args[0].(string) - - decoded, err := base64.StdEncoding.DecodeString(input) - if err != nil { - return nil - } - - var result map[string]any - - if err := json.Unmarshal(decoded, &result); err != nil { - return nil - } - return result -} diff --git a/pkg/iac/scanners/azure/functions/base64_test.go b/pkg/iac/scanners/azure/functions/base64_test.go deleted file mode 100644 index 648617ee40eb..000000000000 --- a/pkg/iac/scanners/azure/functions/base64_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package functions - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Base64Call(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "simple base64 call", - args: []any{ - "hello, world", - }, - expected: "aGVsbG8sIHdvcmxk", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Base64(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} - -func Test_Base64ToStringCall(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "simple base64ToString call", - args: []any{ - "aGVsbG8sIHdvcmxk", - }, - expected: "hello, world", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Base64ToString(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} - -func Test_Base64ToJsonCall(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "simple base64ToJson call", - args: []any{ - "eyJoZWxsbyI6ICJ3b3JsZCJ9", - }, - expected: `{"hello":"world"}`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Base64ToJson(tt.args...) - - actualContent, err := json.Marshal(actual) - require.NoError(t, err) - - assert.Equal(t, tt.expected, string(actualContent)) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/bool.go b/pkg/iac/scanners/azure/functions/bool.go deleted file mode 100644 index cb3031dba4cb..000000000000 --- a/pkg/iac/scanners/azure/functions/bool.go +++ /dev/null @@ -1,20 +0,0 @@ -package functions - -import "strings" - -func Bool(args ...any) any { - if len(args) != 1 { - return false - } - - switch input := args[0].(type) { - case bool: - return input - case string: - input = strings.ToLower(input) - return input == "true" || input == "1" || input == "yes" || input == "on" - case int: - return input == 1 - } - return false -} diff --git a/pkg/iac/scanners/azure/functions/bool_test.go b/pkg/iac/scanners/azure/functions/bool_test.go deleted file mode 100644 index 054dd52776ad..000000000000 --- a/pkg/iac/scanners/azure/functions/bool_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Bool(t *testing.T) { - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "Bool with true", - args: []any{true}, - expected: true, - }, - { - name: "Bool with false", - args: []any{false}, - expected: false, - }, - { - name: "Bool with 1", - args: []any{1}, - expected: true, - }, - { - name: "Bool with 0", - args: []any{0}, - expected: false, - }, - { - name: "Bool with true string", - args: []any{"true"}, - expected: true, - }, - { - name: "Bool with false string", - args: []any{"false"}, - expected: false, - }, - { - name: "Bool with 1 string", - args: []any{"1"}, - expected: true, - }, - { - name: "Bool with 0 string", - args: []any{"0"}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Bool(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/casing.go b/pkg/iac/scanners/azure/functions/casing.go deleted file mode 100644 index 2df433a5ade2..000000000000 --- a/pkg/iac/scanners/azure/functions/casing.go +++ /dev/null @@ -1,29 +0,0 @@ -package functions - -import "strings" - -func ToLower(args ...any) any { - if len(args) != 1 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - return strings.ToLower(input) -} - -func ToUpper(args ...any) any { - if len(args) != 1 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - return strings.ToUpper(input) -} diff --git a/pkg/iac/scanners/azure/functions/casing_test.go b/pkg/iac/scanners/azure/functions/casing_test.go deleted file mode 100644 index d94fca7ab050..000000000000 --- a/pkg/iac/scanners/azure/functions/casing_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_ToLower(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "lowercase a string", - args: []any{ - "HELLO", - }, - expected: "hello", - }, - { - name: "lowercase a string with a non-string input", - args: []any{ - 10, - }, - expected: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := ToLower(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} - -func Test_ToUpper(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "uppercase a string", - args: []any{ - "hello", - }, - expected: "HELLO", - }, - { - name: "uppercase a string with a non-string input", - args: []any{ - 10, - }, - expected: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := ToUpper(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/coalesce.go b/pkg/iac/scanners/azure/functions/coalesce.go deleted file mode 100644 index cec7220a83ba..000000000000 --- a/pkg/iac/scanners/azure/functions/coalesce.go +++ /dev/null @@ -1,10 +0,0 @@ -package functions - -func Coalesce(args ...any) any { - for _, arg := range args { - if arg != nil { - return arg - } - } - return nil -} diff --git a/pkg/iac/scanners/azure/functions/coalesce_test.go b/pkg/iac/scanners/azure/functions/coalesce_test.go deleted file mode 100644 index b0d0459bde54..000000000000 --- a/pkg/iac/scanners/azure/functions/coalesce_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Coalesce(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "coalesce with nil", - args: []any{ - nil, - }, - expected: nil, - }, - { - name: "coalesce with nil and string", - args: []any{ - nil, - "test", - }, - expected: "test", - }, - { - name: "coalesce with nil and string and int", - args: []any{ - nil, - "test", - 1, - }, - expected: "test", - }, - { - name: "coalesce with nil and nil and array", - args: []any{ - nil, - nil, - []any{"a", "b", "c"}, - }, - expected: []any{"a", "b", "c"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Coalesce(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/concat.go b/pkg/iac/scanners/azure/functions/concat.go deleted file mode 100644 index ac15c1e0097d..000000000000 --- a/pkg/iac/scanners/azure/functions/concat.go +++ /dev/null @@ -1,28 +0,0 @@ -package functions - -import ( - "fmt" -) - -func Concat(args ...any) any { - - switch args[0].(type) { - case string: - var result string - for _, arg := range args { - result += fmt.Sprintf("%v", arg) - } - return result - case any: - var result []any - for _, arg := range args { - argArr, ok := arg.([]any) - if !ok { - continue - } - result = append(result, argArr...) - } - return result - } - return "" -} diff --git a/pkg/iac/scanners/azure/functions/concat_test.go b/pkg/iac/scanners/azure/functions/concat_test.go deleted file mode 100644 index 0418058c483c..000000000000 --- a/pkg/iac/scanners/azure/functions/concat_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_StringConcatenation(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "simple string concatenation", - args: []any{ - "hello", - ", ", - "world", - "!", - }, - expected: "hello, world!", - }, - { - name: "string concatenation with non strings", - args: []any{ - "pi to 3 decimal places is ", - 3.142, - }, - expected: "pi to 3 decimal places is 3.142", - }, - { - name: "string concatenation with multiple primitives", - args: []any{ - "to say that ", - 3, - " is greater than ", - 5, - " would be ", - false, - }, - expected: "to say that 3 is greater than 5 would be false", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - concatenated := Concat(tt.args...) - require.Equal(t, tt.expected, concatenated) - }) - } -} - -func Test_ArrayConcatenation(t *testing.T) { - tests := []struct { - name string - args []any - expected []any - }{ - { - name: "simple array concatenation", - args: []any{ - []any{1, 2, 3}, - []any{4, 5, 6}, - }, - expected: []any{1, 2, 3, 4, 5, 6}, - }, - { - name: "array concatenation with non arrays", - args: []any{ - []any{1, 2, 3}, - 4, - }, - expected: []any{1, 2, 3}, - }, - { - name: "array concatenation with multiple primitives", - args: []any{ - []any{1, 2, 3}, - 4, - []any{5, 6, 7}, - }, - expected: []any{1, 2, 3, 5, 6, 7}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - concatenated := Concat(tt.args...) - require.Equal(t, tt.expected, concatenated) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/contains.go b/pkg/iac/scanners/azure/functions/contains.go deleted file mode 100644 index 3d735c95c210..000000000000 --- a/pkg/iac/scanners/azure/functions/contains.go +++ /dev/null @@ -1,40 +0,0 @@ -package functions - -import ( - "fmt" - "strings" -) - -func Contains(args ...any) any { - - if len(args) != 2 { - return false - } - - container := args[0] - itemToFind := args[1] - - switch cType := container.(type) { - case string: - switch iType := itemToFind.(type) { - case string: - return strings.Contains(strings.ToLower(cType), strings.ToLower(iType)) - case int, int32, int64, uint, uint32, uint64: - return strings.Contains(strings.ToLower(cType), fmt.Sprintf("%d", iType)) - } - case []any: - for _, item := range cType { - if item == itemToFind { - return true - } - } - case map[string]any: - for key := range cType { - if key == itemToFind { - return true - } - } - } - - return false -} diff --git a/pkg/iac/scanners/azure/functions/contains_test.go b/pkg/iac/scanners/azure/functions/contains_test.go deleted file mode 100644 index 37fd793cdeff..000000000000 --- a/pkg/iac/scanners/azure/functions/contains_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_Contains(t *testing.T) { - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "simple true string contains", - args: []any{ - "hello, world", - "hell", - }, - expected: true, - }, - { - name: "simple false string contains", - args: []any{ - "hello, world", - "help", - }, - expected: false, - }, - { - name: "simple true string contains with case sensitivity", - args: []any{ - "hello, world", - "HELL", - }, - expected: true, - }, - { - name: "simple true string contains with number", - args: []any{ - "You're my number 1", - 1, - }, - expected: true, - }, - { - name: "true object contains key", - args: []any{ - map[string]any{ - "hello": "world", - }, - "hello", - }, - expected: true, - }, - { - name: "false object contains key", - args: []any{ - map[string]any{ - "hello": "world", - }, - "world", - }, - expected: false, - }, - { - name: "true array contains value", - args: []any{ - []any{ - "hello", "world", - }, - "hello", - }, - expected: true, - }, - { - name: "false array contains value", - args: []any{ - []any{ - "hello", "world", - }, - "help", - }, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - doesContain := Contains(tt.args...) - require.Equal(t, tt.expected, doesContain) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/copy_index.go b/pkg/iac/scanners/azure/functions/copy_index.go deleted file mode 100644 index 777d9a6237f6..000000000000 --- a/pkg/iac/scanners/azure/functions/copy_index.go +++ /dev/null @@ -1,25 +0,0 @@ -package functions - -var loopCounter = make(map[string]int) - -func CopyIndex(args ...any) any { - loopName := "default" - offset := 1 - if len(args) > 0 { - if providedLoopName, ok := args[0].(string); ok { - loopName = providedLoopName - } - } - if len(args) > 1 { - if providedOffset, ok := args[1].(int); ok { - offset = providedOffset - } - } - - if _, ok := loopCounter[loopName]; !ok { - loopCounter[loopName] = 0 - } - - loopCounter[loopName] += offset - return loopCounter[loopName] -} diff --git a/pkg/iac/scanners/azure/functions/copy_index_test.go b/pkg/iac/scanners/azure/functions/copy_index_test.go deleted file mode 100644 index c161d2596223..000000000000 --- a/pkg/iac/scanners/azure/functions/copy_index_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package functions - -import "testing" - -func Test_CopyIndex(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "CopyIndex with 1", - args: []any{}, - expected: 1, - }, - { - name: "CopyIndex with 2", - args: []any{}, - expected: 2, - }, - { - name: "CopyIndex with 3", - args: []any{}, - expected: 3, - }, - { - name: "CopyIndex with loopName", - args: []any{"loop1"}, - expected: 1, - }, - { - name: "CopyIndex with same lo" + - "opName", - args: []any{"loop1"}, - expected: 2, - }, - { - name: "CopyIndex with loopName", - args: []any{"loop2", 10}, - expected: 10, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := CopyIndex(tt.args...) - if got != tt.expected { - t.Errorf("CopyIndex() = %v, want %v", got, tt.expected) - } - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/create_array.go b/pkg/iac/scanners/azure/functions/create_array.go deleted file mode 100644 index ad92670e10be..000000000000 --- a/pkg/iac/scanners/azure/functions/create_array.go +++ /dev/null @@ -1,11 +0,0 @@ -package functions - -func CreateArray(args ...any) any { - var result []any - if len(args) == 0 { - return result - } - - result = append(result, args...) - return result -} diff --git a/pkg/iac/scanners/azure/functions/create_array_test.go b/pkg/iac/scanners/azure/functions/create_array_test.go deleted file mode 100644 index d2d6062b9bd5..000000000000 --- a/pkg/iac/scanners/azure/functions/create_array_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_CreateArray(t *testing.T) { - - tests := []struct { - name string - args []any - expected any - }{ - { - name: "create array with strings", - args: []any{ - "Hello", - "World", - }, - expected: []any{"Hello", "World"}, - }, - { - name: "create array with ints", - - args: []any{ - 1, 2, 3, - }, - expected: []any{1, 2, 3}, - }, - { - name: "create array with arrays", - args: []any{ - []any{1, 2, 3}, - []any{4, 5, 6}, - }, - expected: []any{[]any{1, 2, 3}, []any{4, 5, 6}}, - }, - { - name: "create arrau with maps", - args: []any{ - map[string]any{ - "one": "a", - }, - map[string]any{ - "two": "b", - }, - }, - expected: []any{ - map[string]any{ - "one": "a", - }, - map[string]any{ - "two": "b", - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := CreateArray(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/create_object.go b/pkg/iac/scanners/azure/functions/create_object.go deleted file mode 100644 index 48a0ebafb673..000000000000 --- a/pkg/iac/scanners/azure/functions/create_object.go +++ /dev/null @@ -1,21 +0,0 @@ -package functions - -func CreateObject(args ...any) any { - obj := make(map[string]any) - if len(args) == 0 { - return obj - } - - // if there aren't even pairs then return an empty object - if len(args)%2 != 0 { - return obj - } - - for i := 0; i < len(args); i += 2 { - key := args[i].(string) - value := args[i+1] - obj[key] = value - } - - return obj -} diff --git a/pkg/iac/scanners/azure/functions/create_object_test.go b/pkg/iac/scanners/azure/functions/create_object_test.go deleted file mode 100644 index 10cc72d52ede..000000000000 --- a/pkg/iac/scanners/azure/functions/create_object_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_CreateObject(t *testing.T) { - - tests := []struct { - name string - args []any - expected any - }{ - { - name: "CreateObject with no args", - args: []any{}, - expected: make(map[string]any), - }, - { - name: "CreateObject with one arg", - args: []any{"foo", "bar"}, - expected: map[string]any{"foo": "bar"}, - }, - { - name: "CreateObject with two args", - args: []any{"foo", "bar", "baz", "qux"}, - expected: map[string]any{"foo": "bar", "baz": "qux"}, - }, - { - name: "CreateObject with three args", - args: []any{"foo", "bar", "baz", 1, "quux", true}, - expected: map[string]any{"foo": "bar", "baz": 1, "quux": true}, - }, - { - name: "CreateObject with odd number of args", - args: []any{"foo", "bar", "baz"}, - expected: make(map[string]any), - }, - { - name: "CreateObject with odd number of args", - args: []any{"foo", "bar", "baz", []string{"Hello", "World"}}, - expected: map[string]any{ - "foo": "bar", - "baz": []string{ - "Hello", "World", - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := CreateObject(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/data_uri.go b/pkg/iac/scanners/azure/functions/data_uri.go deleted file mode 100644 index 3b147b568f09..000000000000 --- a/pkg/iac/scanners/azure/functions/data_uri.go +++ /dev/null @@ -1,36 +0,0 @@ -package functions - -import ( - "fmt" - "strings" -) - -func DataUri(args ...any) any { - if len(args) == 0 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - return fmt.Sprintf("data:text/plain;charset=utf8;base64,%s", Base64(input)) -} - -func DataUriToString(args ...any) any { - if len(args) == 0 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - parts := strings.Split(input, "base64,") - if len(parts) != 2 { - return "" - } - - return Base64ToString(parts[1]) -} diff --git a/pkg/iac/scanners/azure/functions/data_uri_test.go b/pkg/iac/scanners/azure/functions/data_uri_test.go deleted file mode 100644 index 87f8a16de609..000000000000 --- a/pkg/iac/scanners/azure/functions/data_uri_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_data_uri_from_string(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "data uri from string", - args: []any{ - "Hello", - }, - expected: "data:text/plain;charset=utf8;base64,SGVsbG8=", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dataUri := DataUri(tt.args...) - require.Equal(t, tt.expected, dataUri) - }) - } -} - -func Test_string_from_data_uri(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "data uri to string", - args: []any{ - "data:;base64,SGVsbG8sIFdvcmxkIQ==", - }, - expected: "Hello, World!", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dataUri := DataUriToString(tt.args...) - require.Equal(t, tt.expected, dataUri) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/date_time_add.go b/pkg/iac/scanners/azure/functions/date_time_add.go deleted file mode 100644 index 2e3c06ae8220..000000000000 --- a/pkg/iac/scanners/azure/functions/date_time_add.go +++ /dev/null @@ -1,116 +0,0 @@ -package functions - -import ( - "errors" - "fmt" - "regexp" - "strconv" - "time" -) - -var pattern = regexp.MustCompile(`^P((?P\d+)Y)?((?P\d+)M)?((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$`) - -func DateTimeAdd(args ...any) any { - if len(args) < 2 { - return nil - } - - base, ok := args[0].(string) - if !ok { - return nil - } - - format := time.RFC3339 - if len(args) == 3 { - if providedFormat, ok := args[2].(string); ok { - format = convertFormat(providedFormat) - } - - } - - baseTime, err := time.Parse(format, base) - if err != nil { - return nil - } - - duration, err := parseISO8601(args[1].(string)) - if err != nil { - return nil - } - - timeDuration := duration.timeDuration() - baseTime = baseTime.Add(timeDuration) - - if ok { - return baseTime.Format(format) - } - - return baseTime.Format(time.RFC3339) -} - -type Iso8601Duration struct { - Y int - M int - W int - D int - // Time Component - TH int - TM int - TS int -} - -func parseISO8601(from string) (Iso8601Duration, error) { - var match []string - var d Iso8601Duration - - if pattern.MatchString(from) { - match = pattern.FindStringSubmatch(from) - } else { - return d, errors.New("could not parse duration string") - } - - for i, name := range pattern.SubexpNames() { - part := match[i] - if i == 0 || name == "" || part == "" { - continue - } - - val, err := strconv.Atoi(part) - if err != nil { - return d, err - } - switch name { - case "year": - d.Y = val - case "month": - d.M = val - case "week": - d.W = val - case "day": - d.D = val - case "hour": - d.TH = val - case "minute": - d.TM = val - case "second": - d.TS = val - default: - return d, fmt.Errorf("unknown field %s", name) - } - } - - return d, nil -} - -func (d Iso8601Duration) timeDuration() time.Duration { - var dur time.Duration - dur += time.Duration(d.TH) * time.Hour - dur += time.Duration(d.TM) * time.Minute - dur += time.Duration(d.TS) * time.Second - dur += time.Duration(d.D) * 24 * time.Hour - dur += time.Duration(d.W) * 7 * 24 * time.Hour - dur += time.Duration(d.M) * 30 * 24 * time.Hour - dur += time.Duration(d.Y) * 365 * 24 * time.Hour - - return dur -} diff --git a/pkg/iac/scanners/azure/functions/date_time_epoch.go b/pkg/iac/scanners/azure/functions/date_time_epoch.go deleted file mode 100644 index 5c42a4e25baa..000000000000 --- a/pkg/iac/scanners/azure/functions/date_time_epoch.go +++ /dev/null @@ -1,38 +0,0 @@ -package functions - -import ( - "time" - - smithyTime "github.com/aws/smithy-go/time" -) - -func DateTimeFromEpoch(args ...any) any { - if len(args) != 1 { - return nil - } - - epoch, ok := args[0].(int) - if !ok { - return nil - } - - return smithyTime.ParseEpochSeconds(float64(epoch)).Format(time.RFC3339) -} - -func DateTimeToEpoch(args ...any) any { - if len(args) != 1 { - return nil - } - - dateTime, ok := args[0].(string) - if !ok { - return nil - } - - parsed, err := time.Parse(time.RFC3339, dateTime) - if err != nil { - return nil - } - - return int(parsed.Unix()) -} diff --git a/pkg/iac/scanners/azure/functions/date_time_epoch_test.go b/pkg/iac/scanners/azure/functions/date_time_epoch_test.go deleted file mode 100644 index 1df45badd7cc..000000000000 --- a/pkg/iac/scanners/azure/functions/date_time_epoch_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_DateTimeFromEpoch(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "datetime from epoch", - args: []any{ - 1683040573, - }, - expected: "2023-05-02T15:16:13Z", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := DateTimeFromEpoch(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} - -func Test_DateTimeToEpoch(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "datetime to epoch", - args: []any{ - "2023-05-02T15:16:13Z", - }, - expected: 1683040573, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := DateTimeToEpoch(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/datetime_add_test.go b/pkg/iac/scanners/azure/functions/datetime_add_test.go deleted file mode 100644 index 3334323bf09c..000000000000 --- a/pkg/iac/scanners/azure/functions/datetime_add_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_DateTimeAdd(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - - { - name: "datetime add 1 years", - args: []any{ - "2010-01-01T00:00:00Z", - "P1Y", - }, - expected: "2011-01-01T00:00:00Z", - }, - { - name: "datetime add 3 months", - args: []any{ - "2010-01-01T00:00:00Z", - "P3M", - }, - expected: "2010-04-01T00:00:00Z", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := DateTimeAdd(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} - -func Test_ISO8601DurationParse(t *testing.T) { - tests := []struct { - name string - args string - expected Iso8601Duration - }{ - - { - name: "parse 1 year", - args: "P1Y", - expected: Iso8601Duration{Y: 1}, - }, - { - name: "parse 3 months", - args: "P3M", - expected: Iso8601Duration{M: 3}, - }, - { - name: "parse 12 hours", - args: "PT12H", - expected: Iso8601Duration{TH: 12}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual, err := parseISO8601(tt.args) - require.NoError(t, err) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/deployment.go b/pkg/iac/scanners/azure/functions/deployment.go deleted file mode 100644 index bde37b4915ff..000000000000 --- a/pkg/iac/scanners/azure/functions/deployment.go +++ /dev/null @@ -1,75 +0,0 @@ -package functions - -type DeploymentData interface { - GetParameter(name string) any - GetVariable(variableName string) any - GetEnvVariable(envVariableName string) any -} - -func Deployment(deploymentProvider DeploymentData, args ...any) any { - - /* - - { - "name": "", - "properties": { - "templateLink": { - "uri": "" - }, - "template": { - "$schema": "", - "contentVersion": "", - "parameters": {}, - "variables": {}, - "resources": [], - "outputs": {} - }, - "templateHash": "", - "parameters": {}, - "mode": "", - "provisioningState": "" - } - } - - */ - - return nil -} - -func Environment(envProvider DeploymentData, args ...any) any { - if len(args) == 0 { - return nil - } - - envVarName, ok := args[0].(string) - if !ok { - return nil - } - return envProvider.GetEnvVariable(envVarName) -} - -func Variables(varProvider DeploymentData, args ...any) any { - if len(args) == 0 { - return nil - } - - varName, ok := args[0].(string) - if !ok { - return nil - } - return varProvider.GetVariable(varName) -} - -func Parameters(paramProvider DeploymentData, args ...any) any { - if len(args) == 0 { - return nil - } - - paramName, ok := args[0].(string) - if !ok { - return nil - } - - return paramProvider.GetParameter(paramName) - -} diff --git a/pkg/iac/scanners/azure/functions/div.go b/pkg/iac/scanners/azure/functions/div.go deleted file mode 100644 index 2890e3f8c89c..000000000000 --- a/pkg/iac/scanners/azure/functions/div.go +++ /dev/null @@ -1,15 +0,0 @@ -package functions - -func Div(args ...any) any { - - if len(args) != 2 { - return nil - } - - if a, ok := args[0].(int); ok { - if b, ok := args[1].(int); ok { - return a / b - } - } - return nil -} diff --git a/pkg/iac/scanners/azure/functions/div_test.go b/pkg/iac/scanners/azure/functions/div_test.go deleted file mode 100644 index 55a429908bcb..000000000000 --- a/pkg/iac/scanners/azure/functions/div_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Div(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "Div 2 by 1", - args: []any{2, 1}, - expected: 2, - }, - { - name: "Div 4 by 2", - args: []any{4, 2}, - expected: 2, - }, - { - name: "Div 6 by 2", - args: []any{6, 2}, - expected: 3, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Div(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/empty.go b/pkg/iac/scanners/azure/functions/empty.go deleted file mode 100644 index 96848b9dcd5a..000000000000 --- a/pkg/iac/scanners/azure/functions/empty.go +++ /dev/null @@ -1,33 +0,0 @@ -package functions - -func Empty(args ...any) any { - - if len(args) != 1 { - return false - } - - container := args[0] - - switch cType := container.(type) { - case string: - return cType == "" - case map[string]any: - return len(cType) == 0 - case any: - switch iType := cType.(type) { - case []string: - return len(iType) == 0 - case []bool: - return len(iType) == 0 - case []int: - return len(iType) == 0 - case []float64: - return len(iType) == 0 - case map[string]any: - return len(iType) == 0 - } - - } - - return false -} diff --git a/pkg/iac/scanners/azure/functions/empty_test.go b/pkg/iac/scanners/azure/functions/empty_test.go deleted file mode 100644 index e325f3c80447..000000000000 --- a/pkg/iac/scanners/azure/functions/empty_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_Empty(t *testing.T) { - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "string is empty", - args: []any{ - "", - }, - expected: true, - }, - { - name: "string is not empty", - args: []any{ - "hello, world", - }, - expected: false, - }, - { - name: "array is empty", - args: []any{ - []string{}, - }, - expected: true, - }, - { - name: "array is not empty", - args: []any{ - []string{"Hello", "World"}, - }, - expected: false, - }, - { - name: "map is empty", - args: []any{ - make(map[string]any), - }, - expected: true, - }, - { - name: "map is not empty", - args: []any{ - map[string]any{ - "hello": "world", - }, - "world", - }, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - doesContain := Empty(tt.args...) - require.Equal(t, tt.expected, doesContain) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/ends_with.go b/pkg/iac/scanners/azure/functions/ends_with.go deleted file mode 100644 index 1ca6b099657f..000000000000 --- a/pkg/iac/scanners/azure/functions/ends_with.go +++ /dev/null @@ -1,22 +0,0 @@ -package functions - -import "strings" - -func EndsWith(args ...any) any { - - if len(args) != 2 { - return false - } - - stringToSearch, ok := args[0].(string) - if !ok { - return false - } - - stringToFind, ok := args[1].(string) - if !ok { - return false - } - - return strings.HasSuffix(stringToSearch, stringToFind) -} diff --git a/pkg/iac/scanners/azure/functions/ends_with_test.go b/pkg/iac/scanners/azure/functions/ends_with_test.go deleted file mode 100644 index d8703d6c6493..000000000000 --- a/pkg/iac/scanners/azure/functions/ends_with_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_EndsWith(t *testing.T) { - - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "string ends with", - args: []any{ - "Hello world!", - "world!", - }, - expected: true, - }, - { - name: "string does not end with", - args: []any{ - "Hello world!", - "world", - }, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := EndsWith(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/equals.go b/pkg/iac/scanners/azure/functions/equals.go deleted file mode 100644 index 2034e18bc0c2..000000000000 --- a/pkg/iac/scanners/azure/functions/equals.go +++ /dev/null @@ -1,25 +0,0 @@ -package functions - -func Equals(args ...any) any { - if len(args) != 2 { - return false - } - - slice1, ok := args[0].([]any) - if ok { - slice2, ok := args[1].([]any) - if ok { - if len(slice1) != len(slice2) { - return false - } - for i := 0; i < len(slice1); i++ { - if slice1[i] != slice2[i] { - return false - } - } - return true - } - } - - return args[0] == args[1] -} diff --git a/pkg/iac/scanners/azure/functions/equals_test.go b/pkg/iac/scanners/azure/functions/equals_test.go deleted file mode 100644 index afee2c3d92a1..000000000000 --- a/pkg/iac/scanners/azure/functions/equals_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Equals(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "equals with nil", - args: []any{ - nil, - }, - expected: false, - }, - { - name: "equals with nil and string", - args: []any{ - nil, - "test", - }, - expected: false, - }, - { - name: "equals with nil and string and int", - args: []any{ - nil, - "test", - 1, - }, - expected: false, - }, - { - name: "equals with nil and nil and array", - args: []any{ - nil, - nil, - []any{"a", "b", "c"}, - }, - expected: false, - }, - { - name: "equals with nil and nil", - args: []any{ - nil, - nil, - }, - expected: true, - }, - { - name: "equals with string and string", - args: []any{ - "test", - "test", - }, - expected: true, - }, - { - name: "equals with string and string", - args: []any{ - "test", - "test1", - }, - expected: false, - }, - { - name: "equals with int and int", - args: []any{ - 1, - 1, - }, - expected: true, - }, - { - name: "equals with int and int", - args: []any{ - 1, - 2, - }, - expected: false, - }, - { - name: "equals with array and array", - args: []any{ - []any{"a", "b", "c"}, - []any{"a", "b", "c"}, - }, - expected: true, - }, - { - name: "equals with array and array", - args: []any{ - []any{"a", "b", "c"}, - []any{"a", "b", "d"}, - }, - expected: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Equals(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/false.go b/pkg/iac/scanners/azure/functions/false.go deleted file mode 100644 index 267cb936846e..000000000000 --- a/pkg/iac/scanners/azure/functions/false.go +++ /dev/null @@ -1,5 +0,0 @@ -package functions - -func False(args ...any) any { - return false -} diff --git a/pkg/iac/scanners/azure/functions/first.go b/pkg/iac/scanners/azure/functions/first.go deleted file mode 100644 index 83bd61eba20c..000000000000 --- a/pkg/iac/scanners/azure/functions/first.go +++ /dev/null @@ -1,37 +0,0 @@ -package functions - -func First(args ...any) any { - if len(args) != 1 { - return "" - } - - container := args[0] - - switch cType := container.(type) { - case string: - if cType != "" { - return string(cType[0]) - } - case any: - switch iType := cType.(type) { - case []string: - if len(iType) > 0 { - return iType[0] - } - case []bool: - if len(iType) > 0 { - return iType[0] - } - case []int: - if len(iType) > 0 { - return iType[0] - } - case []float64: - if len(iType) > 0 { - return iType[0] - } - } - } - - return "" -} diff --git a/pkg/iac/scanners/azure/functions/first_test.go b/pkg/iac/scanners/azure/functions/first_test.go deleted file mode 100644 index 690cd3d1f77d..000000000000 --- a/pkg/iac/scanners/azure/functions/first_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_First(t *testing.T) { - test := []struct { - name string - args []any - expected any - }{ - { - name: "first in empty string", - args: []any{ - "", - }, - expected: "", - }, - { - name: "first in string", - args: []any{ - "Hello", - }, - expected: "H", - }, - { - name: "first in empty slice", - args: []any{ - []string{}, - }, - expected: "", - }, - { - name: "first in slice", - args: []any{ - []string{"Hello", "World"}, - }, - expected: "Hello", - }, - } - - for _, tt := range test { - t.Run(tt.name, func(t *testing.T) { - actual := First(tt.args...) - require.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/float.go b/pkg/iac/scanners/azure/functions/float.go deleted file mode 100644 index d7a1acfcdfac..000000000000 --- a/pkg/iac/scanners/azure/functions/float.go +++ /dev/null @@ -1,20 +0,0 @@ -package functions - -import "strconv" - -func Float(args ...any) any { - if len(args) != 1 { - return 0.0 - } - if a, ok := args[0].(int); ok { - return float64(a) - } - if a, ok := args[0].(string); ok { - f, err := strconv.ParseFloat(a, 64) - if err != nil { - return 0.0 - } - return f - } - return 0.0 -} diff --git a/pkg/iac/scanners/azure/functions/float_test.go b/pkg/iac/scanners/azure/functions/float_test.go deleted file mode 100644 index 63610183bcdd..000000000000 --- a/pkg/iac/scanners/azure/functions/float_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package functions - -import "testing" - -func Test_Float(t *testing.T) { - tests := []struct { - name string - args []any - expected float64 - }{ - { - name: "Float with 1", - args: []any{1}, - expected: 1.0, - }, - { - name: "Float with 2", - args: []any{"2"}, - expected: 2.0, - }, - { - name: "Float with 3", - args: []any{"2.3"}, - expected: 2.3, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Float(tt.args...) - if got != tt.expected { - t.Errorf("Float() = %v, want %v", got, tt.expected) - } - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/format.go b/pkg/iac/scanners/azure/functions/format.go deleted file mode 100644 index 48395f374aa6..000000000000 --- a/pkg/iac/scanners/azure/functions/format.go +++ /dev/null @@ -1,31 +0,0 @@ -package functions - -import ( - "fmt" - "strings" -) - -func Format(args ...any) any { - formatter := generateFormatterString(args...) - - return fmt.Sprintf(formatter, args[1:]...) -} - -func generateFormatterString(args ...any) string { - - formatter, ok := args[0].(string) - if !ok { - return "" - } - for i, arg := range args[1:] { - switch arg.(type) { - case string: - formatter = strings.ReplaceAll(formatter, fmt.Sprintf("{%d}", i), "%s") - case int, int32, int64, uint, uint32, uint64: - formatter = strings.ReplaceAll(formatter, fmt.Sprintf("{%d}", i), "%d") - case float64, float32: - formatter = strings.ReplaceAll(formatter, fmt.Sprintf("{%d}", i), "%f") - } - } - return formatter -} diff --git a/pkg/iac/scanners/azure/functions/format_test.go b/pkg/iac/scanners/azure/functions/format_test.go deleted file mode 100644 index 34a0138d1f0a..000000000000 --- a/pkg/iac/scanners/azure/functions/format_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_FormatCall(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "simple format call", - args: []any{ - "{0}/{1}", - "myPostgreSQLServer", - "log_checkpoints", - }, - expected: "myPostgreSQLServer/log_checkpoints", - }, - { - name: "complex format call", - args: []any{ - "{0} + {1} = {2}", - 1, 2, 3, - }, - expected: "1 + 2 = 3", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Format(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/functions.go b/pkg/iac/scanners/azure/functions/functions.go deleted file mode 100644 index f24112d80953..000000000000 --- a/pkg/iac/scanners/azure/functions/functions.go +++ /dev/null @@ -1,99 +0,0 @@ -package functions - -var deploymentFuncs = map[string]func(dp DeploymentData, args ...any) any{ - "parameters": Parameters, - "deployment": Deployment, - "environment": Environment, - "variables": Variables, -} -var generalFuncs = map[string]func(...any) any{ - - "add": Add, - "and": And, - "array": Array, - "base64": Base64, - "base64ToJson": Base64ToJson, - "bool": Bool, - "coalesce": Coalesce, - "concat": Concat, - "contains": Contains, - "copyIndex": CopyIndex, - "createArray": CreateArray, - "createObject": CreateObject, - "dataUri": DataUri, - "dataUriToString": DataUriToString, - "dateTimeAdd": DateTimeAdd, - "dateTimeFromEpoch": DateTimeFromEpoch, - "dateTimeToEpoch": DateTimeToEpoch, - "div": Div, - "empty": Empty, - "endsWith": EndsWith, - "equals": Equals, - "extensionResourceId": ExtensionResourceID, - "false": False, - "float": Float, - "format": Format, - "greater": Greater, - "greaterOrEquals": GreaterOrEquals, - "guid": Guid, - "if": If, - "indexOf": IndexOf, - "int": Int, - "intersection": Intersection, - "items": Items, - "join": Join, - "lastIndexOf": LastIndexOf, - "length": Length, - "less": Less, - "lessOrEquals": LessOrEquals, - // "list": List, - "managementGroup": ManagementGroup, - "managementGroupResourceId": ManagementGroupResourceID, - "max": Max, - "min": Min, - "mod": Mod, - "mul": Mul, - "newGuid": NewGuid, - "not": Not, - "null": Null, - "or": Or, - "padLeft": PadLeft, - "pickZones": PickZones, - "range": Range, - "reference": Reference, - "replace": Replace, - "resourceGroup": ResourceGroup, - "resourceId": ResourceID, - "skip": Skip, - "split": Split, - "startsWith": StartsWith, - "string": String, - "sub": Sub, - "subscription": Subscription, - "subscriptionResourceId": SubscriptionResourceID, - "substring": SubString, - "tenant": Tenant, - "tenantResourceId": TenantResourceID, - "toLower": ToLower, - "toUpper": ToUpper, - "trim": Trim, - "true": True, - "union": Union, - "union:": Union, - "uniqueString": UniqueString, - "uri": Uri, - "utcNow": UTCNow, -} - -func Evaluate(deploymentProvider DeploymentData, name string, args ...any) any { - - if f, ok := deploymentFuncs[name]; ok { - return f(deploymentProvider, args...) - } - - if f, ok := generalFuncs[name]; ok { - return f(args...) - } - - return nil -} diff --git a/pkg/iac/scanners/azure/functions/greater.go b/pkg/iac/scanners/azure/functions/greater.go deleted file mode 100644 index 458985862010..000000000000 --- a/pkg/iac/scanners/azure/functions/greater.go +++ /dev/null @@ -1,47 +0,0 @@ -package functions - -func Greater(args ...any) any { - - if len(args) != 2 { - return false - } - - switch arg0 := args[0].(type) { - case int: - arg1, ok := args[1].(int) - if ok { - return arg0 > arg1 - } - case string: - arg1, ok := args[1].(string) - if ok { - return arg0 > arg1 - } - } - - return false -} - -func GreaterOrEquals(args ...any) any { - - if len(args) != 2 { - return false - } - - switch arg0 := args[0].(type) { - case nil: - return args[1] == nil - case int: - arg1, ok := args[1].(int) - if ok { - return arg0 >= arg1 - } - case string: - arg1, ok := args[1].(string) - if ok { - return arg0 >= arg1 - } - } - - return false -} diff --git a/pkg/iac/scanners/azure/functions/greater_test.go b/pkg/iac/scanners/azure/functions/greater_test.go deleted file mode 100644 index ae5fd1c72a7f..000000000000 --- a/pkg/iac/scanners/azure/functions/greater_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Greater(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - - { - name: "greater with nil and string", - args: []any{ - nil, - "test", - }, - expected: false, - }, - { - name: "greater with nil and nil", - args: []any{ - nil, - nil, - }, - expected: false, - }, - { - name: "greater with string and string", - args: []any{ - "test", - "test", - }, - expected: false, - }, - { - name: "greater with string and int", - args: []any{ - "test", - 1, - }, - expected: false, - }, - { - name: "greater with int and int", - args: []any{ - 1, - 1, - }, - expected: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Greater(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} - -func Test_GreaterThanOrEqual(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - - { - name: "greater with nil and string", - args: []any{ - nil, - "test", - }, - expected: false, - }, - { - name: "greater with nil and nil", - args: []any{ - nil, - nil, - }, - expected: true, - }, - { - name: "greater with string and string", - args: []any{ - "test", - "test", - }, - expected: true, - }, - { - name: "greater with string and int", - args: []any{ - "test", - 1, - }, - expected: false, - }, - { - name: "greater with int and int", - args: []any{ - 1, - 1, - }, - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := GreaterOrEquals(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/guid.go b/pkg/iac/scanners/azure/functions/guid.go deleted file mode 100644 index 203accc93292..000000000000 --- a/pkg/iac/scanners/azure/functions/guid.go +++ /dev/null @@ -1,44 +0,0 @@ -package functions - -import ( - "crypto/sha256" - "strings" - - "github.com/google/uuid" -) - -func Guid(args ...any) any { - - if len(args) == 0 { - return "" - } - - hashParts := make([]string, len(args)) - for i, str := range args { - hashParts[i] = str.(string) - } - - guid, err := generateSeededGUID(hashParts...) - if err != nil { - return "" - } - - return guid.String() -} - -func generateSeededGUID(seedParts ...string) (uuid.UUID, error) { - var id uuid.UUID - - stringToHash := strings.Join(seedParts, "") - - hsha2 := sha256.Sum256([]byte(stringToHash)) - - copy(id[:], hsha2[:16]) - id[6] = (id[6] & 0x0f) | 0x40 // Version 4 - id[8] = (id[8] & 0x3f) | 0x80 // Variant is 10 - return id, nil -} - -func NewGuid(args ...any) any { - return uuid.NewString() -} diff --git a/pkg/iac/scanners/azure/functions/guid_test.go b/pkg/iac/scanners/azure/functions/guid_test.go deleted file mode 100644 index d29eed0ebd33..000000000000 --- a/pkg/iac/scanners/azure/functions/guid_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_Guid(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "guid from a string", - args: []any{ - "hello", - }, - expected: "2cf24dba-5fb0-430e-a6e8-3b2ac5b9e29e", - }, - { - name: "guid from an string", - args: []any{}, - expected: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - guid := Guid(tt.args...) - require.Equal(t, tt.expected, guid) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/if.go b/pkg/iac/scanners/azure/functions/if.go deleted file mode 100644 index 03cfa752c71f..000000000000 --- a/pkg/iac/scanners/azure/functions/if.go +++ /dev/null @@ -1,15 +0,0 @@ -package functions - -func If(args ...any) any { - - if len(args) != 3 { - return nil - } - - if condition, ok := args[0].(bool); ok { - if condition { - return args[1] - } - } - return args[2] -} diff --git a/pkg/iac/scanners/azure/functions/if_test.go b/pkg/iac/scanners/azure/functions/if_test.go deleted file mode 100644 index b13c040efaf1..000000000000 --- a/pkg/iac/scanners/azure/functions/if_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_If(t *testing.T) { - - tests := []struct { - name string - args []any - expected any - }{ - { - name: "If with true", - args: []any{true, "true", "false"}, - expected: "true", - }, - { - name: "If with false", - args: []any{false, "true", "false"}, - expected: "false", - }, - { - name: "If with true and slice returned", - args: []any{ - true, - []any{"Hello", "World"}, - []any{"Goodbye", "World"}, - }, - expected: []any{"Hello", "World"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := If(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/index_of.go b/pkg/iac/scanners/azure/functions/index_of.go deleted file mode 100644 index 25242f90fd7b..000000000000 --- a/pkg/iac/scanners/azure/functions/index_of.go +++ /dev/null @@ -1,22 +0,0 @@ -package functions - -import "strings" - -func IndexOf(args ...any) any { - - if len(args) != 2 { - return -1 - } - - stringToSearch, ok := args[0].(string) - if !ok { - return -1 - } - - stringToFind, ok := args[1].(string) - if !ok { - return -1 - } - - return strings.Index(stringToSearch, stringToFind) -} diff --git a/pkg/iac/scanners/azure/functions/index_of_test.go b/pkg/iac/scanners/azure/functions/index_of_test.go deleted file mode 100644 index 0e5214aaa8e8..000000000000 --- a/pkg/iac/scanners/azure/functions/index_of_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_IndexOf(t *testing.T) { - - tests := []struct { - name string - args []any - expected int - }{ - { - name: "get index of string that is there", - args: []any{ - "Hello world!", - "Hell", - }, - expected: 0, - }, - { - name: "get index of string that is there as well", - args: []any{ - "Hello world!", - "world", - }, - expected: 6, - }, - { - name: "get index of string that isn't there", - args: []any{ - "Hello world!", - "planet!", - }, - expected: -1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := IndexOf(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/int.go b/pkg/iac/scanners/azure/functions/int.go deleted file mode 100644 index 9d82077e5ab5..000000000000 --- a/pkg/iac/scanners/azure/functions/int.go +++ /dev/null @@ -1,20 +0,0 @@ -package functions - -import "strconv" - -func Int(args ...any) any { - if len(args) != 1 { - return 0 - } - if a, ok := args[0].(int); ok { - return a - } - if a, ok := args[0].(string); ok { - i, err := strconv.Atoi(a) - if err != nil { - return 0 - } - return i - } - return 0 -} diff --git a/pkg/iac/scanners/azure/functions/int_test.go b/pkg/iac/scanners/azure/functions/int_test.go deleted file mode 100644 index aa3e8cd07a13..000000000000 --- a/pkg/iac/scanners/azure/functions/int_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package functions - -import "testing" - -func Test_Int(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "Int with 1", - args: []any{1}, - expected: 1, - }, - { - name: "Int with 2", - args: []any{"2"}, - expected: 2, - }, - { - name: "Int with 2.3", - args: []any{"2.3"}, - expected: 0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Int(tt.args...) - if got != tt.expected { - t.Errorf("Int() = %v, want %v", got, tt.expected) - } - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/intersection.go b/pkg/iac/scanners/azure/functions/intersection.go deleted file mode 100644 index b813fa77544e..000000000000 --- a/pkg/iac/scanners/azure/functions/intersection.go +++ /dev/null @@ -1,76 +0,0 @@ -package functions - -import "sort" - -func Intersection(args ...any) any { - - if args == nil || len(args) < 2 { - return []any{} - } - - switch args[0].(type) { - case map[string]any: - return intersectionMap(args...) - case any: - return intersectionArray(args...) - } - - return []any{} -} - -func intersectionArray(args ...any) any { - var result []any - hash := make(map[any]bool) - - for _, arg := range args[0].([]any) { - hash[arg] = true - } - - for i := 1; i < len(args); i++ { - workingHash := make(map[any]bool) - argArr, ok := args[i].([]any) - if !ok { - continue - } - for _, item := range argArr { - if _, ok := hash[item]; ok { - workingHash[item] = true - } - } - hash = workingHash - } - - for k := range hash { - result = append(result, k) - } - - sort.Slice(result, func(i, j int) bool { - return result[i].(string) < result[j].(string) - }) - - return result -} - -func intersectionMap(args ...any) any { - hash := make(map[string]any) - - for k, v := range args[0].(map[string]any) { - hash[k] = v - } - - for i := 1; i < len(args); i++ { - workingHash := make(map[string]any) - argArr, ok := args[i].(map[string]any) - if !ok { - continue - } - for k, v := range argArr { - if ev, ok := hash[k]; ok && ev == v { - workingHash[k] = v - } - } - hash = workingHash - } - - return hash -} diff --git a/pkg/iac/scanners/azure/functions/intersection_test.go b/pkg/iac/scanners/azure/functions/intersection_test.go deleted file mode 100644 index 85b6b0564e30..000000000000 --- a/pkg/iac/scanners/azure/functions/intersection_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Intersect(t *testing.T) { - - tests := []struct { - name string - args []any - expected any - }{ - { - name: "intersect two arrays", - args: []any{ - []any{"a", "b", "c"}, - []any{"b", "c", "d"}, - }, - expected: []any{"b", "c"}, - }, - { - name: "intersect three arrays", - args: []any{ - []any{"a", "b", "c", "d"}, - []any{"b", "c", "d"}, - []any{"b", "c"}, - }, - expected: []any{"b", "c"}, - }, - { - name: "intersect two arrays with one empty", - args: []any{ - []any{"a", "b", "c"}, - []any{}, - }, - expected: []any(nil), - }, - { - name: "intersect two arrays with both empty", - args: []any{ - []any{}, - []any{}, - }, - expected: []any(nil), - }, - { - name: "intersect two arrays with both nil", - args: []any{ - nil, - nil, - }, - expected: []any{}, - }, - { - name: "intersect two maps", - args: []any{ - map[string]any{ - "a": "a", - "b": "b", - "c": "c", - }, - map[string]any{ - "b": "b", - "c": "c", - "d": "d", - }, - }, - expected: map[string]any{ - "b": "b", - "c": "c", - }, - }, - { - name: "intersect three maps", - args: []any{ - map[string]any{ - "a": "a", - "b": "b", - "c": "c", - }, - map[string]any{ - "b": "b", - "c": "c", - "d": "d", - }, - map[string]any{ - "b": "b", - "d": "d", - }, - }, - expected: map[string]any{ - "b": "b", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Intersection(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/items.go b/pkg/iac/scanners/azure/functions/items.go deleted file mode 100644 index 3f68d8a15ba7..000000000000 --- a/pkg/iac/scanners/azure/functions/items.go +++ /dev/null @@ -1,6 +0,0 @@ -package functions - -func Items(args ...any) any { - - return nil -} diff --git a/pkg/iac/scanners/azure/functions/join.go b/pkg/iac/scanners/azure/functions/join.go deleted file mode 100644 index 10fd76ee9f43..000000000000 --- a/pkg/iac/scanners/azure/functions/join.go +++ /dev/null @@ -1,22 +0,0 @@ -package functions - -import "strings" - -func Join(args ...any) any { - - if len(args) != 2 { - return "" - } - - container, ok := args[0].([]string) - if !ok { - return "" - } - - separator, ok := args[1].(string) - if !ok { - return "" - } - - return strings.Join(container, separator) -} diff --git a/pkg/iac/scanners/azure/functions/join_test.go b/pkg/iac/scanners/azure/functions/join_test.go deleted file mode 100644 index 614bdfa94ee5..000000000000 --- a/pkg/iac/scanners/azure/functions/join_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Join(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "join strings with no items", - args: []any{ - []string{}, - " ", - }, - expected: "", - }, - { - name: "join strings", - args: []any{ - []string{"Hello", "World"}, - " ", - }, - expected: "Hello World", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Join(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/json.go b/pkg/iac/scanners/azure/functions/json.go deleted file mode 100644 index 07f7c1828ae0..000000000000 --- a/pkg/iac/scanners/azure/functions/json.go +++ /dev/null @@ -1,20 +0,0 @@ -package functions - -import "encoding/json" - -func JSON(args ...any) any { - if len(args) != 1 { - return "" - } - - value, ok := args[0].(string) - if !ok { - return "" - } - - var jsonType map[string]any - if err := json.Unmarshal([]byte(value), &jsonType); err != nil { - return "" - } - return jsonType -} diff --git a/pkg/iac/scanners/azure/functions/json_test.go b/pkg/iac/scanners/azure/functions/json_test.go deleted file mode 100644 index 46f2285618cd..000000000000 --- a/pkg/iac/scanners/azure/functions/json_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_JSON(t *testing.T) { - - tests := []struct { - name string - args []any - expected map[string]any - }{ - { - name: "simple json string to json type", - args: []any{ - `{"hello": "world"}`, - }, - expected: map[string]any{ - "hello": "world", - }, - }, - { - name: "more complex json string to json type", - args: []any{ - `{"hello": ["world", "world2"]}`, - }, - expected: map[string]any{ - "hello": []any{"world", "world2"}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := JSON(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/last.go b/pkg/iac/scanners/azure/functions/last.go deleted file mode 100644 index bbf307515d82..000000000000 --- a/pkg/iac/scanners/azure/functions/last.go +++ /dev/null @@ -1,37 +0,0 @@ -package functions - -func Last(args ...any) any { - if len(args) != 1 { - return "" - } - - container := args[0] - - switch cType := container.(type) { - case string: - if cType != "" { - return string(cType[len(cType)-1]) - } - case any: - switch iType := cType.(type) { - case []string: - if len(iType) > 0 { - return iType[len(iType)-1] - } - case []bool: - if len(iType) > 0 { - return iType[len(iType)-1] - } - case []int: - if len(iType) > 0 { - return iType[len(iType)-1] - } - case []float64: - if len(iType) > 0 { - return iType[len(iType)-1] - } - } - } - - return "" -} diff --git a/pkg/iac/scanners/azure/functions/last_index_of.go b/pkg/iac/scanners/azure/functions/last_index_of.go deleted file mode 100644 index 1fa46d132c79..000000000000 --- a/pkg/iac/scanners/azure/functions/last_index_of.go +++ /dev/null @@ -1,22 +0,0 @@ -package functions - -import "strings" - -func LastIndexOf(args ...any) any { - - if len(args) != 2 { - return -1 - } - - stringToSearch, ok := args[0].(string) - if !ok { - return -1 - } - - stringToFind, ok := args[1].(string) - if !ok { - return -1 - } - - return strings.LastIndex(stringToSearch, stringToFind) -} diff --git a/pkg/iac/scanners/azure/functions/last_index_of_test.go b/pkg/iac/scanners/azure/functions/last_index_of_test.go deleted file mode 100644 index 3b83a68fe744..000000000000 --- a/pkg/iac/scanners/azure/functions/last_index_of_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_LastIndexOf(t *testing.T) { - - tests := []struct { - name string - args []any - expected int - }{ - { - name: "get last index of string that is there", - args: []any{ - "Hello world!", - "l", - }, - expected: 9, - }, - { - name: "get last index of string that is there as well", - args: []any{ - "Hello world!", - "world", - }, - expected: 6, - }, - { - name: "get last index of string that isn't there", - args: []any{ - "Hello world!", - "planet!", - }, - expected: -1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := LastIndexOf(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/last_test.go b/pkg/iac/scanners/azure/functions/last_test.go deleted file mode 100644 index 4073392b131d..000000000000 --- a/pkg/iac/scanners/azure/functions/last_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_Last(t *testing.T) { - test := []struct { - name string - args []any - expected any - }{ - { - name: "last in empty string", - args: []any{ - "", - }, - expected: "", - }, - { - name: "last in string", - args: []any{ - "Hello", - }, - expected: "o", - }, - { - name: "last in empty slice", - args: []any{ - []string{}, - }, - expected: "", - }, - { - name: "last in slice", - args: []any{ - []string{"Hello", "World"}, - }, - expected: "World", - }, - } - - for _, tt := range test { - t.Run(tt.name, func(t *testing.T) { - actual := Last(tt.args...) - require.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/length.go b/pkg/iac/scanners/azure/functions/length.go deleted file mode 100644 index c63a00329709..000000000000 --- a/pkg/iac/scanners/azure/functions/length.go +++ /dev/null @@ -1,29 +0,0 @@ -package functions - -func Length(args ...any) any { - - if len(args) != 1 { - return 0 - } - - switch ctype := args[0].(type) { - case string: - return len(ctype) - case map[string]any: - return len(ctype) - case any: - switch iType := ctype.(type) { - case []string: - return len(iType) - case []bool: - return len(iType) - case []int: - return len(iType) - case []float64: - return len(iType) - case []any: - return len(iType) - } - } - return 0 -} diff --git a/pkg/iac/scanners/azure/functions/length_test.go b/pkg/iac/scanners/azure/functions/length_test.go deleted file mode 100644 index 09a9f68a4a67..000000000000 --- a/pkg/iac/scanners/azure/functions/length_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Length(t *testing.T) { - - tests := []struct { - name string - args []any - expected int - }{ - { - name: "length of a string", - args: []any{ - "hello", - }, - expected: 5, - }, - { - name: "length of an empty string", - args: []any{ - "", - }, - expected: 0, - }, - { - name: "length of an empty slice", - args: []any{ - []string{}, - }, - expected: 0, - }, - { - name: "length of an slice with items", - args: []any{ - []string{ - "hello", "world", - }, - }, - expected: 2, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Length(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/less.go b/pkg/iac/scanners/azure/functions/less.go deleted file mode 100644 index 5cdd7cb84c1d..000000000000 --- a/pkg/iac/scanners/azure/functions/less.go +++ /dev/null @@ -1,47 +0,0 @@ -package functions - -func Less(args ...any) any { - - if len(args) != 2 { - return false - } - - switch arg0 := args[0].(type) { - case int: - arg1, ok := args[1].(int) - if ok { - return arg0 < arg1 - } - case string: - arg1, ok := args[1].(string) - if ok { - return arg0 < arg1 - } - } - - return false -} - -func LessOrEquals(args ...any) any { - - if len(args) != 2 { - return false - } - - switch arg0 := args[0].(type) { - case nil: - return args[1] == nil - case int: - arg1, ok := args[1].(int) - if ok { - return arg0 <= arg1 - } - case string: - arg1, ok := args[1].(string) - if ok { - return arg0 <= arg1 - } - } - - return false -} diff --git a/pkg/iac/scanners/azure/functions/less_test.go b/pkg/iac/scanners/azure/functions/less_test.go deleted file mode 100644 index 43c7b01e63d6..000000000000 --- a/pkg/iac/scanners/azure/functions/less_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Less(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - - { - name: "less with nil and string", - args: []any{ - nil, - "test", - }, - expected: false, - }, - { - name: "less with nil and nil", - args: []any{ - nil, - nil, - }, - expected: false, - }, - { - name: "less with string and string", - args: []any{ - "test", - "test", - }, - expected: false, - }, - { - name: "less with string and int", - args: []any{ - "test", - 1, - }, - expected: false, - }, - { - name: "less with int and int", - args: []any{ - 1, - 1, - }, - expected: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Less(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} - -func Test_LessThanOrEqual(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - - { - name: "less with nil and string", - args: []any{ - nil, - "test", - }, - expected: false, - }, - { - name: "less with nil and nil", - args: []any{ - nil, - nil, - }, - expected: true, - }, - { - name: "less with string and string", - args: []any{ - "test", - "test", - }, - expected: true, - }, - { - name: "less with string and int", - args: []any{ - "test", - 1, - }, - expected: false, - }, - { - name: "less with int and int", - args: []any{ - 1, - 1, - }, - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := LessOrEquals(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/max.go b/pkg/iac/scanners/azure/functions/max.go deleted file mode 100644 index e0519bc96e2f..000000000000 --- a/pkg/iac/scanners/azure/functions/max.go +++ /dev/null @@ -1,32 +0,0 @@ -package functions - -func Max(args ...any) any { - switch args[0].(type) { - case int: - var ints []int - for _, arg := range args { - ints = append(ints, arg.(int)) - } - return maxInt(ints) - case any: - if iType, ok := args[0].([]int); ok { - return maxInt(iType) - } - } - return 0 -} - -func maxInt(args []int) int { - if len(args) == 0 { - return 0 - } - - maxN := args[0] - - for i := 1; i < len(args); i++ { - if args[i] > maxN { - maxN = args[i] - } - } - return maxN -} diff --git a/pkg/iac/scanners/azure/functions/max_test.go b/pkg/iac/scanners/azure/functions/max_test.go deleted file mode 100644 index a64afa1e0277..000000000000 --- a/pkg/iac/scanners/azure/functions/max_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Max(t *testing.T) { - test := []struct { - name string - args []any - expected int - }{ - { - name: "max of empty slice", - args: []any{ - []int{}, - }, - expected: 0, - }, - { - name: "max of slice", - args: []any{ - []int{1, 2, 3}, - }, - expected: 3, - }, - { - name: "max of slice with negative numbers", - args: []any{ - []int{-1, -2, -3}, - }, - expected: -1, - }, - { - name: "max of slice with negative and positive numbers", - args: []any{ - []int{-1, 2, -3}, - }, - expected: 2, - }, - { - name: "max of comma separated numbers", - args: []any{ - 1, 2, 3, 4, 5, - }, - expected: 5, - }, - } - - for _, tt := range test { - t.Run(tt.name, func(t *testing.T) { - actual := Max(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/min.go b/pkg/iac/scanners/azure/functions/min.go deleted file mode 100644 index cc857aef2b16..000000000000 --- a/pkg/iac/scanners/azure/functions/min.go +++ /dev/null @@ -1,32 +0,0 @@ -package functions - -func Min(args ...any) any { - switch args[0].(type) { - case int: - var ints []int - for _, arg := range args { - ints = append(ints, arg.(int)) - } - return minInt(ints) - case any: - if iType, ok := args[0].([]int); ok { - return minInt(iType) - } - } - return 0 -} - -func minInt(args []int) int { - if len(args) == 0 { - return 0 - } - - minN := args[0] - - for i := 1; i < len(args); i++ { - if args[i] < minN { - minN = args[i] - } - } - return minN -} diff --git a/pkg/iac/scanners/azure/functions/min_test.go b/pkg/iac/scanners/azure/functions/min_test.go deleted file mode 100644 index c6a0da68a226..000000000000 --- a/pkg/iac/scanners/azure/functions/min_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Min(t *testing.T) { - test := []struct { - name string - args []any - expected int - }{ - { - name: "min of empty slice", - args: []any{ - []int{}, - }, - expected: 0, - }, - { - name: "min of slice", - args: []any{ - []int{1, 2, 3}, - }, - expected: 1, - }, - { - name: "min of slice with negative numbers", - args: []any{ - []int{-1, -2, -3}, - }, - expected: -3, - }, - { - name: "min of slice with negative and positive numbers", - args: []any{ - []int{-1, 2, -3}, - }, - expected: -3, - }, - { - name: "min of comma separated numbers", - args: []any{ - 1, 2, 3, 4, 5, - }, - expected: 1, - }, - } - - for _, tt := range test { - t.Run(tt.name, func(t *testing.T) { - actual := Min(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/mod.go b/pkg/iac/scanners/azure/functions/mod.go deleted file mode 100644 index 8c03d4241df2..000000000000 --- a/pkg/iac/scanners/azure/functions/mod.go +++ /dev/null @@ -1,14 +0,0 @@ -package functions - -func Mod(args ...any) any { - if len(args) != 2 { - return 0 - } - - if a, ok := args[0].(int); ok { - if b, ok := args[1].(int); ok { - return a % b - } - } - return 0 -} diff --git a/pkg/iac/scanners/azure/functions/mod_test.go b/pkg/iac/scanners/azure/functions/mod_test.go deleted file mode 100644 index 60a055ae1544..000000000000 --- a/pkg/iac/scanners/azure/functions/mod_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package functions - -import "testing" - -func Test_Mod(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "Mod with 1 and 2", - args: []any{1, 2}, - expected: 1, - }, - { - name: "Mod with 2 and 3", - args: []any{2, 3}, - expected: 2, - }, - { - name: "Mod with 3 and -4", - args: []any{3, -4}, - expected: 3, - }, - { - name: "Mod with 7 and 3", - args: []any{7, 3}, - expected: 1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Mod(tt.args...) - if got != tt.expected { - t.Errorf("Mod() = %v, want %v", got, tt.expected) - } - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/mul.go b/pkg/iac/scanners/azure/functions/mul.go deleted file mode 100644 index 0cf8a8242c56..000000000000 --- a/pkg/iac/scanners/azure/functions/mul.go +++ /dev/null @@ -1,15 +0,0 @@ -package functions - -func Mul(args ...any) any { - - if len(args) != 2 { - return nil - } - - if a, ok := args[0].(int); ok { - if b, ok := args[1].(int); ok { - return a * b - } - } - return nil -} diff --git a/pkg/iac/scanners/azure/functions/mul_test.go b/pkg/iac/scanners/azure/functions/mul_test.go deleted file mode 100644 index 77a74a4e2cf1..000000000000 --- a/pkg/iac/scanners/azure/functions/mul_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Mul(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "multiply -2 by 1", - args: []any{-2, 1}, - expected: -2, - }, - { - name: "multiply 4 by 2", - args: []any{4, 2}, - expected: 8, - }, - { - name: "multiply 6 by 3", - args: []any{6, 3}, - expected: 18, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Mul(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/not.go b/pkg/iac/scanners/azure/functions/not.go deleted file mode 100644 index 7b12d169fc18..000000000000 --- a/pkg/iac/scanners/azure/functions/not.go +++ /dev/null @@ -1,13 +0,0 @@ -package functions - -func Not(args ...any) any { - - if len(args) != 1 { - return false - } - - if condition, ok := args[0].(bool); ok { - return !condition - } - return false -} diff --git a/pkg/iac/scanners/azure/functions/not_test.go b/pkg/iac/scanners/azure/functions/not_test.go deleted file mode 100644 index 808e3b5643dc..000000000000 --- a/pkg/iac/scanners/azure/functions/not_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Not(t *testing.T) { - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "Not with true", - args: []any{true}, - expected: false, - }, - { - name: "Not with false", - args: []any{false}, - expected: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Not(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/null.go b/pkg/iac/scanners/azure/functions/null.go deleted file mode 100644 index 21ccb9714b7c..000000000000 --- a/pkg/iac/scanners/azure/functions/null.go +++ /dev/null @@ -1,5 +0,0 @@ -package functions - -func Null(args ...any) any { - return nil -} diff --git a/pkg/iac/scanners/azure/functions/null_test.go b/pkg/iac/scanners/azure/functions/null_test.go deleted file mode 100644 index 3394193415fb..000000000000 --- a/pkg/iac/scanners/azure/functions/null_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Null(t *testing.T) { - - assert.Nil(t, Null()) -} diff --git a/pkg/iac/scanners/azure/functions/or.go b/pkg/iac/scanners/azure/functions/or.go deleted file mode 100644 index b94b706e50ae..000000000000 --- a/pkg/iac/scanners/azure/functions/or.go +++ /dev/null @@ -1,20 +0,0 @@ -package functions - -func Or(args ...any) any { - - if len(args) <= 1 { - return false - } - - for _, arg := range args { - arg1, ok := arg.(bool) - if !ok { - return false - } - if arg1 { - return true - } - - } - return false -} diff --git a/pkg/iac/scanners/azure/functions/or_test.go b/pkg/iac/scanners/azure/functions/or_test.go deleted file mode 100644 index 83c7d29cf35d..000000000000 --- a/pkg/iac/scanners/azure/functions/or_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Or(t *testing.T) { - - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "And with same 2 bools", - args: []any{true, true}, - expected: true, - }, - { - name: "And with same 3 bools", - args: []any{true, true, true}, - expected: true, - }, - { - name: "And with different 4 bools", - args: []any{true, true, false, true}, - expected: true, - }, - { - name: "And with same false 4 bools", - args: []any{false, false, false, false}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Or(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/pad.go b/pkg/iac/scanners/azure/functions/pad.go deleted file mode 100644 index a95a2e626303..000000000000 --- a/pkg/iac/scanners/azure/functions/pad.go +++ /dev/null @@ -1,32 +0,0 @@ -package functions - -import "strings" - -func PadLeft(args ...any) any { - if len(args) != 3 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - length, ok := args[1].(int) - if !ok { - return "" - } - - pad, ok := args[2].(string) - if !ok { - return "" - } - - if len(input) >= length { - return input - } - - repeat := (length - len(input)) / len(pad) - - return strings.Repeat(pad, repeat) + input -} diff --git a/pkg/iac/scanners/azure/functions/pad_test.go b/pkg/iac/scanners/azure/functions/pad_test.go deleted file mode 100644 index aefc367f12af..000000000000 --- a/pkg/iac/scanners/azure/functions/pad_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_PadLeft(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "pad left with a input smaller than length", - args: []any{ - "1234", - 8, - "0", - }, - expected: "00001234", - }, - { - name: "pad left with a input larger than length", - args: []any{ - "1234", - 2, - "0", - }, - expected: "1234", - }, - { - name: "pad left with a input same as than length", - args: []any{ - "1234", - 4, - "0", - }, - expected: "1234", - }, - { - name: "pad left with larger padding character", - args: []any{ - "1234", - 8, - "00", - }, - expected: "00001234", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := PadLeft(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/parameters.go b/pkg/iac/scanners/azure/functions/parameters.go deleted file mode 100644 index b13ee3d60e4e..000000000000 --- a/pkg/iac/scanners/azure/functions/parameters.go +++ /dev/null @@ -1 +0,0 @@ -package functions diff --git a/pkg/iac/scanners/azure/functions/pick_zones.go b/pkg/iac/scanners/azure/functions/pick_zones.go deleted file mode 100644 index 696cfe5a1835..000000000000 --- a/pkg/iac/scanners/azure/functions/pick_zones.go +++ /dev/null @@ -1,23 +0,0 @@ -package functions - -func PickZones(args ...any) any { - if len(args) < 3 { - return nil - } - numOfZones := 1 - - if len(args) > 3 { - numOfZones = args[3].(int) - if numOfZones > 3 { - numOfZones = 3 - } - } - - var zones []int - - for i := 1; i <= numOfZones; i++ { - zones = append(zones, i) - } - - return zones -} diff --git a/pkg/iac/scanners/azure/functions/pick_zones_test.go b/pkg/iac/scanners/azure/functions/pick_zones_test.go deleted file mode 100644 index 19db480f9b0d..000000000000 --- a/pkg/iac/scanners/azure/functions/pick_zones_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_PickZones(t *testing.T) { - assert.Equal(t, []int{1}, PickZones("Microsoft.Compute", "virtualmachines", "eu-west-1")) - assert.Equal(t, []int{1, 2}, PickZones("Microsoft.Compute", "virtualmachines", "eu-west-1", 2)) - assert.Equal(t, []int{1, 2, 3}, PickZones("Microsoft.Compute", "virtualmachines", "eu-west-1", 3)) - assert.Equal(t, []int{1, 2, 3}, PickZones("Microsoft.Compute", "virtualmachines", "eu-west-1", 4)) -} diff --git a/pkg/iac/scanners/azure/functions/range.go b/pkg/iac/scanners/azure/functions/range.go deleted file mode 100644 index 33090e276505..000000000000 --- a/pkg/iac/scanners/azure/functions/range.go +++ /dev/null @@ -1,30 +0,0 @@ -package functions - -func Range(args ...any) any { - - if len(args) != 2 { - return []any{} - } - - start, ok := args[0].(int) - if !ok { - return []int{} - } - - count, ok := args[1].(int) - if !ok { - return []int{} - } - - if count > 10000 { - count = 10000 - } - - result := make([]int, count) - - for i := 0; i < count; i++ { - result[i] = start + i - } - - return result -} diff --git a/pkg/iac/scanners/azure/functions/range_test.go b/pkg/iac/scanners/azure/functions/range_test.go deleted file mode 100644 index eb41cb9b83ec..000000000000 --- a/pkg/iac/scanners/azure/functions/range_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Range(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "range for 3 from 1", - args: []any{ - 1, - 3, - }, - expected: []int{1, 2, 3}, - }, - { - name: "range with for 10 from 3", - args: []any{ - 3, - 10, - }, - expected: []int{3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, - }, - { - name: "range with for 10 from -10", - args: []any{ - -10, - 10, - }, - expected: []int{-10, -9, -8, -7, -6, -5, -4, -3, -2, -1}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Range(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/reference.go b/pkg/iac/scanners/azure/functions/reference.go deleted file mode 100644 index cb768e102e0d..000000000000 --- a/pkg/iac/scanners/azure/functions/reference.go +++ /dev/null @@ -1,12 +0,0 @@ -package functions - -import "fmt" - -// Reference function can't work as per Azure because it requires Azure ARM logic -// best effort is to return the resourcename with a suffix to try and make it unique -func Reference(args ...any) any { - if len(args) < 1 { - return nil - } - return fmt.Sprintf("%v-reference", args[0]) -} diff --git a/pkg/iac/scanners/azure/functions/reference_test.go b/pkg/iac/scanners/azure/functions/reference_test.go deleted file mode 100644 index c669fe98d3f0..000000000000 --- a/pkg/iac/scanners/azure/functions/reference_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Reference(t *testing.T) { - assert.Equal(t, "test-reference", Reference("test")) - assert.Equal(t, "123-reference", Reference(123)) -} diff --git a/pkg/iac/scanners/azure/functions/replace.go b/pkg/iac/scanners/azure/functions/replace.go deleted file mode 100644 index bbe6ec8f4d9d..000000000000 --- a/pkg/iac/scanners/azure/functions/replace.go +++ /dev/null @@ -1,26 +0,0 @@ -package functions - -import "strings" - -func Replace(args ...any) any { - if len(args) != 3 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - purana, ok := args[1].(string) - if !ok { - return "" - } - - nava, ok := args[2].(string) - if !ok { - return "" - } - - return strings.ReplaceAll(input, purana, nava) -} diff --git a/pkg/iac/scanners/azure/functions/replace_test.go b/pkg/iac/scanners/azure/functions/replace_test.go deleted file mode 100644 index ed827d2a7756..000000000000 --- a/pkg/iac/scanners/azure/functions/replace_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Replace(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "replace a string", - args: []any{ - "hello", - "l", - "p", - }, - expected: "heppo", - }, - { - name: "replace a string with invalid replacement", - args: []any{ - "hello", - "q", - "p", - }, - expected: "hello", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Replace(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/resource.go b/pkg/iac/scanners/azure/functions/resource.go deleted file mode 100644 index 164fc6e42819..000000000000 --- a/pkg/iac/scanners/azure/functions/resource.go +++ /dev/null @@ -1,48 +0,0 @@ -package functions - -import ( - "fmt" -) - -func ResourceID(args ...any) any { - if len(args) < 2 { - return nil - } - - var resourceID string - - for _, arg := range args { - resourceID += "/" + fmt.Sprintf("%v", arg) - } - - return resourceID -} - -func ExtensionResourceID(args ...any) any { - if len(args) < 3 { - return nil - } - - var resourceID string - - for _, arg := range args { - resourceID += "/" + fmt.Sprintf("%v", arg) - } - - return resourceID -} - -func ResourceGroup(args ...any) any { - return fmt.Sprintf(`{ -"id": "/subscriptions/%s/resourceGroups/PlaceHolderResourceGroup", -"name": "Placeholder Resource Group", -"type":"Microsoft.Resources/resourceGroups", -"location": "westus", -"managedBy": "%s", -"tags": { -}, -"properties": { - "provisioningState": "Succeeded -} -}`, subscriptionID, managingResourceID) -} diff --git a/pkg/iac/scanners/azure/functions/resource_test.go b/pkg/iac/scanners/azure/functions/resource_test.go deleted file mode 100644 index d6dac14b4184..000000000000 --- a/pkg/iac/scanners/azure/functions/resource_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_ResourceID(t *testing.T) { - assert.Equal(t, "/test1/test2", ResourceID("test1", "test2")) - assert.Equal(t, "/test1/123", ResourceID("test1", 123)) -} diff --git a/pkg/iac/scanners/azure/functions/scope.go b/pkg/iac/scanners/azure/functions/scope.go deleted file mode 100644 index 5801cba7c4b0..000000000000 --- a/pkg/iac/scanners/azure/functions/scope.go +++ /dev/null @@ -1,106 +0,0 @@ -package functions - -import ( - "fmt" - - "github.com/google/uuid" -) - -var ( - tenantID = uuid.NewString() - groupID = uuid.NewString() - updaterID = uuid.NewString() - subscriptionID = uuid.NewString() - managingResourceID = uuid.NewString() -) - -func ManagementGroup(_ ...any) any { - - return fmt.Sprintf(`{ - "id": "/providers/Microsoft.Management/managementGroups/mgPlaceholder", - "name": "mgPlaceholder", - "properties": { - "details": { - "parent": { - "displayName": "Tenant Root Group", - "id": "/providers/Microsoft.Management/managementGroups/%[1]s", - "name": "%[1]s" - }, - "updatedBy": "%[2]s", - "updatedTime": "2020-07-23T21:05:52.661306Z", - "version": "1" - }, - "displayName": "Management PlaceHolder Group", - "tenantId": "%[3]s" - }, - "type": "/providers/Microsoft.Management/managementGroups" - } -`, groupID, updaterID, tenantID) -} - -func ManagementGroupResourceID(args ...any) any { - if len(args) < 2 { - return "" - } - - switch len(args) { - case 3: - return fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s/providers/%s/%s/%s", groupID, args[0], args[1], args[2]) - case 4: - return fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s/providers/%s/%s/%s", args[0], args[1], args[2], args[3]) - default: - return fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s/providers/%s/%s", groupID, args[0], args[1]) - } - -} - -func Subscription(_ ...any) any { - return fmt.Sprintf(`{ - "id": "/subscriptions/%[1]s", - "subscriptionId": "%[1]s", - "tenantId": "%[2]s", - "displayName": "Placeholder Subscription" -}`, subscriptionID, tenantID) -} - -func SubscriptionResourceID(args ...any) any { - if len(args) < 2 { - return nil - } - - switch len(args) { - - case 3: - return fmt.Sprintf("/subscriptions/%s/providers/%s/%s/%s", subscriptionID, args[0], args[1], args[2]) - case 4: - // subscription ID has been provided so use that - return fmt.Sprintf("/subscriptions/%s/providers/%s/%s/%s", args[0], args[1], args[2], args[3]) - default: - - return fmt.Sprintf("/subscriptions/%s/providers/%s/%s", subscriptionID, args[0], args[1]) - } -} - -func Tenant(_ ...any) any { - return fmt.Sprintf(`{ - "countryCode": "US", - "displayName": "Placeholder Tenant Name", - "id": "/tenants/%[1]s", - "tenantId": "%[1]s" - }`, tenantID) -} - -func TenantResourceID(args ...any) any { - if len(args) < 2 { - return nil - } - - switch len(args) { - case 3: - return fmt.Sprintf("/providers/%s/%s/%s", args[0], args[1], args[2]) - - default: - return fmt.Sprintf("/providers/%s/%s", args[0], args[1]) - } - -} diff --git a/pkg/iac/scanners/azure/functions/scope_test.go b/pkg/iac/scanners/azure/functions/scope_test.go deleted file mode 100644 index 10fe7a5f6d02..000000000000 --- a/pkg/iac/scanners/azure/functions/scope_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_SubscriptionResourceID(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "scope resource id with subscription ID", - args: []any{ - "4ec875a5-41a2-4837-88cf-4266466e65ed", - "Microsoft.Authorization/roleDefinitions", - "8e3af657-a8ff-443c-a75c-2fe8c4bcb635", - "b34282f6-5e3c-4306-8741-ebd7a871d187", - }, - expected: "/subscriptions/4ec875a5-41a2-4837-88cf-4266466e65ed/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635/b34282f6-5e3c-4306-8741-ebd7a871d187", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := SubscriptionResourceID(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/skip.go b/pkg/iac/scanners/azure/functions/skip.go deleted file mode 100644 index dd99604fb680..000000000000 --- a/pkg/iac/scanners/azure/functions/skip.go +++ /dev/null @@ -1,34 +0,0 @@ -package functions - -func Skip(args ...any) any { - if len(args) != 2 { - return "" - } - - count, ok := args[1].(int) - if !ok { - return "" - } - switch input := args[0].(type) { - case string: - if count > len(input) { - return "" - } - return input[count:] - case any: - switch iType := input.(type) { - case []int: - return iType[count:] - case []string: - return iType[count:] - case []bool: - return iType[count:] - case []float64: - return iType[count:] - case []any: - return iType[count:] - } - } - - return "" -} diff --git a/pkg/iac/scanners/azure/functions/skip_test.go b/pkg/iac/scanners/azure/functions/skip_test.go deleted file mode 100644 index 3a5645cbf6ee..000000000000 --- a/pkg/iac/scanners/azure/functions/skip_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Skip(t *testing.T) { - - tests := []struct { - name string - args []any - expected any - }{ - { - name: "skip a string", - args: []any{ - "hello", - 1, - }, - expected: "ello", - }, - { - name: "skip beyond the length a string", - args: []any{ - "hello", - 6, - }, - expected: "", - }, - { - name: "skip with a zero count on a string", - args: []any{ - "hello", - 0, - }, - expected: "hello", - }, - { - name: "skip with slice of ints", - args: []any{ - []int{1, 2, 3, 4, 5}, - 2, - }, - expected: []int{3, 4, 5}, - }, - { - name: "skip with slice of strings", - args: []any{ - []string{"hello", "world"}, - 1, - }, - expected: []string{"world"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Skip(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/split.go b/pkg/iac/scanners/azure/functions/split.go deleted file mode 100644 index 598f1995211d..000000000000 --- a/pkg/iac/scanners/azure/functions/split.go +++ /dev/null @@ -1,35 +0,0 @@ -package functions - -import "strings" - -func Split(args ...any) any { - if len(args) != 2 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - switch separator := args[1].(type) { - case string: - return strings.Split(input, separator) - case any: - if separator, ok := separator.([]string); ok { - m := make(map[rune]int) - for _, r := range separator { - r := rune(r[0]) - m[r] = 1 - } - - splitter := func(r rune) bool { - return m[r] == 1 - } - - return strings.FieldsFunc(input, splitter) - } - - } - return []string{} -} diff --git a/pkg/iac/scanners/azure/functions/split_test.go b/pkg/iac/scanners/azure/functions/split_test.go deleted file mode 100644 index e396dd2ef937..000000000000 --- a/pkg/iac/scanners/azure/functions/split_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Split(t *testing.T) { - tests := []struct { - name string - args []any - expected []string - }{ - { - name: "split a string", - args: []any{ - "hello, world", - ",", - }, - expected: []string{"hello", " world"}, - }, - { - name: "split a string with multiple separators", - args: []any{ - "one;two,three", - []string{",", ";"}, - }, - expected: []string{"one", "two", "three"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Split(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/starts_with.go b/pkg/iac/scanners/azure/functions/starts_with.go deleted file mode 100644 index 3ae79bfff34b..000000000000 --- a/pkg/iac/scanners/azure/functions/starts_with.go +++ /dev/null @@ -1,22 +0,0 @@ -package functions - -import "strings" - -func StartsWith(args ...any) any { - - if len(args) != 2 { - return false - } - - stringToSearch, ok := args[0].(string) - if !ok { - return false - } - - stringToFind, ok := args[1].(string) - if !ok { - return false - } - - return strings.HasPrefix(stringToSearch, stringToFind) -} diff --git a/pkg/iac/scanners/azure/functions/starts_with_test.go b/pkg/iac/scanners/azure/functions/starts_with_test.go deleted file mode 100644 index 400b36c1b894..000000000000 --- a/pkg/iac/scanners/azure/functions/starts_with_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_StartsWith(t *testing.T) { - - tests := []struct { - name string - args []any - expected bool - }{ - { - name: "string ends with", - args: []any{ - "Hello, world!", - "Hello,", - }, - expected: true, - }, - { - name: "string does not end with", - args: []any{ - "Hello world!", - "Hello,", - }, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := StartsWith(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/string.go b/pkg/iac/scanners/azure/functions/string.go deleted file mode 100644 index 278f09629f9d..000000000000 --- a/pkg/iac/scanners/azure/functions/string.go +++ /dev/null @@ -1,16 +0,0 @@ -package functions - -import "fmt" - -func String(args ...any) any { - if len(args) != 1 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return fmt.Sprintf("%v", args[0]) - } - - return input -} diff --git a/pkg/iac/scanners/azure/functions/string_test.go b/pkg/iac/scanners/azure/functions/string_test.go deleted file mode 100644 index 2e8e35bbde88..000000000000 --- a/pkg/iac/scanners/azure/functions/string_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_String(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "string from a string", - args: []any{ - "hello", - }, - expected: "hello", - }, - { - name: "string from a bool", - args: []any{ - false, - }, - expected: "false", - }, - { - name: "string from an int", - args: []any{ - 10, - }, - expected: "10", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := String(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/sub.go b/pkg/iac/scanners/azure/functions/sub.go deleted file mode 100644 index 08751ff94656..000000000000 --- a/pkg/iac/scanners/azure/functions/sub.go +++ /dev/null @@ -1,15 +0,0 @@ -package functions - -func Sub(args ...any) any { - - if len(args) != 2 { - return nil - } - - if a, ok := args[0].(int); ok { - if b, ok := args[1].(int); ok { - return a - b - } - } - return nil -} diff --git a/pkg/iac/scanners/azure/functions/sub_test.go b/pkg/iac/scanners/azure/functions/sub_test.go deleted file mode 100644 index 66bd0c403758..000000000000 --- a/pkg/iac/scanners/azure/functions/sub_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Sub(t *testing.T) { - tests := []struct { - name string - args []any - expected int - }{ - { - name: "subtract 2 from 5", - args: []any{5, 2}, - expected: 3, - }, - { - name: "subtract 2 from 1", - args: []any{1, 2}, - expected: -1, - }, - { - name: "subtract 3 from 2", - args: []any{2, 3}, - expected: -1, - }, - { - name: "subtract -4 from 3", - args: []any{3, -4}, - expected: 7, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Sub(tt.args...) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/substring.go b/pkg/iac/scanners/azure/functions/substring.go deleted file mode 100644 index 502ba6ac5df9..000000000000 --- a/pkg/iac/scanners/azure/functions/substring.go +++ /dev/null @@ -1,36 +0,0 @@ -package functions - -func SubString(args ...any) any { - if len(args) < 2 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - start, ok := args[1].(int) - if !ok { - return "" - } - - if len(args) == 2 { - args = append(args, len(input)) - } - - length, ok := args[2].(int) - if !ok { - return "" - } - - if start > len(input) { - return "" - } - - if start+length > len(input) { - return input[start:] - } - - return input[start : start+length] -} diff --git a/pkg/iac/scanners/azure/functions/substring_test.go b/pkg/iac/scanners/azure/functions/substring_test.go deleted file mode 100644 index 633e21b66996..000000000000 --- a/pkg/iac/scanners/azure/functions/substring_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_SubString(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "substring a string", - args: []any{ - "hello", - 1, - 3, - }, - expected: "ell", - }, - { - name: "substring a string with no upper bound", - args: []any{ - "hello", - 1, - }, - expected: "ello", - }, - { - name: "substring a string with start higher than the length", - args: []any{ - "hello", - 10, - }, - expected: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := SubString(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/take.go b/pkg/iac/scanners/azure/functions/take.go deleted file mode 100644 index 9e43ed2ecb9d..000000000000 --- a/pkg/iac/scanners/azure/functions/take.go +++ /dev/null @@ -1,49 +0,0 @@ -package functions - -func Take(args ...any) any { - if len(args) != 2 { - return "" - } - - count, ok := args[1].(int) - if !ok { - return "" - } - switch input := args[0].(type) { - case string: - if count > len(input) { - return input - } - return input[:count] - case any: - switch iType := input.(type) { - case []int: - if count > len(iType) { - return iType - } - return iType[:count] - case []string: - if count > len(iType) { - return iType - } - return iType[:count] - case []bool: - if count > len(iType) { - return iType - } - return iType[:count] - case []float64: - if count > len(iType) { - return iType - } - return iType[:count] - case []any: - if count > len(iType) { - return iType - } - return iType[:count] - } - } - - return "" -} diff --git a/pkg/iac/scanners/azure/functions/take_test.go b/pkg/iac/scanners/azure/functions/take_test.go deleted file mode 100644 index 8334a1b7e121..000000000000 --- a/pkg/iac/scanners/azure/functions/take_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Take(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "take a string", - args: []any{ - "hello", - 2, - }, - expected: "he", - }, - { - name: "take a string with invalid count", - args: []any{ - "hello", - 10, - }, - expected: "hello", - }, - { - name: "take a string from slice", - args: []any{ - []string{"a", "b", "c"}, - 2, - }, - expected: []string{"a", "b"}, - }, - { - name: "take a string from a slice", - args: []any{ - []string{"a", "b", "c"}, - 2, - }, - expected: []string{"a", "b"}, - }, - { - name: "take a string from a slice with invalid count", - args: []any{ - []string{"a", "b", "c"}, - 10, - }, - expected: []string{"a", "b", "c"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Take(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/trim.go b/pkg/iac/scanners/azure/functions/trim.go deleted file mode 100644 index 76006154fc7b..000000000000 --- a/pkg/iac/scanners/azure/functions/trim.go +++ /dev/null @@ -1,16 +0,0 @@ -package functions - -import "strings" - -func Trim(args ...any) any { - if len(args) != 1 { - return "" - } - - input, ok := args[0].(string) - if !ok { - return "" - } - - return strings.TrimSpace(input) -} diff --git a/pkg/iac/scanners/azure/functions/trim_test.go b/pkg/iac/scanners/azure/functions/trim_test.go deleted file mode 100644 index b77e7b2e229d..000000000000 --- a/pkg/iac/scanners/azure/functions/trim_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package functions - -import "testing" - -func Test_Trim(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "trim a string", - args: []any{ - " hello ", - }, - expected: "hello", - }, - { - name: "trim a string with multiple spaces", - args: []any{ - " hello ", - }, - expected: "hello", - }, - { - name: "trim a string with tabs", - args: []any{ - " hello ", - }, - expected: "hello", - }, - { - name: "trim a string with new lines", - args: []any{ - ` - -hello - -`, - }, - expected: "hello", - }, - { - name: "trim a string with tabs, spaces and new lines", - args: []any{ - ` - -hello - -`, - }, - expected: "hello", - }, - { - name: "trim a string with non string input", - args: []any{ - 10, - }, - expected: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Trim(tt.args...) - if actual != tt.expected { - t.Errorf("Trim(%v) = %v, expected %v", tt.args, actual, tt.expected) - } - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/true.go b/pkg/iac/scanners/azure/functions/true.go deleted file mode 100644 index 422b2beb58eb..000000000000 --- a/pkg/iac/scanners/azure/functions/true.go +++ /dev/null @@ -1,5 +0,0 @@ -package functions - -func True(args ...any) any { - return true -} diff --git a/pkg/iac/scanners/azure/functions/union.go b/pkg/iac/scanners/azure/functions/union.go deleted file mode 100644 index 0f55b0b90c62..000000000000 --- a/pkg/iac/scanners/azure/functions/union.go +++ /dev/null @@ -1,58 +0,0 @@ -package functions - -import "sort" - -func Union(args ...any) any { - if len(args) == 0 { - return []any{} - } - if len(args) == 1 { - return args[0] - } - - switch args[0].(type) { - case map[string]any: - return unionMap(args...) - case any: - return unionArray(args...) - } - - return []any{} - -} - -func unionMap(args ...any) any { - result := make(map[string]any) - - for _, arg := range args { - if iType, ok := arg.(map[string]any); ok { - for k, v := range iType { - result[k] = v - } - } - } - - return result -} - -func unionArray(args ...any) any { - var result []any - union := make(map[any]bool) - - for _, arg := range args { - if iType, ok := arg.([]any); ok { - for _, item := range iType { - union[item] = true - } - } - } - - for k := range union { - result = append(result, k) - } - sort.Slice(result, func(i, j int) bool { - return result[i].(string) < result[j].(string) - }) - - return result -} diff --git a/pkg/iac/scanners/azure/functions/union_test.go b/pkg/iac/scanners/azure/functions/union_test.go deleted file mode 100644 index 6c030f7d8b8b..000000000000 --- a/pkg/iac/scanners/azure/functions/union_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Union(t *testing.T) { - tests := []struct { - name string - args []any - expected any - }{ - { - name: "union single array", - args: []any{ - []any{"a", "b", "c"}, - }, - expected: []any{"a", "b", "c"}, - }, - { - name: "union two arrays", - args: []any{ - []any{"a", "b", "c"}, - []any{"b", "c", "d"}, - }, - expected: []any{"a", "b", "c", "d"}, - }, - { - name: "union two arrays", - args: []any{ - []any{"a", "b", "c"}, - []any{"b", "c", "d"}, - []any{"b", "c", "d", "e"}, - }, - expected: []any{"a", "b", "c", "d", "e"}, - }, - { - name: "union single maps", - args: []any{ - map[string]any{ - "a": "a", - "b": "b", - "c": "c", - }, - }, - expected: map[string]any{ - "a": "a", - "b": "b", - "c": "c", - }, - }, - { - name: "union two maps", - args: []any{ - map[string]any{ - "a": "a", - "b": "b", - "c": "c", - }, - map[string]any{ - "b": "b", - "c": "c", - "d": "d", - }, - }, - expected: map[string]any{ - "a": "a", - "b": "b", - "c": "c", - "d": "d", - }, - }, - { - name: "union three maps", - args: []any{ - map[string]any{ - "a": "a", - "b": "b", - "c": "c", - }, - map[string]any{ - "b": "b", - "c": "c", - "d": "d", - }, - map[string]any{ - "b": "b", - "c": "c", - "e": "e", - }, - }, - expected: map[string]any{ - "a": "a", - "b": "b", - "c": "c", - "d": "d", - "e": "e", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Union(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/unique_string.go b/pkg/iac/scanners/azure/functions/unique_string.go deleted file mode 100644 index 72b5fb5aabc6..000000000000 --- a/pkg/iac/scanners/azure/functions/unique_string.go +++ /dev/null @@ -1,21 +0,0 @@ -package functions - -import ( - "crypto/sha256" - "encoding/hex" - "strings" -) - -func UniqueString(args ...any) any { - if len(args) == 0 { - return "" - } - - hashParts := make([]string, len(args)) - for i, str := range args { - hashParts[i] = str.(string) - } - - hash := sha256.New().Sum([]byte(strings.Join(hashParts, ""))) - return hex.EncodeToString(hash)[:13] -} diff --git a/pkg/iac/scanners/azure/functions/unique_string_test.go b/pkg/iac/scanners/azure/functions/unique_string_test.go deleted file mode 100644 index 1cbc3ba03476..000000000000 --- a/pkg/iac/scanners/azure/functions/unique_string_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_UniqueString(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "unique string from a string", - args: []any{ - "hello", - }, - expected: "68656c6c6fe3b", - }, - { - name: "unique string from a string", - args: []any{ - "hello", - "world", - }, - expected: "68656c6c6f776", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := UniqueString(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/functions/uri.go b/pkg/iac/scanners/azure/functions/uri.go deleted file mode 100644 index f3ff499740ba..000000000000 --- a/pkg/iac/scanners/azure/functions/uri.go +++ /dev/null @@ -1,29 +0,0 @@ -package functions - -import ( - "net/url" - "path" -) - -func Uri(args ...any) any { - if len(args) != 2 { - return "" - } - - result, err := joinPath(args[0].(string), args[1].(string)) - if err != nil { - return "" - } - return result -} - -// Backport url.JoinPath until we're ready for Go 1.19 -func joinPath(base string, elem ...string) (string, error) { - u, err := url.Parse(base) - if err != nil { - return "", err - } - elem = append([]string{u.EscapedPath()}, elem...) - u.Path = path.Join(elem...) - return u.String(), nil -} diff --git a/pkg/iac/scanners/azure/functions/uri_test.go b/pkg/iac/scanners/azure/functions/uri_test.go deleted file mode 100644 index 0577093ce4eb..000000000000 --- a/pkg/iac/scanners/azure/functions/uri_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package functions - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_Uri(t *testing.T) { - tests := []struct { - name string - args []any - expected string - }{ - { - name: "uri from a base and relative with no trailing slash", - args: []any{ - "http://contoso.org/firstpath", - "myscript.sh", - }, - expected: "http://contoso.org/firstpath/myscript.sh", - }, - { - name: "uri from a base and relative with trailing slash", - args: []any{ - "http://contoso.org/firstpath/", - "myscript.sh", - }, - expected: "http://contoso.org/firstpath/myscript.sh", - }, - { - name: "uri from a base with trailing slash and relative with ../", - args: []any{ - "http://contoso.org/firstpath/", - "../myscript.sh", - }, - expected: "http://contoso.org/myscript.sh", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := Uri(tt.args...) - require.Equal(t, tt.expected, actual) - }) - } - -} diff --git a/pkg/iac/scanners/azure/functions/utc_now.go b/pkg/iac/scanners/azure/functions/utc_now.go deleted file mode 100644 index dcb53d9d2740..000000000000 --- a/pkg/iac/scanners/azure/functions/utc_now.go +++ /dev/null @@ -1,47 +0,0 @@ -package functions - -import ( - "strings" - "time" -) - -func UTCNow(args ...any) any { - if len(args) > 1 { - return nil - } - - if len(args) == 1 { - format, ok := args[0].(string) - if ok { - goFormat := convertFormat(format) - return time.Now().UTC().Format(goFormat) - } - } - - return time.Now().UTC().Format(time.RFC3339) -} - -// don't look directly at this code -func convertFormat(format string) string { - goFormat := format - goFormat = strings.ReplaceAll(goFormat, "yyyy", "2006") - goFormat = strings.ReplaceAll(goFormat, "yy", "06") - goFormat = strings.ReplaceAll(goFormat, "MMMM", "January") - goFormat = strings.ReplaceAll(goFormat, "MMM", "Jan") - goFormat = strings.ReplaceAll(goFormat, "MM", "01") - goFormat = strings.ReplaceAll(goFormat, "M", "1") - goFormat = strings.ReplaceAll(goFormat, "dd", "02") - goFormat = strings.ReplaceAll(goFormat, "d", "2") - goFormat = strings.ReplaceAll(goFormat, "HH", "15") - goFormat = strings.ReplaceAll(goFormat, "H", "3") - goFormat = strings.ReplaceAll(goFormat, "hh", "03") - goFormat = strings.ReplaceAll(goFormat, "h", "3") - goFormat = strings.ReplaceAll(goFormat, "mm", "04") - goFormat = strings.ReplaceAll(goFormat, "m", "4") - goFormat = strings.ReplaceAll(goFormat, "ss", "05") - goFormat = strings.ReplaceAll(goFormat, "s", "5") - goFormat = strings.ReplaceAll(goFormat, "tt", "PM") - goFormat = strings.ReplaceAll(goFormat, "t", "PM") - return goFormat - -} diff --git a/pkg/iac/scanners/azure/functions/utc_now_test.go b/pkg/iac/scanners/azure/functions/utc_now_test.go deleted file mode 100644 index 94263af9f9aa..000000000000 --- a/pkg/iac/scanners/azure/functions/utc_now_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package functions - -import ( - "fmt" - "strconv" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func Test_UTCNow(t *testing.T) { - - tests := []struct { - name string - args []any - expected string - }{ - { - name: "utc now day", - args: []any{ - "d", - }, - expected: strconv.Itoa(time.Now().UTC().Day()), - }, - { - name: "utc now date", - args: []any{ - "yyyy-M-d", - }, - expected: fmt.Sprintf("%d-%d-%d", time.Now().UTC().Year(), time.Now().UTC().Month(), time.Now().UTC().Day()), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := UTCNow(tt.args...) - assert.Equal(t, tt.expected, actual) - }) - } -} diff --git a/pkg/iac/scanners/azure/resolver/resolver.go b/pkg/iac/scanners/azure/resolver/resolver.go deleted file mode 100644 index 3794ed20ab64..000000000000 --- a/pkg/iac/scanners/azure/resolver/resolver.go +++ /dev/null @@ -1,51 +0,0 @@ -package resolver - -import ( - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/expressions" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Resolver interface { - ResolveExpression(expression azure2.Value) azure2.Value - SetDeployment(d *azure2.Deployment) -} - -func NewResolver() Resolver { - return &resolver{} -} - -type resolver struct { - deployment *azure2.Deployment -} - -func (r *resolver) SetDeployment(d *azure2.Deployment) { - r.deployment = d -} - -func (r *resolver) ResolveExpression(expression azure2.Value) azure2.Value { - if expression.Kind != azure2.KindExpression { - return expression - } - if r.deployment == nil { - panic("cannot resolve expression on nil deployment") - } - code := expression.AsString() - - resolved, err := r.resolveExpressionString(code, expression.GetMetadata()) - if err != nil { - expression.Kind = azure2.KindUnresolvable - return expression - } - return resolved -} - -func (r *resolver) resolveExpressionString(code string, metadata iacTypes.Metadata) (azure2.Value, error) { - et, err := expressions.NewExpressionTree(code) - if err != nil { - return azure2.NullValue, err - } - - evaluatedValue := et.Evaluate(r.deployment) - return azure2.NewValue(evaluatedValue, metadata), nil -} diff --git a/pkg/iac/scanners/azure/resolver/resolver_test.go b/pkg/iac/scanners/azure/resolver/resolver_test.go deleted file mode 100644 index 9b9502c06c08..000000000000 --- a/pkg/iac/scanners/azure/resolver/resolver_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package resolver - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolveFunc(t *testing.T) { - - tests := []struct { - name string - expr string - expected string - }{ - { - name: "simple format call", - expr: "format('{0}/{1}', 'myPostgreSQLServer', 'log_checkpoints')", - expected: "myPostgreSQLServer/log_checkpoints", - }, - { - name: "simple format call with numbers", - expr: "format('{0} + {1} = {2}', 1, 2, 3)", - expected: "1 + 2 = 3", - }, - { - name: "format with nested format", - expr: "format('{0} + {1} = {2}', format('{0}', 1), 2, 3)", - expected: "1 + 2 = 3", - }, - { - name: "format with multiple nested format", - expr: "format('{0} + {1} = {2}', format('{0}', 1), 2, format('{0}', 3))", - expected: "1 + 2 = 3", - }, - { - name: "format with nested base64", - expr: "format('the base64 of \"hello, world\" is {0}', base64('hello, world'))", - expected: "the base64 of \"hello, world\" is aGVsbG8sIHdvcmxk", - }, - { - name: "dateTimeAdd with add a day", - expr: "dateTimeAdd(utcNow('yyyy-MM-dd'), 'P1D', 'yyyy-MM-dd')", - expected: time.Now().UTC().AddDate(0, 0, 1).Format("2006-01-02"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resolver := resolver{} - - resolvedValue, err := resolver.resolveExpressionString(tt.expr, types.NewTestMetadata()) - require.NoError(t, err) - require.Equal(t, azure2.KindString, resolvedValue.Kind) - - require.Equal(t, tt.expected, resolvedValue.AsString()) - }) - } -} - -func Test_resolveParameter(t *testing.T) { - tests := []struct { - name string - deployment *azure2.Deployment - expr string - expected string - }{ - { - name: "format call with parameter", - deployment: &azure2.Deployment{ - Parameters: []azure2.Parameter{ - { - Variable: azure2.Variable{ - Name: "dbName", - Value: azure2.NewValue("myPostgreSQLServer", types.NewTestMetadata()), - }, - }, - }, - }, - expr: "format('{0}/{1}', parameters('dbName'), 'log_checkpoints')", - expected: "myPostgreSQLServer/log_checkpoints", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resolver := resolver{ - deployment: tt.deployment, - } - - resolvedValue, err := resolver.resolveExpressionString(tt.expr, types.NewTestMetadata()) - require.NoError(t, err) - require.Equal(t, azure2.KindString, resolvedValue.Kind) - - require.Equal(t, tt.expected, resolvedValue.AsString()) - }) - } - -} diff --git a/pkg/iac/scanners/azure/value.go b/pkg/iac/scanners/azure/value.go deleted file mode 100644 index 0adc02b84268..000000000000 --- a/pkg/iac/scanners/azure/value.go +++ /dev/null @@ -1,363 +0,0 @@ -package azure - -import ( - "slices" - "strings" - "time" - - armjson2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/arm/parser/armjson" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type EvalContext struct{} - -type Kind string - -const ( - KindUnresolvable Kind = "unresolvable" - KindNull Kind = "null" - KindBoolean Kind = "boolean" - KindString Kind = "string" - KindNumber Kind = "number" - KindObject Kind = "object" - KindArray Kind = "array" - KindExpression Kind = "expression" -) - -type Value struct { - types.Metadata - rLit any - rMap map[string]Value - rArr []Value - Kind Kind - Comments []string -} - -var NullValue = Value{ - Kind: KindNull, -} - -func NewValue(value any, metadata types.Metadata) Value { - - v := Value{ - Metadata: metadata, - } - - switch ty := value.(type) { - case []any: - v.Kind = KindArray - for _, child := range ty { - if internal, ok := child.(Value); ok { - v.rArr = append(v.rArr, internal) - } else { - v.rArr = append(v.rArr, NewValue(child, metadata)) - } - } - case []Value: - v.Kind = KindArray - v.rArr = append(v.rArr, ty...) - - case map[string]any: - v.Kind = KindObject - v.rMap = make(map[string]Value) - for key, val := range ty { - if internal, ok := val.(Value); ok { - v.rMap[key] = internal - } else { - v.rMap[key] = NewValue(val, metadata) - } - } - case map[string]Value: - v.Kind = KindObject - v.rMap = make(map[string]Value) - for key, val := range ty { - v.rMap[key] = val - } - case string: - v.Kind = KindString - v.rLit = ty - case int, int64, int32, float32, float64, int8, int16, uint8, uint16, uint32, uint64: - v.Kind = KindNumber - v.rLit = ty - case bool: - v.Kind = KindBoolean - v.rLit = ty - case nil: - v.Kind = KindNull - v.rLit = ty - default: - v.Kind = KindUnresolvable - v.rLit = ty - } - - return v -} - -func (v *Value) GetMetadata() types.Metadata { - return v.Metadata -} - -func (v *Value) UnmarshalJSONWithMetadata(node armjson2.Node) error { - - v.updateValueKind(node) - - v.Metadata = node.Metadata() - - switch node.Kind() { - case armjson2.KindArray: - err := v.unmarshallArray(node) - if err != nil { - return err - } - case armjson2.KindObject: - err := v.unmarshalObject(node) - if err != nil { - return err - } - case armjson2.KindString: - err := v.unmarshalString(node) - if err != nil { - return err - } - default: - if err := node.Decode(&v.rLit); err != nil { - return err - } - } - - for _, comment := range node.Comments() { - var str string - if err := comment.Decode(&str); err != nil { - return err - } - // remove `\r` from comment when running windows - str = strings.ReplaceAll(str, "\r", "") - - v.Comments = append(v.Comments, str) - } - return nil -} - -func (v *Value) unmarshalString(node armjson2.Node) error { - var str string - if err := node.Decode(&str); err != nil { - return err - } - if strings.HasPrefix(str, "[") && !strings.HasPrefix(str, "[[") && strings.HasSuffix(str, "]") { - // function! - v.Kind = KindExpression - v.rLit = str[1 : len(str)-1] - } else { - v.rLit = str - } - return nil -} - -func (v *Value) unmarshalObject(node armjson2.Node) error { - obj := make(map[string]Value) - for i := 0; i < len(node.Content()); i += 2 { - var key string - if err := node.Content()[i].Decode(&key); err != nil { - return err - } - var val Value - if err := val.UnmarshalJSONWithMetadata(node.Content()[i+1]); err != nil { - return err - } - obj[key] = val - } - v.rMap = obj - return nil -} - -func (v *Value) unmarshallArray(node armjson2.Node) error { - var arr []Value - for _, child := range node.Content() { - var val Value - if err := val.UnmarshalJSONWithMetadata(child); err != nil { - return err - } - arr = append(arr, val) - } - v.rArr = arr - return nil -} - -func (v *Value) updateValueKind(node armjson2.Node) { - switch node.Kind() { - case armjson2.KindString: - v.Kind = KindString - case armjson2.KindNumber: - v.Kind = KindNumber - case armjson2.KindBoolean: - v.Kind = KindBoolean - case armjson2.KindObject: - v.Kind = KindObject - case armjson2.KindNull: - v.Kind = KindNull - case armjson2.KindArray: - v.Kind = KindArray - default: - panic(node.Kind()) - } -} - -func (v Value) AsString() string { - v.Resolve() - - if v.Kind != KindString { - return "" - } - - return v.rLit.(string) -} - -func (v Value) AsBool() bool { - v.Resolve() - if v.Kind != KindBoolean { - return false - } - return v.rLit.(bool) -} - -func (v Value) AsInt() int { - v.Resolve() - if v.Kind != KindNumber { - return 0 - } - return int(v.rLit.(int64)) -} - -func (v Value) AsFloat() float64 { - v.Resolve() - if v.Kind != KindNumber { - return 0 - } - return v.rLit.(float64) -} - -func (v Value) AsIntValue(defaultValue int, metadata types.Metadata) types.IntValue { - v.Resolve() - if v.Kind != KindNumber { - return types.Int(defaultValue, metadata) - } - return types.Int(v.AsInt(), metadata) -} - -func (v Value) AsBoolValue(defaultValue bool, metadata types.Metadata) types.BoolValue { - v.Resolve() - if v.Kind == KindString { - possibleValue := strings.ToLower(v.rLit.(string)) - if slices.Contains([]string{ - "true", - "1", - "yes", - "on", - "enabled", - }, possibleValue) { - return types.Bool(true, metadata) - } - } - - if v.Kind != KindBoolean { - return types.Bool(defaultValue, metadata) - } - - return types.Bool(v.rLit.(bool), v.GetMetadata()) -} - -func (v Value) EqualTo(value any) bool { - switch ty := value.(type) { - case string: - return v.AsString() == ty - default: - panic("not supported") - } -} - -func (v Value) AsStringValue(defaultValue string, metadata types.Metadata) types.StringValue { - v.Resolve() - if v.Kind != KindString { - return types.StringDefault(defaultValue, metadata) - } - return types.String(v.rLit.(string), v.Metadata) -} - -func (v Value) GetMapValue(key string) Value { - v.Resolve() - if v.Kind != KindObject { - return NullValue - } - return v.rMap[key] -} - -func (v Value) AsMap() map[string]Value { - v.Resolve() - if v.Kind != KindObject { - return nil - } - return v.rMap -} - -func (v Value) AsList() []Value { - v.Resolve() - if v.Kind != KindArray { - return nil - } - return v.rArr -} - -func (v Value) Raw() any { - switch v.Kind { - case KindArray: - // TODO: recursively build raw array - return nil - case KindObject: - // TODO: recursively build raw object - return nil - default: - return v.rLit - } -} - -func (v *Value) Resolve() { - if v.Kind != KindExpression { - return - } - // if resolver, ok := v.Metadata.Internal().(Resolver); ok { - // *v = resolver.ResolveExpression(*v) - // } -} - -func (v Value) HasKey(key string) bool { - v.Resolve() - _, ok := v.rMap[key] - return ok -} - -func (v Value) AsTimeValue(metadata types.Metadata) types.TimeValue { - v.Resolve() - if v.Kind != KindString { - return types.Time(time.Time{}, metadata) - } - if v.Kind == KindNumber { - return types.Time(time.Unix(int64(v.AsFloat()), 0), metadata) - } - t, err := time.Parse(time.RFC3339, v.rLit.(string)) - if err != nil { - return types.Time(time.Time{}, metadata) - } - return types.Time(t, metadata) -} - -func (v Value) AsStringValuesList(defaultValue string) (stringValues []types.StringValue) { - v.Resolve() - if v.Kind != KindArray { - return - } - for _, item := range v.rArr { - stringValues = append(stringValues, item.AsStringValue(defaultValue, item.Metadata)) - } - - return stringValues -} diff --git a/pkg/iac/scanners/azure/value_test.go b/pkg/iac/scanners/azure/value_test.go deleted file mode 100644 index 9392250b03d4..000000000000 --- a/pkg/iac/scanners/azure/value_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package azure - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_ValueAsInt(t *testing.T) { - val := NewValue(int64(10), types.NewTestMetadata()) - assert.Equal(t, 10, val.AsInt()) -} diff --git a/pkg/iac/scanners/cloudformation/cftypes/types.go b/pkg/iac/scanners/cloudformation/cftypes/types.go deleted file mode 100644 index 6a533aae179c..000000000000 --- a/pkg/iac/scanners/cloudformation/cftypes/types.go +++ /dev/null @@ -1,39 +0,0 @@ -package cftypes - -import "reflect" - -type CfType string - -const ( - String CfType = "string" - Int CfType = "int" - Float64 CfType = "float64" - Bool CfType = "bool" - Map CfType = "map" - List CfType = "list" - Unknown CfType = "unknown" -) - -func TypeFromGoValue(value any) CfType { - if value == nil { - return Unknown - } - switch reflect.TypeOf(value).Kind() { - case reflect.String: - return String - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return Int - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return Int - case reflect.Float32, reflect.Float64: - return Float64 - case reflect.Bool: - return Bool - case reflect.Map: - return Map - case reflect.Slice: - return List - default: - return Unknown - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/errors.go b/pkg/iac/scanners/cloudformation/parser/errors.go deleted file mode 100644 index 655f137cd271..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/errors.go +++ /dev/null @@ -1,24 +0,0 @@ -package parser - -import ( - "fmt" -) - -type InvalidContentError struct { - source string - err error -} - -func NewErrInvalidContent(source string, err error) *InvalidContentError { - return &InvalidContentError{ - source: source, - err: err, - } -} -func (e *InvalidContentError) Error() string { - return fmt.Sprintf("Invalid content in file: %s. Error: %v", e.source, e.err) -} - -func (e *InvalidContentError) Reason() error { - return e.err -} diff --git a/pkg/iac/scanners/cloudformation/parser/file_context.go b/pkg/iac/scanners/cloudformation/parser/file_context.go deleted file mode 100644 index e1c8cfa87f40..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/file_context.go +++ /dev/null @@ -1,83 +0,0 @@ -package parser - -import ( - "github.com/samber/lo" - - "github.com/aquasecurity/trivy/pkg/iac/ignore" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type SourceFormat string - -const ( - YamlSourceFormat SourceFormat = "yaml" - JsonSourceFormat SourceFormat = "json" -) - -type FileContexts []*FileContext - -type FileContext struct { - filepath string - lines []string - SourceFormat SourceFormat - Ignores ignore.Rules - Parameters map[string]*Parameter `json:"Parameters" yaml:"Parameters"` - Resources map[string]*Resource `json:"Resources" yaml:"Resources"` - Globals map[string]*Resource `json:"Globals" yaml:"Globals"` - Mappings map[string]any `json:"Mappings,omitempty" yaml:"Mappings"` - Conditions map[string]Property `json:"Conditions,omitempty" yaml:"Conditions"` -} - -func (t *FileContext) GetResourceByLogicalID(name string) *Resource { - for n, r := range t.Resources { - if name == n { - return r - } - } - return nil -} - -func (t *FileContext) GetResourcesByType(names ...string) []*Resource { - var resources []*Resource - for _, r := range t.Resources { - for _, name := range names { - if name == r.Type() { - // - resources = append(resources, r) - } - } - } - return resources -} - -func (t *FileContext) Metadata() iacTypes.Metadata { - rng := iacTypes.NewRange(t.filepath, 1, len(t.lines), "", nil) - - return iacTypes.NewMetadata(rng, NewCFReference("Template", rng).String()) -} - -func (t *FileContext) overrideParameters(params map[string]any) { - for key := range t.Parameters { - if val, ok := params[key]; ok { - t.Parameters[key].UpdateDefault(val) - } - } -} - -func (t *FileContext) missingParameterValues() []string { - var missing []string - for key := range t.Parameters { - if t.Parameters[key].inner.Default == nil { - missing = append(missing, key) - } - } - return missing -} - -func (t *FileContext) stripNullProperties() { - for _, resource := range t.Resources { - resource.Inner.Properties = lo.OmitBy(resource.Inner.Properties, func(k string, v *Property) bool { - return v.IsNil() - }) - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/file_context_test.go b/pkg/iac/scanners/cloudformation/parser/file_context_test.go deleted file mode 100644 index 80bdfa72eb05..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/file_context_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package parser - -import ( - "slices" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFileContext_OverrideParameters(t *testing.T) { - tests := []struct { - name string - ctx FileContext - arg map[string]any - expected map[string]*Parameter - }{ - { - name: "happy", - ctx: FileContext{ - Parameters: map[string]*Parameter{ - "BucketName": { - inner: parameterInner{ - Type: "String", - Default: "test", - }, - }, - "QueueName": { - inner: parameterInner{ - Type: "String", - }, - }, - }, - }, - arg: map[string]any{ - "BucketName": "test2", - "QueueName": "test", - "SomeKey": "some_value", - }, - expected: map[string]*Parameter{ - "BucketName": { - inner: parameterInner{ - Type: "String", - Default: "test2", - }, - }, - "QueueName": { - inner: parameterInner{ - Type: "String", - Default: "test", - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.ctx.overrideParameters(tt.arg) - assert.Equal(t, tt.expected, tt.ctx.Parameters) - }) - } -} - -func TestFileContext_MissingParameterValues(t *testing.T) { - - tests := []struct { - name string - ctx FileContext - expected []string - }{ - { - name: "happy", - ctx: FileContext{ - Parameters: map[string]*Parameter{ - "BucketName": { - inner: parameterInner{ - Type: "String", - Default: "test", - }, - }, - "QueueName": { - inner: parameterInner{ - Type: "String", - }, - }, - "KMSKey": { - inner: parameterInner{ - Type: "String", - Default: nil, - }, - }, - }, - }, - expected: []string{"KMSKey", "QueueName"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.ctx.missingParameterValues() - slices.Sort(got) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_and.go b/pkg/iac/scanners/cloudformation/parser/fn_and.go deleted file mode 100644 index a155120e413a..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_and.go +++ /dev/null @@ -1,40 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveAnd(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::And"].AsList() - - if len(refValue) < 2 { - return abortIntrinsic(property, "Fn::And should have at least 2 values, returning original Property") - } - - results := make([]bool, len(refValue)) - for i := 0; i < len(refValue); i++ { - - r := false - if refValue[i].IsBool() { - r = refValue[i].AsBool() - } - - results[i] = r - } - - theSame := allSameStrings(results) - return property.deriveResolved(cftypes.Bool, theSame), true -} - -func allSameStrings(a []bool) bool { - for i := 1; i < len(a); i++ { - if a[i] != a[0] { - return false - } - } - return true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_and_test.go b/pkg/iac/scanners/cloudformation/parser/fn_and_test.go deleted file mode 100644 index 5222decef06f..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_and_test.go +++ /dev/null @@ -1,185 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_and_value(t *testing.T) { - - property1 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - - property2 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - andProperty := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::And": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - property1, - property2, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(andProperty) - require.True(t, success) - - assert.True(t, resolvedProperty.IsTrue()) -} - -func Test_resolve_and_value_not_the_same(t *testing.T) { - - property1 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - }, - }, - }, - }, - }, - } - - property2 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - andProperty := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::And": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - property1, - property2, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(andProperty) - require.True(t, success) - - assert.False(t, resolvedProperty.IsTrue()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_base64.go b/pkg/iac/scanners/cloudformation/parser/fn_base64.go deleted file mode 100644 index ad94ed08d6e8..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_base64.go +++ /dev/null @@ -1,19 +0,0 @@ -package parser - -import ( - "encoding/base64" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveBase64(property *Property) (*Property, bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Base64"].AsString() - - retVal := base64.StdEncoding.EncodeToString([]byte(refValue)) - - return property.deriveResolved(cftypes.String, retVal), true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_base64_test.go b/pkg/iac/scanners/cloudformation/parser/fn_base64_test.go deleted file mode 100644 index 7fdc77b30fa5..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_base64_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_base64_value(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Base64": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "HelloWorld", - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.Equal(t, "SGVsbG9Xb3JsZA==", resolvedProperty.AsString()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_builtin.go b/pkg/iac/scanners/cloudformation/parser/fn_builtin.go deleted file mode 100644 index 6859fa0f4ff2..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_builtin.go +++ /dev/null @@ -1,65 +0,0 @@ -package parser - -import ( - "errors" - "net" - - "github.com/apparentlymart/go-cidr/cidr" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func GetAzs(property *Property) (*Property, bool) { - return property.deriveResolved(cftypes.List, []*Property{ - property.deriveResolved(cftypes.String, "us-east-1a"), - property.deriveResolved(cftypes.String, "us-east-1a"), - property.deriveResolved(cftypes.String, "us-east-1a"), - }), true -} - -func GetCidr(property *Property) (*Property, bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Cidr"] - if refValue.IsNotList() || len(refValue.AsList()) != 3 { - return abortIntrinsic(property, "Fn::Cidr expects a list of 3 attributes") - } - - listParts := refValue.AsList() - ipaddressProp := listParts[0] - ipAddress := "10.0.0.0/2" - if ipaddressProp.IsString() { - ipAddress = ipaddressProp.AsString() - } - count := listParts[1].AsInt() - bit := listParts[2].AsInt() - - ranges, err := calculateCidrs(ipAddress, count, bit, property) - if err != nil { - return abortIntrinsic(property, "Could not calculate the required ranges") - } - return property.deriveResolved(cftypes.List, ranges), true -} - -func calculateCidrs(ipaddress string, count, bit int, original *Property) ([]*Property, error) { - - var cidrProperties []*Property - - _, network, err := net.ParseCIDR(ipaddress) - if err != nil { - return nil, err - } - - for i := 0; i < count; i++ { - next, err := cidr.Subnet(network, bit, i) - if err != nil { - return nil, errors.New("failed to create cidr blocks") - } - - cidrProperties = append(cidrProperties, original.deriveResolved(cftypes.String, next.String())) - } - - return cidrProperties, nil -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_builtin_test.go b/pkg/iac/scanners/cloudformation/parser/fn_builtin_test.go deleted file mode 100644 index c5fbce41b489..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_builtin_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_cidr_generator(t *testing.T) { - - original := &Property{ - ctx: nil, - name: "cidr", - comment: "", - Inner: PropertyInner{ - Type: "", - Value: nil, - }, - } - - ranges, err := calculateCidrs("10.1.0.0/16", 4, 4, original) - require.NoError(t, err) - require.Len(t, ranges, 4) - - results := make(map[int]string) - for i, property := range ranges { - value := property.AsString() - results[i] = value - } - - assert.Equal(t, "10.1.0.0/20", results[0]) - assert.Equal(t, "10.1.16.0/20", results[1]) - assert.Equal(t, "10.1.32.0/20", results[2]) - assert.Equal(t, "10.1.48.0/20", results[3]) -} - -func Test_cidr_generator_8_bits(t *testing.T) { - original := &Property{ - ctx: nil, - name: "cidr", - comment: "", - Inner: PropertyInner{ - Type: "", - Value: nil, - }, - } - - ranges, err := calculateCidrs("10.1.0.0/16", 4, 8, original) - require.NoError(t, err) - require.Len(t, ranges, 4) - - results := make(map[int]string) - for i, property := range ranges { - value := property.AsString() - results[i] = value - } - - assert.Equal(t, "10.1.0.0/24", results[0]) - assert.Equal(t, "10.1.1.0/24", results[1]) - assert.Equal(t, "10.1.2.0/24", results[2]) - assert.Equal(t, "10.1.3.0/24", results[3]) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_condition.go b/pkg/iac/scanners/cloudformation/parser/fn_condition.go deleted file mode 100644 index 8d5c923936ab..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_condition.go +++ /dev/null @@ -1,21 +0,0 @@ -package parser - -func ResolveCondition(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refProp := property.AsMap()["Condition"] - if refProp.IsNotString() { - return nil, false - } - refValue := refProp.AsString() - - for k, prop := range property.ctx.Conditions { - if k == refValue { - return prop.resolveValue() - } - } - - return nil, false -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_condition_test.go b/pkg/iac/scanners/cloudformation/parser/fn_condition_test.go deleted file mode 100644 index c5f0af721c36..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_condition_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func Test_resolve_condition_value(t *testing.T) { - - fctx := new(FileContext) - fctx.Conditions = map[string]Property{ - "SomeCondition": { - ctx: fctx, - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - ctx: fctx, - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "some val", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "some val", - }, - }, - }, - }, - }, - }, - }, - }, - "EnableVersioning": { - ctx: fctx, - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Condition": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "SomeCondition", - }, - }, - }, - }, - }, - } - - property := &Property{ - ctx: fctx, - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::If": { - ctx: fctx, - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "EnableVersioning", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "Enabled", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "Suspended", - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.Equal(t, "Enabled", resolvedProperty.AsString()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_equals.go b/pkg/iac/scanners/cloudformation/parser/fn_equals.go deleted file mode 100644 index 4043735849a2..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_equals.go +++ /dev/null @@ -1,21 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveEquals(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Equals"].AsList() - - if len(refValue) != 2 { - return abortIntrinsic(property, "Fn::Equals should have exactly 2 values, returning original Property") - } - - propA, _ := refValue[0].resolveValue() - propB, _ := refValue[1].resolveValue() - return property.deriveResolved(cftypes.Bool, propA.EqualTo(propB.RawValue())), true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_equals_test.go b/pkg/iac/scanners/cloudformation/parser/fn_equals_test.go deleted file mode 100644 index 0e550d162986..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_equals_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_equals_value(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.True(t, resolvedProperty.IsTrue()) -} - -func Test_resolve_equals_value_to_false(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.False(t, resolvedProperty.IsTrue()) -} - -func Test_resolve_equals_value_to_true_when_boolean(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.Bool, - Value: true, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.Bool, - Value: true, - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - assert.True(t, resolvedProperty.IsTrue()) -} - -func Test_resolve_equals_value_when_one_is_a_reference(t *testing.T) { - - property := &Property{ - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "staging", - }, - }, - { - ctx: &FileContext{ - filepath: "", - Parameters: map[string]*Parameter{ - "Environment": { - inner: parameterInner{ - Type: "string", - Default: "staging", - }, - }, - }, - }, - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Ref": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "Environment", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.True(t, resolvedProperty.IsTrue()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_find_in_map.go b/pkg/iac/scanners/cloudformation/parser/fn_find_in_map.go deleted file mode 100644 index b379cba3527e..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_find_in_map.go +++ /dev/null @@ -1,45 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveFindInMap(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::FindInMap"].AsList() - - if len(refValue) != 3 { - return abortIntrinsic(property, "Fn::FindInMap should have exactly 3 values, returning original Property") - } - - mapName := refValue[0].AsString() - topLevelKey := refValue[1].AsString() - secondaryLevelKey := refValue[2].AsString() - - if property.ctx == nil { - return abortIntrinsic(property, "the property does not have an attached context, returning original Property") - } - - m, ok := property.ctx.Mappings[mapName] - if !ok { - return abortIntrinsic(property, "could not find map %s, returning original Property") - } - - mapContents := m.(map[string]any) - - k, ok := mapContents[topLevelKey] - if !ok { - return abortIntrinsic(property, "could not find %s in the %s map, returning original Property", topLevelKey, mapName) - } - - mapValues := k.(map[string]any) - - if prop, ok := mapValues[secondaryLevelKey]; !ok { - return abortIntrinsic(property, "could not find a value for %s in %s, returning original Property", secondaryLevelKey, topLevelKey) - } else { - return property.deriveResolved(cftypes.String, prop), true - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_find_in_map_test.go b/pkg/iac/scanners/cloudformation/parser/fn_find_in_map_test.go deleted file mode 100644 index 254c0b63c464..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_find_in_map_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_resolve_find_in_map_value(t *testing.T) { - - source := `--- -Parameters: - Environment: - Type: String - Default: production -Mappings: - CacheNodeTypes: - production: - NodeType: cache.t2.large - test: - NodeType: cache.t2.small - dev: - NodeType: cache.t2.micro -Resources: - ElasticacheSecurityGroup: - Type: 'AWS::EC2::SecurityGroup' - Properties: - GroupDescription: Elasticache Security Group - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 11211 - ToPort: 11211 - SourceSecurityGroupName: !Ref InstanceSecurityGroup - ElasticacheCluster: - Type: 'AWS::ElastiCache::CacheCluster' - Properties: - Engine: memcached - CacheNodeType: !FindInMap [ CacheNodeTypes, production, NodeType ] - NumCacheNodes: '1' - VpcSecurityGroupIds: - - !GetAtt - - ElasticacheSecurityGroup - - GroupId -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("ElasticacheCluster") - assert.NotNil(t, testRes) - - nodeTypeProp := testRes.GetStringProperty("CacheNodeType", "") - assert.Equal(t, "cache.t2.large", nodeTypeProp.Value()) -} - -func Test_resolve_find_in_map_with_nested_intrinsic_value(t *testing.T) { - - source := `--- -Parameters: - Environment: - Type: String - Default: dev -Mappings: - CacheNodeTypes: - production: - NodeType: cache.t2.large - test: - NodeType: cache.t2.small - dev: - NodeType: cache.t2.micro -Resources: - ElasticacheSecurityGroup: - Type: 'AWS::EC2::SecurityGroup' - Properties: - GroupDescription: Elasticache Security Group - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 11211 - ToPort: 11211 - SourceSecurityGroupName: !Ref InstanceSecurityGroup - ElasticacheCluster: - Type: 'AWS::ElastiCache::CacheCluster' - Properties: - Engine: memcached - CacheNodeType: !FindInMap [ CacheNodeTypes, !Ref Environment, NodeType ] - NumCacheNodes: '1' - VpcSecurityGroupIds: - - !GetAtt - - ElasticacheSecurityGroup - - GroupId -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("ElasticacheCluster") - assert.NotNil(t, testRes) - - nodeTypeProp := testRes.GetStringProperty("CacheNodeType", "") - assert.Equal(t, "cache.t2.micro", nodeTypeProp.Value()) -} - -func Test_InferType(t *testing.T) { - source := `--- -Mappings: - ApiDB: - MultiAZ: - development: False -Resources: - ApiDB: - Type: AWS::RDS::DBInstance - Properties: - MultiAZ: !FindInMap [ApiDB, MultiAZ, development] -` - - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("ApiDB") - require.NotNil(t, testRes) - - nodeTypeProp := testRes.GetBoolProperty("MultiAZ") - assert.False(t, nodeTypeProp.Value()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_get_attr.go b/pkg/iac/scanners/cloudformation/parser/fn_get_attr.go deleted file mode 100644 index f6754d16a9b3..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_get_attr.go +++ /dev/null @@ -1,46 +0,0 @@ -package parser - -import ( - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveGetAtt(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValueProp := property.AsMap()["Fn::GetAtt"] - - var refValue []string - - if refValueProp.IsString() { - refValue = strings.Split(refValueProp.AsString(), ".") - } - - if refValueProp.IsList() { - for _, p := range refValueProp.AsList() { - refValue = append(refValue, p.AsString()) - } - } - - if len(refValue) != 2 { - return abortIntrinsic(property, "Fn::GetAtt should have exactly 2 values, returning original Property") - } - - logicalId := refValue[0] - attribute := refValue[1] - - referencedResource := property.ctx.GetResourceByLogicalID(logicalId) - if referencedResource == nil || referencedResource.IsNil() { - return property.deriveResolved(cftypes.String, ""), true - } - - referencedProperty := referencedResource.GetProperty(attribute) - if referencedProperty.IsNil() { - return property.deriveResolved(cftypes.String, referencedResource.ID()), true - } - - return property.deriveResolved(referencedProperty.Type(), referencedProperty.RawValue()), true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_get_attr_test.go b/pkg/iac/scanners/cloudformation/parser/fn_get_attr_test.go deleted file mode 100644 index 83e059c5c0f9..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_get_attr_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_resolve_get_attr_value(t *testing.T) { - - source := `--- -Resources: - ElasticacheSecurityGroup: - Type: 'AWS::EC2::SecurityGroup' - Properties: - GroupDescription: Elasticache Security Group - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 11211 - ToPort: 11211 - SourceSecurityGroupName: !Ref InstanceSecurityGroup - ElasticacheCluster: - Type: 'AWS::ElastiCache::CacheCluster' - Properties: - Engine: memcached - CacheNodeType: cache.t2.micro - NumCacheNodes: '1' - VpcSecurityGroupIds: - - !GetAtt - - ElasticacheSecurityGroup - - GroupId -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("ElasticacheCluster") - assert.NotNil(t, testRes) - - sgProp := testRes.GetProperty("VpcSecurityGroupIds") - require.True(t, sgProp.IsNotNil()) - require.True(t, sgProp.IsList()) - - for _, property := range sgProp.AsList() { - resolved, success := ResolveIntrinsicFunc(property) - require.True(t, success) - assert.True(t, resolved.IsNotNil()) - } - -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_if.go b/pkg/iac/scanners/cloudformation/parser/fn_if.go deleted file mode 100644 index d444952ff38a..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_if.go +++ /dev/null @@ -1,40 +0,0 @@ -package parser - -func ResolveIf(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::If"].AsList() - - if len(refValue) != 3 { - return abortIntrinsic(property, "Fn::If should have exactly 3 values, returning original Property") - } - - condition, _ := refValue[0].resolveValue() - trueState, _ := refValue[1].resolveValue() - falseState, _ := refValue[2].resolveValue() - - conditionMet := false - - con, _ := condition.resolveValue() - if con.IsBool() { - conditionMet = con.AsBool() - } else if property.ctx.Conditions != nil && - condition.IsString() { - - condition := property.ctx.Conditions[condition.AsString()] - if condition.isFunction() { - con, _ := condition.resolveValue() - if con.IsBool() { - conditionMet = con.AsBool() - } - } - } - - if conditionMet { - return trueState, true - } else { - return falseState, true - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_if_test.go b/pkg/iac/scanners/cloudformation/parser/fn_if_test.go deleted file mode 100644 index 4ecd5f9483c8..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_if_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_if_value(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::If": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.Bool, - Value: true, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.Equal(t, "foo", resolvedProperty.String()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_join.go b/pkg/iac/scanners/cloudformation/parser/fn_join.go deleted file mode 100644 index e1d39dc702f7..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_join.go +++ /dev/null @@ -1,34 +0,0 @@ -package parser - -import ( - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveJoin(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Join"].AsList() - - if len(refValue) != 2 { - return abortIntrinsic(property, "Fn::Join should have exactly 2 values, returning original Property") - } - - joiner := refValue[0].AsString() - items := refValue[1].AsList() - - var itemValues []string - for _, item := range items { - resolved, success := item.resolveValue() - if success { - itemValues = append(itemValues, resolved.AsString()) - } - } - - joined := strings.Join(itemValues, joiner) - - return property.deriveResolved(cftypes.String, joined), true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_join_test.go b/pkg/iac/scanners/cloudformation/parser/fn_join_test.go deleted file mode 100644 index 628ade9a1acf..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_join_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_join_value(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Join": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "::", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "s3", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "part1", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "part2", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.Equal(t, "s3::part1::part2", resolvedProperty.AsString()) -} - -func Test_resolve_join_value_with_reference(t *testing.T) { - - property := &Property{ - ctx: &FileContext{ - filepath: "", - Parameters: map[string]*Parameter{ - "Environment": { - inner: parameterInner{ - Type: "string", - Default: "staging", - }, - }, - }, - }, - name: "EnvironmentBucket", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Join": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "::", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "s3", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "part1", - }, - }, - { - ctx: &FileContext{ - filepath: "", - Parameters: map[string]*Parameter{ - "Environment": { - inner: parameterInner{ - Type: "string", - Default: "staging", - }, - }, - }, - }, - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Ref": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "Environment", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.Equal(t, "s3::part1::staging", resolvedProperty.AsString()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_length.go b/pkg/iac/scanners/cloudformation/parser/fn_length.go deleted file mode 100644 index 2026dd4170e9..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_length.go +++ /dev/null @@ -1,26 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveLength(property *Property) (*Property, bool) { - if !property.isFunction() { - return property, true - } - - val := property.AsMap()["Fn::Length"] - if val.IsList() { - return property.deriveResolved(cftypes.Int, val.Len()), true - } else if val.IsMap() { - resolved, _ := val.resolveValue() - - if resolved.IsList() { - return property.deriveResolved(cftypes.Int, resolved.Len()), true - } - return resolved, false - } - - return property, false - -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_length_test.go b/pkg/iac/scanners/cloudformation/parser/fn_length_test.go deleted file mode 100644 index 402211bdcd98..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_length_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func Test_ResolveLength_WhenPropIsArray(t *testing.T) { - prop := &Property{ - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Length": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.Int, - Value: 1, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "IntParameter", - }, - }, - }, - }, - }, - }, - }, - } - resolved, ok := ResolveIntrinsicFunc(prop) - require.True(t, ok) - require.True(t, resolved.IsInt()) - require.Equal(t, 2, resolved.AsInt()) -} - -func Test_ResolveLength_WhenPropIsIntrinsicFunction(t *testing.T) { - fctx := &FileContext{ - Parameters: map[string]*Parameter{ - "SomeParameter": { - inner: parameterInner{ - Type: "string", - Default: "a|b|c|d", - }, - }, - }, - } - prop := &Property{ - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Length": { - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Split": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "|", - }, - }, - { - ctx: fctx, - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Ref": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "SomeParameter", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - resolved, ok := ResolveIntrinsicFunc(prop) - require.True(t, ok) - require.True(t, resolved.IsInt()) - require.Equal(t, 4, resolved.AsInt()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_not.go b/pkg/iac/scanners/cloudformation/parser/fn_not.go deleted file mode 100644 index fa76db76319b..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_not.go +++ /dev/null @@ -1,25 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveNot(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Not"].AsList() - - if len(refValue) != 1 { - return abortIntrinsic(property, "Fn::No should have at only 1 values, returning original Property") - } - - funcToInvert, _ := refValue[0].resolveValue() - - if funcToInvert.IsBool() { - return property.deriveResolved(cftypes.Bool, !funcToInvert.AsBool()), true - } - - return property, false -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_not_test.go b/pkg/iac/scanners/cloudformation/parser/fn_not_test.go deleted file mode 100644 index 563964c32290..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_not_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_not_value(t *testing.T) { - property1 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - }, - }, - }, - }, - }, - } - - notProperty := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Not": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - property1, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(notProperty) - require.True(t, success) - - assert.True(t, resolvedProperty.IsTrue()) -} - -func Test_resolve_not_value_when_true(t *testing.T) { - property1 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - - notProperty := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Not": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - property1, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(notProperty) - require.True(t, success) - - assert.False(t, resolvedProperty.IsTrue()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_or.go b/pkg/iac/scanners/cloudformation/parser/fn_or.go deleted file mode 100644 index 48fd802d1065..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_or.go +++ /dev/null @@ -1,41 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveOr(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Or"].AsList() - - if len(refValue) < 2 { - return abortIntrinsic(property, "Fn::Or should have at least 2 values, returning original Property") - } - - results := make([]bool, len(refValue)) - for i := 0; i < len(refValue); i++ { - - r := false - if refValue[i].IsBool() { - r = refValue[i].AsBool() - } - - results[i] = r - } - - atleastOne := atleastOne(results) - return property.deriveResolved(cftypes.Bool, atleastOne), true -} - -func atleastOne(a []bool) bool { - for _, b := range a { - if b { - return true - } - } - - return false -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_or_test.go b/pkg/iac/scanners/cloudformation/parser/fn_or_test.go deleted file mode 100644 index 7e0e1dcf0f0a..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_or_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_or_value(t *testing.T) { - property1 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - }, - }, - }, - }, - }, - } - - property2 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - orProperty := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Or": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - property1, - property2, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(orProperty) - require.True(t, success) - - assert.True(t, resolvedProperty.IsTrue()) -} - -func Test_resolve_or_value_when_neither_true(t *testing.T) { - property1 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - }, - }, - }, - }, - }, - } - - property2 := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bar", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }, - } - orProperty := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Or": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - property1, - property2, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(orProperty) - require.True(t, success) - - assert.False(t, resolvedProperty.IsTrue()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_ref.go b/pkg/iac/scanners/cloudformation/parser/fn_ref.go deleted file mode 100644 index afc6ead7cf20..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_ref.go +++ /dev/null @@ -1,55 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveReference(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refProp := property.AsMap()["Ref"] - if refProp.IsNotString() { - return property, false - } - refValue := refProp.AsString() - - if pseudo, ok := pseudoParameters[refValue]; ok { - return property.deriveResolved(pseudo.t, pseudo.val), true - } - - if property.ctx == nil { - return property, false - } - - var param *Parameter - for k := range property.ctx.Parameters { - if k != refValue { - continue - } - param = property.ctx.Parameters[k] - resolvedType := param.Type() - - switch param.Default().(type) { - case bool: - resolvedType = cftypes.Bool - case string: - resolvedType = cftypes.String - case int: - resolvedType = cftypes.Int - } - - resolved = property.deriveResolved(resolvedType, param.Default()) - return resolved, true - } - - for k := range property.ctx.Resources { - if k == refValue { - res := property.ctx.Resources[k] - resolved = property.deriveResolved(cftypes.String, res.ID()) - break - } - } - return resolved, true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_ref_test.go b/pkg/iac/scanners/cloudformation/parser/fn_ref_test.go deleted file mode 100644 index 7f0e141b96a6..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_ref_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_resolve_referenced_value(t *testing.T) { - - property := &Property{ - ctx: &FileContext{ - filepath: "", - Parameters: map[string]*Parameter{ - "BucketName": { - inner: parameterInner{ - Type: "string", - Default: "someBucketName", - }, - }, - }, - }, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Ref": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "BucketName", - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - - assert.Equal(t, "someBucketName", resolvedProperty.AsString()) -} - -func Test_property_value_correct_when_not_reference(t *testing.T) { - - property := &Property{ - ctx: &FileContext{ - filepath: "", - }, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.String, - Value: "someBucketName", - }, - } - - // should fail when trying to resolve function that is not in fact a function - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.False(t, success) - - assert.Equal(t, "someBucketName", resolvedProperty.AsString()) -} - -func Test_resolve_ref_with_pseudo_value(t *testing.T) { - source := `--- -Resources: - TestInstance: - Type: AWS::EC2::Instance - Properties: - ImageId: "ami-79fd7eee" - KeyName: !Join [":", ["aws", !Ref AWS::Region, "key" ]] -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("TestInstance") - require.NotNil(t, testRes) - - keyNameProp := testRes.GetProperty("KeyName") - require.NotNil(t, keyNameProp) - - assert.Equal(t, "aws:eu-west-1:key", keyNameProp.AsString()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_select.go b/pkg/iac/scanners/cloudformation/parser/fn_select.go deleted file mode 100644 index c528223a2325..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_select.go +++ /dev/null @@ -1,41 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveSelect(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Select"].AsList() - - if len(refValue) != 2 { - return abortIntrinsic(property, "Fn::Select should have exactly 2 values, returning original Property") - } - - index := refValue[0] - list := refValue[1] - - if index.IsNotInt() { - if index.IsConvertableTo(cftypes.Int) { - // - index = index.ConvertTo(cftypes.Int) - } else { - return abortIntrinsic(property, "index on property [%s] should be an int, returning original Property", property.name) - } - } - - if list.IsNotList() { - return abortIntrinsic(property, "list on property [%s] should be a list, returning original Property", property.name) - } - - listItems := list.AsList() - - if len(listItems) <= index.AsInt() { - return nil, false - } - - return listItems[index.AsInt()], true -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_select_test.go b/pkg/iac/scanners/cloudformation/parser/fn_select_test.go deleted file mode 100644 index 92b634457b2d..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_select_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_resolve_select_value(t *testing.T) { - - source := `--- -Parameters: - EngineIndex: - Type: Integer - Default: 1 -Resources: - ElasticacheCluster: - Type: 'AWS::ElastiCache::CacheCluster' - Properties: - Engine: !Select [ !Ref EngineIndex, [memcached, redis ]] - CacheNodeType: cache.t2.micro - NumCacheNodes: '1' -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("ElasticacheCluster") - assert.NotNil(t, testRes) - - engineProp := testRes.GetProperty("Engine") - require.True(t, engineProp.IsNotNil()) - require.True(t, engineProp.IsString()) - - require.Equal(t, "redis", engineProp.AsString()) -} - -func Test_SelectPseudoListParam(t *testing.T) { - src := `--- -Resources: - myASGrpOne: - Type: AWS::AutoScaling::AutoScalingGroup - Version: "2009-05-15" - Properties: - AvailabilityZones: - - "us-east-1a" - LaunchConfigurationName: - Ref: MyLaunchConfiguration - MinSize: "0" - MaxSize: "0" - NotificationConfigurations: - - TopicARN: - Fn::Select: - - "1" - - Ref: AWS::NotificationARNs - NotificationTypes: - - autoscaling:EC2_INSTANCE_LAUNCH - - autoscaling:EC2_INSTANCE_LAUNCH_ERROR - -` - - ctx := createTestFileContext(t, src) - require.NotNil(t, ctx) - - resource := ctx.GetResourceByLogicalID("myASGrpOne") - require.NotNil(t, resource) - - notification := resource.GetProperty("NotificationConfigurations") - require.True(t, notification.IsNotNil()) - require.True(t, notification.IsList()) - first := notification.AsList()[0] - require.True(t, first.IsMap()) - topic, ok := first.AsMap()["TopicARN"] - require.True(t, ok) - require.Equal(t, "notification::arn::2", topic.AsString()) - -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_split.go b/pkg/iac/scanners/cloudformation/parser/fn_split.go deleted file mode 100644 index cddda20ef190..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_split.go +++ /dev/null @@ -1,44 +0,0 @@ -package parser - -import ( - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveSplit(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Split"].AsList() - - if len(refValue) != 2 { - return abortIntrinsic(property, "Fn::Split should have exactly 2 values, returning original Property") - } - - delimiterProp := refValue[0] - splitProp := refValue[1] - - if !splitProp.IsString() || !delimiterProp.IsString() { - abortIntrinsic(property, "Fn::Split requires two strings as input, returning original Property") - - } - - propertyList := createPropertyList(splitProp, delimiterProp, property) - - return property.deriveResolved(cftypes.List, propertyList), true -} - -func createPropertyList(splitProp, delimiterProp, parent *Property) []*Property { - - splitString := splitProp.AsString() - delimiter := delimiterProp.AsString() - - splits := strings.Split(splitString, delimiter) - var props []*Property - for _, split := range splits { - props = append(props, parent.deriveResolved(cftypes.String, split)) - } - return props -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_split_test.go b/pkg/iac/scanners/cloudformation/parser/fn_split_test.go deleted file mode 100644 index 33ca111c15ea..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_split_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -/* - Fn::Split: ["::", "s3::bucket::to::split"] - -*/ - -func Test_resolve_split_value(t *testing.T) { - - property := &Property{ - ctx: &FileContext{}, - name: "BucketName", - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Split": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "::", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "s3::bucket::to::split", - }, - }, - }, - }, - }, - }, - }, - } - - resolvedProperty, success := ResolveIntrinsicFunc(property) - require.True(t, success) - assert.True(t, resolvedProperty.IsNotNil()) - assert.True(t, resolvedProperty.IsList()) - listContents := resolvedProperty.AsList() - assert.Len(t, listContents, 4) - -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_sub.go b/pkg/iac/scanners/cloudformation/parser/fn_sub.go deleted file mode 100644 index ad990bba3b32..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_sub.go +++ /dev/null @@ -1,71 +0,0 @@ -package parser - -import ( - "fmt" - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func ResolveSub(property *Property) (resolved *Property, success bool) { - if !property.isFunction() { - return property, true - } - - refValue := property.AsMap()["Fn::Sub"] - - if refValue.IsString() { - return resolveStringSub(refValue, property), true - } - - if refValue.IsList() { - return resolveMapSub(refValue, property) - } - - return property, false -} - -func resolveMapSub(refValue, original *Property) (*Property, bool) { - refValues := refValue.AsList() - if len(refValues) != 2 { - return abortIntrinsic(original, "Fn::Sub with list expects 2 values, returning original property") - } - - workingString := refValues[0].AsString() - components := refValues[1].AsMap() - - for k, v := range components { - replacement := "[failed to resolve]" - switch v.Type() { - case cftypes.Map: - resolved, _ := ResolveIntrinsicFunc(v) - replacement = resolved.AsString() - case cftypes.String: - replacement = v.AsString() - case cftypes.Int: - replacement = strconv.Itoa(v.AsInt()) - case cftypes.Bool: - replacement = strconv.FormatBool(v.AsBool()) - case cftypes.List: - var parts []string - for _, p := range v.AsList() { - parts = append(parts, p.String()) - } - replacement = fmt.Sprintf("[%s]", strings.Join(parts, ", ")) - } - workingString = strings.ReplaceAll(workingString, fmt.Sprintf("${%s}", k), replacement) - } - - return original.deriveResolved(cftypes.String, workingString), true -} - -func resolveStringSub(refValue, original *Property) *Property { - workingString := refValue.AsString() - - for k, param := range pseudoParameters { - workingString = strings.ReplaceAll(workingString, fmt.Sprintf("${%s}", k), fmt.Sprintf("%v", param.getRawValue())) - } - - return original.deriveResolved(cftypes.String, workingString) -} diff --git a/pkg/iac/scanners/cloudformation/parser/fn_sub_test.go b/pkg/iac/scanners/cloudformation/parser/fn_sub_test.go deleted file mode 100644 index 5ab98a59692b..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/fn_sub_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_resolve_sub_value(t *testing.T) { - source := `--- -Resources: - TestInstance: - Type: AWS::EC2::Instance - Properties: - ImageId: "ami-79fd7eee" - KeyName: "testkey" - UserData: - !Sub | - #!/bin/bash -xe - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region} -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("TestInstance") - require.NotNil(t, testRes) - - userDataProp := testRes.GetProperty("UserData") - require.NotNil(t, userDataProp) - - assert.Equal(t, "#!/bin/bash -xe\nyum update -y aws-cfn-bootstrap\n/opt/aws/bin/cfn-init -v --stack cfsec-test-stack --resource LaunchConfig --configsets wordpress_install --region eu-west-1\n/opt/aws/bin/cfn-signal -e $? --stack cfsec-test-stack --resource WebServerGroup --region eu-west-1\n", userDataProp.AsString()) -} - -func Test_resolve_sub_value_with_base64(t *testing.T) { - - source := `--- -Resources: - TestInstance: - Type: AWS::EC2::Instance - Properties: - ImageId: "ami-79fd7eee" - KeyName: "testkey" - UserData: - Fn::Base64: - !Sub | - #!/bin/bash -xe - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region}` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("TestInstance") - require.NotNil(t, testRes) - - userDataProp := testRes.GetProperty("UserData") - require.NotNil(t, userDataProp) - - assert.Equal(t, "IyEvYmluL2Jhc2ggLXhlCnl1bSB1cGRhdGUgLXkgYXdzLWNmbi1ib290c3RyYXAKL29wdC9hd3MvYmluL2Nmbi1pbml0IC12IC0tc3RhY2sgY2ZzZWMtdGVzdC1zdGFjayAtLXJlc291cmNlIExhdW5jaENvbmZpZyAtLWNvbmZpZ3NldHMgd29yZHByZXNzX2luc3RhbGwgLS1yZWdpb24gZXUtd2VzdC0xCi9vcHQvYXdzL2Jpbi9jZm4tc2lnbmFsIC1lICQ/IC0tc3RhY2sgY2ZzZWMtdGVzdC1zdGFjayAtLXJlc291cmNlIFdlYlNlcnZlckdyb3VwIC0tcmVnaW9uIGV1LXdlc3QtMQ==", userDataProp.AsString()) -} - -func Test_resolve_sub_value_with_map(t *testing.T) { - - source := `--- -Parameters: - RootDomainName: - Type: String - Default: somedomain.com -Resources: - TestDistribution: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - DefaultCacheBehavior: - TargetOriginId: target - ViewerProtocolPolicy: https-only - Enabled: true - Origins: - - DomainName: - !Sub - - www.${Domain} - - { Domain: !Ref RootDomainName } - Id: somedomain1 - - -` - ctx := createTestFileContext(t, source) - require.NotNil(t, ctx) - - testRes := ctx.GetResourceByLogicalID("TestDistribution") - require.NotNil(t, testRes) - - originsList := testRes.GetProperty("DistributionConfig.Origins") - - domainNameProp := originsList.AsList()[0].GetProperty("DomainName") - require.NotNil(t, domainNameProp) - - assert.Equal(t, "www.somedomain.com", domainNameProp.AsString()) - -} diff --git a/pkg/iac/scanners/cloudformation/parser/intrinsics.go b/pkg/iac/scanners/cloudformation/parser/intrinsics.go deleted file mode 100644 index cda2bde29e41..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/intrinsics.go +++ /dev/null @@ -1,106 +0,0 @@ -package parser - -import ( - "fmt" - "strings" - - "gopkg.in/yaml.v3" -) - -var intrinsicFuncs map[string]func(property *Property) (*Property, bool) - -func init() { - intrinsicFuncs = map[string]func(property *Property) (*Property, bool){ - "Ref": ResolveReference, - "Fn::Base64": ResolveBase64, - "Fn::Equals": ResolveEquals, - "Fn::Join": ResolveJoin, - "Fn::Split": ResolveSplit, - "Fn::Sub": ResolveSub, - "Fn::FindInMap": ResolveFindInMap, - "Fn::Select": ResolveSelect, - "Fn::GetAtt": ResolveGetAtt, - "Fn::GetAZs": GetAzs, - "Fn::Cidr": GetCidr, - "Fn::ImportValue": ImportPlaceholder, - "Fn::If": ResolveIf, - "Fn::And": ResolveAnd, - "Fn::Or": ResolveOr, - "Fn::Not": ResolveNot, - "Fn::Length": ResolveLength, - "Condition": ResolveCondition, - } -} - -func ImportPlaceholder(property *Property) (*Property, bool) { - property.unresolved = true - return property, false -} - -func PassthroughResolution(property *Property) (*Property, bool) { - return property, false -} - -func IsIntrinsicFunc(node *yaml.Node) bool { - if node == nil || node.Tag == "" { - return false - } - - nodeTag := strings.TrimPrefix(node.Tag, "!") - if nodeTag != "Ref" && nodeTag != "Condition" { - nodeTag = fmt.Sprintf("Fn::%s", nodeTag) - } - for tag := range intrinsicFuncs { - - if nodeTag == tag { - return true - } - } - return false -} - -func IsIntrinsic(key string) bool { - for tag := range intrinsicFuncs { - if tag == key { - return true - } - } - return false -} - -func ResolveIntrinsicFunc(property *Property) (*Property, bool) { - if property == nil { - return nil, false - } - if !property.IsMap() { - return property, false - } - - for funcName := range property.AsMap() { - if fn := intrinsicFuncs[funcName]; fn != nil { - prop, resolved := fn(property) - if prop == nil || !resolved { - return prop, false - } - - prop.inferType() - return prop, true - } - } - return property, false -} - -func getIntrinsicTag(tag string) string { - tag = strings.TrimPrefix(tag, "!") - switch tag { - case "Ref", "Contains": - return tag - default: - return fmt.Sprintf("Fn::%s", tag) - } -} - -func abortIntrinsic(property *Property, _ string, _ ...string) (*Property, bool) { - // - return property, false -} diff --git a/pkg/iac/scanners/cloudformation/parser/intrinsics_test.go b/pkg/iac/scanners/cloudformation/parser/intrinsics_test.go deleted file mode 100644 index a69e04dd0fba..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/intrinsics_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" -) - -func Test_is_intrinsic_returns_expected(t *testing.T) { - - testCases := []struct { - nodeTag string - expectedResult bool - }{ - { - nodeTag: "!Ref", - expectedResult: true, - }, - { - nodeTag: "!Join", - expectedResult: true, - }, - { - nodeTag: "!Sub", - expectedResult: true, - }, - { - nodeTag: "!Equals", - expectedResult: true, - }, - { - nodeTag: "!Equal", - expectedResult: false, - }, - } - - for _, tt := range testCases { - n := &yaml.Node{ - Tag: tt.nodeTag, - } - assert.Equal(t, tt.expectedResult, IsIntrinsicFunc(n)) - } - -} diff --git a/pkg/iac/scanners/cloudformation/parser/parameter.go b/pkg/iac/scanners/cloudformation/parser/parameter.go deleted file mode 100644 index 4cfdfd1705b6..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/parameter.go +++ /dev/null @@ -1,145 +0,0 @@ -package parser - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/jfather" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -type Parameter struct { - inner parameterInner -} - -type parameterInner struct { - Type string `yaml:"Type"` - Default any `yaml:"Default"` -} - -func (p *Parameter) UnmarshalYAML(node *yaml.Node) error { - return node.Decode(&p.inner) -} - -func (p *Parameter) UnmarshalJSONWithMetadata(node jfather.Node) error { - - var inner parameterInner - - if err := node.Decode(&inner); err != nil { - return err - } - - // jfather parses Number without fraction as int64 - // https://github.com/liamg/jfather/blob/4ef05d70c05af167226d3333a4ec7d8ac3c9c281/parse_number.go#L33-L42 - switch v := inner.Default.(type) { - case int64: - inner.Default = int(v) - default: - inner.Default = v - } - - p.inner = inner - return nil -} - -func (p *Parameter) Type() cftypes.CfType { - switch p.inner.Type { - case "Boolean": - return cftypes.Bool - case "String": - return cftypes.String - case "Integer": - return cftypes.Int - default: - return cftypes.String - } -} - -func (p *Parameter) Default() any { - return p.inner.Default -} - -func (p *Parameter) UpdateDefault(inVal any) { - passedVal := inVal.(string) - - switch p.inner.Type { - case "Boolean": - p.inner.Default, _ = strconv.ParseBool(passedVal) - case "String": - p.inner.Default = passedVal - case "Integer": - p.inner.Default, _ = strconv.Atoi(passedVal) - default: - p.inner.Default = passedVal - } -} - -type Parameters map[string]any - -func (p *Parameters) Merge(other Parameters) { - for k, v := range other { - (*p)[k] = v - } -} - -func (p *Parameters) UnmarshalJSON(data []byte) error { - (*p) = make(Parameters) - - if len(data) == 0 { - return nil - } - - switch { - case data[0] == '{' && data[len(data)-1] == '}': // object - // CodePipeline like format - var params struct { - Params map[string]any `json:"Parameters"` - } - - if err := json.Unmarshal(data, ¶ms); err != nil { - return err - } - - (*p) = params.Params - case data[0] == '[' && data[len(data)-1] == ']': // array - // Original format - var params []string - - if err := json.Unmarshal(data, ¶ms); err == nil { - for _, param := range params { - parts := strings.Split(param, "=") - if len(parts) != 2 { - return fmt.Errorf("invalid key-value parameter: %q", param) - } - (*p)[parts[0]] = parts[1] - } - return nil - } - - // CloudFormation like format - var cfparams []struct { - ParameterKey string `json:"ParameterKey"` - ParameterValue string `json:"ParameterValue"` - } - - d := json.NewDecoder(bytes.NewReader(data)) - d.DisallowUnknownFields() - if err := d.Decode(&cfparams); err != nil { - return err - } - - for _, param := range cfparams { - (*p)[param.ParameterKey] = param.ParameterValue - } - default: - return errors.New("unsupported parameters format") - } - - return nil -} diff --git a/pkg/iac/scanners/cloudformation/parser/parameters_test.go b/pkg/iac/scanners/cloudformation/parser/parameters_test.go deleted file mode 100644 index 703f07f5fe12..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/parameters_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package parser - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestParameters_UnmarshalJSON(t *testing.T) { - tests := []struct { - name string - source string - expected Parameters - wantErr bool - }{ - { - name: "original format", - source: `[ - "Key1=Value1", - "Key2=Value2" - ]`, - expected: map[string]any{ - "Key1": "Value1", - "Key2": "Value2", - }, - }, - { - name: "CloudFormation like format", - source: `[ - { - "ParameterKey": "Key1", - "ParameterValue": "Value1" - }, - { - "ParameterKey": "Key2", - "ParameterValue": "Value2" - } - ]`, - expected: map[string]any{ - "Key1": "Value1", - "Key2": "Value2", - }, - }, - { - name: "CloudFormation like format, with unknown fields", - source: `[ - { - "ParameterKey": "Key1", - "ParameterValue": "Value1" - }, - { - "ParameterKey": "Key2", - "ParameterValue": "Value2", - "UsePreviousValue": true - } - ]`, - wantErr: true, - }, - { - name: "CodePipeline like format", - source: `{ - "Parameters": { - "Key1": "Value1", - "Key2": "Value2" - } - }`, - expected: map[string]any{ - "Key1": "Value1", - "Key2": "Value2", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var params Parameters - - err := json.Unmarshal([]byte(tt.source), ¶ms) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - assert.Equal(t, tt.expected, params) - }) - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/parser.go b/pkg/iac/scanners/cloudformation/parser/parser.go deleted file mode 100644 index eda88408ec06..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/parser.go +++ /dev/null @@ -1,207 +0,0 @@ -package parser - -import ( - "context" - "encoding/json" - "fmt" - "io" - "io/fs" - "path" - "path/filepath" - "strings" - - "github.com/hashicorp/go-multierror" - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/jfather" - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger - parameterFiles []string - parameters map[string]any - overridedParameters Parameters - configsFS fs.FS -} - -type Option func(*Parser) - -func WithParameters(params map[string]any) Option { - return func(p *Parser) { - p.parameters = params - } -} - -func WithParameterFiles(files ...string) Option { - return func(p *Parser) { - p.parameterFiles = files - } -} - -func WithConfigsFS(fsys fs.FS) Option { - return func(p *Parser) { - p.configsFS = fsys - } -} - -func New(opts ...Option) *Parser { - p := &Parser{ - logger: log.WithPrefix("cloudformation parser"), - } - for _, option := range opts { - option(p) - } - return p -} - -func (p *Parser) ParseFS(ctx context.Context, fsys fs.FS, dir string) (FileContexts, error) { - var contexts FileContexts - if err := fs.WalkDir(fsys, filepath.ToSlash(dir), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - c, err := p.ParseFile(ctx, fsys, path) - if err != nil { - p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) - return nil - } - contexts = append(contexts, c) - return nil - }); err != nil { - return nil, err - } - return contexts, nil -} - -func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, filePath string) (fctx *FileContext, err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("panic during parse: %s", e) - } - }() - - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - if p.configsFS == nil { - p.configsFS = fsys - } - - if err := p.parseParams(); err != nil { - return nil, fmt.Errorf("failed to parse parameters file: %w", err) - } - - sourceFmt := YamlSourceFormat - if path.Ext(filePath) == ".json" { - sourceFmt = JsonSourceFormat - } - - f, err := fsys.Open(filePath) - if err != nil { - return nil, err - } - defer f.Close() - - content, err := io.ReadAll(f) - if err != nil { - return nil, err - } - - lines := strings.Split(string(content), "\n") - - fctx = &FileContext{ - filepath: filePath, - lines: lines, - SourceFormat: sourceFmt, - } - - switch sourceFmt { - case YamlSourceFormat: - if err := yaml.Unmarshal(content, fctx); err != nil { - return nil, NewErrInvalidContent(filePath, err) - } - fctx.Ignores = ignore.Parse(string(content), filePath, "") - case JsonSourceFormat: - if err := jfather.Unmarshal(content, fctx); err != nil { - return nil, NewErrInvalidContent(filePath, err) - } - } - - fctx.stripNullProperties() - - fctx.overrideParameters(p.overridedParameters) - - if params := fctx.missingParameterValues(); len(params) > 0 { - p.logger.Warn("Missing parameter values", log.FilePath(filePath), log.String("parameters", strings.Join(params, ", "))) - } - - fctx.lines = lines - fctx.SourceFormat = sourceFmt - fctx.filepath = filePath - - p.logger.Debug("Context loaded from source", log.FilePath(filePath)) - - // the context must be set to conditions before resources - for _, c := range fctx.Conditions { - c.setContext(fctx) - } - - for name, r := range fctx.Resources { - r.configureResource(name, fsys, filePath, fctx) - } - - return fctx, nil -} - -func (p *Parser) parseParams() error { - if p.overridedParameters != nil { // parameters have already been parsed - return nil - } - - params := make(Parameters) - - var errs error - for _, path := range p.parameterFiles { - if parameters, err := p.parseParametersFile(path); err != nil { - errs = multierror.Append(errs, err) - } else { - params.Merge(parameters) - } - } - - if errs != nil { - return errs - } - - params.Merge(p.parameters) - - p.overridedParameters = params - return nil -} - -func (p *Parser) parseParametersFile(filePath string) (Parameters, error) { - f, err := p.configsFS.Open(filePath) - if err != nil { - return nil, fmt.Errorf("parameters file %q open error: %w", filePath, err) - } - - var parameters Parameters - if err := json.NewDecoder(f).Decode(¶meters); err != nil { - return nil, err - } - return parameters, nil -} diff --git a/pkg/iac/scanners/cloudformation/parser/parser_test.go b/pkg/iac/scanners/cloudformation/parser/parser_test.go deleted file mode 100644 index 99dd62294eee..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/parser_test.go +++ /dev/null @@ -1,491 +0,0 @@ -package parser - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" -) - -func parseFile(t *testing.T, source, name string) (FileContexts, error) { - tmp, err := os.MkdirTemp(os.TempDir(), "defsec") - require.NoError(t, err) - defer func() { _ = os.RemoveAll(tmp) }() - require.NoError(t, os.WriteFile(filepath.Join(tmp, name), []byte(source), 0600)) - fs := os.DirFS(tmp) - return New().ParseFS(context.TODO(), fs, ".") -} - -func Test_parse_yaml(t *testing.T) { - - source := `--- -Parameters: - BucketName: - Type: String - Default: naughty - EncryptBucket: - Type: Boolean - Default: false -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: naughty - BucketEncryption: - ServerSideEncryptionConfiguration: - - BucketKeyEnabled: - Ref: EncryptBucket` - - files, err := parseFile(t, source, "cf.yaml") - require.NoError(t, err) - assert.Len(t, files, 1) - file := files[0] - - assert.Len(t, file.Resources, 1) - assert.Len(t, file.Parameters, 2) - - bucket, ok := file.Resources["S3Bucket"] - require.True(t, ok, "S3Bucket resource should be available") - assert.Equal(t, "cf.yaml", bucket.Range().GetFilename()) - assert.Equal(t, 10, bucket.Range().GetStartLine()) - assert.Equal(t, 17, bucket.Range().GetEndLine()) -} - -func Test_parse_json(t *testing.T) { - source := `{ - "Parameters": { - "BucketName": { - "Type": "String", - "Default": "naughty" - }, - "BucketKeyEnabled": { - "Type": "Boolean", - "Default": false - } - }, - "Resources": { - "S3Bucket": { - "Type": "AWS::S3::Bucket", - "properties": { - "BucketName": { - "Ref": "BucketName" - }, - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "BucketKeyEnabled": { - "Ref": "BucketKeyEnabled" - } - } - ] - } - } - } - } -} -` - - files, err := parseFile(t, source, "cf.json") - require.NoError(t, err) - assert.Len(t, files, 1) - file := files[0] - - assert.Len(t, file.Resources, 1) - assert.Len(t, file.Parameters, 2) -} - -func Test_parse_yaml_with_map_ref(t *testing.T) { - - source := `--- -Parameters: - BucketName: - Type: String - Default: referencedBucket - EncryptBucket: - Type: Boolean - Default: false -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: - Ref: BucketName - BucketEncryption: - ServerSideEncryptionConfiguration: - - BucketKeyEnabled: - Ref: EncryptBucket` - - files, err := parseFile(t, source, "cf.yaml") - require.NoError(t, err) - assert.Len(t, files, 1) - file := files[0] - - assert.Len(t, file.Resources, 1) - assert.Len(t, file.Parameters, 2) - - res := file.GetResourceByLogicalID("S3Bucket") - assert.NotNil(t, res) - - refProp := res.GetProperty("BucketName") - assert.False(t, refProp.IsNil()) - assert.Equal(t, "referencedBucket", refProp.AsString()) -} - -func Test_parse_yaml_with_intrinsic_functions(t *testing.T) { - - source := `--- -Parameters: - BucketName: - Type: String - Default: somebucket - EncryptBucket: - Type: Boolean - Default: false -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: !Ref BucketName - BucketEncryption: - ServerSideEncryptionConfiguration: - - BucketKeyEnabled: false -` - - files, err := parseFile(t, source, "cf.yaml") - require.NoError(t, err) - assert.Len(t, files, 1) - ctx := files[0] - - assert.Len(t, ctx.Resources, 1) - assert.Len(t, ctx.Parameters, 2) - - res := ctx.GetResourceByLogicalID("S3Bucket") - assert.NotNil(t, res) - - refProp := res.GetProperty("BucketName") - assert.False(t, refProp.IsNil()) - assert.Equal(t, "somebucket", refProp.AsString()) -} - -func createTestFileContext(t *testing.T, source string) *FileContext { - contexts, err := parseFile(t, source, "main.yaml") - require.NoError(t, err) - require.Len(t, contexts, 1) - return contexts[0] -} - -func Test_parse_yaml_use_condition_in_resource(t *testing.T) { - source := `--- -AWSTemplateFormatVersion: "2010-09-09" -Description: some description -Parameters: - ServiceName: - Type: String - Description: The service name - EnvName: - Type: String - Description: Optional environment name to prefix all resources with - Default: "" - -Conditions: - SuffixResources: !Not [!Equals [!Ref EnvName, ""]] - -Resources: - ErrorTimedOutMetricFilter: - Type: AWS::Logs::MetricFilter - Properties: - FilterPattern: '?ERROR ?error ?Error ?"timed out"' # If log contains one of these error words or timed out - LogGroupName: - !If [ - SuffixResources, - !Sub "/aws/lambda/${ServiceName}-${EnvName}", - !Sub "/aws/lambda/${ServiceName}", - ] - MetricTransformations: - - MetricName: !Sub "${ServiceName}-ErrorLogCount" - MetricNamespace: market-LogMetrics - MetricValue: 1 - DefaultValue: 0 -` - - files, err := parseFile(t, source, "cf.yaml") - require.NoError(t, err) - assert.Len(t, files, 1) - ctx := files[0] - - assert.Len(t, ctx.Parameters, 2) - assert.Len(t, ctx.Conditions, 1) - assert.Len(t, ctx.Resources, 1) - - res := ctx.GetResourceByLogicalID("ErrorTimedOutMetricFilter") - assert.NotNil(t, res) - - refProp := res.GetProperty("LogGroupName") - assert.False(t, refProp.IsNil()) - assert.Equal(t, "/aws/lambda/${ServiceName}", refProp.AsString()) -} - -func TestParse_WithParameters(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "main.yaml": `AWSTemplateFormatVersion: 2010-09-09 -Parameters: - KmsMasterKeyId: - Type: String -Resources: - TestQueue: - Type: 'AWS::SQS::Queue' - Properties: - QueueName: test-queue - KmsMasterKeyId: !Ref KmsMasterKeyId - `, - }) - - params := map[string]any{ - "KmsMasterKeyId": "some_id", - } - p := New(WithParameters(params)) - - files, err := p.ParseFS(context.TODO(), fs, ".") - require.NoError(t, err) - require.Len(t, files, 1) - - file := files[0] - res := file.GetResourceByLogicalID("TestQueue") - assert.NotNil(t, res) - - kmsProp := res.GetProperty("KmsMasterKeyId") - assert.False(t, kmsProp.IsNil()) - assert.Equal(t, "some_id", kmsProp.AsString()) -} - -func TestParse_WithParameterFiles(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.yaml": `AWSTemplateFormatVersion: 2010-09-09 -Parameters: - KmsMasterKeyId: - Type: String -Resources: - TestQueue: - Type: 'AWS::SQS::Queue' - Properties: - QueueName: test-queue - KmsMasterKeyId: !Ref KmsMasterKeyId -`, - "params.json": `[ - { - "ParameterKey": "KmsMasterKeyId", - "ParameterValue": "some_id" - } -] - `, - }) - - p := New(WithParameterFiles("params.json")) - - files, err := p.ParseFS(context.TODO(), fs, ".") - require.NoError(t, err) - require.Len(t, files, 1) - - file := files[0] - res := file.GetResourceByLogicalID("TestQueue") - assert.NotNil(t, res) - - kmsProp := res.GetProperty("KmsMasterKeyId") - assert.False(t, kmsProp.IsNil()) - assert.Equal(t, "some_id", kmsProp.AsString()) -} - -func TestParse_WithConfigFS(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "queue.yaml": `AWSTemplateFormatVersion: 2010-09-09 -Parameters: - KmsMasterKeyId: - Type: String -Resources: - TestQueue: - Type: 'AWS::SQS::Queue' - Properties: - QueueName: testqueue - KmsMasterKeyId: !Ref KmsMasterKeyId -`, - "bucket.yaml": `AWSTemplateFormatVersion: '2010-09-09' -Description: Bucket -Parameters: - BucketName: - Type: String -Resources: - S3Bucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Ref BucketName -`, - }) - - configFS := testutil.CreateFS(t, map[string]string{ - "/workdir/parameters/queue.json": `[ - { - "ParameterKey": "KmsMasterKeyId", - "ParameterValue": "some_id" - } - ] - `, - "/workdir/parameters/s3.json": `[ - { - "ParameterKey": "BucketName", - "ParameterValue": "testbucket" - } - ]`, - }) - - p := New( - WithParameterFiles("/workdir/parameters/queue.json", "/workdir/parameters/s3.json"), - WithConfigsFS(configFS), - ) - - files, err := p.ParseFS(context.TODO(), fs, ".") - require.NoError(t, err) - require.Len(t, files, 2) - - for _, file := range files { - if strings.Contains(file.filepath, "queue") { - res := file.GetResourceByLogicalID("TestQueue") - assert.NotNil(t, res) - - kmsProp := res.GetProperty("KmsMasterKeyId") - assert.False(t, kmsProp.IsNil()) - assert.Equal(t, "some_id", kmsProp.AsString()) - } else if strings.Contains(file.filepath, "s3") { - res := file.GetResourceByLogicalID("S3Bucket") - assert.NotNil(t, res) - - bucketNameProp := res.GetProperty("BucketName") - assert.False(t, bucketNameProp.IsNil()) - assert.Equal(t, "testbucket", bucketNameProp.AsString()) - } - } -} - -func TestJsonWithNumbers(t *testing.T) { - src := ` -{ - "AWSTemplateFormatVersion": "2010-09-09", - "Parameters": { - "SomeIntParam": { - "Type": "Number", - "Default": 1 - }, - "SomeFloatParam": { - "Type": "Number", - "Default": 1.1 - } - }, - "Resources": { - "SomeResource": { - "Type": "Test::Resource", - "Properties": { - "SomeIntProp": 1, - "SomeFloatProp": 1.1 - } - } - } -} -` - - fsys := testutil.CreateFS(t, map[string]string{ - "main.json": src, - }) - - files, err := New().ParseFS(context.TODO(), fsys, ".") - - require.NoError(t, err) - require.Len(t, files, 1) - - file := files[0] - - assert.Equal(t, 1, file.Parameters["SomeIntParam"].Default()) - assert.InEpsilon(t, 1.1, file.Parameters["SomeFloatParam"].Default(), 0.0001) - - res := file.GetResourcesByType("Test::Resource") - assert.NotNil(t, res) - assert.Len(t, res, 1) - - assert.Equal(t, 1, res[0].GetProperty("SomeIntProp").AsIntValue().Value()) - assert.Equal(t, 0, res[0].GetProperty("SomeFloatProp").AsIntValue().Value()) -} - -func TestParameterIsNull(t *testing.T) { - src := `--- -AWSTemplateFormatVersion: 2010-09-09 - -Parameters: - Email: - Type: String - -Conditions: - SubscribeEmail: !Not [!Equals [ !Ref Email, ""]] -` - - fsys := testutil.CreateFS(t, map[string]string{ - "main.yaml": src, - }) - - files, err := New().ParseFS(context.TODO(), fsys, ".") - require.NoError(t, err) - require.Len(t, files, 1) -} - -func Test_TemplateWithNullProperty(t *testing.T) { - src := `AWSTemplateFormatVersion: "2010-09-09" -Resources: - TestBucket: - Type: "AWS::S3::Bucket" - Properties: - BucketName:` - - fsys := testutil.CreateFS(t, map[string]string{ - "main.yaml": src, - }) - - files, err := New().ParseFS(context.TODO(), fsys, ".") - require.NoError(t, err) - require.Len(t, files, 1) - - file := files[0] - - res := file.GetResourceByLogicalID("TestBucket") - - assert.True(t, res.GetProperty("BucketName").IsNil()) -} - -func Test_TemplateWithNullNestedProperty(t *testing.T) { - src := `AWSTemplateFormatVersion: "2010-09-09" -Description: "BAD" -Resources: - TestBucket: - Type: "AWS::S3::Bucket" - Properties: - BucketName: test - PublicAccessBlockConfiguration: - BlockPublicAcls: null` - - fsys := testutil.CreateFS(t, map[string]string{ - "main.yaml": src, - }) - - files, err := New().ParseFS(context.TODO(), fsys, ".") - require.NoError(t, err) - require.Len(t, files, 1) - - file := files[0] - - res := file.GetResourceByLogicalID("TestBucket") - - assert.True(t, res.GetProperty("PublicAccessBlockConfiguration.BlockPublicAcls").IsNil()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/property.go b/pkg/iac/scanners/cloudformation/parser/property.go deleted file mode 100644 index 606b9d28b341..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/property.go +++ /dev/null @@ -1,424 +0,0 @@ -package parser - -import ( - "encoding/json" - "io/fs" - "strconv" - "strings" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/jfather" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type EqualityOptions = int - -const ( - IgnoreCase EqualityOptions = iota -) - -type Property struct { - ctx *FileContext - name string - comment string - rng iacTypes.Range - parentRange iacTypes.Range - Inner PropertyInner - logicalId string - unresolved bool -} - -type PropertyInner struct { - Type cftypes.CfType - Value any `json:"Value" yaml:"Value"` -} - -func (p *Property) Comment() string { - return p.comment -} - -func (p *Property) setName(name string) { - p.name = name - if p.Type() == cftypes.Map { - for n, subProp := range p.AsMap() { - if subProp == nil { - continue - } - subProp.setName(n) - } - } -} - -func (p *Property) setContext(ctx *FileContext) { - p.ctx = ctx - - if p.IsMap() { - for _, subProp := range p.AsMap() { - if subProp == nil { - continue - } - subProp.setContext(ctx) - } - } - - if p.IsList() { - for _, subProp := range p.AsList() { - subProp.setContext(ctx) - } - } -} - -func (p *Property) setFileAndParentRange(target fs.FS, filepath string, parentRange iacTypes.Range) { - p.rng = iacTypes.NewRange(filepath, p.rng.GetStartLine(), p.rng.GetEndLine(), p.rng.GetSourcePrefix(), target) - p.parentRange = parentRange - - switch p.Type() { - case cftypes.Map: - for _, subProp := range p.AsMap() { - if subProp == nil { - continue - } - subProp.setFileAndParentRange(target, filepath, parentRange) - } - case cftypes.List: - for _, subProp := range p.AsList() { - if subProp == nil { - continue - } - subProp.setFileAndParentRange(target, filepath, parentRange) - } - } -} - -func (p *Property) UnmarshalYAML(node *yaml.Node) error { - p.rng = iacTypes.NewRange("", node.Line, calculateEndLine(node), "", nil) - - p.comment = node.LineComment - return setPropertyValueFromYaml(node, &p.Inner) -} - -func (p *Property) UnmarshalJSONWithMetadata(node jfather.Node) error { - p.rng = iacTypes.NewRange("", node.Range().Start.Line, node.Range().End.Line, "", nil) - return setPropertyValueFromJson(node, &p.Inner) -} - -func (p *Property) Type() cftypes.CfType { - return p.Inner.Type -} - -func (p *Property) Range() iacTypes.Range { - return p.rng -} - -func (p *Property) Metadata() iacTypes.Metadata { - return iacTypes.NewMetadata(p.Range(), p.name). - WithParent(iacTypes.NewMetadata(p.parentRange, p.logicalId)) -} - -func (p *Property) isFunction() bool { - if p == nil { - return false - } - if p.Type() == cftypes.Map { - for n := range p.AsMap() { - return IsIntrinsic(n) - } - } - return false -} - -func (p *Property) RawValue() any { - return p.Inner.Value -} - -func (p *Property) AsRawStrings() ([]string, error) { - - if len(p.ctx.lines) < p.rng.GetEndLine() { - return p.ctx.lines, nil - } - return p.ctx.lines[p.rng.GetStartLine()-1 : p.rng.GetEndLine()], nil -} - -func (p *Property) resolveValue() (*Property, bool) { - if !p.isFunction() || p.IsUnresolved() { - return p, true - } - - resolved, ok := ResolveIntrinsicFunc(p) - if ok { - return resolved, true - } - - p.unresolved = true - return p, false -} - -func (p *Property) GetStringProperty(path string, defaultValue ...string) iacTypes.StringValue { - defVal := "" - if len(defaultValue) > 0 { - defVal = defaultValue[0] - } - - if p.IsUnresolved() { - return iacTypes.StringUnresolvable(p.Metadata()) - } - - prop := p.GetProperty(path) - if prop.IsNotString() { - return p.StringDefault(defVal) - } - return prop.AsStringValue() -} - -func (p *Property) StringDefault(defaultValue string) iacTypes.StringValue { - return iacTypes.StringDefault(defaultValue, p.Metadata()) -} - -func (p *Property) GetBoolProperty(path string, defaultValue ...bool) iacTypes.BoolValue { - defVal := false - if len(defaultValue) > 0 { - defVal = defaultValue[0] - } - - if p.IsUnresolved() { - return iacTypes.BoolUnresolvable(p.Metadata()) - } - - prop := p.GetProperty(path) - - if prop.isFunction() { - prop, _ = prop.resolveValue() - } - - if prop.IsNotBool() { - return p.inferBool(prop, defVal) - } - return prop.AsBoolValue() -} - -func (p *Property) GetIntProperty(path string, defaultValue ...int) iacTypes.IntValue { - defVal := 0 - if len(defaultValue) > 0 { - defVal = defaultValue[0] - } - - if p.IsUnresolved() { - return iacTypes.IntUnresolvable(p.Metadata()) - } - - prop := p.GetProperty(path) - - if prop.IsNotInt() { - return p.IntDefault(defVal) - } - return prop.AsIntValue() -} - -func (p *Property) BoolDefault(defaultValue bool) iacTypes.BoolValue { - return iacTypes.BoolDefault(defaultValue, p.Metadata()) -} - -func (p *Property) IntDefault(defaultValue int) iacTypes.IntValue { - return iacTypes.IntDefault(defaultValue, p.Metadata()) -} - -func (p *Property) GetProperty(path string) *Property { - - pathParts := strings.Split(path, ".") - - first := pathParts[0] - property := p - - if p.isFunction() { - property, _ = p.resolveValue() - } - - if property.IsNotMap() { - return nil - } - - for n, p := range property.AsMap() { - if n == first { - property = p - break - } - } - - if len(pathParts) == 1 || property == nil { - return property - } - - if nestedProperty := property.GetProperty(strings.Join(pathParts[1:], ".")); nestedProperty != nil { - if nestedProperty.isFunction() { - resolved, _ := nestedProperty.resolveValue() - return resolved - } else { - return nestedProperty - } - } - - return &Property{} -} - -func (p *Property) deriveResolved(propType cftypes.CfType, propValue any) *Property { - return &Property{ - ctx: p.ctx, - name: p.name, - comment: p.comment, - rng: p.rng, - parentRange: p.parentRange, - logicalId: p.logicalId, - Inner: PropertyInner{ - Type: propType, - Value: propValue, - }, - } -} - -func (p *Property) ParentRange() iacTypes.Range { - return p.parentRange -} - -func (p *Property) inferBool(prop *Property, defaultValue bool) iacTypes.BoolValue { - if prop.IsString() { - if prop.EqualTo("true", IgnoreCase) { - return iacTypes.Bool(true, prop.Metadata()) - } - if prop.EqualTo("yes", IgnoreCase) { - return iacTypes.Bool(true, prop.Metadata()) - } - if prop.EqualTo("1", IgnoreCase) { - return iacTypes.Bool(true, prop.Metadata()) - } - if prop.EqualTo("false", IgnoreCase) { - return iacTypes.Bool(false, prop.Metadata()) - } - if prop.EqualTo("no", IgnoreCase) { - return iacTypes.Bool(false, prop.Metadata()) - } - if prop.EqualTo("0", IgnoreCase) { - return iacTypes.Bool(false, prop.Metadata()) - } - } - - if prop.IsInt() { - if prop.EqualTo(0) { - return iacTypes.Bool(false, prop.Metadata()) - } - if prop.EqualTo(1) { - return iacTypes.Bool(true, prop.Metadata()) - } - } - - return p.BoolDefault(defaultValue) -} - -func (p *Property) String() string { - r := "" - switch p.Type() { - case cftypes.String: - r = p.AsString() - case cftypes.Int: - r = strconv.Itoa(p.AsInt()) - } - return r -} - -func (p *Property) SetLogicalResource(id string) { - p.logicalId = id - - if p.isFunction() { - return - } - - if p.IsMap() { - for _, subProp := range p.AsMap() { - if subProp == nil { - continue - } - subProp.SetLogicalResource(id) - } - } - - if p.IsList() { - for _, subProp := range p.AsList() { - subProp.SetLogicalResource(id) - } - } - -} - -func (p *Property) GetJsonBytes(squashList ...bool) []byte { - if p.IsNil() { - return []byte{} - } - lines, err := p.AsRawStrings() - if err != nil { - return nil - } - if p.ctx.SourceFormat == JsonSourceFormat { - return []byte(strings.Join(lines, " ")) - } - - if len(squashList) > 0 { - lines[0] = strings.Replace(lines[0], "-", " ", 1) - } - - lines = removeLeftMargin(lines) - - yamlContent := strings.Join(lines, "\n") - var body any - if err := yaml.Unmarshal([]byte(yamlContent), &body); err != nil { - return nil - } - jsonBody := convert(body) - policyJson, err := json.Marshal(jsonBody) - if err != nil { - return nil - } - return policyJson -} - -func (p *Property) GetJsonBytesAsString(squashList ...bool) string { - return string(p.GetJsonBytes(squashList...)) -} - -func removeLeftMargin(lines []string) []string { - if len(lines) == 0 { - return lines - } - prefixSpace := len(lines[0]) - len(strings.TrimLeft(lines[0], " ")) - - for i, line := range lines { - if len(line) >= prefixSpace { - lines[i] = line[prefixSpace:] - } - } - return lines -} - -func convert(input any) any { - switch x := input.(type) { - case map[any]any: - outpMap := make(map[string]any) - for k, v := range x { - outpMap[k.(string)] = convert(v) - } - return outpMap - case []any: - for i, v := range x { - x[i] = convert(v) - } - } - return input -} - -func (p *Property) inferType() { - typ := cftypes.TypeFromGoValue(p.Inner.Value) - if typ == cftypes.Unknown { - return - } - p.Inner.Type = typ -} diff --git a/pkg/iac/scanners/cloudformation/parser/property_conversion.go b/pkg/iac/scanners/cloudformation/parser/property_conversion.go deleted file mode 100644 index 1053afa1b3f7..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/property_conversion.go +++ /dev/null @@ -1,146 +0,0 @@ -package parser - -import ( - "fmt" - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/log" -) - -func (p *Property) IsConvertableTo(conversionType cftypes.CfType) bool { - if p.IsNil() { - return false - } - - switch conversionType { - case cftypes.Int: - return p.isConvertableToInt() - case cftypes.Bool: - return p.isConvertableToBool() - case cftypes.String: - return p.isConvertableToString() - } - return false -} - -func (p *Property) isConvertableToString() bool { - switch p.Type() { - case cftypes.Map: - return false - case cftypes.List: - for _, p := range p.AsList() { - if !p.IsString() { - return false - } - } - } - return true -} - -func (p *Property) isConvertableToBool() bool { - switch p.Type() { - case cftypes.String: - return p.EqualTo("true", IgnoreCase) || p.EqualTo("false", IgnoreCase) || - p.EqualTo("1", IgnoreCase) || p.EqualTo("0", IgnoreCase) - - case cftypes.Int: - return p.EqualTo(1) || p.EqualTo(0) - case cftypes.Bool: - return true - } - return false -} - -func (p *Property) isConvertableToInt() bool { - switch p.Type() { - case cftypes.String: - if _, err := strconv.Atoi(p.AsString()); err == nil { - return true - } - case cftypes.Bool, cftypes.Int: - return true - } - return false -} - -func (p *Property) ConvertTo(conversionType cftypes.CfType) *Property { - if p.IsNil() { - return nil - } - - if p.Type() == conversionType { - return p - } - - if !p.IsConvertableTo(conversionType) { - log.Debug("Failed to convert property", - log.String("from", string(p.Type())), - log.String("to", string(conversionType)), - log.Any("range", p.Range().String()), - ) - return p - } - switch conversionType { - case cftypes.Int: - return p.convertToInt() - case cftypes.Bool: - return p.convertToBool() - case cftypes.String: - return p.convertToString() - } - return p -} - -func (p *Property) convertToString() *Property { - switch p.Type() { - case cftypes.Int: - return p.deriveResolved(cftypes.String, strconv.Itoa(p.AsInt())) - case cftypes.Bool: - return p.deriveResolved(cftypes.String, strconv.FormatBool(p.AsBool())) - case cftypes.List: - var parts []string - for _, property := range p.AsList() { - parts = append(parts, property.AsString()) - } - return p.deriveResolved(cftypes.String, fmt.Sprintf("[%s]", strings.Join(parts, ", "))) - } - return p -} - -func (p *Property) convertToBool() *Property { - switch p.Type() { - case cftypes.String: - if p.EqualTo("true", IgnoreCase) || p.EqualTo("1") { - return p.deriveResolved(cftypes.Bool, true) - } - if p.EqualTo("false", IgnoreCase) || p.EqualTo("0") { - return p.deriveResolved(cftypes.Bool, false) - } - case cftypes.Int: - if p.EqualTo(1) { - return p.deriveResolved(cftypes.Bool, true) - } - if p.EqualTo(0) { - return p.deriveResolved(cftypes.Bool, false) - } - } - return p -} - -func (p *Property) convertToInt() *Property { - // - switch p.Type() { - case cftypes.String: - if val, err := strconv.Atoi(p.AsString()); err == nil { - return p.deriveResolved(cftypes.Int, val) - } - case cftypes.Bool: - if p.IsTrue() { - return p.deriveResolved(cftypes.Int, 1) - } - return p.deriveResolved(cftypes.Int, 0) - } - return p -} diff --git a/pkg/iac/scanners/cloudformation/parser/property_helpers.go b/pkg/iac/scanners/cloudformation/parser/property_helpers.go deleted file mode 100644 index 868f4d9231cd..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/property_helpers.go +++ /dev/null @@ -1,266 +0,0 @@ -package parser - -import ( - "strconv" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func (p *Property) IsNil() bool { - return p == nil || p.Inner.Value == nil -} - -func (p *Property) IsNotNil() bool { - return !p.IsUnresolved() && !p.IsNil() -} - -func (p *Property) Is(t cftypes.CfType) bool { - if p.IsNil() || p.IsUnresolved() { - return false - } - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.Is(t) - } - } - return p.Inner.Type == t -} - -func (p *Property) IsString() bool { - return p.Is(cftypes.String) -} - -func (p *Property) IsNotString() bool { - return !p.IsUnresolved() && !p.IsString() -} - -func (p *Property) IsInt() bool { - return p.Is(cftypes.Int) -} - -func (p *Property) IsNotInt() bool { - return !p.IsUnresolved() && !p.IsInt() -} - -func (p *Property) IsMap() bool { - if p.IsNil() || p.IsUnresolved() { - return false - } - return p.Inner.Type == cftypes.Map -} - -func (p *Property) IsNotMap() bool { - return !p.IsUnresolved() && !p.IsMap() -} - -func (p *Property) IsList() bool { - return p.Is(cftypes.List) -} - -func (p *Property) IsNotList() bool { - return !p.IsUnresolved() && !p.IsList() -} - -func (p *Property) IsBool() bool { - return p.Is(cftypes.Bool) -} - -func (p *Property) IsUnresolved() bool { - return p != nil && p.unresolved -} - -func (p *Property) IsNotBool() bool { - return !p.IsUnresolved() && !p.IsBool() -} - -func (p *Property) AsString() string { - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.AsString() - } - return "" - } - if p.IsNil() { - return "" - } - if !p.IsString() { - return "" - } - - return p.Inner.Value.(string) -} - -func (p *Property) AsStringValue() iacTypes.StringValue { - if p.unresolved { - return iacTypes.StringUnresolvable(p.Metadata()) - } - return iacTypes.StringExplicit(p.AsString(), p.Metadata()) -} - -func (p *Property) AsInt() int { - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.AsInt() - } - return 0 - } - if p.IsNotInt() { - if p.isConvertableToInt() { - return p.convertToInt().AsInt() - } - return 0 - } - - return p.Inner.Value.(int) -} - -func (p *Property) AsIntValue() iacTypes.IntValue { - if p.unresolved { - return iacTypes.IntUnresolvable(p.Metadata()) - } - return iacTypes.IntExplicit(p.AsInt(), p.Metadata()) -} - -func (p *Property) AsBool() bool { - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.AsBool() - } - return false - } - if !p.IsBool() { - return false - } - return p.Inner.Value.(bool) -} - -func (p *Property) AsBoolValue() iacTypes.BoolValue { - if p.unresolved { - return iacTypes.BoolUnresolvable(p.Metadata()) - } - return iacTypes.Bool(p.AsBool(), p.Metadata()) -} - -func (p *Property) AsMap() map[string]*Property { - val, ok := p.Inner.Value.(map[string]*Property) - if !ok { - return nil - } - return val -} - -func (p *Property) AsList() []*Property { - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.AsList() - } - return []*Property{} - } - - if list, ok := p.Inner.Value.([]*Property); ok { - return list - } - return nil -} - -func (p *Property) Len() int { - return len(p.AsList()) -} - -func (p *Property) EqualTo(checkValue any, equalityOptions ...EqualityOptions) bool { - var ignoreCase bool - for _, option := range equalityOptions { - if option == IgnoreCase { - ignoreCase = true - } - } - - switch checkerVal := checkValue.(type) { - case string: - if p.IsNil() { - return false - } - - if p.Inner.Type == cftypes.String || p.IsString() { - if ignoreCase { - return strings.EqualFold(p.AsString(), checkerVal) - } - return p.AsString() == checkerVal - } else if p.Inner.Type == cftypes.Int || p.IsInt() { - if val, err := strconv.Atoi(checkerVal); err == nil { - return p.AsInt() == val - } - } - return false - case bool: - if p.Inner.Type == cftypes.Bool || p.IsBool() { - return p.AsBool() == checkerVal - } - case int: - if p.Inner.Type == cftypes.Int || p.IsInt() { - return p.AsInt() == checkerVal - } - } - - return false - -} - -func (p *Property) IsTrue() bool { - if p.IsNil() || !p.IsBool() { - return false - } - - return p.AsBool() -} - -func (p *Property) IsEmpty() bool { - - if p.IsNil() { - return true - } - if p.IsUnresolved() { - return false - } - - switch p.Inner.Type { - case cftypes.String: - return p.AsString() == "" - case cftypes.List, cftypes.Map: - return len(p.AsList()) == 0 - default: - return false - } -} - -func (p *Property) Contains(checkVal any) bool { - if p == nil || p.IsNil() { - return false - } - - switch p.Type() { - case cftypes.List: - for _, p := range p.AsList() { - if p.EqualTo(checkVal) { - return true - } - } - case cftypes.Map: - if _, ok := checkVal.(string); !ok { - return false - } - for key := range p.AsMap() { - if key == checkVal.(string) { - return true - } - } - case cftypes.String: - if _, ok := checkVal.(string); !ok { - return false - } - return strings.Contains(p.AsString(), checkVal.(string)) - } - return false -} diff --git a/pkg/iac/scanners/cloudformation/parser/property_helpers_test.go b/pkg/iac/scanners/cloudformation/parser/property_helpers_test.go deleted file mode 100644 index 36f2cba89289..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/property_helpers_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func newProp(inner PropertyInner) *Property { - return &Property{ - name: "test_prop", - ctx: &FileContext{}, - rng: types.NewRange("testfile", 1, 1, "", nil), - Inner: inner, - } -} - -func Test_EqualTo(t *testing.T) { - tests := []struct { - name string - property *Property - checkValue any - opts []EqualityOptions - isEqual bool - }{ - { - name: "prop is nil", - property: nil, - checkValue: "some value", - isEqual: false, - }, - { - name: "compare strings", - property: newProp(PropertyInner{ - Type: cftypes.String, - Value: "is str", - }), - checkValue: "is str", - isEqual: true, - }, - { - name: "compare strings ignoring case", - property: newProp(PropertyInner{ - Type: cftypes.String, - Value: "is str", - }), - opts: []EqualityOptions{IgnoreCase}, - checkValue: "Is StR", - isEqual: true, - }, - { - name: "strings ate not equal", - property: newProp(PropertyInner{ - Type: cftypes.String, - Value: "some value", - }), - checkValue: "some other value", - isEqual: false, - }, - { - name: "compare prop with a int represented by a string", - property: newProp(PropertyInner{ - Type: cftypes.Int, - Value: 147, - }), - checkValue: "147", - isEqual: true, - }, - { - name: "compare ints", - property: newProp(PropertyInner{ - Type: cftypes.Int, - Value: 701, - }), - checkValue: 701, - isEqual: true, - }, - { - name: "compare bools", - property: newProp(PropertyInner{ - Type: cftypes.Bool, - Value: true, - }), - checkValue: true, - isEqual: true, - }, - { - name: "prop is string fn", - property: newProp(PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::If": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.Bool, - Value: false, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "bad", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "some value", - }, - }, - }, - }, - }, - }, - }), - checkValue: "some value", - isEqual: true, - }, - { - name: "prop is int fn", - property: newProp(PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::If": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.Bool, - Value: true, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.Int, - Value: 121, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.Int, - Value: -1, - }, - }, - }, - }, - }, - }, - }), - checkValue: 121, - isEqual: true, - }, - { - name: "prop is bool fn", - property: newProp(PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::Equals": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "foo", - }, - }, - }, - }, - }, - }, - }), - checkValue: true, - isEqual: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.isEqual, tt.property.EqualTo(tt.checkValue, tt.opts...)) - }) - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/pseudo_parameters.go b/pkg/iac/scanners/cloudformation/parser/pseudo_parameters.go deleted file mode 100644 index 814ba52ee61e..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/pseudo_parameters.go +++ /dev/null @@ -1,48 +0,0 @@ -package parser - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -type pseudoParameter struct { - t cftypes.CfType - val any - raw any -} - -var pseudoParameters = map[string]pseudoParameter{ - "AWS::AccountId": {t: cftypes.String, val: "123456789012"}, - "AWS::NotificationARNs": { - t: cftypes.List, - val: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "notification::arn::1", - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "notification::arn::2", - }, - }, - }, - raw: []string{"notification::arn::1", "notification::arn::2"}, - }, - "AWS::NoValue": {t: cftypes.String, val: ""}, - "AWS::Partition": {t: cftypes.String, val: "aws"}, - "AWS::Region": {t: cftypes.String, val: "eu-west-1"}, - "AWS::StackId": {t: cftypes.String, val: "arn:aws:cloudformation:eu-west-1:stack/ID"}, - "AWS::StackName": {t: cftypes.String, val: "cfsec-test-stack"}, - "AWS::URLSuffix": {t: cftypes.String, val: "amazonaws.com"}, -} - -func (p pseudoParameter) getRawValue() any { - switch p.t { - case cftypes.List: - return p.raw - default: - return p.val - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/pseudo_parameters_test.go b/pkg/iac/scanners/cloudformation/parser/pseudo_parameters_test.go deleted file mode 100644 index e653b74e65ef..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/pseudo_parameters_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Raw(t *testing.T) { - tests := []struct { - name string - key string - expected any - }{ - { - name: "parameter with a string type value", - key: "AWS::AccountId", - expected: "123456789012", - }, - { - name: "a parameter with a list type value", - key: "AWS::NotificationARNs", - expected: []string{"notification::arn::1", "notification::arn::2"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if val, ok := pseudoParameters[tt.key]; ok { - assert.Equal(t, tt.expected, val.getRawValue()) - } else { - t.Fatal("unexpected parameter key") - } - }) - } -} diff --git a/pkg/iac/scanners/cloudformation/parser/reference.go b/pkg/iac/scanners/cloudformation/parser/reference.go deleted file mode 100644 index 705eef2747af..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/reference.go +++ /dev/null @@ -1,21 +0,0 @@ -package parser - -import ( - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type CFReference struct { - logicalId string - resourceRange iacTypes.Range -} - -func NewCFReference(id string, resourceRange iacTypes.Range) CFReference { - return CFReference{ - logicalId: id, - resourceRange: resourceRange, - } -} - -func (cf CFReference) String() string { - return cf.resourceRange.String() -} diff --git a/pkg/iac/scanners/cloudformation/parser/resource.go b/pkg/iac/scanners/cloudformation/parser/resource.go deleted file mode 100644 index b5910c144bcf..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/resource.go +++ /dev/null @@ -1,208 +0,0 @@ -package parser - -import ( - "io/fs" - "strings" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/jfather" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Resource struct { - ctx *FileContext - rng iacTypes.Range - id string - comment string - Inner ResourceInner -} - -type ResourceInner struct { - Type string `json:"Type" yaml:"Type"` - Properties map[string]*Property `json:"Properties" yaml:"Properties"` -} - -func (r *Resource) configureResource(id string, target fs.FS, filepath string, ctx *FileContext) { - r.setId(id) - r.setFile(target, filepath) - r.setContext(ctx) -} - -func (r *Resource) setId(id string) { - r.id = id - - for n, p := range r.properties() { - p.setName(n) - } -} - -func (r *Resource) setFile(target fs.FS, filepath string) { - r.rng = iacTypes.NewRange(filepath, r.rng.GetStartLine(), r.rng.GetEndLine(), r.rng.GetSourcePrefix(), target) - - for _, p := range r.Inner.Properties { - p.setFileAndParentRange(target, filepath, r.rng) - } -} - -func (r *Resource) setContext(ctx *FileContext) { - r.ctx = ctx - - for _, p := range r.Inner.Properties { - p.SetLogicalResource(r.id) - p.setContext(ctx) - } -} - -func (r *Resource) UnmarshalYAML(value *yaml.Node) error { - r.rng = iacTypes.NewRange("", value.Line-1, calculateEndLine(value), "", nil) - r.comment = value.LineComment - return value.Decode(&r.Inner) -} - -func (r *Resource) UnmarshalJSONWithMetadata(node jfather.Node) error { - r.rng = iacTypes.NewRange("", node.Range().Start.Line, node.Range().End.Line, "", nil) - return node.Decode(&r.Inner) -} - -func (r *Resource) ID() string { - return r.id -} - -func (r *Resource) Type() string { - return r.Inner.Type -} - -func (r *Resource) Range() iacTypes.Range { - return r.rng -} - -func (r *Resource) SourceFormat() SourceFormat { - return r.ctx.SourceFormat -} - -func (r *Resource) Metadata() iacTypes.Metadata { - return iacTypes.NewMetadata(r.Range(), NewCFReference(r.id, r.rng).String()) -} - -func (r *Resource) properties() map[string]*Property { - return r.Inner.Properties -} - -func (r *Resource) IsNil() bool { - return r.id == "" -} - -func (r *Resource) GetProperty(path string) *Property { - - pathParts := strings.Split(path, ".") - - first := pathParts[0] - property := &Property{} - - if p, exists := r.properties()[first]; exists { - property = p - } - - if len(pathParts) == 1 || property.IsNil() { - if property.isFunction() { - resolved, _ := property.resolveValue() - return resolved - } - return property - } - - if nestedProperty := property.GetProperty(strings.Join(pathParts[1:], ".")); nestedProperty != nil { - return nestedProperty - } - - return &Property{} -} - -func (r *Resource) GetStringProperty(path string, defaultValue ...string) iacTypes.StringValue { - defVal := "" - if len(defaultValue) > 0 { - defVal = defaultValue[0] - } - - prop := r.GetProperty(path) - - if prop.IsNotString() { - return r.StringDefault(defVal) - } - return prop.AsStringValue() -} - -func (r *Resource) GetBoolProperty(path string, defaultValue ...bool) iacTypes.BoolValue { - defVal := false - if len(defaultValue) > 0 { - defVal = defaultValue[0] - } - - prop := r.GetProperty(path) - - if prop.IsNotBool() { - return r.inferBool(prop, defVal) - } - return prop.AsBoolValue() -} - -func (r *Resource) GetIntProperty(path string, defaultValue ...int) iacTypes.IntValue { - defVal := 0 - if len(defaultValue) > 0 { - defVal = defaultValue[0] - } - - prop := r.GetProperty(path) - - if prop.IsNotInt() { - return r.IntDefault(defVal) - } - return prop.AsIntValue() -} - -func (r *Resource) StringDefault(defaultValue string) iacTypes.StringValue { - return iacTypes.StringDefault(defaultValue, r.Metadata()) -} - -func (r *Resource) BoolDefault(defaultValue bool) iacTypes.BoolValue { - return iacTypes.BoolDefault(defaultValue, r.Metadata()) -} - -func (r *Resource) IntDefault(defaultValue int) iacTypes.IntValue { - return iacTypes.IntDefault(defaultValue, r.Metadata()) -} - -func (r *Resource) inferBool(prop *Property, defaultValue bool) iacTypes.BoolValue { - if prop.IsString() { - if prop.EqualTo("true", IgnoreCase) { - return iacTypes.Bool(true, prop.Metadata()) - } - if prop.EqualTo("yes", IgnoreCase) { - return iacTypes.Bool(true, prop.Metadata()) - } - if prop.EqualTo("1", IgnoreCase) { - return iacTypes.Bool(true, prop.Metadata()) - } - if prop.EqualTo("false", IgnoreCase) { - return iacTypes.Bool(false, prop.Metadata()) - } - if prop.EqualTo("no", IgnoreCase) { - return iacTypes.Bool(false, prop.Metadata()) - } - if prop.EqualTo("0", IgnoreCase) { - return iacTypes.Bool(false, prop.Metadata()) - } - } - - if prop.IsInt() { - if prop.EqualTo(0) { - return iacTypes.Bool(false, prop.Metadata()) - } - if prop.EqualTo(1) { - return iacTypes.Bool(true, prop.Metadata()) - } - } - - return r.BoolDefault(defaultValue) -} diff --git a/pkg/iac/scanners/cloudformation/parser/resource_test.go b/pkg/iac/scanners/cloudformation/parser/resource_test.go deleted file mode 100644 index 1b67fc1d773b..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/resource_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" -) - -func Test_GetProperty_PropIsFunction(t *testing.T) { - resource := Resource{ - Inner: ResourceInner{ - Type: "AWS::S3::Bucket", - Properties: map[string]*Property{ - "BucketName": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "mybucket", - }, - }, - "VersioningConfiguration": { - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Fn::If": { - Inner: PropertyInner{ - Type: cftypes.List, - Value: []*Property{ - { - Inner: PropertyInner{ - Type: cftypes.Bool, - Value: false, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Status": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "Enabled", - }, - }, - }, - }, - }, - { - Inner: PropertyInner{ - Type: cftypes.Map, - Value: map[string]*Property{ - "Status": { - Inner: PropertyInner{ - Type: cftypes.String, - Value: "Suspended", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - prop := resource.GetProperty("VersioningConfiguration.Status") - require.NotNil(t, prop) - require.True(t, prop.IsString()) - require.Equal(t, "Suspended", prop.AsString()) -} diff --git a/pkg/iac/scanners/cloudformation/parser/util.go b/pkg/iac/scanners/cloudformation/parser/util.go deleted file mode 100644 index c6babcb7a8f3..000000000000 --- a/pkg/iac/scanners/cloudformation/parser/util.go +++ /dev/null @@ -1,152 +0,0 @@ -package parser - -import ( - "strconv" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/jfather" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes" - "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser" -) - -func setPropertyValueFromJson(node jfather.Node, propertyData *PropertyInner) error { - - switch node.Kind() { - case jfather.KindNumber: - var val any - if err := node.Decode(&val); err != nil { - return err - } - switch v := val.(type) { - case float64: - propertyData.Type = cftypes.Float64 - propertyData.Value = v - case int64: - propertyData.Type = cftypes.Int - propertyData.Value = int(v) - } - return nil - case jfather.KindBoolean: - propertyData.Type = cftypes.Bool - return node.Decode(&propertyData.Value) - case jfather.KindString: - propertyData.Type = cftypes.String - return node.Decode(&propertyData.Value) - case jfather.KindObject: - var childData map[string]*Property - if err := node.Decode(&childData); err != nil { - return err - } - propertyData.Type = cftypes.Map - propertyData.Value = childData - return nil - case jfather.KindArray: - var childData []*Property - if err := node.Decode(&childData); err != nil { - return err - } - propertyData.Type = cftypes.List - propertyData.Value = childData - return nil - default: - propertyData.Type = cftypes.String - return node.Decode(&propertyData.Value) - } - -} - -func setPropertyValueFromYaml(node *yaml.Node, propertyData *PropertyInner) error { - if IsIntrinsicFunc(node) { - var newContent []*yaml.Node - - newContent = append(newContent, &yaml.Node{ - Tag: "!!str", - Value: getIntrinsicTag(node.Tag), - Kind: yaml.ScalarNode, - }) - - newContent = createNode(node, newContent) - - node.Tag = string(parser.TagMap) - node.Kind = yaml.MappingNode - node.Content = newContent - } - - if node.Content == nil { - - switch node.Tag { - case "!!int": - propertyData.Type = cftypes.Int - propertyData.Value, _ = strconv.Atoi(node.Value) - case "!!bool": - propertyData.Type = cftypes.Bool - propertyData.Value, _ = strconv.ParseBool(node.Value) - case "!!float": - propertyData.Type = cftypes.Float64 - propertyData.Value, _ = strconv.ParseFloat(node.Value, 64) - case "!!str", "!!string": - propertyData.Type = cftypes.String - propertyData.Value = node.Value - } - return nil - } - - switch node.Tag { - case string(parser.TagMap): - var childData map[string]*Property - if err := node.Decode(&childData); err != nil { - return err - } - propertyData.Type = cftypes.Map - propertyData.Value = childData - return nil - case "!!seq": - var childData []*Property - if err := node.Decode(&childData); err != nil { - return err - } - propertyData.Type = cftypes.List - propertyData.Value = childData - return nil - } - - return nil -} - -func createNode(node *yaml.Node, newContent []*yaml.Node) []*yaml.Node { - if node.Content == nil { - newContent = append(newContent, &yaml.Node{ - Tag: "!!str", - Value: node.Value, - Kind: yaml.ScalarNode, - }) - } else { - - newNode := &yaml.Node{ - Content: node.Content, - Kind: node.Kind, - } - - switch node.Kind { - case yaml.SequenceNode: - newNode.Tag = "!!seq" - case yaml.MappingNode: - newNode.Tag = string(parser.TagMap) - case yaml.ScalarNode: - default: - newNode.Tag = node.Tag - } - newContent = append(newContent, newNode) - } - return newContent -} - -func calculateEndLine(node *yaml.Node) int { - if node.Content == nil { - return node.Line - } - - return calculateEndLine(node.Content[len(node.Content)-1]) - -} diff --git a/pkg/iac/scanners/cloudformation/scanner.go b/pkg/iac/scanners/cloudformation/scanner.go deleted file mode 100644 index f29e98213291..000000000000 --- a/pkg/iac/scanners/cloudformation/scanner.go +++ /dev/null @@ -1,173 +0,0 @@ -package cloudformation - -import ( - "context" - "fmt" - "io/fs" - "sort" - "sync" - - adapter "github.com/aquasecurity/trivy/pkg/iac/adapters/cloudformation" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -func WithParameters(params map[string]any) options.ScannerOption { - return func(cs options.ConfigurableScanner) { - if s, ok := cs.(*Scanner); ok { - s.addParserOption(parser.WithParameters(params)) - } - } -} - -func WithParameterFiles(files ...string) options.ScannerOption { - return func(cs options.ConfigurableScanner) { - if s, ok := cs.(*Scanner); ok { - s.addParserOption(parser.WithParameterFiles(files...)) - } - } -} - -func WithConfigsFS(fsys fs.FS) options.ScannerOption { - return func(cs options.ConfigurableScanner) { - if s, ok := cs.(*Scanner); ok { - s.addParserOption(parser.WithConfigsFS(fsys)) - } - } -} - -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner - options []options.ScannerOption - parserOptions []parser.Option -} - -func (s *Scanner) addParserOption(opt parser.Option) { - s.parserOptions = append(s.parserOptions, opt) -} - -func (s *Scanner) Name() string { - return "CloudFormation" -} - -// New creates a new Scanner -func New(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("cloudformation scanner"), - } - for _, opt := range opts { - opt(s) - } - s.parser = parser.New(s.parserOptions...) - return s -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (results scan.Results, err error) { - - contexts, err := s.parser.ParseFS(ctx, fsys, dir) - if err != nil { - return nil, err - } - - if len(contexts) == 0 { - return nil, nil - } - - regoScanner, err := s.initRegoScanner(fsys) - if err != nil { - return nil, err - } - - for _, cfCtx := range contexts { - if cfCtx == nil { - continue - } - fileResults, err := s.scanFileContext(ctx, regoScanner, cfCtx, fsys) - if err != nil { - return nil, err - } - results = append(results, fileResults...) - } - sort.Slice(results, func(i, j int) bool { - return results[i].Rule().AVDID < results[j].Rule().AVDID - }) - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - cfCtx, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - - regoScanner, err := s.initRegoScanner(fsys) - if err != nil { - return nil, err - } - - results, err := s.scanFileContext(ctx, regoScanner, cfCtx, fsys) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", fsys, false) - - sort.Slice(results, func(i, j int) bool { - return results[i].Rule().AVDID < results[j].Rule().AVDID - }) - return results, nil -} - -func (s *Scanner) scanFileContext(ctx context.Context, regoScanner *rego.Scanner, cfCtx *parser.FileContext, fsys fs.FS) (scan.Results, error) { - state := adapter.Adapt(*cfCtx) - if state == nil { - return nil, nil - } - - results, err := regoScanner.ScanInput(ctx, types.SourceCloud, rego.Input{ - Path: cfCtx.Metadata().Range().GetFilename(), - FS: fsys, - Contents: state.ToRego(), - }) - if err != nil { - return nil, fmt.Errorf("rego scan error: %w", err) - } - - // ignore a result based on user input - results.Ignore(cfCtx.Ignores, nil) - - for _, ignored := range results.GetIgnored() { - s.logger.Info("Ignore finding", - log.String("rule", ignored.Rule().LongID()), - log.String("range", ignored.Range().String()), - ) - } - - return results, nil -} diff --git a/pkg/iac/scanners/cloudformation/scanner_test.go b/pkg/iac/scanners/cloudformation/scanner_test.go deleted file mode 100644 index 36ed3724e160..000000000000 --- a/pkg/iac/scanners/cloudformation/scanner_test.go +++ /dev/null @@ -1,229 +0,0 @@ -package cloudformation - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/main.yaml": `--- -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: public-bucket - -`, - "/rules/rule.rego": `package builtin.dockerfile.DS006 - -__rego_metadata__ := { - "id": "DS006", - "avd_id": "AVD-DS-0006", - "title": "COPY '--from' referring to the current image", - "short_code": "no-self-referencing-copy-from", - "version": "v1.0.0", - "severity": "CRITICAL", - "type": "Dockerfile Security Check", - "description": "COPY '--from' should not mention the current FROM alias, since it is impossible to copy from itself.", - "recommended_actions": "Change the '--from' so that it will not refer to itself", - "url": "https://docs.docker.com/develop/develop-images/multistage-build/", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "defsec", "subtypes": [{"service": "s3", "provider": "aws"}]}], -} - -deny[res] { - res := { - "msg": "oh no", - "filepath": "code/main.yaml", - "startline": 6, - "endline": 6, - } -} - -`, - }) - - scanner := New(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-DS-0006", - Aliases: []string{"DS006"}, - ShortCode: "no-self-referencing-copy-from", - Summary: "COPY '--from' referring to the current image", - Explanation: "COPY '--from' should not mention the current FROM alias, since it is impossible to copy from itself.", - Impact: "", - Resolution: "Change the '--from' so that it will not refer to itself", - Provider: "cloud", - Service: "general", - Links: []string{"https://docs.docker.com/develop/develop-images/multistage-build/"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - RegoPackage: "data.builtin.dockerfile.DS006", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) - - failure := results.GetFailed()[0] - actualCode, err := failure.GetCode() - require.NoError(t, err) - for i := range actualCode.Lines { - actualCode.Lines[i].Highlighted = "" - } - assert.Equal(t, []scan.Line{ - { - Number: 6, - Content: " BucketName: public-bucket", - IsCause: true, - FirstCause: true, - LastCause: true, - Annotation: "", - }, - }, actualCode.Lines) -} - -const bucketNameCheck = `# METADATA -# title: "test rego" -# scope: package -# schemas: -# - input: schema["cloud"] -# custom: -# id: AVD-AWS-001 -# avd_id: AVD-AWS-001 -# provider: aws -# service: s3 -# severity: LOW -# input: -# selector: -# - type: cloud -# subtypes: -# - service: s3 -# provider: aws -package user.aws.aws001 - -deny[res] { - bucket := input.aws.s3.buckets[_] - bucket.name.value == "test-bucket" - res := result.new("Denied", bucket.name) -} - -deny[res] { - bucket := input.aws.s3.buckets[_] - algo := bucket.encryption.algorithm - algo.value == "AES256" - res := result.new("Denied", algo) -} -` - -func TestIgnore(t *testing.T) { - tests := []struct { - name string - src string - ignored int - }{ - { - name: "without ignore", - src: `--- -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: test-bucket -`, - ignored: 0, - }, - { - name: "rule before resource", - src: `--- -Resources: -#trivy:ignore:AVD-AWS-001 - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: test-bucket -`, - ignored: 1, - }, - { - name: "rule before property", - src: `--- -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: -#trivy:ignore:AVD-AWS-001 - BucketName: test-bucket -`, - ignored: 1, - }, - { - name: "rule on the same line with the property", - src: `--- -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: test-bucket #trivy:ignore:AVD-AWS-001 -`, - ignored: 1, - }, - { - name: "rule on the same line with the nested property", - src: `--- -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: test-bucket - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: AES256 #trivy:ignore:AVD-AWS-001 -`, - ignored: 1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fsys := testutil.CreateFS(t, map[string]string{ - "/code/main.yaml": tt.src, - }) - - scanner := New( - rego.WithEmbeddedPolicies(false), - rego.WithPolicyReader(strings.NewReader(bucketNameCheck)), - rego.WithPolicyNamespaces("user"), - ) - - results, err := scanner.ScanFS(context.TODO(), fsys, "code") - require.NoError(t, err) - - if tt.ignored == 0 { - require.Len(t, results.GetFailed(), 1) - } else { - assert.Len(t, results.GetIgnored(), tt.ignored) - } - }) - } -} diff --git a/pkg/iac/scanners/cloudformation/test/cf_scanning_test.go b/pkg/iac/scanners/cloudformation/test/cf_scanning_test.go deleted file mode 100644 index 46eed857a75f..000000000000 --- a/pkg/iac/scanners/cloudformation/test/cf_scanning_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package test - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation" -) - -func Test_basic_cloudformation_scanning(t *testing.T) { - cfScanner := cloudformation.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)) - - results, err := cfScanner.ScanFS(context.TODO(), os.DirFS("./examples/bucket"), ".") - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) -} - -func Test_cloudformation_scanning_has_expected_errors(t *testing.T) { - cfScanner := cloudformation.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)) - - results, err := cfScanner.ScanFS(context.TODO(), os.DirFS("./examples/bucket"), ".") - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) -} diff --git a/pkg/iac/scanners/cloudformation/test/examples/bucket/bucket.yaml b/pkg/iac/scanners/cloudformation/test/examples/bucket/bucket.yaml deleted file mode 100644 index 21f1c25042b0..000000000000 --- a/pkg/iac/scanners/cloudformation/test/examples/bucket/bucket.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -AWSTemplateFormatVersion: "2010-09-09" -Description: An example Stack for a bucket -Parameters: - BucketName: - Type: String - Default: naughty-bucket - EncryptBucket: - Type: Boolean - Default: false -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: - Ref: BucketName - PublicAccessBlockConfiguration: - BlockPublicAcls: false - BlockPublicPolicy: false - IgnorePublicAcls: true - RestrictPublicBuckets: false - BucketEncryption: - ServerSideEncryptionConfiguration: - - BucketKeyEnabled: !Ref EncryptBucket diff --git a/pkg/iac/scanners/cloudformation/test/examples/ignores/bucket_with_ignores.yaml b/pkg/iac/scanners/cloudformation/test/examples/ignores/bucket_with_ignores.yaml deleted file mode 100644 index ec5e8a8d7661..000000000000 --- a/pkg/iac/scanners/cloudformation/test/examples/ignores/bucket_with_ignores.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -AWSTemplateFormatVersion: "2010-09-09" -Description: An example Stack for a bucket -Parameters: - BucketName: - Type: String - Default: naughty-bucket - EncryptBucket: - Type: Boolean - Default: false -Resources: - S3Bucket: - Type: 'AWS::S3::Bucket' - Properties: - BucketName: - Ref: BucketName - PublicAccessBlockConfiguration: - BlockPublicAcls: false - BlockPublicPolicy: false # cfsec:ignore:AVD-AWS-0087 - IgnorePublicAcls: true - RestrictPublicBuckets: false - BucketEncryption: - ServerSideEncryptionConfiguration: - - BucketKeyEnabled: !Ref EncryptBucket diff --git a/pkg/iac/scanners/cloudformation/test/examples/roles/roles.yml b/pkg/iac/scanners/cloudformation/test/examples/roles/roles.yml deleted file mode 100644 index 5b927457762b..000000000000 --- a/pkg/iac/scanners/cloudformation/test/examples/roles/roles.yml +++ /dev/null @@ -1,51 +0,0 @@ -Resources: - LambdaAPIRole: - Type: "AWS::IAM::Role" - Properties: - RoleName: "${self:service}-${self:provider.stage}-LambdaAPI" - Policies: - - PolicyName: "${self:service}-${self:provider.stage}-lambda" - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - "logs:CreateLogStream" - - "logs:CreateLogGroup" - - "logs:PutLogEvents" - Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${self:service}-${self:provider.stage}*:*" - - !If - - EnableCrossAccountSnsPublish - - PolicyName: "${self:service}-${self:provider.stage}-asngen-sns-publish" - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - "SNS:Publish" - Resource: - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-PurchaseOrder.fifo" - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-Vendor.fifo" - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-Customer.fifo" - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-Manufacturer.fifo" - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-ManufacturerItem.fifo" - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-Item.fifo" - - !Sub "arn:aws:sns:${self:provider.region}:${self:provider.itopia_account_id}:${self:provider.stage}-*-VendorItem.fifo" - - !Ref "AWS::NoValue" - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - "lambda.amazonaws.com" - Action: - - "sts:AssumeRole" - - - - -Conditions: - EnableCrossAccountSnsPublish: !Equals - - ${env:ALLOW_SNS_PUBLISH, true} - - true diff --git a/pkg/iac/scanners/dockerfile/parser/parser.go b/pkg/iac/scanners/dockerfile/parser/parser.go deleted file mode 100644 index 2079b269f124..000000000000 --- a/pkg/iac/scanners/dockerfile/parser/parser.go +++ /dev/null @@ -1,131 +0,0 @@ -package parser - -import ( - "context" - "fmt" - "io" - "strings" - - "github.com/moby/buildkit/frontend/dockerfile/instructions" - "github.com/moby/buildkit/frontend/dockerfile/parser" - - "github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile" -) - -func Parse(_ context.Context, r io.Reader, path string) (any, error) { - parsed, err := parser.Parse(r) - if err != nil { - return nil, fmt.Errorf("dockerfile parse error: %w", err) - } - - var ( - parsedFile dockerfile.Dockerfile - stage dockerfile.Stage - stageIndex int - ) - - fromValue := "args" - for _, child := range parsed.AST.Children { - child.Value = strings.ToLower(child.Value) - - instr, err := instructions.ParseInstruction(child) - if err != nil { - return nil, fmt.Errorf("parse dockerfile instruction: %w", err) - } - - if _, ok := instr.(*instructions.Stage); ok { - if len(stage.Commands) > 0 { - parsedFile.Stages = append(parsedFile.Stages, stage) - } - if fromValue != "args" { - stageIndex++ - } - fromValue = strings.TrimSpace(strings.TrimPrefix(child.Original, "FROM ")) - stage = dockerfile.Stage{ - Name: fromValue, - } - } - - cmd := dockerfile.Command{ - Cmd: child.Value, - Original: child.Original, - Flags: child.Flags, - Stage: stageIndex, - Path: path, - StartLine: child.StartLine, - EndLine: child.EndLine, - } - - // processing statement with sub-statement - // example: ONBUILD RUN foo bar - // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#onbuild - if child.Next != nil && len(child.Next.Children) > 0 { - cmd.SubCmd = child.Next.Children[0].Value - child = child.Next.Children[0] - } - - // mark if the instruction is in exec form - // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#exec-form - cmd.JSON = child.Attributes["json"] - - // heredoc may contain a script that will be executed in the shell, so we need to process it - // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#here-documents - if len(child.Heredocs) > 0 && child.Next != nil { - cmd.Original = originalFromHeredoc(child) - cmd.Value = []string{processHeredoc(child)} - } else { - for n := child.Next; n != nil; n = n.Next { - cmd.Value = append(cmd.Value, n.Value) - } - } - - stage.Commands = append(stage.Commands, cmd) - - } - if len(stage.Commands) > 0 { - parsedFile.Stages = append(parsedFile.Stages, stage) - } - - return &parsedFile, nil -} - -func originalFromHeredoc(node *parser.Node) string { - var sb strings.Builder - sb.WriteString(node.Original) - sb.WriteRune('\n') - for i, heredoc := range node.Heredocs { - sb.WriteString(heredoc.Content) - sb.WriteString(heredoc.Name) - if i != len(node.Heredocs)-1 { - sb.WriteRune('\n') - } - } - - return sb.String() -} - -// heredoc processing taken from here -// https://github.com/moby/buildkit/blob/9a39e2c112b7c98353c27e64602bc08f31fe356e/frontend/dockerfile/dockerfile2llb/convert.go#L1200 -func processHeredoc(node *parser.Node) string { - if parser.MustParseHeredoc(node.Next.Value) == nil || strings.HasPrefix(node.Heredocs[0].Content, "#!") { - // more complex heredoc is passed to the shell as is - var sb strings.Builder - sb.WriteString(node.Next.Value) - for _, heredoc := range node.Heredocs { - sb.WriteRune('\n') - sb.WriteString(heredoc.Content) - sb.WriteString(heredoc.Name) - } - return sb.String() - } - - // simple heredoc and the content is run in a shell - content := node.Heredocs[0].Content - if node.Heredocs[0].Chomp { - content = parser.ChompHeredocContent(content) - } - - content = strings.ReplaceAll(content, "\r\n", "\n") - cmds := strings.Split(strings.TrimSuffix(content, "\n"), "\n") - return strings.Join(cmds, " ; ") -} diff --git a/pkg/iac/scanners/dockerfile/parser/parser_test.go b/pkg/iac/scanners/dockerfile/parser/parser_test.go deleted file mode 100644 index ed73def7e9f8..000000000000 --- a/pkg/iac/scanners/dockerfile/parser/parser_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package parser_test - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile" - "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser" -) - -func Test_Parser(t *testing.T) { - input := `FROM ubuntu:18.04 -COPY . /app -RUN make /app -CMD python /app/app.py -` - - res, err := parser.Parse(context.TODO(), strings.NewReader(input), "Dockerfile") - require.NoError(t, err) - - df, ok := res.(*dockerfile.Dockerfile) - require.True(t, ok) - - assert.Len(t, df.Stages, 1) - - assert.Equal(t, "ubuntu:18.04", df.Stages[0].Name) - commands := df.Stages[0].Commands - assert.Len(t, commands, 4) - - // FROM ubuntu:18.04 - assert.Equal(t, "from", commands[0].Cmd) - assert.Equal(t, "ubuntu:18.04", commands[0].Value[0]) - assert.Equal(t, "Dockerfile", commands[0].Path) - assert.Equal(t, 1, commands[0].StartLine) - assert.Equal(t, 1, commands[0].EndLine) - - // COPY . /app - assert.Equal(t, "copy", commands[1].Cmd) - assert.Equal(t, ". /app", strings.Join(commands[1].Value, " ")) - assert.Equal(t, "Dockerfile", commands[1].Path) - assert.Equal(t, 2, commands[1].StartLine) - assert.Equal(t, 2, commands[1].EndLine) - - // RUN make /app - assert.Equal(t, "run", commands[2].Cmd) - assert.Equal(t, "make /app", commands[2].Value[0]) - assert.Equal(t, "Dockerfile", commands[2].Path) - assert.Equal(t, 3, commands[2].StartLine) - assert.Equal(t, 3, commands[2].EndLine) - - // CMD python /app/app.py - assert.Equal(t, "cmd", commands[3].Cmd) - assert.Equal(t, "python /app/app.py", commands[3].Value[0]) - assert.Equal(t, "Dockerfile", commands[3].Path) - assert.Equal(t, 4, commands[3].StartLine) - assert.Equal(t, 4, commands[3].EndLine) -} - -func Test_ParseHeredocs(t *testing.T) { - tests := []struct { - name string - src string - expected string - }{ - { - name: "multi-line script", - src: `RUN < /tmp/output && echo 'done' -hello -mr -potato -EOF`, - expected: "cat < /tmp/output && echo 'done'\nhello\nmr\npotato\nEOF", - }, - { - name: "redirect to file", - src: `RUN < /etc/config.yaml -key1: value1 -key2: value2 -EOF`, - expected: "< /etc/config.yaml\nkey1: value1\nkey2: value2\nEOF", - }, - { - name: "with a shebang", - src: `RUN < 1 { - return manifestFilePathParts[1] - } - return manifestFilePathParts[0] -} diff --git a/pkg/iac/scanners/helm/parser/parser_tar.go b/pkg/iac/scanners/helm/parser/parser_tar.go deleted file mode 100644 index 8e2ba97a81d2..000000000000 --- a/pkg/iac/scanners/helm/parser/parser_tar.go +++ /dev/null @@ -1,176 +0,0 @@ -package parser - -import ( - "archive/tar" - "compress/gzip" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path" - "path/filepath" - - "github.com/liamg/memoryfs" - - "github.com/aquasecurity/trivy/pkg/iac/detection" - "github.com/aquasecurity/trivy/pkg/log" -) - -var errSkipFS = errors.New("skip parse FS") - -func (p *Parser) addTarToFS(archivePath string) (fs.FS, error) { - tarFS := memoryfs.CloneFS(p.workingFS) - - file, err := tarFS.Open(archivePath) - if err != nil { - return nil, fmt.Errorf("failed to open tar: %w", err) - } - defer file.Close() - - var tr *tar.Reader - - if detection.IsZip(archivePath) { - zipped, err := gzip.NewReader(file) - if err != nil { - return nil, fmt.Errorf("failed to create gzip reader: %w", err) - } - defer zipped.Close() - tr = tar.NewReader(zipped) - } else { - tr = tar.NewReader(file) - } - - checkExistedChart := true - symlinks := make(map[string]string) - - for { - header, err := tr.Next() - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return nil, fmt.Errorf("failed to get next entry: %w", err) - } - - name := filepath.ToSlash(header.Name) - - if checkExistedChart { - // Do not add archive files to FS if the chart already exists - // This can happen when the source chart is located next to an archived chart (with the `helm package` command) - // The first level folder in the archive is equal to the Chart name - if _, err := tarFS.Stat(path.Dir(archivePath) + "/" + path.Dir(name)); err == nil { - return nil, errSkipFS - } - checkExistedChart = false - } - - // get the individual path and extract to the current directory - targetPath := path.Join(path.Dir(archivePath), path.Clean(name)) - - link := filepath.ToSlash(header.Linkname) - - switch header.Typeflag { - case tar.TypeDir: - if err := tarFS.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil && !errors.Is(err, fs.ErrExist) { - return nil, err - } - case tar.TypeReg: - p.logger.Debug("Unpacking tar entry", log.FilePath(targetPath)) - if err := copyFile(tarFS, tr, targetPath); err != nil { - return nil, err - } - case tar.TypeSymlink: - if path.IsAbs(link) { - p.logger.Debug("Symlink is absolute, skipping", log.String("link", link)) - continue - } - - symlinks[targetPath] = path.Join(path.Dir(targetPath), link) // nolint:gosec // virtual file system is used - default: - return nil, fmt.Errorf("header type %q is not supported", header.Typeflag) - } - } - - for target, link := range symlinks { - if err := copySymlink(tarFS, link, target); err != nil { - return nil, fmt.Errorf("copy symlink error: %w", err) - } - } - - if err := tarFS.Remove(archivePath); err != nil { - return nil, fmt.Errorf("remove tar from FS error: %w", err) - } - - return tarFS, nil -} - -func copySymlink(fsys *memoryfs.FS, src, dst string) error { - fi, err := fsys.Stat(src) - if err != nil { - return nil - } - if fi.IsDir() { - if err := copyDir(fsys, src, dst); err != nil { - return fmt.Errorf("copy dir error: %w", err) - } - return nil - } - - if err := copyFileLazy(fsys, src, dst); err != nil { - return fmt.Errorf("copy file error: %w", err) - } - - return nil -} - -func copyFile(fsys *memoryfs.FS, src io.Reader, dst string) error { - if err := fsys.MkdirAll(path.Dir(dst), fs.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) { - return fmt.Errorf("mkdir error: %w", err) - } - - b, err := io.ReadAll(src) - if err != nil { - return fmt.Errorf("read error: %w", err) - } - - if err := fsys.WriteFile(dst, b, fs.ModePerm); err != nil { - return fmt.Errorf("write file error: %w", err) - } - - return nil -} - -func copyDir(fsys *memoryfs.FS, src, dst string) error { - walkFn := func(filePath string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - - if entry.IsDir() { - return nil - } - - dst := path.Join(dst, filePath[len(src):]) - - if err := copyFileLazy(fsys, filePath, dst); err != nil { - return fmt.Errorf("copy file error: %w", err) - } - return nil - } - - return fs.WalkDir(fsys, src, walkFn) -} - -func copyFileLazy(fsys *memoryfs.FS, src, dst string) error { - if err := fsys.MkdirAll(path.Dir(dst), fs.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) { - return fmt.Errorf("mkdir error: %w", err) - } - return fsys.WriteLazyFile(dst, func() (io.Reader, error) { - f, err := fsys.Open(src) - if err != nil { - return nil, err - } - return f, nil - }, fs.ModePerm) -} diff --git a/pkg/iac/scanners/helm/parser/parser_test.go b/pkg/iac/scanners/helm/parser/parser_test.go deleted file mode 100644 index 01aa697d559c..000000000000 --- a/pkg/iac/scanners/helm/parser/parser_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package parser - -import ( - "context" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestParseFS(t *testing.T) { - t.Run("source chart is located next to an same archived chart", func(t *testing.T) { - p, err := New(".") - require.NoError(t, err) - require.NoError(t, p.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", "chart-and-archived-chart")), ".")) - - expectedFiles := []string{ - "my-chart/Chart.yaml", - "my-chart/templates/pod.yaml", - } - assert.Equal(t, expectedFiles, p.filepaths) - }) - - t.Run("archive with symlinks", func(t *testing.T) { - // mkdir -p chart && cd $_ - // touch Chart.yaml - // mkdir -p dir && cp -p Chart.yaml dir/Chart.yaml - // mkdir -p sym-to-file && ln -s ../Chart.yaml sym-to-file/Chart.yaml - // ln -s dir sym-to-dir - // mkdir rec-sym && touch rec-sym/Chart.yaml - // ln -s . ./rec-sym/a - // cd .. && tar -czvf chart.tar.gz chart && rm -rf chart - p, err := New(".") - require.NoError(t, err) - - fsys := os.DirFS(filepath.Join("testdata", "archive-with-symlinks")) - require.NoError(t, p.ParseFS(context.TODO(), fsys, "chart.tar.gz")) - - expectedFiles := []string{ - "chart/Chart.yaml", - "chart/dir/Chart.yaml", - "chart/rec-sym/Chart.yaml", - "chart/rec-sym/a/Chart.yaml", - "chart/sym-to-dir/Chart.yaml", - "chart/sym-to-file/Chart.yaml", - } - assert.Equal(t, expectedFiles, p.filepaths) - }) - - t.Run("chart with multiple archived deps", func(t *testing.T) { - p, err := New(".") - require.NoError(t, err) - - fsys := os.DirFS(filepath.Join("testdata", "multiple-archived-deps")) - require.NoError(t, p.ParseFS(context.TODO(), fsys, ".")) - - expectedFiles := []string{ - "Chart.yaml", - "charts/common-2.26.0.tgz", - "charts/opentelemetry-collector-0.108.0.tgz", - } - assert.Equal(t, expectedFiles, p.filepaths) - }) - - t.Run("archives are not dependencies", func(t *testing.T) { - p, err := New(".") - require.NoError(t, err) - - fsys := os.DirFS(filepath.Join("testdata", "non-deps-archives")) - require.NoError(t, p.ParseFS(context.TODO(), fsys, ".")) - - expectedFiles := []string{ - "Chart.yaml", - "backup_charts/wordpress-operator/Chart.yaml", - "backup_charts/mysql-operator/Chart.yaml", - } - assert.Subset(t, p.filepaths, expectedFiles) - }) -} diff --git a/pkg/iac/scanners/helm/parser/testdata/archive-with-symlinks/chart.tar.gz b/pkg/iac/scanners/helm/parser/testdata/archive-with-symlinks/chart.tar.gz deleted file mode 100644 index a3183710c17f..000000000000 Binary files a/pkg/iac/scanners/helm/parser/testdata/archive-with-symlinks/chart.tar.gz and /dev/null differ diff --git a/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart-0.1.0.tgz b/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart-0.1.0.tgz deleted file mode 100644 index e36b2b474f3e..000000000000 Binary files a/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart-0.1.0.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart/Chart.yaml deleted file mode 100644 index 767f748a8d59..000000000000 --- a/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -name: my-chart -description: A Helm chart for Kubernetes -type: application -version: 0.1.0 -appVersion: "1.16.0" diff --git a/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart/templates/pod.yaml b/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart/templates/pod.yaml deleted file mode 100644 index 3649247c1bb1..000000000000 --- a/pkg/iac/scanners/helm/parser/testdata/chart-and-archived-chart/my-chart/templates/pod.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - labels: - app: nginx -spec: - replicas: 3 - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx:1.14.2 - ports: - - containerPort: 80 \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml deleted file mode 100644 index 82d6c918088f..000000000000 --- a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v2 -appVersion: "1.1" -description: Test Chart -name: y-chart -version: 1.0.0 -kubeVersion: ">=1.21" - -dependencies: - - name: common - repository: https://charts.bitnami.com/bitnami - version: 2.26.0 - - name: opentelemetry-collector - version: 0.108.0 - repository: https://open-telemetry.github.io/opentelemetry-helm-charts \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz deleted file mode 100644 index 43f85ba36a91..000000000000 Binary files a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz deleted file mode 100644 index 0ea88fb48247..000000000000 Binary files a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml deleted file mode 100644 index 65225b732935..000000000000 --- a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v2 -appVersion: "1.1" -description: Test Chart -name: y-chart -version: 1.0.0 -kubeVersion: ">=1.21" - -dependencies: - - name: mysql-operator - repository: https://mysql.github.io/mysql-operator/ - version: 2.2.2 - - name: wordpress-operator - version: 0.12.4 - repository: https://helm-charts.bitpoke.io \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz deleted file mode 100644 index b80fc3c0425e..000000000000 Binary files a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz deleted file mode 100644 index 4086e9f1a6ba..000000000000 Binary files a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/parser/vals.go b/pkg/iac/scanners/helm/parser/vals.go deleted file mode 100644 index f2589b3caec7..000000000000 --- a/pkg/iac/scanners/helm/parser/vals.go +++ /dev/null @@ -1,114 +0,0 @@ -package parser - -import ( - "fmt" - "io" - "net/url" - "os" - "strings" - - "gopkg.in/yaml.v3" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/strvals" -) - -type ValueOptions struct { - ValueFiles []string - StringValues []string - Values []string - FileValues []string -} - -// MergeValues merges values from files specified via -f/--values and directly -// via --set, --set-string, or --set-file, marshaling them to YAML -func (opts *ValueOptions) MergeValues() (map[string]any, error) { - base := make(map[string]any) - - // User specified a values files via -f/--values - for _, filePath := range opts.ValueFiles { - currentMap := make(map[string]any) - - bytes, err := readFile(filePath) - if err != nil { - return nil, err - } - - if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { - return nil, fmt.Errorf("failed to parse %s: %w", filePath, err) - } - // Merge with the previous map - base = mergeMaps(base, currentMap) - } - - // User specified a value via --set - for _, value := range opts.Values { - if err := strvals.ParseInto(value, base); err != nil { - return nil, fmt.Errorf("failed parsing --set data, %w", err) - } - } - - // User specified a value via --set-string - for _, value := range opts.StringValues { - if err := strvals.ParseIntoString(value, base); err != nil { - return nil, fmt.Errorf("failed parsing --set-string data %w", err) - } - } - - // User specified a value via --set-file - for _, value := range opts.FileValues { - reader := func(rs []rune) (any, error) { - bytes, err := readFile(string(rs)) - if err != nil { - return nil, err - } - return string(bytes), err - } - if err := strvals.ParseIntoFile(value, base, reader); err != nil { - return nil, fmt.Errorf("failed parsing --set-file data: %w", err) - } - } - - return base, nil -} - -func mergeMaps(a, b map[string]any) map[string]any { - out := make(map[string]any, len(a)) - for k, v := range a { - out[k] = v - } - for k, v := range b { - if v, ok := v.(map[string]any); ok { - if bv, ok := out[k]; ok { - if bv, ok := bv.(map[string]any); ok { - out[k] = mergeMaps(bv, v) - continue - } - } - } - out[k] = v - } - return out -} - -// readFile load a file from stdin, the local directory, or a remote file with a url. -func readFile(filePath string) ([]byte, error) { - if strings.TrimSpace(filePath) == "-" { - return io.ReadAll(os.Stdin) - } - u, _ := url.Parse(filePath) - - // FIXME: maybe someone handle other protocols like ftp. - if u.Scheme == "http" || u.Scheme == "https" { - g, err := getter.NewHTTPGetter() - if err != nil { - return nil, err - } - data, err := g.Get(filePath, getter.WithURL(filePath)) - if err != nil { - return nil, err - } - return data.Bytes(), err - } else { - return os.ReadFile(filePath) - } -} diff --git a/pkg/iac/scanners/helm/scanner.go b/pkg/iac/scanners/helm/scanner.go deleted file mode 100644 index 6ab324f66f28..000000000000 --- a/pkg/iac/scanners/helm/scanner.go +++ /dev/null @@ -1,177 +0,0 @@ -package helm - -import ( - "context" - "fmt" - "io" - "io/fs" - "path/filepath" - "strings" - "sync" - - "github.com/liamg/memoryfs" - - "github.com/aquasecurity/trivy/pkg/iac/detection" - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/helm/parser" - kparser "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - options []options.ScannerOption - parserOptions []parser.Option - regoScanner *rego.Scanner -} - -// New creates a new Scanner -func New(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("helm scanner"), - } - - for _, option := range opts { - option(s) - } - return s -} - -func (s *Scanner) addParserOptions(opts ...parser.Option) { - s.parserOptions = append(s.parserOptions, opts...) -} - -func (s *Scanner) Name() string { - return "Helm" -} - -func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, path string) (scan.Results, error) { - - if err := s.initRegoScanner(target); err != nil { - return nil, fmt.Errorf("failed to init rego scanner: %w", err) - } - - var results []scan.Result - if err := fs.WalkDir(target, path, func(path string, d fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - if err != nil { - return err - } - - if d.IsDir() { - return nil - } - - if detection.IsArchive(path) { - if scanResults, err := s.getScanResults(path, ctx, target); err != nil { - return err - } else { - results = append(results, scanResults...) - } - } - - if strings.HasSuffix(path, "Chart.yaml") { - if scanResults, err := s.getScanResults(filepath.Dir(path), ctx, target); err != nil { - return err - } else { - results = append(results, scanResults...) - } - return fs.SkipDir - } - - return nil - }); err != nil { - return nil, err - } - - return results, nil - -} - -func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) (results []scan.Result, err error) { - helmParser, err := parser.New(path, s.parserOptions...) - if err != nil { - return nil, err - } - - if err := helmParser.ParseFS(ctx, target, path); err != nil { - return nil, err - } - - chartFiles, err := helmParser.RenderedChartFiles() - if err != nil { // not valid helm, maybe some other yaml etc., abort - s.logger.Error( - "Failed to render Chart files", - log.FilePath(path), log.Err(err), - ) - return nil, nil - } - - for _, file := range chartFiles { - file := file - s.logger.Debug("Processing rendered chart file", log.FilePath(file.TemplateFilePath)) - - ignoreRules := ignore.Parse(file.ManifestContent, file.TemplateFilePath, "") - manifests, err := kparser.Parse(ctx, strings.NewReader(file.ManifestContent), file.TemplateFilePath) - if err != nil { - return nil, fmt.Errorf("unmarshal yaml: %w", err) - } - for _, manifest := range manifests { - fileResults, err := s.regoScanner.ScanInput(ctx, types.SourceKubernetes, rego.Input{ - Path: file.TemplateFilePath, - Contents: manifest, - FS: target, - }) - if err != nil { - return nil, fmt.Errorf("scanning error: %w", err) - } - - if len(fileResults) > 0 { - renderedFS := memoryfs.New() - if err := renderedFS.MkdirAll(filepath.Dir(file.TemplateFilePath), fs.ModePerm); err != nil { - return nil, err - } - if err := renderedFS.WriteLazyFile(file.TemplateFilePath, func() (io.Reader, error) { - return strings.NewReader(file.ManifestContent), nil - }, fs.ModePerm); err != nil { - return nil, err - } - fileResults.SetSourceAndFilesystem(helmParser.ChartSource, renderedFS, detection.IsArchive(helmParser.ChartSource)) - fileResults.Ignore(ignoreRules, nil) - } - - results = append(results, fileResults...) - } - - } - return results, nil -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) error { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return nil - } - regoScanner := rego.NewScanner(s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return err - } - s.regoScanner = regoScanner - return nil -} diff --git a/pkg/iac/scanners/helm/test/mysql/.helmignore b/pkg/iac/scanners/helm/test/mysql/.helmignore deleted file mode 100644 index f0c131944441..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/pkg/iac/scanners/helm/test/mysql/Chart.lock b/pkg/iac/scanners/helm/test/mysql/Chart.lock deleted file mode 100644 index 2a6356005c25..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: common - repository: https://charts.bitnami.com/bitnami - version: 1.11.1 -digest: sha256:a000bcd4d4cdd813c67d633b5523b4a4cd478fb95f1cae665d9b0ba5c45b40e2 -generated: "2022-02-16T22:19:57.971058445Z" diff --git a/pkg/iac/scanners/helm/test/mysql/Chart.yaml b/pkg/iac/scanners/helm/test/mysql/Chart.yaml deleted file mode 100644 index 7d5f5c6ce834..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/Chart.yaml +++ /dev/null @@ -1,28 +0,0 @@ -annotations: - category: Database -apiVersion: v2 -appVersion: 8.0.28 -dependencies: -- name: common - repository: https://charts.bitnami.com/bitnami - tags: - - bitnami-common - version: 1.x.x -description: MySQL is a fast, reliable, scalable, and easy to use open source relational - database system. Designed to handle mission-critical, heavy-load production applications. -home: https://github.com/bitnami/charts/tree/master/bitnami/mysql -icon: https://bitnami.com/assets/stacks/mysql/img/mysql-stack-220x234.png -keywords: -- mysql -- database -- sql -- cluster -- high availability -maintainers: -- email: containers@bitnami.com - name: Bitnami -name: mysql -sources: -- https://github.com/bitnami/bitnami-docker-mysql -- https://mysql.com -version: 8.8.26 diff --git a/pkg/iac/scanners/helm/test/mysql/README.md b/pkg/iac/scanners/helm/test/mysql/README.md deleted file mode 100644 index b03fa495893f..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/README.md +++ /dev/null @@ -1,491 +0,0 @@ - - -# MySQL packaged by Bitnami - -MySQL is a fast, reliable, scalable, and easy to use open source relational database system. Designed to handle mission-critical, heavy-load production applications. - -[Overview of MySQL](http://www.mysql.com) - -Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. - -## TL;DR - -```bash -$ helm repo add bitnami https://charts.bitnami.com/bitnami -$ helm install my-release bitnami/mysql -``` - -## Introduction - -This chart bootstraps a [MySQL](https://github.com/bitnami/bitnami-docker-mysql) replication cluster deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. - -Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This Helm chart has been tested on top of [Bitnami Kubernetes Production Runtime](https://kubeprod.io/) (BKPR). Deploy BKPR to get automated TLS certificates, logging and monitoring for your applications. - -## Prerequisites - -- Kubernetes 1.19+ -- Helm 3.2.0+ -- PV provisioner support in the underlying infrastructure - -## Installing the Chart - -To install the chart with the release name `my-release`: - -```bash -$ helm repo add bitnami https://charts.bitnami.com/bitnami -$ helm install my-release bitnami/mysql -``` - -These commands deploy MySQL on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. - -> **Tip**: List all releases using `helm list` - -## Uninstalling the Chart - -To uninstall/delete the `my-release` deployment: - -```bash -$ helm delete my-release -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Parameters - -### Global parameters - -| Name | Description | Value | -| ------------------------- | ----------------------------------------------- | ----- | -| `global.imageRegistry` | Global Docker image registry | `""` | -| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | -| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | - - -### Common parameters - -| Name | Description | Value | -| ------------------------ | --------------------------------------------------------------------------------------------------------- | --------------- | -| `nameOverride` | String to partially override common.names.fullname template (will maintain the release name) | `""` | -| `fullnameOverride` | String to fully override common.names.fullname template | `""` | -| `clusterDomain` | Cluster domain | `cluster.local` | -| `commonAnnotations` | Common annotations to add to all MySQL resources (sub-charts are not considered). Evaluated as a template | `{}` | -| `commonLabels` | Common labels to add to all MySQL resources (sub-charts are not considered). Evaluated as a template | `{}` | -| `extraDeploy` | Array with extra yaml to deploy with the chart. Evaluated as a template | `[]` | -| `schedulerName` | Use an alternate scheduler, e.g. "stork". | `""` | -| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | -| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | -| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | - - -### MySQL common parameters - -| Name | Description | Value | -| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `image.registry` | MySQL image registry | `docker.io` | -| `image.repository` | MySQL image repository | `bitnami/mysql` | -| `image.tag` | MySQL image tag (immutable tags are recommended) | `8.0.28-debian-10-r0` | -| `image.pullPolicy` | MySQL image pull policy | `IfNotPresent` | -| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | -| `image.debug` | Specify if debug logs should be enabled | `false` | -| `architecture` | MySQL architecture (`standalone` or `replication`) | `standalone` | -| `auth.rootPassword` | Password for the `root` user. Ignored if existing secret is provided | `""` | -| `auth.database` | Name for a custom database to create | `my_database` | -| `auth.username` | Name for a custom user to create | `""` | -| `auth.password` | Password for the new user. Ignored if existing secret is provided | `""` | -| `auth.replicationUser` | MySQL replication user | `replicator` | -| `auth.replicationPassword` | MySQL replication user password. Ignored if existing secret is provided | `""` | -| `auth.existingSecret` | Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` | `""` | -| `auth.forcePassword` | Force users to specify required passwords | `false` | -| `auth.usePasswordFiles` | Mount credentials as files instead of using an environment variable | `false` | -| `auth.customPasswordFiles` | Use custom password files when `auth.usePasswordFiles` is set to `true`. Define path for keys `root` and `user`, also define `replicator` if `architecture` is set to `replication` | `{}` | -| `initdbScripts` | Dictionary of initdb scripts | `{}` | -| `initdbScriptsConfigMap` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) | `""` | - - -### MySQL Primary parameters - -| Name | Description | Value | -| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------- | -| `primary.command` | Override default container command on MySQL Primary container(s) (useful when using custom images) | `[]` | -| `primary.args` | Override default container args on MySQL Primary container(s) (useful when using custom images) | `[]` | -| `primary.hostAliases` | Deployment pod host aliases | `[]` | -| `primary.configuration` | Configure MySQL Primary with a custom my.cnf file | `""` | -| `primary.existingConfigmap` | Name of existing ConfigMap with MySQL Primary configuration. | `""` | -| `primary.updateStrategy` | Update strategy type for the MySQL primary statefulset | `RollingUpdate` | -| `primary.rollingUpdatePartition` | Partition update strategy for MySQL Primary statefulset | `""` | -| `primary.podAnnotations` | Additional pod annotations for MySQL primary pods | `{}` | -| `primary.podAffinityPreset` | MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `primary.podAntiAffinityPreset` | MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `primary.nodeAffinityPreset.type` | MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `primary.nodeAffinityPreset.key` | MySQL primary node label key to match Ignored if `primary.affinity` is set. | `""` | -| `primary.nodeAffinityPreset.values` | MySQL primary node label values to match. Ignored if `primary.affinity` is set. | `[]` | -| `primary.affinity` | Affinity for MySQL primary pods assignment | `{}` | -| `primary.nodeSelector` | Node labels for MySQL primary pods assignment | `{}` | -| `primary.tolerations` | Tolerations for MySQL primary pods assignment | `[]` | -| `primary.podSecurityContext.enabled` | Enable security context for MySQL primary pods | `true` | -| `primary.podSecurityContext.fsGroup` | Group ID for the mounted volumes' filesystem | `1001` | -| `primary.containerSecurityContext.enabled` | MySQL primary container securityContext | `true` | -| `primary.containerSecurityContext.runAsUser` | User ID for the MySQL primary container | `1001` | -| `primary.resources.limits` | The resources limits for MySQL primary containers | `{}` | -| `primary.resources.requests` | The requested resources for MySQL primary containers | `{}` | -| `primary.livenessProbe.enabled` | Enable livenessProbe | `true` | -| `primary.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | -| `primary.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | -| `primary.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | -| `primary.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | -| `primary.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | -| `primary.readinessProbe.enabled` | Enable readinessProbe | `true` | -| `primary.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | -| `primary.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | -| `primary.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | -| `primary.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | -| `primary.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | -| `primary.startupProbe.enabled` | Enable startupProbe | `true` | -| `primary.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `15` | -| `primary.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | -| `primary.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | -| `primary.startupProbe.failureThreshold` | Failure threshold for startupProbe | `10` | -| `primary.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | -| `primary.customLivenessProbe` | Override default liveness probe for MySQL primary containers | `{}` | -| `primary.customReadinessProbe` | Override default readiness probe for MySQL primary containers | `{}` | -| `primary.customStartupProbe` | Override default startup probe for MySQL primary containers | `{}` | -| `primary.extraFlags` | MySQL primary additional command line flags | `""` | -| `primary.extraEnvVars` | Extra environment variables to be set on MySQL primary containers | `[]` | -| `primary.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for MySQL primary containers | `""` | -| `primary.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for MySQL primary containers | `""` | -| `primary.persistence.enabled` | Enable persistence on MySQL primary replicas using a `PersistentVolumeClaim`. If false, use emptyDir | `true` | -| `primary.persistence.existingClaim` | Name of an existing `PersistentVolumeClaim` for MySQL primary replicas | `""` | -| `primary.persistence.storageClass` | MySQL primary persistent volume storage Class | `""` | -| `primary.persistence.annotations` | MySQL primary persistent volume claim annotations | `{}` | -| `primary.persistence.accessModes` | MySQL primary persistent volume access Modes | `["ReadWriteOnce"]` | -| `primary.persistence.size` | MySQL primary persistent volume size | `8Gi` | -| `primary.persistence.selector` | Selector to match an existing Persistent Volume | `{}` | -| `primary.extraVolumes` | Optionally specify extra list of additional volumes to the MySQL Primary pod(s) | `[]` | -| `primary.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the MySQL Primary container(s) | `[]` | -| `primary.initContainers` | Add additional init containers for the MySQL Primary pod(s) | `[]` | -| `primary.sidecars` | Add additional sidecar containers for the MySQL Primary pod(s) | `[]` | -| `primary.service.type` | MySQL Primary K8s service type | `ClusterIP` | -| `primary.service.port` | MySQL Primary K8s service port | `3306` | -| `primary.service.nodePort` | MySQL Primary K8s service node port | `""` | -| `primary.service.clusterIP` | MySQL Primary K8s service clusterIP IP | `""` | -| `primary.service.loadBalancerIP` | MySQL Primary loadBalancerIP if service type is `LoadBalancer` | `""` | -| `primary.service.externalTrafficPolicy` | Enable client source IP preservation | `Cluster` | -| `primary.service.loadBalancerSourceRanges` | Addresses that are allowed when MySQL Primary service is LoadBalancer | `[]` | -| `primary.service.annotations` | Provide any additional annotations which may be required | `{}` | -| `primary.pdb.enabled` | Enable/disable a Pod Disruption Budget creation for MySQL primary pods | `false` | -| `primary.pdb.minAvailable` | Minimum number/percentage of MySQL primary pods that should remain scheduled | `1` | -| `primary.pdb.maxUnavailable` | Maximum number/percentage of MySQL primary pods that may be made unavailable | `""` | -| `primary.podLabels` | MySQL Primary pod label. If labels are same as commonLabels , this will take precedence | `{}` | - - -### MySQL Secondary parameters - -| Name | Description | Value | -| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------- | -| `secondary.replicaCount` | Number of MySQL secondary replicas | `1` | -| `secondary.hostAliases` | Deployment pod host aliases | `[]` | -| `secondary.command` | Override default container command on MySQL Secondary container(s) (useful when using custom images) | `[]` | -| `secondary.args` | Override default container args on MySQL Secondary container(s) (useful when using custom images) | `[]` | -| `secondary.configuration` | Configure MySQL Secondary with a custom my.cnf file | `""` | -| `secondary.existingConfigmap` | Name of existing ConfigMap with MySQL Secondary configuration. | `""` | -| `secondary.updateStrategy` | Update strategy type for the MySQL secondary statefulset | `RollingUpdate` | -| `secondary.rollingUpdatePartition` | Partition update strategy for MySQL Secondary statefulset | `""` | -| `secondary.podAnnotations` | Additional pod annotations for MySQL secondary pods | `{}` | -| `secondary.podAffinityPreset` | MySQL secondary pod affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `secondary.podAntiAffinityPreset` | MySQL secondary pod anti-affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `secondary.nodeAffinityPreset.type` | MySQL secondary node affinity preset type. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `secondary.nodeAffinityPreset.key` | MySQL secondary node label key to match Ignored if `secondary.affinity` is set. | `""` | -| `secondary.nodeAffinityPreset.values` | MySQL secondary node label values to match. Ignored if `secondary.affinity` is set. | `[]` | -| `secondary.affinity` | Affinity for MySQL secondary pods assignment | `{}` | -| `secondary.nodeSelector` | Node labels for MySQL secondary pods assignment | `{}` | -| `secondary.tolerations` | Tolerations for MySQL secondary pods assignment | `[]` | -| `secondary.podSecurityContext.enabled` | Enable security context for MySQL secondary pods | `true` | -| `secondary.podSecurityContext.fsGroup` | Group ID for the mounted volumes' filesystem | `1001` | -| `secondary.containerSecurityContext.enabled` | MySQL secondary container securityContext | `true` | -| `secondary.containerSecurityContext.runAsUser` | User ID for the MySQL secondary container | `1001` | -| `secondary.resources.limits` | The resources limits for MySQL secondary containers | `{}` | -| `secondary.resources.requests` | The requested resources for MySQL secondary containers | `{}` | -| `secondary.livenessProbe.enabled` | Enable livenessProbe | `true` | -| `secondary.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | -| `secondary.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | -| `secondary.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | -| `secondary.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | -| `secondary.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | -| `secondary.readinessProbe.enabled` | Enable readinessProbe | `true` | -| `secondary.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | -| `secondary.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | -| `secondary.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | -| `secondary.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | -| `secondary.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | -| `secondary.startupProbe.enabled` | Enable startupProbe | `true` | -| `secondary.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `15` | -| `secondary.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | -| `secondary.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | -| `secondary.startupProbe.failureThreshold` | Failure threshold for startupProbe | `15` | -| `secondary.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | -| `secondary.customLivenessProbe` | Override default liveness probe for MySQL secondary containers | `{}` | -| `secondary.customReadinessProbe` | Override default readiness probe for MySQL secondary containers | `{}` | -| `secondary.customStartupProbe` | Override default startup probe for MySQL secondary containers | `{}` | -| `secondary.extraFlags` | MySQL secondary additional command line flags | `""` | -| `secondary.extraEnvVars` | An array to add extra environment variables on MySQL secondary containers | `[]` | -| `secondary.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for MySQL secondary containers | `""` | -| `secondary.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for MySQL secondary containers | `""` | -| `secondary.persistence.enabled` | Enable persistence on MySQL secondary replicas using a `PersistentVolumeClaim` | `true` | -| `secondary.persistence.storageClass` | MySQL secondary persistent volume storage Class | `""` | -| `secondary.persistence.annotations` | MySQL secondary persistent volume claim annotations | `{}` | -| `secondary.persistence.accessModes` | MySQL secondary persistent volume access Modes | `["ReadWriteOnce"]` | -| `secondary.persistence.size` | MySQL secondary persistent volume size | `8Gi` | -| `secondary.persistence.selector` | Selector to match an existing Persistent Volume | `{}` | -| `secondary.extraVolumes` | Optionally specify extra list of additional volumes to the MySQL secondary pod(s) | `[]` | -| `secondary.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the MySQL secondary container(s) | `[]` | -| `secondary.initContainers` | Add additional init containers for the MySQL secondary pod(s) | `[]` | -| `secondary.sidecars` | Add additional sidecar containers for the MySQL secondary pod(s) | `[]` | -| `secondary.service.type` | MySQL secondary Kubernetes service type | `ClusterIP` | -| `secondary.service.port` | MySQL secondary Kubernetes service port | `3306` | -| `secondary.service.nodePort` | MySQL secondary Kubernetes service node port | `""` | -| `secondary.service.clusterIP` | MySQL secondary Kubernetes service clusterIP IP | `""` | -| `secondary.service.loadBalancerIP` | MySQL secondary loadBalancerIP if service type is `LoadBalancer` | `""` | -| `secondary.service.externalTrafficPolicy` | Enable client source IP preservation | `Cluster` | -| `secondary.service.loadBalancerSourceRanges` | Addresses that are allowed when MySQL secondary service is LoadBalancer | `[]` | -| `secondary.service.annotations` | Provide any additional annotations which may be required | `{}` | -| `secondary.pdb.enabled` | Enable/disable a Pod Disruption Budget creation for MySQL secondary pods | `false` | -| `secondary.pdb.minAvailable` | Minimum number/percentage of MySQL secondary pods that should remain scheduled | `1` | -| `secondary.pdb.maxUnavailable` | Maximum number/percentage of MySQL secondary pods that may be made unavailable | `""` | -| `secondary.podLabels` | Additional pod labels for MySQL secondary pods | `{}` | - - -### RBAC parameters - -| Name | Description | Value | -| ---------------------------- | ------------------------------------------------------ | ------- | -| `serviceAccount.create` | Enable the creation of a ServiceAccount for MySQL pods | `true` | -| `serviceAccount.name` | Name of the created ServiceAccount | `""` | -| `serviceAccount.annotations` | Annotations for MySQL Service Account | `{}` | -| `rbac.create` | Whether to create & use RBAC resources or not | `false` | - - -### Network Policy - -| Name | Description | Value | -| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------- | -| `networkPolicy.enabled` | Enable creation of NetworkPolicy resources | `false` | -| `networkPolicy.allowExternal` | The Policy model to apply. | `true` | -| `networkPolicy.explicitNamespacesSelector` | A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed to MySQL | `{}` | - - -### Volume Permissions parameters - -| Name | Description | Value | -| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------- | -| `volumePermissions.enabled` | Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` | `false` | -| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | -| `volumePermissions.image.repository` | Init container volume-permissions image repository | `bitnami/bitnami-shell` | -| `volumePermissions.image.tag` | Init container volume-permissions image tag (immutable tags are recommended) | `10-debian-10-r312` | -| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | -| `volumePermissions.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | -| `volumePermissions.resources` | Init container volume-permissions resources | `{}` | - - -### Metrics parameters - -| Name | Description | Value | -| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------- | -| `metrics.enabled` | Start a side-car prometheus exporter | `false` | -| `metrics.image.registry` | Exporter image registry | `docker.io` | -| `metrics.image.repository` | Exporter image repository | `bitnami/mysqld-exporter` | -| `metrics.image.tag` | Exporter image tag (immutable tags are recommended) | `0.13.0-debian-10-r216` | -| `metrics.image.pullPolicy` | Exporter image pull policy | `IfNotPresent` | -| `metrics.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | -| `metrics.service.type` | Kubernetes service type for MySQL Prometheus Exporter | `ClusterIP` | -| `metrics.service.port` | MySQL Prometheus Exporter service port | `9104` | -| `metrics.service.annotations` | Prometheus exporter service annotations | `{}` | -| `metrics.extraArgs.primary` | Extra args to be passed to mysqld_exporter on Primary pods | `[]` | -| `metrics.extraArgs.secondary` | Extra args to be passed to mysqld_exporter on Secondary pods | `[]` | -| `metrics.resources.limits` | The resources limits for MySQL prometheus exporter containers | `{}` | -| `metrics.resources.requests` | The requested resources for MySQL prometheus exporter containers | `{}` | -| `metrics.livenessProbe.enabled` | Enable livenessProbe | `true` | -| `metrics.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `120` | -| `metrics.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | -| `metrics.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | -| `metrics.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | -| `metrics.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | -| `metrics.readinessProbe.enabled` | Enable readinessProbe | `true` | -| `metrics.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `30` | -| `metrics.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | -| `metrics.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | -| `metrics.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | -| `metrics.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | -| `metrics.serviceMonitor.enabled` | Create ServiceMonitor Resource for scraping metrics using PrometheusOperator | `false` | -| `metrics.serviceMonitor.namespace` | Specify the namespace in which the serviceMonitor resource will be created | `""` | -| `metrics.serviceMonitor.interval` | Specify the interval at which metrics should be scraped | `30s` | -| `metrics.serviceMonitor.scrapeTimeout` | Specify the timeout after which the scrape is ended | `""` | -| `metrics.serviceMonitor.relabellings` | Specify Metric Relabellings to add to the scrape endpoint | `[]` | -| `metrics.serviceMonitor.honorLabels` | Specify honorLabels parameter to add the scrape endpoint | `false` | -| `metrics.serviceMonitor.additionalLabels` | Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with | `{}` | - - -The above parameters map to the env variables defined in [bitnami/mysql](https://github.com/bitnami/bitnami-docker-mysql). For more information please refer to the [bitnami/mysql](https://github.com/bitnami/bitnami-docker-mysql) image documentation. - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```bash -$ helm install my-release \ - --set auth.rootPassword=secretpassword,auth.database=app_database \ - bitnami/mysql -``` - -The above command sets the MySQL `root` account password to `secretpassword`. Additionally it creates a database named `app_database`. - -> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. - -Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, - -```bash -$ helm install my-release -f values.yaml bitnami/mysql -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - -## Configuration and installation details - -### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) - -It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. - -Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. - -### Use a different MySQL version - -To modify the application version used in this chart, specify a different version of the image using the `image.tag` parameter and/or a different repository using the `image.repository` parameter. Refer to the [chart documentation for more information on these parameters and how to use them with images from a private registry](https://docs.bitnami.com/kubernetes/infrastructure/mysql/configuration/change-image-version/). - -### Customize a new MySQL instance - -The [Bitnami MySQL](https://github.com/bitnami/bitnami-docker-mysql) image allows you to use your custom scripts to initialize a fresh instance. Custom scripts may be specified using the `initdbScripts` parameter. Alternatively, an external ConfigMap may be created with all the initialization scripts and the ConfigMap passed to the chart via the `initdbScriptsConfigMap` parameter. Note that this will override the `initdbScripts` parameter. - -The allowed extensions are `.sh`, `.sql` and `.sql.gz`. - -These scripts are treated differently depending on their extension. While `.sh` scripts are executed on all the nodes, `.sql` and `.sql.gz` scripts are only executed on the primary nodes. This is because `.sh` scripts support conditional tests to identify the type of node they are running on, while such tests are not supported in `.sql` or `sql.gz` files. - -Refer to the [chart documentation for more information and a usage example](http://docs.bitnami.com/kubernetes/infrastructure/mysql/configuration/customize-new-instance/). - -### Sidecars and Init Containers - -If you have a need for additional containers to run within the same pod as MySQL, you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. - -```yaml -sidecars: - - name: your-image-name - image: your-image - imagePullPolicy: Always - ports: - - name: portname - containerPort: 1234 -``` - -Similarly, you can add extra init containers using the `initContainers` parameter. - -```yaml -initContainers: - - name: your-image-name - image: your-image - imagePullPolicy: Always - ports: - - name: portname - containerPort: 1234 -``` - -## Persistence - -The [Bitnami MySQL](https://github.com/bitnami/bitnami-docker-mysql) image stores the MySQL data and configurations at the `/bitnami/mysql` path of the container. - -The chart mounts a [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) volume at this location. The volume is created using dynamic volume provisioning by default. An existing PersistentVolumeClaim can also be defined for this purpose. - -If you encounter errors when working with persistent volumes, refer to our [troubleshooting guide for persistent volumes](https://docs.bitnami.com/kubernetes/faq/troubleshooting/troubleshooting-persistence-volumes/). - -## Network Policy - -To enable network policy for MySQL, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), and set `networkPolicy.enabled` to `true`. - -For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: - -```console -$ kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" -``` - -With NetworkPolicy enabled, traffic will be limited to just port 3306. - -For more precise policy, set `networkPolicy.allowExternal=false`. This will only allow pods with the generated client label to connect to MySQL. -This label will be displayed in the output of a successful install. - -## Pod affinity - -This chart allows you to set your custom affinity using the `XXX.affinity` parameter(s). Find more information about Pod affinity in the [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). - -As an alternative, you can use the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/master/bitnami/common#affinities) chart. To do so, set the `XXX.podAffinityPreset`, `XXX.podAntiAffinityPreset`, or `XXX.nodeAffinityPreset` parameters. - -## Troubleshooting - -Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). - -## Upgrading - -It's necessary to set the `auth.rootPassword` parameter when upgrading for readiness/liveness probes to work properly. When you install this chart for the first time, some notes will be displayed providing the credentials you must use under the 'Administrator credentials' section. Please note down the password and run the command below to upgrade your chart: - -```bash -$ helm upgrade my-release bitnami/mysql --set auth.rootPassword=[ROOT_PASSWORD] -``` - -| Note: you need to substitute the placeholder _[ROOT_PASSWORD]_ with the value obtained in the installation notes. - -### To 8.0.0 - -- Several parameters were renamed or disappeared in favor of new ones on this major version: - - The terms *master* and *slave* have been replaced by the terms *primary* and *secondary*. Therefore, parameters prefixed with `master` or `slave` are now prefixed with `primary` or `secondary`, respectively. - - Credentials parameters are reorganized under the `auth` parameter. - - `replication.enabled` parameter is deprecated in favor of `architecture` parameter that accepts two values: `standalone` and `replication`. -- Chart labels were adapted to follow the [Helm charts standard labels](https://helm.sh/docs/chart_best_practices/labels/#standard-labels). -- This version also introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/master/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. - -Consequences: - -- Backwards compatibility is not guaranteed. To upgrade to `8.0.0`, install a new release of the MySQL chart, and migrate the data from your previous release. You have 2 alternatives to do so: - - Create a backup of the database, and restore it on the new release using tools such as [mysqldump](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html). - - Reuse the PVC used to hold the master data on your previous release. To do so, use the `primary.persistence.existingClaim` parameter. The following example assumes that the release name is `mysql`: - -```bash -$ helm install mysql bitnami/mysql --set auth.rootPassword=[ROOT_PASSWORD] --set primary.persistence.existingClaim=[EXISTING_PVC] -``` - -| Note: you need to substitute the placeholder _[EXISTING_PVC]_ with the name of the PVC used on your previous release, and _[ROOT_PASSWORD]_ with the root password used in your previous release. - -### To 7.0.0 - -[On November 13, 2020, Helm v2 support formally ended](https://github.com/helm/charts#status-of-the-project). This major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. - -[Learn more about this change and related upgrade considerations](https://docs.bitnami.com/kubernetes/infrastructure/mysql/administration/upgrade-helm3/). - -### To 3.0.0 - -Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments. -Use the workaround below to upgrade from versions previous to 3.0.0. The following example assumes that the release name is mysql: - -```console -$ kubectl delete statefulset mysql-master --cascade=false -$ kubectl delete statefulset mysql-slave --cascade=false -``` - -## License - -Copyright © 2022 Bitnami - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/.helmignore b/pkg/iac/scanners/helm/test/mysql/charts/common/.helmignore deleted file mode 100644 index 50af03172541..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/Chart.yaml b/pkg/iac/scanners/helm/test/mysql/charts/common/Chart.yaml deleted file mode 100644 index 87226649a57c..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/Chart.yaml +++ /dev/null @@ -1,23 +0,0 @@ -annotations: - category: Infrastructure -apiVersion: v2 -appVersion: 1.11.1 -description: A Library Helm Chart for grouping common logic between bitnami charts. - This chart is not deployable by itself. -home: https://github.com/bitnami/charts/tree/master/bitnami/common -icon: https://bitnami.com/downloads/logos/bitnami-mark.png -keywords: -- common -- helper -- template -- function -- bitnami -maintainers: -- email: containers@bitnami.com - name: Bitnami -name: common -sources: -- https://github.com/bitnami/charts -- https://www.bitnami.com/ -type: library -version: 1.11.1 diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/README.md b/pkg/iac/scanners/helm/test/mysql/charts/common/README.md deleted file mode 100644 index da84c426d0db..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/README.md +++ /dev/null @@ -1,345 +0,0 @@ -# Bitnami Common Library Chart - -A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts. - -## TL;DR - -```yaml -dependencies: - - name: common - version: 0.x.x - repository: https://charts.bitnami.com/bitnami -``` - -```bash -$ helm dependency update -``` - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "common.names.fullname" . }} -data: - myvalue: "Hello World" -``` - -## Introduction - -This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. - -Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This Helm chart has been tested on top of [Bitnami Kubernetes Production Runtime](https://kubeprod.io/) (BKPR). Deploy BKPR to get automated TLS certificates, logging and monitoring for your applications. - -## Prerequisites - -- Kubernetes 1.19+ -- Helm 3.2.0+ - -## Parameters - -The following table lists the helpers available in the library which are scoped in different sections. - -### Affinities - -| Helper identifier | Description | Expected Input | -|-------------------------------|------------------------------------------------------|------------------------------------------------| -| `common.affinities.node.soft` | Return a soft nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | -| `common.affinities.node.hard` | Return a hard nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | -| `common.affinities.pod.soft` | Return a soft podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | -| `common.affinities.pod.hard` | Return a hard podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | - -### Capabilities - -| Helper identifier | Description | Expected Input | -|------------------------------------------------|------------------------------------------------------------------------------------------------|-------------------| -| `common.capabilities.kubeVersion` | Return the target Kubernetes version (using client default if .Values.kubeVersion is not set). | `.` Chart context | -| `common.capabilities.cronjob.apiVersion` | Return the appropriate apiVersion for cronjob. | `.` Chart context | -| `common.capabilities.deployment.apiVersion` | Return the appropriate apiVersion for deployment. | `.` Chart context | -| `common.capabilities.statefulset.apiVersion` | Return the appropriate apiVersion for statefulset. | `.` Chart context | -| `common.capabilities.ingress.apiVersion` | Return the appropriate apiVersion for ingress. | `.` Chart context | -| `common.capabilities.rbac.apiVersion` | Return the appropriate apiVersion for RBAC resources. | `.` Chart context | -| `common.capabilities.crd.apiVersion` | Return the appropriate apiVersion for CRDs. | `.` Chart context | -| `common.capabilities.policy.apiVersion` | Return the appropriate apiVersion for podsecuritypolicy. | `.` Chart context | -| `common.capabilities.networkPolicy.apiVersion` | Return the appropriate apiVersion for networkpolicy. | `.` Chart context | -| `common.capabilities.supportsHelmVersion` | Returns true if the used Helm version is 3.3+ | `.` Chart context | - -### Errors - -| Helper identifier | Description | Expected Input | -|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| -| `common.errors.upgrade.passwords.empty` | It will ensure required passwords are given when we are upgrading a chart. If `validationErrors` is not empty it will throw an error and will stop the upgrade action. | `dict "validationErrors" (list $validationError00 $validationError01) "context" $` | - -### Images - -| Helper identifier | Description | Expected Input | -|-----------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| -| `common.images.image` | Return the proper and full image name | `dict "imageRoot" .Values.path.to.the.image "global" $`, see [ImageRoot](#imageroot) for the structure. | -| `common.images.pullSecrets` | Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global` | -| `common.images.renderPullSecrets` | Return the proper Docker Image Registry Secret Names (evaluates values as templates) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $` | - -### Ingress - -| Helper identifier | Description | Expected Input | -|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `common.ingress.backend` | Generate a proper Ingress backend entry depending on the API version | `dict "serviceName" "foo" "servicePort" "bar"`, see the [Ingress deprecation notice](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/) for the syntax differences | -| `common.ingress.supportsPathType` | Prints "true" if the pathType field is supported | `.` Chart context | -| `common.ingress.supportsIngressClassname` | Prints "true" if the ingressClassname field is supported | `.` Chart context | -| `common.ingress.certManagerRequest` | Prints "true" if required cert-manager annotations for TLS signed certificates are set in the Ingress annotations | `dict "annotations" .Values.path.to.the.ingress.annotations` | - -### Labels - -| Helper identifier | Description | Expected Input | -|-----------------------------|------------------------------------------------------|-------------------| -| `common.labels.standard` | Return Kubernetes standard labels | `.` Chart context | -| `common.labels.matchLabels` | Return the proper Docker Image Registry Secret Names | `.` Chart context | - -### Names - -| Helper identifier | Description | Expected Input | -|-------------------------|------------------------------------------------------------|-------------------| -| `common.names.name` | Expand the name of the chart or use `.Values.nameOverride` | `.` Chart context | -| `common.names.fullname` | Create a default fully qualified app name. | `.` Chart context | -| `common.names.chart` | Chart name plus version | `.` Chart context | - -### Secrets - -| Helper identifier | Description | Expected Input | -|---------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `common.secrets.name` | Generate the name of the secret. | `dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $` see [ExistingSecret](#existingsecret) for the structure. | -| `common.secrets.key` | Generate secret key. | `dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName"` see [ExistingSecret](#existingsecret) for the structure. | -| `common.passwords.manage` | Generate secret password or retrieve one if already created. | `dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $`, length, strong and chartNAme fields are optional. | -| `common.secrets.exists` | Returns whether a previous generated secret already exists. | `dict "secret" "secret-name" "context" $` | - -### Storage - -| Helper identifier | Description | Expected Input | -|-------------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------| -| `common.storage.class` | Return the proper Storage Class | `dict "persistence" .Values.path.to.the.persistence "global" $`, see [Persistence](#persistence) for the structure. | - -### TplValues - -| Helper identifier | Description | Expected Input | -|---------------------------|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `common.tplvalues.render` | Renders a value that contains template | `dict "value" .Values.path.to.the.Value "context" $`, value is the value should rendered as template, context frequently is the chart context `$` or `.` | - -### Utils - -| Helper identifier | Description | Expected Input | -|--------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------| -| `common.utils.fieldToEnvVar` | Build environment variable name given a field. | `dict "field" "my-password"` | -| `common.utils.secret.getvalue` | Print instructions to get a secret value. | `dict "secret" "secret-name" "field" "secret-value-field" "context" $` | -| `common.utils.getValueFromKey` | Gets a value from `.Values` object given its key path | `dict "key" "path.to.key" "context" $` | -| `common.utils.getKeyFromList` | Returns first `.Values` key with a defined value or first of the list if all non-defined | `dict "keys" (list "path.to.key1" "path.to.key2") "context" $` | - -### Validations - -| Helper identifier | Description | Expected Input | -|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `common.validations.values.single.empty` | Validate a value must not be empty. | `dict "valueKey" "path.to.value" "secret" "secret.name" "field" "my-password" "subchart" "subchart" "context" $` secret, field and subchart are optional. In case they are given, the helper will generate a how to get instruction. See [ValidateValue](#validatevalue) | -| `common.validations.values.multiple.empty` | Validate a multiple values must not be empty. It returns a shared error for all the values. | `dict "required" (list $validateValueConf00 $validateValueConf01) "context" $`. See [ValidateValue](#validatevalue) | -| `common.validations.values.mariadb.passwords` | This helper will ensure required password for MariaDB are not empty. It returns a shared error for all the values. | `dict "secret" "mariadb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mariadb chart and the helper. | -| `common.validations.values.postgresql.passwords` | This helper will ensure required password for PostgreSQL are not empty. It returns a shared error for all the values. | `dict "secret" "postgresql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use postgresql chart and the helper. | -| `common.validations.values.redis.passwords` | This helper will ensure required password for Redis™ are not empty. It returns a shared error for all the values. | `dict "secret" "redis-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use redis chart and the helper. | -| `common.validations.values.cassandra.passwords` | This helper will ensure required password for Cassandra are not empty. It returns a shared error for all the values. | `dict "secret" "cassandra-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use cassandra chart and the helper. | -| `common.validations.values.mongodb.passwords` | This helper will ensure required password for MongoDB® are not empty. It returns a shared error for all the values. | `dict "secret" "mongodb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mongodb chart and the helper. | - -### Warnings - -| Helper identifier | Description | Expected Input | -|------------------------------|----------------------------------|------------------------------------------------------------| -| `common.warnings.rollingTag` | Warning about using rolling tag. | `ImageRoot` see [ImageRoot](#imageroot) for the structure. | - -## Special input schemas - -### ImageRoot - -```yaml -registry: - type: string - description: Docker registry where the image is located - example: docker.io - -repository: - type: string - description: Repository and image name - example: bitnami/nginx - -tag: - type: string - description: image tag - example: 1.16.1-debian-10-r63 - -pullPolicy: - type: string - description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - -pullSecrets: - type: array - items: - type: string - description: Optionally specify an array of imagePullSecrets (evaluated as templates). - -debug: - type: boolean - description: Set to true if you would like to see extra information on logs - example: false - -## An instance would be: -# registry: docker.io -# repository: bitnami/nginx -# tag: 1.16.1-debian-10-r63 -# pullPolicy: IfNotPresent -# debug: false -``` - -### Persistence - -```yaml -enabled: - type: boolean - description: Whether enable persistence. - example: true - -storageClass: - type: string - description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. - example: "-" - -accessMode: - type: string - description: Access mode for the Persistent Volume Storage. - example: ReadWriteOnce - -size: - type: string - description: Size the Persistent Volume Storage. - example: 8Gi - -path: - type: string - description: Path to be persisted. - example: /bitnami - -## An instance would be: -# enabled: true -# storageClass: "-" -# accessMode: ReadWriteOnce -# size: 8Gi -# path: /bitnami -``` - -### ExistingSecret - -```yaml -name: - type: string - description: Name of the existing secret. - example: mySecret -keyMapping: - description: Mapping between the expected key name and the name of the key in the existing secret. - type: object - -## An instance would be: -# name: mySecret -# keyMapping: -# password: myPasswordKey -``` - -#### Example of use - -When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. - -```yaml -# templates/secret.yaml ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "common.names.fullname" . }} - labels: - app: {{ include "common.names.fullname" . }} -type: Opaque -data: - password: {{ .Values.password | b64enc | quote }} - -# templates/dpl.yaml ---- -... - env: - - name: PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} - key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} -... - -# values.yaml ---- -name: mySecret -keyMapping: - password: myPasswordKey -``` - -### ValidateValue - -#### NOTES.txt - -```console -{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} -{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} - -{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} -``` - -If we force those values to be empty we will see some alerts - -```console -$ helm install test mychart --set path.to.value00="",path.to.value01="" - 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: - - export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 --decode) - - 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: - - export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 --decode) -``` - -## Upgrading - -### To 1.0.0 - -[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. - -**What changes were introduced in this major version?** - -- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. -- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. -- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts - -**Considerations when upgrading to this version** - -- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues -- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore -- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 - -**Useful links** - -- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ -- https://helm.sh/docs/topics/v2_v3_migration/ -- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ - -## License - -Copyright © 2022 Bitnami - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_affinities.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_affinities.tpl deleted file mode 100644 index 189ea403d558..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_affinities.tpl +++ /dev/null @@ -1,102 +0,0 @@ -{{/* vim: set filetype=mustache: */}} - -{{/* -Return a soft nodeAffinity definition -{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} -*/}} -{{- define "common.affinities.nodes.soft" -}} -preferredDuringSchedulingIgnoredDuringExecution: - - preference: - matchExpressions: - - key: {{ .key }} - operator: In - values: - {{- range .values }} - - {{ . | quote }} - {{- end }} - weight: 1 -{{- end -}} - -{{/* -Return a hard nodeAffinity definition -{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} -*/}} -{{- define "common.affinities.nodes.hard" -}} -requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: {{ .key }} - operator: In - values: - {{- range .values }} - - {{ . | quote }} - {{- end }} -{{- end -}} - -{{/* -Return a nodeAffinity definition -{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} -*/}} -{{- define "common.affinities.nodes" -}} - {{- if eq .type "soft" }} - {{- include "common.affinities.nodes.soft" . -}} - {{- else if eq .type "hard" }} - {{- include "common.affinities.nodes.hard" . -}} - {{- end -}} -{{- end -}} - -{{/* -Return a soft podAffinity/podAntiAffinity definition -{{ include "common.affinities.pods.soft" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "context" $) -}} -*/}} -{{- define "common.affinities.pods.soft" -}} -{{- $component := default "" .component -}} -{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} -preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} - {{- if not (empty $component) }} - {{ printf "app.kubernetes.io/component: %s" $component }} - {{- end }} - {{- range $key, $value := $extraMatchLabels }} - {{ $key }}: {{ $value | quote }} - {{- end }} - namespaces: - - {{ .context.Release.Namespace | quote }} - topologyKey: kubernetes.io/hostname - weight: 1 -{{- end -}} - -{{/* -Return a hard podAffinity/podAntiAffinity definition -{{ include "common.affinities.pods.hard" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "context" $) -}} -*/}} -{{- define "common.affinities.pods.hard" -}} -{{- $component := default "" .component -}} -{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} -requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} - {{- if not (empty $component) }} - {{ printf "app.kubernetes.io/component: %s" $component }} - {{- end }} - {{- range $key, $value := $extraMatchLabels }} - {{ $key }}: {{ $value | quote }} - {{- end }} - namespaces: - - {{ .context.Release.Namespace | quote }} - topologyKey: kubernetes.io/hostname -{{- end -}} - -{{/* -Return a podAffinity/podAntiAffinity definition -{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} -*/}} -{{- define "common.affinities.pods" -}} - {{- if eq .type "soft" }} - {{- include "common.affinities.pods.soft" . -}} - {{- else if eq .type "hard" }} - {{- include "common.affinities.pods.hard" . -}} - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_capabilities.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_capabilities.tpl deleted file mode 100644 index b94212bbe77c..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_capabilities.tpl +++ /dev/null @@ -1,128 +0,0 @@ -{{/* vim: set filetype=mustache: */}} - -{{/* -Return the target Kubernetes version -*/}} -{{- define "common.capabilities.kubeVersion" -}} -{{- if .Values.global }} - {{- if .Values.global.kubeVersion }} - {{- .Values.global.kubeVersion -}} - {{- else }} - {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} - {{- end -}} -{{- else }} -{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for poddisruptionbudget. -*/}} -{{- define "common.capabilities.policy.apiVersion" -}} -{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "policy/v1beta1" -}} -{{- else -}} -{{- print "policy/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for networkpolicy. -*/}} -{{- define "common.capabilities.networkPolicy.apiVersion" -}} -{{- if semverCompare "<1.7-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "extensions/v1beta1" -}} -{{- else -}} -{{- print "networking.k8s.io/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for cronjob. -*/}} -{{- define "common.capabilities.cronjob.apiVersion" -}} -{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "batch/v1beta1" -}} -{{- else -}} -{{- print "batch/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for deployment. -*/}} -{{- define "common.capabilities.deployment.apiVersion" -}} -{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "extensions/v1beta1" -}} -{{- else -}} -{{- print "apps/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for statefulset. -*/}} -{{- define "common.capabilities.statefulset.apiVersion" -}} -{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "apps/v1beta1" -}} -{{- else -}} -{{- print "apps/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for ingress. -*/}} -{{- define "common.capabilities.ingress.apiVersion" -}} -{{- if .Values.ingress -}} -{{- if .Values.ingress.apiVersion -}} -{{- .Values.ingress.apiVersion -}} -{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "extensions/v1beta1" -}} -{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "networking.k8s.io/v1beta1" -}} -{{- else -}} -{{- print "networking.k8s.io/v1" -}} -{{- end }} -{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "extensions/v1beta1" -}} -{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "networking.k8s.io/v1beta1" -}} -{{- else -}} -{{- print "networking.k8s.io/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for RBAC resources. -*/}} -{{- define "common.capabilities.rbac.apiVersion" -}} -{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "rbac.authorization.k8s.io/v1beta1" -}} -{{- else -}} -{{- print "rbac.authorization.k8s.io/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for CRDs. -*/}} -{{- define "common.capabilities.crd.apiVersion" -}} -{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "apiextensions.k8s.io/v1beta1" -}} -{{- else -}} -{{- print "apiextensions.k8s.io/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Returns true if the used Helm version is 3.3+. -A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. -This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. -**To be removed when the catalog's minimun Helm version is 3.3** -*/}} -{{- define "common.capabilities.supportsHelmVersion" -}} -{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} - {{- true -}} -{{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_errors.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_errors.tpl deleted file mode 100644 index a79cc2e322e0..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_errors.tpl +++ /dev/null @@ -1,23 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Through error when upgrading using empty passwords values that must not be empty. - -Usage: -{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} -{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} -{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} - -Required password params: - - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. - - context - Context - Required. Parent context. -*/}} -{{- define "common.errors.upgrade.passwords.empty" -}} - {{- $validationErrors := join "" .validationErrors -}} - {{- if and $validationErrors .context.Release.IsUpgrade -}} - {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} - {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} - {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} - {{- $errorString = print $errorString "\n%s" -}} - {{- printf $errorString $validationErrors | fail -}} - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_images.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_images.tpl deleted file mode 100644 index 42ffbc7227eb..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_images.tpl +++ /dev/null @@ -1,75 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Return the proper image name -{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} -*/}} -{{- define "common.images.image" -}} -{{- $registryName := .imageRoot.registry -}} -{{- $repositoryName := .imageRoot.repository -}} -{{- $tag := .imageRoot.tag | toString -}} -{{- if .global }} - {{- if .global.imageRegistry }} - {{- $registryName = .global.imageRegistry -}} - {{- end -}} -{{- end -}} -{{- if $registryName }} -{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} -{{- else -}} -{{- printf "%s:%s" $repositoryName $tag -}} -{{- end -}} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) -{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} -*/}} -{{- define "common.images.pullSecrets" -}} - {{- $pullSecrets := list }} - - {{- if .global }} - {{- range .global.imagePullSecrets -}} - {{- $pullSecrets = append $pullSecrets . -}} - {{- end -}} - {{- end -}} - - {{- range .images -}} - {{- range .pullSecrets -}} - {{- $pullSecrets = append $pullSecrets . -}} - {{- end -}} - {{- end -}} - - {{- if (not (empty $pullSecrets)) }} -imagePullSecrets: - {{- range $pullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names evaluating values as templates -{{ include "common.images.renderPullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $) }} -*/}} -{{- define "common.images.renderPullSecrets" -}} - {{- $pullSecrets := list }} - {{- $context := .context }} - - {{- if $context.Values.global }} - {{- range $context.Values.global.imagePullSecrets -}} - {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} - {{- end -}} - {{- end -}} - - {{- range .images -}} - {{- range .pullSecrets -}} - {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} - {{- end -}} - {{- end -}} - - {{- if (not (empty $pullSecrets)) }} -imagePullSecrets: - {{- range $pullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_ingress.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_ingress.tpl deleted file mode 100644 index 8caf73a61082..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_ingress.tpl +++ /dev/null @@ -1,68 +0,0 @@ -{{/* vim: set filetype=mustache: */}} - -{{/* -Generate backend entry that is compatible with all Kubernetes API versions. - -Usage: -{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} - -Params: - - serviceName - String. Name of an existing service backend - - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. - - context - Dict - Required. The context for the template evaluation. -*/}} -{{- define "common.ingress.backend" -}} -{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} -{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} -serviceName: {{ .serviceName }} -servicePort: {{ .servicePort }} -{{- else -}} -service: - name: {{ .serviceName }} - port: - {{- if typeIs "string" .servicePort }} - name: {{ .servicePort }} - {{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }} - number: {{ .servicePort | int }} - {{- end }} -{{- end -}} -{{- end -}} - -{{/* -Print "true" if the API pathType field is supported -Usage: -{{ include "common.ingress.supportsPathType" . }} -*/}} -{{- define "common.ingress.supportsPathType" -}} -{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} -{{- print "false" -}} -{{- else -}} -{{- print "true" -}} -{{- end -}} -{{- end -}} - -{{/* -Returns true if the ingressClassname field is supported -Usage: -{{ include "common.ingress.supportsIngressClassname" . }} -*/}} -{{- define "common.ingress.supportsIngressClassname" -}} -{{- if semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .) -}} -{{- print "false" -}} -{{- else -}} -{{- print "true" -}} -{{- end -}} -{{- end -}} - -{{/* -Return true if cert-manager required annotations for TLS signed -certificates are set in the Ingress annotations -Ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations -Usage: -{{ include "common.ingress.certManagerRequest" ( dict "annotations" .Values.path.to.the.ingress.annotations ) }} -*/}} -{{- define "common.ingress.certManagerRequest" -}} -{{ if or (hasKey .annotations "cert-manager.io/cluster-issuer") (hasKey .annotations "cert-manager.io/issuer") }} - {{- true -}} -{{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_labels.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_labels.tpl deleted file mode 100644 index 252066c7e2b3..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_labels.tpl +++ /dev/null @@ -1,18 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Kubernetes standard labels -*/}} -{{- define "common.labels.standard" -}} -app.kubernetes.io/name: {{ include "common.names.name" . }} -helm.sh/chart: {{ include "common.names.chart" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector -*/}} -{{- define "common.labels.matchLabels" -}} -app.kubernetes.io/name: {{ include "common.names.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_names.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_names.tpl deleted file mode 100644 index cf0323171f39..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_names.tpl +++ /dev/null @@ -1,52 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "common.names.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "common.names.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "common.names.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create a default fully qualified dependency name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -Usage: -{{ include "common.names.dependency.fullname" (dict "chartName" "dependency-chart-name" "chartValues" .Values.dependency-chart "context" $) }} -*/}} -{{- define "common.names.dependency.fullname" -}} -{{- if .chartValues.fullnameOverride -}} -{{- .chartValues.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .chartName .chartValues.nameOverride -}} -{{- if contains $name .context.Release.Name -}} -{{- .context.Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .context.Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_secrets.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_secrets.tpl deleted file mode 100644 index a1afc1195996..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_secrets.tpl +++ /dev/null @@ -1,131 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Generate secret name. - -Usage: -{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} - -Params: - - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user - to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. - +info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret - - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. - - context - Dict - Required. The context for the template evaluation. -*/}} -{{- define "common.secrets.name" -}} -{{- $name := (include "common.names.fullname" .context) -}} - -{{- if .defaultNameSuffix -}} -{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- with .existingSecret -}} -{{- if not (typeIs "string" .) -}} -{{- with .name -}} -{{- $name = . -}} -{{- end -}} -{{- else -}} -{{- $name = . -}} -{{- end -}} -{{- end -}} - -{{- printf "%s" $name -}} -{{- end -}} - -{{/* -Generate secret key. - -Usage: -{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} - -Params: - - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user - to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. - +info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret - - key - String - Required. Name of the key in the secret. -*/}} -{{- define "common.secrets.key" -}} -{{- $key := .key -}} - -{{- if .existingSecret -}} - {{- if not (typeIs "string" .existingSecret) -}} - {{- if .existingSecret.keyMapping -}} - {{- $key = index .existingSecret.keyMapping $.key -}} - {{- end -}} - {{- end }} -{{- end -}} - -{{- printf "%s" $key -}} -{{- end -}} - -{{/* -Generate secret password or retrieve one if already created. - -Usage: -{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }} - -Params: - - secret - String - Required - Name of the 'Secret' resource where the password is stored. - - key - String - Required - Name of the key in the secret. - - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. - - length - int - Optional - Length of the generated random password. - - strong - Boolean - Optional - Whether to add symbols to the generated random password. - - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. - - context - Context - Required - Parent context. -*/}} -{{- define "common.secrets.passwords.manage" -}} - -{{- $password := "" }} -{{- $subchart := "" }} -{{- $chartName := default "" .chartName }} -{{- $passwordLength := default 10 .length }} -{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} -{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} -{{- $secretData := (lookup "v1" "Secret" $.context.Release.Namespace .secret).data }} -{{- if $secretData }} - {{- if hasKey $secretData .key }} - {{- $password = index $secretData .key }} - {{- else }} - {{- printf "\nPASSWORDS ERROR: The secret \"%s\" does not contain the key \"%s\"\n" .secret .key | fail -}} - {{- end -}} -{{- else if $providedPasswordValue }} - {{- $password = $providedPasswordValue | toString | b64enc | quote }} -{{- else }} - - {{- if .context.Values.enabled }} - {{- $subchart = $chartName }} - {{- end -}} - - {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} - {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} - {{- $passwordValidationErrors := list $requiredPasswordError -}} - {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} - - {{- if .strong }} - {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} - {{- $password = randAscii $passwordLength }} - {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} - {{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }} - {{- else }} - {{- $password = randAlphaNum $passwordLength | b64enc | quote }} - {{- end }} -{{- end -}} -{{- printf "%s" $password -}} -{{- end -}} - -{{/* -Returns whether a previous generated secret already exists - -Usage: -{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} - -Params: - - secret - String - Required - Name of the 'Secret' resource where the password is stored. - - context - Context - Required - Parent context. -*/}} -{{- define "common.secrets.exists" -}} -{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }} -{{- if $secret }} - {{- true -}} -{{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_storage.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_storage.tpl deleted file mode 100644 index 60e2a844f6eb..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_storage.tpl +++ /dev/null @@ -1,23 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Return the proper Storage Class -{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} -*/}} -{{- define "common.storage.class" -}} - -{{- $storageClass := .persistence.storageClass -}} -{{- if .global -}} - {{- if .global.storageClass -}} - {{- $storageClass = .global.storageClass -}} - {{- end -}} -{{- end -}} - -{{- if $storageClass -}} - {{- if (eq "-" $storageClass) -}} - {{- printf "storageClassName: \"\"" -}} - {{- else }} - {{- printf "storageClassName: %s" $storageClass -}} - {{- end -}} -{{- end -}} - -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_tplvalues.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_tplvalues.tpl deleted file mode 100644 index 2db166851bb5..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_tplvalues.tpl +++ /dev/null @@ -1,13 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Renders a value that contains template. -Usage: -{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} -*/}} -{{- define "common.tplvalues.render" -}} - {{- if typeIs "string" .value }} - {{- tpl .value .context }} - {{- else }} - {{- tpl (.value | toYaml) .context }} - {{- end }} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_utils.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_utils.tpl deleted file mode 100644 index ea083a249f80..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_utils.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Print instructions to get a secret value. -Usage: -{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} -*/}} -{{- define "common.utils.secret.getvalue" -}} -{{- $varname := include "common.utils.fieldToEnvVar" . -}} -export {{ $varname }}=$(kubectl get secret --namespace {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 --decode) -{{- end -}} - -{{/* -Build env var name given a field -Usage: -{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} -*/}} -{{- define "common.utils.fieldToEnvVar" -}} - {{- $fieldNameSplit := splitList "-" .field -}} - {{- $upperCaseFieldNameSplit := list -}} - - {{- range $fieldNameSplit -}} - {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} - {{- end -}} - - {{ join "_" $upperCaseFieldNameSplit }} -{{- end -}} - -{{/* -Gets a value from .Values given -Usage: -{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} -*/}} -{{- define "common.utils.getValueFromKey" -}} -{{- $splitKey := splitList "." .key -}} -{{- $value := "" -}} -{{- $latestObj := $.context.Values -}} -{{- range $splitKey -}} - {{- if not $latestObj -}} - {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} - {{- end -}} - {{- $value = ( index $latestObj . ) -}} - {{- $latestObj = $value -}} -{{- end -}} -{{- printf "%v" (default "" $value) -}} -{{- end -}} - -{{/* -Returns first .Values key with a defined value or first of the list if all non-defined -Usage: -{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} -*/}} -{{- define "common.utils.getKeyFromList" -}} -{{- $key := first .keys -}} -{{- $reverseKeys := reverse .keys }} -{{- range $reverseKeys }} - {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} - {{- if $value -}} - {{- $key = . }} - {{- end -}} -{{- end -}} -{{- printf "%s" $key -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_warnings.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_warnings.tpl deleted file mode 100644 index ae10fa41ee7d..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/_warnings.tpl +++ /dev/null @@ -1,14 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Warning about using rolling tag. -Usage: -{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} -*/}} -{{- define "common.warnings.rollingTag" -}} - -{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} -WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. -+info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ -{{- end }} - -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_cassandra.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_cassandra.tpl deleted file mode 100644 index ded1ae3bcad7..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_cassandra.tpl +++ /dev/null @@ -1,72 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Validate Cassandra required passwords are not empty. - -Usage: -{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} -Params: - - secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret" - - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false -*/}} -{{- define "common.validations.values.cassandra.passwords" -}} - {{- $existingSecret := include "common.cassandra.values.existingSecret" . -}} - {{- $enabled := include "common.cassandra.values.enabled" . -}} - {{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}} - {{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}} - - {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} - {{- $requiredPasswords := list -}} - - {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} - - {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} - - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for existingSecret. - -Usage: -{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} -Params: - - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false -*/}} -{{- define "common.cassandra.values.existingSecret" -}} - {{- if .subchart -}} - {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} - {{- else -}} - {{- .context.Values.dbUser.existingSecret | quote -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for enabled cassandra. - -Usage: -{{ include "common.cassandra.values.enabled" (dict "context" $) }} -*/}} -{{- define "common.cassandra.values.enabled" -}} - {{- if .subchart -}} - {{- printf "%v" .context.Values.cassandra.enabled -}} - {{- else -}} - {{- printf "%v" (not .context.Values.enabled) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for the key dbUser - -Usage: -{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false -*/}} -{{- define "common.cassandra.values.key.dbUser" -}} - {{- if .subchart -}} - cassandra.dbUser - {{- else -}} - dbUser - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_mariadb.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_mariadb.tpl deleted file mode 100644 index b6906ff77b72..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_mariadb.tpl +++ /dev/null @@ -1,103 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Validate MariaDB required passwords are not empty. - -Usage: -{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} -Params: - - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" - - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false -*/}} -{{- define "common.validations.values.mariadb.passwords" -}} - {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} - {{- $enabled := include "common.mariadb.values.enabled" . -}} - {{- $architecture := include "common.mariadb.values.architecture" . -}} - {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} - {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} - {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} - {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} - {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} - - {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} - {{- $requiredPasswords := list -}} - - {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} - - {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} - {{- if not (empty $valueUsername) -}} - {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} - {{- end -}} - - {{- if (eq $architecture "replication") -}} - {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} - {{- end -}} - - {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} - - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for existingSecret. - -Usage: -{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} -Params: - - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false -*/}} -{{- define "common.mariadb.values.auth.existingSecret" -}} - {{- if .subchart -}} - {{- .context.Values.mariadb.auth.existingSecret | quote -}} - {{- else -}} - {{- .context.Values.auth.existingSecret | quote -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for enabled mariadb. - -Usage: -{{ include "common.mariadb.values.enabled" (dict "context" $) }} -*/}} -{{- define "common.mariadb.values.enabled" -}} - {{- if .subchart -}} - {{- printf "%v" .context.Values.mariadb.enabled -}} - {{- else -}} - {{- printf "%v" (not .context.Values.enabled) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for architecture - -Usage: -{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false -*/}} -{{- define "common.mariadb.values.architecture" -}} - {{- if .subchart -}} - {{- .context.Values.mariadb.architecture -}} - {{- else -}} - {{- .context.Values.architecture -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for the key auth - -Usage: -{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false -*/}} -{{- define "common.mariadb.values.key.auth" -}} - {{- if .subchart -}} - mariadb.auth - {{- else -}} - auth - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_mongodb.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_mongodb.tpl deleted file mode 100644 index a071ea4d3127..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_mongodb.tpl +++ /dev/null @@ -1,108 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Validate MongoDB® required passwords are not empty. - -Usage: -{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} -Params: - - secret - String - Required. Name of the secret where MongoDB® values are stored, e.g: "mongodb-passwords-secret" - - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false -*/}} -{{- define "common.validations.values.mongodb.passwords" -}} - {{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}} - {{- $enabled := include "common.mongodb.values.enabled" . -}} - {{- $authPrefix := include "common.mongodb.values.key.auth" . -}} - {{- $architecture := include "common.mongodb.values.architecture" . -}} - {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} - {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} - {{- $valueKeyDatabase := printf "%s.database" $authPrefix -}} - {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} - {{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}} - {{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}} - - {{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}} - - {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") (eq $authEnabled "true") -}} - {{- $requiredPasswords := list -}} - - {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} - - {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} - {{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }} - {{- if and $valueUsername $valueDatabase -}} - {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} - {{- end -}} - - {{- if (eq $architecture "replicaset") -}} - {{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}} - {{- end -}} - - {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} - - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for existingSecret. - -Usage: -{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} -Params: - - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false -*/}} -{{- define "common.mongodb.values.auth.existingSecret" -}} - {{- if .subchart -}} - {{- .context.Values.mongodb.auth.existingSecret | quote -}} - {{- else -}} - {{- .context.Values.auth.existingSecret | quote -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for enabled mongodb. - -Usage: -{{ include "common.mongodb.values.enabled" (dict "context" $) }} -*/}} -{{- define "common.mongodb.values.enabled" -}} - {{- if .subchart -}} - {{- printf "%v" .context.Values.mongodb.enabled -}} - {{- else -}} - {{- printf "%v" (not .context.Values.enabled) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for the key auth - -Usage: -{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false -*/}} -{{- define "common.mongodb.values.key.auth" -}} - {{- if .subchart -}} - mongodb.auth - {{- else -}} - auth - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for architecture - -Usage: -{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false -*/}} -{{- define "common.mongodb.values.architecture" -}} - {{- if .subchart -}} - {{- .context.Values.mongodb.architecture -}} - {{- else -}} - {{- .context.Values.architecture -}} - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_postgresql.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_postgresql.tpl deleted file mode 100644 index 164ec0d01252..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_postgresql.tpl +++ /dev/null @@ -1,129 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Validate PostgreSQL required passwords are not empty. - -Usage: -{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} -Params: - - secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret" - - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false -*/}} -{{- define "common.validations.values.postgresql.passwords" -}} - {{- $existingSecret := include "common.postgresql.values.existingSecret" . -}} - {{- $enabled := include "common.postgresql.values.enabled" . -}} - {{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}} - {{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}} - {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} - {{- $requiredPasswords := list -}} - {{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}} - - {{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}} - {{- if (eq $enabledReplication "true") -}} - {{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}} - {{- end -}} - - {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to decide whether evaluate global values. - -Usage: -{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} -Params: - - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" -*/}} -{{- define "common.postgresql.values.use.global" -}} - {{- if .context.Values.global -}} - {{- if .context.Values.global.postgresql -}} - {{- index .context.Values.global.postgresql .key | quote -}} - {{- end -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for existingSecret. - -Usage: -{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} -*/}} -{{- define "common.postgresql.values.existingSecret" -}} - {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} - - {{- if .subchart -}} - {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} - {{- else -}} - {{- default (.context.Values.existingSecret | quote) $globalValue -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for enabled postgresql. - -Usage: -{{ include "common.postgresql.values.enabled" (dict "context" $) }} -*/}} -{{- define "common.postgresql.values.enabled" -}} - {{- if .subchart -}} - {{- printf "%v" .context.Values.postgresql.enabled -}} - {{- else -}} - {{- printf "%v" (not .context.Values.enabled) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for the key postgressPassword. - -Usage: -{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false -*/}} -{{- define "common.postgresql.values.key.postgressPassword" -}} - {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} - - {{- if not $globalValue -}} - {{- if .subchart -}} - postgresql.postgresqlPassword - {{- else -}} - postgresqlPassword - {{- end -}} - {{- else -}} - global.postgresql.postgresqlPassword - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for enabled.replication. - -Usage: -{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false -*/}} -{{- define "common.postgresql.values.enabled.replication" -}} - {{- if .subchart -}} - {{- printf "%v" .context.Values.postgresql.replication.enabled -}} - {{- else -}} - {{- printf "%v" .context.Values.replication.enabled -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for the key replication.password. - -Usage: -{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false -*/}} -{{- define "common.postgresql.values.key.replicationPassword" -}} - {{- if .subchart -}} - postgresql.replication.password - {{- else -}} - replication.password - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_redis.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_redis.tpl deleted file mode 100644 index 5d72959b9eee..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_redis.tpl +++ /dev/null @@ -1,76 +0,0 @@ - -{{/* vim: set filetype=mustache: */}} -{{/* -Validate Redis™ required passwords are not empty. - -Usage: -{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} -Params: - - secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret" - - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false -*/}} -{{- define "common.validations.values.redis.passwords" -}} - {{- $enabled := include "common.redis.values.enabled" . -}} - {{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}} - {{- $standarizedVersion := include "common.redis.values.standarized.version" . }} - - {{- $existingSecret := ternary (printf "%s%s" $valueKeyPrefix "auth.existingSecret") (printf "%s%s" $valueKeyPrefix "existingSecret") (eq $standarizedVersion "true") }} - {{- $existingSecretValue := include "common.utils.getValueFromKey" (dict "key" $existingSecret "context" .context) }} - - {{- $valueKeyRedisPassword := ternary (printf "%s%s" $valueKeyPrefix "auth.password") (printf "%s%s" $valueKeyPrefix "password") (eq $standarizedVersion "true") }} - {{- $valueKeyRedisUseAuth := ternary (printf "%s%s" $valueKeyPrefix "auth.enabled") (printf "%s%s" $valueKeyPrefix "usePassword") (eq $standarizedVersion "true") }} - - {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} - {{- $requiredPasswords := list -}} - - {{- $useAuth := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUseAuth "context" .context) -}} - {{- if eq $useAuth "true" -}} - {{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}} - {{- end -}} - - {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right value for enabled redis. - -Usage: -{{ include "common.redis.values.enabled" (dict "context" $) }} -*/}} -{{- define "common.redis.values.enabled" -}} - {{- if .subchart -}} - {{- printf "%v" .context.Values.redis.enabled -}} - {{- else -}} - {{- printf "%v" (not .context.Values.enabled) -}} - {{- end -}} -{{- end -}} - -{{/* -Auxiliary function to get the right prefix path for the values - -Usage: -{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} -Params: - - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false -*/}} -{{- define "common.redis.values.keys.prefix" -}} - {{- if .subchart -}}redis.{{- else -}}{{- end -}} -{{- end -}} - -{{/* -Checks whether the redis chart's includes the standarizations (version >= 14) - -Usage: -{{ include "common.redis.values.standarized.version" (dict "context" $) }} -*/}} -{{- define "common.redis.values.standarized.version" -}} - - {{- $standarizedAuth := printf "%s%s" (include "common.redis.values.keys.prefix" .) "auth" -}} - {{- $standarizedAuthValues := include "common.utils.getValueFromKey" (dict "key" $standarizedAuth "context" .context) }} - - {{- if $standarizedAuthValues -}} - {{- true -}} - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_validations.tpl b/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_validations.tpl deleted file mode 100644 index 9a814cf40dcb..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/templates/validations/_validations.tpl +++ /dev/null @@ -1,46 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Validate values must not be empty. - -Usage: -{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} -{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} -{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} - -Validate value params: - - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" - - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" - - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" -*/}} -{{- define "common.validations.values.multiple.empty" -}} - {{- range .required -}} - {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} - {{- end -}} -{{- end -}} - -{{/* -Validate a value must not be empty. - -Usage: -{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} - -Validate value params: - - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" - - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" - - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" - - subchart - String - Optional - Name of the subchart that the validated password is part of. -*/}} -{{- define "common.validations.values.single.empty" -}} - {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} - {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} - - {{- if not $value -}} - {{- $varname := "my-value" -}} - {{- $getCurrentValue := "" -}} - {{- if and .secret .field -}} - {{- $varname = include "common.utils.fieldToEnvVar" . -}} - {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} - {{- end -}} - {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} - {{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/charts/common/values.yaml b/pkg/iac/scanners/helm/test/mysql/charts/common/values.yaml deleted file mode 100644 index f2df68e5e6af..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/charts/common/values.yaml +++ /dev/null @@ -1,5 +0,0 @@ -## bitnami/common -## It is required by CI/CD tools and processes. -## @skip exampleValue -## -exampleValue: common-chart diff --git a/pkg/iac/scanners/helm/test/mysql/ci/values-production-with-rbac.yaml b/pkg/iac/scanners/helm/test/mysql/ci/values-production-with-rbac.yaml deleted file mode 100644 index d3370c931113..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/ci/values-production-with-rbac.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Test values file for generating all of the yaml and check that -# the rendering is correct - -architecture: replication -auth: - usePasswordFiles: true - -primary: - extraEnvVars: - - name: TEST - value: "3" - podDisruptionBudget: - create: true - -secondary: - replicaCount: 2 - extraEnvVars: - - name: TEST - value: "2" - podDisruptionBudget: - create: true - -serviceAccount: - create: true - name: mysql-service-account -rbac: - create: true - -metrics: - enabled: true diff --git a/pkg/iac/scanners/helm/test/mysql/templates/NOTES.txt b/pkg/iac/scanners/helm/test/mysql/templates/NOTES.txt deleted file mode 100644 index 1b8b6d5ea7d2..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/NOTES.txt +++ /dev/null @@ -1,102 +0,0 @@ -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -** Please be patient while the chart is being deployed ** - -{{- if .Values.diagnosticMode.enabled }} -The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: - - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} - -Get the list of pods by executing: - - kubectl get pods --namespace {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} - -Access the pod you want to debug by executing - - kubectl exec --namespace {{ .Release.Namespace }} -ti -- bash - -In order to replicate the container startup scripts execute this command: - - /opt/bitnami/scripts/mysql/entrypoint.sh /opt/bitnami/scripts/mysql/run.sh - -{{- else }} - -Tip: - - Watch the deployment status using the command: kubectl get pods -w --namespace {{ .Release.Namespace }} - -Services: - - echo Primary: {{ include "mysql.primary.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}:{{ .Values.primary.service.port }} -{{- if eq .Values.architecture "replication" }} - echo Secondary: {{ include "mysql.secondary.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}:{{ .Values.secondary.service.port }} -{{- end }} - -Execute the following to get the administrator credentials: - - echo Username: root - MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "mysql.secretName" . }} -o jsonpath="{.data.mysql-root-password}" | base64 --decode) - -To connect to your database: - - 1. Run a pod that you can use as a client: - - kubectl run {{ include "common.names.fullname" . }}-client --rm --tty -i --restart='Never' --image {{ template "mysql.image" . }} --namespace {{ .Release.Namespace }} --command -- bash - - 2. To connect to primary service (read/write): - - mysql -h {{ include "mysql.primary.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} -uroot -p"$MYSQL_ROOT_PASSWORD" - -{{- if eq .Values.architecture "replication" }} - - 3. To connect to secondary service (read-only): - - mysql -h {{ include "mysql.secondary.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} -uroot -p"$MYSQL_ROOT_PASSWORD" -{{- end }} - -{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} -Note: Since NetworkPolicy is enabled, only pods with label {{ template "common.names.fullname" . }}-client=true" will be able to connect to MySQL. -{{- end }} - -{{- if .Values.metrics.enabled }} - -To access the MySQL Prometheus metrics from outside the cluster execute the following commands: - - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ printf "%s-metrics" (include "common.names.fullname" .) }} {{ .Values.metrics.service.port }}:{{ .Values.metrics.service.port }} & - curl http://127.0.0.1:{{ .Values.metrics.service.port }}/metrics - -{{- end }} - -To upgrade this helm chart: - - 1. Obtain the password as described on the 'Administrator credentials' section and set the 'root.password' parameter as shown below: - - ROOT_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ include "common.names.fullname" . }} -o jsonpath="{.data.mysql-root-password}" | base64 --decode) - helm upgrade --namespace {{ .Release.Namespace }} {{ .Release.Name }} bitnami/mysql --set auth.rootPassword=$ROOT_PASSWORD - -{{ include "mysql.validateValues" . }} -{{ include "mysql.checkRollingTags" . }} -{{- if and (not .Values.auth.existingSecret) (not .Values.auth.customPasswordFiles) -}} - {{- $secretName := include "mysql.secretName" . -}} - {{- $requiredPasswords := list -}} - - {{- $requiredRootPassword := dict "valueKey" "auth.rootPassword" "secret" $secretName "field" "mysql-root-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} - - {{- if not (empty .Values.auth.username) -}} - {{- $requiredPassword := dict "valueKey" "auth.password" "secret" $secretName "field" "mysql-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} - {{- end -}} - - {{- if (eq .Values.architecture "replication") -}} - {{- $requiredReplicationPassword := dict "valueKey" "auth.replicationPassword" "secret" $secretName "field" "mysql-replication-password" -}} - {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} - {{- end -}} - - {{- $mysqlPasswordValidationErrors := include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" $) -}} - {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $mysqlPasswordValidationErrors "context" $) -}} -{{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/_helpers.tpl b/pkg/iac/scanners/helm/test/mysql/templates/_helpers.tpl deleted file mode 100644 index 6c2bcff81398..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/_helpers.tpl +++ /dev/null @@ -1,192 +0,0 @@ -{{/* vim: set filetype=mustache: */}} - -{{- define "mysql.primary.fullname" -}} -{{- if eq .Values.architecture "replication" }} -{{- printf "%s-%s" (include "common.names.fullname" .) "primary" | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- include "common.names.fullname" . -}} -{{- end -}} -{{- end -}} - -{{- define "mysql.secondary.fullname" -}} -{{- printf "%s-%s" (include "common.names.fullname" .) "secondary" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Return the proper MySQL image name -*/}} -{{- define "mysql.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper metrics image name -*/}} -{{- define "mysql.metrics.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.metrics.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper image name (for the init container volume-permissions image) -*/}} -{{- define "mysql.volumePermissions.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names -*/}} -{{- define "mysql.imagePullSecrets" -}} -{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.volumePermissions.image) "global" .Values.global) }} -{{- end -}} - -{{ template "mysql.initdbScriptsCM" . }} -{{/* -Get the initialization scripts ConfigMap name. -*/}} -{{- define "mysql.initdbScriptsCM" -}} -{{- if .Values.initdbScriptsConfigMap -}} - {{- printf "%s" .Values.initdbScriptsConfigMap -}} -{{- else -}} - {{- printf "%s-init-scripts" (include "mysql.primary.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* - Returns the proper service account name depending if an explicit service account name is set - in the values file. If the name is not set it will default to either mysql.fullname if serviceAccount.create - is true or default otherwise. -*/}} -{{- define "mysql.serviceAccountName" -}} - {{- if .Values.serviceAccount.create -}} - {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} - {{- else -}} - {{ default "default" .Values.serviceAccount.name }} - {{- end -}} -{{- end -}} - -{{/* -Return the configmap with the MySQL Primary configuration -*/}} -{{- define "mysql.primary.configmapName" -}} -{{- if .Values.primary.existingConfigmap -}} - {{- printf "%s" (tpl .Values.primary.existingConfigmap $) -}} -{{- else -}} - {{- printf "%s" (include "mysql.primary.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Return true if a configmap object should be created for MySQL Secondary -*/}} -{{- define "mysql.primary.createConfigmap" -}} -{{- if and .Values.primary.configuration (not .Values.primary.existingConfigmap) }} - {{- true -}} -{{- else -}} -{{- end -}} -{{- end -}} - -{{/* -Return the configmap with the MySQL Primary configuration -*/}} -{{- define "mysql.secondary.configmapName" -}} -{{- if .Values.secondary.existingConfigmap -}} - {{- printf "%s" (tpl .Values.secondary.existingConfigmap $) -}} -{{- else -}} - {{- printf "%s" (include "mysql.secondary.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Return true if a configmap object should be created for MySQL Secondary -*/}} -{{- define "mysql.secondary.createConfigmap" -}} -{{- if and (eq .Values.architecture "replication") .Values.secondary.configuration (not .Values.secondary.existingConfigmap) }} - {{- true -}} -{{- else -}} -{{- end -}} -{{- end -}} - -{{/* -Return the secret with MySQL credentials -*/}} -{{- define "mysql.secretName" -}} - {{- if .Values.auth.existingSecret -}} - {{- printf "%s" .Values.auth.existingSecret -}} - {{- else -}} - {{- printf "%s" (include "common.names.fullname" .) -}} - {{- end -}} -{{- end -}} - -{{/* -Return true if a secret object should be created for MySQL -*/}} -{{- define "mysql.createSecret" -}} -{{- if and (not .Values.auth.existingSecret) (not .Values.auth.customPasswordFiles) }} - {{- true -}} -{{- end -}} -{{- end -}} - -{{/* -Returns the available value for certain key in an existing secret (if it exists), -otherwise it generates a random value. -*/}} -{{- define "getValueFromSecret" }} - {{- $len := (default 16 .Length) | int -}} - {{- $obj := (lookup "v1" "Secret" .Namespace .Name).data -}} - {{- if $obj }} - {{- index $obj .Key | b64dec -}} - {{- else -}} - {{- randAlphaNum $len -}} - {{- end -}} -{{- end }} - -{{- define "mysql.root.password" -}} - {{- if not (empty .Values.auth.rootPassword) }} - {{- .Values.auth.rootPassword }} - {{- else if (not .Values.auth.forcePassword) }} - {{- include "getValueFromSecret" (dict "Namespace" .Release.Namespace "Name" (include "common.names.fullname" .) "Length" 10 "Key" "mysql-root-password") }} - {{- else }} - {{- required "A MySQL Root Password is required!" .Values.auth.rootPassword }} - {{- end }} -{{- end -}} - -{{- define "mysql.password" -}} - {{- if and (not (empty .Values.auth.username)) (not (empty .Values.auth.password)) }} - {{- .Values.auth.password }} - {{- else if (not .Values.auth.forcePassword) }} - {{- include "getValueFromSecret" (dict "Namespace" .Release.Namespace "Name" (include "common.names.fullname" .) "Length" 10 "Key" "mysql-password") }} - {{- else }} - {{- required "A MySQL Database Password is required!" .Values.auth.password }} - {{- end }} -{{- end -}} - -{{- define "mysql.replication.password" -}} - {{- if not (empty .Values.auth.replicationPassword) }} - {{- .Values.auth.replicationPassword }} - {{- else if (not .Values.auth.forcePassword) }} - {{- include "getValueFromSecret" (dict "Namespace" .Release.Namespace "Name" (include "common.names.fullname" .) "Length" 10 "Key" "mysql-replication-password") }} - {{- else }} - {{- required "A MySQL Replication Password is required!" .Values.auth.replicationPassword }} - {{- end }} -{{- end -}} - -{{/* Check if there are rolling tags in the images */}} -{{- define "mysql.checkRollingTags" -}} -{{- include "common.warnings.rollingTag" .Values.image }} -{{- include "common.warnings.rollingTag" .Values.metrics.image }} -{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} -{{- end -}} - -{{/* -Compile all warnings into a single message, and call fail. -*/}} -{{- define "mysql.validateValues" -}} -{{- $messages := list -}} -{{- $messages := without $messages "" -}} -{{- $message := join "\n" $messages -}} - -{{- if $message -}} -{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} -{{- end -}} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/extra-list.yaml b/pkg/iac/scanners/helm/test/mysql/templates/extra-list.yaml deleted file mode 100644 index 9ac65f9e16f4..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/extra-list.yaml +++ /dev/null @@ -1,4 +0,0 @@ -{{- range .Values.extraDeploy }} ---- -{{ include "common.tplvalues.render" (dict "value" . "context" $) }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/metrics-svc.yaml b/pkg/iac/scanners/helm/test/mysql/templates/metrics-svc.yaml deleted file mode 100644 index fb0d9d761dc6..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/metrics-svc.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if .Values.metrics.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ printf "%s-metrics" (include "common.names.fullname" .) }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - app.kubernetes.io/component: metrics - {{- if or .Values.metrics.service.annotations .Values.commonAnnotations }} - annotations: - {{- if .Values.metrics.service.annotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.metrics.service.annotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- end }} -spec: - type: {{ .Values.metrics.service.type }} - ports: - - port: {{ .Values.metrics.service.port }} - targetPort: metrics - protocol: TCP - name: metrics - selector: {{- include "common.labels.matchLabels" $ | nindent 4 }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/networkpolicy.yaml b/pkg/iac/scanners/helm/test/mysql/templates/networkpolicy.yaml deleted file mode 100644 index a0d1d01d4079..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/networkpolicy.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.networkPolicy.enabled }} -kind: NetworkPolicy -apiVersion: {{ template "common.capabilities.networkPolicy.apiVersion" . }} -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - {{- include "common.labels.matchLabels" . | nindent 6 }} - ingress: - # Allow inbound connections - - ports: - - port: {{ .Values.primary.service.port }} - {{- if not .Values.networkPolicy.allowExternal }} - from: - - podSelector: - matchLabels: - {{ template "common.names.fullname" . }}-client: "true" - {{- if .Values.networkPolicy.explicitNamespacesSelector }} - namespaceSelector: -{{ toYaml .Values.networkPolicy.explicitNamespacesSelector | indent 12 }} - {{- end }} - - podSelector: - matchLabels: - {{- include "common.labels.matchLabels" . | nindent 14 }} - {{- end }} - {{- if .Values.metrics.enabled }} - # Allow prometheus scrapes - - ports: - - port: 9104 - {{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/primary/configmap.yaml b/pkg/iac/scanners/helm/test/mysql/templates/primary/configmap.yaml deleted file mode 100644 index 540b7b9072e9..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/primary/configmap.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if (include "mysql.primary.createConfigmap" .) }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "mysql.primary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - my.cnf: |- - {{ .Values.primary.configuration | nindent 4 }} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/primary/initialization-configmap.yaml b/pkg/iac/scanners/helm/test/mysql/templates/primary/initialization-configmap.yaml deleted file mode 100644 index 83cbaea74883..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/primary/initialization-configmap.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if and .Values.initdbScripts (not .Values.initdbScriptsConfigMap) }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ printf "%s-init-scripts" (include "mysql.primary.fullname" .) }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: -{{- include "common.tplvalues.render" (dict "value" .Values.initdbScripts "context" .) | nindent 2 }} -{{ end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/primary/pdb.yaml b/pkg/iac/scanners/helm/test/mysql/templates/primary/pdb.yaml deleted file mode 100644 index 106ad5207e5a..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/primary/pdb.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- if .Values.primary.pdb.enabled }} -apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} -kind: PodDisruptionBudget -metadata: - name: {{ include "mysql.primary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - {{- if .Values.primary.pdb.minAvailable }} - minAvailable: {{ .Values.primary.pdb.minAvailable }} - {{- end }} - {{- if .Values.primary.pdb.maxUnavailable }} - maxUnavailable: {{ .Values.primary.pdb.maxUnavailable }} - {{- end }} - selector: - matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} - app.kubernetes.io/component: primary -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/primary/statefulset.yaml b/pkg/iac/scanners/helm/test/mysql/templates/primary/statefulset.yaml deleted file mode 100644 index 6f9c99ea66d9..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/primary/statefulset.yaml +++ /dev/null @@ -1,368 +0,0 @@ -apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} -kind: StatefulSet -metadata: - name: {{ include "mysql.primary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.primary.podLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.primary.podLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - replicas: 1 - selector: - matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} - app.kubernetes.io/component: primary - serviceName: {{ include "mysql.primary.fullname" . }} - updateStrategy: - type: {{ .Values.primary.updateStrategy }} - {{- if (eq "Recreate" .Values.primary.updateStrategy) }} - rollingUpdate: null - {{- else if .Values.primary.rollingUpdatePartition }} - rollingUpdate: - partition: {{ .Values.primary.rollingUpdatePartition }} - {{- end }} - template: - metadata: - annotations: - {{- if (include "mysql.primary.createConfigmap" .) }} - checksum/configuration: {{ include (print $.Template.BasePath "/primary/configmap.yaml") . | sha256sum }} - {{- end }} - {{- if .Values.primary.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.primary.podAnnotations "context" $) | nindent 8 }} - {{- end }} - labels: {{- include "common.labels.standard" . | nindent 8 }} - app.kubernetes.io/component: primary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 8 }} - {{- end }} - {{- if .Values.primary.podLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.primary.podLabels "context" $ ) | nindent 8 }} - {{- end }} - spec: - {{- include "mysql.imagePullSecrets" . | nindent 6 }} - {{- if .Values.primary.hostAliases }} - hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.primary.hostAliases "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.schedulerName }} - schedulerName: {{ .Values.schedulerName | quote }} - {{- end }} - serviceAccountName: {{ template "mysql.serviceAccountName" . }} - {{- if .Values.primary.affinity }} - affinity: {{- include "common.tplvalues.render" (dict "value" .Values.primary.affinity "context" $) | nindent 8 }} - {{- else }} - affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAffinityPreset "component" "primary" "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAntiAffinityPreset "component" "primary" "context" $) | nindent 10 }} - nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.primary.nodeAffinityPreset.type "key" .Values.primary.nodeAffinityPreset.key "values" .Values.primary.nodeAffinityPreset.values) | nindent 10 }} - {{- end }} - {{- if .Values.primary.nodeSelector }} - nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.primary.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.primary.tolerations }} - tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.primary.tolerations "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName | quote }} - {{- end }} - {{- if .Values.primary.podSecurityContext.enabled }} - securityContext: {{- omit .Values.primary.podSecurityContext "enabled" | toYaml | nindent 8 }} - {{- end }} - {{- if or .Values.primary.initContainers (and .Values.primary.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.primary.persistence.enabled) }} - initContainers: - {{- if .Values.primary.initContainers }} - {{- include "common.tplvalues.render" (dict "value" .Values.primary.initContainers "context" $) | nindent 8 }} - {{- end }} - {{- if and .Values.primary.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.primary.persistence.enabled }} - - name: volume-permissions - image: {{ include "mysql.volumePermissions.image" . }} - imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} - command: - - /bin/bash - - -ec - - | - chown -R {{ .Values.primary.containerSecurityContext.runAsUser }}:{{ .Values.primary.podSecurityContext.fsGroup }} /bitnami/mysql - securityContext: - runAsUser: 0 - {{- if .Values.volumePermissions.resources }} - resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: data - mountPath: /bitnami/mysql - {{- end }} - {{- end }} - containers: - - name: mysql - image: {{ include "mysql.image" . }} - imagePullPolicy: {{ .Values.image.pullPolicy | quote }} - {{- if .Values.primary.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.primary.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} - {{- else if .Values.primary.command }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.primary.command "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} - {{- else if .Values.primary.args }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.primary.args "context" $) | nindent 12 }} - {{- end }} - env: - - name: BITNAMI_DEBUG - value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} - {{- if .Values.auth.usePasswordFiles }} - - name: MYSQL_ROOT_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysql/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} - {{- else }} - - name: MYSQL_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: {{ template "mysql.secretName" . }} - key: mysql-root-password - {{- end }} - {{- if not (empty .Values.auth.username) }} - - name: MYSQL_USER - value: {{ .Values.auth.username | quote }} - {{- if .Values.auth.usePasswordFiles }} - - name: MYSQL_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysql/secrets/mysql-password" .Values.auth.customPasswordFiles.user }} - {{- else }} - - name: MYSQL_PASSWORD - valueFrom: - secretKeyRef: - name: {{ template "mysql.secretName" . }} - key: mysql-password - {{- end }} - {{- end }} - - name: MYSQL_DATABASE - value: {{ .Values.auth.database | quote }} - {{- if eq .Values.architecture "replication" }} - - name: MYSQL_REPLICATION_MODE - value: "master" - - name: MYSQL_REPLICATION_USER - value: {{ .Values.auth.replicationUser | quote }} - {{- if .Values.auth.usePasswordFiles }} - - name: MYSQL_REPLICATION_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysql/secrets/mysql-replication-password" .Values.auth.customPasswordFiles.replicator }} - {{- else }} - - name: MYSQL_REPLICATION_PASSWORD - valueFrom: - secretKeyRef: - name: {{ template "mysql.secretName" . }} - key: mysql-replication-password - {{- end }} - {{- end }} - {{- if .Values.primary.extraFlags }} - - name: MYSQL_EXTRA_FLAGS - value: "{{ .Values.primary.extraFlags }}" - {{- end }} - {{- if .Values.primary.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- if or .Values.primary.extraEnvVarsCM .Values.primary.extraEnvVarsSecret }} - envFrom: - {{- if .Values.primary.extraEnvVarsCM }} - - configMapRef: - name: {{ .Values.primary.extraEnvVarsCM }} - {{- end }} - {{- if .Values.primary.extraEnvVarsSecret }} - - secretRef: - name: {{ .Values.primary.extraEnvVarsSecret }} - {{- end }} - {{- end }} - ports: - - name: mysql - containerPort: 3306 - {{- if not .Values.diagnosticMode.enabled }} - {{- if .Values.primary.livenessProbe.enabled }} - livenessProbe: {{- omit .Values.primary.livenessProbe "enabled" | toYaml | nindent 12 }} - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - {{- else if .Values.primary.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.primary.customLivenessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.primary.readinessProbe.enabled }} - readinessProbe: {{- omit .Values.primary.readinessProbe "enabled" | toYaml | nindent 12 }} - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - {{- else if .Values.primary.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.primary.customReadinessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.primary.startupProbe.enabled }} - startupProbe: {{- omit .Values.primary.startupProbe "enabled" | toYaml | nindent 12 }} - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - {{- else if .Values.primary.customStartupProbe }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.primary.customStartupProbe "context" $) | nindent 12 }} - {{- end }} - {{- end }} - {{- if .Values.primary.resources }} - resources: {{ toYaml .Values.primary.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: data - mountPath: /bitnami/mysql - {{- if or .Values.initdbScriptsConfigMap .Values.initdbScripts }} - - name: custom-init-scripts - mountPath: /docker-entrypoint-initdb.d - {{- end }} - {{- if or .Values.primary.configuration .Values.primary.existingConfigmap }} - - name: config - mountPath: /opt/bitnami/mysql/conf/my.cnf - subPath: my.cnf - {{- end }} - {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} - - name: mysql-credentials - mountPath: /opt/bitnami/mysql/secrets/ - {{- end }} - {{- if .Values.primary.extraVolumeMounts }} - {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.metrics.enabled }} - - name: metrics - image: {{ include "mysql.metrics.image" . }} - imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} - env: - {{- if .Values.auth.usePasswordFiles }} - - name: MYSQL_ROOT_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysqld-exporter/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} - {{- else }} - - name: MYSQL_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "mysql.secretName" . }} - key: mysql-root-password - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} - {{- else }} - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - DATA_SOURCE_NAME="root:${password_aux}@(localhost:3306)/" /bin/mysqld_exporter {{- range .Values.metrics.extraArgs.primary }} {{ . }} {{- end }} - {{- end }} - ports: - - name: metrics - containerPort: 9104 - {{- if not .Values.diagnosticMode.enabled }} - {{- if .Values.metrics.livenessProbe.enabled }} - livenessProbe: {{- omit .Values.metrics.livenessProbe "enabled" | toYaml | nindent 12 }} - httpGet: - path: /metrics - port: metrics - {{- end }} - {{- if .Values.metrics.readinessProbe.enabled }} - readinessProbe: {{- omit .Values.metrics.readinessProbe "enabled" | toYaml | nindent 12 }} - httpGet: - path: /metrics - port: metrics - {{- end }} - {{- end }} - {{- if .Values.metrics.resources }} - resources: {{- toYaml .Values.metrics.resources | nindent 12 }} - {{- end }} - {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} - volumeMounts: - - name: mysql-credentials - mountPath: /opt/bitnami/mysqld-exporter/secrets/ - {{- end }} - {{- end }} - {{- if .Values.primary.sidecars }} - {{- include "common.tplvalues.render" (dict "value" .Values.primary.sidecars "context" $) | nindent 8 }} - {{- end }} - volumes: - {{- if or .Values.primary.configuration .Values.primary.existingConfigmap }} - - name: config - configMap: - name: {{ include "mysql.primary.configmapName" . }} - {{- end }} - {{- if or .Values.initdbScriptsConfigMap .Values.initdbScripts }} - - name: custom-init-scripts - configMap: - name: {{ include "mysql.initdbScriptsCM" . }} - {{- end }} - {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} - - name: mysql-credentials - secret: - secretName: {{ include "mysql.secretName" . }} - items: - - key: mysql-root-password - path: mysql-root-password - - key: mysql-password - path: mysql-password - {{- if eq .Values.architecture "replication" }} - - key: mysql-replication-password - path: mysql-replication-password - {{- end }} - {{- end }} - {{- if .Values.primary.extraVolumes }} - {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraVolumes "context" $) | nindent 8 }} - {{- end }} - {{- if and .Values.primary.persistence.enabled .Values.primary.persistence.existingClaim }} - - name: data - persistentVolumeClaim: - claimName: {{ tpl .Values.primary.persistence.existingClaim . }} - {{- else if not .Values.primary.persistence.enabled }} - - name: data - emptyDir: {} - {{- else if and .Values.primary.persistence.enabled (not .Values.primary.persistence.existingClaim) }} - volumeClaimTemplates: - - metadata: - name: data - labels: {{ include "common.labels.matchLabels" . | nindent 10 }} - app.kubernetes.io/component: primary - {{- if .Values.primary.persistence.annotations }} - annotations: - {{- toYaml .Values.primary.persistence.annotations | nindent 10 }} - {{- end }} - spec: - accessModes: - {{- range .Values.primary.persistence.accessModes }} - - {{ . | quote }} - {{- end }} - resources: - requests: - storage: {{ .Values.primary.persistence.size | quote }} - {{ include "common.storage.class" (dict "persistence" .Values.primary.persistence "global" .Values.global) }} - {{- if .Values.primary.persistence.selector }} - selector: {{- include "common.tplvalues.render" (dict "value" .Values.primary.persistence.selector "context" $) | nindent 10 }} - {{- end -}} - {{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/primary/svc-headless.yaml b/pkg/iac/scanners/helm/test/mysql/templates/primary/svc-headless.yaml deleted file mode 100644 index 49e6e5798783..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/primary/svc-headless.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "mysql.primary.fullname" . }}-headless - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - type: ClusterIP - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: mysql - port: {{ .Values.primary.service.port }} - targetPort: mysql - selector: {{ include "common.labels.matchLabels" . | nindent 4 }} - app.kubernetes.io/component: primary diff --git a/pkg/iac/scanners/helm/test/mysql/templates/primary/svc.yaml b/pkg/iac/scanners/helm/test/mysql/templates/primary/svc.yaml deleted file mode 100644 index b46e6faa8149..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/primary/svc.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "mysql.primary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.primary.service.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.primary.service.annotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.primary.service.type }} - {{- if and (eq .Values.primary.service.type "ClusterIP") .Values.primary.service.clusterIP }} - clusterIP: {{ .Values.primary.service.clusterIP }} - {{- end }} - {{- if and .Values.primary.service.loadBalancerIP (eq .Values.primary.service.type "LoadBalancer") }} - loadBalancerIP: {{ .Values.primary.service.loadBalancerIP }} - externalTrafficPolicy: {{ .Values.primary.service.externalTrafficPolicy | quote }} - {{- end }} - {{- if and (eq .Values.primary.service.type "LoadBalancer") .Values.primary.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- toYaml .Values.primary.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} - ports: - - name: mysql - port: {{ .Values.primary.service.port }} - protocol: TCP - targetPort: mysql - {{- if (and (or (eq .Values.primary.service.type "NodePort") (eq .Values.primary.service.type "LoadBalancer")) .Values.primary.service.nodePort) }} - nodePort: {{ .Values.primary.service.nodePort }} - {{- else if eq .Values.primary.service.type "ClusterIP" }} - nodePort: null - {{- end }} - selector: {{ include "common.labels.matchLabels" . | nindent 4 }} - app.kubernetes.io/component: primary diff --git a/pkg/iac/scanners/helm/test/mysql/templates/role.yaml b/pkg/iac/scanners/helm/test/mysql/templates/role.yaml deleted file mode 100644 index 4cbdd5c9ff20..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/role.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.serviceAccount.create .Values.rbac.create }} -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -kind: Role -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -rules: - - apiGroups: - - "" - resources: - - endpoints - verbs: - - get -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/rolebinding.yaml b/pkg/iac/scanners/helm/test/mysql/templates/rolebinding.yaml deleted file mode 100644 index 90ede32f5fc7..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/rolebinding.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.serviceAccount.create .Values.rbac.create }} -kind: RoleBinding -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -subjects: - - kind: ServiceAccount - name: {{ include "mysql.serviceAccountName" . }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "common.names.fullname" . -}} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/secondary/configmap.yaml b/pkg/iac/scanners/helm/test/mysql/templates/secondary/configmap.yaml deleted file mode 100644 index 682e3e19ba96..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/secondary/configmap.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if (include "mysql.secondary.createConfigmap" .) }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "mysql.secondary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: secondary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - my.cnf: |- - {{ .Values.secondary.configuration | nindent 4 }} -{{- end -}} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/secondary/pdb.yaml b/pkg/iac/scanners/helm/test/mysql/templates/secondary/pdb.yaml deleted file mode 100644 index 49c7e167c0a2..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/secondary/pdb.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- if and (eq .Values.architecture "replication") .Values.secondary.pdb.enabled }} -apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} -kind: PodDisruptionBudget -metadata: - name: {{ include "mysql.secondary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: secondary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - {{- if .Values.secondary.pdb.minAvailable }} - minAvailable: {{ .Values.secondary.pdb.minAvailable }} - {{- end }} - {{- if .Values.secondary.pdb.maxUnavailable }} - maxUnavailable: {{ .Values.secondary.pdb.maxUnavailable }} - {{- end }} - selector: - matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} - app.kubernetes.io/component: secondary -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/secondary/statefulset.yaml b/pkg/iac/scanners/helm/test/mysql/templates/secondary/statefulset.yaml deleted file mode 100644 index ef196ebf6df0..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/secondary/statefulset.yaml +++ /dev/null @@ -1,338 +0,0 @@ -{{- if eq .Values.architecture "replication" }} -apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} -kind: StatefulSet -metadata: - name: {{ include "mysql.secondary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: secondary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.secondary.podLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.secondary.podLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - replicas: {{ .Values.secondary.replicaCount }} - selector: - matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} - app.kubernetes.io/component: secondary - serviceName: {{ include "mysql.secondary.fullname" . }} - updateStrategy: - type: {{ .Values.secondary.updateStrategy }} - {{- if (eq "Recreate" .Values.secondary.updateStrategy) }} - rollingUpdate: null - {{- else if .Values.secondary.rollingUpdatePartition }} - rollingUpdate: - partition: {{ .Values.secondary.rollingUpdatePartition }} - {{- end }} - template: - metadata: - annotations: - {{- if (include "mysql.secondary.createConfigmap" .) }} - checksum/configuration: {{ include (print $.Template.BasePath "/secondary/configmap.yaml") . | sha256sum }} - {{- end }} - {{- if .Values.secondary.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.secondary.podAnnotations "context" $) | nindent 8 }} - {{- end }} - labels: {{- include "common.labels.standard" . | nindent 8 }} - app.kubernetes.io/component: secondary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 8 }} - {{- end }} - {{- if .Values.secondary.podLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.secondary.podLabels "context" $ ) | nindent 8 }} - {{- end }} - spec: - {{- include "mysql.imagePullSecrets" . | nindent 6 }} - {{- if .Values.secondary.hostAliases }} - hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.hostAliases "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.schedulerName }} - schedulerName: {{ .Values.schedulerName | quote }} - {{- end }} - serviceAccountName: {{ include "mysql.serviceAccountName" . }} - {{- if .Values.secondary.affinity }} - affinity: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.affinity "context" $) | nindent 8 }} - {{- else }} - affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.secondary.podAffinityPreset "component" "secondary" "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.secondary.podAntiAffinityPreset "component" "secondary" "context" $) | nindent 10 }} - nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.secondary.nodeAffinityPreset.type "key" .Values.secondary.nodeAffinityPreset.key "values" .Values.secondary.nodeAffinityPreset.values) | nindent 10 }} - {{- end }} - {{- if .Values.secondary.nodeSelector }} - nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.secondary.tolerations }} - tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.tolerations "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName | quote }} - {{- end }} - {{- if .Values.secondary.podSecurityContext.enabled }} - securityContext: {{- omit .Values.secondary.podSecurityContext "enabled" | toYaml | nindent 8 }} - {{- end }} - {{- if or .Values.secondary.initContainers (and .Values.secondary.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.secondary.persistence.enabled) }} - initContainers: - {{- if .Values.secondary.initContainers }} - {{- include "common.tplvalues.render" (dict "value" .Values.secondary.initContainers "context" $) | nindent 8 }} - {{- end }} - {{- if and .Values.secondary.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.secondary.persistence.enabled }} - - name: volume-permissions - image: {{ include "mysql.volumePermissions.image" . }} - imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} - command: - - /bin/bash - - -ec - - | - chown -R {{ .Values.secondary.containerSecurityContext.runAsUser }}:{{ .Values.secondary.podSecurityContext.fsGroup }} /bitnami/mysql - securityContext: - runAsUser: 0 - {{- if .Values.volumePermissions.resources }} - resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: data - mountPath: /bitnami/mysql - {{- end }} - {{- end }} - containers: - - name: mysql - image: {{ include "mysql.image" . }} - imagePullPolicy: {{ .Values.image.pullPolicy | quote }} - {{- if .Values.secondary.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.secondary.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} - {{- else if .Values.secondary.command }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.command "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} - {{- else if .Values.secondary.args }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.args "context" $) | nindent 12 }} - {{- end }} - env: - - name: BITNAMI_DEBUG - value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} - - name: MYSQL_REPLICATION_MODE - value: "slave" - - name: MYSQL_MASTER_HOST - value: {{ include "mysql.primary.fullname" . }} - - name: MYSQL_MASTER_PORT_NUMBER - value: {{ .Values.primary.service.port | quote }} - - name: MYSQL_MASTER_ROOT_USER - value: "root" - - name: MYSQL_REPLICATION_USER - value: {{ .Values.auth.replicationUser | quote }} - {{- if .Values.auth.usePasswordFiles }} - - name: MYSQL_MASTER_ROOT_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysql/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} - - name: MYSQL_REPLICATION_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysql/secrets/mysql-replication-password" .Values.auth.customPasswordFiles.replicator }} - {{- else }} - - name: MYSQL_MASTER_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: {{ template "mysql.secretName" . }} - key: mysql-root-password - - name: MYSQL_REPLICATION_PASSWORD - valueFrom: - secretKeyRef: - name: {{ template "mysql.secretName" . }} - key: mysql-replication-password - {{- end }} - {{- if .Values.secondary.extraFlags }} - - name: MYSQL_EXTRA_FLAGS - value: "{{ .Values.secondary.extraFlags }}" - {{- end }} - {{- if .Values.secondary.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- if or .Values.secondary.extraEnvVarsCM .Values.secondary.extraEnvVarsSecret }} - envFrom: - {{- if .Values.secondary.extraEnvVarsCM }} - - configMapRef: - name: {{ .Values.secondary.extraEnvVarsCM }} - {{- end }} - {{- if .Values.secondary.extraEnvVarsSecret }} - - secretRef: - name: {{ .Values.secondary.extraEnvVarsSecret }} - {{- end }} - {{- end }} - ports: - - name: mysql - containerPort: 3306 - {{- if not .Values.diagnosticMode.enabled }} - {{- if .Values.secondary.livenessProbe.enabled }} - livenessProbe: {{- omit .Values.secondary.livenessProbe "enabled" | toYaml | nindent 12 }} - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - {{- else if .Values.secondary.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.customLivenessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.secondary.readinessProbe.enabled }} - readinessProbe: {{- omit .Values.secondary.readinessProbe "enabled" | toYaml | nindent 12 }} - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - {{- else if .Values.secondary.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.customReadinessProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.secondary.startupProbe.enabled }} - startupProbe: {{- omit .Values.secondary.startupProbe "enabled" | toYaml | nindent 12 }} - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - {{- else if .Values.secondary.customStartupProbe }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.customStartupProbe "context" $) | nindent 12 }} - {{- end }} - {{- end }} - {{- if .Values.secondary.resources }} - resources: {{ toYaml .Values.secondary.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: data - mountPath: /bitnami/mysql - {{- if or .Values.secondary.configuration .Values.secondary.existingConfigmap }} - - name: config - mountPath: /opt/bitnami/mysql/conf/my.cnf - subPath: my.cnf - {{- end }} - {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} - - name: mysql-credentials - mountPath: /opt/bitnami/mysql/secrets/ - {{- end }} - {{- if .Values.secondary.extraVolumeMounts }} - {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.metrics.enabled }} - - name: metrics - image: {{ include "mysql.metrics.image" . }} - imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} - env: - {{- if .Values.auth.usePasswordFiles }} - - name: MYSQL_ROOT_PASSWORD_FILE - value: {{ default "/opt/bitnami/mysqld-exporter/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} - {{- else }} - - name: MYSQL_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: {{ template "mysql.secretName" . }} - key: mysql-root-password - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} - {{- else }} - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - DATA_SOURCE_NAME="root:${password_aux}@(localhost:3306)/" /bin/mysqld_exporter {{- range .Values.metrics.extraArgs.secondary }} {{ . }} {{- end }} - {{- end }} - ports: - - name: metrics - containerPort: 9104 - {{- if not .Values.diagnosticMode.enabled }} - {{- if .Values.metrics.livenessProbe.enabled }} - livenessProbe: {{- omit .Values.metrics.livenessProbe "enabled" | toYaml | nindent 12 }} - httpGet: - path: /metrics - port: metrics - {{- end }} - {{- if .Values.metrics.readinessProbe.enabled }} - readinessProbe: {{- omit .Values.metrics.readinessProbe "enabled" | toYaml | nindent 12 }} - httpGet: - path: /metrics - port: metrics - {{- end }} - {{- end }} - {{- if .Values.metrics.resources }} - resources: {{- toYaml .Values.metrics.resources | nindent 12 }} - {{- end }} - {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} - volumeMounts: - - name: mysql-credentials - mountPath: /opt/bitnami/mysqld-exporter/secrets/ - {{- end }} - {{- end }} - {{- if .Values.secondary.sidecars }} - {{- include "common.tplvalues.render" (dict "value" .Values.secondary.sidecars "context" $) | nindent 8 }} - {{- end }} - volumes: - {{- if or .Values.secondary.configuration .Values.secondary.existingConfigmap }} - - name: config - configMap: - name: {{ include "mysql.secondary.configmapName" . }} - {{- end }} - {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} - - name: mysql-credentials - secret: - secretName: {{ template "mysql.secretName" . }} - items: - - key: mysql-root-password - path: mysql-root-password - - key: mysql-replication-password - path: mysql-replication-password - {{- end }} - {{- if .Values.secondary.extraVolumes }} - {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraVolumes "context" $) | nindent 8 }} - {{- end }} - {{- if not .Values.secondary.persistence.enabled }} - - name: data - emptyDir: {} - {{- else }} - volumeClaimTemplates: - - metadata: - name: data - labels: {{ include "common.labels.matchLabels" . | nindent 10 }} - app.kubernetes.io/component: secondary - {{- if .Values.secondary.persistence.annotations }} - annotations: - {{- toYaml .Values.secondary.persistence.annotations | nindent 10 }} - {{- end }} - spec: - accessModes: - {{- range .Values.secondary.persistence.accessModes }} - - {{ . | quote }} - {{- end }} - resources: - requests: - storage: {{ .Values.secondary.persistence.size | quote }} - {{ include "common.storage.class" (dict "persistence" .Values.secondary.persistence "global" .Values.global) }} - {{- if .Values.secondary.persistence.selector }} - selector: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.persistence.selector "context" $) | nindent 10 }} - {{- end -}} - {{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/secondary/svc-headless.yaml b/pkg/iac/scanners/helm/test/mysql/templates/secondary/svc-headless.yaml deleted file mode 100644 index 703d8e747b75..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/secondary/svc-headless.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- if eq .Values.architecture "replication" }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "mysql.secondary.fullname" . }}-headless - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: secondary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - type: ClusterIP - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: mysql - port: {{ .Values.secondary.service.port }} - targetPort: mysql - selector: {{ include "common.labels.matchLabels" . | nindent 4 }} - app.kubernetes.io/component: secondary -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/secondary/svc.yaml b/pkg/iac/scanners/helm/test/mysql/templates/secondary/svc.yaml deleted file mode 100644 index 74a4c6ef5fb8..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/secondary/svc.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if eq .Values.architecture "replication" }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "mysql.secondary.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: secondary - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.secondary.service.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.secondary.service.annotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.secondary.service.type }} - {{- if and (eq .Values.secondary.service.type "ClusterIP") .Values.secondary.service.clusterIP }} - clusterIP: {{ .Values.secondary.service.clusterIP }} - {{- end }} - {{- if and .Values.secondary.service.loadBalancerIP (eq .Values.secondary.service.type "LoadBalancer") }} - loadBalancerIP: {{ .Values.secondary.service.loadBalancerIP }} - externalTrafficPolicy: {{ .Values.secondary.service.externalTrafficPolicy | quote }} - {{- end }} - {{- if and (eq .Values.secondary.service.type "LoadBalancer") .Values.secondary.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- toYaml .Values.secondary.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} - ports: - - name: mysql - port: {{ .Values.secondary.service.port }} - protocol: TCP - targetPort: mysql - {{- if (and (or (eq .Values.secondary.service.type "NodePort") (eq .Values.secondary.service.type "LoadBalancer")) .Values.secondary.service.nodePort) }} - nodePort: {{ .Values.secondary.service.nodePort }} - {{- else if eq .Values.secondary.service.type "ClusterIP" }} - nodePort: null - {{- end }} - selector: {{ include "common.labels.matchLabels" . | nindent 4 }} - app.kubernetes.io/component: secondary -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/secrets.yaml b/pkg/iac/scanners/helm/test/mysql/templates/secrets.yaml deleted file mode 100644 index 9412fc35a5bc..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/secrets.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if eq (include "mysql.createSecret" .) "true" }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: Opaque -data: - mysql-root-password: {{ include "mysql.root.password" . | b64enc | quote }} - mysql-password: {{ include "mysql.password" . | b64enc | quote }} - {{- if eq .Values.architecture "replication" }} - mysql-replication-password: {{ include "mysql.replication.password" . | b64enc | quote }} - {{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/serviceaccount.yaml b/pkg/iac/scanners/helm/test/mysql/templates/serviceaccount.yaml deleted file mode 100644 index 59eb10409d91..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/serviceaccount.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if .Values.serviceAccount.create }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "mysql.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.serviceAccount.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.serviceAccount.annotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -{{- if (not .Values.auth.customPasswordFiles) }} -secrets: - - name: {{ template "mysql.secretName" . }} -{{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/templates/servicemonitor.yaml b/pkg/iac/scanners/helm/test/mysql/templates/servicemonitor.yaml deleted file mode 100644 index f082dd5409d6..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/templates/servicemonitor.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ include "common.names.fullname" . }} - {{- if .Values.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.metrics.serviceMonitor.namespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.additionalLabels }} - {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.additionalLabels "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - endpoints: - - port: metrics - {{- if .Values.metrics.serviceMonitor.interval }} - interval: {{ .Values.metrics.serviceMonitor.interval }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} - scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.honorLabels }} - honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.relabellings }} - metricRelabelings: {{- toYaml .Values.metrics.serviceMonitor.relabellings | nindent 6 }} - {{- end }} - namespaceSelector: - matchNames: - - {{ .Release.Namespace }} - selector: - matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} - app.kubernetes.io/component: metrics -{{- end }} diff --git a/pkg/iac/scanners/helm/test/mysql/values.schema.json b/pkg/iac/scanners/helm/test/mysql/values.schema.json deleted file mode 100644 index 8021a4603600..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/values.schema.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "type": "object", - "properties": { - "architecture": { - "type": "string", - "title": "MySQL architecture", - "form": true, - "description": "Allowed values: `standalone` or `replication`", - "enum": ["standalone", "replication"] - }, - "auth": { - "type": "object", - "title": "Authentication configuration", - "form": true, - "required": ["database", "username", "password"], - "properties": { - "rootPassword": { - "type": "string", - "title": "MySQL root password", - "description": "Defaults to a random 10-character alphanumeric string if not set" - }, - "database": { - "type": "string", - "title": "MySQL custom database name" - }, - "username": { - "type": "string", - "title": "MySQL custom username" - }, - "password": { - "type": "string", - "title": "MySQL custom password" - }, - "replicationUser": { - "type": "string", - "title": "MySQL replication username" - }, - "replicationPassword": { - "type": "string", - "title": "MySQL replication password" - } - } - }, - "primary": { - "type": "object", - "title": "Primary database configuration", - "form": true, - "properties": { - "podSecurityContext": { - "type": "object", - "title": "MySQL primary Pod security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "fsGroup": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "primary/podSecurityContext/enabled" - } - } - } - }, - "containerSecurityContext": { - "type": "object", - "title": "MySQL primary container security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "runAsUser": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "primary/containerSecurityContext/enabled" - } - } - } - }, - "persistence": { - "type": "object", - "title": "Enable persistence using Persistent Volume Claims", - "properties": { - "enabled": { - "type": "boolean", - "default": true, - "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" - }, - "size": { - "type": "string", - "title": "Persistent Volume Size", - "form": true, - "render": "slider", - "sliderMin": 1, - "sliderUnit": "Gi", - "hidden": { - "value": false, - "path": "primary/persistence/enabled" - } - } - } - } - } - }, - "secondary": { - "type": "object", - "title": "Secondary database configuration", - "form": true, - "properties": { - "podSecurityContext": { - "type": "object", - "title": "MySQL secondary Pod security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "fsGroup": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "secondary/podSecurityContext/enabled" - } - } - } - }, - "containerSecurityContext": { - "type": "object", - "title": "MySQL secondary container security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "runAsUser": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "secondary/containerSecurityContext/enabled" - } - } - } - }, - "persistence": { - "type": "object", - "title": "Enable persistence using Persistent Volume Claims", - "properties": { - "enabled": { - "type": "boolean", - "default": true, - "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" - }, - "size": { - "type": "string", - "title": "Persistent Volume Size", - "form": true, - "render": "slider", - "sliderMin": 1, - "sliderUnit": "Gi", - "hidden": { - "value": false, - "path": "secondary/persistence/enabled" - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/mysql/values.yaml b/pkg/iac/scanners/helm/test/mysql/values.yaml deleted file mode 100644 index 3900e865955c..000000000000 --- a/pkg/iac/scanners/helm/test/mysql/values.yaml +++ /dev/null @@ -1,1020 +0,0 @@ -## @section Global parameters -## Global Docker image parameters -## Please, note that this will override the image parameters, including dependencies, configured to use the global value -## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass - -## @param global.imageRegistry Global Docker image registry -## @param global.imagePullSecrets [array] Global Docker registry secret names as an array -## @param global.storageClass Global StorageClass for Persistent Volume(s) -## -global: - imageRegistry: "" - ## E.g. - ## imagePullSecrets: - ## - myRegistryKeySecretName - ## - imagePullSecrets: [] - storageClass: "" - -## @section Common parameters - -## @param nameOverride String to partially override common.names.fullname template (will maintain the release name) -## -nameOverride: "" -## @param fullnameOverride String to fully override common.names.fullname template -## -fullnameOverride: "" -## @param clusterDomain Cluster domain -## -clusterDomain: cluster.local -## @param commonAnnotations [object] Common annotations to add to all MySQL resources (sub-charts are not considered). Evaluated as a template -## -commonAnnotations: {} -## @param commonLabels [object] Common labels to add to all MySQL resources (sub-charts are not considered). Evaluated as a template -## -commonLabels: {} -## @param extraDeploy [array] Array with extra yaml to deploy with the chart. Evaluated as a template -## -extraDeploy: [] -## @param schedulerName Use an alternate scheduler, e.g. "stork". -## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ -## -schedulerName: "" - -## Enable diagnostic mode in the deployment -## -diagnosticMode: - ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) - ## - enabled: false - ## @param diagnosticMode.command Command to override all containers in the deployment - ## - command: - - sleep - ## @param diagnosticMode.args Args to override all containers in the deployment - ## - args: - - infinity - -## @section MySQL common parameters - -## Bitnami MySQL image -## ref: https://hub.docker.com/r/bitnami/mysql/tags/ -## @param image.registry MySQL image registry -## @param image.repository MySQL image repository -## @param image.tag MySQL image tag (immutable tags are recommended) -## @param image.pullPolicy MySQL image pull policy -## @param image.pullSecrets [array] Specify docker-registry secret names as an array -## @param image.debug Specify if debug logs should be enabled -## -image: - registry: docker.io - repository: bitnami/mysql - tag: 8.0.28-debian-10-r23 - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## Example: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## Set to true if you would like to see extra information on logs - ## It turns BASH and/or NAMI debugging in the image - ## - debug: false -## @param architecture MySQL architecture (`standalone` or `replication`) -## -architecture: standalone -## MySQL Authentication parameters -## -auth: - ## @param auth.rootPassword Password for the `root` user. Ignored if existing secret is provided - ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-the-root-password-on-first-run - ## - rootPassword: "" - ## @param auth.database Name for a custom database to create - ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-on-first-run - ## - database: my_database - ## @param auth.username Name for a custom user to create - ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-user-on-first-run - ## - username: "" - ## @param auth.password Password for the new user. Ignored if existing secret is provided - ## - password: "" - ## @param auth.replicationUser MySQL replication user - ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-up-a-replication-cluster - ## - replicationUser: replicator - ## @param auth.replicationPassword MySQL replication user password. Ignored if existing secret is provided - ## - replicationPassword: "" - ## @param auth.existingSecret Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` - ## NOTE: When it's set the auth.rootPassword, auth.password, auth.replicationPassword are ignored. - ## - existingSecret: "" - ## @param auth.forcePassword Force users to specify required passwords - ## - forcePassword: false - ## @param auth.usePasswordFiles Mount credentials as files instead of using an environment variable - ## - usePasswordFiles: false - ## @param auth.customPasswordFiles [object] Use custom password files when `auth.usePasswordFiles` is set to `true`. Define path for keys `root` and `user`, also define `replicator` if `architecture` is set to `replication` - ## Example: - ## customPasswordFiles: - ## root: /vault/secrets/mysql-root - ## user: /vault/secrets/mysql-user - ## replicator: /vault/secrets/mysql-replicator - ## - customPasswordFiles: {} -## @param initdbScripts [object] Dictionary of initdb scripts -## Specify dictionary of scripts to be run at first boot -## Example: -## initdbScripts: -## my_init_script.sh: | -## #!/bin/bash -## echo "Do something." -## -initdbScripts: {} -## @param initdbScriptsConfigMap ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) -## -initdbScriptsConfigMap: "" - -## @section MySQL Primary parameters - -primary: - ## @param primary.command [array] Override default container command on MySQL Primary container(s) (useful when using custom images) - ## - command: [] - ## @param primary.args [array] Override default container args on MySQL Primary container(s) (useful when using custom images) - ## - args: [] - ## @param primary.hostAliases [array] Deployment pod host aliases - ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ - ## - hostAliases: [] - ## @param primary.configuration [string] Configure MySQL Primary with a custom my.cnf file - ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file - ## - configuration: |- - [mysqld] - default_authentication_plugin=mysql_native_password - skip-name-resolve - explicit_defaults_for_timestamp - basedir=/opt/bitnami/mysql - plugin_dir=/opt/bitnami/mysql/lib/plugin - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - datadir=/bitnami/mysql/data - tmpdir=/opt/bitnami/mysql/tmp - max_allowed_packet=16M - bind-address=0.0.0.0 - pid-file=/opt/bitnami/mysql/tmp/mysqld.pid - log-error=/opt/bitnami/mysql/logs/mysqld.log - character-set-server=UTF8 - collation-server=utf8_general_ci - - [client] - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - default-character-set=UTF8 - plugin_dir=/opt/bitnami/mysql/lib/plugin - - [manager] - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - pid-file=/opt/bitnami/mysql/tmp/mysqld.pid - ## @param primary.existingConfigmap Name of existing ConfigMap with MySQL Primary configuration. - ## NOTE: When it's set the 'configuration' parameter is ignored - ## - existingConfigmap: "" - ## @param primary.updateStrategy Update strategy type for the MySQL primary statefulset - ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies - ## - updateStrategy: RollingUpdate - ## @param primary.rollingUpdatePartition Partition update strategy for MySQL Primary statefulset - ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions - ## - rollingUpdatePartition: "" - ## @param primary.podAnnotations [object] Additional pod annotations for MySQL primary pods - ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - ## - podAnnotations: {} - ## @param primary.podAffinityPreset MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## - podAffinityPreset: "" - ## @param primary.podAntiAffinityPreset MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## - podAntiAffinityPreset: soft - ## MySQL Primary node affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity - ## - nodeAffinityPreset: - ## @param primary.nodeAffinityPreset.type MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` - ## - type: "" - ## @param primary.nodeAffinityPreset.key MySQL primary node label key to match Ignored if `primary.affinity` is set. - ## E.g. - ## key: "kubernetes.io/e2e-az-name" - ## - key: "" - ## @param primary.nodeAffinityPreset.values [array] MySQL primary node label values to match. Ignored if `primary.affinity` is set. - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] - ## @param primary.affinity [object] Affinity for MySQL primary pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - ## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set - ## - affinity: {} - ## @param primary.nodeSelector [object] Node labels for MySQL primary pods assignment - ## ref: https://kubernetes.io/docs/user-guide/node-selection/ - ## - nodeSelector: {} - ## @param primary.tolerations [array] Tolerations for MySQL primary pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ - ## - tolerations: [] - ## MySQL primary Pod security context - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod - ## @param primary.podSecurityContext.enabled Enable security context for MySQL primary pods - ## @param primary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem - ## - podSecurityContext: - enabled: true - fsGroup: 1001 - ## MySQL primary container security context - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container - ## @param primary.containerSecurityContext.enabled MySQL primary container securityContext - ## @param primary.containerSecurityContext.runAsUser User ID for the MySQL primary container - ## - containerSecurityContext: - enabled: true - runAsUser: 1001 - ## MySQL primary container's resource requests and limits - ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ - ## We usually recommend not to specify default resources and to leave this as a conscious - ## choice for the user. This also increases chances charts run on environments with little - ## resources, such as Minikube. If you do want to specify resources, uncomment the following - ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. - ## @param primary.resources.limits [object] The resources limits for MySQL primary containers - ## @param primary.resources.requests [object] The requested resources for MySQL primary containers - ## - resources: - ## Example: - ## limits: - ## cpu: 250m - ## memory: 256Mi - limits: {} - ## Examples: - ## requests: - ## cpu: 250m - ## memory: 256Mi - requests: {} - ## Configure extra options for liveness probe - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - ## @param primary.livenessProbe.enabled Enable livenessProbe - ## @param primary.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe - ## @param primary.livenessProbe.periodSeconds Period seconds for livenessProbe - ## @param primary.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe - ## @param primary.livenessProbe.failureThreshold Failure threshold for livenessProbe - ## @param primary.livenessProbe.successThreshold Success threshold for livenessProbe - ## - livenessProbe: - enabled: true - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 1 - failureThreshold: 3 - successThreshold: 1 - ## Configure extra options for readiness probe - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - ## @param primary.readinessProbe.enabled Enable readinessProbe - ## @param primary.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe - ## @param primary.readinessProbe.periodSeconds Period seconds for readinessProbe - ## @param primary.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe - ## @param primary.readinessProbe.failureThreshold Failure threshold for readinessProbe - ## @param primary.readinessProbe.successThreshold Success threshold for readinessProbe - ## - readinessProbe: - enabled: true - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 1 - failureThreshold: 3 - successThreshold: 1 - ## Configure extra options for startupProbe probe - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - ## @param primary.startupProbe.enabled Enable startupProbe - ## @param primary.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe - ## @param primary.startupProbe.periodSeconds Period seconds for startupProbe - ## @param primary.startupProbe.timeoutSeconds Timeout seconds for startupProbe - ## @param primary.startupProbe.failureThreshold Failure threshold for startupProbe - ## @param primary.startupProbe.successThreshold Success threshold for startupProbe - ## - startupProbe: - enabled: true - initialDelaySeconds: 15 - periodSeconds: 10 - timeoutSeconds: 1 - failureThreshold: 10 - successThreshold: 1 - ## @param primary.customLivenessProbe [object] Override default liveness probe for MySQL primary containers - ## - customLivenessProbe: {} - ## @param primary.customReadinessProbe [object] Override default readiness probe for MySQL primary containers - ## - customReadinessProbe: {} - ## @param primary.customStartupProbe [object] Override default startup probe for MySQL primary containers - ## - customStartupProbe: {} - ## @param primary.extraFlags MySQL primary additional command line flags - ## Can be used to specify command line flags, for example: - ## E.g. - ## extraFlags: "--max-connect-errors=1000 --max_connections=155" - ## - extraFlags: "" - ## @param primary.extraEnvVars [array] Extra environment variables to be set on MySQL primary containers - ## E.g. - ## extraEnvVars: - ## - name: TZ - ## value: "Europe/Paris" - ## - extraEnvVars: [] - ## @param primary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL primary containers - ## - extraEnvVarsCM: "" - ## @param primary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL primary containers - ## - extraEnvVarsSecret: "" - ## Enable persistence using Persistent Volume Claims - ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ - ## - persistence: - ## @param primary.persistence.enabled Enable persistence on MySQL primary replicas using a `PersistentVolumeClaim`. If false, use emptyDir - ## - enabled: true - ## @param primary.persistence.existingClaim Name of an existing `PersistentVolumeClaim` for MySQL primary replicas - ## NOTE: When it's set the rest of persistence parameters are ignored - ## - existingClaim: "" - ## @param primary.persistence.storageClass MySQL primary persistent volume storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - storageClass: "" - ## @param primary.persistence.annotations [object] MySQL primary persistent volume claim annotations - ## - annotations: {} - ## @param primary.persistence.accessModes MySQL primary persistent volume access Modes - ## - accessModes: - - ReadWriteOnce - ## @param primary.persistence.size MySQL primary persistent volume size - ## - size: 8Gi - ## @param primary.persistence.selector [object] Selector to match an existing Persistent Volume - ## selector: - ## matchLabels: - ## app: my-app - ## - selector: {} - ## @param primary.extraVolumes [array] Optionally specify extra list of additional volumes to the MySQL Primary pod(s) - ## - extraVolumes: [] - ## @param primary.extraVolumeMounts [array] Optionally specify extra list of additional volumeMounts for the MySQL Primary container(s) - ## - extraVolumeMounts: [] - ## @param primary.initContainers [array] Add additional init containers for the MySQL Primary pod(s) - ## - initContainers: [] - ## @param primary.sidecars [array] Add additional sidecar containers for the MySQL Primary pod(s) - ## - sidecars: [] - ## MySQL Primary Service parameters - ## - service: - ## @param primary.service.type MySQL Primary K8s service type - ## - type: ClusterIP - ## @param primary.service.port MySQL Primary K8s service port - ## - port: 3306 - ## @param primary.service.nodePort MySQL Primary K8s service node port - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport - ## - nodePort: "" - ## @param primary.service.clusterIP MySQL Primary K8s service clusterIP IP - ## e.g: - ## clusterIP: None - ## - clusterIP: "" - ## @param primary.service.loadBalancerIP MySQL Primary loadBalancerIP if service type is `LoadBalancer` - ## Set the LoadBalancer service type to internal only - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer - ## - loadBalancerIP: "" - ## @param primary.service.externalTrafficPolicy Enable client source IP preservation - ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip - ## - externalTrafficPolicy: Cluster - ## @param primary.service.loadBalancerSourceRanges [array] Addresses that are allowed when MySQL Primary service is LoadBalancer - ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service - ## E.g. - ## loadBalancerSourceRanges: - ## - 10.10.10.0/24 - ## - loadBalancerSourceRanges: [] - ## @param primary.service.annotations [object] Provide any additional annotations which may be required - ## - annotations: {} - ## MySQL primary Pod Disruption Budget configuration - ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ - ## - pdb: - ## @param primary.pdb.enabled Enable/disable a Pod Disruption Budget creation for MySQL primary pods - ## - enabled: false - ## @param primary.pdb.minAvailable Minimum number/percentage of MySQL primary pods that should remain scheduled - ## - minAvailable: 1 - ## @param primary.pdb.maxUnavailable Maximum number/percentage of MySQL primary pods that may be made unavailable - ## - maxUnavailable: "" - ## @param primary.podLabels [object] MySQL Primary pod label. If labels are same as commonLabels , this will take precedence - ## - podLabels: {} - -## @section MySQL Secondary parameters - -secondary: - ## @param secondary.replicaCount Number of MySQL secondary replicas - ## - replicaCount: 1 - ## @param secondary.hostAliases [array] Deployment pod host aliases - ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ - ## - hostAliases: [] - ## @param secondary.command [array] Override default container command on MySQL Secondary container(s) (useful when using custom images) - ## - command: [] - ## @param secondary.args [array] Override default container args on MySQL Secondary container(s) (useful when using custom images) - ## - args: [] - ## @param secondary.configuration [string] Configure MySQL Secondary with a custom my.cnf file - ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file - ## - configuration: |- - [mysqld] - default_authentication_plugin=mysql_native_password - skip-name-resolve - explicit_defaults_for_timestamp - basedir=/opt/bitnami/mysql - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - datadir=/bitnami/mysql/data - tmpdir=/opt/bitnami/mysql/tmp - max_allowed_packet=16M - bind-address=0.0.0.0 - pid-file=/opt/bitnami/mysql/tmp/mysqld.pid - log-error=/opt/bitnami/mysql/logs/mysqld.log - character-set-server=UTF8 - collation-server=utf8_general_ci - - [client] - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - default-character-set=UTF8 - - [manager] - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - pid-file=/opt/bitnami/mysql/tmp/mysqld.pid - ## @param secondary.existingConfigmap Name of existing ConfigMap with MySQL Secondary configuration. - ## NOTE: When it's set the 'configuration' parameter is ignored - ## - existingConfigmap: "" - ## @param secondary.updateStrategy Update strategy type for the MySQL secondary statefulset - ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies - ## - updateStrategy: RollingUpdate - ## @param secondary.rollingUpdatePartition Partition update strategy for MySQL Secondary statefulset - ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions - ## - rollingUpdatePartition: "" - ## @param secondary.podAnnotations [object] Additional pod annotations for MySQL secondary pods - ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - ## - podAnnotations: {} - ## @param secondary.podAffinityPreset MySQL secondary pod affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## - podAffinityPreset: "" - ## @param secondary.podAntiAffinityPreset MySQL secondary pod anti-affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## Allowed values: soft, hard - ## - podAntiAffinityPreset: soft - ## MySQL Secondary node affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity - ## - nodeAffinityPreset: - ## @param secondary.nodeAffinityPreset.type MySQL secondary node affinity preset type. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` - ## - type: "" - ## @param secondary.nodeAffinityPreset.key MySQL secondary node label key to match Ignored if `secondary.affinity` is set. - ## E.g. - ## key: "kubernetes.io/e2e-az-name" - ## - key: "" - ## @param secondary.nodeAffinityPreset.values [array] MySQL secondary node label values to match. Ignored if `secondary.affinity` is set. - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] - ## @param secondary.affinity [object] Affinity for MySQL secondary pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - ## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set - ## - affinity: {} - ## @param secondary.nodeSelector [object] Node labels for MySQL secondary pods assignment - ## ref: https://kubernetes.io/docs/user-guide/node-selection/ - ## - nodeSelector: {} - ## @param secondary.tolerations [array] Tolerations for MySQL secondary pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ - ## - tolerations: [] - ## MySQL secondary Pod security context - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod - ## @param secondary.podSecurityContext.enabled Enable security context for MySQL secondary pods - ## @param secondary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem - ## - podSecurityContext: - enabled: true - fsGroup: 1001 - ## MySQL secondary container security context - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container - ## @param secondary.containerSecurityContext.enabled MySQL secondary container securityContext - ## @param secondary.containerSecurityContext.runAsUser User ID for the MySQL secondary container - ## - containerSecurityContext: - enabled: true - runAsUser: 1001 - ## MySQL secondary container's resource requests and limits - ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ - ## We usually recommend not to specify default resources and to leave this as a conscious - ## choice for the user. This also increases chances charts run on environments with little - ## resources, such as Minikube. If you do want to specify resources, uncomment the following - ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. - ## @param secondary.resources.limits [object] The resources limits for MySQL secondary containers - ## @param secondary.resources.requests [object] The requested resources for MySQL secondary containers - ## - resources: - ## Example: - ## limits: - ## cpu: 250m - ## memory: 256Mi - limits: {} - ## Examples: - ## requests: - ## cpu: 250m - ## memory: 256Mi - requests: {} - ## Configure extra options for liveness probe - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - ## @param secondary.livenessProbe.enabled Enable livenessProbe - ## @param secondary.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe - ## @param secondary.livenessProbe.periodSeconds Period seconds for livenessProbe - ## @param secondary.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe - ## @param secondary.livenessProbe.failureThreshold Failure threshold for livenessProbe - ## @param secondary.livenessProbe.successThreshold Success threshold for livenessProbe - ## - livenessProbe: - enabled: true - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 1 - failureThreshold: 3 - successThreshold: 1 - ## Configure extra options for readiness probe - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - ## @param secondary.readinessProbe.enabled Enable readinessProbe - ## @param secondary.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe - ## @param secondary.readinessProbe.periodSeconds Period seconds for readinessProbe - ## @param secondary.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe - ## @param secondary.readinessProbe.failureThreshold Failure threshold for readinessProbe - ## @param secondary.readinessProbe.successThreshold Success threshold for readinessProbe - ## - readinessProbe: - enabled: true - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 1 - failureThreshold: 3 - successThreshold: 1 - ## Configure extra options for startupProbe probe - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - ## @param secondary.startupProbe.enabled Enable startupProbe - ## @param secondary.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe - ## @param secondary.startupProbe.periodSeconds Period seconds for startupProbe - ## @param secondary.startupProbe.timeoutSeconds Timeout seconds for startupProbe - ## @param secondary.startupProbe.failureThreshold Failure threshold for startupProbe - ## @param secondary.startupProbe.successThreshold Success threshold for startupProbe - ## - startupProbe: - enabled: true - initialDelaySeconds: 15 - periodSeconds: 10 - timeoutSeconds: 1 - failureThreshold: 15 - successThreshold: 1 - ## @param secondary.customLivenessProbe [object] Override default liveness probe for MySQL secondary containers - ## - customLivenessProbe: {} - ## @param secondary.customReadinessProbe [object] Override default readiness probe for MySQL secondary containers - ## - customReadinessProbe: {} - ## @param secondary.customStartupProbe [object] Override default startup probe for MySQL secondary containers - ## - customStartupProbe: {} - ## @param secondary.extraFlags MySQL secondary additional command line flags - ## Can be used to specify command line flags, for example: - ## E.g. - ## extraFlags: "--max-connect-errors=1000 --max_connections=155" - ## - extraFlags: "" - ## @param secondary.extraEnvVars [array] An array to add extra environment variables on MySQL secondary containers - ## E.g. - ## extraEnvVars: - ## - name: TZ - ## value: "Europe/Paris" - ## - extraEnvVars: [] - ## @param secondary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL secondary containers - ## - extraEnvVarsCM: "" - ## @param secondary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL secondary containers - ## - extraEnvVarsSecret: "" - ## Enable persistence using Persistent Volume Claims - ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ - ## - persistence: - ## @param secondary.persistence.enabled Enable persistence on MySQL secondary replicas using a `PersistentVolumeClaim` - ## - enabled: true - ## @param secondary.persistence.storageClass MySQL secondary persistent volume storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - storageClass: "" - ## @param secondary.persistence.annotations [object] MySQL secondary persistent volume claim annotations - ## - annotations: {} - ## @param secondary.persistence.accessModes MySQL secondary persistent volume access Modes - ## - accessModes: - - ReadWriteOnce - ## @param secondary.persistence.size MySQL secondary persistent volume size - ## - size: 8Gi - ## @param secondary.persistence.selector [object] Selector to match an existing Persistent Volume - ## selector: - ## matchLabels: - ## app: my-app - ## - selector: {} - ## @param secondary.extraVolumes [array] Optionally specify extra list of additional volumes to the MySQL secondary pod(s) - ## - extraVolumes: [] - ## @param secondary.extraVolumeMounts [array] Optionally specify extra list of additional volumeMounts for the MySQL secondary container(s) - ## - extraVolumeMounts: [] - ## @param secondary.initContainers [array] Add additional init containers for the MySQL secondary pod(s) - ## - initContainers: [] - ## @param secondary.sidecars [array] Add additional sidecar containers for the MySQL secondary pod(s) - ## - sidecars: [] - ## MySQL Secondary Service parameters - ## - service: - ## @param secondary.service.type MySQL secondary Kubernetes service type - ## - type: ClusterIP - ## @param secondary.service.port MySQL secondary Kubernetes service port - ## - port: 3306 - ## @param secondary.service.nodePort MySQL secondary Kubernetes service node port - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport - ## - nodePort: "" - ## @param secondary.service.clusterIP MySQL secondary Kubernetes service clusterIP IP - ## e.g: - ## clusterIP: None - ## - clusterIP: "" - ## @param secondary.service.loadBalancerIP MySQL secondary loadBalancerIP if service type is `LoadBalancer` - ## Set the LoadBalancer service type to internal only - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer - ## - loadBalancerIP: "" - ## @param secondary.service.externalTrafficPolicy Enable client source IP preservation - ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip - ## - externalTrafficPolicy: Cluster - ## @param secondary.service.loadBalancerSourceRanges [array] Addresses that are allowed when MySQL secondary service is LoadBalancer - ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service - ## E.g. - ## loadBalancerSourceRanges: - ## - 10.10.10.0/24 - ## - loadBalancerSourceRanges: [] - ## @param secondary.service.annotations [object] Provide any additional annotations which may be required - ## - annotations: {} - ## MySQL secondary Pod Disruption Budget configuration - ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ - ## - pdb: - ## @param secondary.pdb.enabled Enable/disable a Pod Disruption Budget creation for MySQL secondary pods - ## - enabled: false - ## @param secondary.pdb.minAvailable Minimum number/percentage of MySQL secondary pods that should remain scheduled - ## - minAvailable: 1 - ## @param secondary.pdb.maxUnavailable Maximum number/percentage of MySQL secondary pods that may be made unavailable - ## - maxUnavailable: "" - ## @param secondary.podLabels [object] Additional pod labels for MySQL secondary pods - ## - podLabels: {} - -## @section RBAC parameters - -## MySQL pods ServiceAccount -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ -## -serviceAccount: - ## @param serviceAccount.create Enable the creation of a ServiceAccount for MySQL pods - ## - create: true - ## @param serviceAccount.name Name of the created ServiceAccount - ## If not set and create is true, a name is generated using the mysql.fullname template - ## - name: "" - ## @param serviceAccount.annotations [object] Annotations for MySQL Service Account - ## - annotations: {} -## Role Based Access -## ref: https://kubernetes.io/docs/admin/authorization/rbac/ -## -rbac: - ## @param rbac.create Whether to create & use RBAC resources or not - ## - create: false - -## @section Network Policy - -## MySQL Nework Policy configuration -## -networkPolicy: - ## @param networkPolicy.enabled Enable creation of NetworkPolicy resources - ## - enabled: false - ## @param networkPolicy.allowExternal The Policy model to apply. - ## When set to false, only pods with the correct - ## client label will have network access to the port MySQL is listening - ## on. When true, MySQL will accept connections from any source - ## (with the correct destination port). - ## - allowExternal: true - ## @param networkPolicy.explicitNamespacesSelector [object] A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed to MySQL - ## If explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace - ## and that match other criteria, the ones that have the good label, can reach the DB. - ## But sometimes, we want the DB to be accessible to clients from other namespaces, in this case, we can use this - ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. - ## - ## Example: - ## explicitNamespacesSelector: - ## matchLabels: - ## role: frontend - ## matchExpressions: - ## - {key: role, operator: In, values: [frontend]} - ## - explicitNamespacesSelector: {} - -## @section Volume Permissions parameters - -## Init containers parameters: -## volumePermissions: Change the owner and group of the persistent volume mountpoint to runAsUser:fsGroup values from the securityContext section. -## -volumePermissions: - ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` - ## - enabled: false - ## @param volumePermissions.image.registry Init container volume-permissions image registry - ## @param volumePermissions.image.repository Init container volume-permissions image repository - ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended) - ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy - ## @param volumePermissions.image.pullSecrets [array] Specify docker-registry secret names as an array - ## - image: - registry: docker.io - repository: bitnami/bitnami-shell - tag: 10-debian-10-r349 - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## e.g: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## @param volumePermissions.resources [object] Init container volume-permissions resources - ## - resources: {} - -## @section Metrics parameters - -## Mysqld Prometheus exporter parameters -## -metrics: - ## @param metrics.enabled Start a side-car prometheus exporter - ## - enabled: false - ## @param metrics.image.registry Exporter image registry - ## @param metrics.image.repository Exporter image repository - ## @param metrics.image.tag Exporter image tag (immutable tags are recommended) - ## @param metrics.image.pullPolicy Exporter image pull policy - ## @param metrics.image.pullSecrets [array] Specify docker-registry secret names as an array - ## - image: - registry: docker.io - repository: bitnami/mysqld-exporter - tag: 0.13.0-debian-10-r256 - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## e.g: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## MySQL Prometheus exporter service parameters - ## Mysqld Prometheus exporter liveness and readiness probes - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## @param metrics.service.type Kubernetes service type for MySQL Prometheus Exporter - ## @param metrics.service.port MySQL Prometheus Exporter service port - ## @param metrics.service.annotations [object] Prometheus exporter service annotations - ## - service: - type: ClusterIP - port: 9104 - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.service.port }}" - ## @param metrics.extraArgs.primary [array] Extra args to be passed to mysqld_exporter on Primary pods - ## @param metrics.extraArgs.secondary [array] Extra args to be passed to mysqld_exporter on Secondary pods - ## ref: https://github.com/prometheus/mysqld_exporter/ - ## E.g. - ## - --collect.auto_increment.columns - ## - --collect.binlog_size - ## - --collect.engine_innodb_status - ## - --collect.engine_tokudb_status - ## - --collect.global_status - ## - --collect.global_variables - ## - --collect.info_schema.clientstats - ## - --collect.info_schema.innodb_metrics - ## - --collect.info_schema.innodb_tablespaces - ## - --collect.info_schema.innodb_cmp - ## - --collect.info_schema.innodb_cmpmem - ## - --collect.info_schema.processlist - ## - --collect.info_schema.processlist.min_time - ## - --collect.info_schema.query_response_time - ## - --collect.info_schema.tables - ## - --collect.info_schema.tables.databases - ## - --collect.info_schema.tablestats - ## - --collect.info_schema.userstats - ## - --collect.perf_schema.eventsstatements - ## - --collect.perf_schema.eventsstatements.digest_text_limit - ## - --collect.perf_schema.eventsstatements.limit - ## - --collect.perf_schema.eventsstatements.timelimit - ## - --collect.perf_schema.eventswaits - ## - --collect.perf_schema.file_events - ## - --collect.perf_schema.file_instances - ## - --collect.perf_schema.indexiowaits - ## - --collect.perf_schema.tableiowaits - ## - --collect.perf_schema.tablelocks - ## - --collect.perf_schema.replication_group_member_stats - ## - --collect.slave_status - ## - --collect.slave_hosts - ## - --collect.heartbeat - ## - --collect.heartbeat.database - ## - --collect.heartbeat.table - ## - extraArgs: - primary: [] - secondary: [] - ## Mysqld Prometheus exporter resource requests and limits - ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ - ## We usually recommend not to specify default resources and to leave this as a conscious - ## choice for the user. This also increases chances charts run on environments with little - ## resources, such as Minikube. If you do want to specify resources, uncomment the following - ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. - ## @param metrics.resources.limits [object] The resources limits for MySQL prometheus exporter containers - ## @param metrics.resources.requests [object] The requested resources for MySQL prometheus exporter containers - ## - resources: - ## Example: - ## limits: - ## cpu: 100m - ## memory: 256Mi - limits: {} - ## Examples: - ## requests: - ## cpu: 100m - ## memory: 256Mi - requests: {} - ## Mysqld Prometheus exporter liveness probe - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## @param metrics.livenessProbe.enabled Enable livenessProbe - ## @param metrics.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe - ## @param metrics.livenessProbe.periodSeconds Period seconds for livenessProbe - ## @param metrics.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe - ## @param metrics.livenessProbe.failureThreshold Failure threshold for livenessProbe - ## @param metrics.livenessProbe.successThreshold Success threshold for livenessProbe - ## - livenessProbe: - enabled: true - initialDelaySeconds: 120 - periodSeconds: 10 - timeoutSeconds: 1 - successThreshold: 1 - failureThreshold: 3 - ## Mysqld Prometheus exporter readiness probe - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## @param metrics.readinessProbe.enabled Enable readinessProbe - ## @param metrics.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe - ## @param metrics.readinessProbe.periodSeconds Period seconds for readinessProbe - ## @param metrics.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe - ## @param metrics.readinessProbe.failureThreshold Failure threshold for readinessProbe - ## @param metrics.readinessProbe.successThreshold Success threshold for readinessProbe - ## - readinessProbe: - enabled: true - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 1 - successThreshold: 1 - failureThreshold: 3 - ## Prometheus Service Monitor - ## ref: https://github.com/coreos/prometheus-operator - ## - serviceMonitor: - ## @param metrics.serviceMonitor.enabled Create ServiceMonitor Resource for scraping metrics using PrometheusOperator - ## - enabled: false - ## @param metrics.serviceMonitor.namespace Specify the namespace in which the serviceMonitor resource will be created - ## - namespace: "" - ## @param metrics.serviceMonitor.interval Specify the interval at which metrics should be scraped - ## - interval: 30s - ## @param metrics.serviceMonitor.scrapeTimeout Specify the timeout after which the scrape is ended - ## e.g: - ## scrapeTimeout: 30s - ## - scrapeTimeout: "" - ## @param metrics.serviceMonitor.relabellings [array] Specify Metric Relabellings to add to the scrape endpoint - ## - relabellings: [] - ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint - ## - honorLabels: false - ## @param metrics.serviceMonitor.additionalLabels [object] Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with - ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec - ## - additionalLabels: {} diff --git a/pkg/iac/scanners/helm/test/option_test.go b/pkg/iac/scanners/helm/test/option_test.go deleted file mode 100644 index 05bf96ee01d3..000000000000 --- a/pkg/iac/scanners/helm/test/option_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package test - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/helm/parser" -) - -func Test_helm_parser_with_options_with_values_file(t *testing.T) { - - tests := []struct { - testName string - chartName string - valuesFile string - }{ - { - testName: "Parsing directory 'testchart'", - chartName: "testchart", - valuesFile: "values/values.yaml", - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - chartName := test.chartName - - t.Logf("Running test: %s", test.testName) - - var opts []parser.Option - - if test.valuesFile != "" { - opts = append(opts, parser.OptionWithValuesFile(test.valuesFile)) - } - - helmParser, err := parser.New(chartName, opts...) - require.NoError(t, err) - require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) - manifests, err := helmParser.RenderedChartFiles() - require.NoError(t, err) - - assert.Len(t, manifests, 3) - - for _, manifest := range manifests { - expectedPath := filepath.Join("testdata", "expected", "options", chartName, manifest.TemplateFilePath) - - expectedContent, err := os.ReadFile(expectedPath) - require.NoError(t, err) - - cleanExpected := strings.ReplaceAll(string(expectedContent), "\r\n", "\n") - cleanActual := strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n") - - assert.Equal(t, cleanExpected, cleanActual) - } - }) - } -} - -func Test_helm_parser_with_options_with_set_value(t *testing.T) { - - tests := []struct { - testName string - chartName string - valuesFile string - values string - }{ - { - testName: "Parsing directory 'testchart'", - chartName: "testchart", - values: "securityContext.runAsUser=0", - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - chartName := test.chartName - - t.Logf("Running test: %s", test.testName) - - var opts []parser.Option - - if test.valuesFile != "" { - opts = append(opts, parser.OptionWithValuesFile(test.valuesFile)) - } - - if test.values != "" { - opts = append(opts, parser.OptionWithValues(test.values)) - } - - helmParser, err := parser.New(chartName, opts...) - require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") - require.NoError(t, err) - manifests, err := helmParser.RenderedChartFiles() - require.NoError(t, err) - - assert.Len(t, manifests, 3) - - for _, manifest := range manifests { - expectedPath := filepath.Join("testdata", "expected", "options", chartName, manifest.TemplateFilePath) - - expectedContent, err := os.ReadFile(expectedPath) - require.NoError(t, err) - - cleanExpected := strings.ReplaceAll(string(expectedContent), "\r\n", "\n") - cleanActual := strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n") - - assert.Equal(t, cleanExpected, cleanActual) - } - }) - } -} - -func Test_helm_parser_with_options_with_api_versions(t *testing.T) { - - tests := []struct { - testName string - chartName string - apiVersions []string - }{ - { - testName: "Parsing directory 'with-api-version'", - chartName: "with-api-version", - apiVersions: []string{"policy/v1/PodDisruptionBudget"}, - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - chartName := test.chartName - - t.Logf("Running test: %s", test.testName) - - var opts []parser.Option - - if len(test.apiVersions) > 0 { - opts = append(opts, parser.OptionWithAPIVersions(test.apiVersions...)) - } - - helmParser, err := parser.New(chartName, opts...) - require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") - require.NoError(t, err) - manifests, err := helmParser.RenderedChartFiles() - require.NoError(t, err) - - assert.Len(t, manifests, 1) - - for _, manifest := range manifests { - expectedPath := filepath.Join("testdata", "expected", "options", chartName, manifest.TemplateFilePath) - - expectedContent, err := os.ReadFile(expectedPath) - require.NoError(t, err) - - cleanExpected := strings.TrimSpace(strings.ReplaceAll(string(expectedContent), "\r\n", "\n")) - cleanActual := strings.TrimSpace(strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n")) - - assert.Equal(t, cleanExpected, cleanActual) - } - }) - } -} - -func Test_helm_parser_with_options_with_kube_versions(t *testing.T) { - - tests := []struct { - testName string - chartName string - kubeVersion string - expectedError string - }{ - { - testName: "Parsing directory 'with-kube-version'", - chartName: "with-kube-version", - kubeVersion: "1.60", - }, - { - testName: "Parsing directory 'with-kube-version' with invalid kube version", - chartName: "with-kube-version", - kubeVersion: "a.b.c", - expectedError: "Invalid Semantic Version", - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - chartName := test.chartName - - t.Logf("Running test: %s", test.testName) - - var opts []parser.Option - - opts = append(opts, parser.OptionWithKubeVersion(test.kubeVersion)) - - helmParser, err := parser.New(chartName, opts...) - if test.expectedError != "" { - require.EqualError(t, err, test.expectedError) - return - } - require.NoError(t, err) - require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) - manifests, err := helmParser.RenderedChartFiles() - require.NoError(t, err) - - assert.Len(t, manifests, 1) - - for _, manifest := range manifests { - expectedPath := filepath.Join("testdata", "expected", "options", chartName, manifest.TemplateFilePath) - - expectedContent, err := os.ReadFile(expectedPath) - require.NoError(t, err) - - cleanExpected := strings.TrimSpace(strings.ReplaceAll(string(expectedContent), "\r\n", "\n")) - cleanActual := strings.TrimSpace(strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n")) - - assert.Equal(t, cleanExpected, cleanActual) - } - }) - } -} diff --git a/pkg/iac/scanners/helm/test/parser_test.go b/pkg/iac/scanners/helm/test/parser_test.go deleted file mode 100644 index 0116e8b7670b..000000000000 --- a/pkg/iac/scanners/helm/test/parser_test.go +++ /dev/null @@ -1,195 +0,0 @@ -package test - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/detection" - "github.com/aquasecurity/trivy/pkg/iac/scanners/helm/parser" -) - -func Test_helm_parser(t *testing.T) { - - tests := []struct { - testName string - chartName string - }{ - { - testName: "Parsing directory 'testchart'", - chartName: "testchart", - }, - { - testName: "Parsing directory with tarred dependency", - chartName: "with-tarred-dep", - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - chartName := test.chartName - helmParser, err := parser.New(chartName) - require.NoError(t, err) - require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS("testdata"), chartName)) - manifests, err := helmParser.RenderedChartFiles() - require.NoError(t, err) - - assert.Len(t, manifests, 3) - - for _, manifest := range manifests { - expectedPath := filepath.Join("testdata", "expected", chartName, manifest.TemplateFilePath) - - expectedContent, err := os.ReadFile(expectedPath) - require.NoError(t, err) - - got := strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n") - assert.Equal(t, strings.ReplaceAll(string(expectedContent), "\r\n", "\n"), got) - } - }) - } -} - -func Test_helm_parser_where_name_non_string(t *testing.T) { - - tests := []struct { - testName string - chartName string - }{ - { - testName: "Scanning chart with integer for name", - chartName: "numberName", - }, - } - - for _, test := range tests { - chartName := test.chartName - - t.Logf("Running test: %s", test.testName) - - helmParser, err := parser.New(chartName) - require.NoError(t, err) - require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) - } -} - -func Test_tar_is_chart(t *testing.T) { - - tests := []struct { - testName string - archiveFile string - isHelmChart bool - }{ - { - testName: "standard tarball", - archiveFile: "mysql-8.8.26.tar", - isHelmChart: true, - }, - { - testName: "gzip tarball with tar.gz extension", - archiveFile: "mysql-8.8.26.tar.gz", - isHelmChart: true, - }, - { - testName: "broken gzip tarball with tar.gz extension", - archiveFile: "aws-cluster-autoscaler-bad.tar.gz", - isHelmChart: true, - }, - { - testName: "gzip tarball with tgz extension", - archiveFile: "mysql-8.8.26.tgz", - isHelmChart: true, - }, - { - testName: "gzip tarball that has nothing of interest in it", - archiveFile: "nope.tgz", - isHelmChart: false, - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - testPath := filepath.Join("testdata", test.archiveFile) - file, err := os.Open(testPath) - require.NoError(t, err) - defer file.Close() - - assert.Equal(t, test.isHelmChart, detection.IsHelmChartArchive(test.archiveFile, file)) - }) - } -} - -func Test_helm_tarball_parser(t *testing.T) { - - tests := []struct { - testName string - chartName string - archiveFile string - }{ - { - testName: "standard tarball", - chartName: "mysql", - archiveFile: "mysql-8.8.26.tar", - }, - { - testName: "gzip tarball with tar.gz extension", - chartName: "mysql", - archiveFile: "mysql-8.8.26.tar.gz", - }, - { - testName: "gzip tarball with tgz extension", - chartName: "mysql", - archiveFile: "mysql-8.8.26.tgz", - }, - } - - for _, test := range tests { - - t.Logf("Running test: %s", test.testName) - - testPath := filepath.Join("testdata", test.archiveFile) - - testTemp := t.TempDir() - testFileName := filepath.Join(testTemp, test.archiveFile) - require.NoError(t, copyArchive(testPath, testFileName)) - - testFs := os.DirFS(testTemp) - - helmParser, err := parser.New(test.archiveFile) - require.NoError(t, err) - require.NoError(t, helmParser.ParseFS(context.TODO(), testFs, ".")) - - manifests, err := helmParser.RenderedChartFiles() - require.NoError(t, err) - - assert.Len(t, manifests, 6) - - oneOf := []string{ - "configmap.yaml", - "statefulset.yaml", - "svc-headless.yaml", - "svc.yaml", - "secrets.yaml", - "serviceaccount.yaml", - } - - for _, manifest := range manifests { - filename := filepath.Base(manifest.TemplateFilePath) - assert.Contains(t, oneOf, filename) - - if strings.HasSuffix(manifest.TemplateFilePath, "secrets.yaml") { - continue - } - expectedPath := filepath.Join("testdata", "expected", test.chartName, manifest.TemplateFilePath) - - expectedContent, err := os.ReadFile(expectedPath) - require.NoError(t, err) - - assert.Equal(t, strings.ReplaceAll(string(expectedContent), "\r\n", "\n"), strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n")) - } - } -} diff --git a/pkg/iac/scanners/helm/test/scanner_test.go b/pkg/iac/scanners/helm/test/scanner_test.go deleted file mode 100644 index 52c0aa33c6ae..000000000000 --- a/pkg/iac/scanners/helm/test/scanner_test.go +++ /dev/null @@ -1,356 +0,0 @@ -package test - -import ( - "context" - "io" - "os" - "path/filepath" - "runtime" - "sort" - "strings" - "testing" - - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scanners/helm" -) - -func Test_helm_scanner_with_archive(t *testing.T) { - // TODO(simar7): Figure out why this test fails on Winndows only - if runtime.GOOS == "windows" { - t.Skip("skipping test on windows") - } - - tests := []struct { - testName string - chartName string - path string - archiveName string - }{ - { - testName: "Parsing tarball 'mysql-8.8.26.tar'", - chartName: "mysql", - path: filepath.Join("testdata", "mysql-8.8.26.tar"), - archiveName: "mysql-8.8.26.tar", - }, - } - - for _, test := range tests { - t.Logf("Running test: %s", test.testName) - - helmScanner := helm.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)) - - testTemp := t.TempDir() - testFileName := filepath.Join(testTemp, test.archiveName) - require.NoError(t, copyArchive(test.path, testFileName)) - - testFs := os.DirFS(testTemp) - results, err := helmScanner.ScanFS(context.TODO(), testFs, ".") - require.NoError(t, err) - require.NotNil(t, results) - - failed := results.GetFailed() - assert.Len(t, failed, 13) - - visited := make(map[string]bool) - var errorCodes []string - for _, result := range failed { - id := result.Flatten().RuleID - if _, exists := visited[id]; !exists { - visited[id] = true - errorCodes = append(errorCodes, id) - } - } - assert.Len(t, errorCodes, 13) - - sort.Strings(errorCodes) - - assert.Equal(t, []string{ - "AVD-KSV-0001", "AVD-KSV-0003", - "AVD-KSV-0011", "AVD-KSV-0012", "AVD-KSV-0014", - "AVD-KSV-0015", "AVD-KSV-0016", "AVD-KSV-0018", - "AVD-KSV-0020", "AVD-KSV-0021", "AVD-KSV-0030", - "AVD-KSV-0104", "AVD-KSV-0106", - }, errorCodes) - } -} - -func Test_helm_scanner_with_missing_name_can_recover(t *testing.T) { - - tests := []struct { - testName string - chartName string - path string - archiveName string - }{ - { - testName: "Parsing tarball 'aws-cluster-autoscaler-bad.tar.gz'", - chartName: "aws-cluster-autoscaler", - path: filepath.Join("testdata", "aws-cluster-autoscaler-bad.tar.gz"), - archiveName: "aws-cluster-autoscaler-bad.tar.gz", - }, - } - - for _, test := range tests { - t.Logf("Running test: %s", test.testName) - - helmScanner := helm.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)) - - testTemp := t.TempDir() - testFileName := filepath.Join(testTemp, test.archiveName) - require.NoError(t, copyArchive(test.path, testFileName)) - - testFs := os.DirFS(testTemp) - _, err := helmScanner.ScanFS(context.TODO(), testFs, ".") - require.NoError(t, err) - } -} - -func Test_helm_scanner_with_dir(t *testing.T) { - // TODO(simar7): Figure out why this test fails on Winndows only - if runtime.GOOS == "windows" { - t.Skip("skipping test on windows") - } - - tests := []struct { - testName string - chartName string - }{ - { - testName: "Parsing directory testchart'", - chartName: "testchart", - }, - } - - for _, test := range tests { - - t.Logf("Running test: %s", test.testName) - - helmScanner := helm.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)) - - testFs := os.DirFS(filepath.Join("testdata", test.chartName)) - results, err := helmScanner.ScanFS(context.TODO(), testFs, ".") - require.NoError(t, err) - require.NotNil(t, results) - - failed := results.GetFailed() - assert.Len(t, failed, 14) - - visited := make(map[string]bool) - for _, result := range failed { - visited[result.Rule().AVDID] = true - } - errorCodes := lo.Keys(visited) - - assert.ElementsMatch(t, []string{ - "AVD-KSV-0001", "AVD-KSV-0003", - "AVD-KSV-0011", "AVD-KSV-0012", "AVD-KSV-0014", - "AVD-KSV-0015", "AVD-KSV-0016", - "AVD-KSV-0020", "AVD-KSV-0021", "AVD-KSV-0030", - "AVD-KSV-0104", "AVD-KSV-0106", - "AVD-KSV-0117", "AVD-KSV-0110", - }, errorCodes) - - ignored := results.GetIgnored() - assert.Len(t, ignored, 1) - - assert.Equal(t, "AVD-KSV-0018", ignored[0].Rule().AVDID) - assert.Equal(t, "templates/deployment.yaml", ignored[0].Metadata().Range().GetFilename()) - } -} - -func Test_helm_scanner_with_custom_policies(t *testing.T) { - // TODO(simar7): Figure out why this test fails on Winndows only - if runtime.GOOS == "windows" { - t.Skip("skipping test on windows") - } - - regoRule := ` -package user.kubernetes.ID001 - - -__rego_metadata__ := { - "id": "ID001", - "avd_id": "AVD-USR-ID001", - "title": "Services not allowed", - "severity": "LOW", - "description": "Services are not allowed because of some reasons.", -} - -__rego_input__ := { - "selector": [ - {"type": "kubernetes"}, - ], -} - -deny[res] { - input.kind == "Service" - msg := sprintf("Found service '%s' but services are not allowed", [input.metadata.name]) - res := result.new(msg, input) -} -` - tests := []struct { - testName string - chartName string - path string - archiveName string - }{ - { - testName: "Parsing tarball 'mysql-8.8.26.tar'", - chartName: "mysql", - path: filepath.Join("testdata", "mysql-8.8.26.tar"), - archiveName: "mysql-8.8.26.tar", - }, - } - - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - t.Logf("Running test: %s", test.testName) - - helmScanner := helm.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), - rego.WithPolicyDirs("rules"), - rego.WithPolicyNamespaces("user")) - - testTemp := t.TempDir() - testFileName := filepath.Join(testTemp, test.archiveName) - require.NoError(t, copyArchive(test.path, testFileName)) - - policyDirName := filepath.Join(testTemp, "rules") - require.NoError(t, os.Mkdir(policyDirName, 0o700)) - require.NoError(t, os.WriteFile(filepath.Join(policyDirName, "rule.rego"), []byte(regoRule), 0o600)) - - testFs := os.DirFS(testTemp) - - results, err := helmScanner.ScanFS(context.TODO(), testFs, ".") - require.NoError(t, err) - require.NotNil(t, results) - - failed := results.GetFailed() - assert.Len(t, failed, 15) - - visited := make(map[string]bool) - for _, result := range failed { - visited[result.Rule().AVDID] = true - } - errorCodes := lo.Keys(visited) - - assert.ElementsMatch(t, []string{ - "AVD-KSV-0001", "AVD-KSV-0003", - "AVD-KSV-0011", "AVD-KSV-0012", "AVD-KSV-0014", - "AVD-KSV-0015", "AVD-KSV-0016", "AVD-KSV-0018", - "AVD-KSV-0020", "AVD-KSV-0021", "AVD-KSV-0030", - "AVD-KSV-0104", "AVD-KSV-0106", "AVD-USR-ID001", - }, errorCodes) - }) - } -} - -func copyArchive(src, dst string) error { - in, err := os.Open(src) - if err != nil { - return err - } - defer func() { _ = in.Close() }() - - out, err := os.Create(dst) - if err != nil { - return err - } - defer func() { _ = out.Close() }() - - if _, err := io.Copy(out, in); err != nil { - return err - } - return nil -} - -func Test_helm_chart_with_templated_name(t *testing.T) { - helmScanner := helm.New(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)) - testFs := os.DirFS(filepath.Join("testdata", "templated-name")) - _, err := helmScanner.ScanFS(context.TODO(), testFs, ".") - require.NoError(t, err) -} - -func TestCodeShouldNotBeMissing(t *testing.T) { - policy := `# METADATA -# title: "Test rego" -# description: "Test rego" -# scope: package -# schemas: -# - input: schema["kubernetes"] -# custom: -# id: ID001 -# avd_id: AVD-USR-ID001 -# severity: LOW -# input: -# selector: -# - type: kubernetes -package user.kubernetes.ID001 - -deny[res] { - input.spec.replicas == 3 - res := result.new("Replicas are not allowed", input) -} -` - helmScanner := helm.New( - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(false), - rego.WithPolicyNamespaces("user"), - rego.WithPolicyReader(strings.NewReader(policy)), - ) - - results, err := helmScanner.ScanFS(context.TODO(), os.DirFS("testdata/simmilar-templates"), ".") - require.NoError(t, err) - - failedResults := results.GetFailed() - require.Len(t, failedResults, 1) - - failed := failedResults[0] - code, err := failed.GetCode() - require.NoError(t, err) - assert.NotNil(t, code) -} - -func TestScanSubchartOnce(t *testing.T) { - check := `# METADATA -# title: "Test rego" -# description: "Test rego" -# scope: package -# schemas: -# - input: schema["kubernetes"] -# custom: -# id: ID001 -# avd_id: AVD-USR-ID001 -# severity: LOW -# input: -# selector: -# - type: kubernetes -# subtypes: -# - kind: pod -package user.kubernetes.ID001 - -import data.lib.kubernetes - -deny[res] { - container := kubernetes.containers[_] - container.securityContext.readOnlyRootFilesystem == false - res := result.new("set 'securityContext.readOnlyRootFilesystem' to true", container) -} -` - - scanner := helm.New( - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(true), - rego.WithPolicyNamespaces("user"), - rego.WithPolicyReader(strings.NewReader(check)), - ) - - results, err := scanner.ScanFS(context.TODO(), os.DirFS("testdata/with-subchart"), ".") - require.NoError(t, err) - require.Len(t, results, 1) - - assert.Empty(t, results.GetFailed()) -} diff --git a/pkg/iac/scanners/helm/test/testdata/aws-cluster-autoscaler-bad.tar.gz b/pkg/iac/scanners/helm/test/testdata/aws-cluster-autoscaler-bad.tar.gz deleted file mode 100644 index a66f228c9851..000000000000 Binary files a/pkg/iac/scanners/helm/test/testdata/aws-cluster-autoscaler-bad.tar.gz and /dev/null differ diff --git a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/configmap.yaml b/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/configmap.yaml deleted file mode 100644 index 9ee00d2c2c0c..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/configmap.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# Source: mysql/templates/primary/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: mysql - namespace: - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/component: primary -data: - my.cnf: |- - - [mysqld] - default_authentication_plugin=mysql_native_password - skip-name-resolve - explicit_defaults_for_timestamp - basedir=/opt/bitnami/mysql - plugin_dir=/opt/bitnami/mysql/lib/plugin - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - datadir=/bitnami/mysql/data - tmpdir=/opt/bitnami/mysql/tmp - max_allowed_packet=16M - bind-address=0.0.0.0 - pid-file=/opt/bitnami/mysql/tmp/mysqld.pid - log-error=/opt/bitnami/mysql/logs/mysqld.log - character-set-server=UTF8 - collation-server=utf8_general_ci - - [client] - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - default-character-set=UTF8 - plugin_dir=/opt/bitnami/mysql/lib/plugin - - [manager] - port=3306 - socket=/opt/bitnami/mysql/tmp/mysql.sock - pid-file=/opt/bitnami/mysql/tmp/mysqld.pid \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/statefulset.yaml b/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/statefulset.yaml deleted file mode 100644 index a7f5f59d831b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/statefulset.yaml +++ /dev/null @@ -1,147 +0,0 @@ -# Source: mysql/templates/primary/statefulset.yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: mysql - namespace: - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/component: primary -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: mysql - app.kubernetes.io/instance: mysql - app.kubernetes.io/component: primary - serviceName: mysql - updateStrategy: - type: RollingUpdate - template: - metadata: - annotations: - checksum/configuration: 6adfba795651cd736dfa943a87e0853ce417b9fb842b57535e3b1b4e762a33fd - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/component: primary - spec: - - serviceAccountName: mysql - affinity: - podAffinity: - - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/name: mysql - app.kubernetes.io/instance: mysql - app.kubernetes.io/component: primary - namespaces: - - "" - topologyKey: kubernetes.io/hostname - weight: 1 - nodeAffinity: - - securityContext: - fsGroup: 1001 - containers: - - name: mysql - image: docker.io/bitnami/mysql:8.0.28-debian-10-r23 - imagePullPolicy: "IfNotPresent" - securityContext: - runAsUser: 1001 - env: - - name: BITNAMI_DEBUG - value: "false" - - name: MYSQL_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: mysql - key: mysql-root-password - - name: MYSQL_DATABASE - value: "my_database" - ports: - - name: mysql - containerPort: 3306 - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 5 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - readinessProbe: - failureThreshold: 3 - initialDelaySeconds: 5 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - startupProbe: - failureThreshold: 10 - initialDelaySeconds: 15 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - exec: - command: - - /bin/bash - - -ec - - | - password_aux="${MYSQL_ROOT_PASSWORD:-}" - if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then - password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") - fi - mysqladmin status -uroot -p"${password_aux}" - resources: - limits: {} - requests: {} - volumeMounts: - - name: data - mountPath: /bitnami/mysql - - name: config - mountPath: /opt/bitnami/mysql/conf/my.cnf - subPath: my.cnf - volumes: - - name: config - configMap: - name: mysql - volumeClaimTemplates: - - metadata: - name: data - labels: - app.kubernetes.io/name: mysql - app.kubernetes.io/instance: mysql - app.kubernetes.io/component: primary - spec: - accessModes: - - "ReadWriteOnce" - resources: - requests: - storage: "8Gi" \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/svc-headless.yaml b/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/svc-headless.yaml deleted file mode 100644 index 9fe0f11c87ae..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/svc-headless.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Source: mysql/templates/primary/svc-headless.yaml -apiVersion: v1 -kind: Service -metadata: - name: mysql-headless - namespace: - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/component: primary - annotations: -spec: - type: ClusterIP - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: mysql - port: 3306 - targetPort: mysql - selector: - app.kubernetes.io/name: mysql - app.kubernetes.io/instance: mysql - app.kubernetes.io/component: primary \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/svc.yaml b/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/svc.yaml deleted file mode 100644 index 2bbdab8fe468..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/primary/svc.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Source: mysql/templates/primary/svc.yaml -apiVersion: v1 -kind: Service -metadata: - name: mysql - namespace: - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/component: primary - annotations: -spec: - type: ClusterIP - ports: - - name: mysql - port: 3306 - protocol: TCP - targetPort: mysql - nodePort: null - selector: - app.kubernetes.io/name: mysql - app.kubernetes.io/instance: mysql - app.kubernetes.io/component: primary \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/secrets.yaml b/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/secrets.yaml deleted file mode 100644 index ffa6909e2f04..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/secrets.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Source: mysql/templates/secrets.yaml -apiVersion: v1 -kind: Secret -metadata: - name: mysql - namespace: - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm -type: Opaque -data: - mysql-root-password: "aGZYYW1vN3V5NA==" - mysql-password: "eHR6YU9MR1VhbA==" \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/serviceaccount.yaml b/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/serviceaccount.yaml deleted file mode 100644 index 760b8bf731a5..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/mysql/templates/serviceaccount.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Source: mysql/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: mysql - namespace: - labels: - app.kubernetes.io/name: mysql - helm.sh/chart: mysql-8.8.26 - app.kubernetes.io/instance: mysql - app.kubernetes.io/managed-by: Helm - annotations: -secrets: - - name: mysql \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml deleted file mode 100644 index 1280e673b92a..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# Source: testchart/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: testchart - labels: - helm.sh/chart: testchart-0.1.0 - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - template: - metadata: - labels: - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - spec: - serviceAccountName: testchart - securityContext: - {} - containers: - # trivy:ignore:KSV018 - - name: testchart - securityContext: - runAsUser: 0 - image: "nginx:1.16.0" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {} \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/service.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/service.yaml deleted file mode 100644 index 6c6699f3d5dd..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Source: testchart/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: testchart - labels: - helm.sh/chart: testchart-0.1.0 - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: http - protocol: TCP - name: http - selector: - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/serviceaccount.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/serviceaccount.yaml deleted file mode 100644 index 6fe44a89bb3b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Source: testchart/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: testchart - labels: - helm.sh/chart: testchart-0.1.0 - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/with-api-version/templates/pdb.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/with-api-version/templates/pdb.yaml deleted file mode 100644 index 7c7ef5fd74d7..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/options/with-api-version/templates/pdb.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Source: with-api-version/templates/pdb.yaml -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: with-api-version - labels: - helm.sh/chart: with-api-version-0.1.0 - app.kubernetes.io/name: with-api-version - app.kubernetes.io/instance: with-api-version - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm -spec: - selector: - matchLabels: - app.kubernetes.io/name: with-api-version - app.kubernetes.io/instance: with-api-version - maxUnavailable: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml deleted file mode 100644 index 7c7ef5fd74d7..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Source: with-api-version/templates/pdb.yaml -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: with-api-version - labels: - helm.sh/chart: with-api-version-0.1.0 - app.kubernetes.io/name: with-api-version - app.kubernetes.io/instance: with-api-version - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm -spec: - selector: - matchLabels: - app.kubernetes.io/name: with-api-version - app.kubernetes.io/instance: with-api-version - maxUnavailable: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml deleted file mode 100644 index 9a2e277c106a..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# Source: testchart/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: testchart - labels: - helm.sh/chart: testchart-0.1.0 - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - template: - metadata: - labels: - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - spec: - serviceAccountName: testchart - securityContext: - {} - containers: - # trivy:ignore:KSV018 - - name: testchart - securityContext: - {} - image: "nginx:1.16.0" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {} \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/service.yaml b/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/service.yaml deleted file mode 100644 index 6c6699f3d5dd..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Source: testchart/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: testchart - labels: - helm.sh/chart: testchart-0.1.0 - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: http - protocol: TCP - name: http - selector: - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/serviceaccount.yaml b/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/serviceaccount.yaml deleted file mode 100644 index 6fe44a89bb3b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Source: testchart/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: testchart - labels: - helm.sh/chart: testchart-0.1.0 - app.kubernetes.io/name: testchart - app.kubernetes.io/instance: testchart - app.kubernetes.io/version: "1.16.0" - app.kubernetes.io/managed-by: Helm \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/deployment.yaml deleted file mode 100644 index ed57d12a6e2b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/deployment.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# Source: with-tarred-dep/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: with-tarred-dep - labels: - app.kubernetes.io/name: with-tarred-dep - helm.sh/chart: with-tarred-dep-0.1.1 - app.kubernetes.io/instance: with-tarred-dep - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: with-tarred-dep - app.kubernetes.io/instance: with-tarred-dep - template: - metadata: - labels: - app.kubernetes.io/name: with-tarred-dep - helm.sh/chart: with-tarred-dep-0.1.1 - app.kubernetes.io/instance: with-tarred-dep - app.kubernetes.io/managed-by: Helm - spec: - containers: - - name: metadata-service - env: - - name: METADATASERVICE_UPSTREAM_API_URL - value: '' - - name: METADATASERVICE_OIDC_AUDIENCE - value: "" - - name: METADATASERVICE_OIDC_ISSUER - value: "" - - name: METADATASERVICE_OIDC_JWKSURI - value: "" - - name: METADATASERVICE_OIDC_CLAIMS_ROLES - value: "" - - name: METADATASERVICE_OIDC_CLAIMS_USERNAME - value: "" - - name: METADATASERVICE_DB_URI - valueFrom: - secretKeyRef: - name: with-tarred-dep-dbconn - key: uri - image: "ghcr.io/metal-toolbox/hollow-metadataservice:v0.0.1" - imagePullPolicy: Always - volumeMounts: - - name: dbcerts - mountPath: "/dbcerts" - readOnly: true - ports: - - name: http - containerPort: 8000 - protocol: TCP - livenessProbe: - httpGet: - path: /healthz/liveness - port: http - initialDelaySeconds: 5 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz/readiness - port: http - initialDelaySeconds: 5 - timeoutSeconds: 2 - resources: - limits: - cpu: 4 - memory: 4Gi - requests: - cpu: 4 - memory: 4Gi - volumes: - - name: dbcerts - secret: - secretName: with-tarred-dep-crdb-ca - defaultMode: 0400 \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/ingress.yaml b/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/ingress.yaml deleted file mode 100644 index b48564477997..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/ingress.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Source: with-tarred-dep/templates/ingress.yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: with-tarred-dep - labels: - app.kubernetes.io/name: with-tarred-dep - helm.sh/chart: with-tarred-dep-0.1.1 - app.kubernetes.io/instance: with-tarred-dep - app.kubernetes.io/managed-by: Helm -spec: - rules: - - host: metadata-service.mydomain - http: - paths: - - path: /($|metadata|userdata|2009-04-04) - pathType: Prefix - backend: - service: - name: with-tarred-dep - port: - name: http -# tls: [] -# hosts: -# - hollow-metadataservice.mydomain -# secretName: hollow-metadataservice-example-tls \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/service.yaml b/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/service.yaml deleted file mode 100644 index 7d86aeb5b02b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/expected/with-tarred-dep/templates/service.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Source: with-tarred-dep/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: with-tarred-dep - labels: - app.kubernetes.io/name: with-tarred-dep - helm.sh/chart: with-tarred-dep-0.1.1 - app.kubernetes.io/instance: with-tarred-dep - app.kubernetes.io/managed-by: Helm -spec: - ports: - - name: http - port: 80 - protocol: TCP - targetPort: 8000 - - name: https - port: 443 - protocol: TCP - targetPort: 8000 - selector: - app.kubernetes.io/name: with-tarred-dep - app.kubernetes.io/instance: with-tarred-dep - type: ClusterIP \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tar b/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tar deleted file mode 100644 index 53cb6802de42..000000000000 Binary files a/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tar and /dev/null differ diff --git a/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tar.gz b/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tar.gz deleted file mode 100644 index ff8bd1ab402e..000000000000 Binary files a/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tar.gz and /dev/null differ diff --git a/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tgz b/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tgz deleted file mode 100644 index ff8bd1ab402e..000000000000 Binary files a/pkg/iac/scanners/helm/test/testdata/mysql-8.8.26.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/test/testdata/nope.tgz b/pkg/iac/scanners/helm/test/testdata/nope.tgz deleted file mode 100644 index a47332d93877..000000000000 Binary files a/pkg/iac/scanners/helm/test/testdata/nope.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/test/testdata/numberName/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/numberName/Chart.yaml deleted file mode 100644 index e840fbabf456..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/numberName/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -apiVersion: v2 -name: 1001 -version: 1.0.0 \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/simmilar-templates/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/simmilar-templates/Chart.yaml deleted file mode 100644 index e5855a786639..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/simmilar-templates/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -name: test-license-manager -version: 1.10.0 -type: application -appVersion: 1.10.0 -deprecated: false diff --git a/pkg/iac/scanners/helm/test/testdata/simmilar-templates/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/simmilar-templates/templates/deployment.yaml deleted file mode 100644 index eb2b2a343d51..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/simmilar-templates/templates/deployment.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -spec: - replicas: 3 - selector: - matchLabels: - app: myapp - template: - metadata: - labels: - app: myapp - spec: - containers: - - name: myapp - image: test:latest - resources: - limits: - memory: "128Mi" - cpu: "500m" - ports: - - containerPort: 80 diff --git a/pkg/iac/scanners/helm/test/testdata/simmilar-templates/templates/manifest.yaml b/pkg/iac/scanners/helm/test/testdata/simmilar-templates/templates/manifest.yaml deleted file mode 100644 index 49d9df010f82..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/simmilar-templates/templates/manifest.yaml +++ /dev/null @@ -1,2 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment diff --git a/pkg/iac/scanners/helm/test/testdata/templated-name/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/templated-name/Chart.yaml deleted file mode 100644 index e675643d99c9..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/templated-name/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v2 -name: {{COMPONENT_NAME}} -version: {{COMPONENT_VERSION}} -description: A Helm chart for Kubernetes -keywords: - - kublr - - audit diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/.helmignore b/pkg/iac/scanners/helm/test/testdata/testchart/.helmignore deleted file mode 100644 index 0e8a0eb36f4c..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/Chart.yaml deleted file mode 100644 index 0ffb7d074a72..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: testchart -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/NOTES.txt b/pkg/iac/scanners/helm/test/testdata/testchart/templates/NOTES.txt deleted file mode 100644 index 45e51670a862..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/NOTES.txt +++ /dev/null @@ -1,22 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "testchart.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "testchart.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "testchart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "testchart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/_helpers.tpl b/pkg/iac/scanners/helm/test/testdata/testchart/templates/_helpers.tpl deleted file mode 100644 index 4b0db05bf5f5..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "testchart.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "testchart.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "testchart.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "testchart.labels" -}} -helm.sh/chart: {{ include "testchart.chart" . }} -{{ include "testchart.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "testchart.selectorLabels" -}} -app.kubernetes.io/name: {{ include "testchart.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "testchart.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "testchart.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml deleted file mode 100644 index 4f81b4f85eb9..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "testchart.fullname" . }} - labels: - {{- include "testchart.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "testchart.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "testchart.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "testchart.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - # trivy:ignore:KSV018 - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/hpa.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/hpa.yaml deleted file mode 100644 index 51734471d41d..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/hpa.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "testchart.fullname" . }} - labels: - {{- include "testchart.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "testchart.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/ingress.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/ingress.yaml deleted file mode 100644 index 9732d2a24a14..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/ingress.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "testchart.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "testchart.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/service.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/service.yaml deleted file mode 100644 index 86baf148215d..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "testchart.fullname" . }} - labels: - {{- include "testchart.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "testchart.selectorLabels" . | nindent 4 }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/serviceaccount.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/serviceaccount.yaml deleted file mode 100644 index f728deb2a6bb..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "testchart.serviceAccountName" . }} - labels: - {{- include "testchart.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/tests/test-connection.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/tests/test-connection.yaml deleted file mode 100644 index a391ef1c462f..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "testchart.fullname" . }}-test-connection" - labels: - {{- include "testchart.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "testchart.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/values.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/values.yaml deleted file mode 100644 index 4acdf3c931bd..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/testchart/values.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# Default values for testchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: - {} - # fsGroup: 2000 - -securityContext: - {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: - {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/pkg/iac/scanners/helm/test/testdata/with-api-version/.helmignore b/pkg/iac/scanners/helm/test/testdata/with-api-version/.helmignore deleted file mode 100644 index 0e8a0eb36f4c..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-api-version/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/pkg/iac/scanners/helm/test/testdata/with-api-version/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/with-api-version/Chart.yaml deleted file mode 100644 index 22dab35d32f4..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-api-version/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: with-api-version -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" diff --git a/pkg/iac/scanners/helm/test/testdata/with-api-version/templates/_helpers.tpl b/pkg/iac/scanners/helm/test/testdata/with-api-version/templates/_helpers.tpl deleted file mode 100644 index cab726131dc5..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-api-version/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "with-api-version.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "with-api-version.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "with-api-version.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "with-api-version.labels" -}} -helm.sh/chart: {{ include "with-api-version.chart" . }} -{{ include "with-api-version.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "with-api-version.selectorLabels" -}} -app.kubernetes.io/name: {{ include "with-api-version.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "with-api-version.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "with-api-version.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-api-version/templates/pdb.yaml b/pkg/iac/scanners/helm/test/testdata/with-api-version/templates/pdb.yaml deleted file mode 100644 index a0a54cbc232b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-api-version/templates/pdb.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: {{ $.Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" | ternary "policy/v1" "policy/v1beta1" }} -kind: PodDisruptionBudget -metadata: - name: {{ include "with-api-version.fullname" . }} - labels: - {{- include "with-api-version.labels" . | nindent 4 }} -spec: - selector: - matchLabels: - {{- include "with-api-version.selectorLabels" . | nindent 6 }} - maxUnavailable: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/with-api-version/values.yaml b/pkg/iac/scanners/helm/test/testdata/with-api-version/values.yaml deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore b/pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore deleted file mode 100644 index 0e8a0eb36f4c..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml deleted file mode 100644 index 99c44c125940..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v2 -name: with-api-version -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" - -kubeVersion: ">=1.60.0-0" diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl b/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl deleted file mode 100644 index cab726131dc5..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "with-api-version.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "with-api-version.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "with-api-version.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "with-api-version.labels" -}} -helm.sh/chart: {{ include "with-api-version.chart" . }} -{{ include "with-api-version.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "with-api-version.selectorLabels" -}} -app.kubernetes.io/name: {{ include "with-api-version.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "with-api-version.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "with-api-version.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml b/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml deleted file mode 100644 index 0c063e06df97..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: {{ include "with-api-version.fullname" . }} - labels: - {{- include "with-api-version.labels" . | nindent 4 }} -spec: - selector: - matchLabels: - {{- include "with-api-version.selectorLabels" . | nindent 6 }} - maxUnavailable: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/values.yaml b/pkg/iac/scanners/helm/test/testdata/with-kube-version/values.yaml deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/pkg/iac/scanners/helm/test/testdata/with-subchart/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/with-subchart/Chart.yaml deleted file mode 100644 index 3c8c9b71ae45..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-subchart/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -name: test -description: A Helm chart for Kubernetes -type: application -version: 0.1.0 -appVersion: "1.16.0" diff --git a/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/Chart.yaml deleted file mode 100644 index 45cdc636218e..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -name: nginx -description: A Helm chart for Kubernetes -type: application -version: 0.1.0 -appVersion: "1.16.0" diff --git a/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/templates/pod.yaml b/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/templates/pod.yaml deleted file mode 100644 index 70b3a84a8130..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/templates/pod.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: nginx -spec: - containers: - - name: nginx - image: nginx:1.14.2 - ports: - - containerPort: 8080 - securityContext: - readOnlyRootFilesystem: {{ .Values.readOnlyFs }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/values.yaml b/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/values.yaml deleted file mode 100644 index ff3cff9db1e3..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-subchart/charts/nginx/values.yaml +++ /dev/null @@ -1 +0,0 @@ -readOnlyFs: false diff --git a/pkg/iac/scanners/helm/test/testdata/with-subchart/values.yaml b/pkg/iac/scanners/helm/test/testdata/with-subchart/values.yaml deleted file mode 100644 index 1e51a8fed1da..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-subchart/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -nginx: - readOnlyFs: true diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/.helmignore b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/.helmignore deleted file mode 100644 index 50af03172541..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/Chart.yaml deleted file mode 100644 index bd163a944cae..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v2 -name: with-tarred-dep -description: Test With Tarred Dependencies -type: application -version: 0.1.1 -appVersion: "1.0" -sources: - - https://github.com/test/with-tarred-dep -dependencies: - - name: common - repository: https://charts.bitnami.com/bitnami - tags: - - bitnami-common - version: 1.16.1 diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/LICENSE b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/LICENSE deleted file mode 100644 index 261eeb9e9f8b..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/charts/common-1.16.1.tgz b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/charts/common-1.16.1.tgz deleted file mode 100644 index 6a2df2e15b93..000000000000 Binary files a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/charts/common-1.16.1.tgz and /dev/null differ diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/renovate.json b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/renovate.json deleted file mode 100644 index a78e667b7736..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/renovate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "config:base" - ] - } \ No newline at end of file diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/.gitkeep b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/deployment.yaml deleted file mode 100644 index 003d08eb745d..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/deployment.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "common.names.fullname" . }} - labels: {{- include "common.labels.standard" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} - template: - metadata: - labels: {{- include "common.labels.standard" . | nindent 8 }} - spec: - containers: - - name: metadata-service - env: - - name: METADATASERVICE_UPSTREAM_API_URL - value: '{{ .Values.upstreamAPI }}' - - name: METADATASERVICE_OIDC_AUDIENCE - value: "{{ .Values.oidc.audience }}" - - name: METADATASERVICE_OIDC_ISSUER - value: "{{ .Values.oidc.issuer }}" - - name: METADATASERVICE_OIDC_JWKSURI - value: "{{ .Values.oidc.jwksuri }}" - - name: METADATASERVICE_OIDC_CLAIMS_ROLES - value: "{{ .Values.oidc.rolesClaim }}" - - name: METADATASERVICE_OIDC_CLAIMS_USERNAME - value: "{{ .Values.oidc.userClaim }}" - - name: METADATASERVICE_DB_URI - valueFrom: - secretKeyRef: - name: {{ template "common.names.fullname" . }}-dbconn - key: uri - image: "{{ .Values.metadataservice.image.repository }}:{{ .Values.metadataservice.image.tag }}" - imagePullPolicy: Always - volumeMounts: - - name: dbcerts - mountPath: "/dbcerts" - readOnly: true - ports: - - name: http - containerPort: 8000 - protocol: TCP - livenessProbe: - httpGet: - path: /healthz/liveness - port: http - initialDelaySeconds: 5 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz/readiness - port: http - initialDelaySeconds: 5 - timeoutSeconds: 2 - resources: -{{ toYaml .Values.resources | indent 12 }} - volumes: - - name: dbcerts - secret: - secretName: {{ template "common.names.fullname" . }}-crdb-ca - defaultMode: 0400 diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/ingress.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/ingress.yaml deleted file mode 100644 index 45cd321ca9a9..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/ingress.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{- if .Values.ingress.enabled }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ template "common.names.fullname" . }} - labels: {{- include "common.labels.standard" . | nindent 4 }} -spec: - {{- if and .Values.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} - {{- end }} - rules: - {{- range .Values.ingress.hostnames }} - - host: {{ . }} - http: - paths: - - path: / - {{- if $.Values.ingress.publicPaths -}} - ( - {{- range $index,$path := $.Values.ingress.publicPaths }} - {{- if $index }}|{{ end }} - {{- $path }} - {{- end -}} - ) - {{- end }} - pathType: Prefix - backend: - service: - name: {{ template "common.names.fullname" $ }} - port: - name: http - {{- end }} -# tls: [] -# hosts: -# - hollow-metadataservice.mydomain -# secretName: hollow-metadataservice-example-tls -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/secrets-crdb-ca.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/secrets-crdb-ca.yaml deleted file mode 100644 index 18c39c058dcd..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/secrets-crdb-ca.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.crdbCA }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "common.names.fullname" . }}-crdb-ca - namespace: {{ .Release.Namespace | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: Opaque -data: - ca.crt: {{ .Values.crdbCA | b64enc | quote }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/secrets-dbconn.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/secrets-dbconn.yaml deleted file mode 100644 index 06c93061d08c..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/secrets-dbconn.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.dbconnURI }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "common.names.fullname" . }}-dbconn - namespace: {{ .Release.Namespace | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: Opaque -data: - uri: {{ .Values.dbconnURI | b64enc | quote }} -{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/service.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/service.yaml deleted file mode 100644 index fdb8b82d76f8..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/templates/service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "common.names.fullname" . }} - labels: {{- include "common.labels.standard" . | nindent 4 }} -spec: - ports: - - name: http - port: 80 - protocol: TCP - targetPort: 8000 - - name: https - port: 443 - protocol: TCP - targetPort: 8000 - selector:{{ include "common.labels.matchLabels" . | nindent 4 }} - type: ClusterIP diff --git a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/values.yaml b/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/values.yaml deleted file mode 100644 index 7a86583f54e3..000000000000 --- a/pkg/iac/scanners/helm/test/testdata/with-tarred-dep/values.yaml +++ /dev/null @@ -1,30 +0,0 @@ -metadataservice: - image: - repository: ghcr.io/metal-toolbox/hollow-metadataservice - tag: "v0.0.1" - -ingress: - enabled: true - hostnames: - - metadata-service.mydomain - publicPaths: - - $ - - metadata - - userdata - - '2009-04-04' - -oidc: - audience: "" - issuer: "" - jwksuri: "" - rolesClaim: "" - userClaim: "" - -replicaCount: 1 -resources: - limits: - cpu: 4 - memory: 4Gi - requests: - cpu: 4 - memory: 4Gi diff --git a/pkg/iac/scanners/helm/test/values/values.yaml b/pkg/iac/scanners/helm/test/values/values.yaml deleted file mode 100644 index 6f637160ffa9..000000000000 --- a/pkg/iac/scanners/helm/test/values/values.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -securityContext: - runAsUser: 0 \ No newline at end of file diff --git a/pkg/iac/scanners/kubernetes/parser/manifest.go b/pkg/iac/scanners/kubernetes/parser/manifest.go deleted file mode 100644 index 568b0e0e74a1..000000000000 --- a/pkg/iac/scanners/kubernetes/parser/manifest.go +++ /dev/null @@ -1,104 +0,0 @@ -package parser - -import ( - "bytes" - "errors" - "fmt" - "io" - "reflect" - - "github.com/go-json-experiment/json" - "github.com/go-json-experiment/json/jsontext" - "gopkg.in/yaml.v3" -) - -type Manifest struct { - Path string - Content *ManifestNode -} - -func (m *Manifest) UnmarshalYAML(value *yaml.Node) error { - - switch value.Tag { - case string(TagMap): - node := new(ManifestNode) - node.Path = m.Path - if err := value.Decode(node); err != nil { - return err - } - m.Content = node - default: - return fmt.Errorf("failed to handle tag: %s", value.Tag) - } - - return nil -} - -func (m *Manifest) ToRego() any { - return m.Content.ToRego() -} - -func ManifestFromJSON(path string, data []byte) (*Manifest, error) { - root := &ManifestNode{ - Path: path, - } - - if err := json.Unmarshal(data, root, json.WithUnmarshalers( - json.UnmarshalFromFunc(func(dec *jsontext.Decoder, node *ManifestNode, opts json.Options) error { - startOffset := dec.InputOffset() - if err := unmarshalManifestNode(dec, node); err != nil { - return err - } - endOffset := dec.InputOffset() - node.StartLine = 1 + countLines(data, int(startOffset)) - node.EndLine = 1 + countLines(data, int(endOffset)) - node.Path = path - return nil - })), - ); err != nil && !errors.Is(err, io.EOF) { - return nil, err - } - - return &Manifest{ - Path: path, - Content: root, - }, nil -} - -func unmarshalManifestNode(dec *jsontext.Decoder, node *ManifestNode) error { - var valPtr any - var nodeType TagType - switch k := dec.PeekKind(); k { - case 't', 'f': - valPtr = new(bool) - nodeType = TagBool - case '"': - nodeType = TagStr - valPtr = new(string) - case '0': - nodeType = TagInt - valPtr = new(uint64) - case '[', 'n': - valPtr = new([]*ManifestNode) - nodeType = TagSlice - case '{': - valPtr = new(map[string]*ManifestNode) - nodeType = TagMap - case 0: - return dec.SkipValue() - default: - return fmt.Errorf("unexpected token kind %q at %d", k.String(), dec.InputOffset()) - } - - if err := json.UnmarshalDecode(dec, valPtr); err != nil { - return err - } - - node.Value = reflect.ValueOf(valPtr).Elem().Interface() - node.Type = nodeType - return nil -} - -func countLines(data []byte, offset int) int { - return bytes.Count(data[:offset], []byte("\n")) -} diff --git a/pkg/iac/scanners/kubernetes/parser/manifest_node.go b/pkg/iac/scanners/kubernetes/parser/manifest_node.go deleted file mode 100644 index 4a047b264317..000000000000 --- a/pkg/iac/scanners/kubernetes/parser/manifest_node.go +++ /dev/null @@ -1,165 +0,0 @@ -package parser - -import ( - "encoding/base64" - "fmt" - "strconv" - "time" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type TagType string - -const ( - TagBool TagType = "!!bool" - TagInt TagType = "!!int" - TagFloat TagType = "!!float" - TagStr TagType = "!!str" - TagString TagType = "!!string" - TagSlice TagType = "!!seq" - TagMap TagType = "!!map" - TagTimestamp TagType = "!!timestamp" - TagBinary TagType = "!!binary" -) - -type ManifestNode struct { - StartLine int - EndLine int - Offset int - Value any - Type TagType - Path string -} - -func (r *ManifestNode) ToRego() any { - if r == nil { - return nil - } - switch r.Type { - case TagBool, TagInt, TagFloat, TagString, TagStr, TagBinary: - return r.Value - case TagTimestamp: - t, ok := r.Value.(time.Time) - if !ok { - return nil - } - return t.Format(time.RFC3339) - case TagSlice: - var output []any - for _, node := range r.Value.([]*ManifestNode) { - output = append(output, node.ToRego()) - } - return output - case TagMap: - output := make(map[string]any) - output["__defsec_metadata"] = map[string]any{ - "startline": r.StartLine, - "endline": r.EndLine, - "filepath": r.Path, - "offset": r.Offset, - } - for key, node := range r.Value.(map[string]*ManifestNode) { - output[key] = node.ToRego() - } - return output - } - return nil -} - -func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error { - r.StartLine = node.Line - r.EndLine = node.Line - r.Type = TagType(node.Tag) - - switch TagType(node.Tag) { - case TagString, TagStr: - r.Value = node.Value - case TagInt: - val, err := strconv.Atoi(node.Value) - if err != nil { - return fmt.Errorf("failed to parse int: %w", err) - } - r.Value = val - case TagFloat: - val, err := strconv.ParseFloat(node.Value, 64) - if err != nil { - return fmt.Errorf("failed to parse float: %w", err) - } - r.Value = val - case TagBool: - val, err := strconv.ParseBool(node.Value) - if err != nil { - return fmt.Errorf("failed to parse bool: %w", err) - } - r.Value = val - case TagTimestamp: - var val time.Time - if err := node.Decode(&val); err != nil { - return fmt.Errorf("failed to decode timestamp: %w", err) - } - r.Value = val - case TagBinary: - val, err := base64.StdEncoding.DecodeString(node.Value) - if err != nil { - return fmt.Errorf("failed to decode binary data: %w", err) - } - r.Value = val - case TagMap: - return r.handleMapTag(node) - case TagSlice: - return r.handleSliceTag(node) - default: - log.WithPrefix("k8s").Debug("Skipping unsupported node tag", - log.String("tag", node.Tag), - log.FilePath(r.Path), - log.Int("line", node.Line), - ) - } - return nil -} - -func (r *ManifestNode) handleSliceTag(node *yaml.Node) error { - var nodes []*ManifestNode - maxLine := node.Line - for _, contentNode := range node.Content { - newNode := new(ManifestNode) - newNode.Path = r.Path - if err := contentNode.Decode(newNode); err != nil { - return err - } - if newNode.EndLine > maxLine { - maxLine = newNode.EndLine - } - nodes = append(nodes, newNode) - } - r.EndLine = maxLine - r.Value = nodes - return nil -} - -func (r *ManifestNode) handleMapTag(node *yaml.Node) error { - output := make(map[string]*ManifestNode) - var key string - maxLine := node.Line - for i, contentNode := range node.Content { - if i == 0 || i%2 == 0 { - key = contentNode.Value - } else { - newNode := new(ManifestNode) - newNode.Path = r.Path - if err := contentNode.Decode(newNode); err != nil { - return err - } - output[key] = newNode - if newNode.EndLine > maxLine { - maxLine = newNode.EndLine - } - } - } - r.EndLine = maxLine - r.Value = output - return nil -} diff --git a/pkg/iac/scanners/kubernetes/parser/manifest_test.go b/pkg/iac/scanners/kubernetes/parser/manifest_test.go deleted file mode 100644 index fd1d34f3a967..000000000000 --- a/pkg/iac/scanners/kubernetes/parser/manifest_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package parser_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser" -) - -func TestJsonManifestToRego(t *testing.T) { - content := `{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "name": "hello-cpu-limit" - }, - "spec": { - "containers": [ - { - "command": [ - "sh", - "-c", - "echo 'Hello' && sleep 1h" - ], - "image": "busybox", - "name": "hello" - } - ] - } -}` - - const filePath = "pod.json" - manifest, err := parser.ManifestFromJSON(filePath, []byte(content)) - require.NoError(t, err) - - expected := map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": filePath, - "offset": 0, - "startline": 1, - "endline": 20, - }, - "apiVersion": "v1", - "kind": "Pod", - "metadata": map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": filePath, - "offset": 0, - "startline": 4, - "endline": 6, - }, - "name": "hello-cpu-limit", - }, - "spec": map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": filePath, - "offset": 0, - "startline": 7, - "endline": 19, - }, - "containers": []any{ - map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": filePath, - "offset": 0, - "startline": 8, - "endline": 17, - }, - "command": []any{ - "sh", - "-c", - "echo 'Hello' && sleep 1h", - }, - "image": "busybox", - "name": "hello", - }, - }, - }, - } - assert.Equal(t, expected, manifest.ToRego()) -} - -func TestManifestToRego(t *testing.T) { - tests := []struct { - name string - src string - expected any - }{ - { - name: "timestamp tag", - src: `field: !!timestamp 2024-04-01`, - expected: map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": "", - "offset": 0, - "startline": 1, - "endline": 1, - }, - "field": "2024-04-01T00:00:00Z", - }, - }, - { - name: "binary tag", - src: `field: !!binary dGVzdA==`, - expected: map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": "", - "offset": 0, - "startline": 1, - "endline": 1, - }, - "field": []uint8{0x74, 0x65, 0x73, 0x74}, - }, - }, - { - name: "float tag", - src: `field: 1.1`, - expected: map[string]any{ - "__defsec_metadata": map[string]any{ - "filepath": "", - "offset": 0, - "startline": 1, - "endline": 1, - }, - "field": 1.1, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var manifest parser.Manifest - err := yaml.Unmarshal([]byte(tt.src), &manifest) - require.NoError(t, err) - data := manifest.ToRego() - assert.Equal(t, tt.expected, data) - }) - } -} diff --git a/pkg/iac/scanners/kubernetes/parser/parser.go b/pkg/iac/scanners/kubernetes/parser/parser.go deleted file mode 100644 index 5c6b2ba3fe9b..000000000000 --- a/pkg/iac/scanners/kubernetes/parser/parser.go +++ /dev/null @@ -1,49 +0,0 @@ -package parser - -import ( - "context" - "fmt" - "io" - "regexp" - "strings" - - "gopkg.in/yaml.v3" -) - -func Parse(_ context.Context, r io.Reader, path string) ([]any, error) { - contents, err := io.ReadAll(r) - if err != nil { - return nil, err - } - - if len(contents) == 0 { - return nil, nil - } - - if strings.TrimSpace(string(contents))[0] == '{' { - manifest, err := ManifestFromJSON(path, contents) - if err != nil { - return nil, err - } - return []any{manifest.ToRego()}, nil - } - - var results []any - - re := regexp.MustCompile(`(?m:^---\r?\n)`) - pos := 0 - for _, partial := range re.Split(string(contents), -1) { - var result Manifest - result.Path = path - if err := yaml.Unmarshal([]byte(partial), &result); err != nil { - return nil, fmt.Errorf("unmarshal yaml: %w", err) - } - if result.Content != nil { - result.Content.Offset = pos - results = append(results, result.ToRego()) - } - pos += len(strings.Split(partial, "\n")) - } - - return results, nil -} diff --git a/pkg/iac/scanners/kubernetes/scanner.go b/pkg/iac/scanners/kubernetes/scanner.go deleted file mode 100644 index f79d59aaaf12..000000000000 --- a/pkg/iac/scanners/kubernetes/scanner.go +++ /dev/null @@ -1,19 +0,0 @@ -package kubernetes - -import ( - "context" - "io" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" - "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner { - return generic.NewScanner("Kubernetes", types.SourceKubernetes, generic.ParseFunc(parse), opts...) -} - -func parse(ctx context.Context, r io.Reader, path string) (any, error) { - return parser.Parse(ctx, r, path) -} diff --git a/pkg/iac/scanners/kubernetes/scanner_test.go b/pkg/iac/scanners/kubernetes/scanner_test.go deleted file mode 100644 index c9186a7f9c40..000000000000 --- a/pkg/iac/scanners/kubernetes/scanner_test.go +++ /dev/null @@ -1,307 +0,0 @@ -package kubernetes_test - -import ( - "context" - "io/fs" - "strings" - "testing" - "testing/fstest" - - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes" -) - -func Test_ScanYAML(t *testing.T) { - file := ` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -` - fsys := buildFS(map[string]string{ - "code/example.yaml": file, - "checks/rule.rego": `# METADATA -# title: test check -# custom: -# id: KSV011 -# avd_id: AVD-KSV-0011 -# severity: LOW -# input: -# selector: -# - type: kubernetes -package builtin.kubernetes.KSV011 - -import data.lib.kubernetes - -deny[res] { - container := kubernetes.containers[_] - res := result.new("fail", container) -} -`, - }) - - scanner := kubernetes.NewScanner( - rego.WithPolicyFilesystem(fsys), - rego.WithPolicyDirs("checks"), - rego.WithEmbeddedLibraries(true), - ) - - results, err := scanner.ScanFS(context.TODO(), fsys, "code") - require.NoError(t, err) - - failed := results.GetFailed() - require.Len(t, failed, 1) - - assert.Equal(t, "AVD-KSV-0011", failed[0].Rule().AVDID) - assertLines(t, file, failed) -} - -func Test_ScanJSON(t *testing.T) { - - file := ` -{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "name": "hello-cpu-limit" - }, - "spec": { - "containers": [ - { - "command": [ - "sh", - "-c", - "echo 'Hello' && sleep 1h" - ], - "image": "busybox", - "name": "hello" - } - ] - } -} -` - - fsys := buildFS(map[string]string{ - "code/example.json": file, - "checks/rule.rego": `# METADATA -# title: test check -# custom: -# id: KSV011 -# avd_id: AVD-KSV-0011 -# severity: LOW -# input: -# selector: -# - type: kubernetes -package builtin.kubernetes.KSV011 - -import data.lib.kubernetes - -deny[res] { - container := kubernetes.containers[_] - res := result.new("fail", container) -} -`, - }) - - scanner := kubernetes.NewScanner( - rego.WithPolicyFilesystem(fsys), - rego.WithPolicyDirs("checks"), - rego.WithEmbeddedLibraries(true), - ) - - results, err := scanner.ScanFS(context.TODO(), fsys, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - failed := results.GetFailed() - require.Len(t, failed, 1) - - assert.Equal(t, "AVD-KSV-0011", failed[0].Rule().AVDID) - assertLines(t, file, failed) -} - -func Test_YamlWithSeparator(t *testing.T) { - - fsys := buildFS(map[string]string{ - "check.rego": `package defsec - -deny[res] { - input.kind == "Pod" - res := result.new("fail", input) -}`, - "k8s.yaml": ` ---- ---- -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`, - }) - - scanner := kubernetes.NewScanner( - rego.WithPolicyFilesystem(fsys), - rego.WithPolicyDirs("."), - rego.WithEmbeddedLibraries(true), - ) - results, err := scanner.ScanFS(context.TODO(), fsys, ".") - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) -} - -func Test_YamlMultiDocument(t *testing.T) { - file := ` ---- -apiVersion: v1 -kind: Pod -metadata: - name: hello1-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello1' && sleep 1h"] - image: busybox - name: hello1 ---- -apiVersion: v1 -kind: Pod -metadata: - name: hello2-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello2' && sleep 1h"] - image: busybox - name: hello2 -` - fsys := buildFS(map[string]string{ - "check.rego": `package defsec - -deny[res] { - input.kind == "Pod" - res := result.new("fail", input) -}`, - "k8s.yaml": file, - }) - - scanner := kubernetes.NewScanner( - rego.WithPolicyFilesystem(fsys), - rego.WithPolicyDirs("."), - rego.WithEmbeddedLibraries(true), - ) - - results, err := scanner.ScanFS(context.TODO(), fsys, ".") - require.NoError(t, err) - - assertLines(t, file, results) -} - -func Test_CheckWithSubtype(t *testing.T) { - fsys := buildFS(map[string]string{ - "checks/pod_policy.rego": `# METADATA -# title: test check -# scope: package -# schemas: -# - input: schema["kubernetes"] -# custom: -# id: KSV001 -# avd_id: AVD-KSV-0001 -# severity: MEDIUM -# input: -# selector: -# - type: kubernetes -# subtypes: -# - kind: Pod -package builtin.kubernetes.KSV001 - -import data.lib.kubernetes - -deny[res] { - res := result.new("fail", input) -} -`, - "checks/namespace_policy.rego": `# METADATA -# title: test check 2 -# scope: package -# schemas: -# - input: schema["kubernetes"] -# custom: -# id: KSV002 -# avd_id: AVD-KSV-0002 -# severity: LOW -# input: -# selector: -# - type: kubernetes -# subtypes: -# - kind: Namespace -package builtin.kubernetes.KSV002 - -deny[res] { - res := result.new("fail", input) -} -`, - "test/KSV001/pod.yaml": `apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello - securityContext: - capabilities: - drop: - - all -`, - }) - - scanner := kubernetes.NewScanner( - rego.WithEmbeddedLibraries(true), - rego.WithPolicyDirs("checks"), - rego.WithPolicyFilesystem(fsys), - ) - results, err := scanner.ScanFS(context.TODO(), fsys, "test/KSV001") - require.NoError(t, err) - - require.NoError(t, err) - require.Len(t, results.GetFailed(), 1) - - failure := results.GetFailed()[0] - - assert.Equal(t, "AVD-KSV-0001", failure.Rule().AVDID) -} - -func assertLines(t *testing.T, content string, results scan.Results) { - lines := strings.Split(content, "\n") - for _, res := range results { - actualCode, err := res.GetCode() - require.NoError(t, err) - assert.NotEmpty(t, actualCode.Lines) - for _, line := range actualCode.Lines { - assert.Greater(t, len(lines), line.Number) - assert.Equal(t, line.Content, lines[line.Number-1]) - } - } -} - -func buildFS(files map[string]string) fs.FS { - return fstest.MapFS(lo.MapValues(files, func(val string, _ string) *fstest.MapFile { - return &fstest.MapFile{Data: []byte(val)} - })) -} diff --git a/pkg/iac/scanners/options/scanner.go b/pkg/iac/scanners/options/scanner.go deleted file mode 100644 index 420909095bd8..000000000000 --- a/pkg/iac/scanners/options/scanner.go +++ /dev/null @@ -1,5 +0,0 @@ -package options - -type ConfigurableScanner any - -type ScannerOption func(s ConfigurableScanner) diff --git a/pkg/iac/scanners/scanner.go b/pkg/iac/scanners/scanner.go deleted file mode 100644 index e792545b9e97..000000000000 --- a/pkg/iac/scanners/scanner.go +++ /dev/null @@ -1,21 +0,0 @@ -package scanners - -import ( - "context" - "io/fs" - "os" - - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -type WriteFileFS interface { - WriteFile(name string, data []byte, perm os.FileMode) error -} - -type FSScanner interface { - // Name provides the human-readable name of the scanner e.g. "CloudFormation" - Name() string - // ScanFS scans the given filesystem for issues, starting at the provided directory. - // Use '.' to scan an entire filesystem. - ScanFS(ctx context.Context, fs fs.FS, dir string) (scan.Results, error) -} diff --git a/pkg/iac/scanners/terraform/attribute_test.go b/pkg/iac/scanners/terraform/attribute_test.go deleted file mode 100644 index 875c8fc6cc42..000000000000 --- a/pkg/iac/scanners/terraform/attribute_test.go +++ /dev/null @@ -1,713 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func Test_AttributeStartsWith(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue string - expectedResult bool - }{ - { - name: "bucket name starts with bucket", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" -}`, - checkAttribute: "bucket_name", - checkValue: "bucket", - expectedResult: true, - }, - { - name: "bucket acl starts with public", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - checkAttribute: "acl", - checkValue: "public", - expectedResult: true, - }, - { - name: "bucket name doesn't start with secret", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - logging { - target_bucket = aws_s3_bucket.log_bucket.id - target_prefix = "log/" - } -}`, - checkAttribute: "bucket_name", - checkValue: "secret_", - expectedResult: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.StartsWith(test.checkValue)) - } - } - }) - } -} - -func Test_AttributeEndsWith(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue string - expectedResult bool - }{ - { - name: "bucket name ends with Name", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" -}`, - checkAttribute: "bucket_name", - checkValue: "Name", - expectedResult: true, - }, - { - name: "bucket acl ends with read not Read", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - checkAttribute: "acl", - checkValue: "Read", - expectedResult: false, - }, - { - name: "bucket name doesn't end with bucket", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - logging { - target_bucket = aws_s3_bucket.log_bucket.id - target_prefix = "log/" - } -}`, - checkAttribute: "bucket_name", - checkValue: "_bucket", - expectedResult: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.EndsWith(test.checkValue)) - } - } - }) - } -} - -func Test_AttributeContains(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue string - expectedResult bool - ignoreCase bool - }{ - { - name: "bucket name contains Name", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" -}`, - checkAttribute: "bucket_name", - checkValue: "etNa", - expectedResult: true, - }, - { - name: "bucket acl doesn't contain private", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - checkAttribute: "acl", - checkValue: "private", - expectedResult: false, - }, - { - name: "tags attribute is a map with a Department key", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - tags = { - Department = "Finance" - } -}`, - checkAttribute: "tags", - checkValue: "Department", - expectedResult: true, - }, - { - name: "cidr_block has expected subnet", - source: ` -resource "aws_security_group" "my-security_group" { - cidr_block = ["10.0.0.0/16", "172.0.0.0/8" ] -}`, - checkAttribute: "cidr_block", - checkValue: "172.0.0.0/8", - expectedResult: true, - }, - { - name: "autoscaling group has propagated key defined 1st tag is present", - source: ` -resource "aws_autoscaling_group" "my-aws_autoscaling_group" { - tags = [ - { - "key" = "Name" - "propagate_at_launch" = "true" - "value" = "couchbase-seb-develop-dev" - }, - { - "key" = "app" - "propagate_at_launch" = "true" - "value" = "myapp" - } - ] -}`, - checkAttribute: "tags", - checkValue: "Name", - expectedResult: true, - }, - { - name: "autoscaling group has propagated key defined 2nd tag is present", - source: ` -resource "aws_autoscaling_group" "my-aws_autoscaling_group" { - tags = [ - { - "key" = "Name" - "propagate_at_launch" = "true" - "value" = "couchbase-seb-develop-dev" - }, - { - "key" = "app" - "propagate_at_launch" = "true" - "value" = "myapp" - } - ] -}`, - checkAttribute: "tags", - checkValue: "app", - expectedResult: true, - }, - { - name: "autoscaling group has propagated key defined and tag is not present", - source: ` -resource "aws_autoscaling_group" "my-aws_autoscaling_group" { - tags = [ - { - "key" = "Name" - "propagate_at_launch" = "true" - "value" = "couchbase-seb-develop-dev" - }, - { - "key" = "app" - "propagate_at_launch" = "true" - "value" = "myapp" - } - ] -}`, - checkAttribute: "tags", - checkValue: "NotThere", - expectedResult: false, - }, - { - name: "contains array of strings ignores case", - source: ` -resource "aws_security_group" "my-security_group" { - cidr_block = ["Foo", "Bar" ] -}`, - checkAttribute: "cidr_block", - checkValue: "foo", - expectedResult: true, - ignoreCase: true, - }, - { - name: "contains array of strings without ignore case", - source: ` -resource "aws_security_group" "my-security_group" { - cidr_block = ["Foo", "Bar" ] -}`, - checkAttribute: "cidr_block", - checkValue: "foo", - expectedResult: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, b := range module.GetBlocks() { - if !b.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := b.GetAttribute(test.checkAttribute) - if test.ignoreCase { - assert.Equal(t, test.expectedResult, attr.Contains(test.checkValue, terraform.IgnoreCase)) - } else { - assert.Equal(t, test.expectedResult, attr.Contains(test.checkValue)) - } - } - } - }) - } -} - -func Test_AttributeIsAny(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue []any - expectedResult bool - }{ - { - name: "bucket acl is not one of the specified acls", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - checkAttribute: "acl", - checkValue: []any{"private", "authenticated-read"}, - expectedResult: false, - }, - { - name: "bucket acl is one of the specified acls", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "private" -}`, - checkAttribute: "acl", - checkValue: []any{"private", "authenticated-read"}, - expectedResult: true, - }, - { - name: "is is one of the provided valued", - source: ` -resource "aws_security_group" "my-security_group" { - count = 1 -}`, - checkAttribute: "count", - checkValue: []any{1, 2}, - expectedResult: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.IsAny(test.checkValue...)) - } - } - }) - } -} - -func Test_AttributeIsNone(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue []any - expectedResult bool - }{ - { - name: "bucket acl is not one of the specified acls", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - checkAttribute: "acl", - checkValue: []any{"private", "authenticated-read"}, - expectedResult: true, - }, - { - name: "bucket acl is one of the specified acls", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "private" -}`, - checkAttribute: "acl", - checkValue: []any{"private", "authenticated-read"}, - expectedResult: false, - }, - { - name: "count is non-of the provided values", - source: ` -resource "aws_security_group" "my-security_group" { - count = 0 -}`, - checkAttribute: "count", - checkValue: []any{1, 2}, - expectedResult: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.IsNone(test.checkValue...)) - } - } - }) - } -} - -func Test_AttributeIsEmpty(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue []any - expectedResult bool - }{ - { - name: "bucket acl is not empty", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - checkAttribute: "acl", - expectedResult: false, - }, - { - name: "bucket acl is empty", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "" -}`, - checkAttribute: "acl", - expectedResult: true, - }, - { - name: "tags is not empty", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - tags = { - Department = "Finance" - } -}`, - checkAttribute: "tags", - expectedResult: false, - }, - { - name: "tags is empty", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - tags = { - } -}`, - checkAttribute: "tags", - expectedResult: true, - }, - { - name: "cidr is not empty", - source: ` -resource "aws_security_group_rule" "example" { - type = "ingress" - from_port = 0 - to_port = 65535 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - security_group_id = "sg-123456" -}`, - checkAttribute: "cidr_blocks", - expectedResult: false, - }, - { - name: "cidr is empty", - source: ` -resource "aws_security_group_rule" "example" { - type = "ingress" - from_port = 0 - to_port = 65535 - protocol = "tcp" - cidr_blocks = [] - security_group_id = "sg-123456" -}`, - checkAttribute: "cidr_blocks", - expectedResult: true, - }, - { - name: "from_port_is_not_empty", - source: ` -resource "aws_security_group_rule" "example" { - type = "ingress" - from_port = 0 - to_port = 65535 - protocol = "tcp" - cidr_blocks = [] - security_group_id = "sg-123456" -}`, - checkAttribute: "from_port", - expectedResult: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.IsEmpty()) - } - } - }) - } -} - -func Test_AttributeIsLessThan(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue int - expectedResult bool - }{ - { - name: "check attribute is less than check value", - source: ` -resource "numerical_something" "my-bucket" { - value = 100 -}`, - checkAttribute: "value", - checkValue: 200, - expectedResult: true, - }, - { - name: "check attribute is not less than check value", - source: ` -resource "numerical_something" "my-bucket" { - value = 100 -}`, - checkAttribute: "value", - checkValue: 50, - expectedResult: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.LessThan(test.checkValue)) - } - } - }) - } -} - -func Test_AttributeIsLessThanOrEqual(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - checkValue int - expectedResult bool - }{ - { - name: "check attribute is less than or equal check value", - source: ` -resource "numerical_something" "my-bucket" { - value = 100 -}`, - checkAttribute: "value", - checkValue: 100, - expectedResult: true, - }, - { - name: "check attribute is not less than check value", - source: ` -resource "numerical_something" "my-bucket" { - value = 100 -}`, - checkAttribute: "value", - checkValue: 50, - expectedResult: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.LessThanOrEqualTo(test.checkValue)) - } - } - }) - } -} - -func Test_AttributeIsTrue(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - expectedResult bool - }{ - { - name: "check attribute is true", - source: ` -resource "boolean_something" "my-something" { - value = true -}`, - checkAttribute: "value", - expectedResult: true, - }, - { - name: "check attribute as string is true", - source: ` -resource "boolean_something" "my-something" { - value = "true" -}`, - checkAttribute: "value", - expectedResult: true, - }, - { - name: "check attribute as string is false", - source: ` -resource "boolean_something" "my-something" { - value = "true" -}`, - checkAttribute: "value", - expectedResult: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.IsTrue()) - } - } - }) - } -} - -func Test_AttributeIsFalse(t *testing.T) { - var tests = []struct { - name string - source string - checkAttribute string - expectedResult bool - }{ - { - name: "check attribute is false", - source: ` -resource "boolean_something" "my-something" { - value = false -}`, - checkAttribute: "value", - expectedResult: true, - }, - { - name: "check attribute as string is false", - source: ` -resource "boolean_something" "my-something" { - value = "false" -}`, - checkAttribute: "value", - expectedResult: true, - }, - { - name: "check attribute true", - source: ` -resource "boolean_something" "my-something" { - value = "true" -}`, - checkAttribute: "value", - expectedResult: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - if !block.HasChild(test.checkAttribute) { - t.FailNow() - } - attr := block.GetAttribute(test.checkAttribute) - assert.Equal(t, test.expectedResult, attr.IsFalse()) - } - } - }) - } -} diff --git a/pkg/iac/scanners/terraform/block_test.go b/pkg/iac/scanners/terraform/block_test.go deleted file mode 100644 index 8b86d023bf89..000000000000 --- a/pkg/iac/scanners/terraform/block_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_IsPresentCheckOnBlock(t *testing.T) { - var tests = []struct { - name string - source string - expectedAttribute string - }{ - { - name: "expected attribute is present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" -}`, - expectedAttribute: "bucket_name", - }, - { - name: "expected acl attribute is present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" -}`, - expectedAttribute: "acl", - }, - { - name: "expected acl attribute is present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - logging { - target_bucket = aws_s3_bucket.log_bucket.id - target_prefix = "log/" - } -}`, - expectedAttribute: "logging", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - assert.True(t, block.HasChild(test.expectedAttribute)) - assert.True(t, block.HasChild(test.expectedAttribute)) - } - } - }) - } -} - -func Test_IsNotPresentCheckOnBlock(t *testing.T) { - var tests = []struct { - name string - source string - expectedAttribute string - }{ - { - name: "expected attribute is not present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - -}`, - expectedAttribute: "acl", - }, - { - name: "expected acl attribute is not present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - -}`, - expectedAttribute: "logging", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - assert.False(t, block.HasChild(test.expectedAttribute)) - assert.False(t, block.HasChild(test.expectedAttribute)) - } - } - }) - } -} - -func Test_MissingChildNotFoundOnBlock(t *testing.T) { - var tests = []struct { - name string - source string - expectedAttribute string - }{ - { - name: "expected attribute is not present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - -}`, - expectedAttribute: "acl", - }, - { - name: "expected acl attribute is not present", - source: ` -resource "aws_s3_bucket" "my-bucket" { - bucket_name = "bucketName" - acl = "public-read" - -}`, - expectedAttribute: "logging", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - modules := createModulesFromSource(t, test.source, ".tf") - for _, module := range modules { - for _, block := range module.GetBlocks() { - assert.True(t, block.MissingChild(test.expectedAttribute)) - assert.False(t, block.HasChild(test.expectedAttribute)) - } - } - }) - } -} diff --git a/pkg/iac/scanners/terraform/count_test.go b/pkg/iac/scanners/terraform/count_test.go deleted file mode 100644 index fbedaeed5a97..000000000000 --- a/pkg/iac/scanners/terraform/count_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func Test_ResourcesWithCount(t *testing.T) { - var tests = []struct { - name string - source string - expected int - }{ - { - name: "unspecified count defaults to 1", - source: ` - resource "aws_s3_bucket" "test" {} -`, - expected: 1, - }, - { - name: "count is literal 1", - source: ` - resource "aws_s3_bucket" "test" { - count = 1 - } -`, - expected: 1, - }, - { - name: "count is literal 99", - source: ` - resource "aws_s3_bucket" "test" { - count = 99 - } -`, - expected: 99, - }, - { - name: "count is literal 0", - source: ` - resource "aws_s3_bucket" "test" { - count = 0 - } -`, - expected: 0, - }, - { - name: "count is 0 from variable", - source: ` - variable "count" { - default = 0 - } - resource "aws_s3_bucket" "test" { - count = var.count - } -`, - expected: 0, - }, - { - name: "count is 1 from variable", - source: ` - variable "count" { - default = 1 - } - resource "aws_s3_bucket" "test" { - count = var.count - } -`, - expected: 1, - }, - { - name: "count is 1 from variable without default", - source: ` - variable "count" { - } - resource "aws_s3_bucket" "test" { - count = var.count - } -`, - expected: 1, - }, - { - name: "count is 0 from conditional", - source: ` - variable "enabled" { - default = false - } - resource "aws_s3_bucket" "test" { - count = var.enabled ? 1 : 0 - } -`, - expected: 0, - }, - { - name: "count is 1 from conditional", - source: ` - variable "enabled" { - default = true - } - resource "aws_s3_bucket" "test" { - count = var.enabled ? 1 : 0 - } -`, - expected: 1, - }, - { - name: "issue 962", - source: ` - resource "something" "else" { - count = 2 - ok = true - } - - resource "aws_s3_bucket" "test" { - bucket = something.else[0].ok ? "test" : "" - } -`, - expected: 0, - }, - { - name: "Test use of count.index", - source: ` -resource "aws_s3_bucket" "test" { - count = 1 - bucket = var.things[count.index]["ok"] ? "test" : "" -} - -variable "things" { - description = "A list of maps that creates a number of sg" - type = list(map(string)) - - default = [ - { - ok = true - } - ] -} - `, - expected: 0, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - results := scanHCL(t, test.source, - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ) - - assert.Len(t, results.GetFailed(), test.expected) - - if test.expected > 0 { - testutil.AssertRuleFound(t, "aws-s3-non-empty-bucket", results, "false negative found") - } else { - testutil.AssertRuleNotFound(t, "aws-s3-non-empty-bucket", results, "false positive found") - } - }) - } -} diff --git a/pkg/iac/scanners/terraform/deterministic_test.go b/pkg/iac/scanners/terraform/deterministic_test.go deleted file mode 100644 index c0e45fa8d8d8..000000000000 --- a/pkg/iac/scanners/terraform/deterministic_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func Test_DeterministicResults(t *testing.T) { - fsys := testutil.CreateFS(t, map[string]string{ - "first.tf": ` -resource "aws_s3_bucket" "test" { - for_each = other.thing -} - `, - "second.tf": ` -resource "other" "thing" { - for_each = local.list -} - `, - "third.tf": ` -locals { - list = { - a = 1, - b = 2, - } -} - `, - }) - - for i := 0; i < 100; i++ { - results, err := scanFS(fsys, ".", - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ) - require.NoError(t, err) - require.Len(t, results.GetFailed(), 2) - } -} diff --git a/pkg/iac/scanners/terraform/executor/executor.go b/pkg/iac/scanners/terraform/executor/executor.go deleted file mode 100644 index 0f7cd322ee60..000000000000 --- a/pkg/iac/scanners/terraform/executor/executor.go +++ /dev/null @@ -1,241 +0,0 @@ -package executor - -import ( - "context" - "fmt" - "sort" - "strconv" - "strings" - - "github.com/hashicorp/hcl/v2/hclwrite" - "github.com/zclconf/go-cty/cty" - - adapter "github.com/aquasecurity/trivy/pkg/iac/adapters/terraform" - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -// Executor scans HCL blocks by running all registered rules against them -type Executor struct { - workspaceName string - logger *log.Logger - resultsFilters []func(scan.Results) scan.Results - regoScanner *rego.Scanner -} - -// New creates a new Executor -func New(options ...Option) *Executor { - s := &Executor{ - logger: log.WithPrefix("terraform executor"), - } - for _, option := range options { - option(s) - } - return s -} - -func (e *Executor) Execute(ctx context.Context, modules terraform.Modules, basePath string) (scan.Results, error) { - - e.logger.Debug("Adapting modules...") - infra := adapter.Adapt(modules) - e.logger.Debug("Adapted module(s) into state data.", log.Int("count", len(modules))) - - results, err := e.regoScanner.ScanInput(ctx, types.SourceCloud, rego.Input{ - Contents: infra.ToRego(), - Path: basePath, - }) - if err != nil { - return nil, err - } - - e.logger.Debug("Finished applying rules.") - - e.logger.Debug("Applying ignores...") - var ignores ignore.Rules - for _, module := range modules { - ignores = append(ignores, module.Ignores()...) - } - - ignorers := map[string]ignore.Ignorer{ - "ws": workspaceIgnorer(e.workspaceName), - "ignore": attributeIgnorer(modules), - } - - // ignore a result based on user input - results.Ignore(ignores, ignorers) - - for _, ignored := range results.GetIgnored() { - e.logger.Info("Ignore finding", - log.String("rule", ignored.Rule().LongID()), - log.String("range", ignored.Range().String()), - ) - } - - results = e.filterResults(results) - - e.sortResults(results) - for i, res := range results { - if res.Status() != scan.StatusFailed { - continue - } - - res.WithRenderedCause(e.renderCause(modules, res.Range())) - results[i] = res - } - - return results, nil -} - -func (e *Executor) renderCause(modules terraform.Modules, causeRng types.Range) scan.RenderedCause { - tfBlock := findBlockForCause(modules, causeRng) - if tfBlock == nil { - e.logger.Debug("No matching Terraform block found", log.String("cause_range", causeRng.String())) - return scan.RenderedCause{} - } - - block := hclwrite.NewBlock(tfBlock.Type(), normalizeBlockLables(tfBlock)) - - if !writeBlock(tfBlock, block, causeRng) { - e.logger.Debug("No matching block attribute found", log.String("cause_range", causeRng.String())) - return scan.RenderedCause{} - } - - f := hclwrite.NewEmptyFile() - f.Body().AppendBlock(block) - - cause := string(hclwrite.Format(f.Bytes())) - cause = strings.TrimSuffix(cause, "\n") - return scan.RenderedCause{Raw: cause} -} - -// normalizeBlockLables removes indexes and keys from labels. -func normalizeBlockLables(block *terraform.Block) []string { - labels := block.Labels() - if block.IsExpanded() { - nameLabel := labels[len(labels)-1] - idx := strings.LastIndex(nameLabel, "[") - if idx != -1 { - labels[len(labels)-1] = nameLabel[:idx] - } - } - - return labels -} - -func writeBlock(tfBlock *terraform.Block, block *hclwrite.Block, causeRng types.Range) bool { - var found bool - - for _, attr := range tfBlock.Attributes() { - if attr.GetMetadata().Range().Covers(causeRng) && !attr.IsLiteral() { - block.Body().SetAttributeValue(attr.Name(), attr.Value()) - found = true - } - } - - for _, child := range tfBlock.AllBlocks() { - if child.GetMetadata().Range().Covers(causeRng) { - childBlock := hclwrite.NewBlock(child.Type(), nil) - if writeBlock(child, childBlock, causeRng) { - block.Body().AppendBlock(childBlock) - found = true - } - } - } - - return found -} - -func findBlockForCause(modules terraform.Modules, causeRng types.Range) *terraform.Block { - for _, block := range modules.GetBlocks() { - blockRng := block.GetMetadata().Range() - if blockRng.GetFilename() == causeRng.GetFilename() && blockRng.Includes(causeRng) { - return block - } - } - return nil -} - -func (e *Executor) filterResults(results scan.Results) scan.Results { - if len(e.resultsFilters) > 0 && len(results) > 0 { - before := len(results.GetIgnored()) - e.logger.Debug("Applying results filters...") - for _, filter := range e.resultsFilters { - results = filter(results) - } - e.logger.Debug("Applied results filters.", - log.Int("count", len(results.GetIgnored())-before)) - } - - return results -} - -func (e *Executor) sortResults(results []scan.Result) { - sort.Slice(results, func(i, j int) bool { - switch { - case results[i].Rule().LongID() < results[j].Rule().LongID(): - return true - case results[i].Rule().LongID() > results[j].Rule().LongID(): - return false - default: - return results[i].Range().String() > results[j].Range().String() - } - }) -} - -func ignoreByParams(params map[string]string, modules terraform.Modules, m *types.Metadata) bool { - if len(params) == 0 { - return true - } - block := modules.GetBlockByIgnoreRange(m) - if block == nil { - return true - } - for key, param := range params { - val := block.GetValueByPath(key) - switch val.Type() { - case cty.String: - if val.AsString() != param { - return false - } - case cty.Number: - bf := val.AsBigFloat() - f64, _ := bf.Float64() - comparableInt := strconv.Itoa(int(f64)) - comparableFloat := fmt.Sprintf("%f", f64) - if param != comparableInt && param != comparableFloat { - return false - } - case cty.Bool: - if strconv.FormatBool(val.True()) != param { - return false - } - default: - return false - } - } - return true -} - -func workspaceIgnorer(ws string) ignore.Ignorer { - return func(_ types.Metadata, param any) bool { - ignoredWorkspace, ok := param.(string) - if !ok { - return false - } - return ignore.MatchPattern(ws, ignoredWorkspace) - } -} - -func attributeIgnorer(modules terraform.Modules) ignore.Ignorer { - return func(resultMeta types.Metadata, param any) bool { - params, ok := param.(map[string]string) - if !ok { - return false - } - return ignoreByParams(params, modules, &resultMeta) - } -} diff --git a/pkg/iac/scanners/terraform/executor/option.go b/pkg/iac/scanners/terraform/executor/option.go deleted file mode 100644 index 65a6180d7e67..000000000000 --- a/pkg/iac/scanners/terraform/executor/option.go +++ /dev/null @@ -1,26 +0,0 @@ -package executor - -import ( - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -type Option func(s *Executor) - -func OptionWithResultsFilter(f func(scan.Results) scan.Results) Option { - return func(s *Executor) { - s.resultsFilters = append(s.resultsFilters, f) - } -} - -func OptionWithWorkspaceName(workspaceName string) Option { - return func(s *Executor) { - s.workspaceName = workspaceName - } -} - -func OptionWithRegoScanner(s *rego.Scanner) Option { - return func(e *Executor) { - e.regoScanner = s - } -} diff --git a/pkg/iac/scanners/terraform/fs_test.go b/pkg/iac/scanners/terraform/fs_test.go deleted file mode 100644 index fb5448873607..000000000000 --- a/pkg/iac/scanners/terraform/fs_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package terraform - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func Test_OS_FS(t *testing.T) { - s := New( - rego.WithEmbeddedPolicies(true), - rego.WithEmbeddedLibraries(true), - ) - results, err := s.ScanFS(context.TODO(), os.DirFS("testdata"), "fail") - require.NoError(t, err) - assert.NotEmpty(t, results.GetFailed()) -} diff --git a/pkg/iac/scanners/terraform/ignore_test.go b/pkg/iac/scanners/terraform/ignore_test.go deleted file mode 100644 index 7ef6d09d9ca7..000000000000 --- a/pkg/iac/scanners/terraform/ignore_test.go +++ /dev/null @@ -1,621 +0,0 @@ -package terraform - -import ( - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func Test_IgnoreAll(t *testing.T) { - - var testCases = []struct { - name string - source string - assertLength int - }{ - { - name: "inline rule ignore all checks", - source: `resource "aws_s3_bucket" "test" { - bucket = "" // %s:ignore:* -}`, - assertLength: 0, - }, - { - name: "rule above block ignore all checks", - source: `// %s:ignore:* -resource "aws_s3_bucket" "test" {}`, - assertLength: 0, - }, - { - name: "rule above block with boolean parameter", - source: `// %s:ignore:*[object_lock_enabled=false] -resource "aws_s3_bucket" "test" { - object_lock_enabled = false -}`, - assertLength: 0, - }, - { - name: "rule above block with non-matching boolean parameter", - source: `// %s:ignore:*[object_lock_enabled=false] -resource "aws_s3_bucket" "test" { - object_lock_enabled = true -}`, - assertLength: 1, - }, - { - name: "rule above block with string parameter", - source: `// %s:ignore:*[acl=private] -resource "aws_s3_bucket" "test" { - acl = "private" -}`, - assertLength: 0, - }, - { - name: "rule above block with non-matching string parameter", - source: `// %s:ignore:*[acl=private] -resource "aws_s3_bucket" "test" { - acl = "public" -}`, - assertLength: 1, - }, - { - name: "rule above block with int parameter", - source: `// %s:ignore:*[some_int=123] -resource "aws_s3_bucket" "test" { - some_int = 123 -}`, - assertLength: 0, - }, - { - name: "rule above block with non-matching int parameter", - source: `// %s:ignore:*[some_int=456] -resource "aws_s3_bucket" "test" { - some_int = 123 -}`, - assertLength: 1, - }, - { - name: "stacked rules above block", - source: `// %s:ignore:* -// %s:ignore:a -// %s:ignore:b -// %s:ignore:c -// %s:ignore:d -resource "aws_s3_bucket" "test" {} -`, - assertLength: 0, - }, - { - name: "stacked rules above block without a match", - source: `#%s:ignore:* - -#%s:ignore:x -#%s:ignore:a -#%s:ignore:b -#%s:ignore:c -#%s:ignore:d -resource "aws_s3_bucket" "test" {} -`, - assertLength: 1, - }, - { - name: "stacked rules above block without spaces between '#' comments", - source: `#%s:ignore:* -#%s:ignore:a -#%s:ignore:b -#%s:ignore:c -#%s:ignore:d -resource "aws_s3_bucket" "test" {} -`, - assertLength: 0, - }, - { - name: "stacked rules above block without spaces between '//' comments", - source: `//%s:ignore:* -//%s:ignore:a -//%s:ignore:b -//%s:ignore:c -//%s:ignore:d -resource "aws_s3_bucket" "test" {} -`, - assertLength: 0, - }, - { - name: "rule above the finding", - source: `resource "aws_s3_bucket" "test" { - # %s:ignore:aws-s3-non-empty-bucket - bucket = "" -}`, - assertLength: 0, - }, - { - name: "rule with breached expiration date", - source: `resource "aws_s3_bucket" "test" { - bucket = "" # %s:ignore:aws-s3-non-empty-bucket:exp:2000-01-02 -}`, - assertLength: 1, - }, - { - name: "rule with unbreached expiration date", - source: `resource "aws_s3_bucket" "test" { - bucket = "" # %s:ignore:aws-s3-non-empty-bucket:exp:2221-01-02 -}`, - assertLength: 0, - }, - { - name: "rule with invalid expiration date", - source: `resource "aws_s3_bucket" "test" { - bucket = "" # %s:ignore:aws-s3-non-empty-bucket:exp:2221-13-02 -}`, - assertLength: 1, - }, - { - name: "rule above block with unbreached expiration date", - source: `#%s:ignore:aws-s3-non-empty-bucket:exp:2221-01-02 -resource "aws_s3_bucket" "test" {}`, - assertLength: 0, - }, - { - name: "trivy inline rule ignore all checks", - source: `resource "aws_s3_bucket" "test" { - bucket = "" // %s:ignore:* -}`, - assertLength: 0, - }, - { - name: "ignore by nested attribute", - source: `// %s:ignore:*[versioning.enabled=false] -resource "aws_s3_bucket" "test" { - versioning { - enabled = false - } -}`, - assertLength: 0, - }, - { - name: "ignore by nested attribute of another type", - source: `// %s:ignore:*[versioning.enabled=1] -resource "aws_s3_bucket" "test" { - versioning { - enabled = false - } -}`, - assertLength: 1, - }, - { - name: "ignore by non-existent nested attribute", - source: `// %s:ignore:*[versioning.target=foo] -resource "aws_s3_bucket" "test" { - versioning { - enabled = false - } -}`, - assertLength: 1, - }, - { - name: "ignore resource with `for_each` meta-argument", - source: `// %s:ignore:*[acl=public] -resource "aws_s3_bucket" "test" { - for_each = toset(["private", "public"]) - acl = each.value -}`, - assertLength: 1, - }, - { - name: "ignore by dynamic block value", - source: `// %s:ignore:*[versioning.enabled=false] -resource "aws_s3_bucket" "test" { - dynamic "versioning" { - for_each = [{}] - content { - enabled = false - } - } -}`, - assertLength: 0, - }, - { - name: "ignore by each.value", - source: `locals { - acls = toset(["private", "public"]) -} - -// %s:ignore:*[each.value=private] -resource "aws_s3_bucket" "test" { - for_each = local.acls - - acl = each.value -}`, - assertLength: 1, - }, - { - name: "ignore by nested each.value", - source: `locals { - acls = { - private = { - permission = "private" - } - public = { - permission = "public" - } - } -} - -// %s:ignore:*[each.value.permission=private] -resource "aws_s3_bucket" "test" { - for_each = local.acls - - acl = each.value.permission -}`, - assertLength: 1, - }, - { - name: "ignore resource with `count` meta-argument", - source: `// %s:ignore:*[count.index=1] -resource "aws_s3_bucket" "test" { - count = 2 -}`, - assertLength: 1, - }, - { - name: "invalid index when accessing blocks", - source: `// %s:ignore:*[ingress.99.port=9090] -// %s:ignore:*[ingress.-10.port=9090] -resource "aws_s3_bucket" "test" { - dynamic "ingress" { - for_each = [8080, 9090] - content { - port = ingress.value - } - } -}`, - assertLength: 1, - }, - { - name: "ignore by list value", - source: `#%s:ignore:*[someattr.1.Environment=dev] -resource "aws_s3_bucket" "test" { - someattr = [ - { - Environment = "prod" - }, - { - Environment = "dev" - } - ] -}`, - assertLength: 0, - }, - { - name: "ignore by list value with invalid index", - source: `#%s:ignore:*[someattr.-2.Environment=dev] -resource "aws_s3_bucket" "test" { - someattr = [ - { - Environment = "prod" - }, - { - Environment = "dev" - } - ] -}`, - assertLength: 1, - }, - { - name: "ignore by object value", - source: `#%s:ignore:*[tags.Environment=dev] -resource "aws_s3_bucket" "test" { - tags = { - Environment = "dev" - } -}`, - assertLength: 0, - }, - { - name: "ignore by object value in block", - source: `#%s:ignore:*[someblock.tags.Environment=dev] -resource "aws_s3_bucket" "test" { - someblock { - tags = { - Environment = "dev" - } - } -}`, - assertLength: 0, - }, - { - name: "ignore by list value in map", - source: ` -variable "testvar" { - type = map(list(string)) - default = { - server1 = ["web", "dev"] - server2 = ["prod"] - } -} - -#%s:ignore:*[someblock.someattr.server1.1=dev] -resource "aws_s3_bucket" "test" { - someblock { - someattr = var.testvar - } -} -`, - assertLength: 0, - }, - { - name: "ignore by alias", - source: `#%s:ignore:my-alias -resource "aws_s3_bucket" "test" {}`, - assertLength: 0, - }, - { - name: "ignore by alias with trivy prefix", - source: `#%s:ignore:my-alias -resource "aws_s3_bucket" "test" {}`, - assertLength: 0, - }, - { - name: "ignore for implied IAM resource", - source: `# %s:ignore:aws-iam-enforce-mfa -resource "aws_iam_group" "this" { - name = "group-name" -} - -resource "aws_iam_policy" "this" { - name = "test-policy" - policy = data.aws_iam_policy_document.this.json -} - - -resource "aws_iam_group_policy_attachment" "this" { - group = aws_iam_group.this.name - policy_arn = aws_iam_policy.this.arn -} - -data "aws_iam_policy_document" "this" { - statement { - sid = "PublishToCloudWatch" - actions = [ - "cloudwatch:PutMetricData", - ] - resources = ["*"] - } -}`, - assertLength: 0, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - prefixes := []string{"tfsec", "trivy"} - for _, prefix := range prefixes { - t.Run(prefix, func(t *testing.T) { - results := scanHCL( - t, formatWithSingleValue(tc.source, prefix), - rego.WithPolicyReader( - strings.NewReader(emptyBucketCheck), - strings.NewReader(enforceGroupMfaCheck)), - rego.WithPolicyNamespaces("user"), - ) - assert.Len(t, results.GetFailed(), tc.assertLength) - }) - } - }) - } -} - -func formatWithSingleValue(format string, value any) string { - count := strings.Count(format, "%s") - - args := make([]any, count) - for i := range args { - args[i] = value - } - - return fmt.Sprintf(format, args...) -} - -func Test_IgnoreByDynamicBlockValue(t *testing.T) { - - check := `# METADATA -# custom: -# avd_id: USER-TEST-0124 -# short_code: test -# provider: aws -# service: ec2 -package user.test124 - -import rego.v1 - -deny contains res if { - some group in input.aws.ec2.securitygroups - some rule in group.ingressrules - rule.toport.value < 1024 - res := result.new( - sprintf("Port below 1024 is not allowed, but got %s", [rule.toport.value]), - rule.toport, - ) -} -` - - tests := []struct { - name string - source string - expected int - }{ - { - name: "by dynamic value", - source: `// trivy:ignore:*[ingress.from_port=80] -resource "aws_security_group" "loadbalancer" { - name = "test" - - dynamic "ingress" { - for_each = [80, 443] - content { - from_port = ingress.value - to_port = ingress.value - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } - } -} -`, - expected: 0, - }, - { - name: "access by index", - source: `// trivy:ignore:*[ingress.0.from_port=80] -resource "aws_security_group" "loadbalancer" { - name = "test" - - dynamic "ingress" { - for_each = [80, 443] - content { - from_port = ingress.value - to_port = ingress.value - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } - } -} -`, - expected: 0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - results := scanHCL(t, tt.source, - rego.WithPolicyReader(strings.NewReader(check)), - rego.WithPolicyNamespaces("user")) - require.Len(t, results.GetFailed(), tt.expected) - }) - } -} - -func Test_IgnoreByWorkspace(t *testing.T) { - - tests := []struct { - name string - src string - expectedFailed int - }{ - { - name: "with expiry and workspace", - src: `# tfsec:ignore:aws-s3-non-empty-bucket:exp:2221-01-02:ws:testworkspace -resource "aws_s3_bucket" "test" {}`, - expectedFailed: 0, - }, - { - name: "bad workspace", - src: `# tfsec:ignore:aws-s3-non-empty-bucket:exp:2221-01-02:ws:otherworkspace -resource "aws_s3_bucket" "test" {}`, - expectedFailed: 1, - }, - { - name: "with expiry and workspace, trivy prefix", - src: `# trivy:ignore:aws-s3-non-empty-bucket:exp:2221-01-02:ws:testworkspace -resource "aws_s3_bucket" "test" {}`, - expectedFailed: 0, - }, - { - name: "bad workspace, trivy prefix", - src: `# trivy:ignore:aws-s3-non-empty-bucket:exp:2221-01-02:ws:otherworkspace -resource "aws_s3_bucket" "test" {}`, - expectedFailed: 1, - }, - { - name: "workspace with wildcard", - src: `# tfsec:ignore:*:ws:test* -resource "aws_s3_bucket" "test" {}`, - expectedFailed: 0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - results := scanHCL(t, tt.src, - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ScannerWithWorkspaceName("testworkspace"), - ) - assert.Len(t, results.GetFailed(), tt.expectedFailed) - }) - } -} - -func Test_IgnoreInlineByAVDID(t *testing.T) { - testCases := []struct { - input string - }{ - { - input: `resource "aws_s3_bucket" "test" { - bucket = "" # tfsec:ignore:%s -} - `, - }, - { - input: `resource "aws_s3_bucket" "test" { - bucket = "" # trivy:ignore:%s -} - `, - }, - } - - for _, tc := range testCases { - ids := []string{ - "USER-TEST-0123", strings.ToLower("user-test-0123"), - "non-empty-bucket", "aws-s3-non-empty-bucket", - } - - for _, id := range ids { - t.Run(id, func(t *testing.T) { - results := scanHCL(t, fmt.Sprintf(tc.input, id), - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ) - testutil.AssertRuleNotFailed(t, "aws-s3-non-empty-bucket", results, "") - }) - } - } -} - -func TestIgnoreRemoteTerraformResource(t *testing.T) { - - fsys := testutil.CreateFS(t, map[string]string{ - "main.tf": `module "bucket" { - source = "git::https://github.com/test/bucket" -}`, - ".terraform/modules/modules.json": `{ - "Modules": [ - { "Key": "", "Source": "", "Dir": "." }, - { - "Key": "bucket", - "Source": "git::https://github.com/test/bucket", - "Dir": ".terraform/modules/bucket" - } - ] -} -`, - ".terraform/modules/bucket/main.tf": ` -# trivy:ignore:user-test-0123 -resource "aws_s3_bucket" "test" { - bucket = "" -} -`, - }) - - results, err := scanFS(fsys, ".", - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ) - require.NoError(t, err) - testutil.AssertRuleNotFailed(t, "aws-s3-non-empty-bucket", results, "") -} diff --git a/pkg/iac/scanners/terraform/json_test.go b/pkg/iac/scanners/terraform/json_test.go deleted file mode 100644 index aad2d9ac8c32..000000000000 --- a/pkg/iac/scanners/terraform/json_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func TestScanningJSON(t *testing.T) { - - var tests = []struct { - name string - source string - expected bool - }{ - { - name: "check results are picked up in tf json configs", - source: ` - { - "provider": { - "aws": { - "profile": null, - "region": "eu-west-1" - } - }, - "resource": { - "aws_s3_bucket": { - "test": { - "bucket": "" - } - } - } - }`, - expected: true, - }, - { - name: "check attributes are checked in tf json configs", - source: ` - { - "provider": { - "aws": { - "profile": null, - "region": "eu-west-1" - } - }, - "resource": { - "aws_s3_bucket": { - "test": { - "bucket": "test" - } - } - } - }`, - expected: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - results := scanJSON(t, test.source, - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ) - if test.expected { - testutil.AssertRuleFound(t, "aws-s3-non-empty-bucket", results, "false negative found") - } else { - testutil.AssertRuleNotFound(t, "aws-s3-non-empty-bucket", results, "false positive found") - } - }) - } -} diff --git a/pkg/iac/scanners/terraform/module_test.go b/pkg/iac/scanners/terraform/module_test.go deleted file mode 100644 index a70ec36582e1..000000000000 --- a/pkg/iac/scanners/terraform/module_test.go +++ /dev/null @@ -1,380 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func Test_Modules(t *testing.T) { - tests := []struct { - name string - files map[string]string - expected bool - }{ - { - // IMPORTANT: if this test is failing, you probably need to set the version of go-cty in go.mod to the same version that hcl uses. - name: "go-cty compatibility issue", - files: map[string]string{ - "/project/main.tf": ` -data "aws_vpc" "default" { - default = true -} - -module "test" { - source = "../modules/problem/" - cidr_block = data.aws_vpc.default.cidr_block -}`, - "/modules/problem/main.tf": `variable "cidr_block" {} - -variable "open" { - default = false -} - -resource "aws_security_group" "this" { - name = "Test" - - ingress { - description = "HTTPs" - from_port = 443 - to_port = 443 - protocol = "tcp" - self = ! var.open - cidr_blocks = var.open ? [var.cidr_block] : null - } -} - -resource "aws_s3_bucket" "test" {}`, - }, - expected: true, - }, - { - name: "misconfig in sibling directory module", - files: map[string]string{ - "/project/main.tf": ` -module "something" { - source = "../modules/problem" -} -`, - "modules/problem/main.tf": ` -resource "aws_s3_bucket" "test" {}`, - }, - expected: true, - }, - { - name: "ignore misconfig in module", - files: map[string]string{ - "/project/main.tf": ` -#tfsec:ignore:aws-s3-non-empty-bucket -module "something" { - source = "../modules/problem" -} -`, - "modules/problem/main.tf": ` -resource "aws_s3_bucket" "test" {} -`, - }, - expected: false, - }, - { - name: "misconfig in subdirectory module", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "./modules/problem" -} -`, - "project/modules/problem/main.tf": ` -resource "aws_s3_bucket" "test" {} -`, - }, - expected: true, - }, - { - name: "misconfig in parent directory module", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "../problem" -} -`, - "problem/main.tf": ` -resource "aws_s3_bucket" "test" {} -`}, - expected: true, - }, - { - name: "misconfig in reused module", - files: map[string]string{ - "project/main.tf": ` -module "something_good" { - source = "../modules/problem" - bucket = "test" -} - -module "something_bad" { - source = "../modules/problem" - bucket = "" -} -`, - "modules/problem/main.tf": ` -variable "bucket" {} - -resource "aws_s3_bucket" "test" { - bucket = var.bucket -} -`}, - expected: true, - }, - { - name: "misconfig in nested module", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "../modules/a" -} -`, - "modules/a/main.tf": ` - module "something" { - source = "../../modules/b" -} -`, - "modules/b/main.tf": ` -module "something" { - source = "../c" -} -`, - "modules/c/main.tf": `resource "aws_s3_bucket" "test" {}`, - }, - expected: true, - }, - { - name: "misconfig in reused nested module", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "../modules/a" - bucket = "test" -} - -module "something-bad" { - source = "../modules/a" - bucket = "" -} -`, - "modules/a/main.tf": ` -variable "bucket" {} - -module "something" { - source = "../../modules/b" - bucket = var.bucket -} -`, - "modules/b/main.tf": ` -variable "bucket" {} - -module "something" { - source = "../c" - bucket = var.bad -} -`, - "modules/c/main.tf": ` -variable "bucket" {} - -resource "aws_s3_bucket" "test" { - bucket = var.bucket -} -`, - }, - expected: true, - }, - { - name: "misconfig in terraform cached module", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "../modules/somewhere" - bucket = "test" -} -`, - "modules/somewhere/main.tf": ` -module "something_nested" { - count = 1 - source = "github.com/some/module.git" - bucket = "" -} - -variable "bucket" { - default = "" -}`, - "project/.terraform/modules/something.something_nested/main.tf": ` -variable "bucket" {} - -resource "aws_s3_bucket" "test" { - bucket = var.bucket -} -`, - "project/.terraform/modules/modules.json": ` - {"Modules":[ - {"Key":"something","Source":"../modules/somewhere","Version":"2.35.0","Dir":"../modules/somewhere"}, - {"Key":"something.something_nested","Source":"git::https://github.com/some/module.git","Version":"2.35.0","Dir":".terraform/modules/something.something_nested"} - ]} -`, - }, - expected: true, - }, - { - name: "misconfig in reused terraform cached module", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "/nowhere" - bucket = "" -} - -module "something2" { - source = "/nowhere" - bucket = "" -} -`, - "project/.terraform/modules/a/main.tf": ` -variable "bucket" {} - -resource "aws_s3_bucket" "test" { - bucket = var.bucket -} -`, - "project/.terraform/modules/modules.json": ` - {"Modules":[{"Key":"something","Source":"/nowhere","Version":"2.35.0","Dir":".terraform/modules/a"},{"Key":"something2","Source":"/nowhere","Version":"2.35.0","Dir":".terraform/modules/a"}]} -`, - }, - expected: true, - }, - { - name: "misconfig in nested modules with duplicate module names and paths", - files: map[string]string{ - "project/main.tf": ` -module "something" { - source = "../modules/a" - s3_bucket_count = 0 -} - -module "something-bad" { - source = "../modules/a" - s3_bucket_count = 1 -} -`, - "modules/a/main.tf": ` -variable "s3_bucket_count" { - default = 0 -} -module "something" { - source = "../b" - s3_bucket_count = var.s3_bucket_count -} -`, - "modules/b/main.tf": ` -variable "s3_bucket_count" { - default = 0 -} -module "something" { - source = "../c" - s3_bucket_count = var.s3_bucket_count -} -`, - "modules/c/main.tf": ` -variable "s3_bucket_count" { - default = 0 -} - -resource "aws_s3_bucket" "test" { - count = var.s3_bucket_count -} -`, - }, - expected: true, - }, - { - name: "misconfigured attribute referencing to dynamic variable", - files: map[string]string{ - "project/main.tf": ` -resource "something" "this" { - dynamic "blah" { - for_each = ["a"] - content { - bucket = "" - } - } -} - -resource "aws_s3_bucket" "test" { - secure = something.this.blah[0].bucket -} -`}, - expected: true, - }, - { - name: "attribute referencing to dynamic variable without index", - files: map[string]string{ - "project/main.tf": ` -resource "something" "else" { - dynamic "blah" { - for_each = toset(["test"]) - content { - bucket = blah.value - } - } -} - -resource "aws_s3_bucket" "test" { - bucket = something.else.blah.bucket -}`}, - expected: false, - }, - { - name: "references passed to nested module", - files: map[string]string{ - "project/main.tf": ` - -resource "some_resource" "this" { - name = "test" -} - -module "something" { - source = "../modules/a" - bucket = some_resource.this.name -} -`, - "modules/a/main.tf": ` -variable "bucket" { - type = string -} - -resource "aws_s3_bucket" "test" { - bucket = var.bucket -} -`}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fsys := testutil.CreateFS(t, tt.files) - results, err := scanFS(fsys, "project", - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - ) - require.NoError(t, err) - if tt.expected { - testutil.AssertRuleFound(t, "aws-s3-non-empty-bucket", results, "") - } else { - testutil.AssertRuleNotFailed(t, "aws-s3-non-empty-bucket", results, "") - } - }) - } -} diff --git a/pkg/iac/scanners/terraform/options.go b/pkg/iac/scanners/terraform/options.go deleted file mode 100644 index 7547a19a8382..000000000000 --- a/pkg/iac/scanners/terraform/options.go +++ /dev/null @@ -1,110 +0,0 @@ -package terraform - -import ( - "io/fs" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/executor" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser" -) - -type ConfigurableTerraformScanner interface { - options.ConfigurableScanner - SetForceAllDirs(bool) - AddExecutorOptions(options ...executor.Option) - AddParserOptions(options ...parser.Option) -} - -func ScannerWithTFVarsPaths(paths ...string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithTFVarsPaths(paths...)) - } - } -} - -func ScannerWithWorkspaceName(name string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithWorkspaceName(name)) - tf.AddExecutorOptions(executor.OptionWithWorkspaceName(name)) - } - } -} - -func ScannerWithAllDirectories(all bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.SetForceAllDirs(all) - } - } -} - -func ScannerWithSkipDownloaded(skip bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if !skip { - return - } - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddExecutorOptions(executor.OptionWithResultsFilter(func(results scan.Results) scan.Results { - for i, result := range results { - prefix := result.Range().GetSourcePrefix() - if prefix != "" && !strings.HasPrefix(prefix, ".") { - results[i].OverrideStatus(scan.StatusIgnored) - } - } - return results - })) - } - } -} - -func ScannerWithDownloadsAllowed(allowed bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithDownloads(allowed)) - } - } -} - -func ScannerWithSkipCachedModules(b bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithSkipCachedModules(b)) - } - } -} - -func ScannerWithConfigsFileSystem(fsys fs.FS) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithConfigsFS(fsys)) - } - } -} - -func ScannerWithSkipFiles(files []string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithSkipFiles(files)) - } - } -} - -func ScannerWithSkipDirs(dirs []string) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionWithSkipDirs(dirs)) - } - } -} - -func ScannerWithStopOnHCLError(stop bool) options.ScannerOption { - return func(s options.ConfigurableScanner) { - if tf, ok := s.(ConfigurableTerraformScanner); ok { - tf.AddParserOptions(parser.OptionStopOnHCLError(stop)) - } - } -} diff --git a/pkg/iac/scanners/terraform/parser/evaluator.go b/pkg/iac/scanners/terraform/parser/evaluator.go deleted file mode 100644 index c2eaa0769653..000000000000 --- a/pkg/iac/scanners/terraform/parser/evaluator.go +++ /dev/null @@ -1,597 +0,0 @@ -package parser - -import ( - "context" - "errors" - "io/fs" - "reflect" - "slices" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/ext/typeexpr" - "github.com/samber/lo" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - tfcontext "github.com/aquasecurity/trivy/pkg/iac/terraform/context" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -const ( - maxContextIterations = 32 -) - -type evaluator struct { - logger *log.Logger - filesystem fs.FS - ctx *tfcontext.Context - blocks terraform.Blocks - inputVars map[string]cty.Value - moduleMetadata *modulesMetadata - projectRootPath string // root of the current scan - modulePath string - moduleName string - ignores ignore.Rules - parentParser *Parser - allowDownloads bool - skipCachedModules bool -} - -func newEvaluator( - target fs.FS, - parentParser *Parser, - projectRootPath string, - modulePath string, - workingDir string, - moduleName string, - blocks terraform.Blocks, - inputVars map[string]cty.Value, - moduleMetadata *modulesMetadata, - workspace string, - ignores ignore.Rules, - logger *log.Logger, - allowDownloads bool, - skipCachedModules bool, -) *evaluator { - - // create a context to store variables and make functions available - ctx := tfcontext.NewContext(&hcl.EvalContext{ - Functions: Functions(target, modulePath), - }, nil) - - // these variables are made available by terraform to each module - ctx.SetByDot(cty.StringVal(workspace), "terraform.workspace") - ctx.SetByDot(cty.StringVal(projectRootPath), "path.root") - ctx.SetByDot(cty.StringVal(modulePath), "path.module") - ctx.SetByDot(cty.StringVal(workingDir), "path.cwd") - - // each block gets its own scope to define variables in - for _, b := range blocks { - b.OverrideContext(ctx.NewChild()) - } - - return &evaluator{ - filesystem: target, - parentParser: parentParser, - modulePath: modulePath, - moduleName: moduleName, - projectRootPath: projectRootPath, - ctx: ctx, - blocks: blocks, - inputVars: inputVars, - moduleMetadata: moduleMetadata, - ignores: ignores, - logger: logger, - allowDownloads: allowDownloads, - skipCachedModules: skipCachedModules, - } -} - -func (e *evaluator) evaluateStep() { - - e.ctx.Set(e.getValuesByBlockType("variable"), "var") - e.ctx.Set(e.getValuesByBlockType("locals"), "local") - e.ctx.Set(e.getValuesByBlockType("provider"), "provider") - - for typ, resource := range e.getResources() { - e.ctx.Set(resource, typ) - } - - e.ctx.Set(e.getValuesByBlockType("data"), "data") - e.ctx.Set(e.getValuesByBlockType("output"), "output") - e.ctx.Set(e.getValuesByBlockType("module"), "module") -} - -// exportOutputs is used to export module outputs to the parent module -func (e *evaluator) exportOutputs() cty.Value { - data := make(map[string]cty.Value) - for _, block := range e.blocks.OfType("output") { - attr := block.GetAttribute("value") - if attr.IsNil() { - continue - } - data[block.Label()] = attr.Value() - e.logger.Debug( - "Added module output", - log.String("block", block.Label()), - log.String("value", attr.Value().GoString()), - ) - } - return cty.ObjectVal(data) -} - -func (e *evaluator) EvaluateAll(ctx context.Context) (terraform.Modules, map[string]fs.FS) { - - e.logger.Debug("Starting module evaluation...", log.String("path", e.modulePath)) - - fsKey := types.CreateFSKey(e.filesystem) - fsMap := map[string]fs.FS{ - fsKey: e.filesystem, - } - - e.evaluateSteps() - - // expand out resources and modules via count, for-each and dynamic - // (not a typo, we do this twice so every order is processed) - e.blocks = e.expandBlocks(e.blocks) - e.blocks = e.expandBlocks(e.blocks) - - // rootModule is initialized here, but not fully evaluated until all submodules are evaluated. - // Initializing it up front to keep the module hierarchy of parents correct. - rootModule := terraform.NewModule(e.projectRootPath, e.modulePath, e.blocks, e.ignores) - submodules := e.evaluateSubmodules(ctx, rootModule, fsMap) - - e.logger.Debug("Starting post-submodules evaluation...") - e.evaluateSteps() - - e.logger.Debug("Module evaluation complete.") - return append(terraform.Modules{rootModule}, submodules...), fsMap -} - -func (e *evaluator) evaluateSubmodules(ctx context.Context, parent *terraform.Module, fsMap map[string]fs.FS) terraform.Modules { - submodules := e.loadSubmodules(ctx) - - if len(submodules) == 0 { - return nil - } - - e.logger.Debug("Starting submodules evaluation...") - - for i := 0; i < maxContextIterations; i++ { - changed := false - for _, sm := range submodules { - changed = changed || e.evaluateSubmodule(ctx, sm) - } - if !changed { - e.logger.Debug("All submodules are evaluated", log.Int("loop", i)) - break - } - } - - e.logger.Debug("Starting post-submodule evaluation...") - e.evaluateSteps() - - var modules terraform.Modules - for _, sm := range submodules { - // Assign the parent placeholder to any submodules without a parent. Any modules - // with a parent already have their correct parent placeholder assigned. - for _, submod := range sm.modules { - if submod.Parent() == nil { - submod.SetParent(parent) - } - } - - modules = append(modules, sm.modules...) - for k, v := range sm.fsMap { - fsMap[k] = v - } - } - - e.logger.Debug("Finished processing submodule(s).", log.Int("count", len(modules))) - return modules -} - -type submodule struct { - definition *ModuleDefinition - eval *evaluator - modules terraform.Modules - lastState map[string]cty.Value - fsMap map[string]fs.FS -} - -func (e *evaluator) loadSubmodules(ctx context.Context) []*submodule { - var submodules []*submodule - - for _, definition := range e.loadModules(ctx) { - eval, err := definition.Parser.Load(ctx) - if errors.Is(err, ErrNoFiles) { - continue - } else if err != nil { - e.logger.Error("Failed to load submodule", log.String("name", definition.Name), log.Err(err)) - continue - } - - submodules = append(submodules, &submodule{ - definition: definition, - eval: eval, - fsMap: make(map[string]fs.FS), - }) - } - - return submodules -} - -func (e *evaluator) evaluateSubmodule(ctx context.Context, sm *submodule) bool { - inputVars := sm.definition.inputVars() - if len(sm.modules) > 0 { - if reflect.DeepEqual(inputVars, sm.lastState) { - e.logger.Debug("Submodule inputs unchanged", log.String("name", sm.definition.Name)) - return false - } - } - - e.logger.Debug("Evaluating submodule", log.String("name", sm.definition.Name)) - sm.eval.inputVars = inputVars - sm.modules, sm.fsMap = sm.eval.EvaluateAll(ctx) - outputs := sm.eval.exportOutputs() - - // lastState needs to be captured after applying outputs – so that they - // don't get treated as changes – but before running post-submodule - // evaluation, so that changes from that can trigger re-evaluations of - // the submodule if/when they feed back into inputs. - e.ctx.Set(outputs, "module", sm.definition.Name) - sm.lastState = sm.definition.inputVars() - e.evaluateSteps() - return true -} - -func (e *evaluator) evaluateSteps() { - var lastContext hcl.EvalContext - for i := 0; i < maxContextIterations; i++ { - - e.logger.Debug("Starting iteration", log.Int("iteration", i)) - e.evaluateStep() - - // if ctx matches the last evaluation, we can bail, nothing left to resolve - if i > 0 && reflect.DeepEqual(lastContext.Variables, e.ctx.Inner().Variables) { - e.logger.Debug("Context unchanged", log.Int("iteration", i)) - break - } - if len(e.ctx.Inner().Variables) != len(lastContext.Variables) { - lastContext.Variables = make(map[string]cty.Value, len(e.ctx.Inner().Variables)) - } - for k, v := range e.ctx.Inner().Variables { - lastContext.Variables[k] = v - } - } -} - -func (e *evaluator) expandBlocks(blocks terraform.Blocks) terraform.Blocks { - return e.expandDynamicBlocks(e.expandBlockForEaches(e.expandBlockCounts(blocks))...) -} - -func (e *evaluator) expandDynamicBlocks(blocks ...*terraform.Block) terraform.Blocks { - for _, b := range blocks { - if err := b.ExpandBlock(); err != nil { - e.logger.Debug(`Failed to expand dynamic block.`, - log.String("block", b.FullName()), log.Err(err)) - } - } - return blocks -} - -func isBlockSupportsForEachMetaArgument(block *terraform.Block) bool { - return slices.Contains([]string{ - "module", - "resource", - "data", - }, block.Type()) -} - -func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks) terraform.Blocks { - - var forEachFiltered terraform.Blocks - - for _, block := range blocks { - - forEachAttr := block.GetAttribute("for_each") - - if forEachAttr.IsNil() || block.IsExpanded() || !isBlockSupportsForEachMetaArgument(block) { - forEachFiltered = append(forEachFiltered, block) - continue - } - - forEachVal := forEachAttr.Value() - - if forEachVal.IsNull() || !forEachVal.IsKnown() || !forEachAttr.IsIterable() { - e.logger.Debug(`Failed to expand block. Invalid "for-each" argument. Must be known and iterable.`, - log.String("block", block.FullName()), - log.String("value", forEachVal.GoString()), - ) - continue - } - - clones := make(map[string]cty.Value) - _ = forEachAttr.Each(func(key cty.Value, val cty.Value) { - - if val.IsNull() { - return - } - - // instances are identified by a map key (or set member) from the value provided to for_each - idx, err := convert.Convert(key, cty.String) - if err != nil { - e.logger.Debug( - `Failed to expand block. Invalid "for-each" argument: map key (or set value) is not a string`, - log.String("block", block.FullName()), - log.String("key", key.GoString()), - log.String("value", val.GoString()), - log.Err(err), - ) - return - } - - // if the argument is a collection but not a map, then the resource identifier - // is the value of the collection. The exception is the use of for-each inside a dynamic block, - // because in this case the collection element may not be a primitive value. - if (forEachVal.Type().IsCollectionType() || forEachVal.Type().IsTupleType()) && - !forEachVal.Type().IsMapType() { - stringVal, err := convert.Convert(val, cty.String) - if err != nil { - e.logger.Debug( - "Failed to expand block. Invalid 'for-each' argument: value is not a string", - log.String("block", block.FullName()), - log.String("key", idx.AsString()), - log.String("value", val.GoString()), - log.Err(err), - ) - return - } - idx = stringVal - } - - clone := block.Clone(idx) - ctx := clone.Context() - e.copyVariables(block, clone) - - eachObj := cty.ObjectVal(map[string]cty.Value{ - "key": idx, - "value": val, - }) - - ctx.Set(eachObj, "each") - ctx.Set(eachObj, block.TypeLabel()) - forEachFiltered = append(forEachFiltered, clone) - clones[idx.AsString()] = clone.Values() - }) - - metadata := block.GetMetadata() - if len(clones) == 0 { - e.ctx.SetByDot(cty.EmptyTupleVal, metadata.Reference()) - } else { - // The for-each meta-argument creates multiple instances of the resource that are stored in the map. - // So we must replace the old resource with a map with the attributes of the resource. - e.ctx.Replace(cty.ObjectVal(clones), metadata.Reference()) - } - e.logger.Debug("Expanded block into clones via 'for_each' attribute.", - log.String("block", block.FullName()), - log.Int("clones", len(clones)), - ) - } - - return forEachFiltered -} - -func isBlockSupportsCountMetaArgument(block *terraform.Block) bool { - return slices.Contains([]string{ - "module", - "resource", - "data", - }, block.Type()) -} - -func (e *evaluator) expandBlockCounts(blocks terraform.Blocks) terraform.Blocks { - var countFiltered terraform.Blocks - for _, block := range blocks { - countAttr := block.GetAttribute("count") - if countAttr.IsNil() || block.IsExpanded() || !isBlockSupportsCountMetaArgument(block) { - countFiltered = append(countFiltered, block) - continue - } - count := 1 - countAttrVal := countAttr.Value() - if !countAttrVal.IsNull() && countAttrVal.IsKnown() && countAttrVal.Type() == cty.Number { - count = int(countAttr.AsNumber()) - } - - var clones []cty.Value - for i := 0; i < count; i++ { - clone := block.Clone(cty.NumberIntVal(int64(i))) - clones = append(clones, clone.Values()) - countFiltered = append(countFiltered, clone) - metadata := clone.GetMetadata() - e.ctx.SetByDot(clone.Values(), metadata.Reference()) - } - metadata := block.GetMetadata() - if len(clones) == 0 { - e.ctx.SetByDot(cty.EmptyTupleVal, metadata.Reference()) - } else { - e.ctx.SetByDot(cty.TupleVal(clones), metadata.Reference()) - } - e.logger.Debug( - "Expanded block into clones via 'count' attribute.", - log.String("block", block.FullName()), - log.Int("clones", len(clones)), - ) - } - - return countFiltered -} - -func (e *evaluator) copyVariables(from, to *terraform.Block) { - - var fromBase string - var fromRel string - var toRel string - - switch from.Type() { - case "resource": - fromBase = from.TypeLabel() - fromRel = from.NameLabel() - toRel = to.NameLabel() - case "module": - fromBase = from.Type() - fromRel = from.TypeLabel() - toRel = to.TypeLabel() - default: - return - } - - rootCtx := e.ctx.Root() - srcValue := rootCtx.Get(fromBase, fromRel) - if srcValue == cty.NilVal { - return - } - rootCtx.Set(srcValue, fromBase, toRel) -} - -func (e *evaluator) evaluateVariable(b *terraform.Block) (cty.Value, error) { - if b.Label() == "" { - return cty.NilVal, errors.New("empty label - cannot resolve") - } - - attributes := b.Attributes() - if attributes == nil { - return cty.NilVal, errors.New("cannot resolve variable with no attributes") - } - - var valType cty.Type - var defaults *typeexpr.Defaults - if typeAttr, exists := attributes["type"]; exists { - ty, def, err := typeAttr.DecodeVarType() - if err != nil { - return cty.NilVal, err - } - valType = ty - defaults = def - } - - var val cty.Value - - if override, exists := e.inputVars[b.Label()]; exists && override.Type() != cty.NilType { - val = override - } else if def, exists := attributes["default"]; exists { - val = def.NullableValue() - } else { - return cty.NilVal, errors.New("no value found") - } - - if valType != cty.NilType { - if defaults != nil { - val = defaults.Apply(val) - } - - typedVal, err := convert.Convert(val, valType) - if err != nil { - return cty.NilVal, err - } - return typedVal, nil - } - - return val, nil - -} - -func (e *evaluator) evaluateOutput(b *terraform.Block) (cty.Value, error) { - if b.Label() == "" { - return cty.NilVal, errors.New("empty label - cannot resolve") - } - - attribute := b.GetAttribute("value") - if attribute.IsNil() { - return cty.NilVal, errors.New("cannot resolve output with no attributes") - } - return attribute.Value(), nil -} - -// returns true if all evaluations were successful -func (e *evaluator) getValuesByBlockType(blockType string) cty.Value { - - blocksOfType := e.blocks.OfType(blockType) - values := make(map[string]cty.Value) - - for _, b := range blocksOfType { - - switch b.Type() { - case "variable": // variables are special in that their value comes from the "default" attribute - val, err := e.evaluateVariable(b) - if err != nil { - continue - } - values[b.Label()] = val - case "output": - val, err := e.evaluateOutput(b) - if err != nil { - continue - } - values[b.Label()] = val - case "locals", "moved", "import": - for key, val := range b.Values().AsValueMap() { - values[key] = val - } - case "provider", "module", "check": - if b.Label() == "" { - continue - } - values[b.Label()] = b.Values() - case "data": - if len(b.Labels()) < 2 { - continue - } - - blockMap, ok := values[b.Labels()[0]] - if !ok { - values[b.Labels()[0]] = cty.ObjectVal(make(map[string]cty.Value)) - blockMap = values[b.Labels()[0]] - } - - valueMap := blockMap.AsValueMap() - if valueMap == nil { - valueMap = make(map[string]cty.Value) - } - - valueMap[b.Labels()[1]] = b.Values() - values[b.Labels()[0]] = cty.ObjectVal(valueMap) - } - } - - return cty.ObjectVal(values) -} - -func (e *evaluator) getResources() map[string]cty.Value { - values := make(map[string]map[string]cty.Value) - - for _, b := range e.blocks { - if b.Type() != "resource" { - continue - } - - if len(b.Labels()) < 2 { - continue - } - - val, exists := values[b.Labels()[0]] - if !exists { - val = make(map[string]cty.Value) - values[b.Labels()[0]] = val - } - val[b.Labels()[1]] = b.Values() - } - - return lo.MapValues(values, func(v map[string]cty.Value, _ string) cty.Value { - return cty.ObjectVal(v) - }) -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/cidr.go b/pkg/iac/scanners/terraform/parser/funcs/cidr.go deleted file mode 100644 index 23b1c7be0d45..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/cidr.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "fmt" - "math/big" - - "github.com/apparentlymart/go-cidr/cidr" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" -) - -// CidrHostFunc constructs a function that calculates a full host IP address -// within a given IP network address prefix. -var CidrHostFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - { - Name: "hostnum", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var hostNum *big.Int - if err := gocty.FromCtyValue(args[1], &hostNum); err != nil { - return cty.UnknownVal(cty.String), err - } - _, network, err := ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) - } - - ip, err := cidr.HostBig(network, hostNum) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(ip.String()), nil - }, -}) - -// CidrNetmaskFunc constructs a function that converts an IPv4 address prefix given -// in CIDR notation into a subnet mask address. -var CidrNetmaskFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - _, network, err := ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) - } - - if network.IP.To4() == nil { - return cty.UnknownVal(cty.String), fmt.Errorf("IPv6 addresses cannot have a netmask: %s", args[0].AsString()) - } - - return cty.StringVal(IP(network.Mask).String()), nil - }, -}) - -// CidrSubnetFunc constructs a function that calculates a subnet address within -// a given IP network address prefix. -var CidrSubnetFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - { - Name: "newbits", - Type: cty.Number, - }, - { - Name: "netnum", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var newbits int - if err := gocty.FromCtyValue(args[1], &newbits); err != nil { - return cty.UnknownVal(cty.String), err - } - var netnum *big.Int - if err := gocty.FromCtyValue(args[2], &netnum); err != nil { - return cty.UnknownVal(cty.String), err - } - - _, network, err := ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) - } - - newNetwork, err := cidr.SubnetBig(network, newbits, netnum) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(newNetwork.String()), nil - }, -}) - -// CidrSubnetsFunc is similar to CidrSubnetFunc but calculates many consecutive -// subnet addresses at once, rather than just a single subnet extension. -var CidrSubnetsFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - }, - VarParam: &function.Parameter{ - Name: "newbits", - Type: cty.Number, - }, - Type: function.StaticReturnType(cty.List(cty.String)), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - _, network, err := ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid CIDR expression: %s", err) - } - startPrefixLen, _ := network.Mask.Size() - - prefixLengthArgs := args[1:] - if len(prefixLengthArgs) == 0 { - return cty.ListValEmpty(cty.String), nil - } - - var firstLength int - if err := gocty.FromCtyValue(prefixLengthArgs[0], &firstLength); err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(1, err) - } - firstLength += startPrefixLen - - retVals := make([]cty.Value, len(prefixLengthArgs)) - - current, _ := cidr.PreviousSubnet(network, firstLength) - for i, lengthArg := range prefixLengthArgs { - var length int - if err := gocty.FromCtyValue(lengthArg, &length); err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(i+1, err) - } - - if length < 1 { - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "must extend prefix by at least one bit") - } - // For portability with 32-bit systems where the subnet number - // will be a 32-bit int, we only allow extension of 32 bits in - // one call even if we're running on a 64-bit machine. - // (Of course, this is significant only for IPv6.) - if length > 32 { - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "may not extend prefix by more than 32 bits") - } - length += startPrefixLen - if length > (len(network.IP) * 8) { - protocol := "IP" - switch len(network.IP) * 8 { - case 32: - protocol = "IPv4" - case 128: - protocol = "IPv6" - } - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "would extend prefix to %d bits, which is too long for an %s address", length, protocol) - } - - next, rollover := cidr.NextSubnet(current, length) - if rollover || !network.Contains(next.IP) { - // If we run out of suffix bits in the base CIDR prefix then - // NextSubnet will start incrementing the prefix bits, which - // we don't allow because it would then allocate addresses - // outside of the caller's given prefix. - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "not enough remaining address space for a subnet with a prefix of %d bits after %s", length, current.String()) - } - - current = next - retVals[i] = cty.StringVal(current.String()) - } - - return cty.ListVal(retVals), nil - }, -}) diff --git a/pkg/iac/scanners/terraform/parser/funcs/collection.go b/pkg/iac/scanners/terraform/parser/funcs/collection.go deleted file mode 100644 index 687b39b209f1..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/collection.go +++ /dev/null @@ -1,652 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "errors" - "fmt" - "math/big" - "sort" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/function/stdlib" - "github.com/zclconf/go-cty/cty/gocty" -) - -var LengthFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "value", - Type: cty.DynamicPseudoType, - AllowDynamicType: true, - AllowUnknown: true, - AllowMarked: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - collTy := args[0].Type() - switch { - case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType: - return cty.Number, nil - default: - return cty.Number, errors.New("argument must be a string, a collection type, or a structural type") - } - }, - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - coll := args[0] - collTy := args[0].Type() - marks := coll.Marks() - switch { - case collTy == cty.DynamicPseudoType: - return cty.UnknownVal(cty.Number).WithMarks(marks), nil - case collTy.IsTupleType(): - l := len(collTy.TupleElementTypes()) - return cty.NumberIntVal(int64(l)).WithMarks(marks), nil - case collTy.IsObjectType(): - l := len(collTy.AttributeTypes()) - return cty.NumberIntVal(int64(l)).WithMarks(marks), nil - case collTy == cty.String: - // We'll delegate to the cty stdlib strlen function here, because - // it deals with all of the complexities of tokenizing unicode - // grapheme clusters. - return stdlib.Strlen(coll) - case collTy.IsListType() || collTy.IsSetType() || collTy.IsMapType(): - return coll.Length(), nil - default: - // Should never happen, because of the checks in our Type func above - return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)") - } - }, -}) - -// AllTrueFunc constructs a function that returns true if all elements of the -// list are true. If the list is empty, return true. -var AllTrueFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.List(cty.Bool), - }, - }, - Type: function.StaticReturnType(cty.Bool), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - result := cty.True - for it := args[0].ElementIterator(); it.Next(); { - _, v := it.Element() - if !v.IsKnown() { - return cty.UnknownVal(cty.Bool), nil - } - if v.IsNull() { - return cty.False, nil - } - result = result.And(v) - if result.False() { - return cty.False, nil - } - } - return result, nil - }, -}) - -// AnyTrueFunc constructs a function that returns true if any element of the -// list is true. If the list is empty, return false. -var AnyTrueFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.List(cty.Bool), - }, - }, - Type: function.StaticReturnType(cty.Bool), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - result := cty.False - var hasUnknown bool - for it := args[0].ElementIterator(); it.Next(); { - _, v := it.Element() - if !v.IsKnown() { - hasUnknown = true - continue - } - if v.IsNull() { - continue - } - result = result.Or(v) - if result.True() { - return cty.True, nil - } - } - if hasUnknown { - return cty.UnknownVal(cty.Bool), nil - } - return result, nil - }, -}) - -// CoalesceFunc constructs a function that takes any number of arguments and -// returns the first one that isn't empty. This function was copied from go-cty -// stdlib and modified so that it returns the first *non-empty* non-null element -// from a sequence, instead of merely the first non-null. -var CoalesceFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - argTypes := make([]cty.Type, len(args)) - for i, val := range args { - argTypes[i] = val.Type() - } - retType, _ := convert.UnifyUnsafe(argTypes) - if retType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - return retType, nil - }, - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - for _, argVal := range args { - // We already know this will succeed because of the checks in our Type func above - argVal, _ = convert.Convert(argVal, retType) - if !argVal.IsKnown() { - return cty.UnknownVal(retType), nil - } - if argVal.IsNull() { - continue - } - if retType == cty.String && argVal.RawEquals(cty.StringVal("")) { - continue - } - - return argVal, nil - } - return cty.NilVal, errors.New("no non-null, non-empty-string arguments") - }, -}) - -// IndexFunc constructs a function that finds the element index for a given value in a list. -var IndexFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - { - Name: "value", - Type: cty.DynamicPseudoType, - }, - }, - Type: function.StaticReturnType(cty.Number), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) { - return cty.NilVal, errors.New("argument must be a list or tuple") - } - - if !args[0].IsKnown() { - return cty.UnknownVal(cty.Number), nil - } - - if args[0].LengthInt() == 0 { // Easy path - return cty.NilVal, errors.New("cannot search an empty list") - } - - for it := args[0].ElementIterator(); it.Next(); { - i, v := it.Element() - eq, err := stdlib.Equal(v, args[1]) - if err != nil { - return cty.NilVal, err - } - if !eq.IsKnown() { - return cty.UnknownVal(cty.Number), nil - } - if eq.True() { - return i, nil - } - } - return cty.NilVal, errors.New("item not found") - - }, -}) - -// LookupFunc constructs a function that performs dynamic lookups of map types. -var LookupFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "inputMap", - Type: cty.DynamicPseudoType, - AllowMarked: true, - }, - { - Name: "key", - Type: cty.String, - AllowMarked: true, - }, - }, - VarParam: &function.Parameter{ - Name: "default", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - AllowMarked: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) < 1 || len(args) > 3 { - return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args)) - } - - ty := args[0].Type() - - switch { - case ty.IsObjectType(): - if !args[1].IsKnown() { - return cty.DynamicPseudoType, nil - } - - keyVal, _ := args[1].Unmark() - key := keyVal.AsString() - if ty.HasAttribute(key) { - return args[0].GetAttr(key).Type(), nil - } else if len(args) == 3 { - // if the key isn't found but a default is provided, - // return the default type - return args[2].Type(), nil - } - return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key) - case ty.IsMapType(): - if len(args) == 3 { - _, err = convert.Convert(args[2], ty.ElementType()) - if err != nil { - return cty.NilType, function.NewArgErrorf(2, "the default value must have the same type as the map elements") - } - } - return ty.ElementType(), nil - default: - return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument") - } - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var defaultVal cty.Value - defaultValueSet := false - - if len(args) == 3 { - // intentionally leave default value marked - defaultVal = args[2] - defaultValueSet = true - } - - // keep track of marks from the collection and key - var markses []cty.ValueMarks - - // unmark collection, retain marks to reapply later - mapVar, mapMarks := args[0].Unmark() - markses = append(markses, mapMarks) - - // include marks on the key in the result - keyVal, keyMarks := args[1].Unmark() - if len(keyMarks) > 0 { - markses = append(markses, keyMarks) - } - lookupKey := keyVal.AsString() - - if !mapVar.IsKnown() { - return cty.UnknownVal(retType).WithMarks(markses...), nil - } - - if mapVar.Type().IsObjectType() { - if mapVar.Type().HasAttribute(lookupKey) { - return mapVar.GetAttr(lookupKey).WithMarks(markses...), nil - } - } else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True { - return mapVar.Index(cty.StringVal(lookupKey)).WithMarks(markses...), nil - } - - if defaultValueSet { - defaultVal, err = convert.Convert(defaultVal, retType) - if err != nil { - return cty.NilVal, err - } - return defaultVal.WithMarks(markses...), nil - } - - return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf( - "lookup failed to find key %s", redactIfSensitive(lookupKey, keyMarks)) - }, -}) - -// MatchkeysFunc constructs a function that constructs a new list by taking a -// subset of elements from one list whose indexes match the corresponding -// indexes of values in another list. -var MatchkeysFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "values", - Type: cty.List(cty.DynamicPseudoType), - }, - { - Name: "keys", - Type: cty.List(cty.DynamicPseudoType), - }, - { - Name: "searchset", - Type: cty.List(cty.DynamicPseudoType), - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()}) - if ty == cty.NilType { - return cty.NilType, errors.New("keys and searchset must be of the same type") - } - - // the return type is based on args[0] (values) - return args[0].Type(), nil - }, - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - if !args[0].IsKnown() { - return cty.UnknownVal(cty.List(retType.ElementType())), nil - } - - if args[0].LengthInt() != args[1].LengthInt() { - return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal") - } - - output := make([]cty.Value, 0) - values := args[0] - - // Keys and searchset must be the same type. - // We can skip error checking here because we've already verified that - // they can be unified in the Type function - ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()}) - keys, _ := convert.Convert(args[1], ty) - searchset, _ := convert.Convert(args[2], ty) - - // if searchset is empty, return an empty list. - if searchset.LengthInt() == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - - if !values.IsWhollyKnown() || !keys.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - - i := 0 - for it := keys.ElementIterator(); it.Next(); { - _, key := it.Element() - for iter := searchset.ElementIterator(); iter.Next(); { - _, search := iter.Element() - eq, err := stdlib.Equal(key, search) - if err != nil { - return cty.NilVal, err - } - if !eq.IsKnown() { - return cty.ListValEmpty(retType.ElementType()), nil - } - if eq.True() { - v := values.Index(cty.NumberIntVal(int64(i))) - output = append(output, v) - break - } - } - i++ - } - - // if we haven't matched any key, then output is an empty list. - if len(output) == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - return cty.ListVal(output), nil - }, -}) - -// OneFunc returns either the first element of a one-element list, or null -// if given a zero-element list. -var OneFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - ty := args[0].Type() - switch { - case ty.IsListType() || ty.IsSetType(): - return ty.ElementType(), nil - case ty.IsTupleType(): - etys := ty.TupleElementTypes() - switch len(etys) { - case 0: - // No specific type information, so we'll ultimately return - // a null value of unknown type. - return cty.DynamicPseudoType, nil - case 1: - return etys[0], nil - } - } - return cty.NilType, function.NewArgErrorf(0, "must be a list, set, or tuple value with either zero or one elements") - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - val := args[0] - ty := val.Type() - - // Our parameter spec above doesn't set AllowUnknown or AllowNull, - // so we can assume our top-level collection is both known and non-null - // in here. - - switch { - case ty.IsListType() || ty.IsSetType(): - lenVal := val.Length() - if !lenVal.IsKnown() { - return cty.UnknownVal(retType), nil - } - var l int - err := gocty.FromCtyValue(lenVal, &l) - if err != nil { - // It would be very strange to get here, because that would - // suggest that the length is either not a number or isn't - // an integer, which would suggest a bug in cty. - return cty.NilVal, fmt.Errorf("invalid collection length: %s", err) - } - switch l { - case 0: - return cty.NullVal(retType), nil - case 1: - var ret cty.Value - // We'll use an iterator here because that works for both lists - // and sets, whereas indexing directly would only work for lists. - // Since we've just checked the length, we should only actually - // run this loop body once. - for it := val.ElementIterator(); it.Next(); { - _, ret = it.Element() - } - return ret, nil - } - case ty.IsTupleType(): - etys := ty.TupleElementTypes() - switch len(etys) { - case 0: - return cty.NullVal(retType), nil - case 1: - ret := val.Index(cty.NumberIntVal(0)) - return ret, nil - } - } - return cty.NilVal, function.NewArgErrorf(0, "must be a list, set, or tuple value with either zero or one elements") - }, -}) - -// SumFunc constructs a function that returns the sum of all -// numbers provided in a list -var SumFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - }, - Type: function.StaticReturnType(cty.Number), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - - if !args[0].CanIterateElements() { - return cty.NilVal, function.NewArgErrorf(0, "cannot sum noniterable") - } - - if args[0].LengthInt() == 0 { // Easy path - return cty.NilVal, function.NewArgErrorf(0, "cannot sum an empty list") - } - - arg := args[0].AsValueSlice() - ty := args[0].Type() - - if !ty.IsListType() && !ty.IsSetType() && !ty.IsTupleType() { - return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple. Received %s", ty.FriendlyName()) - } - - if !args[0].IsWhollyKnown() { - return cty.UnknownVal(cty.Number), nil - } - - // big.Float.Add can panic if the input values are opposing infinities, - // so we must catch that here in order to remain within - // the cty Function abstraction. - defer func() { - if r := recover(); r != nil { - if _, ok := r.(big.ErrNaN); ok { - ret = cty.NilVal - err = errors.New("can't compute sum of opposing infinities") - } else { - // not a panic we recognize - panic(r) - } - } - }() - - s := arg[0] - if s.IsNull() { - return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") - } - s, err = convert.Convert(s, cty.Number) - if err != nil { - return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") - } - for _, v := range arg[1:] { - if v.IsNull() { - return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") - } - v, err = convert.Convert(v, cty.Number) - if err != nil { - return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") - } - s = s.Add(v) - } - - return s, nil - }, -}) - -// TransposeFunc constructs a function that takes a map of lists of strings and -// swaps the keys and values to produce a new map of lists of strings. -var TransposeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "values", - Type: cty.Map(cty.List(cty.String)), - }, - }, - Type: function.StaticReturnType(cty.Map(cty.List(cty.String))), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - inputMap := args[0] - if !inputMap.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - - outputMap := make(map[string]cty.Value) - tmpMap := make(map[string][]string) - - for it := inputMap.ElementIterator(); it.Next(); { - inKey, inVal := it.Element() - for iter := inVal.ElementIterator(); iter.Next(); { - _, val := iter.Element() - if !val.Type().Equals(cty.String) { - return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings") - } - - outKey := val.AsString() - if _, ok := tmpMap[outKey]; !ok { - tmpMap[outKey] = make([]string, 0) - } - outVal := tmpMap[outKey] - outVal = append(outVal, inKey.AsString()) - sort.Strings(outVal) - tmpMap[outKey] = outVal - } - } - - for outKey, outVal := range tmpMap { - values := make([]cty.Value, 0) - for _, v := range outVal { - values = append(values, cty.StringVal(v)) - } - outputMap[outKey] = cty.ListVal(values) - } - - if len(outputMap) == 0 { - return cty.MapValEmpty(cty.List(cty.String)), nil - } - - return cty.MapVal(outputMap), nil - }, -}) - -// ListFunc constructs a function that takes an arbitrary number of arguments -// and returns a list containing those values in the same order. -// -// This function is deprecated in Terraform v0.12 -var ListFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - return cty.DynamicPseudoType, errors.New("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list") - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - return cty.DynamicVal, errors.New("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list") - }, -}) - -// MapFunc constructs a function that takes an even number of arguments and -// returns a map whose elements are constructed from consecutive pairs of arguments. -// -// This function is deprecated in Terraform v0.12 -var MapFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - return cty.DynamicPseudoType, errors.New("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map") - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - return cty.DynamicVal, errors.New("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map") - }, -}) diff --git a/pkg/iac/scanners/terraform/parser/funcs/conversion.go b/pkg/iac/scanners/terraform/parser/funcs/conversion.go deleted file mode 100644 index 18a1310e69f7..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/conversion.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "strconv" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/function" -) - -// MakeToFunc constructs a "to..." function, like "tostring", which converts -// its argument to a specific type or type kind. -// -// The given type wantTy can be any type constraint that cty's "convert" package -// would accept. In particular, this means that you can pass -// cty.List(cty.DynamicPseudoType) to mean "list of any single type", which -// will then cause cty to attempt to unify all of the element types when given -// a tuple. -func MakeToFunc(wantTy cty.Type) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "v", - // We use DynamicPseudoType rather than wantTy here so that - // all values will pass through the function API verbatim and - // we can handle the conversion logic within the Type and - // Impl functions. This allows us to customize the error - // messages to be more appropriate for an explicit type - // conversion, whereas the cty function system produces - // messages aimed at _implicit_ type conversions. - Type: cty.DynamicPseudoType, - AllowNull: true, - AllowMarked: true, - AllowDynamicType: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - gotTy := args[0].Type() - if gotTy.Equals(wantTy) { - return wantTy, nil - } - conv := convert.GetConversionUnsafe(args[0].Type(), wantTy) - if conv == nil { - // We'll use some specialized errors for some trickier cases, - // but most we can handle in a simple way. - switch { - case gotTy.IsTupleType() && wantTy.IsTupleType(): - return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) - case gotTy.IsObjectType() && wantTy.IsObjectType(): - return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) - default: - return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) - } - } - // If a conversion is available then everything is fine. - return wantTy, nil - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - // We didn't set "AllowUnknown" on our argument, so it is guaranteed - // to be known here but may still be null. - ret, err := convert.Convert(args[0], retType) - if err != nil { - val, _ := args[0].UnmarkDeep() - // Because we used GetConversionUnsafe above, conversion can - // still potentially fail in here. For example, if the user - // asks to convert the string "a" to bool then we'll - // optimistically permit it during type checking but fail here - // once we note that the value isn't either "true" or "false". - gotTy := val.Type() - switch { - case Contains(args[0], MarkedSensitive): - // Generic message so we won't inadvertently disclose - // information about sensitive values. - return cty.NilVal, function.NewArgErrorf(0, "cannot convert this sensitive %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) - - case gotTy == cty.String && wantTy == cty.Bool: - what := "string" - if !val.IsNull() { - what = strconv.Quote(val.AsString()) - } - return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what) - case gotTy == cty.String && wantTy == cty.Number: - what := "string" - if !val.IsNull() { - what = strconv.Quote(val.AsString()) - } - return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what) - default: - return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) - } - } - return ret, nil - }, - }) -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/crypto.go b/pkg/iac/scanners/terraform/parser/funcs/crypto.go deleted file mode 100644 index a7fe0b654d4e..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/crypto.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "crypto/md5" // nolint: gosec - "crypto/rsa" - "crypto/sha1" // nolint: gosec - "crypto/sha256" - "crypto/sha512" - "encoding/asn1" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "hash" - "io" - "io/fs" - "strings" - - uuidv5 "github.com/google/uuid" - uuid "github.com/hashicorp/go-uuid" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" - "golang.org/x/crypto/bcrypt" - "golang.org/x/crypto/ssh" -) - -var UUIDFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - result, err := uuid.GenerateUUID() - if err != nil { - return cty.UnknownVal(cty.String), err - } - return cty.StringVal(result), nil - }, -}) - -var UUIDV5Func = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "namespace", - Type: cty.String, - }, - { - Name: "name", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var namespace uuidv5.UUID - switch { - case args[0].AsString() == "dns": - namespace = uuidv5.NameSpaceDNS - case args[0].AsString() == "url": - namespace = uuidv5.NameSpaceURL - case args[0].AsString() == "oid": - namespace = uuidv5.NameSpaceOID - case args[0].AsString() == "x500": - namespace = uuidv5.NameSpaceX500 - default: - if namespace, err = uuidv5.Parse(args[0].AsString()); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("uuidv5() doesn't support namespace %s (%v)", args[0].AsString(), err) - } - } - val := args[1].AsString() - return cty.StringVal(uuidv5.NewSHA1(namespace, []byte(val)).String()), nil - }, -}) - -// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string -// and encodes it with Base64. -var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString) - -// MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileBase64Sha256Func(target fs.FS, baseDir string) function.Function { - return makeFileHashFunction(target, baseDir, sha256.New, base64.StdEncoding.EncodeToString) -} - -// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string -// and encodes it with Base64. -var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString) - -// MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileBase64Sha512Func(target fs.FS, baseDir string) function.Function { - return makeFileHashFunction(target, baseDir, sha512.New, base64.StdEncoding.EncodeToString) -} - -// BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher. -var BcryptFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - VarParam: &function.Parameter{ - Name: "cost", - Type: cty.Number, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - defaultCost := 10 - - if len(args) > 1 { - var val int - if err := gocty.FromCtyValue(args[1], &val); err != nil { - return cty.UnknownVal(cty.String), err - } - defaultCost = val - } - - if len(args) > 2 { - return cty.UnknownVal(cty.String), errors.New("bcrypt() takes no more than two arguments") - } - - input := args[0].AsString() - out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("error occurred generating password %s", err.Error()) - } - - return cty.StringVal(string(out)), nil - }, -}) - -// Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits. -var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString) - -// MakeFileMd5Func constructs a function that is like Md5Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileMd5Func(target fs.FS, baseDir string) function.Function { - return makeFileHashFunction(target, baseDir, md5.New, hex.EncodeToString) -} - -// RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext. -var RsaDecryptFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "ciphertext", - Type: cty.String, - }, - { - Name: "privatekey", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - s := args[0].AsString() - key := args[1].AsString() - - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "failed to decode input %q: cipher text must be base64-encoded", s) - } - - rawKey, err := ssh.ParseRawPrivateKey([]byte(key)) - if err != nil { - var errStr string - switch e := err.(type) { - case asn1.SyntaxError: - errStr = strings.ReplaceAll(e.Error(), "asn1: syntax error", "invalid ASN1 data in the given private key") - case asn1.StructuralError: - errStr = strings.ReplaceAll(e.Error(), "asn1: structure error", "invalid ASN1 data in the given private key") - default: - errStr = fmt.Sprintf("invalid private key: %s", e) - } - return cty.UnknownVal(cty.String), function.NewArgError(1, errors.New(errStr)) - } - privateKey, ok := rawKey.(*rsa.PrivateKey) - if !ok { - return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "invalid private key type %t", rawKey) - } - - out, err := rsa.DecryptPKCS1v15(nil, privateKey, b) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to decrypt: %s", err) - } - - return cty.StringVal(string(out)), nil - }, -}) - -// Sha1Func constructs a function that computes the SHA1 hash of a given string -// and encodes it with hexadecimal digits. -var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString) - -// MakeFileSha1Func constructs a function that is like Sha1Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileSha1Func(target fs.FS, baseDir string) function.Function { - return makeFileHashFunction(target, baseDir, sha1.New, hex.EncodeToString) -} - -// Sha256Func constructs a function that computes the SHA256 hash of a given string -// and encodes it with hexadecimal digits. -var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString) - -// MakeFileSha256Func constructs a function that is like Sha256Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileSha256Func(target fs.FS, baseDir string) function.Function { - return makeFileHashFunction(target, baseDir, sha256.New, hex.EncodeToString) -} - -// Sha512Func constructs a function that computes the SHA512 hash of a given string -// and encodes it with hexadecimal digits. -var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString) - -// MakeFileSha512Func constructs a function that is like Sha512Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileSha512Func(target fs.FS, baseDir string) function.Function { - return makeFileHashFunction(target, baseDir, sha512.New, hex.EncodeToString) -} - -func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - s := args[0].AsString() - h := hf() - h.Write([]byte(s)) - rv := enc(h.Sum(nil)) - return cty.StringVal(rv), nil - }, - }) -} - -func makeFileHashFunction(target fs.FS, baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - path := args[0].AsString() - f, err := openFile(target, baseDir, path) - if err != nil { - return cty.UnknownVal(cty.String), err - } - defer f.Close() - - h := hf() - _, err = io.Copy(h, f) - if err != nil { - return cty.UnknownVal(cty.String), err - } - rv := enc(h.Sum(nil)) - return cty.StringVal(rv), nil - }, - }) -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/datetime.go b/pkg/iac/scanners/terraform/parser/funcs/datetime.go deleted file mode 100644 index 12d73d58ba1c..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/datetime.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "fmt" - "time" - - "golang.org/x/xerrors" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// TimestampFunc constructs a function that returns a string representation of the current date and time. -var TimestampFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(time.Now().UTC().Format(time.RFC3339)), nil - }, -}) - -// MakeStaticTimestampFunc constructs a function that returns a string -// representation of the date and time specified by the provided argument. -func MakeStaticTimestampFunc(static time.Time) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(static.Format(time.RFC3339)), nil - }, - }) -} - -// TimeAddFunc constructs a function that adds a duration to a timestamp, returning a new timestamp. -var TimeAddFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "timestamp", - Type: cty.String, - }, - { - Name: "duration", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - ts, err := parseTimestamp(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), err - } - duration, err := time.ParseDuration(args[1].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(ts.Add(duration).Format(time.RFC3339)), nil - }, -}) - -// TimeCmpFunc is a function that compares two timestamps. -var TimeCmpFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "timestamp_a", - Type: cty.String, - }, - { - Name: "timestamp_b", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Number), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - tsA, err := parseTimestamp(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(0, err) - } - tsB, err := parseTimestamp(args[1].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(1, err) - } - - switch { - case tsA.Equal(tsB): - return cty.NumberIntVal(0), nil - case tsA.Before(tsB): - return cty.NumberIntVal(-1), nil - default: - // By elimintation, tsA must be after tsB. - return cty.NumberIntVal(1), nil - } - }, -}) - -func parseTimestamp(ts string) (time.Time, error) { - t, err := time.Parse(time.RFC3339, ts) - if err != nil { - switch err := err.(type) { - case *time.ParseError: - // If err is a time.ParseError then its string representation is not - // appropriate since it relies on details of Go's strange date format - // representation, which a caller of our functions is not expected - // to be familiar with. - // - // Therefore we do some light transformation to get a more suitable - // error that should make more sense to our callers. These are - // still not awesome error messages, but at least they refer to - // the timestamp portions by name rather than by Go's example - // values. - if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" { - // For some reason err.Message is populated with a ": " prefix - // by the time package. - return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message) - } - var what string - switch err.LayoutElem { - case "2006": - what = "year" - case "01": - what = "month" - case "02": - what = "day of month" - case "15": - what = "hour" - case "04": - what = "minute" - case "05": - what = "second" - case "Z07:00": - what = "UTC offset" - case "T": - return time.Time{}, xerrors.New("not a valid RFC3339 timestamp: missing required time introducer 'T'") - case ":", "-": - if err.ValueElem == "" { - return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem) - } else { - return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem) - } - default: - // Should never get here, because time.RFC3339 includes only the - // above portions, but since that might change in future we'll - // be robust here. - what = "timestamp segment" - } - if err.ValueElem == "" { - return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what) - } else { - return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what) - } - } - return time.Time{}, err - } - return t, nil -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/encoding.go b/pkg/iac/scanners/terraform/parser/funcs/encoding.go deleted file mode 100644 index 75876dfd33f7..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/encoding.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "bytes" - "compress/gzip" - "encoding/base64" - "errors" - "fmt" - "log" - "net/url" - "unicode/utf8" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "golang.org/x/text/encoding/ianaindex" -) - -// Base64DecodeFunc constructs a function that decodes a string containing a base64 sequence. -var Base64DecodeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - AllowMarked: true, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - str, strMarks := args[0].Unmark() - s := str.AsString() - sDec, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode base64 data %s", redactIfSensitive(s, strMarks)) - } - if !utf8.Valid([]byte(sDec)) { - log.Printf("[DEBUG] the result of decoding the provided string is not valid UTF-8: %s", redactIfSensitive(sDec, strMarks)) - return cty.UnknownVal(cty.String), errors.New("the result of decoding the provided string is not valid UTF-8") - } - return cty.StringVal(string(sDec)).WithMarks(strMarks), nil - }, -}) - -// Base64EncodeFunc constructs a function that encodes a string to a base64 sequence. -var Base64EncodeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil - }, -}) - -// TextEncodeBase64Func constructs a function that encodes a string to a target encoding and then to a base64 sequence. -var TextEncodeBase64Func = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "string", - Type: cty.String, - }, - { - Name: "encoding", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - encoding, err := ianaindex.IANA.Encoding(args[1].AsString()) - if err != nil || encoding == nil { - return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "%q is not a supported IANA encoding name or alias in this Terraform version", args[1].AsString()) - } - - encName, err := ianaindex.IANA.Name(encoding) - if err != nil { // would be weird, since we just read this encoding out - encName = args[1].AsString() - } - - encoder := encoding.NewEncoder() - encodedInput, err := encoder.Bytes([]byte(args[0].AsString())) - if err != nil { - // The string representations of "err" disclose implementation - // details of the underlying library, and the main error we might - // like to return a special message for is unexported as - // golang.org/x/text/encoding/internal.RepertoireError, so this - // is just a generic error message for now. - // - // We also don't include the string itself in the message because - // it can typically be very large, contain newline characters, - // etc. - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given string contains characters that cannot be represented in %s", encName) - } - - return cty.StringVal(base64.StdEncoding.EncodeToString(encodedInput)), nil - }, -}) - -// TextDecodeBase64Func constructs a function that decodes a base64 sequence to a target encoding. -var TextDecodeBase64Func = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "source", - Type: cty.String, - }, - { - Name: "encoding", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - encoding, err := ianaindex.IANA.Encoding(args[1].AsString()) - if err != nil || encoding == nil { - return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "%q is not a supported IANA encoding name or alias in this Terraform version", args[1].AsString()) - } - - encName, err := ianaindex.IANA.Name(encoding) - if err != nil { // would be weird, since we just read this encoding out - encName = args[1].AsString() - } - - s := args[0].AsString() - sDec, err := base64.StdEncoding.DecodeString(s) - if err != nil { - switch err := err.(type) { - case base64.CorruptInputError: - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given value is has an invalid base64 symbol at offset %d", int(err)) - default: - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid source string: %w", err) - } - - } - - decoder := encoding.NewDecoder() - decoded, err := decoder.Bytes(sDec) - if err != nil || bytes.ContainsRune(decoded, '�') { - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given string contains symbols that are not defined for %s", encName) - } - - return cty.StringVal(string(decoded)), nil - }, -}) - -// Base64GzipFunc constructs a function that compresses a string with gzip and then encodes the result in -// Base64 encoding. -var Base64GzipFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - s := args[0].AsString() - - var b bytes.Buffer - gz := gzip.NewWriter(&b) - if _, err := gz.Write([]byte(s)); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to write gzip raw data: %w", err) - } - if err := gz.Flush(); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to flush gzip writer: %w", err) - } - if err := gz.Close(); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to close gzip writer: %w", err) - } - return cty.StringVal(base64.StdEncoding.EncodeToString(b.Bytes())), nil - }, -}) - -// URLEncodeFunc constructs a function that applies URL encoding to a given string. -var URLEncodeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(url.QueryEscape(args[0].AsString())), nil - }, -}) diff --git a/pkg/iac/scanners/terraform/parser/funcs/filesystem.go b/pkg/iac/scanners/terraform/parser/funcs/filesystem.go deleted file mode 100644 index 24ac8580ee77..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/filesystem.go +++ /dev/null @@ -1,405 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "encoding/base64" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path/filepath" - "unicode/utf8" - - "github.com/bmatcuk/doublestar/v4" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/mitchellh/go-homedir" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// MakeFileFunc constructs a function that takes a file path and returns the -// contents of that file, either directly as a string (where valid UTF-8 is -// required) or as a string containing base64 bytes. -func MakeFileFunc(target fs.FS, baseDir string, encBase64 bool) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - path := args[0].AsString() - src, err := readFileBytes(target, baseDir, path) - if err != nil { - err = function.NewArgError(0, err) - return cty.UnknownVal(cty.String), err - } - - switch { - case encBase64: - enc := base64.StdEncoding.EncodeToString(src) - return cty.StringVal(enc), nil - default: - if !utf8.Valid(src) { - return cty.UnknownVal(cty.String), fmt.Errorf("contents of %s are not valid UTF-8; use the filebase64 function to obtain the Base64 encoded contents or the other file functions (e.g. filemd5, filesha256) to obtain file hashing results instead", path) - } - return cty.StringVal(string(src)), nil - } - }, - }) -} - -// MakeTemplateFileFunc constructs a function that takes a file path and -// an arbitrary object of named values and attempts to render the referenced -// file as a template using HCL template syntax. -// -// The template itself may recursively call other functions so a callback -// must be provided to get access to those functions. The template cannot, -// however, access any variables defined in the scope: it is restricted only to -// those variables provided in the second function argument, to ensure that all -// dependencies on other graph nodes can be seen before executing this function. -// -// As a special exception, a referenced template file may not recursively call -// the templatefile function, since that would risk the same file being -// included into itself indefinitely. -func MakeTemplateFileFunc(target fs.FS, baseDir string, funcsCb func() map[string]function.Function) function.Function { - - params := []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - { - Name: "vars", - Type: cty.DynamicPseudoType, - }, - } - - loadTmpl := func(fn string) (hcl.Expression, error) { - // We re-use File here to ensure the same filename interpretation - // as it does, along with its other safety checks. - tmplVal, err := File(target, baseDir, cty.StringVal(fn)) - if err != nil { - return nil, err - } - - expr, diags := hclsyntax.ParseTemplate([]byte(tmplVal.AsString()), fn, hcl.Pos{Line: 1, Column: 1}) - if diags.HasErrors() { - return nil, diags - } - - return expr, nil - } - - renderTmpl := func(expr hcl.Expression, varsVal cty.Value) (cty.Value, error) { - if varsTy := varsVal.Type(); !(varsTy.IsMapType() || varsTy.IsObjectType()) { - return cty.DynamicVal, function.NewArgErrorf(1, "invalid vars value: must be a map") // or an object, but we don't strongly distinguish these most of the time - } - - ctx := &hcl.EvalContext{ - Variables: varsVal.AsValueMap(), - } - - // We require all of the variables to be valid HCL identifiers, because - // otherwise there would be no way to refer to them in the template - // anyway. Rejecting this here gives better feedback to the user - // than a syntax error somewhere in the template itself. - for n := range ctx.Variables { - if !hclsyntax.ValidIdentifier(n) { - // This error message intentionally doesn't describe _all_ of - // the different permutations that are technically valid as an - // HCL identifier, but rather focuses on what we might - // consider to be an "idiomatic" variable name. - return cty.DynamicVal, function.NewArgErrorf(1, "invalid template variable name %q: must start with a letter, followed by zero or more letters, digits, and underscores", n) - } - } - - // We'll pre-check references in the template here so we can give a - // more specialized error message than HCL would by default, so it's - // clearer that this problem is coming from a templatefile call. - for _, traversal := range expr.Variables() { - root := traversal.RootName() - if _, ok := ctx.Variables[root]; !ok { - return cty.DynamicVal, function.NewArgErrorf(1, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange()) - } - } - - givenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems - funcs := make(map[string]function.Function, len(givenFuncs)) - for name, fn := range givenFuncs { - if name == "templatefile" { - // We stub this one out to prevent recursive calls. - funcs[name] = function.New(&function.Spec{ - Params: params, - Type: func(args []cty.Value) (cty.Type, error) { - return cty.NilType, errors.New("cannot recursively call templatefile from inside templatefile call") - }, - }) - continue - } - funcs[name] = fn - } - ctx.Functions = funcs - - val, diags := expr.Value(ctx) - if diags.HasErrors() { - return cty.DynamicVal, diags - } - return val, nil - } - - return function.New(&function.Spec{ - Params: params, - Type: func(args []cty.Value) (cty.Type, error) { - if !(args[0].IsKnown() && args[1].IsKnown()) { - return cty.DynamicPseudoType, nil - } - - // We'll render our template now to see what result type it produces. - // A template consisting only of a single interpolation an potentially - // return any type. - expr, err := loadTmpl(args[0].AsString()) - if err != nil { - return cty.DynamicPseudoType, err - } - - // This is safe even if args[1] contains unknowns because the HCL - // template renderer itself knows how to short-circuit those. - val, err := renderTmpl(expr, args[1]) - return val.Type(), err - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - expr, err := loadTmpl(args[0].AsString()) - if err != nil { - return cty.DynamicVal, err - } - return renderTmpl(expr, args[1]) - }, - }) - -} - -// MakeFileExistsFunc constructs a function that takes a path -// and determines whether a file exists at that path -func MakeFileExistsFunc(target fs.FS, baseDir string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - path := args[0].AsString() - path, err := homedir.Expand(path) - if err != nil { - return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to expand ~: %s", err) - } - - if !filepath.IsAbs(path) { - path = filepath.Join(baseDir, path) - } - - // Trivy uses a virtual file system - path = filepath.ToSlash(path) - - fi, err := fs.Stat(target, path) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return cty.False, nil - } - return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to stat %s", path) - } - - if fi.Mode().IsRegular() { - return cty.True, nil - } - - return cty.False, fmt.Errorf("%s is not a regular file, but %q", - path, fi.Mode().String()) - }, - }) -} - -// MakeFileSetFunc constructs a function that takes a glob pattern -// and enumerates a file set from that pattern -func MakeFileSetFunc(target fs.FS, baseDir string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - { - Name: "pattern", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Set(cty.String)), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - path := args[0].AsString() - pattern := args[1].AsString() - - if !filepath.IsAbs(path) { - path = filepath.Join(baseDir, path) - } - - // Join the path to the glob pattern, and ensure both path and pattern - // agree on path separators, so the globbing works as expected. - pattern = filepath.ToSlash(filepath.Join(path, pattern)) - path = filepath.ToSlash(path) - - matches, err := doublestar.Glob(target, pattern) - if err != nil { - return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to glob pattern (%s): %s", pattern, err) - } - - var matchVals []cty.Value - for _, match := range matches { - fi, err := fs.Stat(target, match) - - if err != nil { - return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to stat (%s): %s", match, err) - } - - if !fi.Mode().IsRegular() { - continue - } - - // Remove the path and file separator from matches. - match, err = filepath.Rel(path, match) - - if err != nil { - return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to trim path of match (%s): %s", match, err) - } - - // Replace any remaining file separators with forward slash (/) - // separators for cross-system compatibility. - match = filepath.ToSlash(match) - - matchVals = append(matchVals, cty.StringVal(match)) - } - - if len(matchVals) == 0 { - return cty.SetValEmpty(cty.String), nil - } - - return cty.SetVal(matchVals), nil - }, - }) -} - -// BasenameFunc constructs a function that takes a string containing a filesystem path -// and removes all except the last portion from it. -var BasenameFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(filepath.Base(args[0].AsString())), nil - }, -}) - -// DirnameFunc constructs a function that takes a string containing a filesystem path -// and removes the last portion from it. -var DirnameFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(filepath.Dir(args[0].AsString())), nil - }, -}) - -// AbsPathFunc constructs a function that converts a filesystem path to an absolute path -var AbsPathFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - absPath, err := filepath.Abs(args[0].AsString()) - return cty.StringVal(filepath.ToSlash(absPath)), err - }, -}) - -// PathExpandFunc constructs a function that expands a leading ~ character to the current user's home directory. -var PathExpandFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - - homePath, err := homedir.Expand(args[0].AsString()) - return cty.StringVal(homePath), err - }, -}) - -func openFile(target fs.FS, baseDir, path string) (fs.File, error) { - path, err := homedir.Expand(path) - if err != nil { - return nil, fmt.Errorf("failed to expand ~: %s", err) - } - - if !filepath.IsAbs(path) { - path = filepath.Join(baseDir, path) - } - - // Trivy uses a virtual file system - path = filepath.ToSlash(path) - - if target != nil { - return target.Open(path) - } - return os.Open(path) -} - -func readFileBytes(target fs.FS, baseDir, path string) ([]byte, error) { - f, err := openFile(target, baseDir, path) - if err != nil { - if os.IsNotExist(err) { - // An extra Terraform-specific hint for this situation - return nil, fmt.Errorf("no file exists at %s; this function works only with files that are distributed as part of the configuration source code, so if this file will be created by a resource in this configuration you must instead obtain this result from an attribute of that resource", path) - } - return nil, err - } - - src, err := io.ReadAll(f) - if err != nil { - return nil, fmt.Errorf("failed to read %s", path) - } - - return src, nil -} - -// File reads the contents of the file at the given path. -// -// The file must contain valid UTF-8 bytes, or this function will return an error. -// -// The underlying function implementation works relative to a particular base -// directory, so this wrapper takes a base directory string and uses it to -// construct the underlying function before calling it. -func File(target fs.FS, baseDir string, path cty.Value) (cty.Value, error) { - fn := MakeFileFunc(target, baseDir, false) - return fn.Call([]cty.Value{path}) -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/ip.go b/pkg/iac/scanners/terraform/parser/funcs/ip.go deleted file mode 100644 index d1cf0352e95f..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/ip.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/ipaddr -package funcs - -import ( - stdnet "net" -) - -// Bigger than we need, not too big to worry about overflow -const bigVal = 0xFFFFFF - -// Decimal to integer. -// Returns number, characters consumed, success. -func dtoi(s string) (n int, i int, ok bool) { - n = 0 - for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { - n = n*10 + int(s[i]-'0') - if n >= bigVal { - return bigVal, i, false - } - } - if i == 0 { - return 0, 0, false - } - return n, i, true -} - -// Hexadecimal to integer. -// Returns number, characters consumed, success. -func xtoi(s string) (n int, i int, ok bool) { - n = 0 - for i = 0; i < len(s); i++ { - if '0' <= s[i] && s[i] <= '9' { - n *= 16 - n += int(s[i] - '0') - } else if 'a' <= s[i] && s[i] <= 'f' { - n *= 16 - n += int(s[i]-'a') + 10 - } else if 'A' <= s[i] && s[i] <= 'F' { - n *= 16 - n += int(s[i]-'A') + 10 - } else { - break - } - if n >= bigVal { - return 0, i, false - } - } - if i == 0 { - return 0, i, false - } - return n, i, true -} - -// -// Lean on the standard net lib as much as possible. -// - -type IP = stdnet.IP -type IPNet = stdnet.IPNet -type ParseError = stdnet.ParseError - -const IPv4len = stdnet.IPv4len -const IPv6len = stdnet.IPv6len - -var CIDRMask = stdnet.CIDRMask -var IPv4 = stdnet.IPv4 - -// Parse IPv4 address (d.d.d.d). -func parseIPv4(s string) IP { - var p [IPv4len]byte - for i := 0; i < IPv4len; i++ { - if len(s) == 0 { - // Missing octets. - return nil - } - if i > 0 { - if s[0] != '.' { - return nil - } - s = s[1:] - } - n, c, ok := dtoi(s) - if !ok || n > 0xFF { - return nil - } - // - // NOTE: This correct check was added for go-1.17, but is a - // backwards-incompatible change for Terraform users, who might have - // already written modules with leading zeroes. - // - // if c > 1 && s[0] == '0' { - // // Reject non-zero components with leading zeroes. - // return nil - //} - s = s[c:] - p[i] = byte(n) - } - if len(s) != 0 { - return nil - } - return IPv4(p[0], p[1], p[2], p[3]) -} - -// parseIPv6 parses s as a literal IPv6 address described in RFC 4291 -// and RFC 5952. -func parseIPv6(s string) (ip IP) { - ip = make(IP, IPv6len) - ellipsis := -1 // position of ellipsis in ip - - // Might have leading ellipsis - if len(s) >= 2 && s[0] == ':' && s[1] == ':' { - ellipsis = 0 - s = s[2:] - // Might be only ellipsis - if len(s) == 0 { - return ip - } - } - - // Loop, parsing hex numbers followed by colon. - i := 0 - for i < IPv6len { - // Hex number. - n, c, ok := xtoi(s) - if !ok || n > 0xFFFF { - return nil - } - - // If followed by dot, might be in trailing IPv4. - if c < len(s) && s[c] == '.' { - if ellipsis < 0 && i != IPv6len-IPv4len { - // Not the right place. - return nil - } - if i+IPv4len > IPv6len { - // Not enough room. - return nil - } - ip4 := parseIPv4(s) - if ip4 == nil { - return nil - } - ip[i] = ip4[12] - ip[i+1] = ip4[13] - ip[i+2] = ip4[14] - ip[i+3] = ip4[15] - s = "" - i += IPv4len - break - } - - // Save this 16-bit chunk. - ip[i] = byte(n >> 8) - ip[i+1] = byte(n) - i += 2 - - // Stop at end of string. - s = s[c:] - if len(s) == 0 { - break - } - - // Otherwise must be followed by colon and more. - if s[0] != ':' || len(s) == 1 { - return nil - } - s = s[1:] - - // Look for ellipsis. - if s[0] == ':' { - if ellipsis >= 0 { // already have one - return nil - } - ellipsis = i - s = s[1:] - if len(s) == 0 { // can be at end - break - } - } - } - - // Must have used entire string. - if len(s) != 0 { - return nil - } - - // If didn't parse enough, expand ellipsis. - if i < IPv6len { - if ellipsis < 0 { - return nil - } - n := IPv6len - i - for j := i - 1; j >= ellipsis; j-- { - ip[j+n] = ip[j] - } - for j := ellipsis + n - 1; j >= ellipsis; j-- { - ip[j] = 0 - } - } else if ellipsis >= 0 { - // Ellipsis must represent at least one 0 group. - return nil - } - return ip -} - -// ParseIP parses s as an IP address, returning the result. -// The string s can be in IPv4 dotted decimal ("192.0.2.1"), IPv6 -// ("2001:db8::68"), or IPv4-mapped IPv6 ("::ffff:192.0.2.1") form. -// If s is not a valid textual representation of an IP address, -// ParseIP returns nil. -func ParseIP(s string) IP { - for i := 0; i < len(s); i++ { - switch s[i] { - case '.': - return parseIPv4(s) - case ':': - return parseIPv6(s) - } - } - return nil -} - -// ParseCIDR parses s as a CIDR notation IP address and prefix length, -// like "192.0.2.0/24" or "2001:db8::/32", as defined in -// RFC 4632 and RFC 4291. -// -// It returns the IP address and the network implied by the IP and -// prefix length. -// For example, ParseCIDR("192.0.2.1/24") returns the IP address -// 192.0.2.1 and the network 192.0.2.0/24. -func ParseCIDR(s string) (IP, *IPNet, error) { - i := indexByteString(s, '/') - if i < 0 { - return nil, nil, &ParseError{Type: "CIDR address", Text: s} - } - addr, mask := s[:i], s[i+1:] - iplen := IPv4len - ip := parseIPv4(addr) - if ip == nil { - iplen = IPv6len - ip = parseIPv6(addr) - } - n, i, ok := dtoi(mask) - if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen { - return nil, nil, &ParseError{Type: "CIDR address", Text: s} - } - m := CIDRMask(n, 8*iplen) - return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil -} - -// This is copied from go/src/internal/bytealg, which includes versions -// optimized for various platforms. Those optimizations are elided here so we -// don't have to maintain them. -func indexByteString(s string, c byte) int { - for i := 0; i < len(s); i++ { - if s[i] == c { - return i - } - } - return -1 -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/marks.go b/pkg/iac/scanners/terraform/parser/funcs/marks.go deleted file mode 100644 index abbc397f1e08..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/marks.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/marks -package funcs - -import ( - "github.com/zclconf/go-cty/cty" - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -// valueMarks allow creating strictly typed values for use as cty.Value marks. -// The variable name for new values should be the title-cased format of the -// value to better match the GoString output for debugging. -type valueMark string - -func (m valueMark) GoString() string { - return "marks." + cases.Title(language.English).String(string(m)) -} - -// Has returns true if and only if the cty.Value has the given mark. -func Has(val cty.Value, mark valueMark) bool { - return val.HasMark(mark) -} - -// Contains returns true if the cty.Value or any any value within it contains -// the given mark. -func Contains(val cty.Value, mark valueMark) bool { - ret := false - _ = cty.Walk(val, func(_ cty.Path, v cty.Value) (bool, error) { - if v.HasMark(mark) { - ret = true - return false, nil - } - return true, nil - }) - return ret -} - -// MarkedSensitive indicates that this value is marked as sensitive in the context of -// Terraform. -const MarkedSensitive = valueMark("sensitive") - -// MarkedRaw is used to indicate to the repl that the value should be written without -// any formatting. -const MarkedRaw = valueMark("raw") diff --git a/pkg/iac/scanners/terraform/parser/funcs/number.go b/pkg/iac/scanners/terraform/parser/funcs/number.go deleted file mode 100644 index 60ebd660bf18..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/number.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "math" - "math/big" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" -) - -// LogFunc constructs a function that returns the logarithm of a given number in a given base. -var LogFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - { - Name: "base", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var num float64 - if err := gocty.FromCtyValue(args[0], &num); err != nil { - return cty.UnknownVal(cty.String), err - } - - var base float64 - if err := gocty.FromCtyValue(args[1], &base); err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil - }, -}) - -// PowFunc constructs a function that returns the logarithm of a given number in a given base. -var PowFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - { - Name: "power", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var num float64 - if err := gocty.FromCtyValue(args[0], &num); err != nil { - return cty.UnknownVal(cty.String), err - } - - var power float64 - if err := gocty.FromCtyValue(args[1], &power); err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.NumberFloatVal(math.Pow(num, power)), nil - }, -}) - -// SignumFunc constructs a function that returns the closest whole number greater -// than or equal to the given value. -var SignumFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var num int - if err := gocty.FromCtyValue(args[0], &num); err != nil { - return cty.UnknownVal(cty.String), err - } - switch { - case num < 0: - return cty.NumberIntVal(-1), nil - case num > 0: - return cty.NumberIntVal(+1), nil - default: - return cty.NumberIntVal(0), nil - } - }, -}) - -// ParseIntFunc constructs a function that parses a string argument and returns an integer of the specified base. -var ParseIntFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "number", - Type: cty.DynamicPseudoType, - AllowMarked: true, - }, - { - Name: "base", - Type: cty.Number, - AllowMarked: true, - }, - }, - - Type: func(args []cty.Value) (cty.Type, error) { - if !args[0].Type().Equals(cty.String) { - return cty.Number, function.NewArgErrorf(0, "first argument must be a string, not %s", args[0].Type().FriendlyName()) - } - return cty.Number, nil - }, - RefineResult: refineNotNull, - - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - var numstr string - var base int - var err error - - numArg, numMarks := args[0].Unmark() - if err = gocty.FromCtyValue(numArg, &numstr); err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(0, err) - } - - baseArg, baseMarks := args[1].Unmark() - if err = gocty.FromCtyValue(baseArg, &base); err != nil { - return cty.UnknownVal(cty.Number), function.NewArgError(1, err) - } - - if base < 2 || base > 62 { - return cty.UnknownVal(cty.Number), function.NewArgErrorf( - 1, - "base must be a whole number between 2 and 62 inclusive", - ) - } - - num, ok := (&big.Int{}).SetString(numstr, base) - if !ok { - return cty.UnknownVal(cty.Number), function.NewArgErrorf( - 0, - "cannot parse %s as a base %s integer", - redactIfSensitive(numstr, numMarks), - redactIfSensitive(base, baseMarks), - ) - } - - parsedNum := cty.NumberVal((&big.Float{}).SetInt(num)).WithMarks(numMarks, baseMarks) - - return parsedNum, nil - }, -}) diff --git a/pkg/iac/scanners/terraform/parser/funcs/redact.go b/pkg/iac/scanners/terraform/parser/funcs/redact.go deleted file mode 100644 index 9f1e8d3551f6..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/redact.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "fmt" - - "github.com/zclconf/go-cty/cty" -) - -func redactIfSensitive(value any, markses ...cty.ValueMarks) string { - if Has(cty.DynamicVal.WithMarks(markses...), MarkedSensitive) { - return "(sensitive value)" - } - switch v := value.(type) { - case string: - return fmt.Sprintf("%q", v) - default: - return fmt.Sprintf("%v", v) - } -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/refinements.go b/pkg/iac/scanners/terraform/parser/funcs/refinements.go deleted file mode 100644 index de9cb08b1604..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/refinements.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "github.com/zclconf/go-cty/cty" -) - -func refineNotNull(b *cty.RefinementBuilder) *cty.RefinementBuilder { - return b.NotNull() -} diff --git a/pkg/iac/scanners/terraform/parser/funcs/sensitive.go b/pkg/iac/scanners/terraform/parser/funcs/sensitive.go deleted file mode 100644 index 3566a678fc9b..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/sensitive.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// SensitiveFunc returns a value identical to its argument except that -// Terraform will consider it to be sensitive. -var SensitiveFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "value", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowNull: true, - AllowMarked: true, - AllowDynamicType: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - // This function only affects the value's marks, so the result - // type is always the same as the argument type. - return args[0].Type(), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - val, _ := args[0].Unmark() - return val.Mark(MarkedSensitive), nil - }, -}) - -// NonsensitiveFunc takes a sensitive value and returns the same value without -// the sensitive marking, effectively exposing the value. -var NonsensitiveFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "value", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowNull: true, - AllowMarked: true, - AllowDynamicType: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - // This function only affects the value's marks, so the result - // type is always the same as the argument type. - return args[0].Type(), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - v, marks := args[0].Unmark() - delete(marks, MarkedSensitive) // remove the sensitive marking - return v.WithMarks(marks), nil - }, -}) - -var IssensitiveFunc = function.New(&function.Spec{ - Params: []function.Parameter{{ - Name: "value", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowNull: true, - AllowMarked: true, - AllowDynamicType: true, - }}, - Type: func(args []cty.Value) (cty.Type, error) { - return cty.Bool, nil - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - s := args[0].HasMark(MarkedSensitive) - return cty.BoolVal(s), nil - }, -}) diff --git a/pkg/iac/scanners/terraform/parser/funcs/string.go b/pkg/iac/scanners/terraform/parser/funcs/string.go deleted file mode 100644 index f859c7af7a31..000000000000 --- a/pkg/iac/scanners/terraform/parser/funcs/string.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copied from github.com/hashicorp/terraform/internal/lang/funcs -package funcs - -import ( - "regexp" - "strings" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// StartsWithFunc constructs a function that checks if a string starts with -// a specific prefix using strings.HasPrefix -var StartsWithFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - AllowUnknown: true, - }, - { - Name: "prefix", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - prefix := args[1].AsString() - - if !args[0].IsKnown() { - // If the unknown value has a known prefix then we might be - // able to still produce a known result. - if prefix == "" { - // The empty string is a prefix of any string. - return cty.True, nil - } - if knownPrefix := args[0].Range().StringPrefix(); knownPrefix != "" { - if strings.HasPrefix(knownPrefix, prefix) { - return cty.True, nil - } - if len(knownPrefix) >= len(prefix) { - // If the prefix we're testing is no longer than the known - // prefix and it didn't match then the full string with - // that same prefix can't match either. - return cty.False, nil - } - } - return cty.UnknownVal(cty.Bool), nil - } - - str := args[0].AsString() - - if strings.HasPrefix(str, prefix) { - return cty.True, nil - } - - return cty.False, nil - }, -}) - -// EndsWithFunc constructs a function that checks if a string ends with -// a specific suffix using strings.HasSuffix -var EndsWithFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - { - Name: "suffix", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - str := args[0].AsString() - suffix := args[1].AsString() - - if strings.HasSuffix(str, suffix) { - return cty.True, nil - } - - return cty.False, nil - }, -}) - -// ReplaceFunc constructs a function that searches a given string for another -// given substring, and replaces each occurrence with a given replacement string. -var ReplaceFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - { - Name: "substr", - Type: cty.String, - }, - { - Name: "replace", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - RefineResult: refineNotNull, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - str := args[0].AsString() - substr := args[1].AsString() - replace := args[2].AsString() - - // We search/replace using a regexp if the string is surrounded - // in forward slashes. - if len(substr) > 1 && substr[0] == '/' && substr[len(substr)-1] == '/' { - re, err := regexp.Compile(substr[1 : len(substr)-1]) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(re.ReplaceAllString(str, replace)), nil - } - - return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil - }, -}) - -// StrContainsFunc searches a given string for another given substring, -// if found the function returns true, otherwise returns false. -var StrContainsFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - { - Name: "substr", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - str := args[0].AsString() - substr := args[1].AsString() - - if strings.Contains(str, substr) { - return cty.True, nil - } - - return cty.False, nil - }, -}) diff --git a/pkg/iac/scanners/terraform/parser/functions.go b/pkg/iac/scanners/terraform/parser/functions.go deleted file mode 100644 index 6f6406d8ed19..000000000000 --- a/pkg/iac/scanners/terraform/parser/functions.go +++ /dev/null @@ -1,137 +0,0 @@ -package parser - -import ( - "io/fs" - - "github.com/hashicorp/hcl/v2/ext/tryfunc" - ctyyaml "github.com/zclconf/go-cty-yaml" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/function/stdlib" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/funcs" -) - -// Functions returns the set of functions that should be used to when evaluating -// expressions in the receiving scope. -func Functions(target fs.FS, baseDir string) map[string]function.Function { - return map[string]function.Function{ - "abs": stdlib.AbsoluteFunc, - "abspath": funcs.AbsPathFunc, - "alltrue": funcs.AllTrueFunc, - "anytrue": funcs.AnyTrueFunc, - "basename": funcs.BasenameFunc, - "base64decode": funcs.Base64DecodeFunc, - "base64encode": funcs.Base64EncodeFunc, - "base64gzip": funcs.Base64GzipFunc, - "base64sha256": funcs.Base64Sha256Func, - "base64sha512": funcs.Base64Sha512Func, - "bcrypt": funcs.BcryptFunc, - "can": tryfunc.CanFunc, - "ceil": stdlib.CeilFunc, - "chomp": stdlib.ChompFunc, - "cidrhost": funcs.CidrHostFunc, - "cidrnetmask": funcs.CidrNetmaskFunc, - "cidrsubnet": funcs.CidrSubnetFunc, - "cidrsubnets": funcs.CidrSubnetsFunc, - "coalesce": funcs.CoalesceFunc, - "coalescelist": stdlib.CoalesceListFunc, - "compact": stdlib.CompactFunc, - "concat": stdlib.ConcatFunc, - "contains": stdlib.ContainsFunc, - "csvdecode": stdlib.CSVDecodeFunc, - "dirname": funcs.DirnameFunc, - "distinct": stdlib.DistinctFunc, - "element": stdlib.ElementFunc, - "endswith": funcs.EndsWithFunc, - "chunklist": stdlib.ChunklistFunc, - "file": funcs.MakeFileFunc(target, baseDir, false), - "fileexists": funcs.MakeFileExistsFunc(target, baseDir), - "fileset": funcs.MakeFileSetFunc(target, baseDir), - "filebase64": funcs.MakeFileFunc(target, baseDir, true), - "filebase64sha256": funcs.MakeFileBase64Sha256Func(target, baseDir), - "filebase64sha512": funcs.MakeFileBase64Sha512Func(target, baseDir), - "filemd5": funcs.MakeFileMd5Func(target, baseDir), - "filesha1": funcs.MakeFileSha1Func(target, baseDir), - "filesha256": funcs.MakeFileSha256Func(target, baseDir), - "filesha512": funcs.MakeFileSha512Func(target, baseDir), - "flatten": stdlib.FlattenFunc, - "floor": stdlib.FloorFunc, - "format": stdlib.FormatFunc, - "formatdate": stdlib.FormatDateFunc, - "formatlist": stdlib.FormatListFunc, - "indent": stdlib.IndentFunc, - "index": funcs.IndexFunc, // stdlib.IndexFunc is not compatible - "join": stdlib.JoinFunc, - "jsondecode": stdlib.JSONDecodeFunc, - "jsonencode": stdlib.JSONEncodeFunc, - "keys": stdlib.KeysFunc, - "length": funcs.LengthFunc, - "list": funcs.ListFunc, - "log": stdlib.LogFunc, - "lookup": funcs.LookupFunc, - "lower": stdlib.LowerFunc, - "map": funcs.MapFunc, - "matchkeys": funcs.MatchkeysFunc, - "max": stdlib.MaxFunc, - "md5": funcs.Md5Func, - "merge": stdlib.MergeFunc, - "min": stdlib.MinFunc, - "one": funcs.OneFunc, - "parseint": stdlib.ParseIntFunc, - "pathexpand": funcs.PathExpandFunc, - "pow": stdlib.PowFunc, - "range": stdlib.RangeFunc, - "regex": stdlib.RegexFunc, - "regexall": stdlib.RegexAllFunc, - "replace": funcs.ReplaceFunc, - "reverse": stdlib.ReverseListFunc, - "rsadecrypt": funcs.RsaDecryptFunc, - "sensitive": funcs.SensitiveFunc, - "nonsensitive": funcs.NonsensitiveFunc, - "issensitive": funcs.IssensitiveFunc, - "setintersection": stdlib.SetIntersectionFunc, - "setproduct": stdlib.SetProductFunc, - "setsubtract": stdlib.SetSubtractFunc, - "setunion": stdlib.SetUnionFunc, - "sha1": funcs.Sha1Func, - "sha256": funcs.Sha256Func, - "sha512": funcs.Sha512Func, - "signum": stdlib.SignumFunc, - "slice": stdlib.SliceFunc, - "sort": stdlib.SortFunc, - "split": stdlib.SplitFunc, - "startswith": funcs.StartsWithFunc, - "strcontains": funcs.StrContainsFunc, - "strrev": stdlib.ReverseFunc, - "substr": stdlib.SubstrFunc, - "sum": funcs.SumFunc, - "textdecodebase64": funcs.TextDecodeBase64Func, - "textencodebase64": funcs.TextEncodeBase64Func, - "timestamp": funcs.TimestampFunc, - "timeadd": stdlib.TimeAddFunc, - "timecmp": funcs.TimeCmpFunc, - "title": stdlib.TitleFunc, - "tostring": funcs.MakeToFunc(cty.String), - "tonumber": funcs.MakeToFunc(cty.Number), - "tobool": funcs.MakeToFunc(cty.Bool), - "toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)), - "tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)), - "tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)), - "transpose": funcs.TransposeFunc, - "trim": stdlib.TrimFunc, - "trimprefix": stdlib.TrimPrefixFunc, - "trimspace": stdlib.TrimSpaceFunc, - "trimsuffix": stdlib.TrimSuffixFunc, - "try": tryfunc.TryFunc, - "upper": stdlib.UpperFunc, - "urlencode": funcs.URLEncodeFunc, - "uuid": funcs.UUIDFunc, - "uuidv5": funcs.UUIDV5Func, - "values": stdlib.ValuesFunc, - "yamldecode": ctyyaml.YAMLDecodeFunc, - "yamlencode": ctyyaml.YAMLEncodeFunc, - "zipmap": stdlib.ZipmapFunc, - } - -} diff --git a/pkg/iac/scanners/terraform/parser/load_module.go b/pkg/iac/scanners/terraform/parser/load_module.go deleted file mode 100644 index 878bf075baec..000000000000 --- a/pkg/iac/scanners/terraform/parser/load_module.go +++ /dev/null @@ -1,174 +0,0 @@ -package parser - -import ( - "context" - "errors" - "fmt" - "io/fs" - "path" - "strings" - - "github.com/zclconf/go-cty/cty" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/log" -) - -type ModuleDefinition struct { - Name string - Path string - FileSystem fs.FS - Definition *terraform.Block - Parser *Parser - External bool -} - -func (d *ModuleDefinition) inputVars() map[string]cty.Value { - inputs := d.Definition.NullableValues().AsValueMap() - if inputs == nil { - return make(map[string]cty.Value) - } - return inputs -} - -// loadModules reads all module blocks and loads them -func (e *evaluator) loadModules(ctx context.Context) []*ModuleDefinition { - var moduleDefinitions []*ModuleDefinition - - expanded := e.expandBlocks(e.blocks.OfType("module")) - - for _, moduleBlock := range expanded { - if moduleBlock.Label() == "" { - continue - } - moduleDefinition, err := e.loadModule(ctx, moduleBlock) - if err != nil { - e.logger.Error("Failed to load module. Maybe try 'terraform init'?", log.Err(err)) - continue - } - - e.logger.Debug("Loaded module", log.String("name", moduleDefinition.Name), log.FilePath(moduleDefinition.Path)) - moduleDefinitions = append(moduleDefinitions, moduleDefinition) - } - - return moduleDefinitions -} - -// takes in a module "x" {} block and loads resources etc. into e.moduleBlocks - additionally returns variables to add to ["module.x.*"] variables -func (e *evaluator) loadModule(ctx context.Context, b *terraform.Block) (*ModuleDefinition, error) { - - metadata := b.GetMetadata() - - if b.Label() == "" { - return nil, fmt.Errorf("module without label at %s", metadata.Range()) - } - - var source string - attrs := b.Attributes() - for _, attr := range attrs { - if attr.Name() == "source" { - sourceVal := attr.Value() - if sourceVal.Type() == cty.String { - source = sourceVal.AsString() - } - } - } - if source == "" { - return nil, fmt.Errorf("could not read module source attribute at %s", metadata.Range().String()) - } - - if def, err := e.loadModuleFromTerraformCache(ctx, b, source); err == nil { - e.logger.Debug("Using module from Terraform cache .terraform/modules", log.String("source", source)) - return def, nil - } - - // we don't have the module installed via 'terraform init' so we need to grab it... - return e.loadExternalModule(ctx, b, source) -} - -func (e *evaluator) loadModuleFromTerraformCache(ctx context.Context, b *terraform.Block, source string) (*ModuleDefinition, error) { - var modulePath string - if e.moduleMetadata != nil { - // if we have module metadata we can parse all the modules as they'll be cached locally! - moduleKey := b.ModuleKey() - for _, module := range e.moduleMetadata.Modules { - if module.Key == moduleKey { - modulePath = path.Clean(path.Join(e.projectRootPath, module.Dir)) - break - } - } - } - if modulePath == "" { - return nil, errors.New("failed to load module from .terraform/modules") - } - if strings.HasPrefix(source, ".") { - source = "" - } - - if prefix, relativeDir, ok := strings.Cut(source, "//"); ok && !strings.HasSuffix(prefix, ":") && strings.Count(prefix, "/") == 2 { - if !strings.HasSuffix(modulePath, relativeDir) { - modulePath = fmt.Sprintf("%s/%s", modulePath, relativeDir) - } - } - - e.logger.Debug("Module resolved using modules.json", - log.String("block", b.FullName()), - log.String("source", source), - log.String("modulePath", modulePath), - ) - moduleParser := e.parentParser.newModuleParser(e.filesystem, source, modulePath, b.Label(), b) - if err := moduleParser.ParseFS(ctx, modulePath); err != nil { - return nil, err - } - return &ModuleDefinition{ - Name: b.Label(), - Path: modulePath, - Definition: b, - Parser: moduleParser, - FileSystem: e.filesystem, - }, nil -} - -func (e *evaluator) loadExternalModule(ctx context.Context, b *terraform.Block, source string) (*ModuleDefinition, error) { - - e.logger.Debug("Locating non-initialized module", log.String("source", source)) - - version := b.GetAttribute("version").AsStringValueOrDefault("", b).Value() - opt := resolvers.Options{ - Source: source, - OriginalSource: source, - Version: version, - OriginalVersion: version, - WorkingDir: e.projectRootPath, - Name: b.FullName(), - ModulePath: e.modulePath, - Logger: log.WithPrefix("module resolver"), - AllowDownloads: e.allowDownloads, - SkipCache: e.skipCachedModules, - } - - filesystem, prefix, downloadPath, err := resolveModule(ctx, e.filesystem, opt) - if err != nil { - return nil, err - } - prefix = path.Join(e.parentParser.moduleSource, prefix) - e.logger.Debug("Module resolved", - log.String("block", b.FullName()), - log.String("source", source), - log.String("prefix", prefix), - log.FilePath(downloadPath), - ) - moduleParser := e.parentParser.newModuleParser(filesystem, prefix, downloadPath, b.Label(), b) - if err := moduleParser.ParseFS(ctx, downloadPath); err != nil { - return nil, err - } - return &ModuleDefinition{ - Name: b.Label(), - Path: downloadPath, - Definition: b, - Parser: moduleParser, - FileSystem: filesystem, - External: true, - }, nil -} diff --git a/pkg/iac/scanners/terraform/parser/load_module_metadata.go b/pkg/iac/scanners/terraform/parser/load_module_metadata.go deleted file mode 100644 index 9648e6475ff8..000000000000 --- a/pkg/iac/scanners/terraform/parser/load_module_metadata.go +++ /dev/null @@ -1,35 +0,0 @@ -package parser - -import ( - "encoding/json" - "io/fs" - "path" -) - -const manifestSnapshotFile = ".terraform/modules/modules.json" - -type modulesMetadata struct { - Modules []struct { - Key string `json:"Key"` - Source string `json:"Source"` - Version string `json:"Version"` - Dir string `json:"Dir"` - } `json:"Modules"` -} - -func loadModuleMetadata(target fs.FS, fullPath string) (*modulesMetadata, string, error) { - metadataPath := path.Join(fullPath, manifestSnapshotFile) - - f, err := target.Open(metadataPath) - if err != nil { - return nil, metadataPath, err - } - defer f.Close() - - var metadata modulesMetadata - if err := json.NewDecoder(f).Decode(&metadata); err != nil { - return nil, metadataPath, err - } - - return &metadata, metadataPath, nil -} diff --git a/pkg/iac/scanners/terraform/parser/load_vars.go b/pkg/iac/scanners/terraform/parser/load_vars.go deleted file mode 100644 index 58f67ce93910..000000000000 --- a/pkg/iac/scanners/terraform/parser/load_vars.go +++ /dev/null @@ -1,83 +0,0 @@ -package parser - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - hcljson "github.com/hashicorp/hcl/v2/json" - "github.com/zclconf/go-cty/cty" -) - -func loadTFVars(srcFS fs.FS, filenames []string) (map[string]cty.Value, error) { - combinedVars := make(map[string]cty.Value) - - for _, env := range os.Environ() { - split := strings.Split(env, "=") - key := split[0] - if !strings.HasPrefix(key, "TF_VAR_") { - continue - } - key = strings.TrimPrefix(key, "TF_VAR_") - var val string - if len(split) > 1 { - val = split[1] - } - combinedVars[key] = cty.StringVal(val) - } - - for _, filename := range filenames { - vars, err := loadTFVarsFile(srcFS, filename) - if err != nil { - return nil, fmt.Errorf("failed to load tfvars from %s: %w", filename, err) - } - for k, v := range vars { - combinedVars[k] = v - } - } - - return combinedVars, nil -} - -func loadTFVarsFile(srcFS fs.FS, filename string) (map[string]cty.Value, error) { - inputVars := make(map[string]cty.Value) - if filename == "" { - return inputVars, nil - } - - src, err := fs.ReadFile(srcFS, filepath.ToSlash(filename)) - if err != nil { - return nil, err - } - - var attrs hcl.Attributes - if strings.HasSuffix(filename, ".json") { - variableFile, err := hcljson.Parse(src, filename) - if err != nil { - return nil, err - } - attrs, err = variableFile.Body.JustAttributes() - if err != nil { - return nil, err - } - } else { - variableFile, err := hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1}) - if err != nil { - return nil, err - } - attrs, err = variableFile.Body.JustAttributes() - if err != nil { - return nil, err - } - } - - for _, attr := range attrs { - inputVars[attr.Name], _ = attr.Expr.Value(&hcl.EvalContext{}) - } - - return inputVars, nil -} diff --git a/pkg/iac/scanners/terraform/parser/load_vars_test.go b/pkg/iac/scanners/terraform/parser/load_vars_test.go deleted file mode 100644 index 384ced396eba..000000000000 --- a/pkg/iac/scanners/terraform/parser/load_vars_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/zclconf/go-cty/cty" - - "github.com/aquasecurity/trivy/internal/testutil" -) - -func Test_TFVarsFile(t *testing.T) { - t.Run("tfvars file", func(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "test.tfvars": `instance_type = "t2.large"`, - }) - - vars, err := loadTFVars(fs, []string{"test.tfvars"}) - require.NoError(t, err) - assert.Equal(t, "t2.large", vars["instance_type"].AsString()) - }) - - t.Run("tfvars json file", func(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "test.tfvars.json": `{ - "variable": { - "foo": { - "default": "bar" - }, - "baz": "qux" - }, - "foo2": true, - "foo3": 3 -}`, - }) - - vars, err := loadTFVars(fs, []string{"test.tfvars.json"}) - require.NoError(t, err) - assert.Equal(t, "bar", vars["variable"].GetAttr("foo").GetAttr("default").AsString()) - assert.Equal(t, "qux", vars["variable"].GetAttr("baz").AsString()) - assert.True(t, vars["foo2"].True()) - assert.True(t, vars["foo3"].Equals(cty.NumberIntVal(3)).True()) - }) -} diff --git a/pkg/iac/scanners/terraform/parser/module_retrieval.go b/pkg/iac/scanners/terraform/parser/module_retrieval.go deleted file mode 100644 index cc374b68eeb7..000000000000 --- a/pkg/iac/scanners/terraform/parser/module_retrieval.go +++ /dev/null @@ -1,35 +0,0 @@ -package parser - -import ( - "context" - "fmt" - "io/fs" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers" - "github.com/aquasecurity/trivy/pkg/log" -) - -type ModuleResolver interface { - Resolve(context.Context, fs.FS, resolvers.Options) (filesystem fs.FS, prefix string, downloadPath string, applies bool, err error) -} - -var defaultResolvers = []ModuleResolver{ - resolvers.Local, - resolvers.Cache, - resolvers.Remote, - resolvers.Registry, -} - -func resolveModule(ctx context.Context, current fs.FS, opt resolvers.Options) (filesystem fs.FS, sourcePrefix, downloadPath string, err error) { - opt.Logger.Debug("Resolving module", - log.String("name", opt.Name), log.String("source", opt.Source)) - for _, resolver := range defaultResolvers { - if filesystem, prefix, path, applies, err := resolver.Resolve(ctx, current, opt); err != nil { - return nil, "", "", err - } else if applies { - opt.Logger.Debug("Module resolved", log.FilePath(path)) - return filesystem, prefix, path, nil - } - } - return nil, "", "", fmt.Errorf("failed to resolve module '%s' with source: %s", opt.Name, opt.Source) -} diff --git a/pkg/iac/scanners/terraform/parser/modules.go b/pkg/iac/scanners/terraform/parser/modules.go deleted file mode 100644 index 415f52a117d0..000000000000 --- a/pkg/iac/scanners/terraform/parser/modules.go +++ /dev/null @@ -1,82 +0,0 @@ -package parser - -import ( - "context" - "path" - "sort" - "strings" - - "github.com/samber/lo" - "github.com/zclconf/go-cty/cty" - - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -// FindRootModules takes a list of module paths and identifies the root local modules. -// It builds a graph based on the module dependencies and determines the modules that have no incoming dependencies, -// considering them as root modules. -func (p *Parser) FindRootModules(ctx context.Context, dirs []string) ([]string, error) { - // skip cached terraform modules as they cannot be root modules - dirs = lo.Filter(dirs, func(dir string, _ int) bool { - return !strings.Contains(dir, ".terraform/modules/") - }) - for _, dir := range dirs { - if err := p.ParseFS(ctx, dir); err != nil { - return nil, err - } - } - - blocks, _, err := p.readBlocks(p.files) - if err != nil { - return nil, err - } - - g := buildGraph(blocks, dirs) - rootModules := g.rootModules() - sort.Strings(rootModules) - return rootModules, nil -} - -type modulesGraph map[string][]string - -func buildGraph(blocks terraform.Blocks, paths []string) modulesGraph { - moduleBlocks := blocks.OfType("module") - - graph := lo.SliceToMap(paths, func(p string) (string, []string) { - return p, nil - }) - - for _, block := range moduleBlocks { - sourceVal := block.GetAttribute("source").Value() - if sourceVal.Type() != cty.String { - continue - } - - source := sourceVal.AsString() - if strings.HasPrefix(source, ".") { - filename := block.GetMetadata().Range().GetFilename() - dir := path.Dir(filename) - graph[dir] = append(graph[dir], path.Join(dir, source)) - } - } - - return graph -} - -func (g modulesGraph) rootModules() []string { - incomingEdges := make(map[string]int) - for _, neighbors := range g { - for _, neighbor := range neighbors { - incomingEdges[neighbor]++ - } - } - - var roots []string - for module := range g { - if incomingEdges[module] == 0 { - roots = append(roots, module) - } - } - - return roots -} diff --git a/pkg/iac/scanners/terraform/parser/modules_test.go b/pkg/iac/scanners/terraform/parser/modules_test.go deleted file mode 100644 index 29dcaa7c39af..000000000000 --- a/pkg/iac/scanners/terraform/parser/modules_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package parser - -import ( - "context" - "path" - "testing" - - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" -) - -func TestFindRootModules(t *testing.T) { - tests := []struct { - name string - files map[string]string - expected []string - }{ - { - name: "multiple root modules", - files: map[string]string{ - "code/main.tf": ` -module "this" { - count = 0 - source = "./modules/s3" -}`, - "code/modules/s3/main.tf": ` -module "this" { - source = "./modules/logging" -} -resource "aws_s3_bucket" "this" { - bucket = "test" -}`, - "code/modules/s3/modules/logging/main.tf": ` -resource "aws_s3_bucket" "this" { - bucket = "test1" -}`, - "code/example/main.tf": ` -module "this" { - source = "../modules/s3" -}`, - }, - expected: []string{ - "code", - "code/example", - }, - }, - { - name: "without module block", - files: map[string]string{ - "code/infra1/main.tf": `resource "test" "this" {}`, - "code/infra2/main.tf": `resource "test" "this" {}`, - }, - expected: []string{ - "code/infra1", - "code/infra2", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fsys := testutil.CreateFS(t, tt.files) - parser := New(fsys, "", OptionStopOnHCLError(true)) - - modules := lo.Map(lo.Keys(tt.files), func(p string, _ int) string { - return path.Dir(p) - }) - - got, err := parser.FindRootModules(context.TODO(), modules) - require.NoError(t, err) - assert.Equal(t, tt.expected, got) - }) - } -} diff --git a/pkg/iac/scanners/terraform/parser/option.go b/pkg/iac/scanners/terraform/parser/option.go deleted file mode 100644 index f9ada6545225..000000000000 --- a/pkg/iac/scanners/terraform/parser/option.go +++ /dev/null @@ -1,63 +0,0 @@ -package parser - -import ( - "io/fs" - - "github.com/zclconf/go-cty/cty" -) - -type Option func(p *Parser) - -func OptionWithTFVarsPaths(paths ...string) Option { - return func(p *Parser) { - p.tfvarsPaths = paths - } -} - -func OptionStopOnHCLError(stop bool) Option { - return func(p *Parser) { - p.stopOnHCLError = stop - } -} - -func OptionsWithTfVars(vars map[string]cty.Value) Option { - return func(p *Parser) { - p.tfvars = vars - } -} - -func OptionWithWorkspaceName(workspaceName string) Option { - return func(p *Parser) { - p.workspaceName = workspaceName - } -} - -func OptionWithDownloads(allowed bool) Option { - return func(p *Parser) { - p.allowDownloads = allowed - } -} - -func OptionWithSkipCachedModules(b bool) Option { - return func(p *Parser) { - p.skipCachedModules = b - } -} - -func OptionWithConfigsFS(fsys fs.FS) Option { - return func(p *Parser) { - p.configsFS = fsys - } -} - -func OptionWithSkipFiles(files []string) Option { - return func(p *Parser) { - p.skipPaths = files - } -} - -func OptionWithSkipDirs(dirs []string) Option { - return func(p *Parser) { - p.skipPaths = dirs - } -} diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go deleted file mode 100644 index d47e241b93c6..000000000000 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ /dev/null @@ -1,434 +0,0 @@ -package parser - -import ( - "bufio" - "context" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path" - "path/filepath" - "sort" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclparse" - "github.com/zclconf/go-cty/cty" - - "github.com/aquasecurity/trivy/pkg/fanal/utils" - "github.com/aquasecurity/trivy/pkg/iac/ignore" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - tfcontext "github.com/aquasecurity/trivy/pkg/iac/terraform/context" - "github.com/aquasecurity/trivy/pkg/log" -) - -type sourceFile struct { - file *hcl.File - path string -} - -// Parser is a tool for parsing terraform templates at a given file system location -type Parser struct { - projectRoot string - moduleName string - modulePath string - moduleSource string - moduleFS fs.FS - moduleBlock *terraform.Block - files []sourceFile - tfvarsPaths []string - tfvars map[string]cty.Value - stopOnHCLError bool - workspaceName string - underlying *hclparse.Parser - children []*Parser - options []Option - logger *log.Logger - allowDownloads bool - skipCachedModules bool - fsMap map[string]fs.FS - configsFS fs.FS - skipPaths []string -} - -// New creates a new Parser -func New(moduleFS fs.FS, moduleSource string, opts ...Option) *Parser { - p := &Parser{ - workspaceName: "default", - underlying: hclparse.NewParser(), - options: opts, - moduleName: "root", - allowDownloads: true, - moduleFS: moduleFS, - moduleSource: moduleSource, - configsFS: moduleFS, - logger: log.WithPrefix("terraform parser").With("module", "root"), - tfvars: make(map[string]cty.Value), - } - - for _, option := range opts { - option(p) - } - - return p -} - -func (p *Parser) newModuleParser(moduleFS fs.FS, moduleSource, modulePath, moduleName string, moduleBlock *terraform.Block) *Parser { - mp := New(moduleFS, moduleSource) - mp.modulePath = modulePath - mp.moduleBlock = moduleBlock - mp.moduleName = moduleName - mp.logger = log.WithPrefix("terraform parser").With("module", moduleName) - mp.projectRoot = p.projectRoot - mp.skipPaths = p.skipPaths - mp.options = p.options - p.children = append(p.children, mp) - for _, option := range p.options { - option(mp) - } - return mp -} - -func (p *Parser) Files() map[string]*hcl.File { - return p.underlying.Files() -} - -func (p *Parser) ParseFile(_ context.Context, fullPath string) error { - - isJSON := strings.HasSuffix(fullPath, ".tf.json") - isHCL := strings.HasSuffix(fullPath, ".tf") - if !isJSON && !isHCL { - return nil - } - - p.logger.Debug("Parsing", log.FilePath(fullPath)) - f, err := p.moduleFS.Open(filepath.ToSlash(fullPath)) - if err != nil { - return err - } - defer func() { _ = f.Close() }() - - data, err := io.ReadAll(f) - if err != nil { - return err - } - - if dir := path.Dir(fullPath); p.projectRoot == "" { - p.logger.Debug("Setting project/module root", log.FilePath(dir)) - p.projectRoot = dir - p.modulePath = dir - } - - var file *hcl.File - var diag hcl.Diagnostics - - if isHCL { - file, diag = p.underlying.ParseHCL(data, fullPath) - } else { - file, diag = p.underlying.ParseJSON(data, fullPath) - } - if diag != nil && diag.HasErrors() { - return diag - } - p.files = append(p.files, sourceFile{ - file: file, - path: fullPath, - }) - - p.logger.Debug("Added file", log.FilePath(fullPath)) - return nil -} - -// ParseFS parses a root module, where it exists at the root of the provided filesystem -func (p *Parser) ParseFS(ctx context.Context, dir string) error { - - dir = path.Clean(dir) - - if p.projectRoot == "" { - p.logger.Debug("Setting project/module root", log.FilePath(dir)) - p.projectRoot = dir - p.modulePath = dir - } - - slashed := filepath.ToSlash(dir) - p.logger.Debug("Parsing FS", log.FilePath(slashed)) - fileInfos, err := fs.ReadDir(p.moduleFS, slashed) - if err != nil { - return err - } - - var paths []string - for _, info := range fileInfos { - realPath := path.Join(dir, info.Name()) - if info.IsDir() { - continue - } - if utils.SkipPath(realPath, utils.CleanSkipPaths(p.skipPaths)) { - p.logger.Debug("Skipping path based on input glob", log.FilePath(realPath), log.Any("glob", p.skipPaths)) - continue - } - paths = append(paths, realPath) - } - sort.Strings(paths) - for _, path := range paths { - var err error - if err = p.ParseFile(ctx, path); err == nil { - continue - } - - if p.stopOnHCLError { - return err - } - var diags hcl.Diagnostics - if errors.As(err, &diags) { - errc := p.showParseErrors(p.moduleFS, path, diags) - if errc == nil { - continue - } - p.logger.Error("Failed to get the causes of the parsing error", log.Err(errc)) - } - p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) - continue - } - - return nil -} - -func (p *Parser) showParseErrors(fsys fs.FS, filePath string, diags hcl.Diagnostics) error { - file, err := fsys.Open(filePath) - if err != nil { - return fmt.Errorf("failed to read file: %w", err) - } - defer file.Close() - - for _, diag := range diags { - if subj := diag.Subject; subj != nil { - lines, err := readLinesFromFile(file, subj.Start.Line, subj.End.Line) - if err != nil { - return err - } - - cause := strings.Join(lines, "\n") - p.logger.Error("Error parsing file", log.FilePath(filePath), - log.String("cause", cause), log.Err(diag)) - } - } - - return nil -} - -func readLinesFromFile(f io.Reader, from, to int) ([]string, error) { - scanner := bufio.NewScanner(f) - rawLines := make([]string, 0, to-from+1) - - for lineNum := 0; scanner.Scan() && lineNum < to; lineNum++ { - if lineNum >= from-1 { - rawLines = append(rawLines, scanner.Text()) - } - } - - if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("failed to scan file: %w", err) - } - - return rawLines, nil -} - -var ErrNoFiles = errors.New("no files found") - -func (p *Parser) Load(ctx context.Context) (*evaluator, error) { - p.logger.Debug("Loading module", log.String("module", p.moduleName)) - - if len(p.files) == 0 { - p.logger.Info("No files found, nothing to do.") - return nil, ErrNoFiles - } - - blocks, ignores, err := p.readBlocks(p.files) - if err != nil { - return nil, err - } - p.logger.Debug("Read block(s) and ignore(s)", - log.Int("blocks", len(blocks)), log.Int("ignores", len(ignores))) - - var inputVars map[string]cty.Value - - switch { - case p.moduleBlock != nil: - inputVars = p.moduleBlock.Values().AsValueMap() - p.logger.Debug("Added input variables from module definition", - log.Int("count", len(inputVars))) - case len(p.tfvars) > 0: - inputVars = p.tfvars - p.logger.Debug("Added input variables from tfvars.", log.Int("count", len(inputVars))) - default: - inputVars, err = loadTFVars(p.configsFS, p.tfvarsPaths) - if err != nil { - return nil, err - } - p.logger.Debug("Added input variables from tfvars", log.Int("count", len(inputVars))) - - if missingVars := missingVariableValues(blocks, inputVars); len(missingVars) > 0 { - p.logger.Warn( - "Variable values was not found in the environment or variable files. Evaluating may not work correctly.", - log.String("variables", strings.Join(missingVars, ", ")), - ) - setNullMissingVariableValues(inputVars, missingVars) - } - } - - modulesMetadata, metadataPath, err := loadModuleMetadata(p.moduleFS, p.projectRoot) - - if err != nil && !errors.Is(err, os.ErrNotExist) { - p.logger.Error("Error loading module metadata", log.Err(err)) - } else if err == nil { - p.logger.Debug("Loaded module metadata for modules", - log.FilePath(metadataPath), - log.Int("count", len(modulesMetadata.Modules)), - ) - } - - workingDir, err := os.Getwd() - if err != nil { - return nil, err - } - - p.logger.Debug("Working directory for module evaluation", log.FilePath(workingDir)) - return newEvaluator( - p.moduleFS, - p, - p.projectRoot, - p.modulePath, - workingDir, - p.moduleName, - blocks, - inputVars, - modulesMetadata, - p.workspaceName, - ignores, - log.WithPrefix("terraform evaluator"), - p.allowDownloads, - p.skipCachedModules, - ), nil -} - -func missingVariableValues(blocks terraform.Blocks, inputVars map[string]cty.Value) []string { - var missing []string - for _, varBlock := range blocks.OfType("variable") { - if varBlock.GetAttribute("default") == nil { - if _, ok := inputVars[varBlock.TypeLabel()]; !ok { - missing = append(missing, varBlock.TypeLabel()) - } - } - } - - return missing -} - -// Set null values for missing variables, to allow expressions using them to be -// still be possibly evaluated to a value different than null. -func setNullMissingVariableValues(inputVars map[string]cty.Value, missingVars []string) { - for _, missingVar := range missingVars { - inputVars[missingVar] = cty.NullVal(cty.DynamicPseudoType) - } -} - -func (p *Parser) EvaluateAll(ctx context.Context) (terraform.Modules, cty.Value, error) { - - e, err := p.Load(ctx) - if errors.Is(err, ErrNoFiles) { - return nil, cty.NilVal, nil - } else if err != nil { - return nil, cty.NilVal, err - } - - modules, fsMap := e.EvaluateAll(ctx) - p.logger.Debug("Finished parsing module") - p.fsMap = fsMap - return modules, e.exportOutputs(), nil -} - -func (p *Parser) GetFilesystemMap() map[string]fs.FS { - if p.fsMap == nil { - return make(map[string]fs.FS) - } - return p.fsMap -} - -func (p *Parser) readBlocks(files []sourceFile) (terraform.Blocks, ignore.Rules, error) { - var blocks terraform.Blocks - var ignores ignore.Rules - moduleCtx := tfcontext.NewContext(&hcl.EvalContext{}, nil) - for _, file := range files { - fileBlocks, err := loadBlocksFromFile(file) - if err != nil { - if p.stopOnHCLError { - return nil, nil, err - } - p.logger.Error("Encountered HCL parse error", log.FilePath(file.path), log.Err(err)) - continue - } - for _, fileBlock := range fileBlocks { - blocks = append(blocks, terraform.NewBlock(fileBlock, moduleCtx, p.moduleBlock, nil, p.moduleSource, p.moduleFS)) - } - fileIgnores := ignore.Parse( - string(file.file.Bytes), - file.path, - p.moduleSource, - &ignore.StringMatchParser{ - SectionKey: "ws", - }, - ¶mParser{}, - ) - ignores = append(ignores, fileIgnores...) - } - - sortBlocksByHierarchy(blocks) - return blocks, ignores, nil -} - -func loadBlocksFromFile(file sourceFile) (hcl.Blocks, error) { - contents, diagnostics := file.file.Body.Content(terraform.Schema) - if diagnostics != nil && diagnostics.HasErrors() { - return nil, diagnostics - } - if contents == nil { - return nil, nil - } - return contents.Blocks, nil -} - -type paramParser struct { - params map[string]string -} - -func (s *paramParser) Key() string { - return "ignore" -} - -func (s *paramParser) Parse(str string) bool { - s.params = make(map[string]string) - - idx := strings.Index(str, "[") - if idx == -1 { - return false - } - - str = str[idx+1:] - - paramStr := strings.TrimSuffix(str, "]") - for _, pair := range strings.Split(paramStr, ",") { - parts := strings.Split(pair, "=") - if len(parts) != 2 { - continue - } - s.params[parts[0]] = parts[1] - } - return true -} - -func (s *paramParser) Param() any { - return s.params -} diff --git a/pkg/iac/scanners/terraform/parser/parser_integration_test.go b/pkg/iac/scanners/terraform/parser/parser_integration_test.go deleted file mode 100644 index 5b301c0e41dd..000000000000 --- a/pkg/iac/scanners/terraform/parser/parser_integration_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" -) - -func Test_DefaultRegistry(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fsys := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -module "registry" { - source = "terraform-aws-modules/vpc/aws" -} -`, - }) - - parser := New(fsys, "", OptionStopOnHCLError(true), OptionWithSkipCachedModules(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 2) -} - -func Test_SpecificRegistry(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fsys := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -module "registry" { - source = "registry.terraform.io/terraform-aws-modules/vpc/aws" -} -`, - }) - - parser := New(fsys, "", OptionStopOnHCLError(true), OptionWithSkipCachedModules(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 2) -} - -func Test_ModuleWithPessimisticVersionConstraint(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fsys := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -module "registry" { - source = "registry.terraform.io/terraform-aws-modules/s3-bucket/aws" - bucket = "my-s3-bucket" - version = "~> 3.1" -} -`, - }) - - parser := New(fsys, "", OptionStopOnHCLError(true), OptionWithSkipCachedModules(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 2) -} - -func Test_ModuleInSubdir(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fsys := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -module "object" { - source = "git::https://github.com/terraform-aws-modules/terraform-aws-s3-bucket.git//modules/object?ref=v4.1.2" - -}`, - }) - - parser := New(fsys, "", OptionStopOnHCLError(true), OptionWithSkipCachedModules(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 2) -} diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go deleted file mode 100644 index 0e64d1014444..000000000000 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ /dev/null @@ -1,2408 +0,0 @@ -package parser - -import ( - "bytes" - "context" - "io/fs" - "log/slog" - "os" - "path/filepath" - "slices" - "sort" - "testing" - "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/zclconf/go-cty/cty" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" -) - -func Test_BasicParsing(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "test.tf": ` - -locals { - proxy = var.cats_mother -} - -variable "cats_mother" { - default = "boots" -} - -provider "cats" { - -} - -moved { - -} - -import { - to = cats_cat.mittens - id = "mittens" -} - -resource "cats_cat" "mittens" { - name = "mittens" - special = true -} - -resource "cats_kitten" "the-great-destroyer" { - name = "the great destroyer" - parent = cats_cat.mittens.name -} - -data "cats_cat" "the-cats-mother" { - name = local.proxy -} - -check "cats_mittens_is_special" { - data "cats_cat" "mittens" { - name = "mittens" - } - - assert { - condition = data.cats_cat.mittens.special == true - error_message = "${data.cats_cat.mittens.name} must be special" - } -} - -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - blocks := modules[0].GetBlocks() - - // variable - variables := blocks.OfType("variable") - require.Len(t, variables, 1) - assert.Equal(t, "variable", variables[0].Type()) - require.Len(t, variables[0].Labels(), 1) - assert.Equal(t, "cats_mother", variables[0].TypeLabel()) - defaultVal := variables[0].GetAttribute("default") - require.NotNil(t, defaultVal) - assert.Equal(t, cty.String, defaultVal.Value().Type()) - assert.Equal(t, "boots", defaultVal.Value().AsString()) - - // provider - providerBlocks := blocks.OfType("provider") - require.Len(t, providerBlocks, 1) - assert.Equal(t, "provider", providerBlocks[0].Type()) - require.Len(t, providerBlocks[0].Labels(), 1) - assert.Equal(t, "cats", providerBlocks[0].TypeLabel()) - - // resources - resourceBlocks := blocks.OfType("resource") - - sort.Slice(resourceBlocks, func(i, j int) bool { - return resourceBlocks[i].TypeLabel() < resourceBlocks[j].TypeLabel() - }) - - require.Len(t, resourceBlocks, 2) - require.Len(t, resourceBlocks[0].Labels(), 2) - - assert.Equal(t, "resource", resourceBlocks[0].Type()) - assert.Equal(t, "cats_cat", resourceBlocks[0].TypeLabel()) - assert.Equal(t, "mittens", resourceBlocks[0].NameLabel()) - - assert.Equal(t, "mittens", resourceBlocks[0].GetAttribute("name").Value().AsString()) - assert.True(t, resourceBlocks[0].GetAttribute("special").Value().True()) - - assert.Equal(t, "resource", resourceBlocks[1].Type()) - assert.Equal(t, "cats_kitten", resourceBlocks[1].TypeLabel()) - assert.Equal(t, "the great destroyer", resourceBlocks[1].GetAttribute("name").Value().AsString()) - assert.Equal(t, "mittens", resourceBlocks[1].GetAttribute("parent").Value().AsString()) - - // import - importBlocks := blocks.OfType("import") - - assert.Equal(t, "import", importBlocks[0].Type()) - require.NotNil(t, importBlocks[0].GetAttribute("to")) - assert.Equal(t, "mittens", importBlocks[0].GetAttribute("id").Value().AsString()) - - // data - dataBlocks := blocks.OfType("data") - require.Len(t, dataBlocks, 1) - require.Len(t, dataBlocks[0].Labels(), 2) - - assert.Equal(t, "data", dataBlocks[0].Type()) - assert.Equal(t, "cats_cat", dataBlocks[0].TypeLabel()) - assert.Equal(t, "the-cats-mother", dataBlocks[0].NameLabel()) - - assert.Equal(t, "boots", dataBlocks[0].GetAttribute("name").Value().AsString()) - - // check - checkBlocks := blocks.OfType("check") - require.Len(t, checkBlocks, 1) - require.Len(t, checkBlocks[0].Labels(), 1) - - assert.Equal(t, "check", checkBlocks[0].Type()) - assert.Equal(t, "cats_mittens_is_special", checkBlocks[0].TypeLabel()) - - require.NotNil(t, checkBlocks[0].GetBlock("data")) - require.NotNil(t, checkBlocks[0].GetBlock("assert")) -} - -func Test_Modules(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -module "my-mod" { - source = "../module" - input = "ok" -} - -output "result" { - value = module.my-mod.mod_result -} -`, - "module/module.tf": ` -variable "input" { - default = "?" -} - -output "mod_result" { - value = var.input -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - require.Len(t, modules, 2) - rootModule := modules[0] - childModule := modules[1] - - moduleBlocks := rootModule.GetBlocks().OfType("module") - require.Len(t, moduleBlocks, 1) - - assert.Equal(t, "module", moduleBlocks[0].Type()) - assert.Equal(t, "module.my-mod", moduleBlocks[0].FullName()) - inputAttr := moduleBlocks[0].GetAttribute("input") - require.NotNil(t, inputAttr) - require.Equal(t, cty.String, inputAttr.Value().Type()) - assert.Equal(t, "ok", inputAttr.Value().AsString()) - - rootOutputs := rootModule.GetBlocks().OfType("output") - require.Len(t, rootOutputs, 1) - assert.Equal(t, "output.result", rootOutputs[0].FullName()) - valAttr := rootOutputs[0].GetAttribute("value") - require.NotNil(t, valAttr) - require.Equal(t, cty.String, valAttr.Type()) - assert.Equal(t, "ok", valAttr.Value().AsString()) - - childOutputs := childModule.GetBlocks().OfType("output") - require.Len(t, childOutputs, 1) - assert.Equal(t, "module.my-mod.output.mod_result", childOutputs[0].FullName()) - childValAttr := childOutputs[0].GetAttribute("value") - require.NotNil(t, childValAttr) - require.Equal(t, cty.String, childValAttr.Type()) - assert.Equal(t, "ok", childValAttr.Value().AsString()) - -} - -func Test_NestedParentModule(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -module "my-mod" { - source = "../." - input = "ok" -} - -output "result" { - value = module.my-mod.mod_result -} -`, - "root.tf": ` -variable "input" { - default = "?" -} - -output "mod_result" { - value = var.input -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 2) - rootModule := modules[0] - childModule := modules[1] - - moduleBlocks := rootModule.GetBlocks().OfType("module") - require.Len(t, moduleBlocks, 1) - - assert.Equal(t, "module", moduleBlocks[0].Type()) - assert.Equal(t, "module.my-mod", moduleBlocks[0].FullName()) - inputAttr := moduleBlocks[0].GetAttribute("input") - require.NotNil(t, inputAttr) - require.Equal(t, cty.String, inputAttr.Value().Type()) - assert.Equal(t, "ok", inputAttr.Value().AsString()) - - rootOutputs := rootModule.GetBlocks().OfType("output") - require.Len(t, rootOutputs, 1) - assert.Equal(t, "output.result", rootOutputs[0].FullName()) - valAttr := rootOutputs[0].GetAttribute("value") - require.NotNil(t, valAttr) - require.Equal(t, cty.String, valAttr.Type()) - assert.Equal(t, "ok", valAttr.Value().AsString()) - - childOutputs := childModule.GetBlocks().OfType("output") - require.Len(t, childOutputs, 1) - assert.Equal(t, "module.my-mod.output.mod_result", childOutputs[0].FullName()) - childValAttr := childOutputs[0].GetAttribute("value") - require.NotNil(t, childValAttr) - require.Equal(t, cty.String, childValAttr.Type()) - assert.Equal(t, "ok", childValAttr.Value().AsString()) -} - -func Test_UndefinedModuleOutputReference(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -resource "something" "blah" { - value = module.x.y -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.False(t, attr.IsResolvable()) -} - -func Test_UndefinedModuleOutputReferenceInSlice(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -resource "something" "blah" { - value = ["first", module.x.y, "last"] -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.True(t, attr.IsResolvable()) - - values := attr.AsStringValueSliceOrEmpty() - require.Len(t, values, 3) - - assert.Equal(t, "first", values[0].Value()) - assert.True(t, values[0].GetMetadata().IsResolvable()) - - assert.False(t, values[1].GetMetadata().IsResolvable()) - - assert.Equal(t, "last", values[2].Value()) - assert.True(t, values[2].GetMetadata().IsResolvable()) -} - -func Test_TemplatedSliceValue(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` - -variable "x" { - default = "hello" -} - -resource "something" "blah" { - value = ["first", "${var.x}-${var.x}", "last"] -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.True(t, attr.IsResolvable()) - - values := attr.AsStringValueSliceOrEmpty() - require.Len(t, values, 3) - - assert.Equal(t, "first", values[0].Value()) - assert.True(t, values[0].GetMetadata().IsResolvable()) - - assert.Equal(t, "hello-hello", values[1].Value()) - assert.True(t, values[1].GetMetadata().IsResolvable()) - - assert.Equal(t, "last", values[2].Value()) - assert.True(t, values[2].GetMetadata().IsResolvable()) -} - -func Test_SliceOfVars(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` - -variable "x" { - default = "1" -} - -variable "y" { - default = "2" -} - -resource "something" "blah" { - value = [var.x, var.y] -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.True(t, attr.IsResolvable()) - - values := attr.AsStringValueSliceOrEmpty() - require.Len(t, values, 2) - - assert.Equal(t, "1", values[0].Value()) - assert.True(t, values[0].GetMetadata().IsResolvable()) - - assert.Equal(t, "2", values[1].Value()) - assert.True(t, values[1].GetMetadata().IsResolvable()) -} - -func Test_VarSlice(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` - -variable "x" { - default = ["a", "b", "c"] -} - -resource "something" "blah" { - value = var.x -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.True(t, attr.IsResolvable()) - - values := attr.AsStringValueSliceOrEmpty() - require.Len(t, values, 3) - - assert.Equal(t, "a", values[0].Value()) - assert.True(t, values[0].GetMetadata().IsResolvable()) - - assert.Equal(t, "b", values[1].Value()) - assert.True(t, values[1].GetMetadata().IsResolvable()) - - assert.Equal(t, "c", values[2].Value()) - assert.True(t, values[2].GetMetadata().IsResolvable()) -} - -func Test_LocalSliceNested(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` - -variable "x" { - default = "a" -} - -locals { - y = [var.x, "b", "c"] -} - -resource "something" "blah" { - value = local.y -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.True(t, attr.IsResolvable()) - - values := attr.AsStringValueSliceOrEmpty() - require.Len(t, values, 3) - - assert.Equal(t, "a", values[0].Value()) - assert.True(t, values[0].GetMetadata().IsResolvable()) - - assert.Equal(t, "b", values[1].Value()) - assert.True(t, values[1].GetMetadata().IsResolvable()) - - assert.Equal(t, "c", values[2].Value()) - assert.True(t, values[2].GetMetadata().IsResolvable()) -} - -func Test_FunctionCall(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` - -variable "x" { - default = ["a", "b"] -} - -resource "something" "blah" { - value = concat(var.x, ["c"]) -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("value") - require.NotNil(t, attr) - - assert.True(t, attr.IsResolvable()) - - values := attr.AsStringValueSliceOrEmpty() - require.Len(t, values, 3) - - assert.Equal(t, "a", values[0].Value()) - assert.True(t, values[0].GetMetadata().IsResolvable()) - - assert.Equal(t, "b", values[1].Value()) - assert.True(t, values[1].GetMetadata().IsResolvable()) - - assert.Equal(t, "c", values[2].Value()) - assert.True(t, values[2].GetMetadata().IsResolvable()) -} - -func Test_NullDefaultValueForVar(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "test.tf": ` -variable "bucket_name" { - type = string - default = null -} - -resource "aws_s3_bucket" "default" { - bucket = var.bucket_name != null ? var.bucket_name : "default" -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("aws_s3_bucket") - require.Len(t, blocks, 1) - block := blocks[0] - - attr := block.GetAttribute("bucket") - require.NotNil(t, attr) - assert.Equal(t, "default", attr.Value().AsString()) -} - -func Test_MultipleInstancesOfSameResource(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "test.tf": ` - -resource "aws_kms_key" "key1" { - description = "Key #1" - enable_key_rotation = true -} - -resource "aws_kms_key" "key2" { - description = "Key #2" - enable_key_rotation = true -} - -resource "aws_s3_bucket" "this" { - bucket = "test" - } - - -resource "aws_s3_bucket_server_side_encryption_configuration" "this1" { - bucket = aws_s3_bucket.this.id - - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = aws_kms_key.key1.arn - sse_algorithm = "aws:kms" - } - } -} - -resource "aws_s3_bucket_server_side_encryption_configuration" "this2" { - bucket = aws_s3_bucket.this.id - - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = aws_kms_key.key2.arn - sse_algorithm = "aws:kms" - } - } -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("aws_s3_bucket_server_side_encryption_configuration") - assert.Len(t, blocks, 2) - - for _, block := range blocks { - attr, parent := block.GetNestedAttribute("rule.apply_server_side_encryption_by_default.kms_master_key_id") - assert.Equal(t, "apply_server_side_encryption_by_default", parent.Type()) - assert.NotNil(t, attr) - assert.NotEmpty(t, attr.Value().AsString()) - } -} - -func Test_IfConfigFsIsNotSet_ThenUseModuleFsForVars(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -variable "bucket_name" { - type = string -} -resource "aws_s3_bucket" "main" { - bucket = var.bucket_name -} -`, - "main.tfvars": `bucket_name = "test_bucket"`, - }) - parser := New(fs, "", - OptionStopOnHCLError(true), - OptionWithTFVarsPaths("main.tfvars"), - ) - - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - blocks := rootModule.GetResourcesByType("aws_s3_bucket") - require.Len(t, blocks, 1) - - block := blocks[0] - - assert.Equal(t, "test_bucket", block.GetAttribute("bucket").AsStringValueOrDefault("", block).Value()) -} - -func Test_ForEachRefToLocals(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -locals { - buckets = toset([ - "foo", - "bar", - ]) -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.key -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("aws_s3_bucket") - assert.Len(t, blocks, 2) - - for _, block := range blocks { - attr := block.GetAttribute("bucket") - require.NotNil(t, attr) - assert.Contains(t, []string{"foo", "bar"}, attr.AsStringValueOrDefault("", block).Value()) - } -} - -func Test_ForEachRefToVariableWithDefault(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -variable "buckets" { - type = set(string) - default = ["foo", "bar"] -} - -resource "aws_s3_bucket" "this" { - for_each = var.buckets - bucket = each.key -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("aws_s3_bucket") - assert.Len(t, blocks, 2) - - for _, block := range blocks { - attr := block.GetAttribute("bucket") - require.NotNil(t, attr) - assert.Contains(t, []string{"foo", "bar"}, attr.AsStringValueOrDefault("", block).Value()) - } -} - -func Test_ForEachRefToVariableFromFile(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -variable "policy_rules" { - type = object({ - secure_tags = optional(map(object({ - session_matcher = optional(string) - priority = number - enabled = optional(bool, true) - })), {}) - }) -} - -resource "google_network_security_gateway_security_policy_rule" "secure_tag_rules" { - for_each = var.policy_rules.secure_tags - provider = google-beta - project = "test" - name = each.key - enabled = each.value.enabled - priority = each.value.priority - session_matcher = each.value.session_matcher -} -`, - "main.tfvars": ` -policy_rules = { - secure_tags = { - secure-tag-1 = { - session_matcher = "host() != 'google.com'" - priority = 1001 - } - } -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true), OptionWithTFVarsPaths("main.tfvars")) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("google_network_security_gateway_security_policy_rule") - assert.Len(t, blocks, 1) - - block := blocks[0] - - assert.Equal(t, "secure-tag-1", block.GetAttribute("name").AsStringValueOrDefault("", block).Value()) - assert.True(t, block.GetAttribute("enabled").AsBoolValueOrDefault(false, block).Value()) - assert.Equal(t, "host() != 'google.com'", block.GetAttribute("session_matcher").AsStringValueOrDefault("", block).Value()) - assert.Equal(t, 1001, block.GetAttribute("priority").AsIntValueOrDefault(0, block).Value()) -} - -func Test_ForEachRefersToMapThatContainsSameStringValues(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": `locals { - buckets = { - bucket1 = "test1" - bucket2 = "test1" - } -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.key -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - bucketBlocks := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, bucketBlocks, 2) - - var labels []string - - for _, b := range bucketBlocks { - labels = append(labels, b.Label()) - } - - expectedLabels := []string{ - `aws_s3_bucket.this["bucket1"]`, - `aws_s3_bucket.this["bucket2"]`, - } - assert.Equal(t, expectedLabels, labels) -} - -func TestDataSourceWithCountMetaArgument(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -data "http" "example" { - count = 2 -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - httpDataSources := rootModule.GetDatasByType("http") - assert.Len(t, httpDataSources, 2) - - var labels []string - for _, b := range httpDataSources { - labels = append(labels, b.Label()) - } - - expectedLabels := []string{ - `http.example[0]`, - `http.example[1]`, - } - assert.Equal(t, expectedLabels, labels) -} - -func TestDataSourceWithForEachMetaArgument(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -locals { - ports = ["80", "8080"] -} -data "http" "example" { - for_each = toset(local.ports) - url = "localhost:${each.key}" -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - httpDataSources := rootModule.GetDatasByType("http") - assert.Len(t, httpDataSources, 2) -} - -func TestForEach(t *testing.T) { - tests := []struct { - name string - src string - expectedBucketName string - expectedNameLabel string - }{ - { - name: "arg is set and ref to each.key", - src: `locals { - buckets = ["bucket1"] -} - -resource "aws_s3_bucket" "this" { - for_each = toset(local.buckets) - bucket = each.key -}`, - expectedBucketName: "bucket1", - expectedNameLabel: `this["bucket1"]`, - }, - { - name: "arg is set and ref to each.value", - src: `locals { - buckets = ["bucket1"] -} - -resource "aws_s3_bucket" "this" { - for_each = toset(local.buckets) - bucket = each.value -}`, - expectedBucketName: "bucket1", - expectedNameLabel: `this["bucket1"]`, - }, - { - name: "arg is map and ref to each.key", - src: `locals { - buckets = { - bucket1key = "bucket1value" - } -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.key -}`, - expectedBucketName: "bucket1key", - expectedNameLabel: `this["bucket1key"]`, - }, - { - name: "arg is map and ref to each.value", - src: `locals { - buckets = { - bucket1key = "bucket1value" - } -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.value -}`, - expectedBucketName: "bucket1value", - expectedNameLabel: `this["bucket1key"]`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": tt.src, - }) - require.Len(t, modules, 1) - - buckets := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, buckets, 1) - - bucket := buckets[0] - bucketName := bucket.GetAttribute("bucket").Value().AsString() - assert.Equal(t, tt.expectedBucketName, bucketName) - - assert.Equal(t, tt.expectedNameLabel, bucket.NameLabel()) - }) - } - -} - -func TestForEachCountExpanded(t *testing.T) { - - tests := []struct { - name string - source string - expectedCount int - }{ - { - name: "arg is list of strings", - source: `locals { - buckets = ["bucket1", "bucket2"] -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.key -}`, - expectedCount: 2, - }, - { - name: "arg is empty list", - source: `locals { - buckets = [] -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.value -}`, - expectedCount: 0, - }, - { - name: "arg is empty set", - source: `locals { - buckets = toset([]) -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.key -}`, - expectedCount: 0, - }, - { - name: "argument set with the same values", - source: `locals { - buckets = ["true", "true"] -} - -resource "aws_s3_bucket" "this" { - for_each = toset(local.buckets) - bucket = each.key -}`, - expectedCount: 1, - }, - { - name: "arg is non-valid set", - source: `locals { - buckets = [{ - bucket1key = "bucket1value" - }] -} - -resource "aws_s3_bucket" "this" { - for_each = toset(local.buckets) - bucket = each.value -}`, - expectedCount: 0, - }, - { - name: "arg is set of strings", - source: `locals { - buckets = ["bucket1", "bucket2"] -} - -resource "aws_s3_bucket" "this" { - for_each = toset(local.buckets) - bucket = each.key -}`, - expectedCount: 2, - }, - { - name: "arg is map", - source: `locals { - buckets = { - 1 = {} - 2 = {} - } -} - -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.key -}`, - expectedCount: 2, - }, - { - name: "arg is empty map", - source: `locals { - buckets = {} -} -resource "aws_s3_bucket" "this" { - for_each = local.buckets - bucket = each.value -} - `, - expectedCount: 0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": tt.source, - }) - assert.Len(t, modules, 1) - - bucketBlocks := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, bucketBlocks, tt.expectedCount) - }) - } -} - -func TestForEachRefToResource(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` - locals { - vpcs = { - "test1" = { - cidr_block = "192.168.0.0/28" - } - "test2" = { - cidr_block = "192.168.1.0/28" - } - } -} - -resource "aws_vpc" "example" { - for_each = local.vpcs - cidr_block = each.value.cidr_block -} - -resource "aws_internet_gateway" "example" { - for_each = aws_vpc.example - vpc_id = each.key -} -`, - }) - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - - blocks := modules.GetResourcesByType("aws_internet_gateway") - require.Len(t, blocks, 2) - - var vpcIds []string - for _, b := range blocks { - vpcIds = append(vpcIds, b.GetAttribute("vpc_id").Value().AsString()) - } - - expectedVpcIds := []string{"test1", "test2"} - assert.Equal(t, expectedVpcIds, vpcIds) -} - -func TestArnAttributeOfBucketIsCorrect(t *testing.T) { - - t.Run("the bucket doesn't have a name", func(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": `resource "aws_s3_bucket" "this" {}`, - }) - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - - blocks := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, blocks, 1) - - bucket := blocks[0] - - values := bucket.Values() - arnVal := values.GetAttr("arn") - assert.True(t, arnVal.Type().Equals(cty.String)) - - id := values.GetAttr("id").AsString() - - arn := arnVal.AsString() - assert.Equal(t, "arn:aws:s3:::"+id, arn) - }) - - t.Run("the bucket has a name", func(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": `resource "aws_s3_bucket" "this" { - bucket = "test" -} - -resource "aws_iam_role" "this" { - name = "test_role" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Sid = "" - Principal = { - Service = "s3.amazonaws.com" - } - }, - ] - }) -} - -resource "aws_iam_role_policy" "this" { - name = "test_policy" - role = aws_iam_role.this.id - policy = data.aws_iam_policy_document.this.json -} - -data "aws_iam_policy_document" "this" { - statement { - effect = "Allow" - actions = [ - "s3:GetObject" - ] - resources = ["${aws_s3_bucket.this.arn}/*"] - } -}`, - }) - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - - blocks := modules[0].GetDatasByType("aws_iam_policy_document") - assert.Len(t, blocks, 1) - - policyDoc := blocks[0] - - statement := policyDoc.GetBlock("statement") - resources := statement.GetAttribute("resources").AsStringValueSliceOrEmpty() - - assert.Len(t, resources, 1) - assert.True(t, resources[0].EqualTo("arn:aws:s3:::test/*")) - }) -} - -func TestForEachWithObjectsOfDifferentTypes(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": `module "backups" { - bucket_name = each.key - client = each.value.client - path_writers = each.value.path_writers - - for_each = { - "bucket1" = { - client = "client1" - path_writers = ["writer1"] // tuple with string - }, - "bucket2" = { - client = "client2" - path_writers = [] // empty tuple - } - } -} -`, - }) - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) -} - -func TestCountMetaArgument(t *testing.T) { - tests := []struct { - name string - src string - expected int - }{ - { - name: "zero resources", - src: `resource "test" "this" { - count = 0 -}`, - expected: 0, - }, - { - name: "several resources", - src: `resource "test" "this" { - count = 2 -}`, - expected: 2, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fsys := testutil.CreateFS(t, map[string]string{ - "main.tf": tt.src, - }) - parser := New(fsys, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - resources := modules.GetResourcesByType("test") - assert.Len(t, resources, tt.expected) - }) - } -} - -func TestCountMetaArgumentInModule(t *testing.T) { - tests := []struct { - name string - files map[string]string - expectedCountModules int - expectedCountResources int - }{ - { - name: "zero modules", - files: map[string]string{ - "main.tf": `module "this" { - count = 0 - source = "./modules/test" -}`, - "modules/test/main.tf": `resource "test" "this" {}`, - }, - expectedCountModules: 1, - expectedCountResources: 0, - }, - { - name: "several modules", - files: map[string]string{ - "main.tf": `module "this" { - count = 2 - source = "./modules/test" -}`, - "modules/test/main.tf": `resource "test" "this" {}`, - }, - expectedCountModules: 3, - expectedCountResources: 2, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fsys := testutil.CreateFS(t, tt.files) - parser := New(fsys, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - assert.Len(t, modules, tt.expectedCountModules) - - resources := modules.GetResourcesByType("test") - assert.Len(t, resources, tt.expectedCountResources) - }) - } -} - -func TestDynamicBlocks(t *testing.T) { - tests := []struct { - name string - src string - expected []any - }{ - { - name: "for-each use tuple of int", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = [80, 443] - content { - bar = foo.value - } - } -}`, - expected: []any{float64(80), float64(443)}, - }, - { - name: "for-each use list of int", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = tolist([80, 443]) - content { - bar = foo.value - } - } -}`, - expected: []any{float64(80), float64(443)}, - }, - { - name: "for-each use set of int", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = toset([80, 443]) - content { - bar = foo.value - } - } -}`, - expected: []any{float64(80), float64(443)}, - }, - { - name: "for-each use list of bool", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = tolist([true]) - content { - bar = foo.value - } - } -}`, - expected: []any{true}, - }, - { - name: "empty for-each", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = [] - content {} - } -}`, - expected: []any{}, - }, - { - name: "for-each use tuple of objects", - src: `variable "test_var" { - type = list(object({ enabled = bool })) - default = [{ enabled = true }] -} - -resource "test_resource" "test" { - dynamic "foo" { - for_each = var.test_var - - content { - bar = foo.value.enabled - } - } -}`, - expected: []any{true}, - }, - { - name: "attribute ref to object key", - src: `variable "some_var" { - type = map( - object({ - tag = string - }) - ) - default = { - ssh = { "tag" = "login" } - http = { "tag" = "proxy" } - https = { "tag" = "proxy" } - } -} - -resource "test_resource" "test" { - dynamic "foo" { - for_each = { for name, values in var.some_var : name => values } - content { - bar = foo.key - } - } -}`, - expected: []any{"ssh", "http", "https"}, - }, - { - name: "attribute ref to object value", - src: `variable "some_var" { - type = map( - object({ - tag = string - }) - ) - default = { - ssh = { "tag" = "login" } - http = { "tag" = "proxy" } - https = { "tag" = "proxy" } - } -} - -resource "test_resource" "test" { - dynamic "foo" { - for_each = { for name, values in var.some_var : name => values } - content { - bar = foo.value.tag - } - } -}`, - expected: []any{"login", "proxy", "proxy"}, - }, - { - name: "attribute ref to map key", - src: `variable "some_var" { - type = map - default = { - ssh = { "tag" = "login" } - http = { "tag" = "proxy" } - https = { "tag" = "proxy" } - } -} - -resource "test_resource" "test" { - dynamic "foo" { - for_each = var.some_var - content { - bar = foo.key - } - } -}`, - expected: []any{"ssh", "http", "https"}, - }, - { - name: "attribute ref to map value", - src: `variable "some_var" { - type = map - default = { - ssh = { "tag" = "login" } - http = { "tag" = "proxy" } - https = { "tag" = "proxy" } - } -} - -resource "test_resource" "test" { - dynamic "foo" { - for_each = var.some_var - content { - bar = foo.value.tag - } - } -}`, - expected: []any{"login", "proxy", "proxy"}, - }, - { - name: "dynamic block with iterator", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = ["foo", "bar"] - iterator = some_iterator - content { - bar = some_iterator.value - } - } -}`, - expected: []any{"foo", "bar"}, - }, - { - name: "iterator and parent block with same name", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = ["foo", "bar"] - iterator = foo - content { - bar = foo.value - } - } -}`, - expected: []any{"foo", "bar"}, - }, - { - name: "for-each use null value", - src: `resource "test_resource" "test" { - dynamic "foo" { - for_each = null - content { - bar = foo.value - } - } -}`, - expected: []any{}, - }, - { - name: "no for-each attribute", - src: `resource "test_resource" "test" { - dynamic "foo" { - content { - bar = foo.value - } - } -}`, - expected: []any{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": tt.src, - }) - require.Len(t, modules, 1) - - resource := modules.GetResourcesByType("test_resource") - require.Len(t, resource, 1) - blocks := resource[0].GetBlocks("foo") - - var vals []any - for _, attr := range blocks { - vals = append(vals, attr.GetAttribute("bar").GetRawValue()) - } - - assert.ElementsMatch(t, tt.expected, vals) - }) - } -} - -func TestNestedDynamicBlock(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": `resource "test_resource" "test" { - dynamic "foo" { - for_each = ["1", "1"] - content { - dynamic "bar" { - for_each = [true, true] - content { - baz = foo.value - qux = bar.value - } - } - } - } -}`, - }) - require.Len(t, modules, 1) - - testResources := modules.GetResourcesByType("test_resource") - assert.Len(t, testResources, 1) - blocks := testResources[0].GetBlocks("foo") - assert.Len(t, blocks, 2) - - var nested []*terraform.Block - for _, block := range blocks { - nested = append(nested, block.GetBlocks("bar")...) - for _, b := range nested { - assert.Equal(t, "1", b.GetAttribute("baz").GetRawValue()) - assert.Equal(t, true, b.GetAttribute("qux").GetRawValue()) - } - } - assert.Len(t, nested, 4) -} - -func parse(t *testing.T, files map[string]string, opts ...Option) terraform.Modules { - fs := testutil.CreateFS(t, files) - opts = append(opts, OptionStopOnHCLError(true)) - parser := New(fs, "", opts...) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - return modules -} - -func TestModuleRefersToOutputOfAnotherModule(t *testing.T) { - files := map[string]string{ - "main.tf": ` -module "module2" { - source = "./modules/foo" -} - -module "module1" { - source = "./modules/bar" - test_var = module.module2.test_out -} -`, - "modules/foo/main.tf": ` -output "test_out" { - value = "test_value" -} -`, - "modules/bar/main.tf": ` -variable "test_var" {} - -resource "test_resource" "this" { - dynamic "dynamic_block" { - for_each = [var.test_var] - content { - some_attr = dynamic_block.value - } - } -} -`, - } - - modules := parse(t, files) - require.Len(t, modules, 3) - - resources := modules.GetResourcesByType("test_resource") - require.Len(t, resources, 1) - - attr, _ := resources[0].GetNestedAttribute("dynamic_block.some_attr") - require.NotNil(t, attr) - - assert.Equal(t, "test_value", attr.GetRawValue()) -} - -// TestNestedModulesOptions ensures parser options are carried to the nested -// submodule evaluators. -// The test will include an invalid module that will fail to download -// if it is attempted. -func TestNestedModulesOptions(t *testing.T) { - // reset the previous default logger - prevLog := slog.Default() - defer slog.SetDefault(prevLog) - var buf bytes.Buffer - slog.SetDefault(slog.New(log.NewHandler(&buf, nil))) - - // Folder structure - // ./ - // ├── main.tf - // └── modules - // ├── city - // │ └── main.tf - // ├── queens - // │ └── main.tf - // └── brooklyn - // └── main.tf - // - // Modules referenced - // main -> city ├─> brooklyn - // └─> queens - files := map[string]string{ - "main.tf": ` -module "city" { - source = "./modules/city" -} - -resource "city" "neighborhoods" { - names = module.city.neighborhoods -} -`, - "modules/city/main.tf": ` -module "brooklyn" { - source = "./brooklyn" -} - -module "queens" { - source = "./queens" -} - -output "neighborhoods" { - value = [module.brooklyn.name, module.queens.name] -} -`, - "modules/city/brooklyn/main.tf": ` -output "name" { - value = "Brooklyn" -} -`, - "modules/city/queens/main.tf": ` -output "name" { - value = "Queens" -} - -module "invalid" { - source = "https://example.invaliddomain" -} -`, - } - - // Using the OptionWithDownloads(false) option will prevent the invalid - // module from being downloaded. If the log exists "failed to download" - // then the submodule evaluator attempted to download, which was disallowed. - modules := parse(t, files, OptionWithDownloads(false)) - require.Len(t, modules, 4) - - resources := modules.GetResourcesByType("city") - require.Len(t, resources, 1) - - for _, res := range resources { - attr, _ := res.GetNestedAttribute("names") - require.NotNil(t, attr, res.FullName()) - assert.Equal(t, []string{"Brooklyn", "Queens"}, attr.GetRawValue()) - } - - require.NotContains(t, buf.String(), "failed to download") - - // Verify module parents are set correctly. - expectedParents := map[string]string{ - ".": "", - "modules/city": ".", - "modules/city/brooklyn": "modules/city", - "modules/city/queens": "modules/city", - } - - for _, mod := range modules { - expected, exists := expectedParents[mod.ModulePath()] - require.Truef(t, exists, "module %s does not exist in assertion", mod.ModulePath()) - if expected == "" { - require.Nil(t, mod.Parent()) - } else { - require.Equal(t, expected, mod.Parent().ModulePath(), "parent of module %q", mod.ModulePath()) - } - } -} - -// TestModuleParents sets up a nested module structure and verifies the -// parent-child relationships are correctly set. -func TestModuleParents(t *testing.T) { - // The setup is a list of continents, some countries, some cities, etc. - dirfs := os.DirFS("./testdata/nested") - parser := New(dirfs, "", - OptionStopOnHCLError(true), - OptionWithDownloads(false), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - // modules only have 'parent'. They do not have children, so create - // a structure that allows traversal from the root to the leafs. - modChildren := make(map[*terraform.Module][]*terraform.Module) - // Keep track of every module that exists - modSet := set.New[*terraform.Module]() - var root *terraform.Module - for _, mod := range modules { - mod := mod - modChildren[mod] = make([]*terraform.Module, 0) - modSet.Append(mod) - - if mod.Parent() == nil { - // Only 1 root should exist - require.Nil(t, root, "root module already set") - root = mod - } - modChildren[mod.Parent()] = append(modChildren[mod.Parent()], mod) - } - - type node struct { - prefix string - modulePath string - children []node - } - - // expectedTree is the full module tree structure. - expectedTree := node{ - modulePath: ".", - children: []node{ - { - modulePath: "north-america", - children: []node{ - { - modulePath: "north-america/united-states", - children: []node{ - {modulePath: "north-america/united-states/springfield", prefix: "illinois-"}, - {modulePath: "north-america/united-states/springfield", prefix: "idaho-"}, - {modulePath: "north-america/united-states/new-york", children: []node{ - {modulePath: "north-america/united-states/new-york/new-york-city"}, - }}, - }, - }, - { - modulePath: "north-america/canada", - children: []node{ - {modulePath: "north-america/canada/springfield", prefix: "ontario-"}, - }, - }, - }, - }, - }, - } - - var assertChild func(t *testing.T, n node, mod *terraform.Module) - assertChild = func(t *testing.T, n node, mod *terraform.Module) { - modSet.Remove(mod) - children := modChildren[mod] - - t.Run(n.modulePath, func(t *testing.T) { - if !assert.Equal(t, len(n.children), len(children), "modChildren count for %s", n.modulePath) { - return - } - for _, child := range children { - // Find the child module that we are expecting. - idx := slices.IndexFunc(n.children, func(node node) bool { - outputBlocks := child.GetBlocks().OfType("output") - outIdx := slices.IndexFunc(outputBlocks, func(outputBlock *terraform.Block) bool { - return outputBlock.Labels()[0] == "name" - }) - if outIdx == -1 { - return false - } - - output := outputBlocks[outIdx] - outVal := output.GetAttribute("value").Value() - if !outVal.Type().Equals(cty.String) { - return false - } - - modName := filepath.Base(node.modulePath) - if outVal.AsString() != node.prefix+modName { - return false - } - - return node.modulePath == child.ModulePath() - }) - if !assert.NotEqualf(t, -1, idx, "module prefix=%s path=%s not found in %s", n.prefix, child.ModulePath(), n.modulePath) { - continue - } - - assertChild(t, n.children[idx], child) - } - }) - - } - - assertChild(t, expectedTree, root) - // If any module was not asserted, the test will fail. This ensures the - // entire module tree is checked. - require.Equal(t, 0, modSet.Size(), "all modules asserted") -} - -func TestCyclicModules(t *testing.T) { - files := map[string]string{ - "main.tf": ` -module "module2" { - source = "./modules/foo" - test_var = passthru.handover.from_1 -} - -// Demonstrates need for evaluateSteps between submodule evaluations. -resource "passthru" "handover" { - from_1 = module.module1.test_out - from_2 = module.module2.test_out -} - -module "module1" { - source = "./modules/bar" - test_var = passthru.handover.from_2 -} -`, - "modules/foo/main.tf": ` -variable "test_var" {} - -resource "test_resource" "this" { - dynamic "dynamic_block" { - for_each = [var.test_var] - content { - some_attr = dynamic_block.value - } - } -} - -output "test_out" { - value = "test_value" -} -`, - "modules/bar/main.tf": ` -variable "test_var" {} - -resource "test_resource" "this" { - dynamic "dynamic_block" { - for_each = [var.test_var] - content { - some_attr = dynamic_block.value - } - } -} - -output "test_out" { - value = test_resource.this.dynamic_block.some_attr -} -`, - } - - modules := parse(t, files) - require.Len(t, modules, 3) - - resources := modules.GetResourcesByType("test_resource") - require.Len(t, resources, 2) - - for _, res := range resources { - attr, _ := res.GetNestedAttribute("dynamic_block.some_attr") - require.NotNil(t, attr, res.FullName()) - assert.Equal(t, "test_value", attr.GetRawValue()) - } -} - -func TestExtractSetValue(t *testing.T) { - files := map[string]string{ - "main.tf": ` -resource "test" "set-value" { - value = toset(["x", "y", "x"]) -} -`, - } - - resources := parse(t, files).GetResourcesByType("test") - require.Len(t, resources, 1) - attr := resources[0].GetAttribute("value") - require.NotNil(t, attr) - assert.Equal(t, []string{"x", "y"}, attr.GetRawValue()) -} - -func TestFunc_fileset(t *testing.T) { - files := map[string]string{ - "main.tf": ` -resource "test" "fileset-func" { - value = fileset(path.module, "**/*.py") -} -`, - "a.py": ``, - "path/b.py": ``, - } - - resources := parse(t, files).GetResourcesByType("test") - require.Len(t, resources, 1) - attr := resources[0].GetAttribute("value") - require.NotNil(t, attr) - assert.Equal(t, []string{"a.py", "path/b.py"}, attr.GetRawValue()) -} - -func TestExprWithMissingVar(t *testing.T) { - files := map[string]string{ - "main.tf": ` -variable "v" { - type = string -} - -resource "test" "values" { - s = "foo-${var.v}" - l1 = ["foo", var.v] - l2 = concat(["foo"], [var.v]) - d1 = {foo = var.v} - d2 = merge({"foo": "bar"}, {"baz": var.v}) -} -`, - } - - resources := parse(t, files).GetResourcesByType("test") - require.Len(t, resources, 1) - - s_attr := resources[0].GetAttribute("s") - require.NotNil(t, s_attr) - assert.Equal(t, "foo-", s_attr.GetRawValue()) - - for _, name := range []string{"l1", "l2", "d1", "d2"} { - attr := resources[0].GetAttribute(name) - require.NotNil(t, attr) - } -} - -func TestVarTypeShortcut(t *testing.T) { - files := map[string]string{ - "main.tf": ` -variable "magic_list" { - type = list - default = ["x", "y"] -} - -variable "magic_map" { - type = map - default = {a = 1, b = 2} -} - -resource "test" "values" { - l = var.magic_list - m = var.magic_map -} -`, - } - - resources := parse(t, files).GetResourcesByType("test") - require.Len(t, resources, 1) - - list_attr := resources[0].GetAttribute("l") - require.NotNil(t, list_attr) - assert.Equal(t, []string{"x", "y"}, list_attr.GetRawValue()) - - map_attr := resources[0].GetAttribute("m") - require.NotNil(t, map_attr) - assert.True(t, map_attr.Value().RawEquals(cty.MapVal(map[string]cty.Value{ - "a": cty.NumberIntVal(1), "b": cty.NumberIntVal(2), - }))) -} - -func Test_LoadLocalCachedModule(t *testing.T) { - fsys := os.DirFS(filepath.Join("testdata", "cached-modules")) - - parser := New( - fsys, "", - OptionStopOnHCLError(true), - OptionWithDownloads(false), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - assert.Len(t, modules, 2) - - buckets := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, buckets, 1) - - assert.Equal(t, "my-private-module/s3-bucket/aws/.terraform/modules/s3-bucket/main.tf", buckets[0].GetMetadata().Range().GetFilename()) - - bucketName := buckets[0].GetAttribute("bucket").Value().AsString() - assert.Equal(t, "my-s3-bucket", bucketName) -} - -func TestTFVarsFileDoesNotExist(t *testing.T) { - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{ - Data: []byte(``), - }, - } - - parser := New( - fsys, "", - OptionStopOnHCLError(true), - OptionWithDownloads(false), - OptionWithTFVarsPaths("main.tfvars"), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - _, _, err := parser.EvaluateAll(context.TODO()) - assert.ErrorContains(t, err, "file does not exist") -} - -func Test_OptionsWithTfVars(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": `resource "test" "this" { - foo = var.foo -} -variable "foo" {} -`}) - - parser := New(fs, "", OptionsWithTfVars( - map[string]cty.Value{ - "foo": cty.StringVal("bar"), - }, - )) - - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - assert.Len(t, modules, 1) - - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("test") - assert.Len(t, blocks, 1) - assert.Equal(t, "bar", blocks[0].GetAttribute("foo").Value().AsString()) -} - -func Test_AWSRegionNameDefined(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "code/test.tf": ` -data "aws_region" "current" {} - -data "aws_region" "other" { - name = "us-east-2" -} - -resource "something" "blah" { - r1 = data.aws_region.current.name - r2 = data.aws_region.other.name -} -`, - }) - - parser := New(fs, "", OptionStopOnHCLError(true)) - require.NoError(t, parser.ParseFS(context.TODO(), "code")) - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - require.Len(t, modules, 1) - rootModule := modules[0] - - blocks := rootModule.GetResourcesByType("something") - require.Len(t, blocks, 1) - block := blocks[0] - - r1 := block.GetAttribute("r1") - require.NotNil(t, r1) - assert.True(t, r1.IsResolvable()) - assert.Equal(t, "current-region", r1.Value().AsString()) - - r2 := block.GetAttribute("r2") - require.NotNil(t, r2) - assert.True(t, r2.IsResolvable()) - assert.Equal(t, "us-east-2", r2.Value().AsString()) -} - -func TestLogAboutMissingVariableValues(t *testing.T) { - var buf bytes.Buffer - slog.SetDefault(slog.New(log.NewHandler(&buf, nil))) - - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{ - Data: []byte(` -variable "foo" {} - -variable "bar" { - default = "bar" -} - -variable "baz" {} -`), - }, - "main.tfvars": &fstest.MapFile{ - Data: []byte(`baz = "baz"`), - }, - } - - parser := New( - fsys, "", - OptionStopOnHCLError(true), - OptionWithTFVarsPaths("main.tfvars"), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - _, err := parser.Load(context.TODO()) - require.NoError(t, err) - - assert.Contains(t, buf.String(), "Variable values was not found in the environment or variable files.") - assert.Contains(t, buf.String(), "variables=\"foo\"") -} - -func TestLoadChildModulesFromLocalCache(t *testing.T) { - var buf bytes.Buffer - slog.SetDefault(slog.New(log.NewHandler(&buf, &log.Options{Level: log.LevelDebug}))) - - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{Data: []byte(`module "level_1" { - source = "./modules/level_1" -}`)}, - "modules/level_1/main.tf": &fstest.MapFile{Data: []byte(`module "level_2" { - source = "../level_2" -}`)}, - "modules/level_2/main.tf": &fstest.MapFile{Data: []byte(`module "level_3" { - count = 2 - source = "../level_3" -}`)}, - "modules/level_3/main.tf": &fstest.MapFile{Data: []byte(`resource "foo" "bar" {}`)}, - ".terraform/modules/modules.json": &fstest.MapFile{Data: []byte(`{ - "Modules": [ - { "Key": "", "Source": "", "Dir": "." }, - { - "Key": "level_1", - "Source": "./modules/level_1", - "Dir": "modules/level_1" - }, - { - "Key": "level_1.level_2", - "Source": "../level_2", - "Dir": "modules/level_2" - }, - { - "Key": "level_1.level_2.level_3", - "Source": "../level_3", - "Dir": "modules/level_3" - } - ] -}`)}, - } - - parser := New( - fsys, "", - OptionStopOnHCLError(true), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - assert.Len(t, modules, 5) - - assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"./modules/level_1\"") - assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"../level_2\"") - assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"../level_3\"") - assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"../level_3\"") -} - -func TestLogParseErrors(t *testing.T) { - var buf bytes.Buffer - slog.SetDefault(slog.New(log.NewHandler(&buf, nil))) - - src := `resource "aws-s3-bucket" "name" { - bucket = < -}` - - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{ - Data: []byte(src), - }, - } - - parser := New(fsys, "") - err := parser.ParseFS(context.TODO(), ".") - require.NoError(t, err) - - assert.Contains(t, buf.String(), `cause=" bucket = <"`) -} - -func Test_PassingNullToChildModule_DoesNotEraseType(t *testing.T) { - tests := []struct { - name string - fsys fs.FS - }{ - { - name: "typed variable", - fsys: fstest.MapFS{ - "main.tf": &fstest.MapFile{Data: []byte(`module "test" { - source = "./modules/test" - test_var = null -}`)}, - "modules/test/main.tf": &fstest.MapFile{Data: []byte(`variable "test_var" { - type = number -} - -resource "foo" "this" { - bar = var.test_var != null ? 1 : 2 -}`)}, - }, - }, - { - name: "typed variable with default", - fsys: fstest.MapFS{ - "main.tf": &fstest.MapFile{Data: []byte(`module "test" { - source = "./modules/test" - test_var = null -}`)}, - "modules/test/main.tf": &fstest.MapFile{Data: []byte(`variable "test_var" { - type = number - default = null -} - -resource "foo" "this" { - bar = var.test_var != null ? 1 : 2 -}`)}, - }, - }, - { - name: "empty variable", - fsys: fstest.MapFS{ - "main.tf": &fstest.MapFile{Data: []byte(`module "test" { - source = "./modules/test" - test_var = null -}`)}, - "modules/test/main.tf": &fstest.MapFile{Data: []byte(`variable "test_var" {} - -resource "foo" "this" { - bar = var.test_var != null ? 1 : 2 -}`)}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := New( - tt.fsys, "", - OptionStopOnHCLError(true), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - _, err := parser.Load(context.TODO()) - require.NoError(t, err) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - res := modules.GetResourcesByType("foo")[0] - attr := res.GetAttribute("bar") - val, _ := attr.Value().AsBigFloat().Int64() - assert.Equal(t, int64(2), val) - }) - } -} - -func TestAttrRefToNullVariable(t *testing.T) { - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{Data: []byte(`variable "name" { - type = string - default = null -} - -resource "aws_s3_bucket" "example" { - bucket = var.name -}`)}, - } - - parser := New(fsys, "", OptionStopOnHCLError(true)) - - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - _, err := parser.Load(context.TODO()) - require.NoError(t, err) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - val := modules.GetResourcesByType("aws_s3_bucket")[0].GetAttribute("bucket").GetRawValue() - assert.Nil(t, val) -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/cache.go b/pkg/iac/scanners/terraform/parser/resolvers/cache.go deleted file mode 100644 index 5e7641af3333..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/cache.go +++ /dev/null @@ -1,75 +0,0 @@ -package resolvers - -import ( - "context" - "crypto/md5" // #nosec - "encoding/hex" - "errors" - "io/fs" - "os" - "path/filepath" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type cacheResolver struct{} - -var Cache = &cacheResolver{} - -const tempDirName = ".aqua" - -var defaultCacheDir = filepath.Join(os.TempDir(), tempDirName, "cache") - -func locateCacheFS(cacheDir string) (fs.FS, error) { - dir, err := locateCacheDir(cacheDir) - if err != nil { - return nil, err - } - return os.DirFS(dir), nil -} - -func locateCacheDir(cacheDir string) (string, error) { - if cacheDir == "" { - cacheDir = defaultCacheDir - } - - if err := os.MkdirAll(cacheDir, 0o750); err != nil { - return "", err - } - if !isWritable(cacheDir) { - return "", errors.New("cache directory is not writable") - } - return cacheDir, nil -} - -func (r *cacheResolver) Resolve(_ context.Context, _ fs.FS, opt Options) (filesystem fs.FS, prefix, downloadPath string, applies bool, err error) { - if opt.SkipCache { - opt.Logger.Debug("Module caching is disabled") - return nil, "", "", false, nil - } - cacheFS, err := locateCacheFS(opt.CacheDir) - if err != nil { - opt.Logger.Debug("No cache filesystem is available on this machine.", log.Err(err)) - return nil, "", "", false, nil - } - - src, subdir := splitPackageSubdirRaw(opt.Source) - key := cacheKey(src, opt.Version) - - opt.Logger.Debug("Trying to resolve module via cache", log.String("key", key)) - if info, err := fs.Stat(cacheFS, filepath.ToSlash(key)); err == nil && info.IsDir() { - opt.Logger.Debug("Module resolved from cache", log.String("key", key)) - cacheDir, err := locateCacheDir(opt.CacheDir) - if err != nil { - return nil, "", "", true, err - } - - return os.DirFS(filepath.Join(cacheDir, key)), opt.OriginalSource, subdir, true, nil - } - return nil, "", "", false, nil -} - -func cacheKey(source, version string) string { - hash := md5.Sum([]byte(source + ":" + version)) // #nosec - return hex.EncodeToString(hash[:]) -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/cache_integration_test.go b/pkg/iac/scanners/terraform/parser/resolvers/cache_integration_test.go deleted file mode 100644 index 6bfe812519bd..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/cache_integration_test.go +++ /dev/null @@ -1,172 +0,0 @@ -//go:build unix - -package resolvers_test - -import ( - "context" - "crypto/tls" - "io/fs" - "net/http" - "net/http/httptest" - "path" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/gittest" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers" - "github.com/aquasecurity/trivy/pkg/log" -) - -type moduleResolver interface { - Resolve(context.Context, fs.FS, resolvers.Options) (fs.FS, string, string, bool, error) -} - -func testOptions(t *testing.T, source string) resolvers.Options { - return resolvers.Options{ - Source: source, - OriginalSource: source, - Version: "", - OriginalVersion: "", - AllowDownloads: true, - CacheDir: t.TempDir(), - Logger: log.WithPrefix("test"), - } -} - -func newRegistry(repoURL string) *httptest.Server { - mux := http.NewServeMux() - mux.HandleFunc("/v1/modules/terraform-aws-modules/s3-bucket/aws/download", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("X-Terraform-Get", repoURL) - w.WriteHeader(http.StatusNoContent) - }) - - return httptest.NewTLSServer(mux) -} - -func buildGitSource(repoURL string) string { return "git::" + repoURL } - -func TestResolveModuleFromCache(t *testing.T) { - - repo := "terraform-aws-s3-bucket" - gs := gittest.NewServer(t, repo, "testdata/terraform-aws-s3-bucket") - defer gs.Close() - - repoURL := gs.URL + "/" + repo + ".git" - - registry := newRegistry(buildGitSource(repoURL)) - defer registry.Close() - - registryAddress := strings.TrimPrefix(registry.URL, "https://") - - tests := []struct { - name string - opts resolvers.Options - firstResolver moduleResolver - expectedSubdir string - expectedString string - }{ - { - name: "registry", - opts: resolvers.Options{ - Source: registryAddress + "/terraform-aws-modules/s3-bucket/aws", - Client: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - }, - }, - firstResolver: resolvers.Registry, - expectedSubdir: ".", - expectedString: "# AWS S3 bucket Terraform module", - }, - { - name: "registry with subdir", - opts: resolvers.Options{ - Source: registryAddress + "/terraform-aws-modules/s3-bucket/aws//modules/object", - Client: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - }, - }, - firstResolver: resolvers.Registry, - expectedSubdir: "modules/object", - expectedString: "# S3 bucket object", - }, - { - name: "remote", - opts: resolvers.Options{ - Source: buildGitSource(repoURL), - }, - firstResolver: resolvers.Remote, - expectedSubdir: ".", - expectedString: "# AWS S3 bucket Terraform module", - }, - { - name: "remote with subdir", - opts: resolvers.Options{ - Source: buildGitSource(repoURL) + "//modules/object", - }, - firstResolver: resolvers.Remote, - expectedSubdir: "modules/object", - expectedString: "# S3 bucket object", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - tt.opts.OriginalSource = tt.opts.Source - tt.opts.AllowDownloads = true - tt.opts.CacheDir = t.TempDir() - tt.opts.Logger = log.WithPrefix("test") - - fsys, _, dir, _, err := tt.firstResolver.Resolve(context.Background(), nil, tt.opts) - require.NoError(t, err) - assert.Equal(t, tt.expectedSubdir, dir) - - b, err := fs.ReadFile(fsys, path.Join(dir, "README.md")) - require.NoError(t, err) - assert.Equal(t, tt.expectedString, string(b)) - - _, _, dir, _, err = resolvers.Cache.Resolve(context.Background(), fsys, tt.opts) - require.NoError(t, err) - assert.Equal(t, tt.expectedSubdir, dir) - - b, err = fs.ReadFile(fsys, path.Join(dir, "README.md")) - require.NoError(t, err) - assert.Equal(t, tt.expectedString, string(b)) - }) - } -} - -func TestResolveModuleFromCacheWithDifferentSubdir(t *testing.T) { - repo := "terraform-aws-s3-bucket" - gs := gittest.NewServer(t, repo, "testdata/terraform-aws-s3-bucket") - defer gs.Close() - - repoURL := gs.URL + "/" + repo + ".git" - - fsys, _, dir, _, err := resolvers.Remote.Resolve( - context.Background(), nil, - testOptions(t, "git::"+repoURL+"//modules/object"), - ) - require.NoError(t, err) - - b, err := fs.ReadFile(fsys, path.Join(dir, "README.md")) - require.NoError(t, err) - assert.Equal(t, "# S3 bucket object", string(b)) - - fsys, _, dir, _, err = resolvers.Remote.Resolve( - context.Background(), nil, - testOptions(t, "git::"+repoURL+"//modules/notification"), - ) - require.NoError(t, err) - - b, err = fs.ReadFile(fsys, path.Join(dir, "README.md")) - require.NoError(t, err) - assert.Equal(t, "# S3 bucket notification", string(b)) -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/local.go b/pkg/iac/scanners/terraform/parser/resolvers/local.go deleted file mode 100644 index a11a294edcfb..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/local.go +++ /dev/null @@ -1,33 +0,0 @@ -package resolvers - -import ( - "context" - "io/fs" - "path" - "path/filepath" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type localResolver struct{} - -var Local = &localResolver{} - -func (r *localResolver) Resolve(_ context.Context, target fs.FS, opt Options) (filesystem fs.FS, prefix, downloadPath string, applies bool, err error) { - if !opt.hasPrefix(".", "..") { - return nil, "", "", false, nil - } - joined := path.Clean(path.Join(opt.ModulePath, opt.Source)) - if _, err := fs.Stat(target, filepath.ToSlash(joined)); err == nil { - opt.Logger.Debug("Module resolved locally", - log.String("name", opt.Name), log.FilePath(joined), - ) - return target, "", joined, true, nil - } - - clean := path.Clean(opt.Source) - opt.Logger.Debug("Module resolved locally", - log.String("name", opt.Name), log.FilePath(clean), - ) - return target, "", clean, true, nil -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/options.go b/pkg/iac/scanners/terraform/parser/resolvers/options.go deleted file mode 100644 index 937f89709dc3..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/options.go +++ /dev/null @@ -1,27 +0,0 @@ -package resolvers - -import ( - "net/http" - "strings" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Options struct { - Source, OriginalSource, Version, OriginalVersion, WorkingDir, Name, ModulePath string - Logger *log.Logger - AllowDownloads bool - SkipCache bool - RelativePath string - CacheDir string - Client *http.Client -} - -func (o *Options) hasPrefix(prefixes ...string) bool { - for _, prefix := range prefixes { - if strings.HasPrefix(o.Source, prefix) { - return true - } - } - return false -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/registry.go b/pkg/iac/scanners/terraform/parser/resolvers/registry.go deleted file mode 100644 index 3b37134b307a..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/registry.go +++ /dev/null @@ -1,219 +0,0 @@ -package resolvers - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io/fs" - "net/http" - "os" - "sort" - "strings" - "time" - - "golang.org/x/net/idna" - - "github.com/aquasecurity/go-version/pkg/version" - "github.com/aquasecurity/trivy/pkg/log" -) - -type registryResolver struct { - client *http.Client -} - -var Registry = ®istryResolver{ - client: &http.Client{ - // give it a maximum 5 seconds to resolve the module - Timeout: time.Second * 5, - }, -} - -type moduleVersions struct { - Modules []struct { - Versions []struct { - Version string `json:"version"` - } `json:"versions"` - } `json:"modules"` -} - -const registryHostname = "registry.terraform.io" - -// nolint -func (r *registryResolver) Resolve(ctx context.Context, target fs.FS, opt Options) (filesystem fs.FS, prefix string, downloadPath string, applies bool, err error) { - - client := r.client - if opt.Client != nil { - client = opt.Client - } - - if !opt.AllowDownloads { - return - } - - inputVersion := opt.Version - source, _ := splitPackageSubdirRaw(opt.OriginalSource) - parts := strings.Split(source, "/") - if len(parts) < 3 || len(parts) > 4 { - return - } - - hostname := registryHostname - var token string - if len(parts) == 4 { - hostname = parts[0] - parts = parts[1:] - - token, err = getPrivateRegistryTokenFromEnvVars(hostname) - if err == nil { - opt.Logger.Debug("Found a token for the registry", log.String("hostname", hostname)) - } else { - opt.Logger.Error( - "Failed to find a token for the registry", - log.String("hostname", hostname), log.Err(err)) - } - } - - moduleName := strings.Join(parts, "/") - - if opt.Version != "" { - versionUrl := fmt.Sprintf("https://%s/v1/modules/%s/versions", hostname, moduleName) - opt.Logger.Debug("Requesting module versions from registry using", - log.String("url", versionUrl)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, versionUrl, nil) - if err != nil { - return nil, "", "", true, err - } - if token != "" { - req.Header.Set("Authorization", "Bearer "+token) - } - resp, err := client.Do(req) - if err != nil { - return nil, "", "", true, err - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode != http.StatusOK { - return nil, "", "", true, fmt.Errorf("unexpected status code for versions endpoint: %d", resp.StatusCode) - } - var availableVersions moduleVersions - if err := json.NewDecoder(resp.Body).Decode(&availableVersions); err != nil { - return nil, "", "", true, err - } - - opt.Version, err = resolveVersion(inputVersion, availableVersions) - if err != nil { - return nil, "", "", true, err - } - opt.Logger.Debug("Found module version", - log.String("version", opt.Version), log.String("constraint", inputVersion)) - } - - var url string - if opt.Version == "" { - url = fmt.Sprintf("https://%s/v1/modules/%s/download", hostname, moduleName) - } else { - url = fmt.Sprintf("https://%s/v1/modules/%s/%s/download", hostname, moduleName, opt.Version) - } - - opt.Logger.Debug("Requesting module source from registry", log.String("url", url)) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, "", "", true, err - } - if token != "" { - req.Header.Set("Authorization", "Bearer "+token) - } - if opt.Version != "" { - req.Header.Set("X-Terraform-Version", opt.Version) - } - - resp, err := client.Do(req) - if err != nil { - return nil, "", "", true, err - } - defer func() { _ = resp.Body.Close() }() - - // OpenTofu may return 200 with body - switch resp.StatusCode { - case http.StatusOK: - // https://opentofu.org/docs/internals/module-registry-protocol/#sample-response-1 - var downloadResponse struct { - Location string `json:"location"` - } - if err := json.NewDecoder(resp.Body).Decode(&downloadResponse); err != nil { - return nil, "", "", true, fmt.Errorf("failed to decode download response: %w", err) - } - - opt.Source = downloadResponse.Location - case http.StatusNoContent: - opt.Source = resp.Header.Get("X-Terraform-Get") - default: - return nil, "", "", true, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - if opt.Source == "" { - return nil, "", "", true, fmt.Errorf("no source was found for the registry at %s", hostname) - } - - opt.Logger.Debug("Module resolved via registry to new source", - log.String("source", opt.Source), log.String("name", moduleName)) - - filesystem, prefix, downloadPath, _, err = Remote.Resolve(ctx, target, opt) - if err != nil { - return nil, "", "", true, err - } - - return filesystem, prefix, downloadPath, true, nil -} - -func getPrivateRegistryTokenFromEnvVars(hostname string) (string, error) { - token := "" - asciiHostname, err := idna.ToASCII(hostname) - if err != nil { - return "", fmt.Errorf("could not convert hostname %s to a punycode encoded ASCII string so cannot find token for this registry", hostname) - } - - envVar := fmt.Sprintf("TF_TOKEN_%s", strings.ReplaceAll(asciiHostname, ".", "_")) - token = os.Getenv(envVar) - - // Dashes in the hostname can optionally be converted to double underscores - if token == "" { - envVar = strings.ReplaceAll(envVar, "-", "__") - token = os.Getenv(envVar) - } - - if token == "" { - return "", fmt.Errorf("no token was found for the registry at %s", hostname) - } - return token, nil -} - -func resolveVersion(input string, versions moduleVersions) (string, error) { - if len(versions.Modules) != 1 { - return "", fmt.Errorf("1 module expected, found %d", len(versions.Modules)) - } - if len(versions.Modules[0].Versions) == 0 { - return "", errors.New("no available versions for module") - } - - constraints, err := version.NewConstraints(input) - if err != nil { - return "", err - } - var realVersions version.Collection - for _, rawVersion := range versions.Modules[0].Versions { - realVersion, err := version.Parse(rawVersion.Version) - if err != nil { - continue - } - realVersions = append(realVersions, realVersion) - } - sort.Sort(sort.Reverse(realVersions)) - for _, realVersion := range realVersions { - if constraints.Check(realVersion) { - return realVersion.String(), nil - } - } - return "", fmt.Errorf("no available versions for module constraint '%s'", input) -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/registry_integration_test.go b/pkg/iac/scanners/terraform/parser/resolvers/registry_integration_test.go deleted file mode 100644 index b8be4b10e0f2..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/registry_integration_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package resolvers_test - -import ( - "context" - "io/fs" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers" - "github.com/aquasecurity/trivy/pkg/log" -) - -func TestResolveModuleFromOpenTofuRegistry(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fsys, _, path, _, err := resolvers.Registry.Resolve(context.Background(), nil, resolvers.Options{ - Source: "registry.opentofu.org/terraform-aws-modules/s3-bucket/aws", - OriginalSource: "registry.opentofu.org/terraform-aws-modules/s3-bucket/aws", - RelativePath: "test", - Name: "bucket", - Version: "4.1.2", - OriginalVersion: "4.1.2", - AllowDownloads: true, - SkipCache: true, - Logger: log.WithPrefix("test"), - }) - require.NoError(t, err) - - _, err = fs.Stat(fsys, filepath.Join(path, "main.tf")) - require.NoError(t, err) -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/registry_test.go b/pkg/iac/scanners/terraform/parser/resolvers/registry_test.go deleted file mode 100644 index d0e679e3dc03..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/registry_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package resolvers - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_getPrivateRegistryTokenFromEnvVars_ErrorsWithNoEnvVarSet(t *testing.T) { - token, err := getPrivateRegistryTokenFromEnvVars("registry.example.com") - assert.Equal(t, "", token) - assert.Equal(t, "no token was found for the registry at registry.example.com", err.Error()) -} - -func Test_getPrivateRegistryTokenFromEnvVars_ConvertsSiteNameToEnvVar(t *testing.T) { - tests := []struct { - name string - siteName string - tokenName string - }{ - { - name: "returns string when simple env var set", - siteName: "registry.example.com", - tokenName: "TF_TOKEN_registry_example_com", - }, - { - name: "allows dashes in hostname to be dashes", - siteName: "my-registry.example.com", - tokenName: "TF_TOKEN_my-registry_example_com", - }, - { - name: "allows dashes in hostname to be double underscores", - siteName: "my-registry.example.com", - tokenName: "TF_TOKEN_my__registry_example_com", - }, - { - name: "handles utf8 to punycode correctly", - siteName: "例えば.com", - tokenName: "TF_TOKEN_xn--r8j3dr99h_com", - }, - { - name: "handles punycode with dash to underscore conversion", - siteName: "café.fr", - tokenName: "TF_TOKEN_xn____caf__dma_fr", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Setenv(tt.tokenName, "abcd") - token, err := getPrivateRegistryTokenFromEnvVars(tt.siteName) - assert.Equal(t, "abcd", token) - require.NoError(t, err) - }) - } -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/remote.go b/pkg/iac/scanners/terraform/parser/resolvers/remote.go deleted file mode 100644 index f23483b413bd..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/remote.go +++ /dev/null @@ -1,106 +0,0 @@ -package resolvers - -import ( - "context" - "fmt" - "io/fs" - "os" - "path/filepath" - "sync/atomic" - - "github.com/hashicorp/go-getter" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type remoteResolver struct { - count int32 -} - -var Remote = &remoteResolver{ - count: 0, -} - -func (r *remoteResolver) incrementCount(o Options) { - - atomic.CompareAndSwapInt32(&r.count, r.count, r.count+1) - o.Logger.Debug("Incrementing the download counter", log.Int("count", int(r.count))) -} - -func (r *remoteResolver) GetDownloadCount() int { - return int(atomic.LoadInt32(&r.count)) -} - -func (r *remoteResolver) Resolve(ctx context.Context, _ fs.FS, opt Options) (filesystem fs.FS, prefix, downloadPath string, applies bool, err error) { - if !opt.hasPrefix("github.com/", "bitbucket.org/", "s3:", "git@", "git:", "hg:", "https:", "gcs:") { - return nil, "", "", false, nil - } - - if !opt.AllowDownloads { - return nil, "", "", false, nil - } - - origSrc, subdir := splitPackageSubdirRaw(opt.OriginalSource) - key := cacheKey(origSrc, opt.OriginalVersion) - opt.Logger.Debug("Caching module", log.String("key", key)) - - baseCacheDir, err := locateCacheDir(opt.CacheDir) - if err != nil { - return nil, "", "", true, fmt.Errorf("failed to locate cache directory: %w", err) - } - - cacheDir := filepath.Join(baseCacheDir, key) - - src, _ := splitPackageSubdirRaw(opt.Source) - - opt.Source = src - if err := r.download(ctx, opt, cacheDir); err != nil { - return nil, "", "", true, err - } - - r.incrementCount(opt) - opt.Logger.Debug("Successfully resolve module via remote download", - log.String("name", opt.Name), - log.String("source", opt.OriginalSource), - ) - return os.DirFS(cacheDir), opt.OriginalSource, subdir, true, nil -} - -func (r *remoteResolver) download(ctx context.Context, opt Options, dst string) error { - _ = os.RemoveAll(dst) - if err := os.MkdirAll(filepath.Dir(dst), 0o750); err != nil { - return err - } - - // Overwrite the file getter so that a file will be copied - getter.Getters["file"] = &getter.FileGetter{Copy: true} - - opt.Logger.Debug("Downloading module", log.String("source", opt.Source)) - - // Build the client - client := &getter.Client{ - Ctx: ctx, - Src: opt.Source, - Dst: dst, - Pwd: opt.WorkingDir, - Getters: getter.Getters, - Mode: getter.ClientModeAny, - } - - terminalPrompt := os.Getenv("GIT_TERMINAL_PROMPT") - if err := os.Setenv("GIT_TERMINAL_PROMPT", "0"); err != nil { - opt.Logger.Error("Failed to set env", log.String("name", "GIT_TERMINAL_PROMPT"), log.Err(err)) - } else { - defer os.Setenv("GIT_TERMINAL_PROMPT", terminalPrompt) - } - - if err := client.Get(); err != nil { - return fmt.Errorf("failed to download: %w", err) - } - - return nil -} - -func (r *remoteResolver) GetSourcePrefix(source string) string { - return source -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/source.go b/pkg/iac/scanners/terraform/parser/resolvers/source.go deleted file mode 100644 index ee5662d6c7d0..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/source.go +++ /dev/null @@ -1,37 +0,0 @@ -package resolvers - -import "strings" - -func splitPackageSubdirRaw(src string) (string, string) { - stop := len(src) - if idx := strings.Index(src, "?"); idx > -1 { - stop = idx - } - - // Calculate an offset to avoid accidentally marking the scheme - // as the dir. - var offset int - if idx := strings.Index(src[:stop], "://"); idx > -1 { - offset = idx + 3 - } - - // First see if we even have an explicit subdir - idx := strings.Index(src[offset:stop], "//") - if idx == -1 { - return src, "." - } - - idx += offset - subdir := src[idx+2:] - src = src[:idx] - - // Next, check if we have query parameters and push them onto the - // URL. - if idx = strings.Index(subdir, "?"); idx > -1 { - query := subdir[idx:] - subdir = subdir[:idx] - src += query - } - - return src, subdir -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/source_test.go b/pkg/iac/scanners/terraform/parser/resolvers/source_test.go deleted file mode 100644 index 46df7db700dc..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/source_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package resolvers - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSplitPackageSubdirRaw(t *testing.T) { - - tests := []struct { - name string - source string - expectedPkg string - expectedSubdir string - }{ - { - name: "address with scheme and query string", - source: "git::https://github.com/aquasecurity/terraform-modules.git//modules/ecs-service?ref=v0.1.0", - expectedPkg: "git::https://github.com/aquasecurity/terraform-modules.git?ref=v0.1.0", - expectedSubdir: "modules/ecs-service", - }, - { - name: "address with scheme", - source: "git::https://github.com/aquasecurity/terraform-modules.git//modules/ecs-service", - expectedPkg: "git::https://github.com/aquasecurity/terraform-modules.git", - expectedSubdir: "modules/ecs-service", - }, - { - name: "registry address", - source: "hashicorp/consul/aws//modules/consul-cluster", - expectedPkg: "hashicorp/consul/aws", - expectedSubdir: "modules/consul-cluster", - }, - { - name: "without subdir", - source: `hashicorp/consul/aws`, - expectedPkg: `hashicorp/consul/aws`, - expectedSubdir: ".", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pkgAddr, subdir := splitPackageSubdirRaw(test.source) - assert.Equal(t, test.expectedPkg, pkgAddr) - assert.Equal(t, test.expectedSubdir, subdir) - }) - } -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/README.md b/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/README.md deleted file mode 100644 index 26e6186c9cbe..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/README.md +++ /dev/null @@ -1 +0,0 @@ -# AWS S3 bucket Terraform module \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/modules/notification/README.md b/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/modules/notification/README.md deleted file mode 100644 index 9f2f884a9586..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/modules/notification/README.md +++ /dev/null @@ -1 +0,0 @@ -# S3 bucket notification \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/modules/object/README.md b/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/modules/object/README.md deleted file mode 100644 index a016e2bb23cb..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/testdata/terraform-aws-s3-bucket/modules/object/README.md +++ /dev/null @@ -1 +0,0 @@ -# S3 bucket object \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/resolvers/writable.go b/pkg/iac/scanners/terraform/parser/resolvers/writable.go deleted file mode 100644 index 84f471f779c2..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/writable.go +++ /dev/null @@ -1,36 +0,0 @@ -//go:build !windows -// +build !windows - -package resolvers - -import ( - "os" - "syscall" -) - -func isWritable(path string) bool { - info, err := os.Stat(path) - if err != nil { - return false - } - - if !info.IsDir() { - return false - } - - // Check if the user bit is enabled in file permission - if info.Mode().Perm()&(1<<(uint(7))) == 0 { - return false - } - - var stat syscall.Stat_t - if err = syscall.Stat(path, &stat); err != nil { - return false - } - - if uint32(os.Geteuid()) != stat.Uid { - return false - } - - return true -} diff --git a/pkg/iac/scanners/terraform/parser/resolvers/writable_windows.go b/pkg/iac/scanners/terraform/parser/resolvers/writable_windows.go deleted file mode 100644 index 69cb3c7169b1..000000000000 --- a/pkg/iac/scanners/terraform/parser/resolvers/writable_windows.go +++ /dev/null @@ -1,24 +0,0 @@ -package resolvers - -import ( - "os" -) - -func isWritable(path string) bool { - - info, err := os.Stat(path) - if err != nil { - return false - } - - if !info.IsDir() { - return false - } - - // Check if the user bit is enabled in file permission - if info.Mode().Perm()&(1<<(uint(7))) == 0 { - return false - } - - return true -} diff --git a/pkg/iac/scanners/terraform/parser/sort.go b/pkg/iac/scanners/terraform/parser/sort.go deleted file mode 100644 index 28fc79b1990c..000000000000 --- a/pkg/iac/scanners/terraform/parser/sort.go +++ /dev/null @@ -1,58 +0,0 @@ -package parser - -import ( - "sort" - - "github.com/aquasecurity/trivy/pkg/iac/terraform" -) - -func sortBlocksByHierarchy(blocks terraform.Blocks) { - c := &counter{ - cache: make(map[string]int), - } - sort.Slice(blocks, func(i, j int) bool { - a := blocks[i] - b := blocks[j] - iDepth, jDepth := c.countBlockRecursion(a, blocks, 0), c.countBlockRecursion(b, blocks, 0) - switch { - case iDepth < jDepth: - return true - case iDepth > jDepth: - return false - default: - return blocks[i].FullName() < blocks[j].FullName() - } - }) -} - -type counter struct { - cache map[string]int -} - -func (c *counter) countBlockRecursion(block *terraform.Block, blocks terraform.Blocks, count int) int { - metadata := block.GetMetadata() - if cached, ok := c.cache[metadata.Reference()]; ok { - return cached - } - var maxCount int - var hasRecursion bool - for _, attrName := range []string{"for_each", "count"} { - if attr := block.GetAttribute(attrName); attr.IsNotNil() { - hasRecursion = true - for _, other := range blocks { - if attr.ReferencesBlock(other) { - depth := c.countBlockRecursion(other, blocks, count) - if depth > maxCount { - maxCount = depth - } - } - } - } - } - if hasRecursion { - maxCount++ - } - result := maxCount + count - c.cache[metadata.Reference()] = result - return result -} diff --git a/pkg/iac/scanners/terraform/parser/testdata/cached-modules/.terraform/modules/modules.json b/pkg/iac/scanners/terraform/parser/testdata/cached-modules/.terraform/modules/modules.json deleted file mode 100644 index 76be610c1339..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/cached-modules/.terraform/modules/modules.json +++ /dev/null @@ -1 +0,0 @@ -{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"s3-bucket","Source":"registry.myregistry.org/my-private-module/s3-bucket/aws","Version":"1.0.0","Dir":".terraform/modules/s3-bucket"}]} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/cached-modules/.terraform/modules/s3-bucket/main.tf b/pkg/iac/scanners/terraform/parser/testdata/cached-modules/.terraform/modules/s3-bucket/main.tf deleted file mode 100644 index 68506b9930b8..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/cached-modules/.terraform/modules/s3-bucket/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "bucket" { - type = string -} - -resource "aws_s3_bucket" "this" { - bucket = var.bucket -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/cached-modules/main.tf b/pkg/iac/scanners/terraform/parser/testdata/cached-modules/main.tf deleted file mode 100644 index 231058b26e01..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/cached-modules/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -module "s3-bucket" { - source = "my-private-module/s3-bucket/aws" - version = "1.0.0" - bucket = "my-s3-bucket" -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/main.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/main.tf deleted file mode 100644 index 1012ab57bfd7..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -module "north-america" { - source = "./north-america" -} - -output "all" { - value = [ - module.north-america, - ] -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/canada/canada.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/canada/canada.tf deleted file mode 100644 index bb27996a0187..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/canada/canada.tf +++ /dev/null @@ -1,17 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}canada" -} - -module "ontario-springfield" { - source = "./springfield" - prefix = "ontario-" -} - -output "ontario-springfield" { - value = module.ontario-springfield.name -} diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/canada/springfield/springfield.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/canada/springfield/springfield.tf deleted file mode 100644 index e8768e67e2a8..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/canada/springfield/springfield.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}springfield" -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/north-america.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/north-america.tf deleted file mode 100644 index 26b53f5f9d8c..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/north-america.tf +++ /dev/null @@ -1,22 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}north-america" -} - -module "canada" { - source = "./canada" - prefix = "" -} - -module "united-states" { - source = "./united-states" - prefix = "" -} - -output "united-states" { - value = module.united-states.name -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/new-york/new-york-city/new-york-city.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/new-york/new-york-city/new-york-city.tf deleted file mode 100644 index e0a024179706..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/new-york/new-york-city/new-york-city.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}new-york-city" -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/new-york/new-york.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/new-york/new-york.tf deleted file mode 100644 index fc87248b5756..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/new-york/new-york.tf +++ /dev/null @@ -1,17 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}new-york" -} - -module "new-york-city" { - source = "./new-york-city" - prefix = "" -} - -output "new-york-city" { - value = module.new-york-city.name -} diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/springfield/springfield.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/springfield/springfield.tf deleted file mode 100644 index e8768e67e2a8..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/springfield/springfield.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}springfield" -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/united-states.tf b/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/united-states.tf deleted file mode 100644 index c6fff86f38bb..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/nested/north-america/united-states/united-states.tf +++ /dev/null @@ -1,38 +0,0 @@ -variable "prefix" { - type = string - default = "" -} - -output "name" { - value = "${var.prefix}united-states" -} - -// Same module twice, with different variables -module "illinois-springfield" { - source = "./springfield" - prefix = "illinois-" -} - -output "illinois-springfield" { - value = module.illinois-springfield.name -} - -module "idaho-springfield" { - source = "./springfield" - prefix = "idaho-" -} - -output "idaho-springfield" { - value = module.idaho-springfield.name -} - -module "new-york" { - source = "./new-york" - prefix = "" -} - -output "new-york" { - value = module.new-york.name -} - - diff --git a/pkg/iac/scanners/terraform/parser/testdata/tfvars/terraform.tfvars b/pkg/iac/scanners/terraform/parser/testdata/tfvars/terraform.tfvars deleted file mode 100644 index 23fee69e2bb1..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/tfvars/terraform.tfvars +++ /dev/null @@ -1 +0,0 @@ -instance_type = "t2.large" \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/parser/testdata/tfvars/terraform.tfvars.json b/pkg/iac/scanners/terraform/parser/testdata/tfvars/terraform.tfvars.json deleted file mode 100644 index bde0e75763b1..000000000000 --- a/pkg/iac/scanners/terraform/parser/testdata/tfvars/terraform.tfvars.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "variable": { - "foo": { - "default": "bar" - }, - "baz": "qux" - }, - "foo2": true, - "foo3": 3 -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraform/performance_test.go b/pkg/iac/scanners/terraform/performance_test.go deleted file mode 100644 index 7ef574e27858..000000000000 --- a/pkg/iac/scanners/terraform/performance_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package terraform - -import ( - "context" - "fmt" - "io/fs" - "testing" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rules" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/executor" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser" -) - -func BenchmarkCalculate(b *testing.B) { - - f, err := createBadBlocks() - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - p := parser.New(f, "", parser.OptionStopOnHCLError(true)) - if err := p.ParseFS(context.TODO(), "project"); err != nil { - b.Fatal(err) - } - modules, _, err := p.EvaluateAll(context.TODO()) - if err != nil { - b.Fatal(err) - } - executor.New().Execute(context.TODO(), modules, "project") - } -} - -func createBadBlocks() (fs.FS, error) { - - files := make(map[string]string) - - files["/project/main.tf"] = ` -module "something" { - source = "../modules/problem" -} -` - - for _, rule := range rules.GetRegistered() { - if rule.GetRule().Terraform == nil { - continue - } - for i, bad := range rule.GetRule().Terraform.BadExamples { - filename := fmt.Sprintf("/modules/problem/%s-%d.tf", rule.GetRule().LongID(), i) - files[filename] = bad - } - } - - f := testutil.CreateFS(&testing.T{}, files) - return f, nil -} diff --git a/pkg/iac/scanners/terraform/scanner.go b/pkg/iac/scanners/terraform/scanner.go deleted file mode 100644 index 21735bff1a52..000000000000 --- a/pkg/iac/scanners/terraform/scanner.go +++ /dev/null @@ -1,243 +0,0 @@ -package terraform - -import ( - "context" - "fmt" - "io/fs" - "path" - "path/filepath" - "sort" - "strings" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/executor" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser" - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" -) - -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) -var _ ConfigurableTerraformScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - options []options.ScannerOption - parserOpt []parser.Option - executorOpt []executor.Option - dirs set.Set[string] - forceAllDirs bool - regoScanner *rego.Scanner - execLock sync.RWMutex -} - -func (s *Scanner) Name() string { - return "Terraform" -} - -func (s *Scanner) SetForceAllDirs(b bool) { - s.forceAllDirs = b -} - -func (s *Scanner) AddParserOptions(opts ...parser.Option) { - s.parserOpt = append(s.parserOpt, opts...) -} - -func (s *Scanner) AddExecutorOptions(opts ...executor.Option) { - s.executorOpt = append(s.executorOpt, opts...) -} - -func New(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - dirs: set.New[string](), - options: opts, - logger: log.WithPrefix("terraform scanner"), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -// terraformRootModule represents the module to be used as the root module for Terraform deployment. -type terraformRootModule struct { - rootPath string - childs terraform.Modules - fsMap map[string]fs.FS -} - -func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, dir string) (scan.Results, error) { - - s.logger.Debug("Scanning directory", log.FilePath(dir)) - - // find directories which directly contain tf files - modulePaths := s.findModules(target, dir, dir) - sort.Strings(modulePaths) - - if len(modulePaths) == 0 { - s.logger.Info("No modules found, skipping directory", log.FilePath(dir)) - return nil, nil - } - - regoScanner, err := s.initRegoScanner(target) - if err != nil { - return nil, err - } - - s.execLock.Lock() - s.executorOpt = append(s.executorOpt, executor.OptionWithRegoScanner(regoScanner)) - s.execLock.Unlock() - - var allResults scan.Results - - p := parser.New(target, "", s.parserOpt...) - rootDirs, err := p.FindRootModules(ctx, modulePaths) - if err != nil { - return nil, fmt.Errorf("failed to find root modules: %w", err) - } - - rootModules := make([]terraformRootModule, 0, len(rootDirs)) - - // parse all root module directories - for _, dir := range rootDirs { - - s.logger.Info("Scanning root module", log.FilePath(dir)) - - p := parser.New(target, "", s.parserOpt...) - - if err := p.ParseFS(ctx, dir); err != nil { - return nil, err - } - - modules, _, err := p.EvaluateAll(ctx) - if err != nil { - return nil, err - } - - rootModules = append(rootModules, terraformRootModule{ - rootPath: dir, - childs: modules, - fsMap: p.GetFilesystemMap(), - }) - } - - for _, module := range rootModules { - s.execLock.RLock() - e := executor.New(s.executorOpt...) - s.execLock.RUnlock() - results, err := e.Execute(ctx, module.childs, module.rootPath) - if err != nil { - return nil, err - } - - for i, result := range results { - if result.Metadata().Range().GetFS() != nil { - continue - } - key := result.Metadata().Range().GetFSKey() - if key == "" { - continue - } - if filesystem, ok := module.fsMap[key]; ok { - override := scan.Results{ - result, - } - override.SetSourceAndFilesystem(result.Range().GetSourcePrefix(), filesystem, false) - results[i] = override[0] - } - } - - allResults = append(allResults, results...) - } - - return allResults, nil -} - -func (s *Scanner) removeNestedDirs(dirs []string) []string { - if s.forceAllDirs { - return dirs - } - var clean []string - for _, dirA := range dirs { - dirOK := true - for _, dirB := range dirs { - if dirA == dirB { - continue - } - if str, err := filepath.Rel(dirB, dirA); err == nil && !strings.HasPrefix(str, "..") { - dirOK = false - break - } - } - if dirOK { - clean = append(clean, dirA) - } - } - return clean -} - -func (s *Scanner) findModules(target fs.FS, scanDir string, dirs ...string) []string { - - var roots []string - var others []string - - for _, dir := range dirs { - if s.isRootModule(target, dir) { - roots = append(roots, dir) - if !s.forceAllDirs { - continue - } - } - - // if this isn't a root module, look at directories inside it - files, err := fs.ReadDir(target, filepath.ToSlash(dir)) - if err != nil { - continue - } - for _, file := range files { - if file.IsDir() { - others = append(others, path.Join(dir, file.Name())) - } - } - } - - if (len(roots) == 0 || s.forceAllDirs) && len(others) > 0 { - roots = append(roots, s.findModules(target, scanDir, others...)...) - } - - return s.removeNestedDirs(roots) -} - -func (s *Scanner) isRootModule(target fs.FS, dir string) bool { - files, err := fs.ReadDir(target, filepath.ToSlash(dir)) - if err != nil { - s.logger.Error("Failed to read dir", log.FilePath(dir), log.Err(err)) - return false - } - for _, file := range files { - if strings.HasSuffix(file.Name(), ".tf") || strings.HasSuffix(file.Name(), ".tf.json") { - return true - } - } - return false -} diff --git a/pkg/iac/scanners/terraform/scanner_integration_test.go b/pkg/iac/scanners/terraform/scanner_integration_test.go deleted file mode 100644 index 3f3e0eb0e32d..000000000000 --- a/pkg/iac/scanners/terraform/scanner_integration_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package terraform - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" -) - -func Test_ScanRemoteModule(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -module "s3_bucket" { - source = "terraform-aws-modules/s3-bucket/aws" - - bucket = "my-s3-bucket" -} -`, - }) - - scanner := New( - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(false), - ScannerWithAllDirectories(true), - ScannerWithSkipCachedModules(true), - ) - - results, err := scanner.ScanFS(context.TODO(), fs, ".") - require.NoError(t, err) - - assert.Len(t, results.GetPassed(), 1) -} - -func Test_ScanChildUseRemoteModule(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - fs := testutil.CreateFS(t, map[string]string{ - "main.tf": ` -module "this" { - source = "./modules/s3" - bucket = "my-s3-bucket" -} -`, - "modules/s3/main.tf": ` -variable "bucket" { - type = string -} - -module "s3_bucket" { - source = "github.com/terraform-aws-modules/terraform-aws-s3-bucket?ref=v3.15.1" - bucket = var.bucket -} -`, - }) - - scanner := New( - rego.WithPolicyReader(strings.NewReader(emptyBucketCheck)), - rego.WithPolicyNamespaces("user"), - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(false), - ScannerWithAllDirectories(true), - ScannerWithSkipCachedModules(true), - ) - - results, err := scanner.ScanFS(context.TODO(), fs, ".") - require.NoError(t, err) - - assert.Len(t, results.GetPassed(), 1) -} - -func Test_OptionWithSkipDownloaded(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fs := testutil.CreateFS(t, map[string]string{ - "test/main.tf": ` -module "s3-bucket" { - source = "terraform-aws-modules/s3-bucket/aws" - version = "3.14.0" - bucket = "mybucket" - create_bucket = true -} -`, - // creating our own rule for the reliability of the test - "/rules/test.rego": ` -package defsec.abcdefg -__rego_input__ := { - "combine": false, - "selector": [{"type": "defsec", "subtypes": [{"service": "s3", "provider": "aws"}]}], -} -deny[cause] { - bucket := input.aws.s3.buckets[_] - bucket.name.value == "mybucket" - cause := bucket.name -}`, - }) - - t.Run("without skip", func(t *testing.T) { - scanner := New( - ScannerWithSkipCachedModules(true), - rego.WithPolicyDirs("rules"), - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(true), - ) - results, err := scanner.ScanFS(context.TODO(), fs, "test") - require.NoError(t, err) - - assert.Len(t, results, 1) - assert.Len(t, results.GetFailed(), 1) - }) - - t.Run("with skip", func(t *testing.T) { - scanner := New( - ScannerWithSkipDownloaded(true), - ScannerWithSkipCachedModules(true), - rego.WithPolicyDirs("rules"), - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(true), - ) - results, err := scanner.ScanFS(context.TODO(), fs, "test") - require.NoError(t, err) - - assert.Len(t, results, 1) - assert.Len(t, results.GetIgnored(), 1) - }) -} - -func Test_OptionWithSkipDownloadedIAMDocument(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test in short mode") - } - - fs := testutil.CreateFS(t, map[string]string{ - "test/main.tf": ` -module "karpenter" { - source = "terraform-aws-modules/eks/aws//modules/karpenter" - version = "19.21.0" - cluster_name = "test" - irsa_oidc_provider_arn = "example" -} -`, - // creating our own rule for the reliability of the test - "/rules/test.rego": ` -package defsec.abcdefg -__rego_input__ := { - "combine": false, - "selector": [{"type": "defsec", "subtypes": [{"service": "iam", "provider": "aws"}]}], -} -allows_permission(statements, permission, effect) { - statement := statements[_] - statement.Effect == effect - action = statement.Action[_] - action == permission -} -deny[res] { - policy := input.aws.iam.policies[_] - value = json.unmarshal(policy.document.value) - statements = value.Statement - not allows_permission(statements, "iam:PassRole", "Deny") - allows_permission(statements, "iam:PassRole", "Allow") - res = result.new("IAM policy allows 'iam:PassRole' action", policy.document) -} -`, - }) - - scanner := New( - ScannerWithSkipDownloaded(true), - ScannerWithSkipCachedModules(true), - rego.WithPolicyDirs("rules"), - rego.WithEmbeddedLibraries(true), - rego.WithEmbeddedPolicies(false), - ) - results, err := scanner.ScanFS(context.TODO(), fs, "test") - require.NoError(t, err) - assert.Len(t, results, 1) - - ignored := results.GetIgnored() - assert.Len(t, ignored, 1) - assert.NotNil(t, ignored[0].Metadata().Parent()) -} diff --git a/pkg/iac/scanners/terraform/scanner_test.go b/pkg/iac/scanners/terraform/scanner_test.go deleted file mode 100644 index 479bbfccda77..000000000000 --- a/pkg/iac/scanners/terraform/scanner_test.go +++ /dev/null @@ -1,1122 +0,0 @@ -package terraform - -import ( - "context" - "fmt" - "strconv" - "strings" - "testing" - "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_OptionWithPolicyDirs(t *testing.T) { - - fsys := testutil.CreateFS(t, map[string]string{ - "/code/main.tf": `resource "aws_s3_bucket" "my-bucket" {}`, - "/rules/test.rego": emptyBucketCheck, - }) - - results, err := scanFS(fsys, "code", - rego.WithPolicyFilesystem(fsys), - rego.WithPolicyDirs("rules"), - rego.WithPolicyNamespaces("user"), - ) - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - failure := results.GetFailed()[0] - - assert.Equal(t, "USER-TEST-0123", failure.Rule().AVDID) - - actualCode, err := failure.GetCode() - require.NoError(t, err) - for i := range actualCode.Lines { - actualCode.Lines[i].Highlighted = "" - } - assert.Equal(t, []scan.Line{ - { - Number: 1, - Content: "resource \"aws_s3_bucket\" \"my-bucket\" {}", - IsCause: true, - FirstCause: true, - LastCause: true, - }, - }, actualCode.Lines) - -} - -func Test_OptionWithPolicyNamespaces(t *testing.T) { - - tests := []struct { - includedNamespaces []string - policyNamespace string - wantFailure bool - }{ - { - includedNamespaces: nil, - policyNamespace: "blah", - wantFailure: false, - }, - { - includedNamespaces: nil, - policyNamespace: "appshield.something", - wantFailure: true, - }, - { - includedNamespaces: nil, - policyNamespace: "defsec.blah", - wantFailure: true, - }, - { - includedNamespaces: []string{"user"}, - policyNamespace: "users", - wantFailure: false, - }, - { - includedNamespaces: []string{"users"}, - policyNamespace: "something.users", - wantFailure: false, - }, - { - includedNamespaces: []string{"users"}, - policyNamespace: "users", - wantFailure: true, - }, - { - includedNamespaces: []string{"users"}, - policyNamespace: "users.my_rule", - wantFailure: true, - }, - { - includedNamespaces: []string{ - "a", - "users", - "b", - }, - policyNamespace: "users", - wantFailure: true, - }, - { - includedNamespaces: []string{"user"}, - policyNamespace: "defsec", - wantFailure: true, - }, - } - - for i, test := range tests { - - t.Run(strconv.Itoa(i), func(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/main.tf": ` -resource "aws_s3_bucket" "my-bucket" { - bucket = "evil" -} -`, - "/rules/test.rego": fmt.Sprintf(` -# METADATA -# custom: -# input: -# selector: -# - type: cloud -# subtypes: -# - service: s3 -# provider: aws -package %s - -deny[cause] { -bucket := input.aws.s3.buckets[_] -bucket.name.value == "evil" -cause := bucket.name -} - - `, test.policyNamespace), - }) - - scanner := New( - rego.WithPolicyDirs("rules"), - rego.WithPolicyNamespaces(test.includedNamespaces...), - ) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - var found bool - for _, result := range results.GetFailed() { - if result.RegoNamespace() == test.policyNamespace && result.RegoRule() == "deny" { - found = true - break - } - } - assert.Equal(t, test.wantFailure, found) - }) - } - -} - -func Test_IAMPolicyRego(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "/code/main.tf": ` -resource "aws_sqs_queue_policy" "bad_example" { - queue_url = aws_sqs_queue.q.id - - policy = < tfplan.Plan.VariablesEntry - 0, // 1: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_init() } -func file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_init() { - if File_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DynamicValue); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Plan); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_rawDesc, - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_goTypes, - DependencyIndexes: file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_depIdxs, - MessageInfos: file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_msgTypes, - }.Build() - File_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto = out.File - file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_rawDesc = nil - file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_goTypes = nil - file_pkg_iac_scanners_terraformplan_snapshot_planproto_planfile_proto_depIdxs = nil -} diff --git a/pkg/iac/scanners/terraformplan/snapshot/planproto/planfile.proto b/pkg/iac/scanners/terraformplan/snapshot/planproto/planfile.proto deleted file mode 100644 index c5615b819e66..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/planproto/planfile.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; -package tfplan; - -option go_package = "github.com/aquasecurity/trivy/iac/scanners/terraformplan/snapshot/planproto"; - -message DynamicValue { - bytes msgpack = 1; -} - -message Plan { - map variables = 2; -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/scanner.go b/pkg/iac/scanners/terraformplan/snapshot/scanner.go deleted file mode 100644 index 3c8dcc8fce0b..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/scanner.go +++ /dev/null @@ -1,80 +0,0 @@ -package snapshot - -import ( - "context" - "errors" - "fmt" - "io" - "io/fs" - - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - terraformScanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform" - tfparser "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser" -) - -type Scanner struct { - inner *terraformScanner.Scanner -} - -func (s *Scanner) Name() string { - return "Terraform Plan Snapshot" -} - -func New(opts ...options.ScannerOption) *Scanner { - scanner := &Scanner{ - inner: terraformScanner.New(opts...), - } - return scanner -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (scan.Results, error) { - var results scan.Results - walkFn := func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - - if d.IsDir() { - return nil - } - - res, err := s.ScanFile(ctx, fsys, path) - if errors.Is(err, errNoTerraformPlan) { - return nil - } else if err != nil { - return err - } - results = append(results, res...) - return nil - } - if err := fs.WalkDir(fsys, dir, walkFn); err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, filepath string) (scan.Results, error) { - file, err := fsys.Open(filepath) - if err != nil { - return nil, err - } - defer file.Close() - return s.Scan(ctx, file) -} - -func (s *Scanner) Scan(ctx context.Context, reader io.Reader) (scan.Results, error) { - snap, err := parseSnapshot(reader) - if err != nil { - return nil, err - } - fsys, err := snap.toFS() - if err != nil { - return nil, fmt.Errorf("failed to convert snapshot to FS: %w", err) - } - - s.inner.AddParserOptions( - tfparser.OptionsWithTfVars(snap.inputVariables), - ) - return s.inner.ScanFS(ctx, fsys, ".") -} diff --git a/pkg/iac/scanners/terraformplan/snapshot/scanner_test.go b/pkg/iac/scanners/terraformplan/snapshot/scanner_test.go deleted file mode 100644 index 4cb3b4d52816..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/scanner_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package snapshot - -import ( - "context" - "os" - "path" - "path/filepath" - "sort" - "testing" - - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - tfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform" -) - -func initScanner(opts ...options.ScannerOption) *Scanner { - defaultOpts := []options.ScannerOption{ - rego.WithEmbeddedPolicies(false), - rego.WithEmbeddedLibraries(true), - rego.WithPolicyNamespaces("user"), - rego.WithPolicyDirs("."), - rego.WithRegoErrorLimits(0), - tfscanner.ScannerWithSkipCachedModules(true), - } - - opts = append(opts, defaultOpts...) - return New(opts...) -} - -func TestScanner_Scan(t *testing.T) { - tests := []struct { - dir string - expectedIDs []string - }{ - { - dir: "just-resource", - expectedIDs: []string{"ID001"}, - }, - { - dir: "with-local-module", - expectedIDs: []string{"ID001"}, - }, - { - dir: "with-remote-module", - expectedIDs: []string{"ID001"}, - }, - } - - for _, tt := range tests { - t.Run(tt.dir, func(t *testing.T) { - f, err := os.Open(filepath.Join("testdata", tt.dir, "tfplan")) - require.NoError(t, err) - defer f.Close() - - policyFS := os.DirFS(filepath.Join("testdata", tt.dir, "checks")) - - s := initScanner(rego.WithPolicyFilesystem(policyFS)) - result, err := s.Scan(context.TODO(), f) - require.NoError(t, err) - - failed := result.GetFailed() - - assert.Len(t, failed, len(tt.expectedIDs)) - - ids := lo.Map(failed, func(res scan.Result, _ int) string { - return res.Rule().AVDID - }) - sort.Strings(ids) - - assert.Equal(t, tt.expectedIDs, ids) - }) - } -} - -func Test_ScanFS(t *testing.T) { - t.Parallel() - - tests := []struct { - dir string - expectedDir string - expectedIDs []string - }{ - { - dir: "just-resource", - expectedIDs: []string{"ID001"}, - }, - { - dir: "with-local-module", - expectedIDs: []string{"ID001"}, - }, - { - dir: "with-remote-module", - expectedIDs: []string{"ID001"}, - }, - { - dir: "with-var", - expectedIDs: []string{"ID001"}, - }, - } - - for _, tc := range tests { - t.Run(tc.dir, func(t *testing.T) { - fs := os.DirFS("testdata") - - scanner := New( - rego.WithPolicyDirs(path.Join(tc.dir, "checks")), - rego.WithPolicyFilesystem(fs), - rego.WithPolicyNamespaces("user"), - rego.WithEmbeddedLibraries(false), - rego.WithEmbeddedPolicies(false), - rego.WithRegoErrorLimits(0), - tfscanner.ScannerWithSkipCachedModules(true), - ) - - results, err := scanner.ScanFS(context.TODO(), fs, path.Join(tc.dir, "tfplan")) - require.NoError(t, err) - require.Len(t, results, 1) - - failed := results.GetFailed() - - assert.Len(t, failed, len(tc.expectedIDs)) - - ids := lo.Map(failed, func(res scan.Result, _ int) string { - return res.Rule().AVDID - }) - sort.Strings(ids) - - assert.Equal(t, tc.expectedIDs, ids) - }) - } -} diff --git a/pkg/iac/scanners/terraformplan/snapshot/snapshot.go b/pkg/iac/scanners/terraformplan/snapshot/snapshot.go deleted file mode 100644 index 72556a984479..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/snapshot.go +++ /dev/null @@ -1,220 +0,0 @@ -package snapshot - -import ( - "archive/zip" - "encoding/json" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path" - "slices" - "strings" - - "github.com/liamg/memoryfs" - "github.com/zclconf/go-cty/cty" - - iox "github.com/aquasecurity/trivy/pkg/x/io" -) - -const ( - configSnapshotPrefix = "tfconfig/" - configSnapshotManifestFile = configSnapshotPrefix + "modules.json" - configSnapshotModulePrefix = configSnapshotPrefix + "m-" - - tfplanFilename = "tfplan" -) - -type ( - configSnapshotModuleRecord struct { - Key string `json:"Key"` - SourceAddr string `json:"Source,omitempty"` - Dir string `json:"Dir"` - } - - configSnapshotModuleManifest []configSnapshotModuleRecord -) - -var errNoTerraformPlan = errors.New("no terraform plan file") - -func IsPlanSnapshot(r io.Reader) bool { - zr, err := readSnapshot(r) - if err != nil { - return false - } - return containsTfplanFile(zr) -} - -func readSnapshot(r io.Reader) (*zip.Reader, error) { - if r == nil { - return nil, errors.New("reader is nil") - } - - rsa, size, err := iox.NewReadSeekerAtWithSize(r) - if err != nil { - return nil, err - } - - zr, err := zip.NewReader(rsa, size) - if err != nil { - return nil, err - } - - return zr, nil -} - -func parseSnapshot(r io.Reader) (*snapshot, error) { - zr, err := readSnapshot(r) - if err != nil { - return nil, err - } - - if !containsTfplanFile(zr) { - return nil, errNoTerraformPlan - } - - snap := &snapshot{ - modules: make(map[string]*snapshotModule), - inputVariables: make(map[string]cty.Value), - } - - var moduleManifest configSnapshotModuleManifest - - for _, file := range zr.File { - switch { - case file.Name == configSnapshotManifestFile: - var err error - moduleManifest, err = readModuleManifest(file) - if err != nil { - return nil, err - } - case strings.HasPrefix(file.Name, configSnapshotModulePrefix): - if err := snap.addFile(file); err != nil { - return nil, err - } - case file.Name == tfplanFilename: - r, err := file.Open() - if err != nil { - return nil, fmt.Errorf("failed to open plan: %w", err) - } - - plan, err := readTfPlan(r) - if err != nil { - _ = r.Close() - return nil, fmt.Errorf("failed to read tfplan: %w", err) - } - _ = r.Close() - - snap.inputVariables, err = plan.inputVariables() - if err != nil { - return nil, err - } - } - } - - for _, record := range moduleManifest { - // skip non-local modules - if record.Dir != "." && !strings.HasPrefix(record.SourceAddr, ".") { - delete(snap.modules, record.Key) - continue - } - modSnap := snap.getOrCreateModuleSnapshot(record.Key) - modSnap.dir = record.Dir - } - - return snap, nil -} - -func containsTfplanFile(zr *zip.Reader) bool { - return slices.ContainsFunc(zr.File, func(f *zip.File) bool { - return f.Name == tfplanFilename - }) -} - -func readModuleManifest(f *zip.File) (configSnapshotModuleManifest, error) { - r, err := f.Open() - if err != nil { - return nil, fmt.Errorf("failed to open module manifest: %s", r) - } - defer r.Close() - - var manifest configSnapshotModuleManifest - if err := json.NewDecoder(r).Decode(&manifest); err != nil { - return nil, fmt.Errorf("failed to read module manifest: %s", f.Name) - } - return manifest, nil -} - -type ( - snapshotModule struct { - // dir is the path, relative to the root directory given when the - // snapshot was created, where the module appears in the snapshot's - // virtual filesystem. - dir string - - // files is a map from each configuration file filename for the - // module to a raw byte representation of the source file contents. - files map[string][]byte - } - - snapshot struct { - modules map[string]*snapshotModule - inputVariables map[string]cty.Value - } -) - -func (s *snapshot) addFile(file *zip.File) error { - relName := file.Name[len(configSnapshotModulePrefix):] - moduleKey, fileName := path.Split(relName) - if moduleKey == "" { - return nil - } - moduleKey = moduleKey[:len(moduleKey)-1] - - r, err := file.Open() - if err != nil { - return fmt.Errorf("failed to open snapshot of %s from module %q: %s", fileName, moduleKey, err) - } - defer r.Close() - - fileSrc, err := io.ReadAll(r) - if err != nil { - return fmt.Errorf("failed to read snapshot of %s from module %q: %s", fileName, moduleKey, err) - } - - modSnap := s.getOrCreateModuleSnapshot(moduleKey) - modSnap.files[fileName] = fileSrc - return nil -} - -func (s *snapshot) getOrCreateModuleSnapshot(key string) *snapshotModule { - modSnap, exists := s.modules[key] - if !exists { - modSnap = &snapshotModule{ - files: make(map[string][]byte), - } - s.modules[key] = modSnap - } - return modSnap -} - -func (s *snapshot) toFS() (fs.FS, error) { - fsys := memoryfs.New() - - for _, module := range s.modules { - if err := fsys.MkdirAll(module.dir, fs.ModePerm); err != nil && !errors.Is(err, os.ErrExist) { - return nil, err - } - for filename, file := range module.files { - filePath := filename - if module.dir != "" { - filePath = path.Join(module.dir, filename) - } - if err := fsys.WriteFile(filePath, file, fs.ModePerm); err != nil { - return nil, fmt.Errorf("failed to add file: %w", err) - } - } - } - return fsys, nil -} diff --git a/pkg/iac/scanners/terraformplan/snapshot/snapshot_test.go b/pkg/iac/scanners/terraformplan/snapshot/snapshot_test.go deleted file mode 100644 index c85791b2f283..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/snapshot_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package snapshot - -import ( - "archive/zip" - "bytes" - "io/fs" - "os" - "path/filepath" - "sort" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/zclconf/go-cty/cty" -) - -func TestReadSnapshot(t *testing.T) { - tests := []struct { - name string - dir string - expectedFiles []string - }{ - { - name: "just resource", - dir: "just-resource", - expectedFiles: []string{"main.tf", "terraform.tf"}, - }, - { - name: "with local module", - dir: "with-local-module", - expectedFiles: []string{"main.tf", "modules/ec2/main.tf", "terraform.tf"}, - }, - { - name: "with nested modules", - dir: "nested-modules", - expectedFiles: []string{ - "main.tf", - "modules/s3/main.tf", - "modules/s3/modules/logging/main.tf", - "terraform.tf", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - f, err := os.Open(filepath.Join("testdata", tt.dir, "tfplan")) - require.NoError(t, err) - defer f.Close() - - snapshot, err := parseSnapshot(f) - require.NoError(t, err) - require.NotNil(t, snapshot) - - fsys, err := snapshot.toFS() - require.NoError(t, err) - - files, err := getAllfiles(fsys) - require.NoError(t, err) - - assert.Equal(t, tt.expectedFiles, files) - }) - } -} - -func getAllfiles(fsys fs.FS) ([]string, error) { - var files []string - walkFn := func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - - if d.IsDir() { - return nil - } - - files = append(files, filepath.ToSlash(path)) - return nil - } - if err := fs.WalkDir(fsys, ".", walkFn); err != nil { - return nil, err - } - - sort.Strings(files) - return files, nil -} - -func TestIsPlanSnapshot(t *testing.T) { - t.Run("TF plan", func(t *testing.T) { - f, err := os.Open(filepath.Join("testdata", "just-resource", "tfplan")) - require.NoError(t, err) - defer f.Close() - - got := IsPlanSnapshot(f) - assert.True(t, got) - }) - - t.Run("just a zip file", func(t *testing.T) { - var b bytes.Buffer - zw := zip.NewWriter(&b) - defer zw.Close() - - w, err := zw.Create("test.txt") - require.NoError(t, err) - - w.Write([]byte("test")) - - got := IsPlanSnapshot(&b) - assert.False(t, got) - }) -} - -func TestPlanWithVariables(t *testing.T) { - f, err := os.Open(filepath.Join("testdata", "with-var", "tfplan")) - require.NoError(t, err) - defer f.Close() - - snapshot, err := parseSnapshot(f) - require.NoError(t, err) - require.NotNil(t, snapshot) - - expectedVars := map[string]cty.Value{ - "bucket_name": cty.StringVal("test-bucket"), - } - assert.Equal(t, expectedVars, snapshot.inputVariables) -} diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/checks/s3-bucket-name.rego b/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/checks/s3-bucket-name.rego deleted file mode 100644 index 798b1b705a49..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/checks/s3-bucket-name.rego +++ /dev/null @@ -1,21 +0,0 @@ -# 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/just-resource/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/main.tf deleted file mode 100644 index 76b7c4788d76..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_s3_bucket" "this" { - bucket = "test-bucket" -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/terraform.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/terraform.tf deleted file mode 100644 index ac2d39c811e5..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - aws = { - source = "aws" - version = "5.35.0" - } - } -} diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/tfplan b/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/tfplan deleted file mode 100644 index 7556492a0dff..000000000000 Binary files a/pkg/iac/scanners/terraformplan/snapshot/testdata/just-resource/tfplan and /dev/null differ diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf deleted file mode 100644 index a474edf3ea6e..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index d847578a1a00..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index b9cb54f04a5b..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/modules/s3/modules/logging/main.tf +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index ac2d39c811e5..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 148507541588..000000000000 Binary files a/pkg/iac/scanners/terraformplan/snapshot/testdata/nested-modules/tfplan and /dev/null differ 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 deleted file mode 100644 index 7ce95b9f9a54..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/checks/ec2-userdata.rego +++ /dev/null @@ -1,21 +0,0 @@ -# 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 deleted file mode 100644 index 9755bb5807a5..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 488bb6200e29..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/modules/ec2/main.tf +++ /dev/null @@ -1,34 +0,0 @@ -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 deleted file mode 100644 index ac2d39c811e5..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index ecd1456588fb..000000000000 Binary files a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-local-module/tfplan and /dev/null differ 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 deleted file mode 100644 index 798b1b705a49..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/checks/s3-bucket-name.rego +++ /dev/null @@ -1,21 +0,0 @@ -# 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 deleted file mode 100644 index de5a5fb6f7dc..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index ac2d39c811e5..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 872ad20646cb..000000000000 Binary files a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-remote-module/tfplan and /dev/null differ diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/checks/s3-bucket-name.rego b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/checks/s3-bucket-name.rego deleted file mode 100644 index 798b1b705a49..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/checks/s3-bucket-name.rego +++ /dev/null @@ -1,21 +0,0 @@ -# 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-var/main.tf b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/main.tf deleted file mode 100644 index a6fc475bfb3e..000000000000 --- a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "bucket_name" {} - -resource "aws_s3_bucket" "this" { - bucket = var.bucket_name -} \ No newline at end of file diff --git a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/tfplan b/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/tfplan deleted file mode 100644 index 78be033f53d4..000000000000 Binary files a/pkg/iac/scanners/terraformplan/snapshot/testdata/with-var/tfplan and /dev/null differ diff --git a/pkg/iac/scanners/terraformplan/tfjson/parser/parser.go b/pkg/iac/scanners/terraformplan/tfjson/parser/parser.go deleted file mode 100644 index a55cfa99384f..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/parser/parser.go +++ /dev/null @@ -1,206 +0,0 @@ -package parser - -import ( - "crypto/md5" //#nosec - "encoding/json" - "fmt" - "io" - "os" - "strings" - - "github.com/liamg/memoryfs" - - "github.com/aquasecurity/trivy/pkg/iac/terraform" - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("tfjson parser"), - } -} - -func (p *Parser) ParseFile(filepath string) (*PlanFile, error) { - - if _, err := os.Stat(filepath); err != nil { - return nil, err - } - - reader, err := os.Open(filepath) - if err != nil { - return nil, err - } - defer reader.Close() - return p.Parse(reader) -} - -func (p *Parser) Parse(reader io.Reader) (*PlanFile, error) { - - var planFile PlanFile - - if err := json.NewDecoder(reader).Decode(&planFile); err != nil { - return nil, err - } - - return &planFile, nil - -} - -func (p *PlanFile) ToFS() (*memoryfs.FS, error) { - - rootFS := memoryfs.New() - - var fileResources []string - - resources, err := getResources(p.PlannedValues.RootModule, p.ResourceChanges, p.Configuration) - if err != nil { - return nil, err - } - - for _, r := range resources { - fileResources = append(fileResources, r.ToHCL()) - } - - fileContent := strings.Join(fileResources, "\n\n") - if err := rootFS.WriteFile("main.tf", []byte(fileContent), os.ModePerm); err != nil { - return nil, err - } - return rootFS, nil - -} - -func getResources(module Module, resourceChanges []ResourceChange, configuration Configuration) ([]terraform.PlanBlock, error) { - var resources []terraform.PlanBlock - for _, r := range module.Resources { - resourceName := r.Name - if strings.HasPrefix(r.Address, "module.") { - hashable := strings.TrimSuffix(strings.Split(r.Address, fmt.Sprintf(".%s.", r.Type))[0], ".data") - /* #nosec */ - hash := fmt.Sprintf("%x", md5.Sum([]byte(hashable))) - resourceName = fmt.Sprintf("%s_%s", r.Name, hash) - } - - res := terraform.NewPlanBlock(r.Mode, r.Type, resourceName) - - changes := getValues(r.Address, resourceChanges) - // process the changes to get the after state - for k, v := range changes.After { - switch t := v.(type) { - case []any: - if len(t) == 0 { - continue - } - val := t[0] - switch v := val.(type) { - // is it a HCL block? - case map[string]any: - res.Blocks[k] = v - // just a normal attribute then - default: - res.Attributes[k] = v - } - default: - res.Attributes[k] = v - } - } - - resourceConfig := getConfiguration(r.Address, configuration.RootModule) - if resourceConfig != nil { - - for attr, val := range resourceConfig.Expressions { - if value, shouldReplace := unpackConfigurationValue(val, r); shouldReplace || !res.HasAttribute(attr) { - res.Attributes[attr] = value - } - } - } - resources = append(resources, *res) - } - - for _, m := range module.ChildModules { - cr, err := getResources(m.Module, resourceChanges, configuration) - if err != nil { - return nil, err - } - resources = append(resources, cr...) - } - - return resources, nil -} - -func unpackConfigurationValue(val any, r Resource) (any, bool) { - if t, ok := val.(map[string]any); ok { - for k, v := range t { - switch k { - case "references": - reference := v.([]any)[0].(string) - if strings.HasPrefix(r.Address, "module.") { - hashable := strings.TrimSuffix(strings.Split(r.Address, fmt.Sprintf(".%s.", r.Type))[0], ".data") - /* #nosec */ - hash := fmt.Sprintf("%x", md5.Sum([]byte(hashable))) - - parts := strings.Split(reference, ".") - var rejoin []string - - name := parts[1] - remainder := parts[2:] - if parts[0] == "data" { - rejoin = append(rejoin, parts[:2]...) - name = parts[2] - remainder = parts[3:] - } else { - rejoin = append(rejoin, parts[:1]...) - } - - rejoin = append(rejoin, fmt.Sprintf("%s_%s", name, hash)) - rejoin = append(rejoin, remainder...) - - reference = strings.Join(rejoin, ".") - } - return terraform.PlanReference{Value: reference}, false - case "constant_value": - return v, false - } - } - } - - return nil, false -} - -func getConfiguration(address string, configuration ConfigurationModule) *ConfigurationResource { - - workingAddress := address - var moduleParts []string - for strings.HasPrefix(workingAddress, "module.") { - workingAddressParts := strings.Split(workingAddress, ".") - moduleParts = append(moduleParts, workingAddressParts[1]) - workingAddress = strings.Join(workingAddressParts[2:], ".") - } - - workingModule := configuration - for _, moduleName := range moduleParts { - if module, ok := workingModule.ModuleCalls[moduleName]; ok { - workingModule = module.Module - } - } - - for _, resource := range workingModule.Resources { - if resource.Address == workingAddress { - return &resource - } - } - - return nil -} - -func getValues(address string, resourceChange []ResourceChange) *ResourceChange { - for _, r := range resourceChange { - if r.Address == address { - return &r - } - } - return nil -} diff --git a/pkg/iac/scanners/terraformplan/tfjson/parser/plan_file.go b/pkg/iac/scanners/terraformplan/tfjson/parser/plan_file.go deleted file mode 100644 index 6275c228182e..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/parser/plan_file.go +++ /dev/null @@ -1,67 +0,0 @@ -package parser - -type Resource struct { - Address string `json:"address"` - ModuleAddress string `json:"module_address"` - Mode string `json:"mode"` - Type string `json:"type"` - Name string `json:"name"` - ProviderName string `json:"provider_name"` - SchemaVersion int `json:"schema_version"` -} - -type ResourceChange struct { - Resource - Change `json:"change"` -} - -type ConfigurationResource struct { - Resource - Expressions map[string]any `json:"expressions"` -} - -type Change struct { - Before map[string]any `json:"before"` - After map[string]any `json:"after"` -} - -type Module struct { - Resources []Resource `json:"resources"` - ChildModules []ChildModule `json:"child_modules"` -} - -type ChildModule struct { - Module - Address string `json:"address"` -} - -type ConfigurationModule struct { - Resources []ConfigurationResource `json:"resources"` - ModuleCalls map[string]CallModule `json:"module_calls"` -} - -type CallModule struct { - Source string `json:"source"` - Module ConfigurationModule `json:"module"` -} - -type ConfigurationChildModule struct { - ConfigurationModule - Address string `json:"address"` -} - -type PlannedValues struct { - RootModule Module `json:"root_module"` -} - -type Configuration struct { - RootModule ConfigurationModule `json:"root_module"` -} - -type PlanFile struct { - FormatVersion string `json:"format_version"` - TerraformVersion string `json:"terraform_version"` - PlannedValues PlannedValues `json:"planned_values"` - ResourceChanges []ResourceChange `json:"resource_changes"` - Configuration Configuration `json:"configuration"` -} diff --git a/pkg/iac/scanners/terraformplan/tfjson/scanner.go b/pkg/iac/scanners/terraformplan/tfjson/scanner.go deleted file mode 100644 index b3e8725c42ae..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/scanner.go +++ /dev/null @@ -1,91 +0,0 @@ -package tfjson - -import ( - "context" - "fmt" - "io" - "io/fs" - - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson/parser" - "github.com/aquasecurity/trivy/pkg/log" -) - -type Scanner struct { - parser *parser.Parser - logger *log.Logger - options []options.ScannerOption - tfScanner *terraform.Scanner -} - -func (s *Scanner) Name() string { - return "Terraform Plan JSON" -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (scan.Results, error) { - - var results scan.Results - - walkFn := func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - - if d.IsDir() { - return nil - } - - res, err := s.ScanFile(path, fsys) - if err != nil { - return fmt.Errorf("failed to scan %s: %w", path, err) - } - - results = append(results, res...) - return nil - } - - if err := fs.WalkDir(fsys, dir, walkFn); err != nil { - return nil, err - } - - return results, nil -} - -func New(opts ...options.ScannerOption) *Scanner { - scanner := &Scanner{ - options: opts, - logger: log.WithPrefix("tfjson scanner"), - parser: parser.New(), - tfScanner: terraform.New(opts...), - } - - return scanner -} - -func (s *Scanner) ScanFile(filepath string, fsys fs.FS) (scan.Results, error) { - - s.logger.Debug("Scanning file", log.FilePath(filepath)) - file, err := fsys.Open(filepath) - if err != nil { - return nil, err - } - defer file.Close() - return s.Scan(file) -} - -func (s *Scanner) Scan(reader io.Reader) (scan.Results, error) { - - planFile, err := s.parser.Parse(reader) - if err != nil { - return nil, err - } - - planFS, err := planFile.ToFS() - if err != nil { - return nil, fmt.Errorf("failed to convert plan to FS: %w", err) - } - - return s.tfScanner.ScanFS(context.TODO(), planFS, ".") -} diff --git a/pkg/iac/scanners/terraformplan/tfjson/scanner_test.go b/pkg/iac/scanners/terraformplan/tfjson/scanner_test.go deleted file mode 100644 index 11e2d577bcf2..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/scanner_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package tfjson - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" -) - -const defaultCheck = `package defsec.abcdefg - -__rego_metadata__ := { - "id": "TEST123", - "avd_id": "AVD-TEST-0123", - "title": "Buckets should not be evil", - "short_code": "no-evil-buckets", - "severity": "CRITICAL", - "type": "DefSec Security Check", - "description": "You should not allow buckets to be evil", - "recommended_actions": "Use a good bucket instead", - "url": "https://google.com/search?q=is+my+bucket+evil", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "cloud", "subtypes": [{"service": "s3", "provider": "aws"}]}], -} - -deny[cause] { - bucket := input.aws.s3.buckets[_] - bucket.name.value == "tfsec-plan-testing" - cause := bucket.name -}` - -func Test_TerraformScanner(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - inputFile string - check string - options []options.ScannerOption - }{ - { - name: "old rego metadata", - inputFile: "test/testdata/plan.json", - check: defaultCheck, - options: []options.ScannerOption{ - rego.WithPolicyDirs("rules"), - }, - }, - { - name: "with user namespace", - inputFile: "test/testdata/plan.json", - check: defaultCheck, - options: []options.ScannerOption{ - rego.WithPolicyDirs("rules"), - rego.WithPolicyNamespaces("user"), - }, - }, - { - name: "with templated plan json", - inputFile: "test/testdata/plan_with_template.json", - check: ` -# METADATA -# title: Bad buckets are bad -# description: Bad buckets are bad because they are not good. -# scope: package -# schemas: -# - input: schema["cloud"] -# custom: -# avd_id: AVD-TEST-0123 -# severity: CRITICAL -# short_code: very-bad-misconfig -# recommended_action: "Fix the s3 bucket" - -package user.foobar.ABC001 - -deny[cause] { - bucket := input.aws.s3.buckets[_] - bucket.name.value == "${template-name-is-$evil}" - cause := bucket.name -} -`, - options: []options.ScannerOption{ - rego.WithPolicyDirs("rules"), - rego.WithPolicyNamespaces("user"), - }, - }, - { - name: "plan with arbitrary name", - inputFile: "test/testdata/arbitrary_name.json", - check: defaultCheck, - options: []options.ScannerOption{ - rego.WithPolicyDirs("rules"), - rego.WithPolicyNamespaces("user"), - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - b, _ := os.ReadFile(tc.inputFile) - fs := testutil.CreateFS(t, map[string]string{ - "/code/main.tfplan.json": string(b), - "/rules/test.rego": tc.check, - }) - - so := append(tc.options, rego.WithPolicyFilesystem(fs)) - scanner := New(so...) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - failure := results.GetFailed()[0] - - assert.Equal(t, "AVD-TEST-0123", failure.Rule().AVDID) - }) - } -} diff --git a/pkg/iac/scanners/terraformplan/tfjson/test/parser_test.go b/pkg/iac/scanners/terraformplan/tfjson/test/parser_test.go deleted file mode 100644 index 85a63c6227ef..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/test/parser_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson/parser" -) - -func Test_Parse_Plan_File(t *testing.T) { - planFile, err := parser.New().ParseFile("testdata/plan.json") - require.NoError(t, err) - - assert.NotNil(t, planFile) - fs, err := planFile.ToFS() - require.NoError(t, err) - - assert.NotNil(t, fs) -} diff --git a/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go b/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go deleted file mode 100644 index d70280da437f..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package json - -import ( - "os" - "testing" - "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson" -) - -func Test_Scanning_Plan(t *testing.T) { - scanner := tfjson.New( - rego.WithEmbeddedPolicies(true), - rego.WithEmbeddedLibraries(true), - ) - b, _ := os.ReadFile("testdata/plan.json") - testFS := fstest.MapFS{ - "testdata/plan.json": {Data: b}, - } - - results, err := scanner.ScanFile("testdata/plan.json", testFS) - require.NoError(t, err) - require.NotNil(t, results) - - var failedResults scan.Results - for _, r := range results { - if r.Status() == scan.StatusFailed { - failedResults = append(failedResults, r) - } - } - - assert.Len(t, failedResults, 8) - -} diff --git a/pkg/iac/scanners/terraformplan/tfjson/test/testdata/arbitrary_name.json b/pkg/iac/scanners/terraformplan/tfjson/test/testdata/arbitrary_name.json deleted file mode 100644 index e0109b0325a8..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/test/testdata/arbitrary_name.json +++ /dev/null @@ -1 +0,0 @@ -{"format_version":"1.2","terraform_version":"1.8.1","planned_values":{"root_module":{"resources":[{"address":"aws_s3_bucket.this","mode":"managed","type":"aws_s3_bucket","name":"this","provider_name":"registry.opentofu.org/hashicorp/aws","schema_version":0,"values":{"bucket":"tfsec-plan-testing","force_destroy":false,"tags":null,"timeouts":null},"sensitive_values":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[],"website":[]}}]}},"resource_drift":[{"address":"aws_security_group.this","mode":"managed","type":"aws_security_group","name":"this","provider_name":"registry.opentofu.org/hashicorp/aws","change":{"actions":["delete"],"before":{"arn":"arn:aws:ec2:us-west-1:145167242158:security-group/sg-0f3ac859864629452","description":"Managed by Terraform","egress":[{"cidr_blocks":[],"description":"","from_port":0,"ipv6_cidr_blocks":[],"prefix_list_ids":[],"protocol":"-1","security_groups":[],"self":false,"to_port":0}],"id":"sg-0f3ac859864629452","ingress":[],"name":"test_sg","name_prefix":"","owner_id":"145167242158","revoke_rules_on_delete":false,"tags":null,"tags_all":{},"timeouts":null,"vpc_id":"vpc-06165c55f5a70fdfa"},"after":null,"after_unknown":{},"before_sensitive":{"egress":[{"cidr_blocks":[],"ipv6_cidr_blocks":[],"prefix_list_ids":[],"security_groups":[]}],"ingress":[],"tags_all":{}},"after_sensitive":false}}],"resource_changes":[{"address":"aws_s3_bucket.this","mode":"managed","type":"aws_s3_bucket","name":"this","provider_name":"registry.opentofu.org/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"bucket":"tfsec-plan-testing","force_destroy":false,"tags":null,"timeouts":null},"after_unknown":{"acceleration_status":true,"acl":true,"arn":true,"bucket_domain_name":true,"bucket_prefix":true,"bucket_regional_domain_name":true,"cors_rule":true,"grant":true,"hosted_zone_id":true,"id":true,"lifecycle_rule":true,"logging":true,"object_lock_configuration":true,"object_lock_enabled":true,"policy":true,"region":true,"replication_configuration":true,"request_payer":true,"server_side_encryption_configuration":true,"tags_all":true,"versioning":true,"website":true,"website_domain":true,"website_endpoint":true},"before_sensitive":false,"after_sensitive":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[],"website":[]}}}],"configuration":{"provider_config":{"aws":{"name":"aws","full_name":"registry.opentofu.org/hashicorp/aws"}},"root_module":{"resources":[{"address":"aws_s3_bucket.this","mode":"managed","type":"aws_s3_bucket","name":"this","provider_config_key":"aws","expressions":{"bucket":{"constant_value":"tfsec-plan-testing"}},"schema_version":0}]}},"timestamp":"2024-08-28T04:27:38Z","errored":false} diff --git a/pkg/iac/scanners/terraformplan/tfjson/test/testdata/plan.json b/pkg/iac/scanners/terraformplan/tfjson/test/testdata/plan.json deleted file mode 100644 index 8a3588e19052..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/test/testdata/plan.json +++ /dev/null @@ -1 +0,0 @@ -{"format_version":"0.2","terraform_version":"1.0.3","variables":{"bucket_name":{"value":"tfsec-plan-testing"}},"planned_values":{"root_module":{"resources":[{"address":"aws_s3_bucket.planbucket","mode":"managed","type":"aws_s3_bucket","name":"planbucket","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"bucket":"tfsec-plan-testing","bucket_prefix":null,"force_destroy":false,"logging":[{"target_bucket":"arn:aws:s3:::iac-tfsec-dev","target_prefix":null}],"tags":null,"versioning":[{"enabled":true,"mfa_delete":false}]},"sensitive_values":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[{}],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[{}],"website":[]}},{"address":"aws_s3_bucket_server_side_encryption_configuration.example","mode":"managed","type":"aws_s3_bucket_server_side_encryption_configuration","name":"example","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"expected_bucket_owner":null,"rule":[{"apply_server_side_encryption_by_default":[{"kms_master_key_id":"","sse_algorithm":"AES256"}],"bucket_key_enabled":true}]},"sensitive_values":{"rule":[{"apply_server_side_encryption_by_default":[{}]}]}},{"address":"aws_security_group.sg","mode":"managed","type":"aws_security_group","name":"sg","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":1,"values":{"description":"Managed by Terraform","ingress":[{"cidr_blocks":["0.0.0.0/0"],"description":"","from_port":80,"ipv6_cidr_blocks":[],"prefix_list_ids":[],"protocol":"tcp","security_groups":[],"self":false,"to_port":80}],"name":"sg","revoke_rules_on_delete":false,"tags":{"Name":"blah"},"tags_all":{"Name":"blah"},"timeouts":null},"sensitive_values":{"egress":[],"ingress":[{"cidr_blocks":[false],"ipv6_cidr_blocks":[],"prefix_list_ids":[],"security_groups":[]}],"tags":{},"tags_all":{}}}]}},"resource_changes":[{"address":"aws_s3_bucket.planbucket","mode":"managed","type":"aws_s3_bucket","name":"planbucket","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"bucket":"tfsec-plan-testing","bucket_prefix":null,"force_destroy":false,"logging":[{"target_bucket":"arn:aws:s3:::iac-tfsec-dev","target_prefix":null}],"tags":null,"versioning":[{"enabled":true,"mfa_delete":false}]},"after_unknown":{"acceleration_status":true,"acl":true,"arn":true,"bucket_domain_name":true,"bucket_regional_domain_name":true,"cors_rule":true,"grant":true,"hosted_zone_id":true,"id":true,"lifecycle_rule":true,"logging":[{}],"object_lock_configuration":true,"object_lock_enabled":true,"policy":true,"region":true,"replication_configuration":true,"request_payer":true,"server_side_encryption_configuration":true,"tags_all":true,"versioning":[{}],"website":true,"website_domain":true,"website_endpoint":true},"before_sensitive":false,"after_sensitive":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[{}],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[{}],"website":[]}}},{"address":"aws_s3_bucket_server_side_encryption_configuration.example","mode":"managed","type":"aws_s3_bucket_server_side_encryption_configuration","name":"example","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"expected_bucket_owner":null,"rule":[{"apply_server_side_encryption_by_default":[{"kms_master_key_id":"","sse_algorithm":"AES256"}],"bucket_key_enabled":true}]},"after_unknown":{"bucket":true,"id":true,"rule":[{"apply_server_side_encryption_by_default":[{}]}]},"before_sensitive":false,"after_sensitive":{"rule":[{"apply_server_side_encryption_by_default":[{}]}]}}},{"address":"aws_security_group.sg","mode":"managed","type":"aws_security_group","name":"sg","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"description":"Managed by Terraform","ingress":[{"cidr_blocks":["0.0.0.0/0"],"description":"","from_port":80,"ipv6_cidr_blocks":[],"prefix_list_ids":[],"protocol":"tcp","security_groups":[],"self":false,"to_port":80}],"name":"sg","revoke_rules_on_delete":false,"tags":{"Name":"blah"},"tags_all":{"Name":"blah"},"timeouts":null},"after_unknown":{"arn":true,"egress":true,"id":true,"ingress":[{"cidr_blocks":[false],"ipv6_cidr_blocks":[],"prefix_list_ids":[],"security_groups":[]}],"name_prefix":true,"owner_id":true,"tags":{},"tags_all":{},"vpc_id":true},"before_sensitive":false,"after_sensitive":{"egress":[],"ingress":[{"cidr_blocks":[false],"ipv6_cidr_blocks":[],"prefix_list_ids":[],"security_groups":[]}],"tags":{},"tags_all":{}}}}],"prior_state":{"format_version":"0.2","terraform_version":"1.0.3","values":{"root_module":{"resources":[{"address":"data.aws_s3_bucket.logging_bucket","mode":"data","type":"aws_s3_bucket","name":"logging_bucket","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"arn":"arn:aws:s3:::iac-tfsec-dev","bucket":"iac-tfsec-dev","bucket_domain_name":"iac-tfsec-dev.s3.amazonaws.com","bucket_regional_domain_name":"iac-tfsec-dev.s3.amazonaws.com","hosted_zone_id":"Z3AQBSTGFYJSTF","id":"iac-tfsec-dev","region":"us-east-1","website_domain":null,"website_endpoint":null},"sensitive_values":{}}]}}},"configuration":{"provider_config":{"aws":{"name":"aws"}},"root_module":{"resources":[{"address":"aws_s3_bucket.planbucket","mode":"managed","type":"aws_s3_bucket","name":"planbucket","provider_config_key":"aws","expressions":{"bucket":{"references":["var.bucket_name"]},"logging":[{"target_bucket":{"references":["data.aws_s3_bucket.logging_bucket.arn","data.aws_s3_bucket.logging_bucket"]}}],"versioning":[{"enabled":{"constant_value":true}}]},"schema_version":0},{"address":"aws_s3_bucket_server_side_encryption_configuration.example","mode":"managed","type":"aws_s3_bucket_server_side_encryption_configuration","name":"example","provider_config_key":"aws","expressions":{"bucket":{"references":["aws_s3_bucket.planbucket.id","aws_s3_bucket.planbucket"]},"rule":[{"apply_server_side_encryption_by_default":[{"sse_algorithm":{"constant_value":"AES256"}}],"bucket_key_enabled":{"constant_value":true}}]},"schema_version":0},{"address":"aws_security_group.sg","mode":"managed","type":"aws_security_group","name":"sg","provider_config_key":"aws","expressions":{"name":{"constant_value":"sg"},"tags":{"constant_value":{"Name":"blah"}}},"schema_version":1},{"address":"data.aws_s3_bucket.logging_bucket","mode":"data","type":"aws_s3_bucket","name":"logging_bucket","provider_config_key":"aws","expressions":{"bucket":{"constant_value":"iac-tfsec-dev"}},"schema_version":0}],"variables":{"bucket_name":{"default":"tfsec-plan-testing"}}}}} diff --git a/pkg/iac/scanners/terraformplan/tfjson/test/testdata/plan_with_template.json b/pkg/iac/scanners/terraformplan/tfjson/test/testdata/plan_with_template.json deleted file mode 100644 index 2ae6e5c8d7ed..000000000000 --- a/pkg/iac/scanners/terraformplan/tfjson/test/testdata/plan_with_template.json +++ /dev/null @@ -1,480 +0,0 @@ -{ - "format_version": "0.2", - "terraform_version": "1.0.3", - "variables": { - "bucket_name": { - "value": "${template-name-is-$evil}" - } - }, - "planned_values": { - "root_module": { - "resources": [ - { - "address": "aws_s3_bucket.planbucket", - "mode": "managed", - "type": "aws_s3_bucket", - "name": "planbucket", - "provider_name": "registry.terraform.io/hashicorp/aws", - "schema_version": 0, - "values": { - "bucket": "${template-name-is-$evil}", - "bucket_prefix": null, - "force_destroy": false, - "logging": [ - { - "target_bucket": "arn:aws:s3:::iac-tfsec-dev", - "target_prefix": null - } - ], - "tags": null, - "versioning": [ - { - "enabled": true, - "mfa_delete": false - } - ] - }, - "sensitive_values": { - "cors_rule": [], - "grant": [], - "lifecycle_rule": [], - "logging": [ - {} - ], - "object_lock_configuration": [], - "replication_configuration": [], - "server_side_encryption_configuration": [], - "tags_all": {}, - "versioning": [ - {} - ], - "website": [] - } - }, - { - "address": "aws_s3_bucket_server_side_encryption_configuration.example", - "mode": "managed", - "type": "aws_s3_bucket_server_side_encryption_configuration", - "name": "example", - "provider_name": "registry.terraform.io/hashicorp/aws", - "schema_version": 0, - "values": { - "expected_bucket_owner": null, - "rule": [ - { - "apply_server_side_encryption_by_default": [ - { - "kms_master_key_id": "", - "sse_algorithm": "AES256" - } - ], - "bucket_key_enabled": true - } - ] - }, - "sensitive_values": { - "rule": [ - { - "apply_server_side_encryption_by_default": [ - {} - ] - } - ] - } - }, - { - "address": "aws_security_group.sg", - "mode": "managed", - "type": "aws_security_group", - "name": "sg", - "provider_name": "registry.terraform.io/hashicorp/aws", - "schema_version": 1, - "values": { - "description": "Managed by Terraform", - "ingress": [ - { - "cidr_blocks": [ - "0.0.0.0/0" - ], - "description": "", - "from_port": 80, - "ipv6_cidr_blocks": [], - "prefix_list_ids": [], - "protocol": "tcp", - "security_groups": [], - "self": false, - "to_port": 80 - } - ], - "name": "sg", - "revoke_rules_on_delete": false, - "tags": { - "Name": "blah" - }, - "tags_all": { - "Name": "blah" - }, - "timeouts": null - }, - "sensitive_values": { - "egress": [], - "ingress": [ - { - "cidr_blocks": [ - false - ], - "ipv6_cidr_blocks": [], - "prefix_list_ids": [], - "security_groups": [] - } - ], - "tags": {}, - "tags_all": {} - } - } - ] - } - }, - "resource_changes": [ - { - "address": "aws_s3_bucket.planbucket", - "mode": "managed", - "type": "aws_s3_bucket", - "name": "planbucket", - "provider_name": "registry.terraform.io/hashicorp/aws", - "change": { - "actions": [ - "create" - ], - "before": null, - "after": { - "bucket": "${template-name-is-$evil}", - "bucket_prefix": null, - "force_destroy": false, - "logging": [ - { - "target_bucket": "arn:aws:s3:::iac-tfsec-dev", - "target_prefix": null - } - ], - "tags": null, - "versioning": [ - { - "enabled": true, - "mfa_delete": false - } - ] - }, - "after_unknown": { - "acceleration_status": true, - "acl": true, - "arn": true, - "bucket_domain_name": true, - "bucket_regional_domain_name": true, - "cors_rule": true, - "grant": true, - "hosted_zone_id": true, - "id": true, - "lifecycle_rule": true, - "logging": [ - {} - ], - "object_lock_configuration": true, - "object_lock_enabled": true, - "policy": true, - "region": true, - "replication_configuration": true, - "request_payer": true, - "server_side_encryption_configuration": true, - "tags_all": true, - "versioning": [ - {} - ], - "website": true, - "website_domain": true, - "website_endpoint": true - }, - "before_sensitive": false, - "after_sensitive": { - "cors_rule": [], - "grant": [], - "lifecycle_rule": [], - "logging": [ - {} - ], - "object_lock_configuration": [], - "replication_configuration": [], - "server_side_encryption_configuration": [], - "tags_all": {}, - "versioning": [ - {} - ], - "website": [] - } - } - }, - { - "address": "aws_s3_bucket_server_side_encryption_configuration.example", - "mode": "managed", - "type": "aws_s3_bucket_server_side_encryption_configuration", - "name": "example", - "provider_name": "registry.terraform.io/hashicorp/aws", - "change": { - "actions": [ - "create" - ], - "before": null, - "after": { - "expected_bucket_owner": null, - "rule": [ - { - "apply_server_side_encryption_by_default": [ - { - "kms_master_key_id": "", - "sse_algorithm": "AES256" - } - ], - "bucket_key_enabled": true - } - ] - }, - "after_unknown": { - "bucket": true, - "id": true, - "rule": [ - { - "apply_server_side_encryption_by_default": [ - {} - ] - } - ] - }, - "before_sensitive": false, - "after_sensitive": { - "rule": [ - { - "apply_server_side_encryption_by_default": [ - {} - ] - } - ] - } - } - }, - { - "address": "aws_security_group.sg", - "mode": "managed", - "type": "aws_security_group", - "name": "sg", - "provider_name": "registry.terraform.io/hashicorp/aws", - "change": { - "actions": [ - "create" - ], - "before": null, - "after": { - "description": "Managed by Terraform", - "ingress": [ - { - "cidr_blocks": [ - "0.0.0.0/0" - ], - "description": "", - "from_port": 80, - "ipv6_cidr_blocks": [], - "prefix_list_ids": [], - "protocol": "tcp", - "security_groups": [], - "self": false, - "to_port": 80 - } - ], - "name": "sg", - "revoke_rules_on_delete": false, - "tags": { - "Name": "blah" - }, - "tags_all": { - "Name": "blah" - }, - "timeouts": null - }, - "after_unknown": { - "arn": true, - "egress": true, - "id": true, - "ingress": [ - { - "cidr_blocks": [ - false - ], - "ipv6_cidr_blocks": [], - "prefix_list_ids": [], - "security_groups": [] - } - ], - "name_prefix": true, - "owner_id": true, - "tags": {}, - "tags_all": {}, - "vpc_id": true - }, - "before_sensitive": false, - "after_sensitive": { - "egress": [], - "ingress": [ - { - "cidr_blocks": [ - false - ], - "ipv6_cidr_blocks": [], - "prefix_list_ids": [], - "security_groups": [] - } - ], - "tags": {}, - "tags_all": {} - } - } - } - ], - "prior_state": { - "format_version": "0.2", - "terraform_version": "1.0.3", - "values": { - "root_module": { - "resources": [ - { - "address": "data.aws_s3_bucket.logging_bucket", - "mode": "data", - "type": "aws_s3_bucket", - "name": "logging_bucket", - "provider_name": "registry.terraform.io/hashicorp/aws", - "schema_version": 0, - "values": { - "arn": "arn:aws:s3:::iac-tfsec-dev", - "bucket": "iac-tfsec-dev", - "bucket_domain_name": "iac-tfsec-dev.s3.amazonaws.com", - "bucket_regional_domain_name": "iac-tfsec-dev.s3.amazonaws.com", - "hosted_zone_id": "Z3AQBSTGFYJSTF", - "id": "iac-tfsec-dev", - "region": "us-east-1", - "website_domain": null, - "website_endpoint": null - }, - "sensitive_values": {} - } - ] - } - } - }, - "configuration": { - "provider_config": { - "aws": { - "name": "aws" - } - }, - "root_module": { - "resources": [ - { - "address": "aws_s3_bucket.planbucket", - "mode": "managed", - "type": "aws_s3_bucket", - "name": "planbucket", - "provider_config_key": "aws", - "expressions": { - "bucket": { - "references": [ - "var.bucket_name" - ] - }, - "logging": [ - { - "target_bucket": { - "references": [ - "data.aws_s3_bucket.logging_bucket.arn", - "data.aws_s3_bucket.logging_bucket" - ] - } - } - ], - "versioning": [ - { - "enabled": { - "constant_value": true - } - } - ] - }, - "schema_version": 0 - }, - { - "address": "aws_s3_bucket_server_side_encryption_configuration.example", - "mode": "managed", - "type": "aws_s3_bucket_server_side_encryption_configuration", - "name": "example", - "provider_config_key": "aws", - "expressions": { - "bucket": { - "references": [ - "aws_s3_bucket.planbucket.id", - "aws_s3_bucket.planbucket" - ] - }, - "rule": [ - { - "apply_server_side_encryption_by_default": [ - { - "sse_algorithm": { - "constant_value": "AES256" - } - } - ], - "bucket_key_enabled": { - "constant_value": true - } - } - ] - }, - "schema_version": 0 - }, - { - "address": "aws_security_group.sg", - "mode": "managed", - "type": "aws_security_group", - "name": "sg", - "provider_config_key": "aws", - "expressions": { - "name": { - "constant_value": "sg" - }, - "tags": { - "constant_value": { - "Name": "blah" - } - } - }, - "schema_version": 1 - }, - { - "address": "data.aws_s3_bucket.logging_bucket", - "mode": "data", - "type": "aws_s3_bucket", - "name": "logging_bucket", - "provider_config_key": "aws", - "expressions": { - "bucket": { - "constant_value": "iac-tfsec-dev" - } - }, - "schema_version": 0 - } - ], - "variables": { - "bucket_name": { - "default": "${template-name-is-$evil}" - } - } - } - } -} \ No newline at end of file diff --git a/pkg/iac/severity/severity.go b/pkg/iac/severity/severity.go deleted file mode 100755 index 45e9e1740d95..000000000000 --- a/pkg/iac/severity/severity.go +++ /dev/null @@ -1,48 +0,0 @@ -package severity - -import ( - "strings" -) - -type Severity string - -const ( - None Severity = "" - Critical Severity = "CRITICAL" - High Severity = "HIGH" - Medium Severity = "MEDIUM" - Low Severity = "LOW" -) - -var ValidSeverity = []Severity{ - Critical, High, Medium, Low, -} - -func (s *Severity) IsValid() bool { - for _, severity := range ValidSeverity { - if severity == *s { - return true - } - } - return false -} - -func (s *Severity) Valid() []Severity { - return ValidSeverity -} - -func StringToSeverity(sev string) Severity { - s := strings.ToUpper(sev) - switch s { - case "CRITICAL", "HIGH", "MEDIUM", "LOW": - return Severity(s) - case "ERROR": - return High - case "WARNING": - return Medium - case "INFO": - return Low - default: - return None - } -} diff --git a/pkg/iac/state/merge.go b/pkg/iac/state/merge.go deleted file mode 100644 index 16e1275813a8..000000000000 --- a/pkg/iac/state/merge.go +++ /dev/null @@ -1,43 +0,0 @@ -package state - -import ( - "reflect" -) - -// Merge merges the states of the services that have been scanned into a single state. -// if a service has data on both a and b, the service data from b will be preferred. -func (a *State) Merge(b *State) (*State, error) { - var output State - - aVal := reflect.ValueOf(a).Elem() - bVal := reflect.ValueOf(b).Elem() - outputVal := reflect.ValueOf(&output).Elem() - - stateType := reflect.ValueOf(a).Elem().Type() - for i := 0; i < stateType.NumField(); i++ { - field := stateType.Field(i) - if !field.IsExported() { - continue - } - if field.Type.Kind() != reflect.Struct { - continue - } - for j := 0; j < field.Type.NumField(); j++ { - serviceField := field.Type.Field(j) - if !serviceField.IsExported() { - continue - } - if serviceField.Type.Kind() != reflect.Struct { - continue - } - if !bVal.Field(i).Field(j).IsZero() { - outputVal.Field(i).Field(j).Set(bVal.Field(i).Field(j)) - } else { - outputVal.Field(i).Field(j).Set(aVal.Field(i).Field(j)) - } - } - } - - normalised := outputVal.Interface().(State) - return &normalised, nil -} diff --git a/pkg/iac/state/merge_test.go b/pkg/iac/state/merge_test.go deleted file mode 100644 index baf3959f3890..000000000000 --- a/pkg/iac/state/merge_test.go +++ /dev/null @@ -1,340 +0,0 @@ -package state - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ec2" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/rds" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_Merging(t *testing.T) { - tests := []struct { - name string - a, b, expected State - }{ - { - name: "both empty", - }, - { - name: "a empty, b has a service", - b: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - expected: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - }, - { - name: "b empty, a has a service", - a: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - expected: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - }, - { - name: "both have differing versions of same service", - a: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - b: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever:B", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere:B", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere:B", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - expected: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever:B", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere:B", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere:B", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - }, - { - name: "each has a different service", - a: State{ - AWS: aws.AWS{ - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - b: State{ - AWS: aws.AWS{ - EC2: ec2.EC2{ - Instances: []ec2.Instance{ - { - Metadata: iacTypes.Metadata{}, - MetadataOptions: ec2.MetadataOptions{ - Metadata: iacTypes.Metadata{}, - HttpTokens: iacTypes.String("something", iacTypes.Metadata{}), - HttpEndpoint: iacTypes.String("something", iacTypes.Metadata{}), - }, - UserData: iacTypes.String("something", iacTypes.Metadata{}), - SecurityGroups: []ec2.SecurityGroup{ - { - Metadata: iacTypes.Metadata{}, - IsDefault: iacTypes.Bool(true, iacTypes.Metadata{}), - Description: iacTypes.String("something", iacTypes.Metadata{}), - IngressRules: []ec2.SecurityGroupRule{ - { - Metadata: iacTypes.Metadata{}, - Description: iacTypes.String("something", iacTypes.Metadata{}), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("something", iacTypes.Metadata{}), - }, - }, - }, - EgressRules: nil, - VPCID: iacTypes.String("something", iacTypes.Metadata{}), - }, - }, - RootBlockDevice: &ec2.BlockDevice{ - Metadata: iacTypes.Metadata{}, - Encrypted: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - EBSBlockDevices: []*ec2.BlockDevice{ - { - Metadata: iacTypes.Metadata{}, - Encrypted: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - }, - }, - expected: State{ - AWS: aws.AWS{ - EC2: ec2.EC2{ - Instances: []ec2.Instance{ - { - Metadata: iacTypes.Metadata{}, - MetadataOptions: ec2.MetadataOptions{ - Metadata: iacTypes.Metadata{}, - HttpTokens: iacTypes.String("something", iacTypes.Metadata{}), - HttpEndpoint: iacTypes.String("something", iacTypes.Metadata{}), - }, - UserData: iacTypes.String("something", iacTypes.Metadata{}), - SecurityGroups: []ec2.SecurityGroup{ - { - Metadata: iacTypes.Metadata{}, - IsDefault: iacTypes.Bool(true, iacTypes.Metadata{}), - Description: iacTypes.String("something", iacTypes.Metadata{}), - IngressRules: []ec2.SecurityGroupRule{ - { - Metadata: iacTypes.Metadata{}, - Description: iacTypes.String("something", iacTypes.Metadata{}), - CIDRs: []iacTypes.StringValue{ - iacTypes.String("something", iacTypes.Metadata{}), - }, - }, - }, - EgressRules: nil, - VPCID: iacTypes.String("something", iacTypes.Metadata{}), - }, - }, - RootBlockDevice: &ec2.BlockDevice{ - Metadata: iacTypes.Metadata{}, - Encrypted: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - EBSBlockDevices: []*ec2.BlockDevice{ - { - Metadata: iacTypes.Metadata{}, - Encrypted: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - RDS: rds.RDS{ - Instances: []rds.Instance{ - { - BackupRetentionPeriodDays: iacTypes.Int(1, iacTypes.Metadata{}), - ReplicationSourceARN: iacTypes.String("arn:whatever", iacTypes.Metadata{}), - PerformanceInsights: rds.PerformanceInsights{ - Metadata: iacTypes.Metadata{}, - Enabled: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - Encryption: rds.Encryption{ - Metadata: iacTypes.Metadata{}, - EncryptStorage: iacTypes.Bool(true, iacTypes.Metadata{}), - KMSKeyID: iacTypes.String("keyidhere", iacTypes.Metadata{}), - }, - PublicAccess: iacTypes.Bool(true, iacTypes.Metadata{}), - }, - }, - }, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - state := test.b - actual, err := test.a.Merge(&state) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, test.expected, *actual) - }) - } - -} diff --git a/pkg/iac/state/state.go b/pkg/iac/state/state.go deleted file mode 100755 index bf04db71aa54..000000000000 --- a/pkg/iac/state/state.go +++ /dev/null @@ -1,34 +0,0 @@ -package state - -import ( - "reflect" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/providers/azure" - "github.com/aquasecurity/trivy/pkg/iac/providers/cloudstack" - "github.com/aquasecurity/trivy/pkg/iac/providers/digitalocean" - "github.com/aquasecurity/trivy/pkg/iac/providers/github" - "github.com/aquasecurity/trivy/pkg/iac/providers/google" - "github.com/aquasecurity/trivy/pkg/iac/providers/kubernetes" - "github.com/aquasecurity/trivy/pkg/iac/providers/nifcloud" - "github.com/aquasecurity/trivy/pkg/iac/providers/openstack" - "github.com/aquasecurity/trivy/pkg/iac/providers/oracle" - "github.com/aquasecurity/trivy/pkg/iac/rego/convert" -) - -type State struct { - AWS aws.AWS - Azure azure.Azure - CloudStack cloudstack.CloudStack - DigitalOcean digitalocean.DigitalOcean - GitHub github.GitHub - Google google.Google - Kubernetes kubernetes.Kubernetes - OpenStack openstack.OpenStack - Oracle oracle.Oracle - Nifcloud nifcloud.Nifcloud -} - -func (a *State) ToRego() any { - return convert.StructToRego(reflect.ValueOf(a)) -} diff --git a/pkg/iac/state/state_test.go b/pkg/iac/state/state_test.go deleted file mode 100644 index 46de9b506d4b..000000000000 --- a/pkg/iac/state/state_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package state - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/s3" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -func Test_RegoConversion(t *testing.T) { - s := State{ - AWS: aws.AWS{ - S3: s3.S3{ - Buckets: []s3.Bucket{ - { - Metadata: iacTypes.NewMetadata( - iacTypes.NewRange("main.tf", 2, 4, "", nil), - "aws_s3_bucket.example", - ), - Name: iacTypes.String("my-bucket", iacTypes.NewMetadata( - iacTypes.NewRange("main.tf", 3, 3, "", nil), - "aws_s3_bucket.example.bucket", - )), - }, - }, - }, - }, - } - converted := s.ToRego() - assert.Equal(t, map[string]any{ - "aws": map[string]any{ - "s3": map[string]any{ - "buckets": []any{ - map[string]any{ - "__defsec_metadata": map[string]any{ - "resource": "aws_s3_bucket.example", - "sourceprefix": "", - "filepath": "main.tf", - "startline": 2, - "endline": 4, - "managed": true, - "unresolvable": false, - "explicit": false, - "fskey": "", - }, - "name": map[string]any{ - "resource": "aws_s3_bucket.example.bucket", - "sourceprefix": "", - "filepath": "main.tf", - "startline": 3, - "endline": 3, - "value": "my-bucket", - "managed": true, - "unresolvable": false, - "explicit": false, - "fskey": "", - }, - }, - }, - }, - }, - }, converted) -} - -func Test_JSONPersistenceOfData(t *testing.T) { - s := State{ - AWS: aws.AWS{ - S3: s3.S3{ - Buckets: []s3.Bucket{ - { - Metadata: iacTypes.NewMetadata( - iacTypes.NewRange("main.tf", 2, 4, "", nil), - "aws_s3_bucket.example", - ), - Name: iacTypes.String("my-bucket", iacTypes.NewMetadata( - iacTypes.NewRange("main.tf", 3, 3, "", nil), - "aws_s3_bucket.example.bucket", - )), - }, - }, - }, - }, - } - data, err := json.Marshal(s) - require.NoError(t, err) - - var restored State - require.NoError(t, json.Unmarshal(data, &restored)) - - assert.Equal(t, s, restored) -} diff --git a/pkg/iac/terraform/attribute.go b/pkg/iac/terraform/attribute.go deleted file mode 100644 index 8462492fce05..000000000000 --- a/pkg/iac/terraform/attribute.go +++ /dev/null @@ -1,1112 +0,0 @@ -package terraform - -import ( - "fmt" - "io/fs" - "reflect" - "regexp" - "strconv" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/ext/typeexpr" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" - - "github.com/aquasecurity/trivy/pkg/iac/terraform/context" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Attribute struct { - hclAttribute *hcl.Attribute - module string - ctx *context.Context - metadata iacTypes.Metadata - reference Reference -} - -func (a *Attribute) DecodeVarType() (cty.Type, *typeexpr.Defaults, error) { - // Special-case the shortcuts for list(any) and map(any) which aren't hcl. - switch hcl.ExprAsKeyword(a.hclAttribute.Expr) { - case "list": - return cty.List(cty.DynamicPseudoType), nil, nil - case "map": - return cty.Map(cty.DynamicPseudoType), nil, nil - } - - t, def, diag := typeexpr.TypeConstraintWithDefaults(a.hclAttribute.Expr) - if diag.HasErrors() { - return cty.NilType, nil, diag - } - return t, def, nil -} - -func NewAttribute(attr *hcl.Attribute, ctx *context.Context, module string, parent iacTypes.Metadata, parentRef Reference, moduleSource string, moduleFS fs.FS) *Attribute { - rng := iacTypes.NewRange( - attr.Range.Filename, - attr.Range.Start.Line, - attr.Range.End.Line, - moduleSource, - moduleFS, - ) - reference := extendReference(parentRef, attr.Name) - metadata := iacTypes.NewMetadata(rng, reference.String()) - return &Attribute{ - hclAttribute: attr, - ctx: ctx, - module: module, - metadata: metadata.WithParent(parent), - reference: reference, - } -} - -func (a *Attribute) HCLAttribute() *hcl.Attribute { - return a.hclAttribute -} - -func (a *Attribute) GetMetadata() iacTypes.Metadata { - return a.metadata -} - -func (a *Attribute) GetRawValue() any { - switch typ := a.Type(); typ { - case cty.String: - return a.Value().AsString() - case cty.Bool: - return a.Value().True() - case cty.Number: - float, _ := a.Value().AsBigFloat().Float64() - return float - default: - switch { - case typ.IsTupleType(), typ.IsListType(), typ.IsSetType(): - values := a.Value().AsValueSlice() - if len(values) == 0 { - return []string{} - } - switch values[0].Type() { - case cty.String: - var output []string - for _, value := range values { - output = append(output, value.AsString()) - } - return output - case cty.Number: - var output []float64 - for _, value := range values { - bf := value.AsBigFloat() - f, _ := bf.Float64() - output = append(output, f) - } - return output - case cty.Bool: - var output []bool - for _, value := range values { - output = append(output, value.True()) - } - return output - } - } - } - return nil -} - -func (a *Attribute) AsBytesValueOrDefault(defaultValue []byte, parent *Block) iacTypes.BytesValue { - if a.IsNil() { - return iacTypes.BytesDefault(defaultValue, parent.GetMetadata()) - } - if a.IsNotResolvable() || !a.IsString() { - return iacTypes.BytesUnresolvable(a.GetMetadata()) - } - return iacTypes.BytesExplicit( - []byte(a.Value().AsString()), - a.GetMetadata(), - ) -} - -func (a *Attribute) AsStringValueOrDefault(defaultValue string, parent *Block) iacTypes.StringValue { - if a.IsNil() { - return iacTypes.StringDefault(defaultValue, parent.GetMetadata()) - } - if a.IsNotResolvable() || !a.IsString() { - return iacTypes.StringUnresolvable(a.GetMetadata()) - } - return iacTypes.StringExplicit( - a.Value().AsString(), - a.GetMetadata(), - ) -} - -func (a *Attribute) AsStringValueSliceOrEmpty() (stringValues []iacTypes.StringValue) { - if a.IsNil() { - return stringValues - } - return a.AsStringValues() -} - -func (a *Attribute) AsStringValuesOrDefault(parent *Block, defaults ...string) []iacTypes.StringValue { - if a.IsNil() { - res := make(iacTypes.StringValueList, 0, len(defaults)) - for _, def := range defaults { - res = append(res, iacTypes.StringDefault(def, parent.GetMetadata())) - } - return res - } - return a.AsStringValues() -} - -func (a *Attribute) AsBoolValueOrDefault(defaultValue bool, parent *Block) iacTypes.BoolValue { - if a.IsNil() { - return iacTypes.BoolDefault(defaultValue, parent.GetMetadata()) - } - if a.IsNotResolvable() || !a.IsBool() { - return iacTypes.BoolUnresolvable(a.GetMetadata()) - } - return iacTypes.BoolExplicit( - a.IsTrue(), - a.GetMetadata(), - ) -} - -func (a *Attribute) AsIntValueOrDefault(defaultValue int, parent *Block) iacTypes.IntValue { - if a.IsNil() { - return iacTypes.IntDefault(defaultValue, parent.GetMetadata()) - } - if a.IsNotResolvable() || !a.IsNumber() { - return iacTypes.IntUnresolvable(a.GetMetadata()) - } - flt := a.AsNumber() - return iacTypes.IntExplicit( - int(flt), - a.GetMetadata(), - ) -} - -func (a *Attribute) IsLiteral() bool { - if a == nil { - return false - } - return len(a.hclAttribute.Expr.Variables()) == 0 -} - -func (a *Attribute) IsResolvable() bool { - if a == nil { - return false - } - return a.Value() != cty.NilVal && a.Value().IsKnown() -} - -func (a *Attribute) IsNotResolvable() bool { - return !a.IsResolvable() -} - -func (a *Attribute) Type() cty.Type { - if a == nil { - return cty.NilType - } - return a.Value().Type() -} - -func (a *Attribute) IsIterable() bool { - if a == nil { - return false - } - return a.Value().Type().IsListType() || a.Value().Type().IsCollectionType() || a.Value().Type().IsObjectType() || a.Value().Type().IsMapType() || a.Value().Type().IsListType() || a.Value().Type().IsSetType() || a.Value().Type().IsTupleType() -} - -func (a *Attribute) Each(f func(key cty.Value, val cty.Value)) error { - if a == nil { - return nil - } - var outerErr error - defer func() { - if err := recover(); err != nil { - outerErr = fmt.Errorf("go-cty bug detected - cannot call ForEachElement: %s", err) - } - }() - val := a.Value() - val.ForEachElement(func(key cty.Value, val cty.Value) (stop bool) { - f(key, val) - return false - }) - return outerErr -} - -func (a *Attribute) IsString() bool { - if a == nil { - return false - } - return !a.Value().IsNull() && a.Value().IsKnown() && a.Value().Type() == cty.String -} - -func (a *Attribute) IsMapOrObject() bool { - if a == nil || a.Value().IsNull() || !a.Value().IsKnown() { - return false - } - - return a.Value().Type().IsObjectType() || a.Value().Type().IsMapType() -} - -func (a *Attribute) IsNumber() bool { - if a != nil && !a.Value().IsNull() && a.Value().IsKnown() { - if a.Value().Type() == cty.Number { - return true - } - if a.Value().Type() == cty.String { - _, err := strconv.ParseFloat(a.Value().AsString(), 64) - return err == nil - } - } - - return false -} - -func (a *Attribute) IsBool() bool { - if a == nil { - return false - } - switch a.Value().Type() { - case cty.Bool, cty.Number: - return true - case cty.String: - val := a.Value().AsString() - val = strings.Trim(val, "\"") - return strings.EqualFold(val, "false") || strings.EqualFold(val, "true") - } - return false -} - -func (a *Attribute) Value() (ctyVal cty.Value) { - if a == nil { - return cty.NilVal - } - defer func() { - if err := recover(); err != nil { - ctyVal = cty.NilVal - } - }() - ctyVal, _ = a.hclAttribute.Expr.Value(a.ctx.Inner()) - if !ctyVal.IsKnown() || ctyVal.IsNull() { - return cty.NilVal - } - return ctyVal -} - -// Allows a null value for a variable https://developer.hashicorp.com/terraform/language/expressions/types#null -func (a *Attribute) NullableValue() (ctyVal cty.Value) { - if a == nil { - return cty.NilVal - } - defer func() { - if err := recover(); err != nil { - ctyVal = cty.NilVal - } - }() - ctyVal, _ = a.hclAttribute.Expr.Value(a.ctx.Inner()) - if !ctyVal.IsKnown() { - return cty.NilVal - } - return ctyVal -} - -func (a *Attribute) Name() string { - if a == nil { - return "" - } - return a.hclAttribute.Name -} - -func (a *Attribute) AsStringValues() iacTypes.StringValueList { - if a == nil { - return nil - } - return a.getStringValues(a.hclAttribute.Expr, a.ctx.Inner()) -} - -// nolint -func (a *Attribute) getStringValues(expr hcl.Expression, ctx *hcl.EvalContext) (results []iacTypes.StringValue) { - - defer func() { - if err := recover(); err != nil { - results = []iacTypes.StringValue{iacTypes.StringUnresolvable(a.metadata)} - } - }() - - switch t := expr.(type) { - case *hclsyntax.TupleConsExpr: - for _, expr := range t.Exprs { - val, err := expr.Value(a.ctx.Inner()) - if err != nil { - results = append(results, iacTypes.StringUnresolvable(a.metadata)) - continue - } - results = append(results, a.valueToString(val)) - } - case *hclsyntax.FunctionCallExpr, *hclsyntax.ConditionalExpr: - subVal, err := t.Value(ctx) - if err != nil { - return append(results, iacTypes.StringUnresolvable(a.metadata)) - } - return a.valueToStrings(subVal) - case *hclsyntax.LiteralValueExpr: - return a.valueToStrings(t.Val) - case *hclsyntax.TemplateExpr: - // walk the parts of the expression to ensure that it has a literal value - for _, p := range t.Parts { - val, err := p.Value(a.ctx.Inner()) - if err != nil { - results = append(results, iacTypes.StringUnresolvable(a.metadata)) - continue - } - value := a.valueToString(val) - results = append(results, value) - } - case *hclsyntax.ScopeTraversalExpr: - // handle the case for referencing a data - if len(t.Variables()) > 0 { - if t.Variables()[0].RootName() == "data" { - // we can't resolve data lookups at this time, so make unresolvable - return append(results, iacTypes.StringUnresolvable(a.metadata)) - } - } - subVal, err := t.Value(ctx) - if err != nil { - return append(results, iacTypes.StringUnresolvable(a.metadata)) - } - return a.valueToStrings(subVal) - default: - val, err := t.Value(a.ctx.Inner()) - if err != nil { - return append(results, iacTypes.StringUnresolvable(a.metadata)) - } - results = a.valueToStrings(val) - } - return results -} - -func (a *Attribute) valueToStrings(value cty.Value) (results []iacTypes.StringValue) { - defer func() { - if err := recover(); err != nil { - results = []iacTypes.StringValue{iacTypes.StringUnresolvable(a.metadata)} - } - }() - if value.IsNull() { - return []iacTypes.StringValue{iacTypes.StringUnresolvable(a.metadata)} - } - if !value.IsKnown() { - return []iacTypes.StringValue{iacTypes.StringUnresolvable(a.metadata)} - } - if value.Type().IsListType() || value.Type().IsTupleType() || value.Type().IsSetType() { - for _, val := range value.AsValueSlice() { - results = append(results, a.valueToString(val)) - } - } - return results -} - -func (a *Attribute) valueToString(value cty.Value) (result iacTypes.StringValue) { - defer func() { - if err := recover(); err != nil { - result = iacTypes.StringUnresolvable(a.metadata) - } - }() - - result = iacTypes.StringUnresolvable(a.metadata) - - if value.IsNull() || !value.IsKnown() { - return result - } - - switch value.Type() { - case cty.String: - return iacTypes.String(value.AsString(), a.metadata) - default: - return result - } -} - -func (a *Attribute) listContains(val cty.Value, stringToLookFor string, ignoreCase bool) bool { - if a == nil { - return false - } - - valueSlice := val.AsValueSlice() - for _, value := range valueSlice { - if value.IsNull() || !value.IsKnown() { - // there is nothing we can do with this value - continue - } - stringToTest := value - if value.Type().IsObjectType() || value.Type().IsMapType() { - valueMap := value.AsValueMap() - stringToTest = valueMap["key"] - } - if value.Type().HasDynamicTypes() { - for _, extracted := range a.extractListValues() { - if extracted == stringToLookFor { - return true - } - } - return false - } - if !value.IsKnown() { - continue - } - if ignoreCase && strings.EqualFold(stringToTest.AsString(), stringToLookFor) { - return true - } - if stringToTest.AsString() == stringToLookFor { - return true - } - } - return false -} - -func (a *Attribute) extractListValues() []string { - var values []string - if a.hclAttribute == nil || a.hclAttribute.Expr == nil || a.hclAttribute.Expr.Variables() == nil { - return values - } - for _, v := range a.hclAttribute.Expr.Variables() { - values = append(values, v.RootName()) - } - return values -} - -func (a *Attribute) mapContains(checkValue any, val cty.Value) bool { - if a == nil { - return false - } - valueMap := val.AsValueMap() - switch t := checkValue.(type) { - case map[any]any: - for k, v := range t { - for key, value := range valueMap { - rawValue := getRawValue(value) - if key == k && evaluate(v, rawValue) { - return true - } - } - } - return false - case map[string]any: - for k, v := range t { - for key, value := range valueMap { - rawValue := getRawValue(value) - if key == k && evaluate(v, rawValue) { - return true - } - } - } - return false - default: - for key := range valueMap { - if key == checkValue { - return true - } - } - return false - } -} - -func (a *Attribute) NotContains(checkValue any, equalityOptions ...EqualityOption) bool { - return !a.Contains(checkValue, equalityOptions...) -} - -func (a *Attribute) Contains(checkValue any, equalityOptions ...EqualityOption) bool { - if a == nil { - return false - } - ignoreCase := false - for _, option := range equalityOptions { - if option == IgnoreCase { - ignoreCase = true - } - } - val := a.Value() - if val.IsNull() { - return false - } - - if val.Type().IsObjectType() || val.Type().IsMapType() { - return a.mapContains(checkValue, val) - } - - stringToLookFor := fmt.Sprintf("%v", checkValue) - - if val.Type().IsListType() || val.Type().IsTupleType() { - return a.listContains(val, stringToLookFor, ignoreCase) - } - - if ignoreCase && containsIgnoreCase(val.AsString(), stringToLookFor) { - return true - } - - return strings.Contains(val.AsString(), stringToLookFor) -} - -func (a *Attribute) OnlyContains(checkValue any) bool { - if a == nil { - return false - } - val := a.Value() - if val.IsNull() { - return false - } - - checkSlice, ok := checkValue.([]any) - if !ok { - return false - } - - if val.Type().IsListType() || val.Type().IsTupleType() { - for _, value := range val.AsValueSlice() { - found := false - for _, cVal := range checkSlice { - switch t := cVal.(type) { - case string: - if t == value.AsString() { - found = true - break - } - case bool: - if t == value.True() { - found = true - break - } - case int, int8, int16, int32, int64: - i, _ := value.AsBigFloat().Int64() - if t == i { - found = true - break - } - case float32, float64: - f, _ := value.AsBigFloat().Float64() - if t == f { - found = true - break - } - } - - } - if !found { - return false - } - } - return true - } - - return false -} - -func containsIgnoreCase(left, substring string) bool { - return strings.Contains(strings.ToLower(left), strings.ToLower(substring)) -} - -func (a *Attribute) StartsWith(prefix any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - return strings.HasPrefix(a.Value().AsString(), fmt.Sprintf("%v", prefix)) - } - return false -} - -func (a *Attribute) EndsWith(suffix any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - return strings.HasSuffix(a.Value().AsString(), fmt.Sprintf("%v", suffix)) - } - return false -} - -type EqualityOption int - -const ( - IgnoreCase EqualityOption = iota -) - -func (a *Attribute) Equals(checkValue any, equalityOptions ...EqualityOption) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - for _, option := range equalityOptions { - if option == IgnoreCase { - return strings.EqualFold(strings.ToLower(a.Value().AsString()), strings.ToLower(fmt.Sprintf("%v", checkValue))) - } - } - result := strings.EqualFold(a.Value().AsString(), fmt.Sprintf("%v", checkValue)) - return result - } - if a.Value().Type() == cty.Bool { - return a.Value().True() == checkValue - } - if a.Value().Type() == cty.Number { - checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) - if err != nil { - return false - } - return a.Value().RawEquals(checkNumber) - } - - return false -} - -func (a *Attribute) NotEqual(checkValue any, equalityOptions ...EqualityOption) bool { - return !a.Equals(checkValue, equalityOptions...) -} - -func (a *Attribute) RegexMatches(re regexp.Regexp) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - match := re.MatchString(a.Value().AsString()) - return match - } - return false -} - -func (a *Attribute) IsNotAny(options ...any) bool { - return !a.IsAny(options...) -} - -func (a *Attribute) IsAny(options ...any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - value := a.Value().AsString() - for _, option := range options { - if option == value { - return true - } - } - } - if a.Value().Type() == cty.Number { - for _, option := range options { - checkValue, err := gocty.ToCtyValue(option, cty.Number) - if err != nil { - return false - } - if a.Value().RawEquals(checkValue) { - return true - } - } - } - return false -} - -func (a *Attribute) IsNone(options ...any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - for _, option := range options { - if option == a.Value().AsString() { - return false - } - } - } - if a.Value().Type() == cty.Number { - for _, option := range options { - checkValue, err := gocty.ToCtyValue(option, cty.Number) - if err != nil { - return false - } - if a.Value().RawEquals(checkValue) { - return false - } - - } - } - - return true -} - -func (a *Attribute) IsTrue() bool { - if a == nil { - return false - } - val := a.Value() - switch val.Type() { - case cty.Bool: - return val.True() - case cty.String: - val := val.AsString() - val = strings.Trim(val, "\"") - return strings.EqualFold(val, "true") - case cty.Number: - val := val.AsBigFloat() - f, _ := val.Float64() - return f > 0 - } - return false -} - -func (a *Attribute) IsFalse() bool { - if a == nil { - return false - } - switch a.Value().Type() { - case cty.Bool: - return a.Value().False() - case cty.String: - val := a.Value().AsString() - val = strings.Trim(val, "\"") - return strings.EqualFold(val, "false") - case cty.Number: - val := a.Value().AsBigFloat() - f, _ := val.Float64() - return f == 0 - } - return false -} - -func (a *Attribute) IsEmpty() bool { - if a == nil { - return false - } - if a.Value().Type() == cty.String { - return a.Value().AsString() == "" - } - if a.Type().IsListType() || a.Type().IsTupleType() { - return len(a.Value().AsValueSlice()) == 0 - } - if a.Type().IsMapType() || a.Type().IsObjectType() { - return len(a.Value().AsValueMap()) == 0 - } - if a.Value().Type() == cty.Number { - // a number can't ever be empty - return false - } - if a.Value().IsNull() { - return a.isNullAttributeEmpty() - } - return true -} - -func (a *Attribute) IsNotEmpty() bool { - return !a.IsEmpty() -} - -func (a *Attribute) isNullAttributeEmpty() bool { - if a == nil { - return false - } - switch t := a.hclAttribute.Expr.(type) { - case *hclsyntax.FunctionCallExpr, *hclsyntax.ScopeTraversalExpr, - *hclsyntax.ConditionalExpr, *hclsyntax.LiteralValueExpr: - return false - case *hclsyntax.TemplateExpr: - // walk the parts of the expression to ensure that it has a literal value - for _, p := range t.Parts { - switch pt := p.(type) { - case *hclsyntax.LiteralValueExpr: - if pt != nil && !pt.Val.IsNull() { - return false - } - case *hclsyntax.ScopeTraversalExpr: - return false - } - } - } - return true -} - -func (a *Attribute) MapValue(mapKey string) cty.Value { - if a == nil { - return cty.NilVal - } - if a.Type().IsObjectType() || a.Type().IsMapType() { - attrMap := a.Value().AsValueMap() - for key, value := range attrMap { - if key == mapKey { - return value - } - } - } - return cty.NilVal -} - -func (a *Attribute) AsMapValue() iacTypes.MapValue { - if a.IsNil() || a.IsNotResolvable() || !a.IsMapOrObject() { - return iacTypes.MapValue{} - } - - values := make(map[string]string) - _ = a.Each(func(key, val cty.Value) { - if key.Type() == cty.String && val.Type() == cty.String { - values[key.AsString()] = val.AsString() - } - }) - - return iacTypes.Map(values, a.GetMetadata()) -} - -func (a *Attribute) LessThan(checkValue any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.Number { - checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) - if err != nil { - return false - } - - return a.Value().LessThan(checkNumber).True() - } - return false -} - -func (a *Attribute) LessThanOrEqualTo(checkValue any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.Number { - checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) - if err != nil { - return false - } - - return a.Value().LessThanOrEqualTo(checkNumber).True() - } - return false -} - -func (a *Attribute) GreaterThan(checkValue any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.Number { - checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) - if err != nil { - return false - } - - return a.Value().GreaterThan(checkNumber).True() - } - return false -} - -func (a *Attribute) GreaterThanOrEqualTo(checkValue any) bool { - if a == nil { - return false - } - if a.Value().Type() == cty.Number { - checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) - if err != nil { - return false - } - - return a.Value().GreaterThanOrEqualTo(checkNumber).True() - } - return false -} - -func (a *Attribute) IsDataBlockReference() bool { - if a == nil { - return false - } - if t, ok := a.hclAttribute.Expr.(*hclsyntax.ScopeTraversalExpr); ok { - split := t.Traversal.SimpleSplit() - return split.Abs.RootName() == "data" - } - return false -} - -func createDotReferenceFromTraversal(parentRef string, traversals ...hcl.Traversal) (*Reference, error) { - var refParts []string - var key cty.Value - for _, x := range traversals { - for _, p := range x { - switch part := p.(type) { - case hcl.TraverseRoot: - refParts = append(refParts, part.Name) - case hcl.TraverseAttr: - refParts = append(refParts, part.Name) - case hcl.TraverseIndex: - key = part.Key - } - } - } - ref, err := newReference(refParts, parentRef) - if err != nil { - return nil, err - } - ref.SetKey(key) - return ref, nil -} - -func (a *Attribute) ReferencesBlock(b *Block) bool { - if a == nil { - return false - } - for _, ref := range a.AllReferences() { - if ref.RefersTo(b.reference) { - return true - } - } - return false -} - -// nolint -func (a *Attribute) AllReferences(blocks ...*Block) []*Reference { - if a == nil { - return nil - } - refs := a.extractReferences() - for _, block := range blocks { - for _, ref := range refs { - if ref.TypeLabel() == "each" && block.HasChild("for_each") { - refs = append(refs, block.GetAttribute("for_each").AllReferences()...) - } - } - } - return refs -} - -// nolint -func (a *Attribute) referencesFromExpression(expression hcl.Expression) []*Reference { - var refs []*Reference - switch t := expression.(type) { - case *hclsyntax.ConditionalExpr: - if ref, err := createDotReferenceFromTraversal(a.module, t.TrueResult.Variables()...); err == nil { - refs = append(refs, ref) - } - if ref, err := createDotReferenceFromTraversal(a.module, t.FalseResult.Variables()...); err == nil { - refs = append(refs, ref) - } - if ref, err := createDotReferenceFromTraversal(a.module, t.Condition.Variables()...); err == nil { - refs = append(refs, ref) - } - case *hclsyntax.ScopeTraversalExpr: - if ref, err := createDotReferenceFromTraversal(a.module, t.Variables()...); err == nil { - refs = append(refs, ref) - } - case *hclsyntax.TemplateWrapExpr: - refs = a.referencesFromExpression(t.Wrapped) - case *hclsyntax.TemplateExpr: - for _, part := range t.Parts { - ref, err := createDotReferenceFromTraversal(a.module, part.Variables()...) - if err != nil { - continue - } - refs = append(refs, ref) - } - case *hclsyntax.TupleConsExpr: - for _, v := range t.Variables() { - if ref, err := createDotReferenceFromTraversal(a.module, v); err == nil { - refs = append(refs, ref) - } - } - case *hclsyntax.RelativeTraversalExpr: - switch s := t.Source.(type) { - case *hclsyntax.IndexExpr: - if collectionRef, err := createDotReferenceFromTraversal(a.module, s.Collection.Variables()...); err == nil { - key, _ := s.Key.Value(a.ctx.Inner()) - collectionRef.SetKey(key) - refs = append(refs, collectionRef) - } - default: - if ref, err := createDotReferenceFromTraversal(a.module, t.Source.Variables()...); err == nil { - refs = append(refs, ref) - } - } - default: - if reflect.TypeOf(expression).String() == "*json.expression" { - if ref, err := createDotReferenceFromTraversal(a.module, expression.Variables()...); err == nil { - refs = append(refs, ref) - } - } - } - return refs -} - -func (a *Attribute) extractReferences() []*Reference { - if a == nil { - return nil - } - return a.referencesFromExpression(a.hclAttribute.Expr) -} - -func (a *Attribute) IsResourceBlockReference(resourceType string) bool { - if a == nil { - return false - } - if t, ok := a.hclAttribute.Expr.(*hclsyntax.ScopeTraversalExpr); ok { - split := t.Traversal.SimpleSplit() - return split.Abs.RootName() == resourceType - } - return false -} - -func (a *Attribute) References(r Reference) bool { - if a == nil { - return false - } - for _, ref := range a.AllReferences() { - if ref.RefersTo(r) { - return true - } - } - return false -} - -func getRawValue(value cty.Value) any { - if value.IsNull() || !value.IsKnown() { - return value - } - - typeName := value.Type().FriendlyName() - - switch typeName { - case "string": - return value.AsString() - case "number": - return value.AsBigFloat() - case "bool": - return value.True() - } - - return value -} - -func (a *Attribute) IsNil() bool { - return a == nil -} - -func (a *Attribute) IsNotNil() bool { - return !a.IsNil() -} - -func (a *Attribute) HasIntersect(checkValues ...any) bool { - if !a.Type().IsListType() && !a.Type().IsTupleType() { - return false - } - - for _, item := range checkValues { - if a.Contains(item) { - return true - } - } - return false - -} - -func (a *Attribute) AsNumber() float64 { - if a.Value().Type() == cty.Number { - v, _ := a.Value().AsBigFloat().Float64() - return v - } - if a.Value().Type() == cty.String { - v, _ := strconv.ParseFloat(a.Value().AsString(), 64) - return v - } - panic("Attribute is not a number") -} diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go deleted file mode 100644 index ec307e44dff9..000000000000 --- a/pkg/iac/terraform/block.go +++ /dev/null @@ -1,724 +0,0 @@ -package terraform - -import ( - "errors" - "fmt" - "io/fs" - "strconv" - "strings" - - "github.com/google/uuid" - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" - - "github.com/aquasecurity/trivy/pkg/iac/terraform/context" - iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Block struct { - id string - hclBlock *hcl.Block - context *context.Context - moduleBlock *Block - parentBlock *Block - expanded bool - cloneIndex int - childBlocks []*Block - attributes []*Attribute - metadata iacTypes.Metadata - moduleSource string - moduleFS fs.FS - reference Reference -} - -func NewBlock(hclBlock *hcl.Block, ctx *context.Context, moduleBlock *Block, parentBlock *Block, moduleSource string, - moduleFS fs.FS, index ...cty.Value) *Block { - if ctx == nil { - ctx = context.NewContext(&hcl.EvalContext{}, nil) - } - - var r hcl.Range - switch body := hclBlock.Body.(type) { - case *hclsyntax.Body: - r = body.SrcRange - default: - r = hclBlock.DefRange - r.End = hclBlock.Body.MissingItemRange().End - } - moduleName := "root" - if moduleBlock != nil { - moduleName = moduleBlock.FullName() - } - rng := iacTypes.NewRange( - r.Filename, - r.Start.Line, - r.End.Line, - moduleSource, - moduleFS, - ) - - var parts []string - // if there are no labels then use the block type - // this is for the case where "special" keywords like "resource" are used - // as normal block names in top level blocks - see issue tfsec#1528 for an example - if hclBlock.Type != "resource" || len(hclBlock.Labels) == 0 { - parts = append(parts, hclBlock.Type) - } - parts = append(parts, hclBlock.Labels...) - - var parent string - if moduleBlock != nil { - parent = moduleBlock.FullName() - } - ref, _ := newReference(parts, parent) - if len(index) > 0 { - key := index[0] - ref.SetKey(key) - } - - metadata := iacTypes.NewMetadata(rng, ref.String()) - - if parentBlock != nil { - metadata = metadata.WithParent(parentBlock.metadata) - } else if moduleBlock != nil { - metadata = metadata.WithParent(moduleBlock.GetMetadata()) - } - - b := Block{ - id: uuid.NewString(), - context: ctx, - hclBlock: hclBlock, - moduleBlock: moduleBlock, - moduleSource: moduleSource, - moduleFS: moduleFS, - parentBlock: parentBlock, - metadata: metadata, - reference: *ref, - } - - var children Blocks - switch body := hclBlock.Body.(type) { - case *hclsyntax.Body: - for _, b2 := range body.Blocks { - children = append(children, NewBlock(b2.AsHCLBlock(), ctx, moduleBlock, &b, moduleSource, moduleFS)) - } - default: - content, _, diag := hclBlock.Body.PartialContent(Schema) - if diag == nil { - for _, hb := range content.Blocks { - children = append(children, NewBlock(hb, ctx, moduleBlock, &b, moduleSource, moduleFS)) - } - } - } - - b.childBlocks = children - - for _, attr := range b.createAttributes() { - b.attributes = append(b.attributes, NewAttribute(attr, ctx, moduleName, metadata, *ref, moduleSource, moduleFS)) - } - - return &b -} - -func (b *Block) HCLBlock() *hcl.Block { - return b.hclBlock -} - -func (b *Block) ID() string { - return b.id -} - -func (b *Block) Reference() Reference { - return b.reference -} - -func (b *Block) GetMetadata() iacTypes.Metadata { - return b.metadata -} - -func (b *Block) GetRawValue() any { - return nil -} - -func (b *Block) injectBlock(block *Block) { - for attrName, attr := range block.Attributes() { - path := fmt.Sprintf("%s.%s.%s", b.reference.String(), block.hclBlock.Type, attrName) - b.context.Root().SetByDot(attr.Value(), path) - } - b.childBlocks = append(b.childBlocks, block) -} - -func (b *Block) markExpanded() { - b.expanded = true -} - -func (b *Block) IsExpanded() bool { - return b.expanded -} - -func (b *Block) inherit(ctx *context.Context, index ...cty.Value) *Block { - return NewBlock(b.copyBlock(), ctx, b.moduleBlock, b.parentBlock, b.moduleSource, b.moduleFS, index...) -} - -func (b *Block) copyBlock() *hcl.Block { - hclBlock := *b.hclBlock - return &hclBlock -} - -func (b *Block) childContext() *context.Context { - if b.context == nil { - return context.NewContext(&hcl.EvalContext{}, nil) - } - return b.context.NewChild() -} - -func (b *Block) Clone(index cty.Value) *Block { - childCtx := b.childContext() - clone := b.inherit(childCtx, index) - - if len(clone.hclBlock.Labels) > 0 { - position := len(clone.hclBlock.Labels) - 1 - labels := make([]string, len(clone.hclBlock.Labels)) - for i := 0; i < len(labels); i++ { - labels[i] = clone.hclBlock.Labels[i] - } - if index.IsKnown() && !index.IsNull() { - switch index.Type() { - case cty.Number: - f, _ := index.AsBigFloat().Float64() - labels[position] = fmt.Sprintf("%s[%d]", clone.hclBlock.Labels[position], int(f)) - case cty.String: - labels[position] = fmt.Sprintf("%s[%q]", clone.hclBlock.Labels[position], index.AsString()) - default: - labels[position] = fmt.Sprintf("%s[%#v]", clone.hclBlock.Labels[position], index) - } - } else { - labels[position] = fmt.Sprintf("%s[%d]", clone.hclBlock.Labels[position], b.cloneIndex) - } - clone.hclBlock.Labels = labels - } - indexVal, _ := gocty.ToCtyValue(index, cty.Number) - clone.context.SetByDot(indexVal, "count.index") - clone.markExpanded() - b.cloneIndex++ - return clone -} - -func (b *Block) Context() *context.Context { - return b.context -} - -func (b *Block) OverrideContext(ctx *context.Context) { - b.context = ctx - for _, block := range b.childBlocks { - block.OverrideContext(ctx.NewChild()) - } - for _, attr := range b.attributes { - attr.ctx = ctx - } -} - -func (b *Block) Type() string { - return b.hclBlock.Type -} - -func (b *Block) Labels() []string { - return b.hclBlock.Labels -} - -func (b *Block) GetFirstMatchingBlock(names ...string) *Block { - var returnBlock *Block - for _, name := range names { - childBlock := b.GetBlock(name) - if childBlock.IsNotNil() { - return childBlock - } - } - return returnBlock -} - -func (b *Block) createAttributes() hcl.Attributes { - switch body := b.hclBlock.Body.(type) { - case *hclsyntax.Body: - attributes := make(hcl.Attributes) - for _, a := range body.Attributes { - attributes[a.Name] = a.AsHCLAttribute() - } - return attributes - default: - _, body, diag := b.hclBlock.Body.PartialContent(Schema) - if diag != nil { - return nil - } - attrs, diag := body.JustAttributes() - if diag != nil { - return nil - } - return attrs - } -} - -func (b *Block) GetBlock(name string) *Block { - var returnBlock *Block - if b == nil || b.hclBlock == nil { - return returnBlock - } - for _, child := range b.childBlocks { - if child.Type() == name { - return child - } - } - return returnBlock -} - -func (b *Block) AllBlocks() Blocks { - if b == nil || b.hclBlock == nil { - return nil - } - return b.childBlocks -} - -func (b *Block) GetBlocks(name string) Blocks { - if b == nil || b.hclBlock == nil { - return nil - } - var results []*Block - for _, child := range b.childBlocks { - if child.Type() == name { - results = append(results, child) - } - } - return results -} - -func (b *Block) GetAttributes() []*Attribute { - if b == nil { - return nil - } - return b.attributes -} - -func (b *Block) GetAttribute(name string) *Attribute { - if b == nil || b.hclBlock == nil { - return nil - } - for _, attr := range b.attributes { - if attr.Name() == name { - return attr - } - } - return nil -} - -// GetValueByPath returns the value of the attribute located at the given path. -// Supports special paths like "count.index," "each.key," and "each.value." -// The path may contain indices, keys and dots (used as separators). -func (b *Block) GetValueByPath(path string) cty.Value { - - if path == "count.index" || path == "each.key" || path == "each.value" { - return b.Context().GetByDot(path) - } - - if restPath, ok := strings.CutPrefix(path, "each.value."); ok { - if restPath == "" { - return cty.NilVal - } - - val := b.Context().GetByDot("each.value") - res, err := getValueByPath(val, strings.Split(restPath, ".")) - if err != nil { - return cty.NilVal - } - return res - } - - attr, restPath := b.getAttributeByPath(path) - - if attr == nil { - return cty.NilVal - } - - if !attr.IsIterable() || len(restPath) == 0 { - return attr.Value() - } - - res, err := getValueByPath(attr.Value(), restPath) - if err != nil { - return cty.NilVal - } - return res -} - -func (b *Block) getAttributeByPath(path string) (*Attribute, []string) { - steps := strings.Split(path, ".") - - if len(steps) == 1 { - return b.GetAttribute(steps[0]), nil - } - - var ( - attribute *Attribute - stepIndex int - ) - - for currentBlock := b; currentBlock != nil && stepIndex < len(steps); { - blocks := currentBlock.GetBlocks(steps[stepIndex]) - var nextBlock *Block - if !hasIndex(steps, stepIndex+1) && len(blocks) > 0 { - // if index is not provided then return the first block for backwards compatibility - nextBlock = blocks[0] - } else if len(blocks) > 1 && stepIndex < len(steps)-2 { - // handling the case when there are multiple blocks with the same name, - // e.g. when using a `dynamic` block - indexVal, err := strconv.Atoi(steps[stepIndex+1]) - if err == nil && indexVal >= 0 && indexVal < len(blocks) { - nextBlock = blocks[indexVal] - stepIndex++ - } - } - - if nextBlock == nil { - attribute = currentBlock.GetAttribute(steps[stepIndex]) - } - - currentBlock = nextBlock - stepIndex++ - } - - return attribute, steps[stepIndex:] -} - -func hasIndex(steps []string, idx int) bool { - if idx < 0 || idx >= len(steps) { - return false - } - _, err := strconv.Atoi(steps[idx]) - return err == nil -} - -func getValueByPath(val cty.Value, path []string) (cty.Value, error) { - var err error - for _, step := range path { - switch valType := val.Type(); { - case valType.IsMapType(): - val, err = cty.IndexStringPath(step).Apply(val) - case valType.IsObjectType(): - val, err = cty.GetAttrPath(step).Apply(val) - case valType.IsListType() || valType.IsTupleType(): - var idx int - idx, err = strconv.Atoi(step) - if err != nil { - return cty.NilVal, fmt.Errorf("index %q is not a number", step) - } - val, err = cty.IndexIntPath(idx).Apply(val) - default: - return cty.NilVal, fmt.Errorf( - "unexpected value type %s for path step %q", - valType.FriendlyName(), step, - ) - } - if err != nil { - return cty.NilVal, err - } - } - return val, nil -} - -func (b *Block) GetNestedAttribute(name string) (*Attribute, *Block) { - - parts := strings.Split(name, ".") - blocks := parts[:len(parts)-1] - attrName := parts[len(parts)-1] - - working := b - for _, subBlock := range blocks { - if checkBlock := working.GetBlock(subBlock); checkBlock == nil { - return nil, working - } else { - working = checkBlock - } - } - - if working != nil { - if attr := working.GetAttribute(attrName); attr != nil { - return attr, working - } - } - - return nil, b -} - -func MapNestedAttribute[T any](block *Block, path string, f func(attr *Attribute, parent *Block) T) T { - return f(block.GetNestedAttribute(path)) -} - -// LocalName is the name relative to the current module -func (b *Block) LocalName() string { - return b.reference.String() -} - -func (b *Block) FullLocalName() string { - if b.parentBlock != nil { - return fmt.Sprintf( - "%s.%s", - b.parentBlock.FullLocalName(), - b.LocalName(), - ) - } - return b.LocalName() -} - -func (b *Block) FullName() string { - - if b.moduleBlock != nil { - return fmt.Sprintf( - "%s.%s", - b.moduleBlock.FullName(), - b.LocalName(), - ) - } - - return b.LocalName() -} - -func (b *Block) ModuleBlock() *Block { - return b.moduleBlock -} - -func (b *Block) ModuleKey() string { - name := b.Reference().NameLabel() - if b.moduleBlock == nil { - return name - } - return fmt.Sprintf("%s.%s", b.moduleBlock.ModuleKey(), name) -} - -func (b *Block) UniqueName() string { - if b.moduleBlock != nil { - return fmt.Sprintf("%s:%s:%s", b.FullName(), b.metadata.Range().GetFilename(), b.moduleBlock.UniqueName()) - } - return fmt.Sprintf("%s:%s", b.FullName(), b.metadata.Range().GetFilename()) -} - -func (b *Block) TypeLabel() string { - if len(b.Labels()) > 0 { - return b.Labels()[0] - } - return "" -} - -func (b *Block) NameLabel() string { - if len(b.Labels()) > 1 { - return b.Labels()[1] - } - return "" -} - -func (b *Block) HasChild(childElement string) bool { - return b.GetAttribute(childElement).IsNotNil() || b.GetBlock(childElement).IsNotNil() -} - -func (b *Block) MissingChild(childElement string) bool { - if b == nil { - return true - } - - return !b.HasChild(childElement) -} - -func (b *Block) MissingNestedChild(name string) bool { - if b == nil { - return true - } - - parts := strings.Split(name, ".") - blocks := parts[:len(parts)-1] - last := parts[len(parts)-1] - - working := b - for _, subBlock := range blocks { - if checkBlock := working.GetBlock(subBlock); checkBlock == nil { - return true - } else { - working = checkBlock - } - } - return !working.HasChild(last) - -} - -func (b *Block) InModule() bool { - if b == nil { - return false - } - return b.moduleBlock != nil -} - -func (b *Block) Label() string { - return strings.Join(b.hclBlock.Labels, ".") -} - -func (b *Block) IsResourceType(resourceType string) bool { - return b.TypeLabel() == resourceType -} - -func (b *Block) IsEmpty() bool { - return len(b.AllBlocks()) == 0 && len(b.GetAttributes()) == 0 -} - -func (b *Block) Attributes() map[string]*Attribute { - attributes := make(map[string]*Attribute) - for _, attr := range b.GetAttributes() { - attributes[attr.Name()] = attr - } - return attributes -} - -func (b *Block) NullableValues() cty.Value { - return b.values(true) -} - -func (b *Block) Values() cty.Value { - return b.values(false) -} - -func (b *Block) values(allowNull bool) cty.Value { - values := createPresetValues(b) - for _, attribute := range b.GetAttributes() { - if attribute.Name() == "for_each" { - continue - } - if allowNull { - values[attribute.Name()] = attribute.NullableValue() - } else { - values[attribute.Name()] = attribute.Value() - } - } - return cty.ObjectVal(postProcessValues(b, values)) -} - -func (b *Block) IsNil() bool { - return b == nil -} - -func (b *Block) IsNotNil() bool { - return !b.IsNil() -} - -func (b *Block) ExpandBlock() error { - var ( - expanded []*Block - errs error - ) - - for _, child := range b.childBlocks { - if child.Type() == "dynamic" { - blocks, err := child.expandDynamic() - if err != nil { - errs = multierror.Append(errs, err) - continue - } - expanded = append(expanded, blocks...) - } - } - - for _, block := range expanded { - b.injectBlock(block) - } - - return errs -} - -func (b *Block) expandDynamic() ([]*Block, error) { - if b.IsExpanded() || b.Type() != "dynamic" { - return nil, nil - } - - realBlockType := b.TypeLabel() - if realBlockType == "" { - return nil, errors.New("dynamic block must have 1 label") - } - - forEachVal, err := b.validateForEach() - if err != nil { - return nil, fmt.Errorf("invalid for-each in %s block: %w", b.FullLocalName(), err) - } - - var ( - expanded []*Block - errs error - ) - - forEachVal.ForEachElement(func(key, val cty.Value) (stop bool) { - if val.IsNull() || !val.IsKnown() { - return - } - - iteratorName, err := b.iteratorName(realBlockType) - if err != nil { - errs = multierror.Append(errs, err) - return - } - - forEachCtx := b.childContext() - obj := cty.ObjectVal(map[string]cty.Value{ - "key": key, - "value": val, - }) - forEachCtx.Set(obj, iteratorName) - - if content := b.GetBlock("content"); content != nil { - inherited := content.inherit(forEachCtx) - inherited.hclBlock.Labels = []string{} - inherited.hclBlock.Type = realBlockType - if err := inherited.ExpandBlock(); err != nil { - errs = multierror.Append(errs, err) - return - } - expanded = append(expanded, inherited) - } - return - }) - - if len(expanded) > 0 { - b.markExpanded() - } - - return expanded, errs -} - -func (b *Block) validateForEach() (cty.Value, error) { - forEachAttr := b.GetAttribute("for_each") - if forEachAttr == nil { - return cty.NilVal, errors.New("for_each attribute required") - } - - forEachVal := forEachAttr.Value() - - if !forEachVal.CanIterateElements() { - return cty.NilVal, fmt.Errorf("cannot use a %s value in for_each. An iterable collection is required", forEachVal.GoString()) - } - - return forEachVal, nil -} - -func (b *Block) iteratorName(blockType string) (string, error) { - iteratorAttr := b.GetAttribute("iterator") - if iteratorAttr == nil { - return blockType, nil - } - - traversal, diags := hcl.AbsTraversalForExpr(iteratorAttr.hclAttribute.Expr) - if diags.HasErrors() { - return "", diags - } - - if len(traversal) != 1 { - return "", errors.New("dynamic iterator must be a single variable name") - } - - return traversal.RootName(), nil -} diff --git a/pkg/iac/terraform/blocks.go b/pkg/iac/terraform/blocks.go deleted file mode 100644 index 311e83583d26..000000000000 --- a/pkg/iac/terraform/blocks.go +++ /dev/null @@ -1,22 +0,0 @@ -package terraform - -type Blocks []*Block - -func (blocks Blocks) OfType(t string) Blocks { - var results []*Block - for _, block := range blocks { - if block.Type() == t { - results = append(results, block) - } - } - return results -} - -func (blocks Blocks) WithID(id string) *Block { - for _, block := range blocks { - if block.ID() == id { - return block - } - } - return nil -} diff --git a/pkg/iac/terraform/context/context.go b/pkg/iac/terraform/context/context.go deleted file mode 100644 index 14e29a9a8378..000000000000 --- a/pkg/iac/terraform/context/context.go +++ /dev/null @@ -1,147 +0,0 @@ -package context - -import ( - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" -) - -type Context struct { - ctx *hcl.EvalContext - parent *Context -} - -func NewContext(ctx *hcl.EvalContext, parent *Context) *Context { - if ctx.Variables == nil { - ctx.Variables = make(map[string]cty.Value) - } - return &Context{ - ctx: ctx, - parent: parent, - } -} - -func (c *Context) NewChild() *Context { - return NewContext(c.ctx.NewChild(), c) -} - -func (c *Context) Parent() *Context { - return c.parent -} - -func (c *Context) Inner() *hcl.EvalContext { - return c.ctx -} - -func (c *Context) Root() *Context { - root := c - for root.Parent() != nil { - root = root.Parent() - } - return root -} - -func (c *Context) Get(parts ...string) cty.Value { - if len(parts) == 0 { - return cty.NilVal - } - - curr := c.ctx.Variables[parts[0]] - if len(parts) == 1 { - return curr - } - - for i, part := range parts[1:] { - if !curr.Type().HasAttribute(part) { - return cty.NilVal - } - - attr := curr.GetAttr(part) - - if i == len(parts)-2 { // iteration from the first element - return attr - } - - if !(attr.IsKnown() && attr.Type().IsObjectType()) { - return cty.NilVal - } - curr = attr - } - - return cty.NilVal -} - -func (c *Context) GetByDot(path string) cty.Value { - return c.Get(strings.Split(path, ".")...) -} - -func (c *Context) SetByDot(val cty.Value, path string) { - c.Set(val, strings.Split(path, ".")...) -} - -func (c *Context) Set(val cty.Value, parts ...string) { - if len(parts) == 0 { - return - } - - v := mergeVars(c.ctx.Variables[parts[0]], parts[1:], val) - c.ctx.Variables[parts[0]] = v -} - -func (c *Context) Replace(val cty.Value, path string) { - parts := strings.Split(path, ".") - if len(parts) == 0 { - return - } - - delete(c.ctx.Variables, parts[0]) - c.Set(val, parts...) -} - -func mergeVars(src cty.Value, parts []string, value cty.Value) cty.Value { - - if len(parts) == 0 { - if isNotEmptyObject(src) && isNotEmptyObject(value) { - return mergeObjects(src, value) - } - return value - } - - data := make(map[string]cty.Value) - if isNotEmptyObject(src) { - data = src.AsValueMap() - if attr, ok := data[parts[0]]; ok { - src = attr - } else { - src = cty.EmptyObjectVal - } - } - - data[parts[0]] = mergeVars(src, parts[1:], value) - - return cty.ObjectVal(data) -} - -func mergeObjects(a, b cty.Value) cty.Value { - output := make(map[string]cty.Value) - - for key, val := range a.AsValueMap() { - output[key] = val - } - b.ForEachElement(func(key, val cty.Value) (stop bool) { - k := key.AsString() - old := output[k] - if old.IsKnown() && isNotEmptyObject(old) && isNotEmptyObject(val) { - output[k] = mergeObjects(old, val) - } else { - output[k] = val - } - return false - }) - return cty.ObjectVal(output) -} - -func isNotEmptyObject(val cty.Value) bool { - return !val.IsNull() && val.IsKnown() && val.Type().IsObjectType() && val.LengthInt() > 0 -} diff --git a/pkg/iac/terraform/context/context_test.go b/pkg/iac/terraform/context/context_test.go deleted file mode 100644 index dfd8e05e5fac..000000000000 --- a/pkg/iac/terraform/context/context_test.go +++ /dev/null @@ -1,275 +0,0 @@ -package context - -import ( - "testing" - - "github.com/hashicorp/hcl/v2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" -) - -func Test_ContextVariables(t *testing.T) { - underlying := &hcl.EvalContext{} - ctx := NewContext(underlying, nil) - - val, err := gocty.ToCtyValue("hello", cty.String) - if err != nil { - t.Fatal(err) - } - - ctx.Set(val, "my", "value") - value := underlying.Variables["my"].AsValueMap()["value"] - assert.Equal(t, "hello", value.AsString()) - -} - -func Test_ContextVariablesPreservation(t *testing.T) { - - underlying := &hcl.EvalContext{} - underlying.Variables = make(map[string]cty.Value) - underlying.Variables["x"], _ = gocty.ToCtyValue("does it work?", cty.String) - str, _ := gocty.ToCtyValue("something", cty.String) - underlying.Variables["my"] = cty.ObjectVal(map[string]cty.Value{ - "other": str, - "obj": cty.ObjectVal(map[string]cty.Value{ - "another": str, - }), - }) - ctx := NewContext(underlying, nil) - - val, err := gocty.ToCtyValue("hello", cty.String) - if err != nil { - t.Fatal(err) - } - - ctx.Set(val, "my", "value") - assert.Equal(t, "hello", underlying.Variables["my"].AsValueMap()["value"].AsString()) - assert.Equal(t, "something", underlying.Variables["my"].AsValueMap()["other"].AsString()) - assert.Equal(t, "something", underlying.Variables["my"].AsValueMap()["obj"].AsValueMap()["another"].AsString()) - assert.Equal(t, "does it work?", underlying.Variables["x"].AsString()) - -} - -func Test_SetWithMerge(t *testing.T) { - hctx := hcl.EvalContext{ - Variables: map[string]cty.Value{ - "my": cty.ObjectVal(map[string]cty.Value{ - "someValue": cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("test"), - "bar": cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("test"), - }), - }), - }), - }, - } - - ctx := NewContext(&hctx, nil) - - val := cty.ObjectVal(map[string]cty.Value{ - "foo2": cty.StringVal("test2"), - "bar": cty.ObjectVal(map[string]cty.Value{ - "foo2": cty.StringVal("test2"), - }), - }) - - ctx.Set(val, "my", "someValue") - got := ctx.Get("my", "someValue") - expected := cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("test"), - "foo2": cty.StringVal("test2"), - "bar": cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("test"), - "foo2": cty.StringVal("test2"), - }), - }) - - assert.Equal(t, expected, got) -} - -func Test_ContextVariablesPreservationByDot(t *testing.T) { - - underlying := &hcl.EvalContext{} - underlying.Variables = make(map[string]cty.Value) - underlying.Variables["x"], _ = gocty.ToCtyValue("does it work?", cty.String) - str, _ := gocty.ToCtyValue("something", cty.String) - underlying.Variables["my"] = cty.ObjectVal(map[string]cty.Value{ - "other": str, - "obj": cty.ObjectVal(map[string]cty.Value{ - "another": str, - }), - }) - ctx := NewContext(underlying, nil) - - val, err := gocty.ToCtyValue("hello", cty.String) - if err != nil { - t.Fatal(err) - } - - ctx.SetByDot(val, "my.something.value") - assert.Equal(t, "hello", underlying.Variables["my"].AsValueMap()["something"].AsValueMap()["value"].AsString()) - assert.Equal(t, "something", underlying.Variables["my"].AsValueMap()["other"].AsString()) - assert.Equal(t, "something", underlying.Variables["my"].AsValueMap()["obj"].AsValueMap()["another"].AsString()) - assert.Equal(t, "does it work?", underlying.Variables["x"].AsString()) -} - -func Test_ContextSetThenImmediateGet(t *testing.T) { - - underlying := &hcl.EvalContext{} - - ctx := NewContext(underlying, nil) - - ctx.Set(cty.ObjectVal(map[string]cty.Value{ - "mod_result": cty.StringVal("ok"), - }), "module", "modulename") - - val := ctx.Get("module", "modulename", "mod_result") - assert.Equal(t, "ok", val.AsString()) -} - -func Test_ContextSetThenImmediateGetWithChild(t *testing.T) { - - underlying := &hcl.EvalContext{} - - ctx := NewContext(underlying, nil) - - childCtx := ctx.NewChild() - - childCtx.Root().Set(cty.ObjectVal(map[string]cty.Value{ - "mod_result": cty.StringVal("ok"), - }), "module", "modulename") - - val := ctx.Get("module", "modulename", "mod_result") - assert.Equal(t, "ok", val.AsString()) -} - -func Test_MergeObjects(t *testing.T) { - - tests := []struct { - name string - oldVal cty.Value - newVal cty.Value - expected cty.Value - }{ - { - name: "happy", - oldVal: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("some_id"), - "arn": cty.StringVal("some_arn"), - }), - }), - newVal: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "arn": cty.StringVal("some_new_arn"), - "bucket": cty.StringVal("test"), - }), - }), - expected: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("some_id"), - "arn": cty.StringVal("some_new_arn"), - "bucket": cty.StringVal("test"), - }), - }), - }, - { - name: "old value is empty", - oldVal: cty.EmptyObjectVal, - newVal: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "bucket": cty.StringVal("test"), - }), - }), - expected: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "bucket": cty.StringVal("test"), - }), - }), - }, - { - name: "new value is empty", - oldVal: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "bucket": cty.StringVal("test"), - }), - }), - newVal: cty.EmptyObjectVal, - expected: cty.ObjectVal(map[string]cty.Value{ - "this": cty.ObjectVal(map[string]cty.Value{ - "bucket": cty.StringVal("test"), - }), - }), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.expected, mergeObjects(tt.oldVal, tt.newVal)) - }) - } - -} - -func Test_IsNotEmptyObject(t *testing.T) { - tests := []struct { - name string - val cty.Value - expected bool - }{ - { - name: "happy", - val: cty.ObjectVal(map[string]cty.Value{ - "field": cty.NilVal, - }), - expected: true, - }, - { - name: "empty object", - val: cty.EmptyObjectVal, - expected: false, - }, - { - name: "nil value", - val: cty.NilVal, - expected: false, - }, - { - name: "dynamic value", - val: cty.DynamicVal, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.expected, isNotEmptyObject(tt.val)) - }) - } -} - -func TestReplace(t *testing.T) { - t.Run("replacement of an existing value", func(t *testing.T) { - underlying := &hcl.EvalContext{} - ctx := NewContext(underlying, nil) - ctx.SetByDot(cty.StringVal("some-value"), "my.value") - require.NotEqual(t, cty.NilVal, ctx.GetByDot("my.value")) - ctx.Replace(cty.NumberIntVal(-1), "my.value") - assert.Equal(t, cty.NumberIntVal(-1), ctx.GetByDot("my.value")) - }) - - t.Run("replacement of a non-existing value", func(t *testing.T) { - underlying := &hcl.EvalContext{} - ctx := NewContext(underlying, nil) - ctx.Replace(cty.NumberIntVal(-1), "my.value") - assert.Equal(t, cty.NumberIntVal(-1), ctx.GetByDot("my.value")) - }) - - t.Run("empty path", func(t *testing.T) { - underlying := &hcl.EvalContext{} - ctx := NewContext(underlying, nil) - ctx.Replace(cty.NumberIntVal(-1), "") - }) -} diff --git a/pkg/iac/terraform/module.go b/pkg/iac/terraform/module.go deleted file mode 100644 index 35c9c1f57993..000000000000 --- a/pkg/iac/terraform/module.go +++ /dev/null @@ -1,196 +0,0 @@ -package terraform - -import ( - "fmt" - "strings" - - "github.com/aquasecurity/trivy/pkg/iac/ignore" -) - -type Module struct { - blocks Blocks - blockMap map[string]Blocks - rootPath string - modulePath string - ignores ignore.Rules - parent *Module -} - -func NewModule(rootPath, modulePath string, blocks Blocks, ignores ignore.Rules) *Module { - - blockMap := make(map[string]Blocks) - - for _, b := range blocks { - if b.NameLabel() != "" { - blockMap[b.TypeLabel()] = append(blockMap[b.TypeLabel()], b) - } - } - - return &Module{ - blocks: blocks, - ignores: ignores, - blockMap: blockMap, - rootPath: rootPath, - modulePath: modulePath, - } -} - -func (c *Module) SetParent(parent *Module) { - c.parent = parent -} - -func (c *Module) RootPath() string { - return c.rootPath -} - -func (c *Module) ModulePath() string { - return c.modulePath -} - -func (c *Module) Parent() *Module { - return c.parent -} - -func (c *Module) Ignores() ignore.Rules { - return c.ignores -} - -func (c *Module) GetBlocks() Blocks { - return c.blocks -} - -func (h *Module) GetBlocksByTypeLabel(typeLabel string) Blocks { - return h.blockMap[typeLabel] -} - -func (c *Module) getBlocksByType(blockType string, labels ...string) Blocks { - if blockType == "module" { - return c.getModuleBlocks() - } - var results Blocks - for _, label := range labels { - for _, block := range c.blockMap[label] { - if block.Type() == blockType { - results = append(results, block) - } - } - } - return results -} - -func (c *Module) getModuleBlocks() Blocks { - var results Blocks - for _, block := range c.blocks { - if block.Type() == "module" { - results = append(results, block) - } - } - return results -} - -func (c *Module) GetResourcesByType(labels ...string) Blocks { - return c.getBlocksByType("resource", labels...) -} - -func (c *Module) GetResourcesByIDs(ids ...string) Blocks { - var blocks Blocks - - for _, id := range ids { - if block := c.blocks.WithID(id); block != nil { - blocks = append(blocks, block) - } - } - return blocks -} - -func (c *Module) GetDatasByType(label string) Blocks { - return c.getBlocksByType("data", label) -} - -func (c *Module) GetProviderBlocksByProvider(providerName, alias string) Blocks { - var results Blocks - for _, block := range c.blocks { - if block.Type() == "provider" && len(block.Labels()) > 0 && block.TypeLabel() == providerName { - if alias != "" { - if block.HasChild("alias") && block.GetAttribute("alias").Equals(strings.ReplaceAll(alias, fmt.Sprintf("%s.", providerName), "")) { - results = append(results, block) - - } - } else if block.MissingChild("alias") { - results = append(results, block) - } - } - } - return results -} - -func (c *Module) GetReferencedBlock(referringAttr *Attribute, parentBlock *Block) (*Block, error) { - for _, ref := range referringAttr.AllReferences() { - if ref.TypeLabel() == "each" { - if forEachAttr := parentBlock.GetAttribute("for_each"); forEachAttr.IsNotNil() { - if b, err := c.GetReferencedBlock(forEachAttr, parentBlock); err == nil { - return b, nil - } - } - } - for _, block := range c.blocks { - if ref.RefersTo(block.reference) { - return block, nil - } - kref := *ref - kref.SetKey(parentBlock.reference.RawKey()) - if kref.RefersTo(block.reference) { - return block, nil - } - } - } - return nil, fmt.Errorf("no referenced block found in '%s'", referringAttr.Name()) -} - -func (c *Module) GetBlockByID(id string) (*Block, error) { - found := c.blocks.WithID(id) - if found == nil { - return nil, fmt.Errorf("no block found with id '%s'", id) - } - return found, nil -} - -func (c *Module) GetReferencingResources(originalBlock *Block, referencingLabel, referencingAttributeName string) Blocks { - return c.GetReferencingBlocks(originalBlock, "resource", referencingLabel, referencingAttributeName) -} - -func (c *Module) GetsModulesBySource(moduleSource string) (Blocks, error) { - var results Blocks - - modules := c.getModuleBlocks() - for _, module := range modules { - if module.HasChild("source") && module.GetAttribute("source").Equals(moduleSource) { - results = append(results, module) - } - } - return results, nil -} - -func (c *Module) GetReferencingBlocks(originalBlock *Block, referencingType, referencingLabel, referencingAttributeName string) Blocks { - blocks := c.getBlocksByType(referencingType, referencingLabel) - var results Blocks - for _, block := range blocks { - attr := block.GetAttribute(referencingAttributeName) - if attr == nil { - continue - } - if attr.References(originalBlock.reference) { - results = append(results, block) - } else { - for _, ref := range attr.AllReferences() { - if ref.TypeLabel() == "each" { - fe := block.GetAttribute("for_each") - if fe.References(originalBlock.reference) { - results = append(results, block) - } - } - } - } - } - return results -} diff --git a/pkg/iac/terraform/modules.go b/pkg/iac/terraform/modules.go deleted file mode 100644 index 24c67e6f161b..000000000000 --- a/pkg/iac/terraform/modules.go +++ /dev/null @@ -1,117 +0,0 @@ -package terraform - -import ( - "errors" - - "github.com/aquasecurity/trivy/pkg/iac/types" -) - -type Modules []*Module - -type ResourceIDResolutions map[string]bool - -func (r ResourceIDResolutions) Resolve(id string) { - r[id] = true -} - -func (r ResourceIDResolutions) Orphans() (orphanIDs []string) { - for id, resolved := range r { - if !resolved { - orphanIDs = append(orphanIDs, id) - } - } - return orphanIDs -} - -func (m Modules) GetDatasByType(typeLabel string) Blocks { - var blocks Blocks - for _, module := range m { - blocks = append(blocks, module.GetDatasByType(typeLabel)...) - } - - return blocks -} - -func (m Modules) GetResourcesByType(typeLabel ...string) Blocks { - var blocks Blocks - for _, module := range m { - blocks = append(blocks, module.GetResourcesByType(typeLabel...)...) - } - - return blocks -} - -func (m Modules) GetChildResourceIDMapByType(typeLabels ...string) ResourceIDResolutions { - blocks := m.GetResourcesByType(typeLabels...) - - idMap := make(map[string]bool) - for _, block := range blocks { - idMap[block.ID()] = false - } - - return idMap -} - -func (m Modules) GetReferencedBlock(referringAttr *Attribute, parentBlock *Block) (*Block, error) { - var bestMatch *Block - for _, module := range m { - b, err := module.GetReferencedBlock(referringAttr, parentBlock) - if err == nil { - if bestMatch == nil || b.moduleBlock == parentBlock.moduleBlock { - bestMatch = b - } - } - } - if bestMatch != nil { - return bestMatch, nil - } - return nil, errors.New("block not found") -} - -func (m Modules) GetReferencingResources(originalBlock *Block, referencingLabel, referencingAttributeName string) Blocks { - var blocks Blocks - for _, module := range m { - blocks = append(blocks, module.GetReferencingResources(originalBlock, referencingLabel, referencingAttributeName)...) - } - - return blocks -} - -func (m Modules) GetBlocks() Blocks { - var blocks Blocks - for _, module := range m { - blocks = append(blocks, module.GetBlocks()...) - } - return blocks -} - -func (m Modules) GetBlockById(id string) (*Block, error) { - for _, module := range m { - if found := module.blocks.WithID(id); found != nil { - return found, nil - } - - } - return nil, errors.New("block not found") -} - -func (m Modules) GetResourceByIDs(id ...string) Blocks { - var blocks Blocks - for _, module := range m { - blocks = append(blocks, module.GetResourcesByIDs(id...)...) - } - - return blocks -} - -func (m Modules) GetBlockByIgnoreRange(blockMetadata *types.Metadata) *Block { - for _, module := range m { - for _, block := range module.GetBlocks() { - metadata := block.GetMetadata() - if blockMetadata.Reference() == metadata.Reference() { - return block - } - } - } - return nil -} diff --git a/pkg/iac/terraform/presets.go b/pkg/iac/terraform/presets.go deleted file mode 100644 index 61ac78bccd2e..000000000000 --- a/pkg/iac/terraform/presets.go +++ /dev/null @@ -1,74 +0,0 @@ -package terraform - -import ( - "fmt" - "math/rand/v2" - "strings" - - "github.com/google/uuid" - "github.com/zclconf/go-cty/cty" -) - -var resourceRandomAttributes = map[string][]string{ - // If the user leaves the name blank, Terraform will automatically generate a unique name - "aws_launch_template": {"name"}, - "random_id": {"hex", "dec", "b64_url", "b64_std"}, - "random_password": {"result", "bcrypt_hash"}, - "random_string": {"result"}, - "random_bytes": {"base64", "hex"}, - "random_uuid": {"result"}, -} - -func createPresetValues(b *Block) map[string]cty.Value { - presets := make(map[string]cty.Value) - - // here we set up common "id" values that are set by the provider - this ensures all blocks have a default - // referencable id/arn. this isn't perfect, but the only way to link blocks in certain circumstances. - presets["id"] = cty.StringVal(b.ID()) - - if strings.HasPrefix(b.TypeLabel(), "aws_") { - presets["arn"] = cty.StringVal(b.ID()) - } - - switch b.TypeLabel() { - // workaround for weird iam feature - case "aws_iam_policy_document": - presets["json"] = cty.StringVal(b.ID()) - // allow referencing the current region name - case "aws_region": - presets["name"] = cty.StringVal("current-region") - case "random_integer": - //nolint:gosec - presets["result"] = cty.NumberIntVal(rand.Int64()) - } - - if attrs, exists := resourceRandomAttributes[b.TypeLabel()]; exists { - for _, attr := range attrs { - presets[attr] = cty.StringVal(uuid.New().String()) - } - } - - return presets -} - -func postProcessValues(b *Block, input map[string]cty.Value) map[string]cty.Value { - - // alias id to "bucket" (bucket name) for s3 bucket resources - if strings.HasPrefix(b.TypeLabel(), "aws_s3_bucket") { - if bucket, ok := input["bucket"]; ok { - input["id"] = bucket - } else { - input["bucket"] = cty.StringVal(b.ID()) - } - } - - if b.TypeLabel() == "aws_s3_bucket" { - var bucketName string - if bucket := input["bucket"]; bucket.Type().Equals(cty.String) { - bucketName = bucket.AsString() - } - input["arn"] = cty.StringVal(fmt.Sprintf("arn:aws:s3:::%s", bucketName)) - } - - return input -} diff --git a/pkg/iac/terraform/reference.go b/pkg/iac/terraform/reference.go deleted file mode 100644 index de601d7433a0..000000000000 --- a/pkg/iac/terraform/reference.go +++ /dev/null @@ -1,178 +0,0 @@ -package terraform - -import ( - "errors" - "fmt" - - "github.com/zclconf/go-cty/cty" -) - -type Reference struct { - blockType Type - typeLabel string - nameLabel string - remainder []string - key cty.Value - parent string -} - -func extendReference(ref Reference, name string) Reference { - child := ref - child.remainder = make([]string, len(ref.remainder)) - if len(ref.remainder) > 0 { - copy(child.remainder, ref.remainder) - } - child.remainder = append(child.remainder, name) - return child -} - -func newReference(parts []string, parentKey string) (*Reference, error) { - - var ref Reference - - if len(parts) == 0 { - return nil, errors.New("cannot create empty reference") - } - - blockType, err := TypeFromRefName(parts[0]) - if err != nil { - blockType = &TypeResource - } - - ref.blockType = *blockType - - if ref.blockType.removeTypeInReference && parts[0] != blockType.name { - ref.typeLabel = parts[0] - if len(parts) > 1 { - ref.nameLabel = parts[1] - } - } else if len(parts) > 1 { - ref.typeLabel = parts[1] - if len(parts) > 2 { - ref.nameLabel = parts[2] - } else { - ref.nameLabel = ref.typeLabel - ref.typeLabel = "" - } - } - if len(parts) > 3 { - ref.remainder = parts[3:] - } - - if parentKey != "root" { - ref.parent = parentKey - } - - return &ref, nil -} - -func (r Reference) BlockType() Type { - return r.blockType -} - -func (r Reference) TypeLabel() string { - return r.typeLabel -} - -func (r Reference) NameLabel() string { - return r.nameLabel -} - -func (r Reference) HumanReadable() string { - if r.parent == "" { - return r.String() - } - return fmt.Sprintf("%s:%s", r.parent, r.String()) -} - -func (r Reference) LogicalID() string { - return r.String() -} - -func (r Reference) String() string { - - base := r.typeLabel - if r.nameLabel != "" { - base = fmt.Sprintf("%s.%s", base, r.nameLabel) - } - - if !r.blockType.removeTypeInReference { - base = r.blockType.Name() - if r.typeLabel != "" { - base += "." + r.typeLabel - } - if r.nameLabel != "" { - base += "." + r.nameLabel - } - } - - base += r.KeyBracketed() - - for _, rem := range r.remainder { - base += "." + rem - } - - return base -} - -func (r Reference) RefersTo(other Reference) bool { - - if r.BlockType() != other.BlockType() { - return false - } - if r.TypeLabel() != other.TypeLabel() { - return false - } - if r.NameLabel() != other.NameLabel() { - return false - } - if (r.Key() != "" || other.Key() != "") && r.Key() != other.Key() { - return false - } - return true -} - -func (r *Reference) SetKey(key cty.Value) { - if key.IsNull() || !key.IsKnown() { - return - } - r.key = key -} - -func (r Reference) KeyBracketed() string { - switch v := key(r).(type) { - case int: - return fmt.Sprintf("[%d]", v) - case string: - if v == "" { - return "" - } - return fmt.Sprintf("[%q]", v) - default: - return "" - } -} - -func (r Reference) RawKey() cty.Value { - return r.key -} - -func (r Reference) Key() string { - return fmt.Sprintf("%v", key(r)) -} - -func key(r Reference) any { - if r.key.IsNull() || !r.key.IsKnown() { - return "" - } - switch r.key.Type() { - case cty.Number: - f := r.key.AsBigFloat() - f64, _ := f.Float64() - return int(f64) - case cty.String: - return r.key.AsString() - default: - return "" - } -} diff --git a/pkg/iac/terraform/reference_test.go b/pkg/iac/terraform/reference_test.go deleted file mode 100644 index 6ad5bc782655..000000000000 --- a/pkg/iac/terraform/reference_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/zclconf/go-cty/cty" -) - -func Test_ReferenceParsing(t *testing.T) { - cases := []struct { - input []string - expected string - }{ - { - input: []string{"module", "my-mod"}, - expected: "module.my-mod", - }, - { - input: []string{"aws_s3_bucket", "test"}, - expected: "aws_s3_bucket.test", - }, - { - input: []string{"resource", "aws_s3_bucket", "test"}, - expected: "aws_s3_bucket.test", - }, - { - input: []string{"module", "my-mod"}, - expected: "module.my-mod", - }, - { - input: []string{"data", "aws_iam_policy_document", "s3_policy"}, - expected: "data.aws_iam_policy_document.s3_policy", - }, - { - input: []string{"provider", "aws"}, - expected: "provider.aws", - }, - { - input: []string{"output", "something"}, - expected: "output.something", - }, - } - - for _, test := range cases { - t.Run(test.expected, func(t *testing.T) { - ref, err := newReference(test.input, "") - require.NoError(t, err) - assert.Equal(t, test.expected, ref.String()) - }) - } -} - -func Test_SetKey(t *testing.T) { - tests := []struct { - name string - key cty.Value - want cty.Value - }{ - { - name: "happy", - key: cty.StringVal("str"), - want: cty.StringVal("str"), - }, - { - name: "null key", - key: cty.NullVal(cty.String), - want: cty.Value{}, - }, - { - name: "unknown key", - key: cty.UnknownVal(cty.String), - want: cty.Value{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Run(tt.name, func(t *testing.T) { - ref, err := newReference([]string{"resource", "test"}, "") - require.NoError(t, err) - - ref.SetKey(tt.key) - - assert.Equal(t, tt.want, ref.RawKey()) - }) - }) - } -} - -func Test_Key(t *testing.T) { - - tests := []struct { - name string - key cty.Value - want string - }{ - { - name: "empty key", - want: "", - }, - { - name: "str key", - key: cty.StringVal("some_value"), - want: "some_value", - }, - { - name: "number key", - key: cty.NumberIntVal(122), - want: "122", - }, - { - name: "bool key", - key: cty.BoolVal(true), - want: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Run(tt.name, func(t *testing.T) { - ref, err := newReference([]string{"resource", "test"}, "") - require.NoError(t, err) - - ref.SetKey(tt.key) - - assert.Equal(t, tt.want, ref.Key()) - }) - }) - } -} - -func Test_KeyBracketed(t *testing.T) { - tests := []struct { - name string - key cty.Value - want string - }{ - { - name: "empty key", - want: "", - }, - { - name: "str key", - key: cty.StringVal("some_value"), - want: "[\"some_value\"]", - }, - { - name: "number key", - key: cty.NumberIntVal(122), - want: "[122]", - }, - { - name: "bool key", - key: cty.BoolVal(true), - want: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ref, err := newReference([]string{"resource", "test"}, "") - require.NoError(t, err) - - ref.SetKey(tt.key) - - assert.Equal(t, tt.want, ref.KeyBracketed()) - }) - } -} diff --git a/pkg/iac/terraform/resource_block.go b/pkg/iac/terraform/resource_block.go deleted file mode 100644 index abce0fcb040e..000000000000 --- a/pkg/iac/terraform/resource_block.go +++ /dev/null @@ -1,193 +0,0 @@ -package terraform - -import ( - "bytes" - "fmt" - "strings" - "text/template" -) - -type PlanReference struct { - Value any -} - -type PlanBlock struct { - Type string - Name string - BlockType string - Blocks map[string]map[string]any - Attributes map[string]any -} - -func NewPlanBlock(blockType, resourceType, resourceName string) *PlanBlock { - if blockType == "managed" { - blockType = "resource" - } - - return &PlanBlock{ - Type: resourceType, - Name: resourceName, - BlockType: blockType, - Blocks: make(map[string]map[string]any), - Attributes: make(map[string]any), - } -} - -func (rb *PlanBlock) HasAttribute(attribute string) bool { - for k := range rb.Attributes { - if k == attribute { - return true - } - } - return false -} - -func (rb *PlanBlock) ToHCL() string { - - resourceTmpl, err := template.New("resource").Funcs(template.FuncMap{ - "RenderValue": renderTemplateValue, - "RenderPrimitive": renderPrimitive, - }).Parse(resourceTemplate) - if err != nil { - panic(err) - } - - var res bytes.Buffer - if err := resourceTmpl.Execute(&res, map[string]any{ - "BlockType": rb.BlockType, - "Type": rb.Type, - "Name": rb.Name, - "Attributes": rb.Attributes, - "Blocks": rb.Blocks, - }); err != nil { - return "" - } - return res.String() -} - -var resourceTemplate = `{{ .BlockType }} "{{ .Type }}" "{{ .Name }}" { - {{ range $name, $value := .Attributes }}{{ if $value }}{{ $name }} {{ RenderValue $value }} - {{end}}{{ end }}{{ range $name, $block := .Blocks }}{{ $name }} { - {{ range $name, $value := $block }}{{ if $value }}{{ $name }} {{ RenderValue $value }} - {{end}}{{ end }}} -{{end}}}` - -func renderTemplateValue(val any) string { - switch t := val.(type) { - case map[string]any: - return fmt.Sprintf("= %s", renderMap(t)) - case []any: - if isMapSlice(t) { - return renderSlice(t) - } - return fmt.Sprintf("= %s", renderSlice(t)) - default: - return fmt.Sprintf("= %s", renderPrimitive(val)) - } -} - -func renderPrimitive(val any) string { - switch t := val.(type) { - case PlanReference: - return fmt.Sprintf("%v", t.Value) - case string: - return parseStringPrimitive(t) - case map[string]any: - return renderMap(t) - case []any: - return renderSlice(t) - default: - return fmt.Sprintf("%#v", t) - } - -} - -func parseStringPrimitive(input string) string { - // we must escape templating - // ref: https://developer.hashicorp.com/terraform/language/expressions/strings#escape-sequences-1 - input = escapeSpecialSequences(input) - if strings.Contains(input, "\n") { - return fmt.Sprintf(`< 0 && rune(input[i-1]) == r { - continue - } - - sb.WriteRune(r) - } else { - sb.WriteRune(r) - } - } - return sb.String() -} diff --git a/pkg/iac/terraform/resource_block_test.go b/pkg/iac/terraform/resource_block_test.go deleted file mode 100644 index 37fb1d32fdcb..000000000000 --- a/pkg/iac/terraform/resource_block_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_EscapeSpecialSequences(t *testing.T) { - tests := []struct { - name string - inp string - expected string - }{ - { - name: "without special sequences", - inp: `"hello world\\"`, - expected: `"hello world\\"`, - }, - { - name: "interpolation", - inp: `"Hello, ${var.name}!"`, - expected: `"Hello, $${var.name}!"`, - }, - { - name: "directive", - inp: `"Hello, %{ if true }foo%{ else }bar%{ endif }!"`, - expected: `"Hello, %%{ if true }foo%%{ else }bar%%{ endif }!"`, - }, - { - name: "interpolation already escaped", - inp: `"Hello, $${var.name}!"`, - expected: `"Hello, $${var.name}!"`, - }, - { - name: "start with special character", - inp: `${var.name}!"`, - expected: `$${var.name}!"`, - }, - { - name: "grok pattern", - inp: "# Grok Pattern Template\ngrok_pattern = \"%{TIMESTAMP_ISO8601:time} \\\\[%{NUMBER:pid}\\\\] %{GREEDYDATA:message}\"", - expected: "# Grok Pattern Template\ngrok_pattern = \"%%{TIMESTAMP_ISO8601:time} \\\\[%%{NUMBER:pid}\\\\] %%{GREEDYDATA:message}\"", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := escapeSpecialSequences(tt.inp) - assert.Equal(t, tt.expected, got) - - // We make sure that the characters are properly escaped - _, diag := hclsyntax.ParseTemplate([]byte(got), "", hcl.InitialPos) - if diag.HasErrors() { - require.NoError(t, diag) - } - }) - } -} diff --git a/pkg/iac/terraform/schema.go b/pkg/iac/terraform/schema.go deleted file mode 100644 index f408d860d0e3..000000000000 --- a/pkg/iac/terraform/schema.go +++ /dev/null @@ -1,55 +0,0 @@ -package terraform - -import "github.com/hashicorp/hcl/v2" - -// Schema is regularly lifted from terraform source to ensure compatibility -var Schema = &hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: "terraform", - }, - { - Type: "required_providers", - }, - { - Type: "provider", - LabelNames: []string{"name"}, - }, - { - Type: "variable", - LabelNames: []string{"name"}, - }, - { - Type: "locals", - }, - { - Type: "output", - LabelNames: []string{"name"}, - }, - { - Type: "module", - LabelNames: []string{"name"}, - }, - { - Type: "check", - LabelNames: []string{"name"}, - }, - { - Type: "resource", - LabelNames: []string{"type", "name"}, - }, - { - Type: "data", - LabelNames: []string{"type", "name"}, - }, - { - Type: "moved", - }, - { - Type: "import", - }, - { - Type: "removed", - }, - }, -} diff --git a/pkg/iac/terraform/type.go b/pkg/iac/terraform/type.go deleted file mode 100644 index 5a91fee61a5f..000000000000 --- a/pkg/iac/terraform/type.go +++ /dev/null @@ -1,108 +0,0 @@ -package terraform - -import "errors" - -type Type struct { - name string - refName string - removeTypeInReference bool -} - -func (t Type) Name() string { - return t.name -} - -func (t Type) ShortName() string { - if t.refName != "" { - return t.refName - } - return t.name -} - -var TypeCheck = Type{ - name: "check", -} - -var TypeData = Type{ - name: "data", -} - -var TypeResource = Type{ - name: "resource", - removeTypeInReference: true, -} - -var TypeVariable = Type{ - name: "variable", - refName: "var", -} - -var TypeImport = Type{ - name: "import", -} - -var TypeLocal = Type{ - name: "locals", - refName: "local", -} - -var TypeMoved = Type{ - name: "moved", -} - -var TypeProvider = Type{ - name: "provider", -} - -var TypeOutput = Type{ - name: "output", -} - -var TypeModule = Type{ - name: "module", -} - -var TypeTerraform = Type{ - name: "terraform", -} - -var ValidTypes = []Type{ - TypeCheck, - TypeData, - TypeImport, - TypeLocal, - TypeModule, - TypeMoved, - TypeOutput, - TypeProvider, - TypeResource, - TypeTerraform, - TypeVariable, -} - -func IsValidType(name string) bool { - for _, valid := range ValidTypes { - if valid.name == name { - return true - } - } - return false -} - -func IsValidBlockReference(name string) bool { - for _, valid := range ValidTypes { - if valid.refName == name { - return true - } - } - return false -} - -func TypeFromRefName(name string) (*Type, error) { - for _, valid := range ValidTypes { - if valid.refName == name || (valid.refName == "" && valid.name == name) { - return &valid, nil - } - } - return nil, errors.New("block type not found") -} diff --git a/pkg/iac/terraform/value_functions.go b/pkg/iac/terraform/value_functions.go deleted file mode 100644 index 1071a608e39c..000000000000 --- a/pkg/iac/terraform/value_functions.go +++ /dev/null @@ -1,87 +0,0 @@ -package terraform - -import ( - "fmt" - "regexp" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" -) - -const ( - functionNameKey = "action" - valueNameKey = "value" -) - -var functions = map[string]func(any, any) bool{ - "isAny": isAny, - "isNone": isNone, - "regexMatches": regexMatches, -} - -func evaluate(criteriaValue, testValue any) bool { - switch t := criteriaValue.(type) { - case map[any]any: - if t[functionNameKey] != nil { - return executeFunction(t[functionNameKey].(string), t[valueNameKey], testValue) - } - case map[string]any: - if t[functionNameKey] != nil { - return executeFunction(t[functionNameKey].(string), t[valueNameKey], testValue) - } - default: - return t == testValue - } - return false -} - -func executeFunction(functionName string, criteriaValues, testValue any) bool { - if functions[functionName] != nil { - return functions[functionName](criteriaValues, testValue) - } - return false -} - -func isAny(criteriaValues, testValue any) bool { - switch t := criteriaValues.(type) { - case []any: - for _, v := range t { - if v == testValue { - return true - } - } - case []string: - for _, v := range t { - if v == testValue.(string) { - return true - } - } - } - return false -} - -func isNone(criteriaValues, testValue any) bool { - return !isAny(criteriaValues, testValue) -} - -func regexMatches(criteriaValue, testValue any) bool { - var patternVal string - switch t := criteriaValue.(type) { - case string: - patternVal = fmt.Sprintf("%v", criteriaValue) - case cty.Value: - if err := gocty.FromCtyValue(t, &patternVal); err != nil { - return false - } - default: - return false - } - - re, err := regexp.Compile(patternVal) - if err != nil { - return false - } - - match := re.MatchString(fmt.Sprintf("%v", testValue)) - return match -} diff --git a/pkg/iac/types/bool.go b/pkg/iac/types/bool.go deleted file mode 100755 index ae12d39198a2..000000000000 --- a/pkg/iac/types/bool.go +++ /dev/null @@ -1,96 +0,0 @@ -package types - -import ( - "encoding/json" -) - -type BoolValue struct { - BaseAttribute - value bool -} - -func (b BoolValue) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "value": b.value, - "metadata": b.metadata, - }) -} - -func (b *BoolValue) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["value"] != nil { - b.value = keys["value"].(bool) - } - if keys["metadata"] != nil { - raw, err := json.Marshal(keys["metadata"]) - if err != nil { - return err - } - var m Metadata - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.metadata = m - } - return nil -} - -func Bool(value bool, metadata Metadata) BoolValue { - return BoolValue{ - value: value, - BaseAttribute: BaseAttribute{metadata: metadata}, - } -} - -func BoolTest(value bool) BoolValue { - return Bool(value, NewTestMetadata()) -} - -func BoolDefault(value bool, metadata Metadata) BoolValue { - b := Bool(value, metadata) - b.BaseAttribute.metadata.isDefault = true - return b -} - -func BoolUnresolvable(m Metadata) BoolValue { - b := Bool(false, m) - b.BaseAttribute.metadata.isUnresolvable = true - return b -} - -func BoolExplicit(value bool, metadata Metadata) BoolValue { - b := Bool(value, metadata) - b.BaseAttribute.metadata.isExplicit = true - return b -} - -func (b BoolValue) Value() bool { - return b.value -} - -func (b BoolValue) GetRawValue() any { - return b.value -} - -func (b BoolValue) IsTrue() bool { - if b.metadata.isUnresolvable { - return false - } - return b.Value() -} - -func (b BoolValue) IsFalse() bool { - if b.metadata.isUnresolvable { - return false - } - return !b.Value() -} - -func (s BoolValue) ToRego() any { - m := s.metadata.ToRego().(map[string]any) - m["value"] = s.Value() - return m -} diff --git a/pkg/iac/types/bool_test.go b/pkg/iac/types/bool_test.go deleted file mode 100644 index a63a95eb7a63..000000000000 --- a/pkg/iac/types/bool_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var fakeMetadata = NewMetadata(NewRange("main.tf", 123, 123, "", nil), "") - -func Test_BoolValueIsTrue(t *testing.T) { - testCases := []struct { - desc string - value bool - expected bool - }{ - { - desc: "returns true when isTrue", - value: true, - expected: true, - }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - val := Bool(tC.value, fakeMetadata) - - assert.Equal(t, tC.expected, val.IsTrue()) - }) - } -} - -func Test_BoolJSON(t *testing.T) { - val := Bool(true, NewMetadata(NewRange("main.tf", 123, 123, "", nil), "")) - data, err := json.Marshal(val) - require.NoError(t, err) - - var restored BoolValue - err = json.Unmarshal(data, &restored) - require.NoError(t, err) - - assert.Equal(t, val, restored) -} diff --git a/pkg/iac/types/bytes.go b/pkg/iac/types/bytes.go deleted file mode 100755 index 01358ad3c8d9..000000000000 --- a/pkg/iac/types/bytes.go +++ /dev/null @@ -1,94 +0,0 @@ -package types - -import ( - "encoding/json" -) - -type BytesValue struct { - BaseAttribute - value []byte -} - -func (b BytesValue) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "value": b.value, - "metadata": b.metadata, - }) -} - -func (b *BytesValue) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["value"] != nil { - raw, err := json.Marshal(keys["value"]) - if err != nil { - return err - } - var m []byte - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.value = m - } - if keys["metadata"] != nil { - raw, err := json.Marshal(keys["metadata"]) - if err != nil { - return err - } - var m Metadata - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.metadata = m - } - return nil -} - -func (b BytesValue) Value() []byte { - return b.value -} - -func (b BytesValue) GetRawValue() any { - return b.value -} - -func (b BytesValue) Len() int { - return len(b.value) -} - -func (b BytesValue) GetMetadata() Metadata { - return b.metadata -} - -func Bytes(value []byte, m Metadata) BytesValue { - return BytesValue{ - value: value, - BaseAttribute: BaseAttribute{metadata: m}, - } -} - -func BytesDefault(value []byte, m Metadata) BytesValue { - b := Bytes(value, m) - b.BaseAttribute.metadata.isDefault = true - return b -} - -func BytesExplicit(value []byte, m Metadata) BytesValue { - b := Bytes(value, m) - b.BaseAttribute.metadata.isExplicit = true - return b -} - -func BytesUnresolvable(m Metadata) BytesValue { - b := Bytes(nil, m) - b.BaseAttribute.metadata.isUnresolvable = true - return b -} - -func (s BytesValue) ToRego() any { - m := s.metadata.ToRego().(map[string]any) - m["value"] = string(s.Value()) - return m -} diff --git a/pkg/iac/types/bytes_test.go b/pkg/iac/types/bytes_test.go deleted file mode 100644 index 709f187b1add..000000000000 --- a/pkg/iac/types/bytes_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_BytesJSON(t *testing.T) { - val := Bytes([]byte{0xff, 0x88, 0x00}, NewMetadata(NewRange("main.tf", 123, 123, "", nil), "")) - data, err := json.Marshal(val) - require.NoError(t, err) - - var restored BytesValue - err = json.Unmarshal(data, &restored) - require.NoError(t, err) - - assert.Equal(t, val, restored) -} diff --git a/pkg/iac/types/compliance.go b/pkg/iac/types/compliance_stub.go similarity index 100% rename from pkg/iac/types/compliance.go rename to pkg/iac/types/compliance_stub.go diff --git a/pkg/iac/types/fskey.go b/pkg/iac/types/fskey.go deleted file mode 100644 index a8b57af81723..000000000000 --- a/pkg/iac/types/fskey.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -import ( - "crypto/sha256" - "fmt" - "io/fs" -) - -func CreateFSKey(filesystem fs.FS) string { - if filesystem == nil { - return "" - } - return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%s%#[1]v", filesystem)))) -} diff --git a/pkg/iac/types/fskey_test.go b/pkg/iac/types/fskey_test.go deleted file mode 100644 index de91b32a0405..000000000000 --- a/pkg/iac/types/fskey_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package types - -import ( - "io/fs" - "os" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/set" -) - -func Test_FSKey(t *testing.T) { - - systems := []fs.FS{ - os.DirFS("."), - os.DirFS(".."), - memoryfs.New(), - memoryfs.New(), - } - - keys := set.New[string]() - - t.Run("uniqueness", func(t *testing.T) { - for _, system := range systems { - key := CreateFSKey(system) - assert.False(t, keys.Contains(key), "filesystem keys should be unique") - keys.Append(key) - } - }) - - t.Run("reproducible", func(t *testing.T) { - for _, system := range systems { - key := CreateFSKey(system) - assert.True(t, keys.Contains(key), "filesystem keys should be reproducible") - } - }) -} diff --git a/pkg/iac/types/int.go b/pkg/iac/types/int.go deleted file mode 100755 index 24c06b72753e..000000000000 --- a/pkg/iac/types/int.go +++ /dev/null @@ -1,118 +0,0 @@ -package types - -import ( - "encoding/json" -) - -type IntValue struct { - BaseAttribute - value int -} - -func (b IntValue) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "value": b.value, - "metadata": b.metadata, - }) -} - -func (b *IntValue) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["value"] != nil { - b.value = int(keys["value"].(float64)) - } - if keys["metadata"] != nil { - raw, err := json.Marshal(keys["metadata"]) - if err != nil { - return err - } - var m Metadata - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.metadata = m - } - return nil -} - -func Int(value int, m Metadata) IntValue { - return IntValue{ - value: value, - BaseAttribute: BaseAttribute{metadata: m}, - } -} - -func IntTest(value int) IntValue { - return Int(value, NewTestMetadata()) -} - -func IntFromInt32(value int32, m Metadata) IntValue { - return Int(int(value), m) -} - -func IntDefault(value int, m Metadata) IntValue { - b := Int(value, m) - b.BaseAttribute.metadata.isDefault = true - return b -} - -func IntUnresolvable(m Metadata) IntValue { - b := Int(0, m) - b.BaseAttribute.metadata.isUnresolvable = true - return b -} - -func IntExplicit(value int, m Metadata) IntValue { - b := Int(value, m) - b.BaseAttribute.metadata.isExplicit = true - return b -} - -func (b IntValue) GetMetadata() Metadata { - return b.metadata -} - -func (b IntValue) Value() int { - return b.value -} - -func (b IntValue) GetRawValue() any { - return b.value -} - -func (b IntValue) NotEqualTo(i int) bool { - if b.metadata.isUnresolvable { - return false - } - return b.value != i -} - -func (b IntValue) EqualTo(i int) bool { - if b.metadata.isUnresolvable { - return false - } - return b.value == i -} - -func (b IntValue) LessThan(i int) bool { - if b.metadata.isUnresolvable { - return false - } - return b.value < i -} - -func (b IntValue) GreaterThan(i int) bool { - if b.metadata.isUnresolvable { - return false - } - return b.value > i -} - -func (s IntValue) ToRego() any { - m := s.metadata.ToRego().(map[string]any) - m["value"] = s.Value() - return m -} diff --git a/pkg/iac/types/int_test.go b/pkg/iac/types/int_test.go deleted file mode 100644 index 83e2d65cf0b0..000000000000 --- a/pkg/iac/types/int_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_IntJSON(t *testing.T) { - val := Int(0x66, NewMetadata(NewRange("main.tf", 123, 123, "", nil), "")) - data, err := json.Marshal(val) - require.NoError(t, err) - - var restored IntValue - err = json.Unmarshal(data, &restored) - require.NoError(t, err) - - assert.Equal(t, val, restored) -} diff --git a/pkg/iac/types/map.go b/pkg/iac/types/map.go deleted file mode 100755 index 01d90543b4e8..000000000000 --- a/pkg/iac/types/map.go +++ /dev/null @@ -1,92 +0,0 @@ -package types - -import ( - "encoding/json" -) - -type MapValue struct { - BaseAttribute - value map[string]string -} - -func (b MapValue) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "value": b.value, - "metadata": b.metadata, - }) -} - -func (b *MapValue) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["value"] != nil { - var target map[string]string - raw, err := json.Marshal(keys["value"]) - if err != nil { - return err - } - if err := json.Unmarshal(raw, &target); err != nil { - return err - } - b.value = target - } - if keys["metadata"] != nil { - raw, err := json.Marshal(keys["metadata"]) - if err != nil { - return err - } - var m Metadata - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.metadata = m - } - return nil -} - -func Map(value map[string]string, m Metadata) MapValue { - return MapValue{ - value: value, - BaseAttribute: BaseAttribute{metadata: m}, - } -} - -func MapDefault(value map[string]string, m Metadata) MapValue { - b := Map(value, m) - b.BaseAttribute.metadata.isDefault = true - return b -} - -func MapExplicit(value map[string]string, m Metadata) MapValue { - b := Map(value, m) - b.BaseAttribute.metadata.isExplicit = true - return b -} - -func (b MapValue) Value() map[string]string { - return b.value -} - -func (b MapValue) GetRawValue() any { - return b.value -} - -func (b MapValue) Len() int { - return len(b.value) -} - -func (b MapValue) HasKey(key string) bool { - if b.value == nil { - return false - } - _, ok := b.value[key] - return ok -} - -func (s MapValue) ToRego() any { - m := s.metadata.ToRego().(map[string]any) - m["value"] = s.Value() - return m -} diff --git a/pkg/iac/types/map_test.go b/pkg/iac/types/map_test.go deleted file mode 100644 index 1dba9504b30c..000000000000 --- a/pkg/iac/types/map_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_MapJSON(t *testing.T) { - val := Map(map[string]string{ - "yeah": "it", - "seems": "to", - "work": "fine", - }, NewMetadata(NewRange("main.tf", 123, 123, "", nil), "")) - data, err := json.Marshal(val) - require.NoError(t, err) - - var restored MapValue - err = json.Unmarshal(data, &restored) - require.NoError(t, err) - - assert.Equal(t, val, restored) -} diff --git a/pkg/iac/types/metadata.go b/pkg/iac/types/metadata.go deleted file mode 100755 index c71460441622..000000000000 --- a/pkg/iac/types/metadata.go +++ /dev/null @@ -1,223 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - "strings" -) - -type Metadata struct { - rnge Range - ref string - isManaged bool - isDefault bool - isExplicit bool - isUnresolvable bool - parent *Metadata - internal any -} - -func (m Metadata) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "range": m.rnge, - "ref": m.ref, - "managed": m.isManaged, - "default": m.isDefault, - "explicit": m.isExplicit, - "unresolvable": m.isUnresolvable, - "parent": m.parent, - }) -} - -func (m *Metadata) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["range"] != nil { - raw, err := json.Marshal(keys["range"]) - if err != nil { - return err - } - var r Range - if err := json.Unmarshal(raw, &r); err != nil { - return err - } - m.rnge = r - } - if keys["ref"] != nil { - m.ref = keys["ref"].(string) - } - if keys["managed"] != nil { - m.isManaged = keys["managed"].(bool) - } - if keys["default"] != nil { - m.isDefault = keys["default"].(bool) - } - if keys["explicit"] != nil { - m.isExplicit = keys["explicit"].(bool) - } - if keys["unresolvable"] != nil { - m.isUnresolvable = keys["unresolvable"].(bool) - } - if keys["parent"] != nil { - if _, ok := keys["parent"].(map[string]any); ok { - raw, err := json.Marshal(keys["parent"]) - if err != nil { - return err - } - var parent Metadata - if err := json.Unmarshal(raw, &parent); err != nil { - return err - } - m.parent = &parent - } - } - return nil -} - -func (m *Metadata) ToRego() any { - input := map[string]any{ - "filepath": m.Range().GetLocalFilename(), - "startline": m.Range().GetStartLine(), - "endline": m.Range().GetEndLine(), - "sourceprefix": m.Range().GetSourcePrefix(), - "managed": m.isManaged, - "explicit": m.isExplicit, - "unresolvable": m.isUnresolvable, - "fskey": CreateFSKey(m.Range().GetFS()), - "resource": m.Reference(), - } - if m.parent != nil { - input["parent"] = m.parent.ToRego() - } - return input -} - -func NewMetadata(r Range, ref string) Metadata { - return Metadata{ - rnge: r, - ref: ref, - isManaged: true, - } -} - -func NewUnresolvableMetadata(r Range, ref string) Metadata { - unres := NewMetadata(r, ref) - unres.isUnresolvable = true - return unres -} - -func NewExplicitMetadata(r Range, ref string) Metadata { - m := NewMetadata(r, ref) - m.isExplicit = true - return m -} - -func (m Metadata) WithParent(p Metadata) Metadata { - m.parent = &p - return m -} - -func (m *Metadata) SetParentPtr(p *Metadata) { - m.parent = p -} - -func (m Metadata) Parent() *Metadata { - return m.parent -} - -func (m Metadata) Root() Metadata { - meta := &m - for meta.Parent() != nil { - meta = meta.Parent() - } - return *meta -} - -func (m Metadata) WithInternal(internal any) Metadata { - m.internal = internal - return m -} - -func (m Metadata) Internal() any { - return m.internal -} - -func (m Metadata) IsMultiLine() bool { - return m.rnge.GetStartLine() < m.rnge.GetEndLine() -} - -func NewUnmanagedMetadata() Metadata { - m := NewMetadata(NewRange("", 0, 0, "", nil), "") - m.isManaged = false - return m -} - -func NewTestMetadata() Metadata { - return NewMetadata(NewRange("test.test", 123, 123, "", nil), "") -} - -func NewApiMetadata(provider string, parts ...string) Metadata { - return NewMetadata(NewRange(fmt.Sprintf("/%s/%s", provider, strings.Join(parts, "/")), 0, 0, "", nil), "") -} - -func NewRemoteMetadata(id string) Metadata { - return NewMetadata(NewRange(id, 0, 0, "remote", nil), id) -} - -func (m Metadata) IsDefault() bool { - return m.isDefault -} - -func (m Metadata) IsResolvable() bool { - return !m.isUnresolvable -} - -func (m Metadata) IsExplicit() bool { - return m.isExplicit -} - -func (m Metadata) String() string { - return m.ref -} - -func (m Metadata) Reference() string { - return m.ref -} - -func (m Metadata) Range() Range { - return m.rnge -} - -func (m Metadata) IsManaged() bool { - return m.isManaged -} - -func (m Metadata) IsUnmanaged() bool { - return !m.isManaged -} - -type BaseAttribute struct { - metadata Metadata -} - -func (b BaseAttribute) GetMetadata() Metadata { - return b.metadata -} - -func (m Metadata) GetMetadata() Metadata { - return m -} - -func (m Metadata) GetRawValue() any { - return nil -} - -func (m *Metadata) SetReference(ref string) { - m.ref = ref -} - -func (m *Metadata) SetRange(r Range) { - m.rnge = r -} diff --git a/pkg/iac/types/metadata_test.go b/pkg/iac/types/metadata_test.go deleted file mode 100644 index f7d46bbf9dbd..000000000000 --- a/pkg/iac/types/metadata_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_MetadataToRego(t *testing.T) { - m1 := NewTestMetadata() - m1.isUnresolvable = true - expected := map[string]any{ - "endline": 123, - "explicit": false, - "filepath": "test.test", - "fskey": "", - "managed": true, - "unresolvable": true, - "resource": "", - "sourceprefix": "", - "startline": 123, - } - assert.Equal(t, expected, m1.ToRego()) - m2 := NewTestMetadata() - m1.SetParentPtr(&m2) - expected["parent"] = map[string]any{ - "endline": 123, - "explicit": false, - "filepath": "test.test", - "fskey": "", - "managed": true, - "unresolvable": false, - "resource": "", - "sourceprefix": "", - "startline": 123, - } - assert.Equal(t, expected, m1.ToRego()) -} diff --git a/pkg/iac/types/range.go b/pkg/iac/types/range.go deleted file mode 100755 index 7bc701ae0918..000000000000 --- a/pkg/iac/types/range.go +++ /dev/null @@ -1,165 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - "io/fs" - "path" -) - -func NewRange(filename string, startLine, endLine int, sourcePrefix string, srcFS fs.FS) Range { - r := Range{ - filename: filename, - startLine: startLine, - endLine: endLine, - fs: srcFS, - fsKey: CreateFSKey(srcFS), - sourcePrefix: sourcePrefix, - } - return r -} - -func NewRangeWithLogicalSource(filename string, startLine int, endLine int, sourcePrefix string, - srcFS fs.FS) Range { - r := Range{ - filename: filename, - startLine: startLine, - endLine: endLine, - fs: srcFS, - fsKey: CreateFSKey(srcFS), - sourcePrefix: sourcePrefix, - isLogicalSource: true, - } - return r -} - -func NewRangeWithFSKey(filename string, startLine, endLine int, sourcePrefix, fsKey string, fsys fs.FS) Range { - r := Range{ - filename: filename, - startLine: startLine, - endLine: endLine, - fs: fsys, - fsKey: fsKey, - sourcePrefix: sourcePrefix, - } - return r -} - -type Range struct { - filename string - startLine int - endLine int - sourcePrefix string - isLogicalSource bool - fs fs.FS - fsKey string -} - -func (r Range) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "filename": r.filename, - "startLine": r.startLine, - "endLine": r.endLine, - "sourcePrefix": r.sourcePrefix, - "fsKey": r.fsKey, - "isLogicalSource": r.isLogicalSource, - }) -} - -func (r *Range) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["filename"] != nil { - r.filename = keys["filename"].(string) - } - if keys["startLine"] != nil { - r.startLine = int(keys["startLine"].(float64)) - } - if keys["endLine"] != nil { - r.endLine = int(keys["endLine"].(float64)) - } - if keys["sourcePrefix"] != nil { - r.sourcePrefix = keys["sourcePrefix"].(string) - } - if keys["fsKey"] != nil { - r.fsKey = keys["fsKey"].(string) - } - if keys["isLogicalSource"] != nil { - r.isLogicalSource = keys["isLogicalSource"].(bool) - } - return nil -} - -func (r Range) GetFSKey() string { - return r.fsKey -} - -func (r Range) LineCount() int { - if r.endLine == 0 { - return 0 - } - return (r.endLine - r.startLine) + 1 -} - -func (r Range) GetFilename() string { - if r.sourcePrefix == "" { - return r.filename - } - if r.isLogicalSource { - return fmt.Sprintf("%s:%s", r.sourcePrefix, r.filename) - } - return path.Join(r.sourcePrefix, r.filename) -} - -func (r Range) GetLocalFilename() string { - return r.filename -} - -func (r Range) GetStartLine() int { - return r.startLine -} - -func (r Range) GetEndLine() int { - return r.endLine -} - -func (r Range) IsMultiLine() bool { - return r.startLine < r.endLine -} - -func (r Range) String() string { - if r.startLine != r.endLine { - return fmt.Sprintf("%s:%d-%d", r.GetFilename(), r.startLine, r.endLine) - } - if r.startLine == 0 && r.endLine == 0 { - return r.GetFilename() - } - return fmt.Sprintf("%s:%d", r.GetFilename(), r.startLine) -} - -func (r Range) GetFS() fs.FS { - return r.fs -} - -func (r Range) GetSourcePrefix() string { - return r.sourcePrefix -} - -func (r Range) Validate() error { - if r.startLine < 0 || r.endLine < 0 || r.startLine > r.endLine { - return fmt.Errorf("invalid range: %s", r.String()) - } - return nil -} - -// Includes returns true if 'other' is strictly inside 'r'. -func (r Range) Includes(other Range) bool { - return r.startLine < other.startLine && r.endLine > other.endLine -} - -// Covers returns true if 'r' fully contains 'other'. -func (r Range) Covers(other Range) bool { - return r.startLine <= other.startLine && r.endLine >= other.endLine -} diff --git a/pkg/iac/types/rules/rule.go b/pkg/iac/types/rules/rule.go deleted file mode 100644 index b86a4ad6d11f..000000000000 --- a/pkg/iac/types/rules/rule.go +++ /dev/null @@ -1,18 +0,0 @@ -package rules - -import ( - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -type RegisteredRule struct { - scan.Rule - Number int -} - -func (r *RegisteredRule) GetRule() scan.Rule { - return r.Rule -} - -func (r *RegisteredRule) AddLink(link string) { - r.Rule.Links = append([]string{link}, r.Rule.Links...) -} diff --git a/pkg/iac/types/sources.go b/pkg/iac/types/sources.go deleted file mode 100644 index 02e43dac3a35..000000000000 --- a/pkg/iac/types/sources.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -type Source string - -const ( - SourceDockerfile Source = "dockerfile" - SourceKubernetes Source = "kubernetes" - SourceRbac Source = "rbac" // deprecated - please use "kubernetes" instead - SourceDefsec Source = "defsec" // deprecated - please use "cloud" instead - SourceCloud Source = "cloud" - SourceYAML Source = "yaml" - SourceJSON Source = "json" - SourceTOML Source = "toml" -) diff --git a/pkg/iac/types/string.go b/pkg/iac/types/string.go deleted file mode 100755 index 0630d18b9c8e..000000000000 --- a/pkg/iac/types/string.go +++ /dev/null @@ -1,194 +0,0 @@ -package types - -import ( - "encoding/json" - "strings" -) - -type StringEqualityOption int - -const ( - IgnoreCase StringEqualityOption = iota - IsPallindrome - IgnoreWhitespace -) - -func String(str string, m Metadata) StringValue { - return StringValue{ - value: str, - BaseAttribute: BaseAttribute{metadata: m}, - } -} - -func StringDefault(value string, m Metadata) StringValue { - b := String(value, m) - b.BaseAttribute.metadata.isDefault = true - return b -} - -func StringUnresolvable(m Metadata) StringValue { - b := String("", m) - b.BaseAttribute.metadata.isUnresolvable = true - return b -} - -func StringExplicit(value string, m Metadata) StringValue { - b := String(value, m) - b.BaseAttribute.metadata.isExplicit = true - return b -} - -func StringTest(value string) StringValue { - return String(value, NewTestMetadata()) -} - -type StringValueList []StringValue - -type StringValue struct { - BaseAttribute - value string -} - -func (l StringValueList) AsStrings() (output []string) { - for _, item := range l { - output = append(output, item.Value()) - } - return output -} - -type stringCheckFunc func(string, string) bool - -func (b StringValue) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "value": b.value, - "metadata": b.metadata, - }) -} - -func (b *StringValue) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["value"] != nil { - b.value = keys["value"].(string) - } - if keys["metadata"] != nil { - raw, err := json.Marshal(keys["metadata"]) - if err != nil { - return err - } - var m Metadata - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.metadata = m - } - return nil -} - -func (s StringValue) ToRego() any { - m := s.metadata.ToRego().(map[string]any) - m["value"] = s.Value() - return m -} - -func (s StringValue) IsOneOf(values ...string) bool { - if s.metadata.isUnresolvable { - return false - } - for _, value := range values { - if value == s.value { - return true - } - } - return false -} - -func (s StringValue) GetMetadata() Metadata { - return s.metadata -} - -func (s StringValue) Value() string { - return s.value -} - -func (b StringValue) GetRawValue() any { - return b.value -} - -func (s StringValue) IsEmpty() bool { - if s.metadata.isUnresolvable { - return false - } - return s.value == "" -} - -func (s StringValue) IsNotEmpty() bool { - if s.metadata.isUnresolvable { - return false - } - return s.value != "" -} - -func (s StringValue) EqualTo(value string, equalityOptions ...StringEqualityOption) bool { - if s.metadata.isUnresolvable { - return false - } - - return s.executePredicate(value, func(a, b string) bool { return a == b }, equalityOptions...) -} - -func (s StringValue) NotEqualTo(value string, equalityOptions ...StringEqualityOption) bool { - if s.metadata.isUnresolvable { - return false - } - - return !s.EqualTo(value, equalityOptions...) -} - -func (s StringValue) StartsWith(prefix string, equalityOptions ...StringEqualityOption) bool { - if s.metadata.isUnresolvable { - return false - } - - return s.executePredicate(prefix, strings.HasPrefix, equalityOptions...) -} - -func (s StringValue) EndsWith(suffix string, equalityOptions ...StringEqualityOption) bool { - if s.metadata.isUnresolvable { - return false - } - return s.executePredicate(suffix, strings.HasSuffix, equalityOptions...) -} - -func (s StringValue) Contains(value string, equalityOptions ...StringEqualityOption) bool { - if s.metadata.isUnresolvable { - return false - } - return s.executePredicate(value, strings.Contains, equalityOptions...) -} - -func (s StringValue) executePredicate(value string, fn stringCheckFunc, equalityOptions ...StringEqualityOption) bool { - subjectString := s.value - searchString := value - - for _, eqOpt := range equalityOptions { - switch eqOpt { - case IgnoreCase: - subjectString = strings.ToLower(subjectString) - searchString = strings.ToLower(searchString) - case IsPallindrome: - var result string - for _, v := range subjectString { - result = string(v) + result - } - subjectString = result - case IgnoreWhitespace: - subjectString = strings.ReplaceAll(subjectString, " ", "") - searchString = strings.ReplaceAll(searchString, " ", "") - } - } - - return fn(subjectString, searchString) -} diff --git a/pkg/iac/types/string_test.go b/pkg/iac/types/string_test.go deleted file mode 100644 index a923d193d85e..000000000000 --- a/pkg/iac/types/string_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_StringValueEqualTo(t *testing.T) { - testCases := []struct { - desc string - input string - check string - ignoreCase bool - expected bool - }{ - { - desc: "return truw when string is equal", - input: "something", - check: "", - expected: false, - }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - }) - } -} - -func Test_StringValueStartsWith(t *testing.T) { - testCases := []struct { - desc string - input string - prefix string - ignoreCase bool - expected bool - }{ - { - desc: "return true when starts with", - input: "something", - prefix: "some", - expected: true, - }, - { - desc: "return false when does not start with", - input: "something", - prefix: "nothing", - expected: false, - }, - { - desc: "return true when starts with", - input: "something", - prefix: "SOME", - ignoreCase: true, - expected: true, - }, - { - desc: "return false when does not start with", - input: "something", - prefix: "SOME", - expected: false, - }, - } - for _, tC := range testCases { - t.Run(tC.desc, func(t *testing.T) { - - val := String(tC.input, fakeMetadata) - - var options []StringEqualityOption - - if tC.ignoreCase { - options = append(options, IgnoreCase) - } - - assert.Equal(t, tC.expected, val.StartsWith(tC.prefix, options...)) - }) - } -} - -func Test_StringJSON(t *testing.T) { - val := String("hello world", NewMetadata(NewRange("main.tf", 123, 123, "", nil), "")) - data, err := json.Marshal(val) - require.NoError(t, err) - - var restored StringValue - err = json.Unmarshal(data, &restored) - require.NoError(t, err) - - assert.Equal(t, val, restored) -} diff --git a/pkg/iac/types/time.go b/pkg/iac/types/time.go deleted file mode 100755 index e04d090964a8..000000000000 --- a/pkg/iac/types/time.go +++ /dev/null @@ -1,102 +0,0 @@ -package types - -import ( - "encoding/json" - "time" -) - -type TimeValue struct { - BaseAttribute - value time.Time -} - -func (b TimeValue) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "value": b.value.Format(time.RFC3339), - "metadata": b.metadata, - }) -} - -func (b *TimeValue) UnmarshalJSON(data []byte) error { - var keys map[string]any - if err := json.Unmarshal(data, &keys); err != nil { - return err - } - if keys["value"] != nil { - if ti, err := time.Parse(time.RFC3339, keys["value"].(string)); err == nil { - b.value = ti - } - } - if keys["metadata"] != nil { - raw, err := json.Marshal(keys["metadata"]) - if err != nil { - return err - } - var m Metadata - if err := json.Unmarshal(raw, &m); err != nil { - return err - } - b.metadata = m - } - return nil -} - -func Time(value time.Time, m Metadata) TimeValue { - return TimeValue{ - value: value, - BaseAttribute: BaseAttribute{metadata: m}, - } -} - -func TimeDefault(value time.Time, m Metadata) TimeValue { - b := Time(value, m) - b.BaseAttribute.metadata.isDefault = true - return b -} - -func TimeExplicit(value time.Time, m Metadata) TimeValue { - b := Time(value, m) - b.BaseAttribute.metadata.isExplicit = true - return b -} - -func TimeUnresolvable(m Metadata) TimeValue { - b := Time(time.Time{}, m) - b.BaseAttribute.metadata.isUnresolvable = true - return b -} - -func (t TimeValue) Value() time.Time { - return t.value -} - -func (t TimeValue) GetRawValue() any { - return t.value -} - -func (t TimeValue) IsNever() bool { - if t.GetMetadata().isUnresolvable { - return false - } - return t.value.IsZero() -} - -func (t TimeValue) Before(i time.Time) bool { - if t.metadata.isUnresolvable { - return false - } - return t.value.Before(i) -} - -func (t TimeValue) After(i time.Time) bool { - if t.metadata.isUnresolvable { - return false - } - return t.value.After(i) -} - -func (t TimeValue) ToRego() any { - m := t.metadata.ToRego().(map[string]any) - m["value"] = t.Value().Format(time.RFC3339) - return m -} diff --git a/pkg/iac/types/time_test.go b/pkg/iac/types/time_test.go deleted file mode 100644 index 5d38b0dfb570..000000000000 --- a/pkg/iac/types/time_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_TimeJSON(t *testing.T) { - val := Time(time.Now(), NewMetadata(NewRange("main.tf", 123, 123, "", nil), "")) - data, err := json.Marshal(val) - require.NoError(t, err) - - var restored TimeValue - err = json.Unmarshal(data, &restored) - require.NoError(t, err) - - assert.Equal(t, val.value.Format(time.RFC3339), restored.Value().Format(time.RFC3339)) - assert.Equal(t, val.metadata, restored.metadata) -} diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 1655c3bc1fdd..c6cec0f1ba75 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -18,7 +18,6 @@ import ( "github.com/aquasecurity/trivy/pkg/dependency/parser/java/jar" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/oci" ) const ( @@ -98,21 +97,6 @@ func (u *Updater) isNewDB(ctx context.Context, meta db.Metadata) bool { return false } -func (u *Updater) downloadDB(ctx context.Context) error { - log.InfoContext(ctx, "Downloading Java DB...") - - artifacts := oci.NewArtifacts(u.repos, u.registryOption) - downloadOpt := oci.DownloadOption{ - MediaType: mediaType, - Quiet: u.quiet, - } - if err := artifacts.Download(ctx, u.dbDir, downloadOpt); err != nil { - return xerrors.Errorf("failed to download Java DB: %w", err) - } - - return nil -} - func Init(cacheDir string, javaDBRepositories []name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { updater = &Updater{ repos: javaDBRepositories, diff --git a/pkg/javadb/download_db.go b/pkg/javadb/download_db.go new file mode 100644 index 000000000000..8d795aa50422 --- /dev/null +++ b/pkg/javadb/download_db.go @@ -0,0 +1,26 @@ +//go:build !trivy_no_javadb + +package javadb + +import ( + "context" + + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/oci" + "golang.org/x/xerrors" +) + +func (u *Updater) downloadDB(ctx context.Context) error { + log.InfoContext(ctx, "Downloading Java DB...") + + artifacts := oci.NewArtifacts(u.repos, u.registryOption) + downloadOpt := oci.DownloadOption{ + MediaType: mediaType, + Quiet: u.quiet, + } + if err := artifacts.Download(ctx, u.dbDir, downloadOpt); err != nil { + return xerrors.Errorf("failed to download Java DB: %w", err) + } + + return nil +} diff --git a/pkg/javadb/download_no_db.go b/pkg/javadb/download_no_db.go new file mode 100644 index 000000000000..578783ed85e4 --- /dev/null +++ b/pkg/javadb/download_no_db.go @@ -0,0 +1,13 @@ +//go:build trivy_no_javadb + +package javadb + +import ( + "context" + + "golang.org/x/xerrors" +) + +func (u *Updater) downloadDB(ctx context.Context) error { + return xerrors.Errorf("download Java DB unavailable") +} diff --git a/pkg/k8s/commands/cluster.go b/pkg/k8s/commands/cluster.go deleted file mode 100644 index 11dd5e0d3a00..000000000000 --- a/pkg/k8s/commands/cluster.go +++ /dev/null @@ -1,102 +0,0 @@ -package commands - -import ( - "context" - - "golang.org/x/xerrors" - - trivy_checks "github.com/aquasecurity/trivy-checks" - k8sArtifacts "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - "github.com/aquasecurity/trivy-kubernetes/pkg/k8s" - "github.com/aquasecurity/trivy-kubernetes/pkg/trivyk8s" - "github.com/aquasecurity/trivy/pkg/commands/operation" - "github.com/aquasecurity/trivy/pkg/flag" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/types" -) - -// clusterRun runs scan on kubernetes cluster -func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) error { - if err := validateReportArguments(opts); err != nil { - return err - } - var artifacts []*k8sArtifacts.Artifact - var err error - switch opts.Format { - case types.FormatCycloneDX: - artifacts, err = trivyk8s.New(cluster).ListClusterBomInfo(ctx) - if err != nil { - return xerrors.Errorf("get k8s artifacts with node info error: %w", err) - } - case types.FormatJSON, types.FormatTable: - k8sOpts := []trivyk8s.K8sOption{ - trivyk8s.WithExcludeNamespaces(opts.ExcludeNamespaces), - trivyk8s.WithIncludeNamespaces(opts.IncludeNamespaces), - trivyk8s.WithExcludeKinds(opts.ExcludeKinds), - trivyk8s.WithIncludeKinds(opts.IncludeKinds), - trivyk8s.WithExcludeOwned(opts.ExcludeOwned), - } - if opts.Scanners.AnyEnabled(types.MisconfigScanner) && !opts.DisableNodeCollector { - artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifactAndNodeInfo(ctx, nodeCollectorOptions(ctx, opts)...) - if err != nil { - return xerrors.Errorf("get k8s artifacts with node info error: %w", err) - } - } else { - artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifacts(ctx) - if err != nil { - return xerrors.Errorf("get k8s artifacts error: %w", err) - } - } - default: - return xerrors.Errorf(`unknown format %q. Use "json" or "table" or "cyclonedx"`, opts.Format) - } - - if !opts.DisableNodeCollector && !opts.Quiet { - log.InfoContext(ctx, "Node scanning is enabled") - log.InfoContext(ctx, "If you want to disable Node scanning via an in-cluster Job, please try '--disable-node-collector' to disable the Node-Collector job.") - } - runner := newRunner(opts, cluster.GetCurrentContext()) - return runner.run(ctx, artifacts) -} - -func nodeCollectorOptions(ctx context.Context, opts flag.Options) []trivyk8s.NodeCollectorOption { - nodeCollectorOptions := []trivyk8s.NodeCollectorOption{ - trivyk8s.WithScanJobNamespace(opts.NodeCollectorNamespace), - trivyk8s.WithIgnoreLabels(opts.ExcludeNodes), - trivyk8s.WithScanJobImageRef(opts.NodeCollectorImageRef), - trivyk8s.WithTolerations(opts.Tolerations), - } - - ctx = log.WithContextPrefix(ctx, log.PrefixMisconfiguration) - contentPath, err := operation.InitBuiltinChecks(ctx, opts.CacheDir, opts.Quiet, opts.SkipCheckUpdate, - opts.MisconfOptions.ChecksBundleRepository, opts.RegistryOpts()) - if err != nil { - log.Error("Falling back to embedded checks", log.Err(err)) - nodeCollectorOptions = append(nodeCollectorOptions, - []trivyk8s.NodeCollectorOption{ - trivyk8s.WithEmbeddedCommandFileSystem(trivy_checks.EmbeddedK8sCommandsFileSystem), - trivyk8s.WithEmbeddedNodeConfigFilesystem(trivy_checks.EmbeddedConfigCommandsFileSystem), - }...) - } - - complianceCommandsIDs := getComplianceCommands(opts) - nodeCollectorOptions = append(nodeCollectorOptions, []trivyk8s.NodeCollectorOption{ - trivyk8s.WithCommandPaths(contentPath), - trivyk8s.WithSpecCommandIds(complianceCommandsIDs), - }...) - return nodeCollectorOptions -} - -func getComplianceCommands(opts flag.Options) []string { - var commands []string - if opts.Compliance.Spec.ID != "" { - for _, control := range opts.Compliance.Spec.Controls { - for _, command := range control.Commands { - if command.ID != "" { - commands = append(commands, command.ID) - } - } - } - } - return commands -} diff --git a/pkg/k8s/commands/run.go b/pkg/k8s/commands/run.go deleted file mode 100644 index 18f6f1883fd8..000000000000 --- a/pkg/k8s/commands/run.go +++ /dev/null @@ -1,157 +0,0 @@ -package commands - -import ( - "context" - "errors" - "fmt" - - "github.com/spf13/viper" - "golang.org/x/xerrors" - - k8sArtifacts "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - "github.com/aquasecurity/trivy-kubernetes/pkg/k8s" - cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" - "github.com/aquasecurity/trivy/pkg/commands/operation" - cr "github.com/aquasecurity/trivy/pkg/compliance/report" - "github.com/aquasecurity/trivy/pkg/flag" - k8sRep "github.com/aquasecurity/trivy/pkg/k8s" - "github.com/aquasecurity/trivy/pkg/k8s/report" - "github.com/aquasecurity/trivy/pkg/k8s/scanner" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/types" - "github.com/aquasecurity/trivy/pkg/version/doc" -) - -// Run runs a k8s scan -func Run(ctx context.Context, args []string, opts flag.Options) error { - clusterOptions := []k8s.ClusterOption{ - k8s.WithKubeConfig(opts.K8sOptions.KubeConfig), - k8s.WithBurst(opts.K8sOptions.Burst), - k8s.WithQPS(opts.K8sOptions.QPS), - } - if len(args) > 0 { - clusterOptions = append(clusterOptions, k8s.WithContext(args[0])) - } - cluster, err := k8s.GetCluster(clusterOptions...) - if err != nil { - return xerrors.Errorf("failed getting k8s cluster: %w", err) - } - ctx, cancel := context.WithTimeout(ctx, opts.Timeout) - - defer func() { - cancel() - if errors.Is(err, context.DeadlineExceeded) { - // e.g. https://trivy.dev/latest/docs/configuration - log.WarnContext(ctx, fmt.Sprintf("Provide a higher timeout value, see %s", doc.URL("/docs/configuration/", ""))) - } - }() - opts.K8sVersion = cluster.GetClusterVersion() - return clusterRun(ctx, opts, cluster) -} - -type runner struct { - flagOpts flag.Options - cluster string -} - -func newRunner(flagOpts flag.Options, cluster string) *runner { - return &runner{ - flagOpts, - cluster, - } -} - -func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) error { - runner, err := cmd.NewRunner(ctx, r.flagOpts) - if err != nil { - if errors.Is(err, cmd.SkipScan) { - return nil - } - return xerrors.Errorf("init error: %w", err) - } - defer func() { - if err := runner.Close(ctx); err != nil { - log.ErrorContext(ctx, "failed to close runner: %s", err) - } - }() - - s := scanner.NewScanner(r.cluster, runner, r.flagOpts) - - // set scanners types by spec - if r.flagOpts.Compliance.Spec.ID != "" { - scanners, err := r.flagOpts.Compliance.Scanners() - if err != nil { - return xerrors.Errorf("scanner error: %w", err) - } - r.flagOpts.ScanOptions.Scanners = scanners - } - var rpt report.Report - log.Info("Scanning K8s...", log.String("K8s", r.cluster)) - rpt, err = s.Scan(ctx, artifacts) - if err != nil { - return xerrors.Errorf("k8s scan error: %w", err) - } - - output, cleanup, err := r.flagOpts.OutputWriter(ctx) - if err != nil { - return xerrors.Errorf("failed to create output file: %w", err) - } - defer cleanup() - - if r.flagOpts.Compliance.Spec.ID != "" { - var scanResults []types.Results - for _, rss := range rpt.Resources { - scanResults = append(scanResults, rss.Results) - } - complianceReport, err := cr.BuildComplianceReport(scanResults, r.flagOpts.Compliance) - if err != nil { - return xerrors.Errorf("compliance report build error: %w", err) - } - return cr.Write(ctx, complianceReport, cr.Option{ - Format: r.flagOpts.Format, - Report: r.flagOpts.ReportFormat, - Output: output, - }) - } - - if err := k8sRep.Write(ctx, rpt, report.Option{ - Format: r.flagOpts.Format, - Report: r.flagOpts.ReportFormat, - Output: output, - Severities: r.flagOpts.Severities, - Scanners: r.flagOpts.ScanOptions.Scanners, - APIVersion: r.flagOpts.AppVersion, - }); err != nil { - return xerrors.Errorf("unable to write results: %w", err) - } - - return operation.Exit(r.flagOpts, rpt.Failed(), types.Metadata{}) -} - -// Full-cluster scanning with '--format table' without explicit '--report all' is not allowed so that it won't mess up user's terminal. -// To show all the results, user needs to specify "--report all" explicitly -// even though the default value of "--report" is "all". -// -// e.g. -// $ trivy k8s --report all cluster -// $ trivy k8s --report all all -// -// Or they can use "--format json" with implicit "--report all". -// -// e.g. $ trivy k8s --format json cluster // All the results are shown in JSON -// -// Single resource scanning is allowed with implicit "--report all". -// -// e.g. $ trivy k8s pod myapp -func validateReportArguments(opts flag.Options) error { - if opts.ReportFormat == "all" && - !viper.IsSet("report") && - opts.Format == "table" { - - m := "All the results in the table format can mess up your terminal. Use \"--report all\" to tell Trivy to output it to your terminal anyway, or consider \"--report summary\" to show the summary output." - - return xerrors.New(m) - } - - return nil -} diff --git a/pkg/k8s/commands/run_stub.go b/pkg/k8s/commands/run_stub.go new file mode 100644 index 000000000000..f6408c8f5d61 --- /dev/null +++ b/pkg/k8s/commands/run_stub.go @@ -0,0 +1,13 @@ +package commands + +import ( + "context" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/flag" +) + +func Run(ctx context.Context, args []string, opts flag.Options) error { + return xerrors.New("pkg/compliance not implemented") +} diff --git a/pkg/k8s/inject.go b/pkg/k8s/inject.go deleted file mode 100644 index e71b11fe57d9..000000000000 --- a/pkg/k8s/inject.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build wireinject -// +build wireinject - -package k8s - -import ( - "github.com/google/wire" - - "github.com/aquasecurity/trivy/pkg/cache" -) - -func initializeScanK8s(localArtifactCache cache.LocalArtifactCache) *ScanKubernetes { - wire.Build(ScanSuperSet) - return &ScanKubernetes{} -} diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go deleted file mode 100644 index 91d09e0be64b..000000000000 --- a/pkg/k8s/k8s.go +++ /dev/null @@ -1,39 +0,0 @@ -package k8s - -import ( - "context" - - "github.com/google/wire" - - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/scanner" - "github.com/aquasecurity/trivy/pkg/scanner/local" - "github.com/aquasecurity/trivy/pkg/types" -) - -// ScanSuperSet binds the dependencies for k8s -var ScanSuperSet = wire.NewSet( - local.SuperSet, - wire.Bind(new(scanner.Driver), new(local.Scanner)), - NewScanKubernetes, -) - -// ScanKubernetes implements the scanner -type ScanKubernetes struct { - localScanner local.Scanner -} - -// NewScanKubernetes is the factory method for scanner -func NewScanKubernetes(s local.Scanner) *ScanKubernetes { - return &ScanKubernetes{localScanner: s} -} - -// NewKubernetesScanner is the factory method for scanner -func NewKubernetesScanner() *ScanKubernetes { - return initializeScanK8s(nil) -} - -// Scan scans k8s core components and return it findings -func (sk ScanKubernetes) Scan(ctx context.Context, target types.ScanTarget, options types.ScanOptions) (types.Results, ftypes.OS, error) { - return sk.localScanner.ScanTarget(ctx, target, options) -} diff --git a/pkg/k8s/report/cyclonedx.go b/pkg/k8s/report/cyclonedx.go deleted file mode 100644 index 1d87769c29b2..000000000000 --- a/pkg/k8s/report/cyclonedx.go +++ /dev/null @@ -1,37 +0,0 @@ -package report - -import ( - "context" - "io" - - cdx "github.com/CycloneDX/cyclonedx-go" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/sbom/core" - "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" -) - -// CycloneDXWriter implements types.Writer -type CycloneDXWriter struct { - encoder cdx.BOMEncoder - marshaler cyclonedx.Marshaler -} - -// NewCycloneDXWriter constract new CycloneDXWriter -func NewCycloneDXWriter(output io.Writer, format cdx.BOMFileFormat, appVersion string) CycloneDXWriter { - encoder := cdx.NewBOMEncoder(output, format) - encoder.SetPretty(true) - encoder.SetEscapeHTML(false) - return CycloneDXWriter{ - encoder: encoder, - marshaler: cyclonedx.NewMarshaler(appVersion), - } -} - -func (w CycloneDXWriter) Write(ctx context.Context, component *core.BOM) error { - bom, err := w.marshaler.Marshal(ctx, component) - if err != nil { - return xerrors.Errorf("CycloneDX marshal error: %w", err) - } - return w.encoder.Encode(bom) -} diff --git a/pkg/k8s/report/json.go b/pkg/k8s/report/json.go deleted file mode 100644 index 12420c0704d5..000000000000 --- a/pkg/k8s/report/json.go +++ /dev/null @@ -1,40 +0,0 @@ -package report - -import ( - "encoding/json" - "fmt" - "io" - - "golang.org/x/xerrors" -) - -type JSONWriter struct { - Output io.Writer - Report string -} - -// Write writes the results in JSON format -func (jw JSONWriter) Write(report Report) error { - var output []byte - var err error - - switch jw.Report { - case AllReport: - output, err = json.MarshalIndent(report, "", " ") - if err != nil { - return xerrors.Errorf("failed to write json: %w", err) - } - case SummaryReport: - output, err = json.MarshalIndent(report.consolidate(), "", " ") - if err != nil { - return xerrors.Errorf("failed to write json: %w", err) - } - default: - return xerrors.Errorf(`report %q not supported. Use "summary" or "all"`, jw.Report) - } - if _, err = fmt.Fprintln(jw.Output, string(output)); err != nil { - return xerrors.Errorf("failed to write json: %w", err) - } - - return nil -} diff --git a/pkg/k8s/report/report.go b/pkg/k8s/report/report.go deleted file mode 100644 index 1f1c1ec50d93..000000000000 --- a/pkg/k8s/report/report.go +++ /dev/null @@ -1,300 +0,0 @@ -package report - -import ( - "errors" - "fmt" - "io" - "slices" - "strings" - - "github.com/samber/lo" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/sbom/core" - "github.com/aquasecurity/trivy/pkg/types" -) - -const ( - AllReport = "all" - SummaryReport = "summary" - - workloadComponent = "workload" - infraComponent = "infra" - infraNamespace = "kube-system" -) - -type Option struct { - Format types.Format - Report string - Output io.Writer - Severities []dbTypes.Severity - ColumnHeading []string - Scanners types.Scanners - APIVersion string -} - -// Report represents a kubernetes scan report -type Report struct { - SchemaVersion int `json:",omitempty"` - ClusterName string - Resources []Resource `json:",omitempty"` - BOM *core.BOM `json:"-"` - name string -} - -// ConsolidatedReport represents a kubernetes scan report with consolidated findings -type ConsolidatedReport struct { - SchemaVersion int `json:",omitempty"` - ClusterName string - Findings []Resource `json:",omitempty"` -} - -// Resource represents a kubernetes resource report -type Resource struct { - Namespace string `json:",omitempty"` - Kind string - Name string - Metadata []types.Metadata `json:",omitempty"` - Results types.Results `json:",omitempty"` - Error string `json:",omitempty"` - - // original report - Report types.Report `json:"-"` -} - -func (r Resource) fullname() string { - return strings.ToLower(fmt.Sprintf("%s/%s/%s", r.Namespace, r.Kind, r.Name)) -} - -// Failed returns whether the k8s report includes any vulnerabilities or misconfigurations -func (r Report) Failed() bool { - for _, v := range r.Resources { - if v.Results.Failed() { - return true - } - } - return false -} - -func (r Report) consolidate() ConsolidatedReport { - consolidated := ConsolidatedReport{ - SchemaVersion: r.SchemaVersion, - ClusterName: r.ClusterName, - } - - index := make(map[string]Resource) - var vulnerabilities []Resource - for _, m := range r.Resources { - if vulnerabilitiesOrSecretResource(m) { - vulnerabilities = append(vulnerabilities, m) - } - if misconfigsResource(m) { - res, ok := index[m.fullname()] - index[m.fullname()] = m - if ok { - index[m.fullname()].Results[0].Misconfigurations = append(index[m.fullname()].Results[0].Misconfigurations, res.Results[0].Misconfigurations...) - } - } - } - - for _, v := range vulnerabilities { - key := v.fullname() - - if res, ok := index[key]; ok { - // Combine metadata - metadata := lo.UniqBy(append(res.Metadata, v.Metadata...), func(x types.Metadata) string { - return x.ImageID - }) - index[key] = Resource{ - Namespace: res.Namespace, - Kind: res.Kind, - Name: res.Name, - Metadata: metadata, - Results: append(res.Results, v.Results...), - Error: res.Error, - } - - continue - } - - index[key] = v - } - - consolidated.Findings = lo.Values(index) - - return consolidated -} - -// Writer defines the result write operation -type Writer interface { - Write(Report) error -} - -type reports struct { - Report Report - Columns []string -} - -// SeparateMisconfigReports returns 3 reports based on scanners and components flags, -// - misconfiguration report -// - rbac report -// - infra checks report -func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners) []reports { - - var workloadMisconfig, infraMisconfig, rbacAssessment, workloadVulnerabilities, infraVulnerabilities, workloadResource []Resource - for _, resource := range k8sReport.Resources { - switch { - case vulnerabilitiesOrSecretResource(resource) && !infraResource(resource): - if resource.Namespace == infraNamespace || nodeInfoResource(resource) { - infraVulnerabilities = append(infraVulnerabilities, nodeKind(resource)) - } else { - workloadVulnerabilities = append(workloadVulnerabilities, resource) - } - case scanners.Enabled(types.RBACScanner) && rbacResource(resource): - rbacAssessment = append(rbacAssessment, resource) - case infraResource(resource): - infraMisconfig = append(infraMisconfig, nodeKind(resource)) - case scanners.Enabled(types.MisconfigScanner) && - !rbacResource(resource): - workloadMisconfig = append(workloadMisconfig, resource) - } - } - - var r []reports - workloadResource = append(workloadResource, workloadVulnerabilities...) - workloadResource = append(workloadResource, workloadMisconfig...) - if shouldAddToReport(scanners) { - workloadReport := Report{ - SchemaVersion: 0, - ClusterName: k8sReport.ClusterName, - Resources: workloadResource, - name: "Workload Assessment", - } - r = append(r, reports{ - Report: workloadReport, - Columns: WorkloadColumns(), - }) - - } - infraMisconfig = append(infraMisconfig, infraVulnerabilities...) - if shouldAddToReport(scanners) { - r = append(r, reports{ - Report: Report{ - SchemaVersion: 0, - ClusterName: k8sReport.ClusterName, - Resources: infraMisconfig, - name: "Infra Assessment", - }, - Columns: InfraColumns(), - }) - } - - if scanners.Enabled(types.RBACScanner) { - r = append(r, reports{ - Report: Report{ - SchemaVersion: 0, - ClusterName: k8sReport.ClusterName, - Resources: rbacAssessment, - name: "RBAC Assessment", - }, - Columns: RoleColumns(), - }) - } - - return r -} - -func rbacResource(misConfig Resource) bool { - return slices.Contains([]string{ - "Role", - "RoleBinding", - "ClusterRole", - "ClusterRoleBinding", - }, misConfig.Kind) -} - -func infraResource(misConfig Resource) bool { - return !rbacResource(misConfig) && (misConfig.Namespace == infraNamespace) || nodeInfoResource(misConfig) -} - -func CreateResource(artifact *artifacts.Artifact, report types.Report, err error) Resource { - r := createK8sResource(artifact, report.Results) - - r.Metadata = []types.Metadata{report.Metadata} - r.Report = report - // if there was any error during the scan - if err != nil { - r.Error = err.Error() - } - - return r -} - -func nodeInfoResource(nodeInfo Resource) bool { - return nodeInfo.Kind == "NodeInfo" || nodeInfo.Kind == "NodeComponents" -} - -func createK8sResource(artifact *artifacts.Artifact, scanResults types.Results) Resource { - results := make([]types.Result, 0, len(scanResults)) - // fix target name - for _, result := range scanResults { - // if resource is a kubernetes file fix the target name, - // to avoid showing the temp file that was removed. - if result.Type == ftypes.Kubernetes { - result.Target = fmt.Sprintf("%s/%s", artifact.Kind, artifact.Name) - } - results = append(results, result) - } - - r := Resource{ - Namespace: artifact.Namespace, - Kind: artifact.Kind, - Name: artifact.Name, - Metadata: []types.Metadata{}, - Results: results, - Report: types.Report{ - Results: results, - ArtifactName: artifact.Name, - }, - } - - return r -} - -func (r Report) PrintErrors() { - for _, resource := range r.Resources { - if resource.Error != "" { - log.Error("Error during vulnerabilities or misconfiguration scan", log.Err(errors.New(resource.Error))) - } - } -} - -func shouldAddToReport(scanners types.Scanners) bool { - return scanners.AnyEnabled( - types.MisconfigScanner, - types.VulnerabilityScanner, - types.SecretScanner) -} - -func vulnerabilitiesOrSecretResource(resource Resource) bool { - for _, result := range resource.Results { - if len(result.Vulnerabilities) > 0 || len(resource.Results[0].Secrets) > 0 { - return true - } - } - return false -} - -func misconfigsResource(resource Resource) bool { - return len(resource.Results) > 0 && len(resource.Results[0].Misconfigurations) > 0 -} - -func nodeKind(resource Resource) Resource { - if nodeInfoResource(resource) { - resource.Kind = "Node" - } - return resource -} diff --git a/pkg/k8s/report/report_stub.go b/pkg/k8s/report/report_stub.go new file mode 100644 index 000000000000..0af682c10593 --- /dev/null +++ b/pkg/k8s/report/report_stub.go @@ -0,0 +1,57 @@ +package report + +import ( + "io" + + dbTypes "github.com/aquasecurity/trivy-db/pkg/types" + "github.com/aquasecurity/trivy/pkg/sbom/core" + "github.com/aquasecurity/trivy/pkg/types" +) + +const ( + AllReport = "all" + SummaryReport = "summary" + + workloadComponent = "workload" + infraComponent = "infra" + infraNamespace = "kube-system" +) + +type Option struct { + Format types.Format + Report string + Output io.Writer + Severities []dbTypes.Severity + ColumnHeading []string + Scanners types.Scanners + APIVersion string +} + +// Report represents a kubernetes scan report +type Report struct { + SchemaVersion int `json:",omitempty"` + ClusterName string + Resources []Resource `json:",omitempty"` + BOM *core.BOM `json:"-"` + name string +} + +// ConsolidatedReport represents a kubernetes scan report with consolidated findings +type ConsolidatedReport struct { + SchemaVersion int `json:",omitempty"` + ClusterName string + Findings []Resource `json:",omitempty"` +} + +// Resource represents a kubernetes resource report +type Resource struct { + Namespace string `json:",omitempty"` + Kind string + Name string + Metadata []types.Metadata `json:",omitempty"` + Results types.Results `json:",omitempty"` + Error string `json:",omitempty"` + + // original report + Report types.Report `json:"-"` +} diff --git a/pkg/k8s/report/report_test.go b/pkg/k8s/report/report_test.go deleted file mode 100644 index 61d382246cd0..000000000000 --- a/pkg/k8s/report/report_test.go +++ /dev/null @@ -1,841 +0,0 @@ -package report - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy/pkg/types" -) - -var ( - deployOrionWithMisconfigs = Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Metadata: []types.Metadata{ - { - ImageID: "123", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - { - ID: "ID102", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - { - ID: "ID103", - Status: types.MisconfStatusFailure, - Severity: "CRITICAL", - }, - { - ID: "ID104", - Status: types.MisconfStatusFailure, - Severity: "UNKNOWN", - }, - { - ID: "ID105", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "ID106", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - }, - }, - }, - } - - deployOrionWithVulns = Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Metadata: []types.Metadata{ - { - ImageID: "123", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-1111", - Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, - }, - { - VulnerabilityID: "CVE-2022-2222", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - { - VulnerabilityID: "CVE-2022-3333", - Vulnerability: dbTypes.Vulnerability{Severity: "HIGH"}, - }, - { - VulnerabilityID: "CVE-2022-4444", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-5555", - Vulnerability: dbTypes.Vulnerability{Severity: "UNKNOWN"}, - }, - { - VulnerabilityID: "CVE-2022-6666", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-7777", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - }, - }, - }, - } - deployOrionWithThirdVulns = Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Metadata: []types.Metadata{ - { - ImageID: "123", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - {}, - {}, - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-1111", - Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, - }, - { - VulnerabilityID: "CVE-2022-2222", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - { - VulnerabilityID: "CVE-2022-3333", - Vulnerability: dbTypes.Vulnerability{Severity: "HIGH"}, - }, - { - VulnerabilityID: "CVE-2022-4444", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-5555", - Vulnerability: dbTypes.Vulnerability{Severity: "UNKNOWN"}, - }, - { - VulnerabilityID: "CVE-2022-6666", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-7777", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - }, - }, - }, - } - - orionDeployWithAnotherMisconfig = Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "ID201", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - }, - }, - }, - } - - image1WithVulns = Resource{ - Namespace: "default", - Kind: "Pod", - Name: "multi-image-pod", - Metadata: []types.Metadata{ - { - ImageID: "image1", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-1111", - Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, - }, - }, - }, - }, - } - - image2WithVulns = Resource{ - Namespace: "default", - Kind: "Pod", - Name: "multi-image-pod", - Metadata: []types.Metadata{ - { - ImageID: "image2", - RepoTags: []string{ - "alpine:3.17.3", - }, - RepoDigests: []string{ - "alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", - }, - }, - }, - Results: types.Results{ - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-2222", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - }, - }, - }, - } - - multiImagePodWithVulns = Resource{ - Namespace: "default", - Kind: "Pod", - Name: "multi-image-pod", - Metadata: []types.Metadata{ - { - ImageID: "image1", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - { - ImageID: "image2", - RepoTags: []string{ - "alpine:3.17.3", - }, - RepoDigests: []string{ - "alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", - }, - }, - }, - Results: types.Results{ - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-1111", - Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, - }, - }, - }, - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-2222", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - }, - }, - }, - } - - deployOrionWithBothVulnsAndMisconfigs = Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Metadata: []types.Metadata{ - { - ImageID: "123", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - { - ID: "ID102", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - { - ID: "ID103", - Status: types.MisconfStatusFailure, - Severity: "CRITICAL", - }, - { - ID: "ID104", - Status: types.MisconfStatusFailure, - Severity: "UNKNOWN", - }, - { - ID: "ID105", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "ID106", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - }, - }, - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-1111", - Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, - }, - { - VulnerabilityID: "CVE-2022-2222", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - { - VulnerabilityID: "CVE-2022-3333", - Vulnerability: dbTypes.Vulnerability{Severity: "HIGH"}, - }, - { - VulnerabilityID: "CVE-2022-4444", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-5555", - Vulnerability: dbTypes.Vulnerability{Severity: "UNKNOWN"}, - }, - { - VulnerabilityID: "CVE-2022-6666", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-7777", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - }, - }, - }, - } - - cronjobHelloWithVulns = Resource{ - Namespace: "default", - Kind: "Cronjob", - Name: "hello", - Metadata: []types.Metadata{ - { - ImageID: "123", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - {Vulnerabilities: []types.DetectedVulnerability{{VulnerabilityID: "CVE-2020-9999"}}}, - }, - } - - podPrometheusWithMisconfigs = Resource{ - Namespace: "default", - Kind: "Pod", - Name: "prometheus", - Metadata: []types.Metadata{ - { - ImageID: "123", - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", - }, - }, - }, - Results: types.Results{ - {Misconfigurations: []types.DetectedMisconfiguration{{ID: "ID100"}}}, - }, - } - - roleWithMisconfig = Resource{ - Namespace: "default", - Kind: "Role", - Name: "system::leader-locking-kube-controller-manager", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "ID100", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - }, - }, - }, - } - - deployLuaWithSecrets = Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "lua", - Results: types.Results{ - { - Secrets: []types.DetectedSecret{ - { - RuleID: "secret1", - Severity: "CRITICAL", - }, - { - RuleID: "secret2", - Severity: "MEDIUM", - }, - }, - }, - }, - } - - apiseverPodWithMisconfigAndInfra = Resource{ - Namespace: "kube-system", - Kind: "Pod", - Name: "kube-apiserver", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "KSV-ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "KSV-ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - { - ID: "KSV-ID102", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - - { - ID: "KCV-ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "KCV-ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - }, - }, - }, - } -) - -func TestReport_consolidate(t *testing.T) { - concatenatedResource := orionDeployWithAnotherMisconfig - concatenatedResource.Results[0].Misconfigurations = append(concatenatedResource.Results[0].Misconfigurations, - deployOrionWithMisconfigs.Results[0].Misconfigurations...) - - tests := []struct { - name string - report Report - expectedFindings map[string]Resource - }{ - { - name: "report with both misconfigs and vulnerabilities", - report: Report{ - Resources: []Resource{ - deployOrionWithVulns, - cronjobHelloWithVulns, - deployOrionWithMisconfigs, - podPrometheusWithMisconfigs, - }, - }, - expectedFindings: map[string]Resource{ - "default/deploy/orion": deployOrionWithBothVulnsAndMisconfigs, - "default/cronjob/hello": cronjobHelloWithVulns, - "default/pod/prometheus": podPrometheusWithMisconfigs, - }, - }, - { - name: "report with only misconfigurations", - report: Report{ - Resources: []Resource{ - deployOrionWithMisconfigs, - podPrometheusWithMisconfigs, - }, - }, - expectedFindings: map[string]Resource{ - "default/deploy/orion": deployOrionWithMisconfigs, - "default/pod/prometheus": podPrometheusWithMisconfigs, - }, - }, - { - name: "report with only vulnerabilities", - report: Report{ - Resources: []Resource{ - deployOrionWithVulns, - cronjobHelloWithVulns, - }, - }, - expectedFindings: map[string]Resource{ - "default/deploy/orion": deployOrionWithVulns, - "default/cronjob/hello": cronjobHelloWithVulns, - }, - }, - { - name: "report with vulnerabilities in the third result", - report: Report{ - Resources: []Resource{ - deployOrionWithThirdVulns, - }, - }, - expectedFindings: map[string]Resource{ - "default/deploy/orion": deployOrionWithThirdVulns, - }, - }, - { - name: "report with misconfigs in image and pod", - report: Report{ - Resources: []Resource{ - deployOrionWithMisconfigs, - orionDeployWithAnotherMisconfig, - }, - }, - expectedFindings: map[string]Resource{ - "default/deploy/orion": concatenatedResource, - }, - }, - { - name: "report with multi image pod containing vulnerabilities", - report: Report{ - Resources: []Resource{ - image1WithVulns, - image2WithVulns, - }, - }, - expectedFindings: map[string]Resource{ - "default/pod/multi-image-pod": multiImagePodWithVulns, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - consolidateReport := tt.report.consolidate() - - if len(consolidateReport.Findings) != len(tt.expectedFindings) { - t.Errorf("expected %d findings, got %d", len(tt.expectedFindings), len(consolidateReport.Findings)) - } - - for _, f := range consolidateReport.Findings { - key := f.fullname() - - expected, found := tt.expectedFindings[key] - if !found { - t.Errorf("key not found: %s", key) - } - - assert.Equal(t, expected, f) - } - }) - } -} - -func TestResource_fullname(t *testing.T) { - tests := []struct { - expected string - resource Resource - }{ - { - "default/deploy/orion", - deployOrionWithBothVulnsAndMisconfigs, - }, - { - "default/deploy/orion", - deployOrionWithMisconfigs, - }, - { - "default/cronjob/hello", - cronjobHelloWithVulns, - }, - { - "default/pod/prometheus", - podPrometheusWithMisconfigs, - }, - } - - for _, tt := range tests { - t.Run(tt.expected, func(t *testing.T) { - assert.Equal(t, tt.expected, tt.resource.fullname()) - }) - } -} - -func TestResourceFailed(t *testing.T) { - tests := []struct { - name string - report Report - expected bool - }{ - { - name: "report with both misconfigs and vulnerabilities", - report: Report{ - Resources: []Resource{ - deployOrionWithVulns, - cronjobHelloWithVulns, - deployOrionWithMisconfigs, - podPrometheusWithMisconfigs, - }, - }, - expected: true, - }, - { - name: "report with only misconfigurations", - report: Report{ - Resources: []Resource{ - deployOrionWithMisconfigs, - podPrometheusWithMisconfigs, - }, - }, - expected: true, - }, - { - name: "report with only vulnerabilities", - report: Report{ - Resources: []Resource{ - deployOrionWithVulns, - cronjobHelloWithVulns, - }, - }, - expected: true, - }, - { - name: "report without vulnerabilities and misconfigurations", - report: Report{}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.expected, tt.report.Failed()) - }) - } -} - -func Test_rbacResource(t *testing.T) { - tests := []struct { - name string - misConfig Resource - want bool - }{ - { - name: "rbac Role resources", - misConfig: Resource{Kind: "Role"}, - want: true, - }, - { - name: "rbac ClusterRole resources", - misConfig: Resource{Kind: "ClusterRole"}, - want: true, - }, - { - name: "rbac RoleBinding resources", - misConfig: Resource{Kind: "RoleBinding"}, - want: true, - }, - { - name: "rbac ClusterRoleBinding resources", - misConfig: Resource{Kind: "ClusterRoleBinding"}, - want: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := rbacResource(test.misConfig) - assert.Equal(t, test.want, got) - }) - } -} - -func Test_separateMisconfigReports(t *testing.T) { - k8sReport := Report{ - Resources: []Resource{ - {Kind: "Role"}, - {Kind: "Deployment"}, - {Kind: "StatefulSet"}, - { - Kind: "Pod", - Namespace: "kube-system", - Results: []types.Result{ - {Misconfigurations: []types.DetectedMisconfiguration{{ID: "KCV-0001"}}}, - {Misconfigurations: []types.DetectedMisconfiguration{{ID: "KSV-0001"}}}, - }, - }, - }, - } - - tests := []struct { - name string - k8sReport Report - scanners types.Scanners - expectedReports []Report - }{ - { - name: "Config, Rbac, and Infra Reports", - k8sReport: k8sReport, - scanners: types.Scanners{ - types.MisconfigScanner, - types.RBACScanner, - }, - expectedReports: []Report{ - // the order matter for the test - { - Resources: []Resource{ - {Kind: "Deployment"}, - {Kind: "StatefulSet"}, - }, - }, - {Resources: []Resource{{Kind: "Pod"}}}, - {Resources: []Resource{{Kind: "Role"}}}, - }, - }, - { - name: "Config and Infra for the same resource", - k8sReport: k8sReport, - scanners: types.Scanners{types.MisconfigScanner}, - expectedReports: []Report{ - // the order matter for the test - { - Resources: []Resource{ - {Kind: "Deployment"}, - {Kind: "StatefulSet"}, - }, - }, - {Resources: []Resource{{Kind: "Pod"}}}, - }, - }, - { - name: "Role Report Only", - k8sReport: k8sReport, - scanners: types.Scanners{types.RBACScanner}, - expectedReports: []Report{ - {Resources: []Resource{{Kind: "Role"}}}, - }, - }, - { - name: "Config Report Only", - k8sReport: k8sReport, - scanners: types.Scanners{types.MisconfigScanner}, - expectedReports: []Report{ - { - Resources: []Resource{ - {Kind: "Deployment"}, - {Kind: "StatefulSet"}, - }, - }, - { - Resources: []Resource{ - {Kind: "Pod"}, - }, - }, - }, - }, - { - name: "Infra Report Only", - k8sReport: k8sReport, - scanners: types.Scanners{types.MisconfigScanner}, - expectedReports: []Report{ - { - Resources: []Resource{ - {Kind: "Deployment"}, - {Kind: "StatefulSet"}, - }, - }, - { - Resources: []Resource{ - {Kind: "Pod"}, - }, - }, - }, - }, - - // TODO: add vuln only - // TODO: add secret only - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - reports := SeparateMisconfigReports(tt.k8sReport, tt.scanners) - assert.Equal(t, len(tt.expectedReports), len(reports)) - - for i := range reports { - assert.Equal(t, len(tt.expectedReports[i].Resources), len(reports[i].Report.Resources)) - for j, m := range tt.expectedReports[i].Resources { - assert.Equal(t, m.Kind, reports[i].Report.Resources[j].Kind) - } - } - }) - } -} diff --git a/pkg/k8s/report/summary.go b/pkg/k8s/report/summary.go deleted file mode 100644 index 9a3d6ee39371..000000000000 --- a/pkg/k8s/report/summary.go +++ /dev/null @@ -1,210 +0,0 @@ -package report - -import ( - "fmt" - "io" - "slices" - "sort" - "strconv" - "strings" - - "golang.org/x/xerrors" - - "github.com/aquasecurity/table" - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - pkgReport "github.com/aquasecurity/trivy/pkg/report/table" - "github.com/aquasecurity/trivy/pkg/types" -) - -type SummaryWriter struct { - Output io.Writer - Severities []string - SeverityHeadings []string - ColumnsHeading []string -} - -func NewSummaryWriter(output io.Writer, requiredSevs []dbTypes.Severity, columnHeading []string) SummaryWriter { - var severities []string - var severityHeadings []string - severities, severityHeadings = getRequiredSeverities(requiredSevs) - return SummaryWriter{ - Output: output, - Severities: severities, - SeverityHeadings: severityHeadings, - ColumnsHeading: columnHeading, - } -} - -func ColumnHeading(scanners types.Scanners, availableColumns []string) []string { - columns := []string{ - NamespaceColumn, - ResourceColumn, - } - securityOptions := make(map[string]any, 0) - // maintain column order (vuln,config,secret) - for _, check := range scanners { - switch check { - case types.VulnerabilityScanner: - securityOptions[VulnerabilitiesColumn] = nil - case types.MisconfigScanner: - securityOptions[MisconfigurationsColumn] = nil - case types.SecretScanner: - securityOptions[SecretsColumn] = nil - case types.RBACScanner: - securityOptions[RbacAssessmentColumn] = nil - } - } - for _, col := range availableColumns { - if _, ok := securityOptions[col]; ok { - columns = append(columns, col) - } - } - return columns -} - -// Write writes the results in a summarized table format -func (s SummaryWriter) Write(report Report) error { - // no report column to print - if len(s.ColumnsHeading) == 2 { - return nil - } - consolidated := report.consolidate() - - if _, err := fmt.Fprintln(s.Output); err != nil { - return xerrors.Errorf("failed to write summary report: %w", err) - } - - if _, err := fmt.Fprintln(s.Output, report.name); err != nil { - return xerrors.Errorf("failed to write summary report title: %w", err) - } - - t := table.New(s.Output) - t.SetRowLines(false) - configureHeader(s, t, s.ColumnsHeading) - - sort.Slice(consolidated.Findings, func(i, j int) bool { - return consolidated.Findings[i].Namespace > consolidated.Findings[j].Namespace - }) - - for _, finding := range consolidated.Findings { - if !finding.Results.Failed() { - continue - } - vCount, mCount, sCount := accumulateSeverityCounts(finding) - name := fmt.Sprintf("%s/%s", finding.Kind, finding.Name) - rowParts := []string{ - finding.Namespace, - name, - } - - if slices.Contains(s.ColumnsHeading, VulnerabilitiesColumn) { - rowParts = append(rowParts, s.generateSummary(vCount)...) - } - - if slices.Contains(s.ColumnsHeading, MisconfigurationsColumn) || - slices.Contains(s.ColumnsHeading, RbacAssessmentColumn) { - rowParts = append(rowParts, s.generateSummary(mCount)...) - } - - if slices.Contains(s.ColumnsHeading, SecretsColumn) { - rowParts = append(rowParts, s.generateSummary(sCount)...) - } - - t.AddRow(rowParts...) - } - - t.Render() - - keyParts := []string{"Severities:"} - for _, s := range s.Severities { - keyParts = append(keyParts, fmt.Sprintf("%s=%s", s[:1], pkgReport.ColorizeSeverity(s, s))) - } - - _, _ = fmt.Fprintln(s.Output, strings.Join(keyParts, " ")) - _, _ = fmt.Fprintln(s.Output) - return nil -} - -func (s SummaryWriter) generateSummary(sevCount map[string]int) []string { - var parts []string - - for _, sev := range s.Severities { - if count, ok := sevCount[sev]; ok { - parts = append(parts, pkgReport.ColorizeSeverity(strconv.Itoa(count), sev)) - } else { - parts = append(parts, " ") - } - } - - return parts -} - -func getRequiredSeverities(requiredSevs []dbTypes.Severity) ([]string, []string) { - requiredSevOrder := []dbTypes.Severity{ - dbTypes.SeverityCritical, - dbTypes.SeverityHigh, - dbTypes.SeverityMedium, - dbTypes.SeverityLow, - dbTypes.SeverityUnknown, - } - var severities []string - var severityHeadings []string - for _, sev := range requiredSevOrder { - for _, p := range requiredSevs { - if p == sev { - severities = append(severities, sev.String()) - severityHeadings = append(severityHeadings, strings.ToUpper(sev.String()[:1])) - continue - } - } - } - return severities, severityHeadings -} - -func accumulateSeverityCounts(finding Resource) (map[string]int, map[string]int, map[string]int) { - vCount := make(map[string]int) - mCount := make(map[string]int) - sCount := make(map[string]int) - for _, r := range finding.Results { - for _, rv := range r.Vulnerabilities { - vCount[rv.Severity]++ - } - for _, rv := range r.Misconfigurations { - mCount[rv.Severity]++ - } - for _, rv := range r.Secrets { - sCount[rv.Severity]++ - } - } - return vCount, mCount, sCount -} - -func configureHeader(s SummaryWriter, t *table.Table, columnHeading []string) { - sevCount := len(s.Severities) - if len(columnHeading) > 2 { - headerRow := []string{ - columnHeading[0], - columnHeading[1], - } - // vulnerabilities headings - count := len(columnHeading) - len(headerRow) - colSpan := []int{ - 1, - 1, - } - headerAlignment := []table.Alignment{ - table.AlignLeft, - table.AlignLeft, - } - for i := 0; i < count; i++ { - headerRow = append(headerRow, s.SeverityHeadings...) - colSpan = append(colSpan, sevCount) - headerAlignment = append(headerAlignment, table.AlignCenter) - } - t.SetHeaders(columnHeading...) - t.AddHeaders(headerRow...) - t.SetAlignment(headerAlignment...) - t.SetAutoMergeHeaders(true) - t.SetHeaderColSpans(0, colSpan...) - } -} diff --git a/pkg/k8s/report/summary_test.go b/pkg/k8s/report/summary_test.go deleted file mode 100644 index dd1b6985e159..000000000000 --- a/pkg/k8s/report/summary_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package report - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/types" -) - -func TestReport_ColumnHeading(t *testing.T) { - allScanners := types.Scanners{ - types.VulnerabilityScanner, - types.MisconfigScanner, - types.SecretScanner, - types.RBACScanner, - } - - tests := []struct { - name string - scanners types.Scanners - availableColumns []string - want []string - }{ - { - name: "filter workload columns", - scanners: allScanners, - availableColumns: WorkloadColumns(), - want: []string{ - NamespaceColumn, - ResourceColumn, - VulnerabilitiesColumn, - MisconfigurationsColumn, - SecretsColumn, - }, - }, - { - name: "filter rbac columns", - scanners: allScanners, - availableColumns: RoleColumns(), - want: []string{ - NamespaceColumn, - ResourceColumn, - RbacAssessmentColumn, - }, - }, - { - name: "filter infra columns", - scanners: allScanners, - availableColumns: InfraColumns(), - want: []string{ - NamespaceColumn, - ResourceColumn, - VulnerabilitiesColumn, - MisconfigurationsColumn, - SecretsColumn, - }, - }, - { - name: "config column only", - scanners: types.Scanners{types.MisconfigScanner}, - availableColumns: WorkloadColumns(), - want: []string{ - NamespaceColumn, - ResourceColumn, - MisconfigurationsColumn, - }, - }, - { - name: "secret column only", - scanners: types.Scanners{types.SecretScanner}, - availableColumns: WorkloadColumns(), - want: []string{ - NamespaceColumn, - ResourceColumn, - SecretsColumn, - }, - }, - { - name: "vuln column only", - scanners: types.Scanners{types.VulnerabilityScanner}, - availableColumns: WorkloadColumns(), - want: []string{ - NamespaceColumn, - ResourceColumn, - VulnerabilitiesColumn, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - column := ColumnHeading(tt.scanners, tt.availableColumns) - if !assert.Equal(t, tt.want, column) { - t.Error(fmt.Errorf("TestReport_ColumnHeading want %v got %v", tt.want, column)) - } - }) - } -} diff --git a/pkg/k8s/report/table.go b/pkg/k8s/report/table.go deleted file mode 100644 index 664a2ffb5edb..000000000000 --- a/pkg/k8s/report/table.go +++ /dev/null @@ -1,86 +0,0 @@ -package report - -import ( - "context" - "fmt" - "io" - "strings" - - "golang.org/x/xerrors" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - pkgReport "github.com/aquasecurity/trivy/pkg/report/table" -) - -type TableWriter struct { - Report string - Output io.Writer - Severities []dbTypes.Severity - ColumnHeading []string -} - -const ( - NamespaceColumn = "Namespace" - ResourceColumn = "Resource" - VulnerabilitiesColumn = "Vulnerabilities" - MisconfigurationsColumn = "Misconfigurations" - SecretsColumn = "Secrets" - RbacAssessmentColumn = "RBAC Assessment" -) - -func WorkloadColumns() []string { - return []string{ - VulnerabilitiesColumn, - MisconfigurationsColumn, - SecretsColumn, - } -} - -func RoleColumns() []string { - return []string{RbacAssessmentColumn} -} - -func InfraColumns() []string { - return []string{ - VulnerabilitiesColumn, - MisconfigurationsColumn, - SecretsColumn, - } -} - -func (tw TableWriter) Write(ctx context.Context, report Report) error { - switch tw.Report { - case AllReport: - t := pkgReport.NewWriter(pkgReport.Options{ - Output: tw.Output, - Severities: tw.Severities, - }) - for i, r := range report.Resources { - if r.Report.Results.Failed() { - updateTargetContext(&report.Resources[i]) - err := t.Write(ctx, r.Report) - if err != nil { - return err - } - } - } - case SummaryReport: - writer := NewSummaryWriter(tw.Output, tw.Severities, tw.ColumnHeading) - return writer.Write(report) - default: - return xerrors.Errorf(`report %q not supported. Use "summary" or "all"`, tw.Report) - } - - return nil -} - -// updateTargetContext add context namespace, kind and name to the target -func updateTargetContext(r *Resource) { - targetName := fmt.Sprintf("namespace: %s, %s: %s", r.Namespace, strings.ToLower(r.Kind), r.Name) - if r.Kind == "NodeComponents" || r.Kind == "NodeInfo" { - targetName = fmt.Sprintf("node: %s", r.Name) - } - for i := range r.Report.Results { - r.Report.Results[i].Target = targetName - } -} diff --git a/pkg/k8s/scanner/io.go b/pkg/k8s/scanner/io.go deleted file mode 100644 index 65f102a4b08c..000000000000 --- a/pkg/k8s/scanner/io.go +++ /dev/null @@ -1,79 +0,0 @@ -package scanner - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - "runtime" - - "golang.org/x/xerrors" - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - "github.com/aquasecurity/trivy/pkg/log" -) - -var r = regexp.MustCompile("[\\\\/:*?<>]") - -func generateTempFileByArtifact(artifact *artifacts.Artifact, tempDir string) (string, error) { - filename := fmt.Sprintf("%s-%s-%s-*.yaml", artifact.Namespace, artifact.Kind, artifact.Name) - if runtime.GOOS == "windows" { - // removes characters not permitted in file/directory names on Windows - filename = filenameWindowsFriendly(filename) - } - file, err := os.CreateTemp(tempDir, filename) - if err != nil { - return "", xerrors.Errorf("failed to create temporary file: %w", err) - } - shouldRemove := false - defer func() { - if err := file.Close(); err != nil { - log.Error("Failed to close temp file", log.FilePath(file.Name()), log.Err(err)) - } - if shouldRemove { - removeFile(file.Name()) - } - }() - if err := yaml.NewEncoder(file).Encode(artifact.RawResource); err != nil { - shouldRemove = true - return "", xerrors.Errorf("failed to encode artifact: %w", err) - } - return filepath.Base(file.Name()), nil -} - -// generateTempDir creates a directory with yaml files generated from kubernetes artifacts -// returns a directory name, a map for mapping a temp target file to k8s artifact and error -func generateTempDir(arts []*artifacts.Artifact) (string, map[string]*artifacts.Artifact, error) { - tempDir, err := os.MkdirTemp("", "trivyk8s*") - if err != nil { - return "", nil, xerrors.Errorf("failed to create temp directory: %w", err) - } - - m := make(map[string]*artifacts.Artifact) - for _, artifact := range arts { - filename, err := generateTempFileByArtifact(artifact, tempDir) - if err != nil { - log.Error("Failed to create temp file", log.FilePath(filename), log.Err(err)) - continue - } - m[filename] = artifact - } - return tempDir, m, nil -} - -func removeDir(dirname string) { - if err := os.RemoveAll(dirname); err != nil { - log.Error("Failed to remove temp directory", log.FilePath(dirname), log.Err(err)) - } -} - -func removeFile(filename string) { - if err := os.Remove(filename); err != nil { - log.Error("Failed to remove temp file", log.FilePath(filename), log.Err(err)) - } -} - -func filenameWindowsFriendly(name string) string { - return r.ReplaceAllString(name, "_") -} diff --git a/pkg/k8s/scanner/io_test.go b/pkg/k8s/scanner/io_test.go deleted file mode 100644 index 9e256b39f2b3..000000000000 --- a/pkg/k8s/scanner/io_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package scanner - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_FilenameWindowsFriendly(t *testing.T) { - - tests := []struct { - name string - fileName string - want string - }{ - { - name: "name with invalid char - colon", - fileName: `kube-system-Role-system:controller:bootstrap-signer-2934213283.yaml`, - want: `kube-system-Role-system_controller_bootstrap-signer-2934213283.yaml`, - }, - { - name: "name with no invalid chars", - fileName: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`, - want: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`, - }, - { - name: "name with no invalid - slash", - fileName: "-ClusterRoleBinding-system\\basic-user-725844313.yaml", - want: `-ClusterRoleBinding-system_basic-user-725844313.yaml`, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := filenameWindowsFriendly(test.fileName) - assert.Equal(t, test.want, got) - }) - } -} diff --git a/pkg/k8s/scanner/scanner.go b/pkg/k8s/scanner/scanner.go deleted file mode 100644 index e1c7ddaa1371..000000000000 --- a/pkg/k8s/scanner/scanner.go +++ /dev/null @@ -1,653 +0,0 @@ -package scanner - -import ( - "bytes" - "context" - "errors" - "fmt" - "sort" - "strings" - - ms "github.com/mitchellh/mapstructure" - "github.com/package-url/packageurl-go" - "github.com/samber/lo" - "golang.org/x/xerrors" - - "github.com/aquasecurity/go-version/pkg/version" - "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - "github.com/aquasecurity/trivy-kubernetes/pkg/bom" - cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" - "github.com/aquasecurity/trivy/pkg/digest" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/flag" - "github.com/aquasecurity/trivy/pkg/k8s" - "github.com/aquasecurity/trivy/pkg/k8s/report" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/parallel" - "github.com/aquasecurity/trivy/pkg/purl" - "github.com/aquasecurity/trivy/pkg/sbom/core" - "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" - "github.com/aquasecurity/trivy/pkg/scanner/local" - "github.com/aquasecurity/trivy/pkg/types" -) - -const ( - k8sCoreComponentNamespace = cyclonedx.Namespace + "resource:" - k8sComponentType = "Type" - k8sComponentName = "Name" - k8sComponentNode = "node" -) - -type Scanner struct { - cluster string - runner cmd.Runner - opts flag.Options -} - -func NewScanner(cluster string, runner cmd.Runner, opts flag.Options) *Scanner { - return &Scanner{ - cluster, - runner, - opts, - } -} - -func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact) (report.Report, error) { - // disable logs before scanning - log.InitLogger(s.opts.Debug, true) - - // enable log, this is done in a defer function, - // to enable logs even when the function returns earlier - // due to an error - defer log.InitLogger(s.opts.Debug, false) - - if s.opts.Format == types.FormatCycloneDX { - kbom, err := s.clusterInfoToReportResources(artifactsData) - if err != nil { - return report.Report{}, err - } - return report.Report{ - SchemaVersion: 0, - BOM: kbom, - }, nil - } - var resourceArtifacts []*artifacts.Artifact - var k8sCoreArtifacts []*artifacts.Artifact - for _, artifact := range artifactsData { - if strings.HasSuffix(artifact.Kind, "Components") || strings.HasSuffix(artifact.Kind, "Cluster") { - k8sCoreArtifacts = append(k8sCoreArtifacts, artifact) - continue - } - resourceArtifacts = append(resourceArtifacts, artifact) - } - - var resources []report.Resource - - // scans kubernetes artifacts as a scope of yaml files - if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) { - misconfigs, err := s.scanMisconfigs(ctx, resourceArtifacts) - if err != nil { - return report.Report{}, xerrors.Errorf("scanning misconfigurations error: %w", err) - } - resources = append(resources, misconfigs...) - } - - // scan images from kubernetes cluster in parallel - if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages { - onItem := func(ctx context.Context, artifact *artifacts.Artifact) ([]report.Resource, error) { - opts := s.opts - opts.Credentials = make([]ftypes.Credential, len(s.opts.Credentials)) - copy(opts.Credentials, s.opts.Credentials) - // add image private registry credential auto detected from workload imagePullsecret / serviceAccount - if len(artifact.Credentials) > 0 { - for _, cred := range artifact.Credentials { - opts.RegistryOptions.Credentials = append(opts.RegistryOptions.Credentials, - ftypes.Credential{ - Username: cred.Username, - Password: cred.Password, - }, - ) - } - } - vulns, err := s.scanVulns(ctx, artifact, opts) - if err != nil { - return nil, xerrors.Errorf("scanning vulnerabilities error: %w", err) - } - return vulns, nil - } - - onResult := func(result []report.Resource) error { - resources = append(resources, result...) - return nil - } - - p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult) - if err := p.Do(ctx); err != nil { - return report.Report{}, err - } - } - - if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner) { - k8sResource, err := s.scanK8sVulns(ctx, k8sCoreArtifacts) - if err != nil { - return report.Report{}, err - } - resources = append(resources, k8sResource...) - } - return report.Report{ - SchemaVersion: 0, - ClusterName: s.cluster, - Resources: resources, - }, nil - -} - -func (s *Scanner) scanVulns(ctx context.Context, artifact *artifacts.Artifact, opts flag.Options) ([]report.Resource, error) { - resources := make([]report.Resource, 0, len(artifact.Images)) - - for _, image := range artifact.Images { - - opts.Target = image - - imageReport, err := s.runner.ScanImage(ctx, opts) - - if err != nil { - resources = append(resources, report.CreateResource(artifact, imageReport, err)) - continue - } - - resource, err := s.filter(ctx, imageReport, artifact) - if err != nil { - return nil, xerrors.Errorf("filter error: %w", err) - } - - resources = append(resources, resource) - } - - return resources, nil -} - -func (s *Scanner) scanMisconfigs(ctx context.Context, k8sArtifacts []*artifacts.Artifact) ([]report.Resource, error) { - dir, artifactsByFilename, err := generateTempDir(k8sArtifacts) - if err != nil { - return nil, xerrors.Errorf("failed to generate temp dir: %w", err) - } - - s.opts.Target = dir - - configReport, err := s.runner.ScanFilesystem(ctx, s.opts) - // remove config files after scanning - removeDir(dir) - - if err != nil { - return nil, xerrors.Errorf("failed to scan filesystem: %w", err) - } - resources := make([]report.Resource, 0, len(k8sArtifacts)) - - for _, res := range configReport.Results { - artifact := artifactsByFilename[res.Target] - - singleReport := types.Report{ - SchemaVersion: configReport.SchemaVersion, - CreatedAt: configReport.CreatedAt, - ArtifactName: res.Target, - ArtifactType: configReport.ArtifactType, - Metadata: configReport.Metadata, - Results: types.Results{res}, - } - - resource, err := s.filter(ctx, singleReport, artifact) - if err != nil { - resource = report.CreateResource(artifact, singleReport, err) - } - resources = append(resources, resource) - } - - return resources, nil -} -func (s *Scanner) filter(ctx context.Context, r types.Report, artifact *artifacts.Artifact) (report.Resource, error) { - var err error - r, err = s.runner.Filter(ctx, s.opts, r) - if err != nil { - return report.Resource{}, xerrors.Errorf("filter error: %w", err) - } - return report.CreateResource(artifact, r, nil), nil -} - -const ( - oci = "oci" - kubelet = "k8s.io/kubelet" - controlPlaneComponents = "ControlPlaneComponents" - clusterInfo = "Cluster" - nodeComponents = "NodeComponents" - nodeCoreComponents = "node-core-components" -) - -func (s *Scanner) scanK8sVulns(ctx context.Context, artifactsData []*artifacts.Artifact) ([]report.Resource, error) { - var resources []report.Resource - var nodeName string - if nodeName = s.findNodeName(artifactsData); nodeName == "" { - return resources, nil - } - - k8sScanner := k8s.NewKubernetesScanner() - scanOptions := types.ScanOptions{ - Scanners: s.opts.Scanners, - PkgTypes: s.opts.PkgTypes, - PkgRelationships: s.opts.PackageOptions.PkgRelationships, - } - for _, artifact := range artifactsData { - switch artifact.Kind { - case controlPlaneComponents: - var comp bom.Component - err := ms.Decode(artifact.RawResource, &comp) - if err != nil { - return nil, err - } - cpcVersion := unifiedVersion(comp.Version) - - lang := k8sNamespace(cpcVersion, nodeName) - results, _, err := k8sScanner.Scan(ctx, types.ScanTarget{ - Applications: []ftypes.Application{ - { - Type: ftypes.LangType(lang), - FilePath: artifact.Name, - Packages: []ftypes.Package{ - { - Name: comp.Name, - Version: cpcVersion, - }, - }, - }, - }, - }, scanOptions) - if err != nil { - return nil, err - } - if results != nil { - resource, err := s.filter(ctx, types.Report{ - Results: results, - ArtifactName: artifact.Name, - }, artifact) - if err != nil { - return nil, err - } - resources = append(resources, resource) - } - case nodeComponents: - var nf bom.NodeInfo - err := ms.Decode(artifact.RawResource, &nf) - if err != nil { - return nil, err - } - kubeletVersion := unifiedVersion(nf.KubeletVersion) - lang := k8sNamespace(kubeletVersion, nodeName) - runtimeName, runtimeVersion := runtimeNameVersion(nf.ContainerRuntimeVersion) - results, _, err := k8sScanner.Scan(ctx, types.ScanTarget{ - Applications: []ftypes.Application{ - { - Type: ftypes.LangType(lang), - FilePath: artifact.Name, - Packages: []ftypes.Package{ - { - Name: kubelet, - Version: kubeletVersion, - }, - }, - }, - { - Type: ftypes.GoBinary, - FilePath: artifact.Name, - Packages: []ftypes.Package{ - { - Name: runtimeName, - Version: runtimeVersion, - }, - }, - }, - }, - }, scanOptions) - if err != nil { - return nil, err - } - if results != nil { - resource, err := s.filter(ctx, types.Report{ - Results: results, - ArtifactName: artifact.Name, - }, artifact) - if err != nil { - return nil, err - } - resources = append(resources, resource) - } - case clusterInfo: - var cf bom.ClusterInfo - err := ms.Decode(artifact.RawResource, &cf) - if err != nil { - return nil, err - } - lang := k8sNamespace(cf.Version, nodeName) - - results, _, err := k8sScanner.Scan(ctx, types.ScanTarget{ - Applications: []ftypes.Application{ - { - Type: ftypes.LangType(lang), - FilePath: artifact.Name, - Packages: []ftypes.Package{ - { - Name: cf.Name, - Version: cf.Version, - }, - }, - }, - }, - }, scanOptions) - if err != nil { - return nil, err - } - if results != nil { - resource, err := s.filter(ctx, types.Report{ - Results: results, - ArtifactName: artifact.Name, - }, artifact) - if err != nil { - return nil, err - } - resources = append(resources, resource) - } - } - } - return resources, nil -} - -func (*Scanner) findNodeName(allArtifact []*artifacts.Artifact) string { - for _, artifact := range allArtifact { - if artifact.Kind != nodeComponents { - continue - } - return artifact.Name - } - return "" -} - -func (s *Scanner) clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.BOM, error) { - var rootComponent *core.Component - var coreComponents []*core.Component - - // Find the first node name to identify AKS cluster - var nodeName string - if nodeName = s.findNodeName(allArtifact); nodeName == "" { - return nil, errors.New("failed to find node name") - } - - kbom := core.NewBOM(core.Options{ - GenerateBOMRef: true, - }) - for _, artifact := range allArtifact { - switch artifact.Kind { - case controlPlaneComponents: - var comp bom.Component - if err := ms.Decode(artifact.RawResource, &comp); err != nil { - return nil, err - } - cVersion := unifiedVersion(comp.Version) - - controlPlane := &core.Component{ - Name: comp.Name, - Version: cVersion, - Type: core.TypeApplication, - Properties: toProperties(comp.Properties, k8sCoreComponentNamespace), - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: generatePURL(comp.Name, cVersion, nodeName), - }, - } - coreComponents = append(coreComponents, controlPlane) - - for _, c := range comp.Containers { - name := fmt.Sprintf("%s/%s", c.Registry, c.Repository) - cDigest := c.Digest - if !strings.Contains(c.Digest, string(digest.SHA256)) { - cDigest = fmt.Sprintf("%s:%s", string(digest.SHA256), cDigest) - } - ver := unifiedVersion(c.Version) - - imagePURL, err := purl.New(purl.TypeOCI, types.Metadata{ - RepoDigests: []string{ - fmt.Sprintf("%s@%s", name, cDigest), - }, - }, ftypes.Package{}) - if err != nil { - return nil, xerrors.Errorf("failed to create PURL: %w", err) - } - - imageComponent := &core.Component{ - Type: core.TypeContainerImage, - Name: name, - Version: cDigest, - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: imagePURL.Unwrap(), - }, - Properties: []core.Property{ - { - Name: core.PropertyPkgID, - Value: fmt.Sprintf("%s:%s", name, ver), - }, - { - Name: core.PropertyPkgType, - Value: oci, - }, - }, - } - kbom.AddRelationship(controlPlane, imageComponent, core.RelationshipDependsOn) - } - case nodeComponents: - var nf bom.NodeInfo - err := ms.Decode(artifact.RawResource, &nf) - if err != nil { - return nil, err - } - coreComponents = append(coreComponents, s.nodeComponent(kbom, nf)) - case clusterInfo: - var cf bom.ClusterInfo - if err := ms.Decode(artifact.RawResource, &cf); err != nil { - return nil, err - } - cVersion := unifiedVersion(cf.Version) - - rootComponent = &core.Component{ - Type: core.TypePlatform, - Name: cf.Name, - Version: cVersion, - Properties: toProperties(cf.Properties, k8sCoreComponentNamespace), - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: generatePURL(cf.Name, cVersion, nodeName), - }, - Root: true, - } - kbom.AddComponent(rootComponent) - default: - return nil, fmt.Errorf("resource kind %s is not supported", artifact.Kind) - } - } - - for _, c := range coreComponents { - kbom.AddRelationship(rootComponent, c, core.RelationshipContains) - } - - return kbom, nil -} - -func (s *Scanner) nodeComponent(b *core.BOM, nf bom.NodeInfo) *core.Component { - osName, osVersion := osNameVersion(nf.OsImage) - runtimeName, runtimeVersion := runtimeNameVersion(nf.ContainerRuntimeVersion) - kubeletVersion := unifiedVersion(nf.KubeletVersion) - properties := toProperties(nf.Properties, "") - properties = append(properties, toProperties(map[string]string{ - k8sComponentType: k8sComponentNode, - k8sComponentName: nf.NodeName, - }, k8sCoreComponentNamespace)...) - - nodeComponent := &core.Component{ - Type: core.TypePlatform, - Name: nf.NodeName, - Properties: properties, - } - - osComponent := &core.Component{ - Type: core.TypeOS, - Name: osName, - Version: osVersion, - Properties: []core.Property{ - { - Name: "Class", - Value: string(types.ClassOSPkg), - }, - { - Name: "Type", - Value: osName, - }, - }, - } - b.AddRelationship(nodeComponent, osComponent, core.RelationshipContains) - - appComponent := &core.Component{ - Type: core.TypeApplication, - Name: nodeCoreComponents, - } - b.AddRelationship(nodeComponent, appComponent, core.RelationshipContains) - - kubeletComponent := &core.Component{ - Type: core.TypeApplication, - Name: kubelet, - Version: kubeletVersion, - Properties: []core.Property{ - { - Name: k8sComponentType, - Value: k8sComponentNode, - Namespace: k8sCoreComponentNamespace, - }, - { - Name: k8sComponentName, - Value: kubelet, - Namespace: k8sCoreComponentNamespace, - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: generatePURL(kubelet, kubeletVersion, nf.NodeName), - }, - } - b.AddRelationship(appComponent, kubeletComponent, core.RelationshipContains) - - runtimeComponent := &core.Component{ - Type: core.TypeApplication, - Name: runtimeName, - Version: runtimeVersion, - Properties: []core.Property{ - { - Name: k8sComponentType, - Value: k8sComponentNode, - Namespace: k8sCoreComponentNamespace, - }, - { - Name: k8sComponentName, - Value: runtimeName, - Namespace: k8sCoreComponentNamespace, - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: packageurl.NewPackageURL(packageurl.TypeGolang, "", runtimeName, runtimeVersion, packageurl.Qualifiers{}, ""), - }, - } - b.AddRelationship(appComponent, runtimeComponent, core.RelationshipContains) - - return nodeComponent -} - -func unifiedVersion(ver string) string { - if strings.HasPrefix(ver, "v") || ver == "" { - return ver - } - return "v" + ver -} - -func osNameVersion(name string) (string, string) { - var buffer bytes.Buffer - var v string - var err error - parts := strings.Split(name, " ") - for _, p := range parts { - _, err = version.Parse(p) - if err != nil { - buffer.WriteString(p + " ") - continue - } - v = p - break - } - return strings.ToLower(strings.TrimSpace(buffer.String())), v -} - -func runtimeNameVersion(name string) (string, string) { - runtime, ver, ok := strings.Cut(name, "://") - if !ok { - return "", "" - } - - switch runtime { - case "cri-o": - name = "github.com/cri-o/cri-o" - case "containerd": - name = "github.com/containerd/containerd" - case "cri-dockerd": - name = "github.com/Mirantis/cri-dockerd" - } - return name, unifiedVersion(ver) -} - -func toProperties(props map[string]string, namespace string) []core.Property { - properties := lo.MapToSlice(props, func(k, v string) core.Property { - return core.Property{ - Name: k, - Value: v, - Namespace: namespace, - } - }) - if len(properties) == 0 { - return nil - } - sort.Slice(properties, func(i, j int) bool { - return properties[i].Name < properties[j].Name - }) - return properties -} - -func generatePURL(name, ver, nodeName string) *packageurl.PackageURL { - var namespace string - // Identify k8s distribution. An empty namespace means upstream. - if namespace = k8sNamespace(ver, nodeName); namespace == "" { - return nil - } else if namespace == "kubernetes" { - namespace = "" - } - - return packageurl.NewPackageURL(purl.TypeK8s, namespace, name, ver, nil, "") -} - -func k8sNamespace(ver, nodeName string) string { - namespace := "kubernetes" - switch { - case strings.Contains(ver, "eks"): - namespace = purl.NamespaceEKS - case strings.Contains(ver, "gke"): - namespace = purl.NamespaceGKE - case strings.Contains(ver, "hotfix"): - if !strings.Contains(nodeName, "aks") { - // Unknown k8s distribution - return "" - } - namespace = purl.NamespaceAKS - case strings.Contains(nodeName, "ocp"): - namespace = purl.NamespaceOCP - } - return namespace -} diff --git a/pkg/k8s/scanner/scanner_test.go b/pkg/k8s/scanner/scanner_test.go deleted file mode 100644 index fcf1c5f10a11..000000000000 --- a/pkg/k8s/scanner/scanner_test.go +++ /dev/null @@ -1,552 +0,0 @@ -package scanner - -import ( - "context" - "sort" - "testing" - - "github.com/package-url/packageurl-go" - "github.com/samber/lo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/flag" - "github.com/aquasecurity/trivy/pkg/purl" - "github.com/aquasecurity/trivy/pkg/sbom/core" - "github.com/aquasecurity/trivy/pkg/uuid" -) - -func TestScanner_Scan(t *testing.T) { - flagOpts := flag.Options{ReportOptions: flag.ReportOptions{Format: "cyclonedx"}} - tests := []struct { - name string - clusterName string - artifacts []*artifacts.Artifact - wantComponents []*core.Component - }{ - { - name: "test cluster info with resources", - clusterName: "test-cluster", - artifacts: []*artifacts.Artifact{ - { - Namespace: "kube-system", - Kind: "Cluster", - Name: "k8s.io/kubernetes", - RawResource: map[string]any{ - "name": "k8s.io/kubernetes", - "version": "1.21.1", - "type": "ClusterInfo", - "Properties": map[string]string{ - "Name": "kind-kind", - "Type": "cluster", - }, - }, - }, - { - Namespace: "kube-system", - Kind: "ControlPlaneComponents", - Name: "k8s.io/apiserver", - RawResource: map[string]any{ - "Containers": []any{ - map[string]any{ - "Digest": "18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f", - "ID": "kube-apiserver:v1.21.1", - "Registry": "k8s.gcr.io", - "Repository": "kube-apiserver", - "Version": "v1.21.1", - }, - }, - "Name": "k8s.io/apiserver", - "Version": "1.21.1", - }, - }, - { - Kind: "NodeComponents", - Name: "kind-control-plane", - RawResource: map[string]any{ - "ContainerRuntimeVersion": "containerd://1.5.2", - "Hostname": "kind-control-plane", - "KubeProxyVersion": "6.2.13-300.fc38.aarch64", - "KubeletVersion": "v1.21.1", - "NodeName": "kind-control-plane", - "OsImage": "Ubuntu 21.04", - "Properties": map[string]string{ - "Architecture": "arm64", - "HostName": "kind-control-plane", - "KernelVersion": "6.2.15-300.fc38.aarch64", - "NodeRole": "master", - "OperatingSystem": "linux", - }, - }, - }, - }, - wantComponents: []*core.Component{ - { - Type: core.TypeApplication, - Name: "github.com/containerd/containerd", - Version: "v1.5.2", - Properties: []core.Property{ - { - Name: k8sComponentName, - Value: "github.com/containerd/containerd", - Namespace: k8sCoreComponentNamespace, - }, - { - Name: k8sComponentType, - Value: "node", - Namespace: k8sCoreComponentNamespace, - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: "golang", - Name: "github.com/containerd/containerd", - Version: "v1.5.2", - Qualifiers: packageurl.Qualifiers{}, - }, - BOMRef: "pkg:golang/github.com%2Fcontainerd%2Fcontainerd@v1.5.2", - }, - }, - { - Type: core.TypeApplication, - Name: "k8s.io/apiserver", - Version: "v1.21.1", - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/apiserver", - Version: "v1.21.1", - }, - BOMRef: "pkg:k8s/k8s.io%2Fapiserver@v1.21.1", - }, - }, - { - Type: core.TypeApplication, - Name: "k8s.io/kubelet", - Version: "v1.21.1", - Properties: []core.Property{ - { - Name: k8sComponentName, - Value: "k8s.io/kubelet", - Namespace: k8sCoreComponentNamespace, - }, - { - Name: k8sComponentType, - Value: "node", - Namespace: k8sCoreComponentNamespace, - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: "k8s", - Name: "k8s.io/kubelet", - Version: "v1.21.1", - }, - BOMRef: "pkg:k8s/k8s.io%2Fkubelet@v1.21.1", - }, - }, - { - Type: core.TypeApplication, - Name: "node-core-components", - PkgIdentifier: ftypes.PkgIdentifier{ - BOMRef: "3ff14136-e09f-4df9-80ea-000000000006", - }, - }, - { - Type: core.TypeContainerImage, - Name: "k8s.gcr.io/kube-apiserver", - Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f", - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: "oci", - Name: "kube-apiserver", - Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f", - Qualifiers: packageurl.Qualifiers{ - { - Key: "repository_url", - Value: "k8s.gcr.io/kube-apiserver", - }, - }, - }, - BOMRef: "pkg:oci/kube-apiserver@sha256%3A18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f?repository_url=k8s.gcr.io%2Fkube-apiserver", - }, - Properties: []core.Property{ - { - Name: core.PropertyPkgID, - Value: "k8s.gcr.io/kube-apiserver:v1.21.1", - }, - { - Name: core.PropertyPkgType, - Value: "oci", - }, - }, - }, - { - Type: core.TypeOS, - Name: "ubuntu", - Version: "21.04", - Properties: []core.Property{ - { - Name: "Class", - Value: "os-pkgs", - Namespace: "", - }, - { - Name: "Type", - Value: "ubuntu", - Namespace: "", - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - BOMRef: "3ff14136-e09f-4df9-80ea-000000000005", - }, - }, - { - Type: core.TypePlatform, - Root: true, - Name: "k8s.io/kubernetes", - Version: "v1.21.1", - Properties: []core.Property{ - { - Name: "Name", - Value: "kind-kind", - Namespace: k8sCoreComponentNamespace, - }, - { - Name: "Type", - Value: "cluster", - Namespace: k8sCoreComponentNamespace, - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/kubernetes", - Version: "v1.21.1", - }, - BOMRef: "pkg:k8s/k8s.io%2Fkubernetes@v1.21.1", - }, - }, - { - Type: core.TypePlatform, - Name: "kind-control-plane", - Properties: []core.Property{ - { - Name: "Architecture", - Value: "arm64", - }, - { - Name: "HostName", - Value: "kind-control-plane", - }, - { - Name: "KernelVersion", - Value: "6.2.15-300.fc38.aarch64", - }, - { - Name: k8sComponentName, - Value: "kind-control-plane", - Namespace: k8sCoreComponentNamespace, - }, - { - Name: "NodeRole", - Value: "master", - }, - { - Name: "OperatingSystem", - Value: "linux", - }, - { - Name: k8sComponentType, - Value: "node", - Namespace: k8sCoreComponentNamespace, - }, - }, - PkgIdentifier: ftypes.PkgIdentifier{ - BOMRef: "3ff14136-e09f-4df9-80ea-000000000004", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() - uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d") - - runner, err := cmd.NewRunner(ctx, flagOpts) - require.NoError(t, err) - - scanner := NewScanner(tt.clusterName, runner, flagOpts) - got, err := scanner.Scan(ctx, tt.artifacts) - require.NoError(t, err) - - gotComponents := lo.Values(got.BOM.Components()) - require.Equal(t, len(tt.wantComponents), len(gotComponents)) - - sort.Slice(gotComponents, func(i, j int) bool { - switch { - case gotComponents[i].Type != gotComponents[j].Type: - return gotComponents[i].Type < gotComponents[j].Type - case gotComponents[i].Name != gotComponents[j].Name: - return gotComponents[i].Name < gotComponents[j].Name - default: - return gotComponents[i].Version < gotComponents[j].Version - } - }) - for i, want := range tt.wantComponents { - assert.EqualExportedValues(t, *want, *gotComponents[i], want.Name) - } - }) - } -} - -func TestTestOsNameVersion(t *testing.T) { - tests := []struct { - name string - nameVersion string - compName string - compVersion string - }{ - - { - name: "valid version", - nameVersion: "ubuntu 20.04", - compName: "ubuntu", - compVersion: "20.04", - }, - { - name: "valid sem version", - nameVersion: "ubuntu 20.04.1", - compName: "ubuntu", - compVersion: "20.04.1", - }, - { - name: "non valid version", - nameVersion: "ubuntu", - compName: "ubuntu", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - name, version := osNameVersion(tt.nameVersion) - assert.Equal(t, name, tt.compName) - assert.Equal(t, version, tt.compVersion) - }) - } -} - -func TestGeneratePURL(t *testing.T) { - tests := []struct { - name string - compName string - compVersion string - nodeName string - want string - }{ - { - name: "native k8s component", - compName: "k8s.io/kubelet", - compVersion: "1.24.10", - nodeName: "kind-kind", - want: "pkg:k8s/k8s.io%2Fkubelet@1.24.10", - }, - - { - name: "GKE", - compName: "k8s.io/kubelet", - compVersion: "1.24.10-gke.2300", - nodeName: "gke-gke1796-default-pool-768cb718-sk1d", - want: "pkg:k8s/gke/k8s.io%2Fkubelet@1.24.10-gke.2300", - }, - { - name: "AKS", - compName: "k8s.io/kubelet", - compVersion: "1.24.10-hotfix.20221110", - nodeName: "aks-default-23814474-vmss000000", - want: "pkg:k8s/aks/k8s.io%2Fkubelet@1.24.10-hotfix.20221110", - }, - { - name: "EKS", - compName: "k8s.io/kubelet", - compVersion: "1.23.17-eks-8ccc7ba", - nodeName: "eks-vmss000000", - want: "pkg:k8s/eks/k8s.io%2Fkubelet@1.23.17-eks-8ccc7ba", - }, - { - name: "Rancher", - compName: "k8s.io/kubelet", - compVersion: "1.24.11+rke2r1", - nodeName: "ip-10-0-5-23", - want: "pkg:k8s/k8s.io%2Fkubelet@1.24.11%2Brke2r1", - }, - { - name: "OCP", - compName: "k8s.io/kubelet", - compVersion: "1.26.7+c7ee51f", - nodeName: "ocp413vpool14000-p8vnm-master-2", - want: "pkg:k8s/ocp/k8s.io%2Fkubelet@1.26.7%2Bc7ee51f", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := generatePURL(tt.compName, tt.compVersion, tt.nodeName) - assert.Equal(t, tt.want, got.String()) - }) - } -} - -func TestK8sNamespace(t *testing.T) { - tests := []struct { - name string - compVersion string - nodeName string - want string - }{ - { - name: "native k8s component", - compVersion: "1.24.10", - nodeName: "kind-kind", - want: "kubernetes", - }, - - { - name: "GKE", - compVersion: "1.24.10-gke.2300", - nodeName: "gke-gke1796-default-pool-768cb718-sk1d", - want: "gke", - }, - { - name: "AKS", - compVersion: "1.24.10-hotfix.20221110", - nodeName: "aks-default-23814474-vmss000000", - want: "aks", - }, - { - name: "EKS", - compVersion: "1.23.17-eks-8ccc7ba", - nodeName: "eks-vmss000000", - want: "eks", - }, - { - name: "Rancher", - compVersion: "1.24.11+rke2r1", - nodeName: "ip-10-0-5-23", - want: "kubernetes", - }, - { - name: "OCP", - compVersion: "1.26.7+c7ee51f", - nodeName: "ocp413vpool14000-p8vnm-master-2", - want: "ocp", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := k8sNamespace(tt.compVersion, tt.nodeName) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestRuntimeVersion(t *testing.T) { - tests := []struct { - name string - runtimeVersion string - wantName string - wantVersion string - }{ - { - name: "containerd", - runtimeVersion: "containerd://1.5.2", - wantName: "github.com/containerd/containerd", - wantVersion: "v1.5.2", - }, - { - name: "cri-o", - runtimeVersion: "cri-o://1.5.2", - wantName: "github.com/cri-o/cri-o", - wantVersion: "v1.5.2", - }, - { - name: "cri-dockerd", - runtimeVersion: "cri-dockerd://1.5.2", - wantName: "github.com/Mirantis/cri-dockerd", - wantVersion: "v1.5.2", - }, - { - name: "na runtime", - runtimeVersion: "cri:1.5.2", - wantName: "", - wantVersion: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotName, gotVersion := runtimeNameVersion(tt.runtimeVersion) - assert.Equal(t, tt.wantName, gotName) - assert.Equal(t, tt.wantVersion, gotVersion) - }) - } -} - -func TestFindNodeName(t *testing.T) { - tests := []struct { - name string - artifacts []*artifacts.Artifact - want string - }{ - { - name: "find node name", - artifacts: []*artifacts.Artifact{ - { - Namespace: "kube-system", - Kind: "Cluster", - Name: "k8s.io/kubernetes", - RawResource: make(map[string]any), - }, - { - Namespace: "kube-system", - Kind: "ControlPlaneComponents", - Name: "k8s.io/apiserver", - RawResource: make(map[string]any), - }, - { - Kind: "NodeComponents", - Name: "kind-control-plane", - RawResource: make(map[string]any), - }, - }, - want: "kind-control-plane", - }, - { - name: "didn't find node name", - artifacts: []*artifacts.Artifact{ - { - Namespace: "kube-system", - Kind: "Cluster", - Name: "k8s.io/kubernetes", - RawResource: make(map[string]any), - }, - { - Namespace: "kube-system", - Kind: "ControlPlaneComponents", - Name: "k8s.io/apiserver", - RawResource: make(map[string]any), - }, - }, - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := Scanner{} - got := s.findNodeName(tt.artifacts) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/pkg/k8s/wire_gen.go b/pkg/k8s/wire_gen.go deleted file mode 100644 index e6c4f7e0dff7..000000000000 --- a/pkg/k8s/wire_gen.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated by Wire. DO NOT EDIT. - -//go:generate go run github.com/google/wire/cmd/wire -//go:build !wireinject -// +build !wireinject - -package k8s - -import ( - "github.com/aquasecurity/trivy-db/pkg/db" - "github.com/aquasecurity/trivy/pkg/cache" - "github.com/aquasecurity/trivy/pkg/fanal/applier" - "github.com/aquasecurity/trivy/pkg/scanner/langpkg" - "github.com/aquasecurity/trivy/pkg/scanner/local" - "github.com/aquasecurity/trivy/pkg/scanner/ospkg" - "github.com/aquasecurity/trivy/pkg/vulnerability" -) - -// Injectors from inject.go: - -func initializeScanK8s(localArtifactCache cache.LocalArtifactCache) *ScanKubernetes { - applierApplier := applier.NewApplier(localArtifactCache) - scanner := ospkg.NewScanner() - langpkgScanner := langpkg.NewScanner() - config := db.Config{} - client := vulnerability.NewClient(config) - localScanner := local.NewScanner(applierApplier, scanner, langpkgScanner, client) - scanKubernetes := NewScanKubernetes(localScanner) - return scanKubernetes -} diff --git a/pkg/k8s/writer.go b/pkg/k8s/writer.go deleted file mode 100644 index abc5bb381b64..000000000000 --- a/pkg/k8s/writer.go +++ /dev/null @@ -1,52 +0,0 @@ -package k8s - -import ( - "context" - "fmt" - - cdx "github.com/CycloneDX/cyclonedx-go" - - "github.com/aquasecurity/trivy/pkg/k8s/report" - "github.com/aquasecurity/trivy/pkg/report/table" - "github.com/aquasecurity/trivy/pkg/types" -) - -// Write writes the results in the give format -func Write(ctx context.Context, k8sreport report.Report, option report.Option) error { - k8sreport.PrintErrors() - - switch option.Format { - case types.FormatJSON: - jwriter := report.JSONWriter{ - Output: option.Output, - Report: option.Report, - } - return jwriter.Write(k8sreport) - case types.FormatTable: - separatedReports := report.SeparateMisconfigReports(k8sreport, option.Scanners) - - if option.Report == report.SummaryReport { - target := fmt.Sprintf("Summary Report for %s", k8sreport.ClusterName) - table.RenderTarget(option.Output, target, table.IsOutputToTerminal(option.Output)) - } - - for _, r := range separatedReports { - writer := &report.TableWriter{ - Output: option.Output, - Report: option.Report, - Severities: option.Severities, - ColumnHeading: report.ColumnHeading(option.Scanners, r.Columns), - } - - if err := writer.Write(ctx, r.Report); err != nil { - return err - } - } - - return nil - case types.FormatCycloneDX: - w := report.NewCycloneDXWriter(option.Output, cdx.BOMFileFormatJSON, option.APIVersion) - return w.Write(ctx, k8sreport.BOM) - } - return nil -} diff --git a/pkg/k8s/writer_test.go b/pkg/k8s/writer_test.go deleted file mode 100644 index 0b2253d9ac24..000000000000 --- a/pkg/k8s/writer_test.go +++ /dev/null @@ -1,439 +0,0 @@ -package k8s - -import ( - "bytes" - "context" - "regexp" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy/pkg/k8s/report" - "github.com/aquasecurity/trivy/pkg/types" -) - -const ( - AllReport = "all" - SummaryReport = "summary" - - tableFormat = "table" - jsonFormat = "json" - cycloneDXFormat = "cyclonedx" -) - -var ( - roleWithMisconfig = report.Resource{ - Namespace: "default", - Kind: "Role", - Name: "system::leader-locking-kube-controller-manager", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "ID100", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - }, - }, - }, - } - apiseverPodWithMisconfigAndInfra = report.Resource{ - Namespace: "kube-system", - Kind: "Pod", - Name: "kube-apiserver", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "KSV-ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "KSV-ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - { - ID: "KSV-ID102", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - - { - ID: "KCV-ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "KCV-ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - }, - }, - }, - } - deployLuaWithSecrets = report.Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "lua", - Results: types.Results{ - { - Secrets: []types.DetectedSecret{ - { - RuleID: "secret1", - Severity: "CRITICAL", - }, - { - RuleID: "secret2", - Severity: "MEDIUM", - }, - }, - }, - }, - } - deployOrionWithMisconfigs = report.Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Results: types.Results{ - { - Misconfigurations: []types.DetectedMisconfiguration{ - { - ID: "ID100", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "ID101", - Status: types.MisconfStatusFailure, - Severity: "MEDIUM", - }, - { - ID: "ID102", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - { - ID: "ID103", - Status: types.MisconfStatusFailure, - Severity: "CRITICAL", - }, - { - ID: "ID104", - Status: types.MisconfStatusFailure, - Severity: "UNKNOWN", - }, - { - ID: "ID105", - Status: types.MisconfStatusFailure, - Severity: "LOW", - }, - { - ID: "ID106", - Status: types.MisconfStatusFailure, - Severity: "HIGH", - }, - }, - }, - }, - } - deployOrionWithVulns = report.Resource{ - Namespace: "default", - Kind: "Deploy", - Name: "orion", - Results: types.Results{ - { - Vulnerabilities: []types.DetectedVulnerability{ - { - VulnerabilityID: "CVE-2022-1111", - Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, - }, - { - VulnerabilityID: "CVE-2022-2222", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - { - VulnerabilityID: "CVE-2022-3333", - Vulnerability: dbTypes.Vulnerability{Severity: "HIGH"}, - }, - { - VulnerabilityID: "CVE-2022-4444", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-5555", - Vulnerability: dbTypes.Vulnerability{Severity: "UNKNOWN"}, - }, - { - VulnerabilityID: "CVE-2022-6666", - Vulnerability: dbTypes.Vulnerability{Severity: "CRITICAL"}, - }, - { - VulnerabilityID: "CVE-2022-7777", - Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, - }, - }, - }, - }, - } -) - -func TestReportWrite_Summary(t *testing.T) { - allSeverities := []dbTypes.Severity{ - dbTypes.SeverityUnknown, - dbTypes.SeverityLow, - dbTypes.SeverityMedium, - dbTypes.SeverityHigh, - dbTypes.SeverityCritical, - } - - tests := []struct { - name string - report report.Report - opt report.Option - scanners types.Scanners - severities []dbTypes.Severity - expectedOutput string - }{ - { - name: "Only config, all serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{deployOrionWithMisconfigs}, - }, - scanners: types.Scanners{types.MisconfigScanner}, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -Workload Assessment -┌───────────┬──────────────┬───────────────────┐ -│ Namespace │ Resource │ Misconfigurations │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -├───────────┼──────────────┼───┼───┼───┼───┼───┤ -│ default │ Deploy/orion │ 1 │ 2 │ 1 │ 2 │ 1 │ -└───────────┴──────────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN - - -Infra Assessment -┌───────────┬──────────┬───────────────────┐ -│ Namespace │ Resource │ Misconfigurations │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -└───────────┴──────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - { - name: "Only vuln, all serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{deployOrionWithVulns}, - }, - scanners: types.Scanners{types.VulnerabilityScanner}, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -Workload Assessment -┌───────────┬──────────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -├───────────┼──────────────┼───┼───┼───┼───┼───┤ -│ default │ Deploy/orion │ 2 │ 1 │ 2 │ 1 │ 1 │ -└───────────┴──────────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN - - -Infra Assessment -┌───────────┬──────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -└───────────┴──────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - { - name: "Only rbac, all serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{roleWithMisconfig}, - }, - scanners: types.Scanners{types.RBACScanner}, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -RBAC Assessment -┌───────────┬─────────────────────────────────────────────────────┬───────────────────┐ -│ Namespace │ Resource │ RBAC Assessment │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -├───────────┼─────────────────────────────────────────────────────┼───┼───┼───┼───┼───┤ -│ default │ Role/system::leader-locking-kube-controller-manager │ │ │ 1 │ │ │ -└───────────┴─────────────────────────────────────────────────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - { - name: "Only secret, all serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{deployLuaWithSecrets}, - }, - scanners: types.Scanners{types.SecretScanner}, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -Workload Assessment -┌───────────┬────────────┬───────────────────┐ -│ Namespace │ Resource │ Secrets │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -├───────────┼────────────┼───┼───┼───┼───┼───┤ -│ default │ Deploy/lua │ 1 │ │ 1 │ │ │ -└───────────┴────────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN - - -Infra Assessment -┌───────────┬──────────┬───────────────────┐ -│ Namespace │ Resource │ Secrets │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -└───────────┴──────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - { - name: "apiserver, only infra and serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{apiseverPodWithMisconfigAndInfra}, - }, - scanners: types.Scanners{types.MisconfigScanner}, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -Workload Assessment -┌───────────┬──────────┬───────────────────┐ -│ Namespace │ Resource │ Misconfigurations │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -└───────────┴──────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN - - -Infra Assessment -┌─────────────┬────────────────────┬───────────────────┐ -│ Namespace │ Resource │ Misconfigurations │ -│ │ ├───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ -├─────────────┼────────────────────┼───┼───┼───┼───┼───┤ -│ kube-system │ Pod/kube-apiserver │ │ 1 │ 2 │ 2 │ │ -└─────────────┴────────────────────┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - { - name: "apiserver, vuln,config,secret and serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{apiseverPodWithMisconfigAndInfra}, - }, - scanners: types.Scanners{ - types.VulnerabilityScanner, - types.MisconfigScanner, - types.SecretScanner, - }, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -Workload Assessment -┌───────────┬──────────┬───────────────────┬───────────────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ Secrets │ -│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ -└───────────┴──────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN - - -Infra Assessment -┌─────────────┬────────────────────┬───────────────────┬───────────────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ Secrets │ -│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ -├─────────────┼────────────────────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ kube-system │ Pod/kube-apiserver │ │ │ │ │ │ │ 1 │ 2 │ 2 │ │ │ │ │ │ │ -└─────────────┴────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - { - name: "apiserver, all misconfig and vuln scanners and serverities", - report: report.Report{ - ClusterName: "test", - Resources: []report.Resource{apiseverPodWithMisconfigAndInfra}, - }, - scanners: types.Scanners{ - types.MisconfigScanner, - types.VulnerabilityScanner, - }, - severities: allSeverities, - expectedOutput: `Summary Report for test -======================= - -Workload Assessment -┌───────────┬──────────┬───────────────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ -│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ -└───────────┴──────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN - - -Infra Assessment -┌─────────────┬────────────────────┬───────────────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ -│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ -├─────────────┼────────────────────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ kube-system │ Pod/kube-apiserver │ │ │ │ │ │ │ 1 │ 2 │ 2 │ │ -└─────────────┴────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ -Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - output := bytes.Buffer{} - - opt := report.Option{ - Format: "table", - Report: "summary", - Output: &output, - Scanners: tc.scanners, - Severities: tc.severities, - } - - err := Write(context.Background(), tc.report, opt) - require.NoError(t, err) - assert.Equal(t, tc.expectedOutput, stripAnsi(output.String()), tc.name) - }) - } -} - -const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" - -var ansiRegexp = regexp.MustCompile(ansi) - -func stripAnsi(str string) string { - return strings.TrimSpace(ansiRegexp.ReplaceAllString(str, "")) -} diff --git a/pkg/licensing/classifier.go b/pkg/licensing/classifier.go index 5f230624bcc9..9faa1bd62d59 100644 --- a/pkg/licensing/classifier.go +++ b/pkg/licensing/classifier.go @@ -1,87 +1,14 @@ package licensing import ( - "fmt" "io" - "sort" - "sync" - classifier "github.com/google/licenseclassifier/v2" - "github.com/google/licenseclassifier/v2/assets" "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/set" ) -var ( - cf *classifier.Classifier - classifierOnce sync.Once - m sync.Mutex -) - -func initGoogleClassifier() error { - // Initialize the default classifier once. - // This loading is expensive and should be called only when the license classification is needed. - var err error - classifierOnce.Do(func() { - log.Debug("Loading the default license classifier...") - cf, err = assets.DefaultClassifier() - }) - return err -} - // Classify detects and classifies the license found in a file func Classify(filePath string, r io.Reader, confidenceLevel float64) (*types.LicenseFile, error) { - content, err := io.ReadAll(r) - if err != nil { - return nil, xerrors.Errorf("unable to read a license file %q: %w", filePath, err) - } - if err = initGoogleClassifier(); err != nil { - return nil, err - } - - var findings types.LicenseFindings - var matchType types.LicenseType - seen := set.New[string]() - - // cf.Match is not thread safe - m.Lock() - - // Use 'github.com/google/licenseclassifier' to find licenses - result := cf.Match(cf.Normalize(content)) - - m.Unlock() - - for _, match := range result.Matches { - if match.Confidence <= confidenceLevel { - continue - } - if seen.Contains(match.Name) { - continue - } - - seen.Append(match.Name) - - switch match.MatchType { - case "Header": - matchType = types.LicenseTypeHeader - case "License": - matchType = types.LicenseTypeFile - } - licenseLink := fmt.Sprintf("https://spdx.org/licenses/%s.html", match.Name) - - findings = append(findings, types.LicenseFinding{ - Name: match.Name, - Confidence: match.Confidence, - Link: licenseLink, - }) - } - sort.Sort(findings) - return &types.LicenseFile{ - Type: matchType, - FilePath: filePath, - Findings: findings, - }, nil + return nil, xerrors.Errorf("not implemented") } diff --git a/pkg/misconf/config_schema.go b/pkg/misconf/config_schema.go deleted file mode 100644 index a728bfa01e3b..000000000000 --- a/pkg/misconf/config_schema.go +++ /dev/null @@ -1,74 +0,0 @@ -package misconf - -import ( - "bytes" - "io/fs" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/xeipuuv/gojsonschema" - "golang.org/x/xerrors" -) - -type ConfigFileSchema struct { - path string - name string - source []byte - schema *gojsonschema.Schema -} - -func LoadConfigSchemas(paths []string) ([]*ConfigFileSchema, error) { - var configSchemas []*ConfigFileSchema - for _, path := range paths { - walkFn := func(path string, info fs.DirEntry, err error) error { - if err != nil { - return err - } - if info.IsDir() || !strings.HasSuffix(info.Name(), ".json") { - return nil - } - - schema, err := newConfigFileSchema(path) - if err != nil { - return xerrors.Errorf("load config file schema: %w", err) - } - - configSchemas = append(configSchemas, schema) - return nil - } - if err := filepath.WalkDir(path, walkFn); err != nil { - return nil, xerrors.Errorf("walk error: %w", err) - } - } - - return configSchemas, nil -} - -func newConfigFileSchema(path string) (*ConfigFileSchema, error) { - b, err := os.ReadFile(path) - if err != nil { - return nil, xerrors.Errorf("read config schema error: %w", err) - } - - // Go's regular expression engine does not support \Z - b = bytes.ReplaceAll(b, []byte(`\\Z`), []byte(`$`)) - - // Go's regular expression engine does not support negative lookahead - b = regexp.MustCompile(`\(\?\!.*\)`).ReplaceAll(b, []byte{}) - schema, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(b)) - if err != nil { - return nil, xerrors.Errorf("compile config schema %s error: %w", path, err) - } - - fileName := filepath.Base(path) - schemaName := strings.TrimSuffix(fileName, filepath.Ext(fileName)) - - return &ConfigFileSchema{ - path: path, - name: schemaName, - schema: schema, - source: b, - }, nil -} diff --git a/pkg/misconf/config_schema_stub.go b/pkg/misconf/config_schema_stub.go new file mode 100644 index 000000000000..331be2b38fe2 --- /dev/null +++ b/pkg/misconf/config_schema_stub.go @@ -0,0 +1,15 @@ +package misconf + +import ( + "golang.org/x/xerrors" +) + +type ConfigFileSchema struct { + path string + name string + source []byte +} + +func LoadConfigSchemas(paths []string) ([]*ConfigFileSchema, error) { + return nil, xerrors.New("pkg/misconf not implemented") +} diff --git a/pkg/misconf/config_schema_test.go b/pkg/misconf/config_schema_test.go deleted file mode 100644 index 8290dbdfaf7c..000000000000 --- a/pkg/misconf/config_schema_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package misconf_test - -import ( - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/misconf" -) - -func Test_LoadConfigSchemas(t *testing.T) { - tests := []struct { - name string - paths []string - want int - }{ - { - name: "load one schema", - paths: []string{ - filepath.Join("testdata", "schemas", "schema1.json"), - }, - want: 1, - }, - { - name: "load dir with schemas", - paths: []string{ - filepath.Join("testdata", "schemas"), - }, - want: 3, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := misconf.LoadConfigSchemas(tt.paths) - require.NoError(t, err) - assert.Len(t, got, tt.want) - }) - } -} diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go deleted file mode 100644 index e52559c4496b..000000000000 --- a/pkg/misconf/scanner.go +++ /dev/null @@ -1,542 +0,0 @@ -package misconf - -import ( - "context" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/samber/lo" - "github.com/xeipuuv/gojsonschema" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/iac/detection" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/azure/arm" - cfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation" - cfparser "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" - dfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile" - "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" - "github.com/aquasecurity/trivy/pkg/iac/scanners/helm" - k8sscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform" - tfprawscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/snapshot" - tfpjsonscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/mapfs" - - _ "embed" -) - -var enablediacTypes = map[detection.FileType]types.ConfigType{ - detection.FileTypeAzureARM: types.AzureARM, - detection.FileTypeCloudFormation: types.CloudFormation, - detection.FileTypeTerraform: types.Terraform, - detection.FileTypeDockerfile: types.Dockerfile, - detection.FileTypeKubernetes: types.Kubernetes, - detection.FileTypeHelm: types.Helm, - detection.FileTypeTerraformPlanJSON: types.TerraformPlanJSON, - detection.FileTypeTerraformPlanSnapshot: types.TerraformPlanSnapshot, - detection.FileTypeJSON: types.JSON, - detection.FileTypeYAML: types.YAML, -} - -type DisabledCheck struct { - ID string - Scanner string // For logging - Reason string // For logging -} - -type ScannerOption struct { - Trace bool - Namespaces []string - PolicyPaths []string - DataPaths []string - DisableEmbeddedPolicies bool - DisableEmbeddedLibraries bool - IncludeDeprecatedChecks bool - - HelmValues []string - HelmValueFiles []string - HelmFileValues []string - HelmStringValues []string - HelmAPIVersions []string - HelmKubeVersion string - TerraformTFVars []string - CloudFormationParamVars []string - TfExcludeDownloaded bool - K8sVersion string - - FilePatterns []string - ConfigFileSchemas []*ConfigFileSchema - - DisabledChecks []DisabledCheck - SkipFiles []string - SkipDirs []string -} - -func (o *ScannerOption) Sort() { - sort.Strings(o.Namespaces) - sort.Strings(o.PolicyPaths) - sort.Strings(o.DataPaths) -} - -type Scanner struct { - fileType detection.FileType - scanner scanners.FSScanner - hasFilePattern bool - configFileSchemas []*ConfigFileSchema -} - -func NewScanner(t detection.FileType, opt ScannerOption) (*Scanner, error) { - opts, err := scannerOptions(t, opt) - if err != nil { - return nil, err - } - - var scanner scanners.FSScanner - switch t { - case detection.FileTypeAzureARM: - scanner = arm.New(opts...) - case detection.FileTypeCloudFormation: - scanner = cfscanner.New(opts...) - case detection.FileTypeDockerfile: - scanner = dfscanner.NewScanner(opts...) - case detection.FileTypeHelm: - scanner = helm.New(opts...) - case detection.FileTypeKubernetes: - scanner = k8sscanner.NewScanner(opts...) - case detection.FileTypeTerraform: - scanner = terraform.New(opts...) - case detection.FileTypeTerraformPlanJSON: - scanner = tfpjsonscanner.New(opts...) - case detection.FileTypeTerraformPlanSnapshot: - scanner = tfprawscanner.New(opts...) - case detection.FileTypeYAML: - scanner = generic.NewYamlScanner(opts...) - case detection.FileTypeJSON: - scanner = generic.NewJsonScanner(opts...) - default: - return nil, xerrors.Errorf("unknown file type: %s", t) - } - - return &Scanner{ - fileType: t, - scanner: scanner, - hasFilePattern: hasFilePattern(t, opt.FilePatterns), - configFileSchemas: opt.ConfigFileSchemas, - }, nil -} - -func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguration, error) { - ctx = log.WithContextPrefix(ctx, log.PrefixMisconfiguration) - newfs, err := s.filterFS(fsys) - if err != nil { - return nil, xerrors.Errorf("fs filter error: %w", err) - } else if newfs == nil { - // Skip scanning if no relevant files are found - return nil, nil - } - - log.DebugContext(ctx, "Scanning files for misconfigurations...", log.String("scanner", s.scanner.Name())) - results, err := s.scanner.ScanFS(ctx, newfs, ".") - if err != nil { - var invalidContentError *cfparser.InvalidContentError - if errors.As(err, &invalidContentError) { - log.ErrorContext(ctx, "scan was broken with InvalidContentError", s.scanner.Name(), log.Err(err)) - return nil, nil - } - return nil, xerrors.Errorf("scan config error: %w", err) - } - - configType := enablediacTypes[s.fileType] - misconfs := ResultsToMisconf(configType, s.scanner.Name(), results) - - // Sort misconfigurations - for _, misconf := range misconfs { - sort.Sort(misconf.Successes) - sort.Sort(misconf.Warnings) - sort.Sort(misconf.Failures) - } - - return misconfs, nil -} - -func (s *Scanner) filterFS(fsys fs.FS) (fs.FS, error) { - mfs, ok := fsys.(*mapfs.FS) - if !ok { - // Unable to filter this filesystem - return fsys, nil - } - - schemas := lo.SliceToMap(s.configFileSchemas, func(schema *ConfigFileSchema) (string, *gojsonschema.Schema) { - return schema.path, schema.schema - }) - - var foundRelevantFile bool - filter := func(path string, d fs.DirEntry) (bool, error) { - file, err := fsys.Open(path) - if err != nil { - return false, err - } - defer file.Close() - - rs, ok := file.(io.ReadSeeker) - if !ok { - return false, xerrors.Errorf("type assertion error: %w", err) - } - - if len(schemas) > 0 && - (s.fileType == detection.FileTypeYAML || s.fileType == detection.FileTypeJSON) && - !detection.IsFileMatchesSchemas(schemas, s.fileType, path, rs) { - return true, nil - } else if !s.hasFilePattern && !detection.IsType(path, rs, s.fileType) { - return true, nil - } - - foundRelevantFile = true - return false, nil - } - newfs, err := mfs.FilterFunc(filter) - if err != nil { - return nil, xerrors.Errorf("fs filter error: %w", err) - } - if !foundRelevantFile { - return nil, nil - } - return newfs, nil -} - -func scannerOptions(t detection.FileType, opt ScannerOption) ([]options.ScannerOption, error) { - disabledCheckIDs := lo.Map(opt.DisabledChecks, func(check DisabledCheck, _ int) string { - log.Info("Check disabled", log.Prefix(log.PrefixMisconfiguration), log.String("ID", check.ID), - log.String("scanner", check.Scanner), log.String("reason", check.Reason)) - return check.ID - }) - - opts := []options.ScannerOption{ - rego.WithEmbeddedPolicies(!opt.DisableEmbeddedPolicies), - rego.WithEmbeddedLibraries(!opt.DisableEmbeddedLibraries), - rego.WithIncludeDeprecatedChecks(opt.IncludeDeprecatedChecks), - rego.WithDisabledCheckIDs(disabledCheckIDs...), - } - - policyFS, policyPaths, err := CreatePolicyFS(opt.PolicyPaths) - if err != nil { - return nil, err - } - if policyFS != nil { - opts = append(opts, rego.WithPolicyFilesystem(policyFS)) - } - - dataFS, dataPaths, err := CreateDataFS(opt.DataPaths, opt.K8sVersion) - if err != nil { - return nil, err - } - - schemas := lo.SliceToMap(opt.ConfigFileSchemas, func(schema *ConfigFileSchema) (string, []byte) { - return schema.name, schema.source - }) - - opts = append(opts, - rego.WithDataDirs(dataPaths...), - rego.WithDataFilesystem(dataFS), - rego.WithCustomSchemas(schemas), - ) - - if opt.Trace { - opts = append(opts, rego.WithPerResultTracing(true)) - } - - if len(policyPaths) > 0 { - opts = append(opts, rego.WithPolicyDirs(policyPaths...)) - } - - if len(opt.DataPaths) > 0 { - opts = append(opts, rego.WithDataDirs(opt.DataPaths...)) - } - - if len(opt.Namespaces) > 0 { - opts = append(opts, rego.WithPolicyNamespaces(opt.Namespaces...)) - } - - switch t { - case detection.FileTypeHelm: - return addHelmOpts(opts, opt), nil - case detection.FileTypeTerraform, detection.FileTypeTerraformPlanSnapshot: - return addTFOpts(opts, opt) - case detection.FileTypeCloudFormation: - return addCFOpts(opts, opt) - default: - return opts, nil - } -} - -func hasFilePattern(t detection.FileType, filePatterns []string) bool { - for _, pattern := range filePatterns { - if strings.HasPrefix(pattern, fmt.Sprintf("%s:", t)) { - return true - } - } - return false -} - -func addTFOpts(opts []options.ScannerOption, scannerOption ScannerOption) ([]options.ScannerOption, error) { - if len(scannerOption.TerraformTFVars) > 0 { - configFS, err := createConfigFS(scannerOption.TerraformTFVars) - if err != nil { - return nil, xerrors.Errorf("failed to create Terraform config FS: %w", err) - } - opts = append( - opts, - terraform.ScannerWithTFVarsPaths(scannerOption.TerraformTFVars...), - terraform.ScannerWithConfigsFileSystem(configFS), - ) - } - - opts = append(opts, - terraform.ScannerWithAllDirectories(true), - terraform.ScannerWithSkipDownloaded(scannerOption.TfExcludeDownloaded), - terraform.ScannerWithSkipFiles(scannerOption.SkipFiles), - terraform.ScannerWithSkipDirs(scannerOption.SkipDirs), - ) - - return opts, nil -} - -func addCFOpts(opts []options.ScannerOption, scannerOption ScannerOption) ([]options.ScannerOption, error) { - if len(scannerOption.CloudFormationParamVars) > 0 { - configFS, err := createConfigFS(scannerOption.CloudFormationParamVars) - if err != nil { - return nil, xerrors.Errorf("failed to create CloudFormation config FS: %w", err) - } - opts = append( - opts, - cfscanner.WithParameterFiles(scannerOption.CloudFormationParamVars...), - cfscanner.WithConfigsFS(configFS), - ) - } - return opts, nil -} - -func addHelmOpts(opts []options.ScannerOption, scannerOption ScannerOption) []options.ScannerOption { - if len(scannerOption.HelmValueFiles) > 0 { - opts = append(opts, helm.ScannerWithValuesFile(scannerOption.HelmValueFiles...)) - } - - if len(scannerOption.HelmValues) > 0 { - opts = append(opts, helm.ScannerWithValues(scannerOption.HelmValues...)) - } - - if len(scannerOption.HelmFileValues) > 0 { - opts = append(opts, helm.ScannerWithFileValues(scannerOption.HelmFileValues...)) - } - - if len(scannerOption.HelmStringValues) > 0 { - opts = append(opts, helm.ScannerWithStringValues(scannerOption.HelmStringValues...)) - } - - if len(scannerOption.HelmAPIVersions) > 0 { - opts = append(opts, helm.ScannerWithAPIVersions(scannerOption.HelmAPIVersions...)) - } - - if scannerOption.HelmKubeVersion != "" { - opts = append(opts, helm.ScannerWithKubeVersion(scannerOption.HelmKubeVersion)) - } - - return opts -} - -func createConfigFS(paths []string) (fs.FS, error) { - mfs := mapfs.New() - for _, path := range paths { - if err := mfs.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) { - return nil, xerrors.Errorf("create dir error: %w", err) - } - if err := mfs.WriteFile(path, path); err != nil { - return nil, xerrors.Errorf("write file error: %w", err) - } - } - return mfs, nil -} - -func CreatePolicyFS(policyPaths []string) (fs.FS, []string, error) { - if len(policyPaths) == 0 { - return nil, nil, nil - } - - mfs := mapfs.New() - for _, p := range policyPaths { - abs, err := filepath.Abs(p) - if err != nil { - return nil, nil, xerrors.Errorf("failed to derive absolute path from '%s': %w", p, err) - } - fi, err := os.Stat(abs) - if errors.Is(err, os.ErrNotExist) { - return nil, nil, xerrors.Errorf("policy file %q not found", abs) - } else if err != nil { - return nil, nil, xerrors.Errorf("file %q stat error: %w", abs, err) - } - - if fi.IsDir() { - if err = mfs.CopyFilesUnder(abs); err != nil { - return nil, nil, xerrors.Errorf("mapfs file copy error: %w", err) - } - } else { - if err := mfs.MkdirAll(filepath.Dir(abs), os.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) { - return nil, nil, xerrors.Errorf("mapfs mkdir error: %w", err) - } - if err := mfs.WriteFile(abs, abs); err != nil { - return nil, nil, xerrors.Errorf("mapfs write error: %w", err) - } - } - } - - // check paths are no longer needed as fs.FS contains only needed files now. - policyPaths = []string{"."} - - return mfs, policyPaths, nil -} - -func CreateDataFS(dataPaths []string, opts ...string) (fs.FS, []string, error) { - fsys := mapfs.New() - - // Check if k8sVersion is provided - if len(opts) > 0 { - k8sVersion := opts[0] - if err := fsys.MkdirAll("system", 0700); err != nil { - return nil, nil, err - } - data := []byte(fmt.Sprintf(`{"k8s": {"version": %q}}`, k8sVersion)) - if err := fsys.WriteVirtualFile("system/k8s-version.json", data, 0600); err != nil { - return nil, nil, err - } - } - - for _, path := range dataPaths { - if err := fsys.CopyFilesUnder(path); err != nil { - return nil, nil, err - } - } - - // dataPaths are no longer needed as fs.FS contains only needed files now. - dataPaths = []string{"."} - - return fsys, dataPaths, nil -} - -// ResultsToMisconf is exported for trivy-plugin-aqua purposes only -func ResultsToMisconf(configType types.ConfigType, scannerName string, results scan.Results) []types.Misconfiguration { - misconfs := make(map[string]types.Misconfiguration) - - for _, result := range results { - flattened := result.Flatten() - - query := fmt.Sprintf("data.%s.%s", result.RegoNamespace(), result.RegoRule()) - - ruleID := result.Rule().AVDID - if result.RegoNamespace() != "" && len(result.Rule().Aliases) > 0 { - ruleID = result.Rule().Aliases[0] - } - - cause := NewCauseWithCode(result, flattened) - - misconfResult := types.MisconfResult{ - Namespace: result.RegoNamespace(), - Query: query, - Message: flattened.Description, - PolicyMetadata: types.PolicyMetadata{ - ID: ruleID, - AVDID: result.Rule().AVDID, - Type: fmt.Sprintf("%s Security Check", scannerName), - Title: result.Rule().Summary, - Description: result.Rule().Explanation, - Severity: string(flattened.Severity), - RecommendedActions: flattened.Resolution, - References: flattened.Links, - }, - CauseMetadata: cause, - Traces: result.Traces(), - } - - filePath := flattened.Location.Filename - misconf, ok := misconfs[filePath] - if !ok { - misconf = types.Misconfiguration{ - FileType: configType, - FilePath: filepath.ToSlash(filePath), // defsec return OS-aware path - } - } - - switch flattened.Status { - case scan.StatusPassed: - misconf.Successes = append(misconf.Successes, misconfResult) - case scan.StatusFailed: - misconf.Failures = append(misconf.Failures, misconfResult) - } - - misconfs[filePath] = misconf - } - - return types.ToMisconfigurations(misconfs) -} - -func NewCauseWithCode(underlying scan.Result, flat scan.FlatResult) types.CauseMetadata { - cause := types.CauseMetadata{ - Resource: flat.Resource, - Provider: flat.RuleProvider.DisplayName(), - Service: flat.RuleService, - StartLine: flat.Location.StartLine, - EndLine: flat.Location.EndLine, - } - for _, o := range flat.Occurrences { - cause.Occurrences = append(cause.Occurrences, types.Occurrence{ - Resource: o.Resource, - Filename: o.Filename, - Location: types.Location{ - StartLine: o.StartLine, - EndLine: o.EndLine, - }, - }) - } - - // only failures have a code cause - // failures can happen either due to lack of - // OR misconfiguration of something - if underlying.Status() == scan.StatusFailed { - if flat.RenderedCause.Raw != "" { - highlighted, _ := scan.Highlight(flat.Location.Filename, flat.RenderedCause.Raw, scan.DarkTheme) - cause.RenderedCause = types.RenderedCause{ - Raw: flat.RenderedCause.Raw, - Highlighted: highlighted, - } - } - - if code, err := underlying.GetCode(); err == nil { - cause.Code = types.Code{ - Lines: lo.Map(code.Lines, func(l scan.Line, i int) types.Line { - return types.Line{ - Number: l.Number, - Content: l.Content, - IsCause: l.IsCause, - Annotation: l.Annotation, - Truncated: l.Truncated, - Highlighted: l.Highlighted, - FirstCause: l.FirstCause, - LastCause: l.LastCause, - } - }), - } - } - } - return cause -} diff --git a/pkg/misconf/scanner_stub.go b/pkg/misconf/scanner_stub.go new file mode 100644 index 000000000000..c131324f2140 --- /dev/null +++ b/pkg/misconf/scanner_stub.go @@ -0,0 +1,65 @@ +package misconf + +import ( + "context" + "io/fs" + "sort" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/iac/detection" + + _ "embed" +) + +type DisabledCheck struct { + ID string + Scanner string // For logging + Reason string // For logging +} + +type ScannerOption struct { + Trace bool + Namespaces []string + PolicyPaths []string + DataPaths []string + DisableEmbeddedPolicies bool + DisableEmbeddedLibraries bool + IncludeDeprecatedChecks bool + + HelmValues []string + HelmValueFiles []string + HelmFileValues []string + HelmStringValues []string + HelmAPIVersions []string + HelmKubeVersion string + TerraformTFVars []string + CloudFormationParamVars []string + TfExcludeDownloaded bool + K8sVersion string + + FilePatterns []string + ConfigFileSchemas []*ConfigFileSchema + + DisabledChecks []DisabledCheck + SkipFiles []string + SkipDirs []string +} + +func (o *ScannerOption) Sort() { + sort.Strings(o.Namespaces) + sort.Strings(o.PolicyPaths) + sort.Strings(o.DataPaths) +} + +type Scanner struct { +} + +func NewScanner(t detection.FileType, opt ScannerOption) (*Scanner, error) { + return nil, xerrors.New("pkg/misconf not implemented") +} + +func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguration, error) { + return nil, xerrors.New("pkg/misconf not implemented") +} diff --git a/pkg/misconf/scanner_test.go b/pkg/misconf/scanner_test.go deleted file mode 100644 index e85caf651a53..000000000000 --- a/pkg/misconf/scanner_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package misconf - -import ( - "context" - "io/fs" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/iac/detection" - "github.com/aquasecurity/trivy/pkg/mapfs" -) - -func TestScannerOption_Sort(t *testing.T) { - type fields struct { - Namespaces []string - PolicyPaths []string - DataPaths []string - } - tests := []struct { - name string - fields fields - want ScannerOption - }{ - { - name: "happy path", - fields: fields{ - Namespaces: []string{ - "main", - "custom", - "default", - }, - PolicyPaths: []string{"policy"}, - DataPaths: []string{ - "data/b", - "data/c", - "data/a", - }, - }, - want: ScannerOption{ - Namespaces: []string{ - "custom", - "default", - "main", - }, - PolicyPaths: []string{"policy"}, - DataPaths: []string{ - "data/a", - "data/b", - "data/c", - }, - }, - }, - { - name: "missing some fields", - fields: fields{ - Namespaces: []string{"main"}, - PolicyPaths: nil, - DataPaths: nil, - }, - want: ScannerOption{ - Namespaces: []string{"main"}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - o := ScannerOption{ - Namespaces: tt.fields.Namespaces, - PolicyPaths: tt.fields.PolicyPaths, - DataPaths: tt.fields.DataPaths, - } - o.Sort() - - assert.Equal(t, tt.want, o) - }) - } -} - -func TestScanner_Scan(t *testing.T) { - type fields struct { - opt ScannerOption - } - type file struct { - path string - content []byte - } - tests := []struct { - name string - fileType detection.FileType - fields fields - files []file - wantFilePath string - wantFileType types.ConfigType - misconfsExpected int - }{ - { - name: "happy path. Dockerfile", - fileType: detection.FileTypeDockerfile, - fields: fields{ - opt: ScannerOption{}, - }, - files: []file{ - { - path: "Dockerfile", - content: []byte(`FROM alpine`), - }, - }, - wantFilePath: "Dockerfile", - wantFileType: types.Dockerfile, - misconfsExpected: 1, - }, - { - name: "happy path. Dockerfile with custom file name", - fileType: detection.FileTypeDockerfile, - fields: fields{ - opt: ScannerOption{ - FilePatterns: []string{"dockerfile:dockerf"}, - }, - }, - files: []file{ - { - path: "dockerf", - content: []byte(`FROM alpine`), - }, - }, - wantFilePath: "dockerf", - wantFileType: types.Dockerfile, - misconfsExpected: 1, - }, - { - name: "happy path. terraform plan file", - fileType: detection.FileTypeTerraformPlanJSON, - files: []file{ - { - path: "main.tfplan.json", - content: []byte(`{"format_version":"1.1","terraform_version":"1.4.6","planned_values":{"root_module":{"resources":[{"address":"aws_s3_bucket.my-bucket","mode":"managed","type":"aws_s3_bucket","name":"my-bucket","provider_name":"registry.terraform.io/hashicorp/aws","schema_version":0,"values":{"bucket":"evil","force_destroy":false,"tags":null,"timeouts":null},"sensitive_values":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[],"website":[]}}]}},"resource_changes":[{"address":"aws_s3_bucket.my-bucket","mode":"managed","type":"aws_s3_bucket","name":"my-bucket","provider_name":"registry.terraform.io/hashicorp/aws","change":{"actions":["create"],"before":null,"after":{"bucket":"evil","force_destroy":false,"tags":null,"timeouts":null},"after_unknown":{"acceleration_status":true,"acl":true,"arn":true,"bucket_domain_name":true,"bucket_prefix":true,"bucket_regional_domain_name":true,"cors_rule":true,"grant":true,"hosted_zone_id":true,"id":true,"lifecycle_rule":true,"logging":true,"object_lock_configuration":true,"object_lock_enabled":true,"policy":true,"region":true,"replication_configuration":true,"request_payer":true,"server_side_encryption_configuration":true,"tags_all":true,"versioning":true,"website":true,"website_domain":true,"website_endpoint":true},"before_sensitive":false,"after_sensitive":{"cors_rule":[],"grant":[],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"replication_configuration":[],"server_side_encryption_configuration":[],"tags_all":{},"versioning":[],"website":[]}}}],"configuration":{"provider_config":{"aws":{"name":"aws","full_name":"registry.terraform.io/hashicorp/aws","expressions":{"profile":{"constant_value":"foo-bar-123123123"},"region":{"constant_value":"us-west-1"}}}},"root_module":{"resources":[{"address":"aws_s3_bucket.my-bucket","mode":"managed","type":"aws_s3_bucket","name":"my-bucket","provider_config_key":"aws","expressions":{"bucket":{"constant_value":"evil"}},"schema_version":0}]}}}`), - }, - }, - wantFilePath: "main.tf", - wantFileType: types.TerraformPlanJSON, - misconfsExpected: 2, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a virtual filesystem for testing - fsys := mapfs.New() - for _, f := range tt.files { - err := fsys.WriteVirtualFile(f.path, f.content, 0666) - require.NoError(t, err) - } - - // s, err := tt.scannerFunc(tt.fields.filePatterns, tt.fields.opt) - s, err := NewScanner(tt.fileType, tt.fields.opt) - require.NoError(t, err) - - misconfs, err := s.Scan(context.Background(), fsys) - require.NoError(t, err) - require.Len(t, misconfs, tt.misconfsExpected, "wrong number of misconfigurations found") - if tt.misconfsExpected == 1 { - assert.Equal(t, tt.wantFilePath, misconfs[0].FilePath, "filePaths don't equal") - assert.Equal(t, tt.wantFileType, misconfs[0].FileType, "fileTypes don't equal") - } - }) - } -} - -func Test_createPolicyFS(t *testing.T) { - t.Run("outside pwd", func(t *testing.T) { - tmpDir := t.TempDir() - require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "subdir", "testdir"), 0750)) - f, got, err := CreatePolicyFS([]string{filepath.Join(tmpDir, "subdir", "testdir")}) - assertFS(t, tmpDir, f, got, err) - }) -} - -func Test_CreateDataFS(t *testing.T) { - t.Run("outside pwd", func(t *testing.T) { - tmpDir := t.TempDir() - require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "subdir", "testdir"), 0750)) - f, got, err := CreateDataFS([]string{filepath.Join(tmpDir, "subdir", "testdir")}) - assertFS(t, tmpDir, f, got, err) - }) -} - -func assertFS(t *testing.T, tmpDir string, f fs.FS, got []string, err error) { - t.Helper() - - require.NoError(t, err) - assert.Equal(t, []string{"."}, got) - - d, err := f.Open(tmpDir) - require.NoError(t, err) - stat, err := d.Stat() - require.NoError(t, err) - assert.True(t, stat.IsDir()) -} diff --git a/pkg/misconf/testdata/schemas/no-schema.file b/pkg/misconf/testdata/schemas/no-schema.file deleted file mode 100644 index 7b4d68d70fca..000000000000 --- a/pkg/misconf/testdata/schemas/no-schema.file +++ /dev/null @@ -1 +0,0 @@ -empty \ No newline at end of file diff --git a/pkg/misconf/testdata/schemas/schema-with-bad-regex.json b/pkg/misconf/testdata/schemas/schema-with-bad-regex.json deleted file mode 100644 index 522a1ccb46e3..000000000000 --- a/pkg/misconf/testdata/schemas/schema-with-bad-regex.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$id": "https://example.com/test.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "patternProperties": { - "^(?![\\.0-9]).": { "type": "string" }, - "test\\Z": { "type": "string" } - } -} diff --git a/pkg/misconf/testdata/schemas/schema1.json b/pkg/misconf/testdata/schemas/schema1.json deleted file mode 100644 index f72cdb0d2016..000000000000 --- a/pkg/misconf/testdata/schemas/schema1.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$id": "https://example.com/test.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "service": { "type": "string" } - }, - "required": ["service"] -} \ No newline at end of file diff --git a/pkg/misconf/testdata/schemas/schema2.json b/pkg/misconf/testdata/schemas/schema2.json deleted file mode 100644 index f72cdb0d2016..000000000000 --- a/pkg/misconf/testdata/schemas/schema2.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$id": "https://example.com/test.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "service": { "type": "string" } - }, - "required": ["service"] -} \ No newline at end of file diff --git a/pkg/oci/artifact.go b/pkg/oci/artifact.go index acef07923ed8..ea53d8285430 100644 --- a/pkg/oci/artifact.go +++ b/pkg/oci/artifact.go @@ -8,7 +8,6 @@ import ( "path/filepath" "sync" - "github.com/cheggaaa/pb/v3" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" @@ -153,25 +152,12 @@ func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption) } func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir string, quiet bool) error { - size, err := layer.Size() - if err != nil { - return xerrors.Errorf("size error: %w", err) - } - rc, err := layer.Compressed() if err != nil { return xerrors.Errorf("failed to fetch the layer: %w", err) } defer rc.Close() - // Show progress bar - bar := pb.Full.Start64(size) - if quiet { - bar.SetWriter(io.Discard) - } - pr := bar.NewProxyReader(rc) - defer bar.Finish() - // https://github.com/hashicorp/go-getter/issues/326 tempDir, err := os.MkdirTemp("", "trivy") if err != nil { @@ -188,7 +174,7 @@ func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir s }() // Download the layer content into a temporal file - if _, err = io.Copy(f, pr); err != nil { + if _, err = io.Copy(f, rc); err != nil { return xerrors.Errorf("copy error: %w", err) } diff --git a/pkg/parallel/pipeline.go b/pkg/parallel/pipeline.go index 7dbc70f3e17c..8bf94e7d2616 100644 --- a/pkg/parallel/pipeline.go +++ b/pkg/parallel/pipeline.go @@ -3,7 +3,6 @@ package parallel import ( "context" - "github.com/cheggaaa/pb/v3" "golang.org/x/sync/errgroup" ) @@ -46,13 +45,6 @@ func NewPipeline[T, U any](numWorkers int, progress bool, items []T, // Do executes pipeline processing. // It exits when any error occurs. func (p *Pipeline[T, U]) Do(ctx context.Context) error { - // progress bar - var bar *pb.ProgressBar - if p.progress { - bar = pb.StartNew(len(p.items)) - defer bar.Finish() - } - g, ctx := errgroup.WithContext(ctx) itemCh := make(chan T) @@ -60,9 +52,6 @@ func (p *Pipeline[T, U]) Do(ctx context.Context) error { g.Go(func() error { defer close(itemCh) for _, item := range p.items { - if p.progress { - bar.Increment() - } select { case itemCh <- item: case <-ctx.Done(): diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go deleted file mode 100644 index 6b1d175e3115..000000000000 --- a/pkg/policy/policy.go +++ /dev/null @@ -1,238 +0,0 @@ -package policy - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/open-policy-agent/opa/bundle" - "golang.org/x/xerrors" - "k8s.io/utils/clock" - - "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/oci" -) - -const ( - BundleVersion = 1 // Latest released MAJOR version for trivy-checks - BundleRepository = "mirror.gcr.io/aquasec/trivy-checks" - policyMediaType = "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip" - updateInterval = 24 * time.Hour -) - -type options struct { - artifact *oci.Artifact - clock clock.Clock -} - -// WithOCIArtifact takes an OCI artifact -func WithOCIArtifact(art *oci.Artifact) Option { - return func(opts *options) { - opts.artifact = art - } -} - -// WithClock takes a clock -func WithClock(c clock.Clock) Option { - return func(opts *options) { - opts.clock = c - } -} - -// Option is a functional option -type Option func(*options) - -// Client implements check operations -type Client struct { - *options - policyDir string - checkBundleRepo string - quiet bool -} - -// Metadata holds default check metadata -type Metadata struct { - Digest string - DownloadedAt time.Time -} - -func (m Metadata) String() string { - return fmt.Sprintf(`Check Bundle: - Digest: %s - DownloadedAt: %s -`, m.Digest, m.DownloadedAt.UTC()) -} - -// NewClient is the factory method for check client -func NewClient(cacheDir string, quiet bool, checkBundleRepo string, opts ...Option) (*Client, error) { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } - - if checkBundleRepo == "" { - checkBundleRepo = fmt.Sprintf("%s:%d", BundleRepository, BundleVersion) - } - - return &Client{ - options: o, - policyDir: filepath.Join(cacheDir, "policy"), - checkBundleRepo: checkBundleRepo, - quiet: quiet, - }, nil -} - -func (c *Client) populateOCIArtifact(ctx context.Context, registryOpts types.RegistryOptions) { - if c.artifact == nil { - log.DebugContext(ctx, "Loading check bundle", log.String("repository", c.checkBundleRepo)) - c.artifact = oci.NewArtifact(c.checkBundleRepo, registryOpts) - } -} - -// DownloadBuiltinChecks download default policies from GitHub Pages -func (c *Client) DownloadBuiltinChecks(ctx context.Context, registryOpts types.RegistryOptions) error { - c.populateOCIArtifact(ctx, registryOpts) - - dst := c.contentDir() - if err := c.artifact.Download(ctx, dst, oci.DownloadOption{ - MediaType: policyMediaType, - Quiet: c.quiet, - }, - ); err != nil { - return xerrors.Errorf("download error: %w", err) - } - - digest, err := c.artifact.Digest(ctx) - if err != nil { - return xerrors.Errorf("digest error: %w", err) - } - log.DebugContext(ctx, "Digest of the built-in checks", log.String("digest", digest)) - - // Update metadata.json with the new digest and the current date - if err = c.updateMetadata(digest, c.clock.Now()); err != nil { - return xerrors.Errorf("unable to update the check metadata: %w", err) - } - - return nil -} - -// LoadBuiltinChecks loads default policies -func (c *Client) LoadBuiltinChecks() ([]string, error) { - f, err := os.Open(c.manifestPath()) - if err != nil { - return nil, xerrors.Errorf("manifest file open error (%s): %w", c.manifestPath(), err) - } - defer f.Close() - - var manifest bundle.Manifest - if err = json.NewDecoder(f).Decode(&manifest); err != nil { - return nil, xerrors.Errorf("json decode error (%s): %w", c.manifestPath(), err) - } - - // If the "roots" field is not included in the manifest it defaults to [""] - // which means that ALL data and check must come from the bundle. - if manifest.Roots == nil || len(*manifest.Roots) == 0 { - return []string{c.contentDir()}, nil - } - - var policyPaths []string - for _, root := range *manifest.Roots { - policyPaths = append(policyPaths, filepath.Join(c.contentDir(), root)) - } - - return policyPaths, nil -} - -// NeedsUpdate returns if the default check should be updated -func (c *Client) NeedsUpdate(ctx context.Context, registryOpts types.RegistryOptions) (bool, error) { - meta, err := c.GetMetadata(ctx) - if err != nil { - return true, nil - } - - // No need to update if it's been within a day since the last update. - if c.clock.Now().Before(meta.DownloadedAt.Add(updateInterval)) { - return false, nil - } - - c.populateOCIArtifact(ctx, registryOpts) - digest, err := c.artifact.Digest(ctx) - if err != nil { - return false, xerrors.Errorf("digest error: %w", err) - } - - if meta.Digest != digest { - return true, nil - } - - // Update DownloadedAt with the current time. - // Otherwise, if there are no updates in the remote registry, - // the digest will be fetched every time even after this. - if err = c.updateMetadata(meta.Digest, time.Now()); err != nil { - return false, xerrors.Errorf("unable to update the check metadata: %w", err) - } - - return false, nil -} - -func (c *Client) contentDir() string { - return filepath.Join(c.policyDir, "content") -} - -func (c *Client) metadataPath() string { - return filepath.Join(c.policyDir, "metadata.json") -} - -func (c *Client) manifestPath() string { - return filepath.Join(c.contentDir(), bundle.ManifestExt) -} - -func (c *Client) updateMetadata(digest string, now time.Time) error { - f, err := os.Create(c.metadataPath()) - if err != nil { - return xerrors.Errorf("failed to open a check manifest: %w", err) - } - defer f.Close() - - meta := Metadata{ - Digest: digest, - DownloadedAt: now, - } - - if err = json.NewEncoder(f).Encode(meta); err != nil { - return xerrors.Errorf("json encode error: %w", err) - } - - return nil -} - -func (c *Client) GetMetadata(ctx context.Context) (*Metadata, error) { - f, err := os.Open(c.metadataPath()) - if err != nil { - log.DebugContext(ctx, "Failed to open the check metadata", log.Err(err)) - return nil, err - } - defer f.Close() - - var meta Metadata - if err = json.NewDecoder(f).Decode(&meta); err != nil { - log.WarnContext(ctx, "Check metadata decode error", log.Err(err)) - return nil, err - } - - return &meta, nil -} - -func (c *Client) Clear() error { - if err := os.RemoveAll(c.policyDir); err != nil { - return xerrors.Errorf("failed to remove check bundle: %w", err) - } - return nil -} diff --git a/pkg/policy/policy_stub.go b/pkg/policy/policy_stub.go new file mode 100644 index 000000000000..8b6f6f370ff5 --- /dev/null +++ b/pkg/policy/policy_stub.go @@ -0,0 +1,91 @@ +package policy + +import ( + "context" + "fmt" + "time" + + "golang.org/x/xerrors" + "k8s.io/utils/clock" + + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/oci" +) + +const ( + BundleVersion = 1 // Latest released MAJOR version for trivy-checks + BundleRepository = "mirror.gcr.io/aquasec/trivy-checks" + policyMediaType = "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip" + updateInterval = 24 * time.Hour +) + +type options struct { + artifact *oci.Artifact + clock clock.Clock +} + +// WithOCIArtifact takes an OCI artifact +func WithOCIArtifact(art *oci.Artifact) Option { + return func(opts *options) { + opts.artifact = art + } +} + +// WithClock takes a clock +func WithClock(c clock.Clock) Option { + return func(opts *options) { + opts.clock = c + } +} + +// Option is a functional option +type Option func(*options) + +// Client implements check operations +type Client struct { + *options + policyDir string + checkBundleRepo string + quiet bool +} + +// Metadata holds default check metadata +type Metadata struct { + Digest string + DownloadedAt time.Time +} + +func (m Metadata) String() string { + return fmt.Sprintf(`Check Bundle: + Digest: %s + DownloadedAt: %s +`, m.Digest, m.DownloadedAt.UTC()) +} + +// NewClient is the factory method for check client +func NewClient(cacheDir string, quiet bool, checkBundleRepo string, opts ...Option) (*Client, error) { + return nil, xerrors.New("pkg/policy not implemented") +} + +// DownloadBuiltinChecks download default policies from GitHub Pages +func (c *Client) DownloadBuiltinChecks(ctx context.Context, registryOpts types.RegistryOptions) error { + return xerrors.New("pkg/policy not implemented") +} + +// LoadBuiltinChecks loads default policies +func (c *Client) LoadBuiltinChecks() ([]string, error) { + return nil, xerrors.New("pkg/policy not implemented") +} + +// NeedsUpdate returns if the default check should be updated +func (c *Client) NeedsUpdate(ctx context.Context, registryOpts types.RegistryOptions) (bool, error) { + return false, xerrors.New("pkg/policy not implemented") +} + +func (c *Client) GetMetadata(ctx context.Context) (*Metadata, error) { + return nil, xerrors.New("pkg/policy not implemented") +} + +func (c *Client) Clear() error { + return xerrors.New("pkg/policy not implemented") +} diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go deleted file mode 100644 index c31de3172cc9..000000000000 --- a/pkg/policy/policy_test.go +++ /dev/null @@ -1,392 +0,0 @@ -package policy_test - -import ( - "context" - "encoding/json" - "errors" - "io" - "os" - "path/filepath" - "testing" - "time" - - v1 "github.com/google/go-containerregistry/pkg/v1" - fakei "github.com/google/go-containerregistry/pkg/v1/fake" - "github.com/google/go-containerregistry/pkg/v1/tarball" - "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "k8s.io/utils/clock" - fake "k8s.io/utils/clock/testing" - - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/oci" - "github.com/aquasecurity/trivy/pkg/policy" -) - -type fakeLayer struct { - v1.Layer -} - -func (f fakeLayer) MediaType() (types.MediaType, error) { - return "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", nil -} - -func newFakeLayer(t *testing.T) v1.Layer { - layer, err := tarball.LayerFromFile("testdata/bundle.tar.gz") - require.NoError(t, err) - require.NotNil(t, layer) - - return fakeLayer{layer} -} - -type brokenLayer struct { - v1.Layer -} - -func (b brokenLayer) MediaType() (types.MediaType, error) { - return "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", nil -} - -func (b brokenLayer) Compressed() (io.ReadCloser, error) { - return nil, errors.New("compressed error") -} - -func newBrokenLayer(t *testing.T) v1.Layer { - layer, err := tarball.LayerFromFile("testdata/bundle.tar.gz") - require.NoError(t, err) - - return brokenLayer{layer} -} - -func TestClient_LoadBuiltinPolicies(t *testing.T) { - tests := []struct { - name string - cacheDir string - want []string - wantErr string - }{ - { - name: "happy path", - cacheDir: "testdata/happy", - want: []string{ - filepath.Join("testdata", "happy", "policy", "content", "kubernetes"), - filepath.Join("testdata", "happy", "policy", "content", "docker"), - }, - }, - { - name: "empty roots", - cacheDir: "testdata/empty", - want: []string{ - filepath.Join("testdata", "empty", "policy", "content"), - }, - }, - { - name: "broken manifest", - cacheDir: "testdata/broken", - want: []string{}, - wantErr: "json decode error", - }, - { - name: "no such file", - cacheDir: "testdata/unknown", - want: []string{}, - wantErr: "manifest file open error", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Mock image - img := new(fakei.FakeImage) - img.LayersReturns([]v1.Layer{newFakeLayer(t)}, nil) - img.ManifestReturns(&v1.Manifest{ - Layers: []v1.Descriptor{ - { - MediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", - Size: 100, - Digest: v1.Hash{ - Algorithm: "sha256", - Hex: "cba33656188782852f58993f45b68bfb8577f64cdcf02a604e3fc2afbeb5f2d8", - }, - Annotations: map[string]string{ - "org.opencontainers.image.title": "bundle.tar.gz", - }, - }, - }, - }, nil) - - // Mock OCI artifact - art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) - c, err := policy.NewClient(tt.cacheDir, true, "", policy.WithOCIArtifact(art)) - require.NoError(t, err) - - got, err := c.LoadBuiltinChecks() - if tt.wantErr != "" { - require.ErrorContains(t, err, tt.wantErr) - return - } - require.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestClient_NeedsUpdate(t *testing.T) { - type digestReturns struct { - h v1.Hash - err error - } - tests := []struct { - name string - clock clock.Clock - digestReturns digestReturns - metadata any - want bool - wantErr bool - }{ - { - name: "recent download", - clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)), - digestReturns: digestReturns{ - h: v1.Hash{ - Algorithm: "sha256", - Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - }, - }, - metadata: policy.Metadata{ - Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`, - DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), - }, - want: false, - }, - { - name: "same digest", - clock: fake.NewFakeClock(time.Date(2021, 1, 2, 1, 0, 0, 0, time.UTC)), - digestReturns: digestReturns{ - h: v1.Hash{ - Algorithm: "sha256", - Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - }, - }, - metadata: policy.Metadata{ - Digest: `sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d`, - DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), - }, - want: false, - }, - { - name: "different digest", - clock: fake.NewFakeClock(time.Date(2021, 1, 2, 1, 0, 0, 0, time.UTC)), - digestReturns: digestReturns{ - h: v1.Hash{ - Algorithm: "sha256", - Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - }, - }, - metadata: policy.Metadata{ - Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`, - DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), - }, - want: true, - }, - { - name: "sad: Digest returns an error", - clock: fake.NewFakeClock(time.Date(2021, 1, 2, 1, 0, 0, 0, time.UTC)), - digestReturns: digestReturns{ - err: errors.New("error"), - }, - metadata: policy.Metadata{ - Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`, - DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), - }, - want: false, - wantErr: true, - }, - { - name: "sad: non-existent metadata", - clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)), - want: true, - }, - { - name: "sad: broken metadata", - clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)), - metadata: `"foo"`, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set up a temporary directory - tmpDir := t.TempDir() - - // Mock image - img := new(fakei.FakeImage) - img.LayersReturns([]v1.Layer{newFakeLayer(t)}, nil) - img.DigestReturns(tt.digestReturns.h, tt.digestReturns.err) - img.ManifestReturns(&v1.Manifest{ - Layers: []v1.Descriptor{ - { - MediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", - Size: 100, - Digest: v1.Hash{ - Algorithm: "sha256", - Hex: "cba33656188782852f58993f45b68bfb8577f64cdcf02a604e3fc2afbeb5f2d8", - }, - Annotations: map[string]string{ - "org.opencontainers.image.title": "bundle.tar.gz", - }, - }, - }, - }, nil) - - // Create a check directory - err := os.MkdirAll(filepath.Join(tmpDir, "policy"), os.ModePerm) - require.NoError(t, err) - - if tt.metadata != nil { - b, err := json.Marshal(tt.metadata) - require.NoError(t, err) - - // Write a metadata file - metadataPath := filepath.Join(tmpDir, "policy", "metadata.json") - err = os.WriteFile(metadataPath, b, os.ModePerm) - require.NoError(t, err) - } - - art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) - c, err := policy.NewClient(tmpDir, true, "", policy.WithOCIArtifact(art), policy.WithClock(tt.clock)) - require.NoError(t, err) - - // Assert results - got, err := c.NeedsUpdate(context.Background(), ftypes.RegistryOptions{}) - assert.Equal(t, tt.wantErr, err != nil) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestClient_DownloadBuiltinPolicies(t *testing.T) { - type digestReturns struct { - h v1.Hash - err error - } - type layersReturns struct { - layers []v1.Layer - err error - } - tests := []struct { - name string - clock clock.Clock - layersReturns layersReturns - digestReturns digestReturns - want *policy.Metadata - wantErr string - }{ - { - name: "happy path", - clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)), - layersReturns: layersReturns{ - layers: []v1.Layer{newFakeLayer(t)}, - }, - digestReturns: digestReturns{ - h: v1.Hash{ - Algorithm: "sha256", - Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - }, - }, - want: &policy.Metadata{ - Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - DownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC), - }, - }, - { - name: "sad: broken layer", - clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)), - layersReturns: layersReturns{ - layers: []v1.Layer{newBrokenLayer(t)}, - }, - digestReturns: digestReturns{ - h: v1.Hash{ - Algorithm: "sha256", - Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - }, - }, - wantErr: "compressed error", - }, - { - name: "sad: Digest returns an error", - clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)), - layersReturns: layersReturns{ - layers: []v1.Layer{newFakeLayer(t)}, - }, - digestReturns: digestReturns{ - err: errors.New("error"), - }, - want: &policy.Metadata{ - Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d", - DownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC), - }, - wantErr: "digest error", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tempDir := t.TempDir() - - // Mock image - img := new(fakei.FakeImage) - img.DigestReturns(tt.digestReturns.h, tt.digestReturns.err) - img.LayersReturns(tt.layersReturns.layers, tt.layersReturns.err) - img.ManifestReturns(&v1.Manifest{ - Layers: []v1.Descriptor{ - { - MediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", - Size: 100, - Digest: v1.Hash{ - Algorithm: "sha256", - Hex: "cba33656188782852f58993f45b68bfb8577f64cdcf02a604e3fc2afbeb5f2d8", - }, - Annotations: map[string]string{ - "org.opencontainers.image.title": "bundle.tar.gz", - }, - }, - }, - }, nil) - - // Mock OCI artifact - art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) - c, err := policy.NewClient(tempDir, true, "", policy.WithClock(tt.clock), policy.WithOCIArtifact(art)) - require.NoError(t, err) - - err = c.DownloadBuiltinChecks(context.Background(), ftypes.RegistryOptions{}) - if tt.wantErr != "" { - require.ErrorContains(t, err, tt.wantErr) - return - } - require.NoError(t, err) - - // Assert metadata.json - metadata := filepath.Join(tempDir, "policy", "metadata.json") - b, err := os.ReadFile(metadata) - require.NoError(t, err) - - got := new(policy.Metadata) - err = json.Unmarshal(b, got) - require.NoError(t, err) - - assert.Equal(t, tt.want, got) - }) - } -} - -func TestClient_Clear(t *testing.T) { - cacheDir := t.TempDir() - err := os.MkdirAll(filepath.Join(cacheDir, "policy"), 0755) - require.NoError(t, err) - - c, err := policy.NewClient(cacheDir, true, "") - require.NoError(t, err) - require.NoError(t, c.Clear()) -} diff --git a/pkg/policy/testdata/broken/policy/content/.manifest b/pkg/policy/testdata/broken/policy/content/.manifest deleted file mode 100644 index c7759bfead42..000000000000 --- a/pkg/policy/testdata/broken/policy/content/.manifest +++ /dev/null @@ -1,3 +0,0 @@ -{ - "revision": "1", - } diff --git a/pkg/policy/testdata/bundle.tar.gz b/pkg/policy/testdata/bundle.tar.gz deleted file mode 100644 index 83fd553a7aa2..000000000000 Binary files a/pkg/policy/testdata/bundle.tar.gz and /dev/null differ diff --git a/pkg/policy/testdata/empty/policy/content/.manifest b/pkg/policy/testdata/empty/policy/content/.manifest deleted file mode 100644 index 68cfbeb68d24..000000000000 --- a/pkg/policy/testdata/empty/policy/content/.manifest +++ /dev/null @@ -1,3 +0,0 @@ -{ - "revision": "1" -} \ No newline at end of file diff --git a/pkg/policy/testdata/happy/policy/content/.manifest b/pkg/policy/testdata/happy/policy/content/.manifest deleted file mode 100644 index a5a92a163963..000000000000 --- a/pkg/policy/testdata/happy/policy/content/.manifest +++ /dev/null @@ -1,4 +0,0 @@ -{ - "revision": "1", - "roots": ["kubernetes", "docker"] - } \ No newline at end of file diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index 74c7c348f58d..9195ee75e306 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -7,7 +7,7 @@ import ( cn "github.com/google/go-containerregistry/pkg/name" version "github.com/knqyf263/go-rpm-version" - packageurl "github.com/package-url/packageurl-go" + "github.com/package-url/packageurl-go" "github.com/samber/lo" "golang.org/x/xerrors" @@ -42,6 +42,10 @@ const ( NamespaceOCP = "ocp" TypeUnknown = "unknown" + + // Temporary type before being added in github.com/package-url/packageurl-go + // cf. https://github.com/package-url/purl-spec/issues/454 + packageurlTypeBottlerocket = "bottlerocket" ) type PackageURL packageurl.PackageURL @@ -80,6 +84,8 @@ func New(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) (*Pac if metadata.OS != nil { namespace = string(metadata.OS.Family) } + case packageurlTypeBottlerocket: + qualifiers = append(qualifiers, packageurl.Qualifiers{packageurl.Qualifier{Key: "distro", Value: fmt.Sprintf("bottlerocket-%s", metadata.OS.Name)}}...) case packageurl.TypeApk: var qs packageurl.Qualifiers name, namespace, qs = parseApk(name, metadata.OS) @@ -443,6 +449,7 @@ func parseJulia(pkgName, pkgUUID string) (string, string, packageurl.Qualifiers) return namespace, name, qualifiers } +// nolint: gocyclo func purlType(t ftypes.TargetType) string { switch t { case ftypes.Jar, ftypes.Pom, ftypes.Gradle, ftypes.Sbt: @@ -482,6 +489,10 @@ func purlType(t ftypes.TargetType) string { ftypes.OpenSUSELeap, ftypes.OpenSUSETumbleweed, ftypes.SLES, ftypes.SLEMicro, ftypes.Photon, ftypes.Azure, ftypes.CBLMariner: return packageurl.TypeRPM + case ftypes.PythonExecutable, ftypes.PhpExecutable, ftypes.NodeJsExecutable, ftypes.JavaExecutable: + return packageurl.TypeGeneric + case ftypes.Bottlerocket: + return packageurlTypeBottlerocket case TypeOCI: return packageurl.TypeOCI case ftypes.Julia: diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index 5607551da77c..e0fc6900d4f7 100644 --- a/pkg/purl/purl_test.go +++ b/pkg/purl/purl_test.go @@ -440,6 +440,42 @@ func TestNewPackageURL(t *testing.T) { }, }, }, + { + name: "bottlerocket package", + typ: ftypes.Bottlerocket, + metadata: types.Metadata{ + OS: &ftypes.OS{ + Family: ftypes.Bottlerocket, + Name: "1.34.0", + }, + }, + pkg: ftypes.Package{ + ID: "glibc@2.40", + Name: "glibc", + Version: "2.40", + Epoch: 1, + Arch: "x86_64", + }, + want: &purl.PackageURL{ + Type: "bottlerocket", + Name: "glibc", + Version: "2.40", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "x86_64", + }, + { + Key: "epoch", + Value: "1", + }, + { + Key: "distro", + Value: "bottlerocket-1.34.0", + }, + }, + }, + }, } for _, tc := range testCases { @@ -710,6 +746,47 @@ func TestPackageURL_Package(t *testing.T) { }, }, }, + { + name: "bottlerocket with epoch", + pkgURL: &purl.PackageURL{ + Type: "bottlerocket", + Name: "glibc", + Version: "2.40", + Qualifiers: packageurl.Qualifiers{ + { + Key: "epoch", + Value: "1", + }, + { + Key: "distro", + Value: "bottlerocket-1.34.0", + }, + }, + }, + wantPkg: &ftypes.Package{ + ID: "glibc@2.40", + Name: "glibc", + Version: "2.40", + Epoch: 1, + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: "bottlerocket", + Name: "glibc", + Version: "2.40", + Qualifiers: packageurl.Qualifiers{ + { + Key: "epoch", + Value: "1", + }, + { + Key: "distro", + Value: "bottlerocket-1.34.0", + }, + }, + }, + }, + }, + }, { name: "wrong epoch", pkgURL: &purl.PackageURL{ diff --git a/pkg/sbom/core/bom.go b/pkg/sbom/core/bom.go index 4f8cec545e02..875de5dac850 100644 --- a/pkg/sbom/core/bom.go +++ b/pkg/sbom/core/bom.go @@ -44,6 +44,12 @@ const ( PropertyLayerDigest = "LayerDigest" PropertyLayerDiffID = "LayerDiffID" + // Red Hat packages only. + // A package can use multiple NVR + Arch content sets or fields. + PropertyContentSet = "ContentSet" + PropertyNVR = "NVR" + PropertyArch = "Arch" + // Relationships RelationshipDescribes RelationshipType = "describes" RelationshipContains RelationshipType = "contains" diff --git a/pkg/sbom/io/decode.go b/pkg/sbom/io/decode.go index 4920f08b7323..d90d59d49a51 100644 --- a/pkg/sbom/io/decode.go +++ b/pkg/sbom/io/decode.go @@ -201,30 +201,8 @@ func (m *Decoder) decodePackage(ctx context.Context, c *core.Component) (*ftypes pkg.Name = m.pkgName(pkg, c) pkg.ID = dependency.ID(p.LangType(), pkg.Name, p.Version) // Re-generate ID with the updated name - var err error - for _, prop := range c.Properties { - switch prop.Name { - case core.PropertyPkgID: - pkg.ID = prop.Value - case core.PropertyFilePath: - pkg.FilePath = prop.Value - case core.PropertySrcName: - pkg.SrcName = prop.Value - case core.PropertySrcVersion: - pkg.SrcVersion = prop.Value - case core.PropertySrcRelease: - pkg.SrcRelease = prop.Value - case core.PropertySrcEpoch: - if pkg.SrcEpoch, err = strconv.Atoi(prop.Value); err != nil { - return nil, xerrors.Errorf("invalid src epoch: %w", err) - } - case core.PropertyModularitylabel: - pkg.Modularitylabel = prop.Value - case core.PropertyLayerDigest: - pkg.Layer.Digest = prop.Value - case core.PropertyLayerDiffID: - pkg.Layer.DiffID = prop.Value - } + if err := fillPkgFieldsFromComponentProps(c.Properties, pkg); err != nil { + return nil, xerrors.Errorf("failed to fill package properties: %w", err) } pkg.Identifier.BOMRef = c.PkgIdentifier.BOMRef @@ -412,3 +390,43 @@ func (m *Decoder) addOrphanPkgs(ctx context.Context, sbom *types.SBOM) error { } return nil } + +func fillPkgFieldsFromComponentProps(props []core.Property, pkg *ftypes.Package) error { + buildInfo := &ftypes.BuildInfo{} + var err error + for _, prop := range props { + switch prop.Name { + case core.PropertyPkgID: + pkg.ID = prop.Value + case core.PropertyFilePath: + pkg.FilePath = prop.Value + case core.PropertySrcName: + pkg.SrcName = prop.Value + case core.PropertySrcVersion: + pkg.SrcVersion = prop.Value + case core.PropertySrcRelease: + pkg.SrcRelease = prop.Value + case core.PropertySrcEpoch: + if pkg.SrcEpoch, err = strconv.Atoi(prop.Value); err != nil { + return xerrors.Errorf("invalid src epoch: %w", err) + } + case core.PropertyModularitylabel: + pkg.Modularitylabel = prop.Value + case core.PropertyLayerDigest: + pkg.Layer.Digest = prop.Value + case core.PropertyLayerDiffID: + pkg.Layer.DiffID = prop.Value + case core.PropertyContentSet: + buildInfo.ContentSets = append(buildInfo.ContentSets, prop.Value) + case core.PropertyNVR: + buildInfo.Nvr = prop.Value + case core.PropertyArch: + buildInfo.Arch = prop.Value + } + } + + if len(buildInfo.ContentSets) > 0 || buildInfo.Nvr != "" { + pkg.BuildInfo = buildInfo + } + return nil +} diff --git a/pkg/sbom/io/encode.go b/pkg/sbom/io/encode.go index 5c0f59afbb20..b3cdb51340fb 100644 --- a/pkg/sbom/io/encode.go +++ b/pkg/sbom/io/encode.go @@ -363,6 +363,29 @@ func (*Encoder) component(result types.Result, pkg ftypes.Package) *core.Compone }, } + // Fill Red Hat specific properties + if pkg.BuildInfo != nil { + for _, cs := range pkg.BuildInfo.ContentSets { + properties = append(properties, core.Property{ + Name: core.PropertyContentSet, + Value: cs, + }) + } + + if pkg.BuildInfo.Nvr != "" { + properties = append(properties, core.Property{ + Name: core.PropertyNVR, + Value: pkg.BuildInfo.Nvr, + }) + } + if pkg.BuildInfo.Arch != "" { + properties = append(properties, core.Property{ + Name: core.PropertyArch, + Value: pkg.BuildInfo.Arch, + }) + } + } + var files []core.File if pkg.FilePath != "" || pkg.Digest != "" { files = append(files, core.File{ diff --git a/pkg/sbom/io/encode_test.go b/pkg/sbom/io/encode_test.go index ad555e3d6df2..018494205059 100644 --- a/pkg/sbom/io/encode_test.go +++ b/pkg/sbom/io/encode_test.go @@ -590,6 +590,207 @@ func TestEncoder_Encode(t *testing.T) { }, }, }, + { + name: "Red Hat container image", + report: types.Report{ + SchemaVersion: 2, + ArtifactName: "redhat/ubi9-minimal", + ArtifactType: ftypes.TypeContainerImage, + Metadata: types.Metadata{ + OS: &ftypes.OS{ + Family: ftypes.RedHat, + Name: "9.5", + }, + RepoTags: []string{ + "redhat/ubi9:latest", + }, + RepoDigests: []string{ + "redhat/ubi9-minimal@sha256:e1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c", + }, + ImageConfig: v1.ConfigFile{ + Config: v1.Config{ + Labels: map[string]string{ + "vendor": "aquasecurity", + }, + }, + }, + }, + Results: []types.Result{ + { + Target: "redhat/ubi9-minimal (redhat 9.5)", + Type: ftypes.RedHat, + Class: types.ClassOSPkg, + Packages: []ftypes.Package{ + { + ID: "glibc@2.34-125.el9_5.8.aarch64", + Name: "glibc", + Version: "2.34", + Release: "125.el9_5.8.aarch64", + Arch: "aarch64", + SrcName: "glibc", + SrcVersion: "2.34", + SrcRelease: "125.el9_5.8", + Maintainer: "Red Hat, Inc.", + BuildInfo: &ftypes.BuildInfo{ + ContentSets: []string{ + "rhel-9-for-aarch64-appstream-rpms", + "rhel-9-for-aarch64-appstream-source-rpms", + }, + }, + Identifier: ftypes.PkgIdentifier{ + UID: "2acbd589f06ebbb8", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Name: "glibc", + Version: "2.34-125.el9_5.8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "redhat-9.5", + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantComponents: map[uuid.UUID]*core.Component{ + uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000001"): { + Type: core.TypeContainerImage, + Name: "redhat/ubi9-minimal", + Root: true, + PkgIdentifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeOCI, + Name: "ubi9-minimal", + Version: "sha256:e1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c", + Qualifiers: packageurl.Qualifiers{ + { + Key: "repository_url", + Value: "index.docker.io/redhat/ubi9-minimal", + }, + }, + }, + BOMRef: "pkg:oci/ubi9-minimal@sha256%3Ae1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c?repository_url=index.docker.io%2Fredhat%2Fubi9-minimal", + }, + Properties: []core.Property{ + { + Name: "Labels:vendor", + Value: "aquasecurity", + }, + { + Name: core.PropertyRepoDigest, + Value: "redhat/ubi9-minimal@sha256:e1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c", + }, + { + Name: core.PropertyRepoTag, + Value: "redhat/ubi9:latest", + }, + { + Name: core.PropertySchemaVersion, + Value: "2", + }, + }, + }, + uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"): { + Type: core.TypeOS, + Name: "redhat", + Version: "9.5", + Properties: []core.Property{ + { + Name: core.PropertyClass, + Value: "os-pkgs", + }, + { + Name: core.PropertyType, + Value: "redhat", + }, + }, + PkgIdentifier: ftypes.PkgIdentifier{ + BOMRef: "3ff14136-e09f-4df9-80ea-000000000002", + }, + }, + uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"): { + Type: core.TypeLibrary, + Name: "glibc", + Version: "2.34-125.el9_5.8", + SrcName: "glibc", + SrcVersion: "2.34-125.el9_5.8", + Supplier: "Red Hat, Inc.", + Properties: []core.Property{ + { + Name: core.PropertyContentSet, + Value: "rhel-9-for-aarch64-appstream-rpms", + }, + { + Name: core.PropertyContentSet, + Value: "rhel-9-for-aarch64-appstream-source-rpms", + }, + { + Name: core.PropertyPkgID, + Value: "glibc@2.34-125.el9_5.8.aarch64", + }, + { + Name: core.PropertyPkgType, + Value: "redhat", + }, + { + Name: core.PropertySrcName, + Value: "glibc", + }, + { + Name: core.PropertySrcRelease, + Value: "125.el9_5.8", + }, + { + Name: core.PropertySrcVersion, + Value: "2.34", + }, + }, + PkgIdentifier: ftypes.PkgIdentifier{ + UID: "2acbd589f06ebbb8", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Name: "glibc", + Version: "2.34-125.el9_5.8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "redhat-9.5", + }, + }, + }, + BOMRef: "pkg:rpm/glibc@2.34-125.el9_5.8?arch=aarch64&distro=redhat-9.5", + }, + }, + }, + wantRels: map[uuid.UUID][]core.Relationship{ + uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000001"): { + { + Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"), + Type: core.RelationshipContains, + }, + }, + uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"): { + { + Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"), + Type: core.RelationshipContains, + }, + }, + uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"): nil, + }, + wantVulns: make(map[uuid.UUID][]core.Vulnerability), + }, { name: "root package", report: types.Report{ diff --git a/pkg/scanner/scan.go b/pkg/scanner/scan.go index 4009ddba8a4d..466179d1c0c8 100644 --- a/pkg/scanner/scan.go +++ b/pkg/scanner/scan.go @@ -2,135 +2,17 @@ package scanner import ( "context" + "errors" - "github.com/google/wire" "golang.org/x/xerrors" - "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/fanal/artifact" - aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image" - flocal "github.com/aquasecurity/trivy/pkg/fanal/artifact/local" - "github.com/aquasecurity/trivy/pkg/fanal/artifact/repo" - "github.com/aquasecurity/trivy/pkg/fanal/artifact/sbom" - "github.com/aquasecurity/trivy/pkg/fanal/artifact/vm" - "github.com/aquasecurity/trivy/pkg/fanal/image" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/report" - "github.com/aquasecurity/trivy/pkg/rpc/client" - "github.com/aquasecurity/trivy/pkg/scanner/local" "github.com/aquasecurity/trivy/pkg/types" ) -/////////////// -// Standalone -/////////////// - -// StandaloneSuperSet is used in the standalone mode -var StandaloneSuperSet = wire.NewSet( - // Cache - cache.New, - wire.Bind(new(cache.ArtifactCache), new(cache.Cache)), - wire.Bind(new(cache.LocalArtifactCache), new(cache.Cache)), - - local.SuperSet, - wire.Bind(new(Driver), new(local.Scanner)), - NewScanner, -) - -// StandaloneDockerSet binds docker dependencies -var StandaloneDockerSet = wire.NewSet( - image.NewContainerImage, - aimage.NewArtifact, - StandaloneSuperSet, -) - -// StandaloneArchiveSet binds archive scan dependencies -var StandaloneArchiveSet = wire.NewSet( - image.NewArchiveImage, - aimage.NewArtifact, - StandaloneSuperSet, -) - -// StandaloneFilesystemSet binds filesystem dependencies -var StandaloneFilesystemSet = wire.NewSet( - flocal.ArtifactSet, - StandaloneSuperSet, -) - -// StandaloneRepositorySet binds repository dependencies -var StandaloneRepositorySet = wire.NewSet( - repo.ArtifactSet, - StandaloneSuperSet, -) - -// StandaloneSBOMSet binds sbom dependencies -var StandaloneSBOMSet = wire.NewSet( - sbom.NewArtifact, - StandaloneSuperSet, -) - -// StandaloneVMSet binds vm dependencies -var StandaloneVMSet = wire.NewSet( - vm.ArtifactSet, - StandaloneSuperSet, -) - -///////////////// -// Client/Server -///////////////// - -// RemoteSuperSet is used in the client mode -var RemoteSuperSet = wire.NewSet( - // Cache - cache.NewRemoteCache, - wire.Bind(new(cache.ArtifactCache), new(*cache.RemoteCache)), // No need for LocalArtifactCache - - client.NewScanner, - wire.Value([]client.Option(nil)), - wire.Bind(new(Driver), new(client.Scanner)), - NewScanner, -) - -// RemoteFilesystemSet binds filesystem dependencies for client/server mode -var RemoteFilesystemSet = wire.NewSet( - flocal.ArtifactSet, - RemoteSuperSet, -) - -// RemoteRepositorySet binds repository dependencies for client/server mode -var RemoteRepositorySet = wire.NewSet( - repo.ArtifactSet, - RemoteSuperSet, -) - -// RemoteSBOMSet binds sbom dependencies for client/server mode -var RemoteSBOMSet = wire.NewSet( - sbom.NewArtifact, - RemoteSuperSet, -) - -// RemoteVMSet binds vm dependencies for client/server mode -var RemoteVMSet = wire.NewSet( - vm.ArtifactSet, - RemoteSuperSet, -) - -// RemoteDockerSet binds remote docker dependencies -var RemoteDockerSet = wire.NewSet( - aimage.NewArtifact, - image.NewContainerImage, - RemoteSuperSet, -) - -// RemoteArchiveSet binds remote archive dependencies -var RemoteArchiveSet = wire.NewSet( - aimage.NewArtifact, - image.NewArchiveImage, - RemoteSuperSet, -) - // Scanner implements the Artifact and Driver operations type Scanner struct { driver Driver @@ -154,9 +36,10 @@ func NewScanner(driver Driver, ar artifact.Artifact) Scanner { // ScanArtifact scans the artifacts and returns results func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (types.Report, error) { artifactInfo, err := s.artifact.Inspect(ctx) - if err != nil { + if err != nil && !errors.Is(err, context.DeadlineExceeded) { return types.Report{}, xerrors.Errorf("failed analysis: %w", err) } + err1 := err defer func() { if err := s.artifact.Clean(artifactInfo); err != nil { log.Warn("Failed to clean the artifact", @@ -179,7 +62,7 @@ func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (t } return types.Report{ - SchemaVersion: report.SchemaVersion, + SchemaVersion: 2, // github.com/aquasecurity/trivy/pkg/report.SchemaVersion but without importing the whole report package CreatedAt: clock.Now(ctx), ArtifactName: artifactInfo.Name, ArtifactType: artifactInfo.Type, @@ -195,5 +78,5 @@ func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (t }, Results: results, BOM: artifactInfo.BOM, - }, nil + }, err1 } diff --git a/pkg/semaphore/semaphore.go b/pkg/semaphore/semaphore.go index 52005ab43e79..4f5a1e7459af 100644 --- a/pkg/semaphore/semaphore.go +++ b/pkg/semaphore/semaphore.go @@ -4,7 +4,9 @@ import "golang.org/x/sync/semaphore" const defaultSize = 5 -func New(parallel int) *semaphore.Weighted { +type Weighted = semaphore.Weighted + +func New(parallel int) *Weighted { if parallel == 0 { parallel = defaultSize } diff --git a/pkg/utils/fsutils/fs.go b/pkg/utils/fsutils/fs.go index b2aea51dda0a..4d0e5b0dfae3 100644 --- a/pkg/utils/fsutils/fs.go +++ b/pkg/utils/fsutils/fs.go @@ -1,6 +1,8 @@ package fsutils import ( + "context" + "errors" "fmt" "io" "io/fs" @@ -67,21 +69,49 @@ func FileExists(filename string) bool { return err == nil && !f.IsDir() } +func DefaultWalkErrorCallback(_ string, err error) error { + if errors.Is(err, fs.ErrPermission) { + return nil + } + return err +} + type WalkDirRequiredFunc func(path string, d fs.DirEntry) bool type WalkDirFunc func(path string, d fs.DirEntry, r io.Reader) error -func WalkDir(fsys fs.FS, root string, required WalkDirRequiredFunc, fn WalkDirFunc) error { +type WalkDirErrorCallback func(path string, err error) error + +func WalkDir(ctx context.Context, fsys fs.FS, root string, required WalkDirRequiredFunc, errCallback WalkDirErrorCallback, fn WalkDirFunc) error { return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { if err != nil { + if os.IsPermission(err) { + return nil + } + if errCallback != nil { + return errCallback(path, err) + } return err } else if !d.Type().IsRegular() || !required(path, d) { return nil } + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + f, err := fsys.Open(path) if err != nil { - return xerrors.Errorf("file open error: %w", err) + if os.IsPermission(err) { + return nil + } + errOpen := fmt.Errorf("file open error: %w", err) + if errCallback != nil { + return errCallback(path, errOpen) + } + return errOpen } defer f.Close() @@ -103,3 +133,9 @@ func RequiredFile(fileNames ...string) WalkDirRequiredFunc { return slices.Contains(fileNames, filepath.Base(filePath)) } } + +func RequiredAll() WalkDirRequiredFunc { + return func(_ string, _ fs.DirEntry) bool { + return true + } +} diff --git a/pkg/vex/repo/repo.go b/pkg/vex/repo/repo.go index b9637c229ee9..c33059577bb1 100644 --- a/pkg/vex/repo/repo.go +++ b/pkg/vex/repo/repo.go @@ -10,7 +10,6 @@ import ( "path/filepath" "time" - "github.com/hashicorp/go-getter" "github.com/hashicorp/go-multierror" "github.com/samber/lo" "golang.org/x/xerrors" @@ -170,7 +169,6 @@ func (r *Repository) downloadManifest(ctx context.Context, opts Options) error { Password: r.Password, Token: r.Token, }, - ClientMode: getter.ClientModeFile, }) if err != nil { _ = os.RemoveAll(r.dir)