@intentius/chant-lexicon-terraform 0.124.31 → 0.124.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__fixtures__/terragrunt/affected-cases.js +111 -0
- package/dist/__fixtures__/terragrunt/record.js +57 -0
- package/dist/behaviour/index.js +6 -0
- package/dist/behaviour/kinds.js +194 -0
- package/dist/behaviour/predict.js +297 -0
- package/dist/behaviour/reference-catalog.js +178 -0
- package/dist/behaviour/request.js +482 -0
- package/dist/change-set.js +234 -0
- package/dist/changed-roots.js +97 -0
- package/dist/codegen/docs-cli.js +3 -0
- package/dist/codegen/docs.js +44 -0
- package/dist/codegen/generate-cli.js +9 -0
- package/dist/codegen/generate.js +59 -0
- package/dist/codegen/package.js +43 -0
- package/dist/commands.js +106 -0
- package/dist/components/capability-plugin.d.ts +38 -2
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +260 -0
- package/dist/composites/terraform-adopt-op.js +152 -0
- package/dist/composites/terraform-apply-op.js +183 -0
- package/dist/composites/terraform-pin-rollout-op.js +45 -0
- package/dist/composites/terraform-watch-op.js +219 -0
- package/dist/config.js +118 -0
- package/dist/describe-resources.js +984 -0
- package/dist/generated/index.js +1 -0
- package/dist/hcl/descend.js +208 -0
- package/dist/hcl/edges.js +287 -0
- package/dist/hcl/jsonencode.js +442 -0
- package/dist/hcl/marker.js +212 -0
- package/dist/hcl/parse.js +372 -0
- package/dist/hcl/references.js +156 -0
- package/dist/hcl/roots.js +98 -0
- package/dist/hcl/suppressions.js +189 -0
- package/dist/hcl/value.js +72 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -0
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.js +501 -0
- package/dist/lint/audit-lineage.js +442 -0
- package/dist/lint/post-synth/blocks.js +87 -0
- package/dist/lint/post-synth/fixtures/load.js +94 -0
- package/dist/lint/post-synth/flat-attributes.js +135 -0
- package/dist/lint/post-synth/index.js +90 -0
- package/dist/lint/post-synth/module-source.js +153 -0
- package/dist/lint/post-synth/scope.js +104 -0
- package/dist/lint/post-synth/terragrunt.js +63 -0
- package/dist/lint/post-synth/tf001.js +138 -0
- package/dist/lint/post-synth/tf002.js +143 -0
- package/dist/lint/post-synth/tf003.js +60 -0
- package/dist/lint/post-synth/tf004.js +54 -0
- package/dist/lint/post-synth/tf005.js +68 -0
- package/dist/lint/post-synth/tf006.js +43 -0
- package/dist/lint/post-synth/tf007.js +66 -0
- package/dist/lint/post-synth/tf008.js +64 -0
- package/dist/lint/post-synth/tf009.js +49 -0
- package/dist/lint/post-synth/tf010.js +40 -0
- package/dist/lint/post-synth/tf011.js +38 -0
- package/dist/lint/post-synth/tf012.js +36 -0
- package/dist/lint/post-synth/tf013.js +53 -0
- package/dist/lint/post-synth/tf014.js +61 -0
- package/dist/lint/post-synth/tf015.js +62 -0
- package/dist/lint/post-synth/tf016.js +66 -0
- package/dist/lint/post-synth/tf017.js +39 -0
- package/dist/lint/post-synth/tf018.js +63 -0
- package/dist/lint/post-synth/tf019.js +60 -0
- package/dist/lint/post-synth/tf020.js +103 -0
- package/dist/lint/post-synth/tf021.js +117 -0
- package/dist/lint/post-synth/tf022.js +51 -0
- package/dist/lint/post-synth/tf024.js +62 -0
- package/dist/lint/post-synth/tf025.js +83 -0
- package/dist/lint/post-synth/tf026.js +74 -0
- package/dist/lint/post-synth/tf027.js +66 -0
- package/dist/lint/post-synth/tf028.js +83 -0
- package/dist/lint/post-synth/tf029.js +73 -0
- package/dist/lint/post-synth/tf030.js +253 -0
- package/dist/lint/post-synth/tf031.js +332 -0
- package/dist/lint/post-synth/tf032.js +201 -0
- package/dist/lint/post-synth/tf033.js +126 -0
- package/dist/lint/post-synth/tf034.js +45 -0
- package/dist/lint/post-synth/tf035.js +63 -0
- package/dist/lint/post-synth/tf036.js +97 -0
- package/dist/lint/post-synth/tf037.js +80 -0
- package/dist/lint/post-synth/tf038.js +63 -0
- package/dist/lint/post-synth/tf039.js +55 -0
- package/dist/lint/post-synth/tf040.js +67 -0
- package/dist/lint/post-synth/tf041.js +53 -0
- package/dist/lint/post-synth/tf042.js +38 -0
- package/dist/lint/post-synth/tf043.js +45 -0
- package/dist/lint/post-synth/tf044.js +45 -0
- package/dist/lint/post-synth/tf045.js +44 -0
- package/dist/lint/rules/fixtures/load.js +50 -0
- package/dist/lint/rules/index.js +4 -0
- package/dist/lint/rules/plan-before-apply.js +136 -0
- package/dist/lint/secret-shape.js +149 -0
- package/dist/live-ownership.js +57 -0
- package/dist/lsp/completions.js +95 -0
- package/dist/lsp/context.js +358 -0
- package/dist/lsp/hover.js +45 -0
- package/dist/lsp/option-keys.js +93 -0
- package/dist/manifest.json +1 -1
- package/dist/op/activities/index.js +30 -0
- package/dist/op/activities/live-detect.js +143 -0
- package/dist/op/activities/pin-rollout.js +45 -0
- package/dist/op/activities/terraform.js +1270 -0
- package/dist/op/activity-contracts.js +162 -0
- package/dist/op/adoption.js +272 -0
- package/dist/op/builders.js +125 -0
- package/dist/op/resolve-root-mode.js +77 -0
- package/dist/package-cli.js +16 -0
- package/dist/pin/edit-ts.js +76 -0
- package/dist/pin/edit.js +118 -0
- package/dist/pin/forge.js +65 -0
- package/dist/pin/index.js +16 -0
- package/dist/pin/rollout.d.ts.map +1 -1
- package/dist/pin/rollout.js +382 -0
- package/dist/pin/scan.js +287 -0
- package/dist/pin/source.js +171 -0
- package/dist/pin/waves.js +109 -0
- package/dist/plan-digest.js +67 -0
- package/dist/plugin.js +323 -0
- package/dist/serializer.js +23 -0
- package/dist/state-ownership.js +29 -0
- package/dist/terragrunt/affected.d.ts.map +1 -1
- package/dist/terragrunt/affected.js +290 -0
- package/dist/terragrunt/index.js +10 -0
- package/dist/terragrunt/mocks.js +270 -0
- package/dist/terragrunt/run.js +264 -0
- package/dist/terragrunt/units.js +195 -0
- package/dist/terragrunt/wave.js +125 -0
- package/dist/validate-cli.js +4 -0
- package/dist/validate.js +31 -0
- package/package.json +10 -10
- package/src/codegen/package.ts +2 -2
- package/src/components/capability-plugin.test.ts +97 -2
- package/src/components/capability-plugin.ts +117 -12
- package/src/components/pr-loop.tofu.test.ts +51 -11
- package/src/index.ts +1 -0
- package/src/pin/rollout.ts +2 -1
- package/src/terragrunt/affected.test.ts +11 -0
- package/src/terragrunt/affected.ts +2 -1
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
const TFSEC_V061 = "https://github.com/aquasecurity/tfsec/blob/v0.61.3/docs/checks/general/secrets";
|
|
2
|
+
/** tflint's official language ruleset. */
|
|
3
|
+
const TFLINT_RULES = "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules";
|
|
4
|
+
/** The community ruleset with the richest set of language rules outside the official one. */
|
|
5
|
+
const REDEPLOY_RULES = "https://github.com/RedeployAB/tflint-ruleset-redeploy/blob/main/docs/rules";
|
|
6
|
+
export const terraformAuditLineage = {
|
|
7
|
+
TF027: [
|
|
8
|
+
{
|
|
9
|
+
tool: "choudoufu",
|
|
10
|
+
rule: "RulePolicyScope",
|
|
11
|
+
url: "https://github.com/INTENTIUS/choudoufu/blob/main/internal/live/lint/policy.go",
|
|
12
|
+
relation: "extends",
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
TF024: [
|
|
16
|
+
{
|
|
17
|
+
tool: "choudoufu",
|
|
18
|
+
rule: "RuleStateBackend",
|
|
19
|
+
url: "https://github.com/INTENTIUS/choudoufu/blob/main/internal/live/lint/issue.go",
|
|
20
|
+
relation: "equivalent",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
TF002: [
|
|
24
|
+
{
|
|
25
|
+
tool: "tflint-ruleset-terraform",
|
|
26
|
+
rule: "terraform_required_providers",
|
|
27
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_required_providers.md",
|
|
28
|
+
relation: "equivalent",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
tool: "terraform-sentinel-policies",
|
|
32
|
+
rule: "require-all-providers-have-version-constraint",
|
|
33
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/tree/main/cloud-agnostic",
|
|
34
|
+
relation: "equivalent",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
tool: "aws-terraform-prescriptive-guidance",
|
|
38
|
+
rule: "Add automated version checks",
|
|
39
|
+
url: "https://docs.aws.amazon.com/prescriptive-guidance/latest/terraform-aws-provider-best-practices/version.html#add-automated-version-checks",
|
|
40
|
+
relation: "equivalent",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
// Sentinel's restrict-terraform-versions checks that required_version is
|
|
44
|
+
// within an allowed range, not that it is present at all: a narrower
|
|
45
|
+
// question than TF003's, so "overlaps" rather than "equivalent" (#2109).
|
|
46
|
+
TF003: [
|
|
47
|
+
{
|
|
48
|
+
tool: "tflint-ruleset-terraform",
|
|
49
|
+
rule: "terraform_required_version",
|
|
50
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_required_version.md",
|
|
51
|
+
relation: "equivalent",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
tool: "terraform-sentinel-policies",
|
|
55
|
+
rule: "restrict-terraform-versions",
|
|
56
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/tree/main/cloud-agnostic",
|
|
57
|
+
relation: "overlaps",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
tool: "hashicorp-style-guide",
|
|
61
|
+
rule: "Version pinning",
|
|
62
|
+
url: "https://developer.hashicorp.com/terraform/language/style#version-pinning",
|
|
63
|
+
relation: "overlaps",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
TF004: [
|
|
67
|
+
{
|
|
68
|
+
tool: "tflint-ruleset-terraform",
|
|
69
|
+
rule: "terraform_module_version",
|
|
70
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_module_version.md",
|
|
71
|
+
relation: "equivalent",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
tool: "checkov",
|
|
75
|
+
rule: "CKV_TF_2",
|
|
76
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/module/generic/RevisionVersionTag.py",
|
|
77
|
+
relation: "equivalent",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
// checkov's CKV_TF_1 accepts any `?ref=`/`&ref=` matching `[?&](ref=).*(\d\.\d).*`,
|
|
81
|
+
// which a branch literally named `v1.2-dev` passes; TF005 requires a full
|
|
82
|
+
// semver tag or a 40-hex SHA, so "extends" (a strict superset), not
|
|
83
|
+
// "equivalent". KICS's query only matches the `git::` prefix, not the
|
|
84
|
+
// github.com/bitbucket.org shorthands or scp-style sources TF005 also
|
|
85
|
+
// covers, so "overlaps" (#2109).
|
|
86
|
+
TF005: [
|
|
87
|
+
{
|
|
88
|
+
tool: "tflint-ruleset-terraform",
|
|
89
|
+
rule: "terraform_module_pinned_source",
|
|
90
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_module_pinned_source.md",
|
|
91
|
+
relation: "equivalent",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
tool: "checkov",
|
|
95
|
+
rule: "CKV_TF_1",
|
|
96
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/module/generic/RevisionHash.py",
|
|
97
|
+
relation: "extends",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
tool: "kics",
|
|
101
|
+
rule: "3a81fc06-566f-492a-91dd-7448e409e2cd",
|
|
102
|
+
url: "https://docs.kics.io/latest/queries/terraform-queries/3a81fc06-566f-492a-91dd-7448e409e2cd/",
|
|
103
|
+
relation: "overlaps",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
TF006: [
|
|
107
|
+
{
|
|
108
|
+
tool: "tflint-ruleset-avm",
|
|
109
|
+
rule: "avm_terraform_sensitive_variable_default_disallowed",
|
|
110
|
+
url: "https://github.com/Azure/tflint-ruleset-avm/blob/main/docs/basic/avm_terraform_sensitive_variable_default_disallowed.md",
|
|
111
|
+
relation: "equivalent",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
TF007: [
|
|
115
|
+
{ tool: "tfsec", rule: "general-secrets-sensitive-in-variable", url: `${TFSEC_V061}/sensitive-in-variable.md`, relation: "equivalent" },
|
|
116
|
+
{ tool: "tfsec", rule: "general-secrets-sensitive-in-local", url: `${TFSEC_V061}/sensitive-in-local.md`, relation: "equivalent" },
|
|
117
|
+
],
|
|
118
|
+
// #2107's rule table credits TF008 with Snyk `SNYK-CC-TF-74` ("Credentials
|
|
119
|
+
// are configured via provider attributes") alongside these three. That
|
|
120
|
+
// credit is deliberately absent here, and the absence is the epic's own
|
|
121
|
+
// decision rather than an oversight: Snyk IaC is a product, not a rule
|
|
122
|
+
// source, which is why it sits in this file's "deliberately excluded" list
|
|
123
|
+
// above and has no `PRIOR_ART` entry to cite. The rules have no open
|
|
124
|
+
// repository behind them, and the public index (security.snyk.io/rules/
|
|
125
|
+
// cloud) does not even enumerate the legacy `SNYK-CC-TF-*` ids, so a
|
|
126
|
+
// credit here could not be swept the way every other one in this file is
|
|
127
|
+
// (scripts/prior-art-sweep.ts). The condition loses nothing by it:
|
|
128
|
+
// `SNYK-CC-TF-74`, checkov's `CKV_AWS_41` and semgrep's
|
|
129
|
+
// `aws-provider-static-credentials` are the same check three times over,
|
|
130
|
+
// and the other two are credited below (#2220).
|
|
131
|
+
TF008: [
|
|
132
|
+
{ tool: "checkov", rule: "CKV_AWS_41", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
|
|
133
|
+
{ tool: "kics", rule: "d7b9d850-3e06-4a75-852f-c46c2e92240b", url: "https://docs.kics.io/latest/queries/terraform-queries/aws/d7b9d850-3e06-4a75-852f-c46c2e92240b/", relation: "overlaps" },
|
|
134
|
+
{
|
|
135
|
+
tool: "semgrep",
|
|
136
|
+
rule: "terraform.aws.security.aws-provider-static-credentials",
|
|
137
|
+
url: "https://github.com/semgrep/semgrep-rules/blob/develop/terraform/aws/security/aws-provider-static-credentials.yaml",
|
|
138
|
+
relation: "overlaps",
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
TF009: [
|
|
142
|
+
{
|
|
143
|
+
tool: "hashicorp-style-guide",
|
|
144
|
+
rule: "Variables: for sensitive variables, such as passwords and private keys, set the sensitive parameter to true",
|
|
145
|
+
url: "https://developer.hashicorp.com/terraform/language/style#variables",
|
|
146
|
+
relation: "equivalent",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
TF010: [
|
|
150
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_typed_variables", url: `${TFLINT_RULES}/terraform_typed_variables.md`, relation: "equivalent" },
|
|
151
|
+
{ tool: "kics", rule: "fc5109bf-01fd-49fb-8bde-4492b543c34a", url: "https://docs.kics.io/latest/queries/terraform-queries/fc5109bf-01fd-49fb-8bde-4492b543c34a/", relation: "equivalent" },
|
|
152
|
+
],
|
|
153
|
+
TF011: [
|
|
154
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_documented_variables", url: `${TFLINT_RULES}/terraform_documented_variables.md`, relation: "equivalent" },
|
|
155
|
+
{ tool: "kics", rule: "2a153952-2544-4687-bcc9-cc8fea814a9b", url: "https://docs.kics.io/latest/queries/terraform-queries/2a153952-2544-4687-bcc9-cc8fea814a9b/", relation: "equivalent" },
|
|
156
|
+
{
|
|
157
|
+
tool: "terraform-sentinel-policies",
|
|
158
|
+
rule: "validate-variables-have-descriptions",
|
|
159
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/blob/main/cloud-agnostic/validate-variables-have-descriptions.sentinel",
|
|
160
|
+
relation: "equivalent",
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
TF012: [
|
|
164
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_documented_outputs", url: `${TFLINT_RULES}/terraform_documented_outputs.md`, relation: "equivalent" },
|
|
165
|
+
{ tool: "kics", rule: "59312e8a-a64e-41e7-a252-618533dd1ea8", url: "https://docs.kics.io/latest/queries/terraform-queries/59312e8a-a64e-41e7-a252-618533dd1ea8/", relation: "equivalent" },
|
|
166
|
+
],
|
|
167
|
+
TF013: [
|
|
168
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_ignore_changes_all", url: `${REDEPLOY_RULES}/terraform_ignore_changes_all.md`, relation: "equivalent" },
|
|
169
|
+
],
|
|
170
|
+
// The two vendor documents below (and the one on TF015, and the HashiCorp
|
|
171
|
+
// style guide on TF021) are `Lineage` entries, not `RuleMeta.authority`
|
|
172
|
+
// ones, though #2107's table calls them "authority". `authority` is not a
|
|
173
|
+
// free-standing "this document says so" field: an authority citation forces
|
|
174
|
+
// `category: "security"` (`packages/core/src/audit/catalog.ts`), and
|
|
175
|
+
// `catalog.test.ts` binds the two together in both directions, "an
|
|
176
|
+
// authority citation always means security" and "authority citations only
|
|
177
|
+
// attach to merge-worthy entries". A provider block in a child module is a
|
|
178
|
+
// structural rule, not a security finding, so citing these guides as
|
|
179
|
+
// authority would have relabelled three rules as security to get a citation
|
|
180
|
+
// rendered. Lineage carries the same URLs and never touches tier or
|
|
181
|
+
// category, so that is where they live (#2112, recorded here by #2220).
|
|
182
|
+
TF014: [
|
|
183
|
+
{
|
|
184
|
+
tool: "tflint-ruleset-avm",
|
|
185
|
+
rule: "avm_terraform_provider_block_disallowed",
|
|
186
|
+
url: "https://github.com/Azure/tflint-ruleset-avm/blob/main/docs/basic/avm_terraform_provider_block_disallowed.md",
|
|
187
|
+
relation: "equivalent",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
tool: "gcp-terraform-best-practices",
|
|
191
|
+
rule: "Build reusable modules: modules must not configure providers",
|
|
192
|
+
url: "https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure#reusable-modules",
|
|
193
|
+
relation: "equivalent",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
tool: "aws-terraform-prescriptive-guidance",
|
|
197
|
+
rule: "Structure: declare provider configurations in the root module",
|
|
198
|
+
url: "https://docs.aws.amazon.com/prescriptive-guidance/latest/terraform-aws-provider-best-practices/structure.html",
|
|
199
|
+
relation: "equivalent",
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
// TF015 has no scanner to credit: no ruleset in the survey checks for a
|
|
203
|
+
// backend or cloud block inside a child module.
|
|
204
|
+
// tflint-ruleset-terraform's index has nothing about backends at all,
|
|
205
|
+
// Redeploy's has nothing either, and AVM's module rules stop at the
|
|
206
|
+
// provider block TF014 credits. What states the rule is Google Cloud's
|
|
207
|
+
// reusable-modules guide, so that document is the single credit here, and
|
|
208
|
+
// the page section says plainly that the credit is a document rather than
|
|
209
|
+
// a tool (#2112). It is lineage rather than `authority` for the reason on
|
|
210
|
+
// TF014 above: an authority citation would force `category: "security"` on
|
|
211
|
+
// a rule about where state configuration belongs.
|
|
212
|
+
TF015: [
|
|
213
|
+
{
|
|
214
|
+
tool: "gcp-terraform-best-practices",
|
|
215
|
+
rule: "Build reusable modules: state configuration belongs to the root module",
|
|
216
|
+
url: "https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure#reusable-modules",
|
|
217
|
+
relation: "equivalent",
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
TF016: [
|
|
221
|
+
// `overlaps`, not `equivalent`: tflint reports the same `x = "${y}"` form
|
|
222
|
+
// and also the object-key case (`"${var.k}" = v`), which this rule leaves
|
|
223
|
+
// alone. See tf016.ts.
|
|
224
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_deprecated_interpolation", url: `${TFLINT_RULES}/terraform_deprecated_interpolation.md`, relation: "overlaps" },
|
|
225
|
+
],
|
|
226
|
+
TF017: [
|
|
227
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_module_depends_on", url: `${REDEPLOY_RULES}/terraform_module_depends_on.md`, relation: "equivalent" },
|
|
228
|
+
],
|
|
229
|
+
TF018: [
|
|
230
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_output_resource", url: `${REDEPLOY_RULES}/terraform_output_resource.md`, relation: "equivalent" },
|
|
231
|
+
],
|
|
232
|
+
TF019: [
|
|
233
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_redundant_default", url: `${REDEPLOY_RULES}/terraform_redundant_default.md`, relation: "equivalent" },
|
|
234
|
+
],
|
|
235
|
+
TF020: [
|
|
236
|
+
// `overlaps`: tflint evaluates expressions and reports the same four
|
|
237
|
+
// kinds of declaration; chant's index is a string scan over hcl2json's
|
|
238
|
+
// preserved expression strings, so it is generous where tflint is exact
|
|
239
|
+
// (see hcl/references.ts and tf020.ts).
|
|
240
|
+
{
|
|
241
|
+
tool: "tflint-ruleset-terraform",
|
|
242
|
+
rule: "terraform_unused_declarations",
|
|
243
|
+
url: `${TFLINT_RULES}/terraform_unused_declarations.md`,
|
|
244
|
+
relation: "overlaps",
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
TF021: [
|
|
248
|
+
// Redeploy reports every `count` over a collection; TF021 reports only
|
|
249
|
+
// the ones that build an identity out of `count.index`, which is a
|
|
250
|
+
// strict subset, hence `overlaps` on both credits rather than
|
|
251
|
+
// `equivalent`. choudoufu's own rule is the survey's deepest reading of
|
|
252
|
+
// the identity-bearing question (#2112). The HashiCorp style guide entry
|
|
253
|
+
// is lineage rather than `authority` for the reason recorded on TF014: an
|
|
254
|
+
// authority citation forces `category: "security"`, and `count` versus
|
|
255
|
+
// `for_each` is not a security finding.
|
|
256
|
+
{
|
|
257
|
+
tool: "tflint-ruleset-redeploy",
|
|
258
|
+
rule: "terraform_prefer_for_each",
|
|
259
|
+
url: `${REDEPLOY_RULES}/terraform_prefer_for_each.md`,
|
|
260
|
+
relation: "overlaps",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
tool: "choudoufu",
|
|
264
|
+
rule: "RuleCountIndex",
|
|
265
|
+
url: "https://github.com/INTENTIUS/choudoufu/blob/main/internal/live/lint/count_index.go",
|
|
266
|
+
relation: "overlaps",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
tool: "hashicorp-style-guide",
|
|
270
|
+
rule: "Resources: use for_each for a dynamic resource count",
|
|
271
|
+
url: "https://developer.hashicorp.com/terraform/language/style#dynamic-resource-count",
|
|
272
|
+
relation: "overlaps",
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
TF022: [
|
|
276
|
+
// tfsec consolidated its three `general/secrets` checks into
|
|
277
|
+
// `no-plaintext-exposure` in v1, which is the one still in the repository's
|
|
278
|
+
// `master` docs; `overlaps` because that consolidation also covers the
|
|
279
|
+
// variable and locals cases TF007 reports separately.
|
|
280
|
+
{
|
|
281
|
+
tool: "tfsec",
|
|
282
|
+
rule: "general-secrets-no-plaintext-exposure",
|
|
283
|
+
url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/general/secrets/no-plaintext-exposure/index.md",
|
|
284
|
+
relation: "overlaps",
|
|
285
|
+
},
|
|
286
|
+
{ tool: "kics", rule: "a88baa34-e2ad-44ea-ad6f-8cac87bc7c71", url: "https://docs.kics.io/latest/secrets/", relation: "overlaps" },
|
|
287
|
+
],
|
|
288
|
+
TF030: [
|
|
289
|
+
// Every credit overlaps; none is equivalent. TF030 is WAW019's port list
|
|
290
|
+
// (22, 3389, 3306, 5432) on 0.0.0.0/0 or ::/0, read across the three
|
|
291
|
+
// Terraform shapes. Checkov checks one port per id and, unlike TF030,
|
|
292
|
+
// reads tcp 0-0 as every port and passes any rule with `self = true`.
|
|
293
|
+
// tfsec flags a /0 on any port; trivy-checks only SSH and RDP; KICS a
|
|
294
|
+
// longer port list; the Sentinel policies only `aws_security_group_rule`
|
|
295
|
+
// with an exact `to_port`. Each upstream id was read from its source on
|
|
296
|
+
// 2026-10-02 (checkov `AbsSecurityGroupUnrestrictedIngress.py`, trivy-checks
|
|
297
|
+
// `no_public_ingress_sgr.rego` metadata, KICS `metadata.json`, tfsec
|
|
298
|
+
// `rules.md`).
|
|
299
|
+
{ tool: "checkov", rule: "CKV_AWS_24", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
|
|
300
|
+
{ tool: "checkov", rule: "CKV_AWS_25", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
|
|
301
|
+
{
|
|
302
|
+
tool: "tfsec",
|
|
303
|
+
rule: "aws-vpc-no-public-ingress-sgr",
|
|
304
|
+
url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/vpc/no-public-ingress-sgr/index.md",
|
|
305
|
+
relation: "overlaps",
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
tool: "trivy-checks",
|
|
309
|
+
rule: "aws-ec2-no-public-ingress-sgr",
|
|
310
|
+
url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/ec2/no_public_ingress_sgr.rego",
|
|
311
|
+
relation: "overlaps",
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
tool: "kics",
|
|
315
|
+
rule: "381c3f2a-ef6f-4eff-99f7-b169cda3422c",
|
|
316
|
+
url: "https://docs.kics.io/latest/queries/terraform-queries/aws/381c3f2a-ef6f-4eff-99f7-b169cda3422c/",
|
|
317
|
+
relation: "overlaps",
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
tool: "terraform-sentinel-policies",
|
|
321
|
+
rule: "restrict-ingress-sg-rule-ssh",
|
|
322
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/blob/main/aws/restrict-ingress-sg-rule-ssh.sentinel",
|
|
323
|
+
relation: "overlaps",
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
tool: "terraform-sentinel-policies",
|
|
327
|
+
rule: "restrict-ingress-sg-rule-rdp",
|
|
328
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/blob/main/aws/restrict-ingress-sg-rule-rdp.sentinel",
|
|
329
|
+
relation: "overlaps",
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
// tfsec's check also flags a service wildcard (`s3:*`), which TF031 does
|
|
333
|
+
// not, so "overlaps". checkov splits the condition four ways: CKV_AWS_63
|
|
334
|
+
// (Action "*" on the policy resources, plus aws_ssoadmin_permission_set_
|
|
335
|
+
// inline_policy, which TF031 does not read) and CKV_AWS_49 (the same on the
|
|
336
|
+
// data source, which TF031 extends with Resource "*"); CKV_AWS_355/356 ask
|
|
337
|
+
// cloudsplaining whether Resource "*" meets a RESTRICTABLE action, a
|
|
338
|
+
// narrower test than TF031's literal one. trivy-checks has no credit:
|
|
339
|
+
// `aws-iam-no-policy-wildcards` (AWS-0057) survives there only as a
|
|
340
|
+
// `deprecated: true` metadata stub with no rule body, added by
|
|
341
|
+
// aquasecurity/trivy-checks c292eb6737e0 ("add removed Go checks as
|
|
342
|
+
// deprecated"), so it checks nothing to be equivalent to (#2286).
|
|
343
|
+
TF031: [
|
|
344
|
+
{
|
|
345
|
+
tool: "tfsec",
|
|
346
|
+
rule: "aws-iam-no-policy-wildcards",
|
|
347
|
+
url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/iam/no-policy-wildcards/index.md",
|
|
348
|
+
relation: "overlaps",
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
tool: "checkov",
|
|
352
|
+
rule: "CKV_AWS_63",
|
|
353
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/IAMStarActionPolicyDocument.py",
|
|
354
|
+
relation: "overlaps",
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
tool: "checkov",
|
|
358
|
+
rule: "CKV_AWS_49",
|
|
359
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/data/aws/StarActionPolicyDocument.py",
|
|
360
|
+
relation: "extends",
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
tool: "checkov",
|
|
364
|
+
rule: "CKV_AWS_355",
|
|
365
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/IAMStarResourcePolicyDocument.py",
|
|
366
|
+
relation: "overlaps",
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
tool: "checkov",
|
|
370
|
+
rule: "CKV_AWS_356",
|
|
371
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/data/aws/ResourcePolicyDocument.py",
|
|
372
|
+
relation: "overlaps",
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
// tfsec's `aws-ecs-no-plaintext-secrets` and its successor in trivy-checks
|
|
376
|
+
// (`long_id` aws-ecs-no-plaintext-secrets, AVD-AWS-0036) fire on the same
|
|
377
|
+
// condition: an environment entry whose name is sensitive or whose value a
|
|
378
|
+
// secret scanner flags. `overlaps`, not `equivalent`, because both evaluate
|
|
379
|
+
// the definition first and so also read what TF032 reports as not
|
|
380
|
+
// determined, and their name list differs from secret-shape.ts's. Checkov
|
|
381
|
+
// has no Terraform check for ECS environment values (its ECS checks cover
|
|
382
|
+
// privilege, read-only root, host process, task role and EFS encryption,
|
|
383
|
+
// `checkov/terraform/checks/resource/aws/ECS*.py`), so it gets no credit.
|
|
384
|
+
TF032: [
|
|
385
|
+
{
|
|
386
|
+
tool: "tfsec",
|
|
387
|
+
rule: "aws-ecs-no-plaintext-secrets",
|
|
388
|
+
url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/ecs/no-plaintext-secrets/index.md",
|
|
389
|
+
relation: "overlaps",
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
tool: "trivy-checks",
|
|
393
|
+
rule: "aws-ecs-no-plaintext-secrets",
|
|
394
|
+
url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/ecs/no_plaintext_secrets.rego",
|
|
395
|
+
relation: "overlaps",
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
// TF033-TF037 (#2288). Every tool below reports an absent attribute the
|
|
399
|
+
// same as an insecure one, which is right for SNS (TF034) and ECR (TF037)
|
|
400
|
+
// and wrong for the rest: SQS encrypts new queues by default, an unset
|
|
401
|
+
// EBS `encrypted` follows the Region's encryption by default, and Aurora
|
|
402
|
+
// encrypts new clusters whatever `storage_encrypted` says. Where chant's
|
|
403
|
+
// answer to absence differs the credit is "overlaps"; where it agrees and
|
|
404
|
+
// the condition is the same, "equivalent". The ECR credits are "overlaps"
|
|
405
|
+
// too: none of them knows provider v6's `IMMUTABLE_WITH_EXCLUSION`, which
|
|
406
|
+
// TF037 passes and they report. trivy-checks ids are the live
|
|
407
|
+
// Rego checks (not `deprecated` stubs) at aquasecurity/trivy-checks
|
|
408
|
+
// 71c05d02845c. tfsec has no TF036 credit: its frozen `rules.md`, which
|
|
409
|
+
// the prior-art sweep indexes, no longer lists an EBS volume check.
|
|
410
|
+
TF033: [
|
|
411
|
+
{ tool: "tfsec", rule: "aws-rds-encrypt-instance-storage-data", url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/rds/encrypt-instance-storage-data/index.md", relation: "overlaps" },
|
|
412
|
+
{ tool: "tfsec", rule: "aws-rds-encrypt-cluster-storage-data", url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/rds/encrypt-cluster-storage-data/index.md", relation: "overlaps" },
|
|
413
|
+
{ tool: "trivy-checks", rule: "aws-rds-encrypt-instance-storage-data", url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/rds/encrypt_instance_storage_data.rego", relation: "overlaps" },
|
|
414
|
+
{ tool: "trivy-checks", rule: "aws-rds-encrypt-cluster-storage-data", url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/rds/encrypt_cluster_storage_data.rego", relation: "overlaps" },
|
|
415
|
+
{ tool: "checkov", rule: "CKV_AWS_16", url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/RDSEncryption.py", relation: "overlaps" },
|
|
416
|
+
{ tool: "checkov", rule: "CKV_AWS_96", url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/AuroraEncryption.py", relation: "overlaps" },
|
|
417
|
+
{ tool: "kics", rule: "08bd0760-8752-44e1-9779-7bb369b2b4e4", url: "https://docs.kics.io/latest/queries/terraform-queries/aws/08bd0760-8752-44e1-9779-7bb369b2b4e4/", relation: "overlaps" },
|
|
418
|
+
],
|
|
419
|
+
TF034: [
|
|
420
|
+
{ tool: "tfsec", rule: "aws-sns-enable-topic-encryption", url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/sns/enable-topic-encryption/index.md", relation: "equivalent" },
|
|
421
|
+
{ tool: "trivy-checks", rule: "aws-sns-enable-topic-encryption", url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/sns/enable_topic_encryption.rego", relation: "equivalent" },
|
|
422
|
+
{ tool: "checkov", rule: "CKV_AWS_26", url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/SNSTopicEncryption.py", relation: "equivalent" },
|
|
423
|
+
{ tool: "kics", rule: "28545147-2fc6-42d5-a1f9-cf226658e591", url: "https://docs.kics.io/latest/queries/terraform-queries/aws/28545147-2fc6-42d5-a1f9-cf226658e591/", relation: "equivalent" },
|
|
424
|
+
],
|
|
425
|
+
TF035: [
|
|
426
|
+
{ tool: "tfsec", rule: "aws-sqs-enable-queue-encryption", url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/sqs/enable-queue-encryption/index.md", relation: "overlaps" },
|
|
427
|
+
{ tool: "trivy-checks", rule: "aws-sqs-enable-queue-encryption", url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/sqs/enable_queue_encryption.rego", relation: "overlaps" },
|
|
428
|
+
{ tool: "checkov", rule: "CKV_AWS_27", url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/SQSQueueEncryption.py", relation: "overlaps" },
|
|
429
|
+
{ tool: "kics", rule: "6e8849c1-3aa7-40e3-9063-b85ee300f29f", url: "https://docs.kics.io/latest/queries/terraform-queries/aws/6e8849c1-3aa7-40e3-9063-b85ee300f29f/", relation: "overlaps" },
|
|
430
|
+
],
|
|
431
|
+
TF036: [
|
|
432
|
+
{ tool: "trivy-checks", rule: "aws-ec2-enable-volume-encryption", url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/ec2/enable_volume_encryption.rego", relation: "overlaps" },
|
|
433
|
+
{ tool: "checkov", rule: "CKV_AWS_3", url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/EBSEncryption.py", relation: "overlaps" },
|
|
434
|
+
{ tool: "kics", rule: "cc997676-481b-4e93-aa81-d19f8c5e9b12", url: "https://docs.kics.io/latest/queries/terraform-queries/aws/cc997676-481b-4e93-aa81-d19f8c5e9b12/", relation: "overlaps" },
|
|
435
|
+
],
|
|
436
|
+
TF037: [
|
|
437
|
+
{ tool: "tfsec", rule: "aws-ecr-enforce-immutable-repository", url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/aws/ecr/enforce-immutable-repository/index.md", relation: "overlaps" },
|
|
438
|
+
{ tool: "trivy-checks", rule: "aws-ecr-enforce-immutable-repository", url: "https://github.com/aquasecurity/trivy-checks/blob/main/checks/cloud/aws/ecr/enforce_immutable_repository.rego", relation: "overlaps" },
|
|
439
|
+
{ tool: "checkov", rule: "CKV_AWS_51", url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/resource/aws/ECRImmutableTags.py", relation: "overlaps" },
|
|
440
|
+
{ tool: "kics", rule: "d1846b12-20c5-4d45-8798-fc35b79268eb", url: "https://docs.kics.io/latest/queries/terraform-queries/aws/d1846b12-20c5-4d45-8798-fc35b79268eb/", relation: "overlaps" },
|
|
441
|
+
],
|
|
442
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared reading helpers for the single-block post-synth checks (#2110).
|
|
3
|
+
*
|
|
4
|
+
* Thirteen of the fourteen rules in that issue do the same three things: pick
|
|
5
|
+
* the entities of one block type out of `ctx.entities`, read one attribute of
|
|
6
|
+
* each body, and report. This module is those three things, so a rule file is
|
|
7
|
+
* its condition and its message and nothing else. It exports no
|
|
8
|
+
* `PostSynthCheck`, so the generated barrel skips it (see
|
|
9
|
+
* `packages/core/src/codegen/generate-post-synth-barrel.ts`).
|
|
10
|
+
*/
|
|
11
|
+
import { isResourceDeclarable } from "@intentius/chant/declarable";
|
|
12
|
+
/** Every block of one entity type, in `ctx.entities` order. */
|
|
13
|
+
export function blocksOfType(ctx, entityType) {
|
|
14
|
+
const blocks = [];
|
|
15
|
+
for (const [key, entity] of ctx.entities) {
|
|
16
|
+
if (entity.entityType !== entityType)
|
|
17
|
+
continue;
|
|
18
|
+
if (!isResourceDeclarable(entity))
|
|
19
|
+
continue;
|
|
20
|
+
const props = entity.props;
|
|
21
|
+
blocks.push({
|
|
22
|
+
key,
|
|
23
|
+
address: typeof props.address === "string" ? props.address : key,
|
|
24
|
+
body: (typeof props.body === "object" && props.body !== null ? props.body : {}),
|
|
25
|
+
file: typeof props.file === "string" ? props.file : "",
|
|
26
|
+
root: typeof props.root === "string" ? props.root : "",
|
|
27
|
+
source: typeof props.source === "string" ? props.source : "",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return blocks;
|
|
31
|
+
}
|
|
32
|
+
/** Blocks of several types at once, in the order the types are given. */
|
|
33
|
+
export function blocksOfTypes(ctx, entityTypes) {
|
|
34
|
+
return entityTypes.flatMap((t) => blocksOfType(ctx, t));
|
|
35
|
+
}
|
|
36
|
+
/** The name half of a `var.<name>` / `output.<name>` address. */
|
|
37
|
+
export function blockName(address) {
|
|
38
|
+
const dot = address.indexOf(".");
|
|
39
|
+
return dot === -1 ? address : address.slice(dot + 1);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The bodies of a nested block (`lifecycle`, `validation`, a provider's
|
|
43
|
+
* `assume_role`). hcl2json encodes a repeatable block as an array of bodies
|
|
44
|
+
* and a singleton as a bare object, so both are normalized to an array here.
|
|
45
|
+
*/
|
|
46
|
+
export function nestedBodies(body, key) {
|
|
47
|
+
const value = body[key];
|
|
48
|
+
if (Array.isArray(value))
|
|
49
|
+
return value.filter((v) => typeof v === "object" && v !== null && !Array.isArray(v));
|
|
50
|
+
if (typeof value === "object" && value !== null)
|
|
51
|
+
return [value];
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
/** Is this attribute value a string that says nothing? */
|
|
55
|
+
export function isBlank(value) {
|
|
56
|
+
return value === undefined || value === null || (typeof value === "string" && value.trim() === "");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Every scalar attribute in a block body, descending through nested blocks so
|
|
60
|
+
* a credential inside `connection {}` or `assume_role {}` is not invisible.
|
|
61
|
+
* Depth-limited: a Terraform block nests a handful of levels at most, and an
|
|
62
|
+
* unbounded walk over a large body buys nothing.
|
|
63
|
+
*/
|
|
64
|
+
export function walkAttributes(body, maxDepth = 3) {
|
|
65
|
+
const out = [];
|
|
66
|
+
const visit = (value, path, name, depth) => {
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
if (depth > maxDepth)
|
|
69
|
+
return;
|
|
70
|
+
for (const item of value)
|
|
71
|
+
visit(item, path, name, depth);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (typeof value === "object" && value !== null) {
|
|
75
|
+
if (depth >= maxDepth)
|
|
76
|
+
return;
|
|
77
|
+
for (const [k, v] of Object.entries(value)) {
|
|
78
|
+
visit(v, path === "" ? k : `${path}.${k}`, k, depth + 1);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (path !== "")
|
|
83
|
+
out.push({ name, path, value });
|
|
84
|
+
};
|
|
85
|
+
visit(body, "", "", 0);
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared post-synth fixture loader.
|
|
3
|
+
*
|
|
4
|
+
* KICS lays its own query fixtures out as `positive.tf`/`negative.tf` pairs
|
|
5
|
+
* beside the query that tests them (survey, #2107): every query is
|
|
6
|
+
* externally auditable because a rule and its evidence live in the same
|
|
7
|
+
* directory. This lexicon adopts that layout: `fixtures/<id>/positive.tf`
|
|
8
|
+
* triggers the check, `fixtures/<id>/negative.tf` does not, and a rule with
|
|
9
|
+
* more than one triggering condition adds `positive-<case>.tf` beside them.
|
|
10
|
+
*
|
|
11
|
+
* KICS itself scans a whole fixture directory in one pass and tells its
|
|
12
|
+
* fixtures apart by the file each finding came from. That doesn't transfer
|
|
13
|
+
* here: TF001 (and most post-synth checks with per-root state, not
|
|
14
|
+
* per-resource) fire once per *root*, deduplicated by root name, so parsing
|
|
15
|
+
* `positive.tf` and `negative.tf` together as one root would let whichever
|
|
16
|
+
* file's terraform block is read first decide the outcome for both, silently
|
|
17
|
+
* discarding the other. `loadFixture` therefore reads exactly the one named
|
|
18
|
+
* file and parses it alone, through the same `blocksToEntities` pass
|
|
19
|
+
* `parseTerraformRootDir` wraps for a real root directory, so sibling
|
|
20
|
+
* fixtures never mix into one build.
|
|
21
|
+
*/
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { blocksToEntities } from "../../../hcl/parse.js";
|
|
26
|
+
import { renderTerraformRoots } from "../../../hcl/roots.js";
|
|
27
|
+
const FIXTURES_DIR = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
/**
|
|
29
|
+
* Parse `fixtures/<checkId>/<name>.tf` into a {@link PostSynthContext} whose
|
|
30
|
+
* `entities` are exactly what a real root module of that one file would
|
|
31
|
+
* produce. `root` (the entity key prefix and the `TerraformEntity.props.root`
|
|
32
|
+
* value) defaults to `checkId`; pass a distinct value to load two fixtures
|
|
33
|
+
* into one context under different root names.
|
|
34
|
+
*
|
|
35
|
+
* Three lines per fixture in a test:
|
|
36
|
+
* ```ts
|
|
37
|
+
* const diags = tf001.check(await loadFixture("TF001", "positive"));
|
|
38
|
+
* expect(diags).toHaveLength(1);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export async function loadFixture(checkId, name, root = checkId) {
|
|
42
|
+
const file = `${name}.tf`;
|
|
43
|
+
const source = readFileSync(join(FIXTURES_DIR, checkId, file), "utf-8");
|
|
44
|
+
const entities = await blocksToEntities([{ name: file, source }], root);
|
|
45
|
+
return {
|
|
46
|
+
outputs: new Map(),
|
|
47
|
+
entities,
|
|
48
|
+
buildResult: {
|
|
49
|
+
outputs: new Map(),
|
|
50
|
+
entities,
|
|
51
|
+
warnings: [],
|
|
52
|
+
errors: [],
|
|
53
|
+
sourceFileCount: 1,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse a whole fixture DIRECTORY, `fixtures/<checkId>/<name>/`, the way
|
|
59
|
+
* `buildRoots()` parses a configured root: its own `.tf` files, then every
|
|
60
|
+
* local `module` call followed into its directory (chant #2112). A rule about
|
|
61
|
+
* child modules (TF014, TF015) or about a whole scope's references (TF020)
|
|
62
|
+
* cannot be shown a single file and still be tested honestly, so its fixture
|
|
63
|
+
* is a tree:
|
|
64
|
+
*
|
|
65
|
+
* ```
|
|
66
|
+
* fixtures/TF014/positive/main.tf # module "cdn" { source = "./modules/cdn" }
|
|
67
|
+
* fixtures/TF014/positive/modules/cdn/main.tf # the provider block the rule reports
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* The fixture directory is also the project root for the descent, so a
|
|
71
|
+
* fixture can exercise the outside-the-project refusal with a `../` source
|
|
72
|
+
* without reaching into the repository around it. `warnings` from the render
|
|
73
|
+
* lands on `buildResult.warnings`, where the descent's own refusals are
|
|
74
|
+
* readable by a test.
|
|
75
|
+
*/
|
|
76
|
+
export async function loadTreeFixture(checkId, name, root = checkId, callModuleType) {
|
|
77
|
+
const dir = join(FIXTURES_DIR, checkId, name);
|
|
78
|
+
const { entities, warnings } = await renderTerraformRoots({
|
|
79
|
+
projectRoot: dir,
|
|
80
|
+
roots: { [root]: { dir } },
|
|
81
|
+
...(callModuleType ? { callModuleType } : {}),
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
outputs: new Map(),
|
|
85
|
+
entities,
|
|
86
|
+
buildResult: {
|
|
87
|
+
outputs: new Map(),
|
|
88
|
+
entities,
|
|
89
|
+
warnings,
|
|
90
|
+
errors: [],
|
|
91
|
+
sourceFileCount: entities.size,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|