@intentius/chant-lexicon-terraform 0.57.0 → 0.58.0
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/composites/terraform-adopt-op.d.ts +126 -0
- package/dist/composites/terraform-adopt-op.d.ts.map +1 -0
- package/dist/composites/terraform-apply-op.d.ts +63 -16
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-watch-op.d.ts +80 -36
- package/dist/composites/terraform-watch-op.d.ts.map +1 -1
- package/dist/config.d.ts +49 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +256 -16
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/hcl/descend.d.ts +98 -0
- package/dist/hcl/descend.d.ts.map +1 -0
- package/dist/hcl/marker.d.ts +130 -0
- package/dist/hcl/marker.d.ts.map +1 -0
- package/dist/hcl/parse.d.ts +142 -9
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/references.d.ts +76 -0
- package/dist/hcl/references.d.ts.map +1 -0
- package/dist/hcl/roots.d.ts +22 -3
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/suppressions.d.ts +54 -0
- package/dist/hcl/suppressions.d.ts.map +1 -0
- package/dist/hcl/value.d.ts +77 -0
- package/dist/hcl/value.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +32 -5
- package/dist/lint/audit-catalog.d.ts +3 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts +68 -0
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/blocks.d.ts +53 -0
- package/dist/lint/post-synth/blocks.d.ts.map +1 -0
- package/dist/lint/post-synth/fixtures/load.d.ts +37 -0
- package/dist/lint/post-synth/fixtures/load.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/module-source.d.ts +38 -0
- package/dist/lint/post-synth/module-source.d.ts.map +1 -0
- package/dist/lint/post-synth/scope.d.ts +65 -0
- package/dist/lint/post-synth/scope.d.ts.map +1 -0
- package/dist/lint/post-synth/tf001.d.ts +18 -0
- package/dist/lint/post-synth/tf001.d.ts.map +1 -1
- package/dist/lint/post-synth/tf002.d.ts +32 -0
- package/dist/lint/post-synth/tf002.d.ts.map +1 -0
- package/dist/lint/post-synth/tf003.d.ts +22 -0
- package/dist/lint/post-synth/tf003.d.ts.map +1 -0
- package/dist/lint/post-synth/tf004.d.ts +22 -0
- package/dist/lint/post-synth/tf004.d.ts.map +1 -0
- package/dist/lint/post-synth/tf005.d.ts +28 -0
- package/dist/lint/post-synth/tf005.d.ts.map +1 -0
- package/dist/lint/post-synth/tf006.d.ts +21 -0
- package/dist/lint/post-synth/tf006.d.ts.map +1 -0
- package/dist/lint/post-synth/tf007.d.ts +21 -0
- package/dist/lint/post-synth/tf007.d.ts.map +1 -0
- package/dist/lint/post-synth/tf008.d.ts +22 -0
- package/dist/lint/post-synth/tf008.d.ts.map +1 -0
- package/dist/lint/post-synth/tf009.d.ts +24 -0
- package/dist/lint/post-synth/tf009.d.ts.map +1 -0
- package/dist/lint/post-synth/tf010.d.ts +19 -0
- package/dist/lint/post-synth/tf010.d.ts.map +1 -0
- package/dist/lint/post-synth/tf011.d.ts +18 -0
- package/dist/lint/post-synth/tf011.d.ts.map +1 -0
- package/dist/lint/post-synth/tf012.d.ts +17 -0
- package/dist/lint/post-synth/tf012.d.ts.map +1 -0
- package/dist/lint/post-synth/tf013.d.ts +21 -0
- package/dist/lint/post-synth/tf013.d.ts.map +1 -0
- package/dist/lint/post-synth/tf014.d.ts +25 -0
- package/dist/lint/post-synth/tf014.d.ts.map +1 -0
- package/dist/lint/post-synth/tf015.d.ts +21 -0
- package/dist/lint/post-synth/tf015.d.ts.map +1 -0
- package/dist/lint/post-synth/tf016.d.ts +30 -0
- package/dist/lint/post-synth/tf016.d.ts.map +1 -0
- package/dist/lint/post-synth/tf017.d.ts +19 -0
- package/dist/lint/post-synth/tf017.d.ts.map +1 -0
- package/dist/lint/post-synth/tf018.d.ts +20 -0
- package/dist/lint/post-synth/tf018.d.ts.map +1 -0
- package/dist/lint/post-synth/tf019.d.ts +25 -0
- package/dist/lint/post-synth/tf019.d.ts.map +1 -0
- package/dist/lint/post-synth/tf020.d.ts +38 -0
- package/dist/lint/post-synth/tf020.d.ts.map +1 -0
- package/dist/lint/post-synth/tf021.d.ts +41 -0
- package/dist/lint/post-synth/tf021.d.ts.map +1 -0
- package/dist/lint/post-synth/tf022.d.ts +24 -0
- package/dist/lint/post-synth/tf022.d.ts.map +1 -0
- package/dist/lint/post-synth/tf024.d.ts +24 -0
- package/dist/lint/post-synth/tf024.d.ts.map +1 -0
- package/dist/lint/post-synth/tf025.d.ts +29 -0
- package/dist/lint/post-synth/tf025.d.ts.map +1 -0
- package/dist/lint/post-synth/tf026.d.ts +21 -0
- package/dist/lint/post-synth/tf026.d.ts.map +1 -0
- package/dist/lint/rules/fixtures/load.d.ts +13 -0
- package/dist/lint/rules/fixtures/load.d.ts.map +1 -0
- package/dist/lint/rules/plan-before-apply.d.ts +11 -0
- package/dist/lint/rules/plan-before-apply.d.ts.map +1 -1
- package/dist/lint/secret-shape.d.ts +68 -0
- package/dist/lint/secret-shape.d.ts.map +1 -0
- package/dist/live-ownership.d.ts +55 -0
- package/dist/live-ownership.d.ts.map +1 -0
- package/dist/lsp/option-keys.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +24 -0
- package/dist/okf/rules/TF002.md +11 -0
- package/dist/okf/rules/TF003.md +11 -0
- package/dist/okf/rules/TF004.md +11 -0
- package/dist/okf/rules/TF005.md +11 -0
- package/dist/okf/rules/TF006.md +11 -0
- package/dist/okf/rules/TF007.md +11 -0
- package/dist/okf/rules/TF008.md +11 -0
- package/dist/okf/rules/TF009.md +11 -0
- package/dist/okf/rules/TF010.md +11 -0
- package/dist/okf/rules/TF011.md +11 -0
- package/dist/okf/rules/TF012.md +11 -0
- package/dist/okf/rules/TF013.md +11 -0
- package/dist/okf/rules/TF014.md +11 -0
- package/dist/okf/rules/TF015.md +11 -0
- package/dist/okf/rules/TF016.md +11 -0
- package/dist/okf/rules/TF017.md +11 -0
- package/dist/okf/rules/TF018.md +11 -0
- package/dist/okf/rules/TF019.md +11 -0
- package/dist/okf/rules/TF020.md +11 -0
- package/dist/okf/rules/TF021.md +11 -0
- package/dist/okf/rules/TF022.md +11 -0
- package/dist/okf/rules/TF024.md +11 -0
- package/dist/okf/rules/TF025.md +11 -0
- package/dist/okf/rules/TF026.md +11 -0
- package/dist/op/activities/index.d.ts +13 -6
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/live-detect.d.ts +51 -0
- package/dist/op/activities/live-detect.d.ts.map +1 -0
- package/dist/op/activities/terraform.d.ts +445 -17
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activity-contracts.d.ts +148 -0
- package/dist/op/activity-contracts.d.ts.map +1 -0
- package/dist/op/adoption.d.ts +118 -0
- package/dist/op/adoption.d.ts.map +1 -0
- package/dist/op/builders.d.ts +46 -6
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/resolve-root-mode.d.ts +45 -0
- package/dist/op/resolve-root-mode.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/blocks.ts +109 -0
- package/dist/rules/module-source.ts +121 -0
- package/dist/rules/plan-before-apply.ts +11 -0
- package/dist/rules/scope.ts +107 -0
- package/dist/rules/tf001.ts +32 -8
- package/dist/rules/tf002.ts +151 -0
- package/dist/rules/tf003.ts +71 -0
- package/dist/rules/tf004.ts +63 -0
- package/dist/rules/tf005.ts +76 -0
- package/dist/rules/tf006.ts +53 -0
- package/dist/rules/tf007.ts +78 -0
- package/dist/rules/tf008.ts +75 -0
- package/dist/rules/tf009.ts +59 -0
- package/dist/rules/tf010.ts +51 -0
- package/dist/rules/tf011.ts +49 -0
- package/dist/rules/tf012.ts +47 -0
- package/dist/rules/tf013.ts +63 -0
- package/dist/rules/tf014.ts +72 -0
- package/dist/rules/tf015.ts +63 -0
- package/dist/rules/tf016.ts +75 -0
- package/dist/rules/tf017.ts +50 -0
- package/dist/rules/tf018.ts +72 -0
- package/dist/rules/tf019.ts +72 -0
- package/dist/rules/tf020.ts +123 -0
- package/dist/rules/tf021.ts +126 -0
- package/dist/rules/tf022.ts +61 -0
- package/dist/rules/tf024.ts +70 -0
- package/dist/rules/tf025.ts +101 -0
- package/dist/rules/tf026.ts +80 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/package.json +7 -2
- package/src/__fixtures__/live/main.tf +28 -0
- package/src/__fixtures__/live-adopt/estate.chdf.hcl +6 -0
- package/src/__fixtures__/live-adopt/main.tf +21 -0
- package/src/__fixtures__/live-estate/README.md +58 -0
- package/src/__fixtures__/live-estate/compute.tf +10 -0
- package/src/__fixtures__/live-estate/estate.chdf.hcl +1 -0
- package/src/__fixtures__/live-estate/logs.tf +8 -0
- package/src/__fixtures__/live-estate/network.tf +24 -0
- package/src/__fixtures__/live-estate/rule.tf +11 -0
- package/src/__fixtures__/live-estate/unowned.tf +21 -0
- package/src/__fixtures__/live-estate/versions.tf +31 -0
- package/src/__fixtures__/live-ls.json +98 -0
- package/src/__fixtures__/live-plan.json +285 -0
- package/src/__fixtures__/live-sidecar/estate.chdf.hcl +3 -0
- package/src/__fixtures__/live-sidecar/main.tf +24 -0
- package/src/__fixtures__/module-tree/README.md +16 -0
- package/src/__fixtures__/module-tree/cycle/main.tf +3 -0
- package/src/__fixtures__/module-tree/cycle/modules/a/main.tf +9 -0
- package/src/__fixtures__/module-tree/cycle/modules/b/main.tf +9 -0
- package/src/__fixtures__/module-tree/missing/main.tf +9 -0
- package/src/__fixtures__/module-tree/outside/root/main.tf +9 -0
- package/src/__fixtures__/module-tree/root/main.tf +28 -0
- package/src/__fixtures__/module-tree/root/modules/cdn/main.tf +15 -0
- package/src/__fixtures__/module-tree/root/modules/cdn/modules/bucket/main.tf +11 -0
- package/src/__fixtures__/module-tree/shared/main.tf +5 -0
- package/src/__fixtures__/references/main.tf +56 -0
- package/src/codegen/lint-rules-page.test.ts +28 -0
- package/src/composites/terraform-adopt-op.acceptance.test.ts +166 -0
- package/src/composites/terraform-adopt-op.test.ts +170 -0
- package/src/composites/terraform-adopt-op.ts +197 -0
- package/src/composites/terraform-apply-op.acceptance.test.ts +183 -5
- package/src/composites/terraform-apply-op.test.ts +167 -15
- package/src/composites/terraform-apply-op.ts +105 -28
- package/src/composites/terraform-watch-op.test.ts +85 -26
- package/src/composites/terraform-watch-op.ts +130 -73
- package/src/config.ts +32 -3
- package/src/describe-resources.live.test.ts +598 -0
- package/src/describe-resources.test.ts +53 -0
- package/src/describe-resources.ts +811 -28
- package/src/hcl/descend.test.ts +162 -0
- package/src/hcl/descend.ts +273 -0
- package/src/hcl/marker.test.ts +147 -0
- package/src/hcl/marker.ts +235 -0
- package/src/hcl/parse.test.ts +69 -0
- package/src/hcl/parse.ts +249 -27
- package/src/hcl/references.test.ts +116 -0
- package/src/hcl/references.ts +172 -0
- package/src/hcl/roots.ts +59 -5
- package/src/hcl/suppressions.test.ts +147 -0
- package/src/hcl/suppressions.ts +204 -0
- package/src/hcl/value.test.ts +73 -0
- package/src/hcl/value.ts +106 -0
- package/src/index.ts +31 -1
- package/src/lint/audit-catalog.ts +264 -3
- package/src/lint/audit-lineage.ts +319 -1
- package/src/lint/audit.test.ts +40 -1
- package/src/lint/post-synth/blocks.ts +109 -0
- package/src/lint/post-synth/fixtures/TF001/negative.tf +28 -0
- package/src/lint/post-synth/fixtures/TF001/positive.tf +24 -0
- package/src/lint/post-synth/fixtures/TF002/negative.tf +17 -0
- package/src/lint/post-synth/fixtures/TF002/positive-legacy.tf +10 -0
- package/src/lint/post-synth/fixtures/TF002/positive-no-version.tf +12 -0
- package/src/lint/post-synth/fixtures/TF002/positive.tf +7 -0
- package/src/lint/post-synth/fixtures/TF003/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF003/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF004/negative-git.tf +3 -0
- package/src/lint/post-synth/fixtures/TF004/negative-local.tf +3 -0
- package/src/lint/post-synth/fixtures/TF004/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF004/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-local.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-registry.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-sha.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive-branch.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive-non-semver.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF006/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF006/positive-empty-default.tf +11 -0
- package/src/lint/post-synth/fixtures/TF006/positive.tf +6 -0
- package/src/lint/post-synth/fixtures/TF007/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF007/positive.tf +9 -0
- package/src/lint/post-synth/fixtures/TF008/negative-variable.tf +10 -0
- package/src/lint/post-synth/fixtures/TF008/negative.tf +8 -0
- package/src/lint/post-synth/fixtures/TF008/positive-other-provider.tf +10 -0
- package/src/lint/post-synth/fixtures/TF008/positive.tf +5 -0
- package/src/lint/post-synth/fixtures/TF009/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF009/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF010/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF010/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF011/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF011/positive-blank.tf +5 -0
- package/src/lint/post-synth/fixtures/TF011/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF012/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF012/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF013/negative.tf +8 -0
- package/src/lint/post-synth/fixtures/TF013/positive-data.tf +8 -0
- package/src/lint/post-synth/fixtures/TF013/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF014/negative/main.tf +26 -0
- package/src/lint/post-synth/fixtures/TF014/negative/modules/cdn/main.tf +19 -0
- package/src/lint/post-synth/fixtures/TF014/positive/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF014/positive/modules/cdn/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF015/negative/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF015/negative/modules/cdn/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF015/positive/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF015/positive/modules/cdn/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF016/negative-template.tf +7 -0
- package/src/lint/post-synth/fixtures/TF016/negative.tf +16 -0
- package/src/lint/post-synth/fixtures/TF016/positive-shared-file.tf +12 -0
- package/src/lint/post-synth/fixtures/TF016/positive.tf +13 -0
- package/src/lint/post-synth/fixtures/TF017/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF017/positive.tf +5 -0
- package/src/lint/post-synth/fixtures/TF018/negative.tf +9 -0
- package/src/lint/post-synth/fixtures/TF018/positive-data.tf +9 -0
- package/src/lint/post-synth/fixtures/TF018/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF019/negative.tf +14 -0
- package/src/lint/post-synth/fixtures/TF019/positive-output.tf +5 -0
- package/src/lint/post-synth/fixtures/TF019/positive.tf +16 -0
- package/src/lint/post-synth/fixtures/TF020/negative.tf +43 -0
- package/src/lint/post-synth/fixtures/TF020/positive.tf +32 -0
- package/src/lint/post-synth/fixtures/TF020/scoped/main.tf +21 -0
- package/src/lint/post-synth/fixtures/TF020/scoped/modules/cdn/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF021/negative.tf +31 -0
- package/src/lint/post-synth/fixtures/TF021/positive-length.tf +8 -0
- package/src/lint/post-synth/fixtures/TF021/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF022/negative-locator.tf +7 -0
- package/src/lint/post-synth/fixtures/TF022/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF022/positive-nested.tf +10 -0
- package/src/lint/post-synth/fixtures/TF022/positive.tf +6 -0
- package/src/lint/post-synth/fixtures/TF023/negative/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF023/positive/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF023/positive/state.json +24 -0
- package/src/lint/post-synth/fixtures/TF024/live-clean/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF024/live-with-backend/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF024/live-with-cloud/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF025/live-clean/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF025/live-with-workspace-ref/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF026/live-default/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF026/live-explicit-delete/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF026/live-keep/main.tf +13 -0
- package/src/lint/post-synth/fixtures/load.ts +108 -0
- package/src/lint/post-synth/index.ts +48 -0
- package/src/lint/post-synth/module-rules.test.ts +153 -0
- package/src/lint/post-synth/module-source.test.ts +75 -0
- package/src/lint/post-synth/module-source.ts +121 -0
- package/src/lint/post-synth/post-synth.test.ts +14 -15
- package/src/lint/post-synth/scope.ts +107 -0
- package/src/lint/post-synth/single-block.test.ts +263 -0
- package/src/lint/post-synth/suppression.test.ts +110 -0
- package/src/lint/post-synth/tf001.ts +32 -8
- package/src/lint/post-synth/tf002.test.ts +93 -0
- package/src/lint/post-synth/tf002.ts +151 -0
- package/src/lint/post-synth/tf003.test.ts +75 -0
- package/src/lint/post-synth/tf003.ts +71 -0
- package/src/lint/post-synth/tf004.test.ts +47 -0
- package/src/lint/post-synth/tf004.ts +63 -0
- package/src/lint/post-synth/tf005.test.ts +57 -0
- package/src/lint/post-synth/tf005.ts +76 -0
- package/src/lint/post-synth/tf006.ts +53 -0
- package/src/lint/post-synth/tf007.ts +78 -0
- package/src/lint/post-synth/tf008.ts +75 -0
- package/src/lint/post-synth/tf009.ts +59 -0
- package/src/lint/post-synth/tf010.ts +51 -0
- package/src/lint/post-synth/tf011.ts +49 -0
- package/src/lint/post-synth/tf012.ts +47 -0
- package/src/lint/post-synth/tf013.ts +63 -0
- package/src/lint/post-synth/tf014.ts +72 -0
- package/src/lint/post-synth/tf015.ts +63 -0
- package/src/lint/post-synth/tf016.ts +75 -0
- package/src/lint/post-synth/tf017.ts +50 -0
- package/src/lint/post-synth/tf018.ts +72 -0
- package/src/lint/post-synth/tf019.ts +72 -0
- package/src/lint/post-synth/tf020.ts +123 -0
- package/src/lint/post-synth/tf021.ts +126 -0
- package/src/lint/post-synth/tf022.ts +61 -0
- package/src/lint/post-synth/tf024.test.ts +42 -0
- package/src/lint/post-synth/tf024.ts +70 -0
- package/src/lint/post-synth/tf025.test.ts +46 -0
- package/src/lint/post-synth/tf025.ts +101 -0
- package/src/lint/post-synth/tf026.test.ts +56 -0
- package/src/lint/post-synth/tf026.ts +80 -0
- package/src/lint/presets.test.ts +72 -0
- package/src/lint/rules/fixtures/TF101/negative-op.ts +2 -0
- package/src/lint/rules/fixtures/TF101/positive-op.ts +2 -0
- package/src/lint/rules/fixtures/load.ts +53 -0
- package/src/lint/rules/plan-before-apply.test.ts +112 -7
- package/src/lint/rules/plan-before-apply.ts +11 -0
- package/src/lint/secret-shape.test.ts +150 -0
- package/src/lint/secret-shape.ts +159 -0
- package/src/live-ownership.ts +60 -0
- package/src/lsp/completions.test.ts +3 -2
- package/src/lsp/option-keys.ts +10 -5
- package/src/op/activities/choudoufu.acceptance.test.ts +143 -0
- package/src/op/activities/choudoufu.test.ts +693 -0
- package/src/op/activities/index.ts +40 -4
- package/src/op/activities/live-detect.test.ts +143 -0
- package/src/op/activities/live-detect.ts +162 -0
- package/src/op/activities/registry.test.ts +11 -3
- package/src/op/activities/terraform.test.ts +7 -3
- package/src/op/activities/terraform.ts +823 -69
- package/src/op/activity-contracts.test.ts +147 -0
- package/src/op/activity-contracts.ts +178 -0
- package/src/op/adoption.test.ts +170 -0
- package/src/op/adoption.ts +241 -0
- package/src/op/builders.test.ts +60 -5
- package/src/op/builders.ts +93 -5
- package/src/op/resolve-root-mode.test.ts +85 -0
- package/src/op/resolve-root-mode.ts +72 -0
- package/src/plugin.test.ts +68 -0
- package/src/plugin.ts +75 -9
- package/src/skills/chant-terraform.md +1 -1
|
@@ -15,7 +15,325 @@
|
|
|
15
15
|
* `terraform_workspace_remote` (a `terraform.workspace` compatibility check
|
|
16
16
|
* against remote execution, a different condition than "no backend is
|
|
17
17
|
* configured at all"). Ship with no lineage rather than invent a credit.
|
|
18
|
+
*
|
|
19
|
+
* TF101 (plan-before-apply, `lint/rules/plan-before-apply.ts`) is a source-level
|
|
20
|
+
* lint rule over chant's own Op builders, not a post-synth check, so it carries
|
|
21
|
+
* no `RuleMeta`/`Lineage` entry here at all: this file's `Record` only ever
|
|
22
|
+
* keys post-synth check ids. Its own page section (docs/pages/lint-rules.mdx)
|
|
23
|
+
* says the same thing directly: no tool anywhere models "this plan file traces
|
|
24
|
+
* back to a specific preceding plan step", because that is a question about
|
|
25
|
+
* chant's own DSL, not about a Terraform root.
|
|
26
|
+
*
|
|
27
|
+
* ## The survey registry
|
|
28
|
+
*
|
|
29
|
+
* #2107 ran three research passes over every serious Terraform lint, scan and
|
|
30
|
+
* policy tool (1,888 lines, 222 cited URLs, attached to that issue as
|
|
31
|
+
* comments). The tools and documents below are what the survey found worth
|
|
32
|
+
* crediting from across that research. #2108 added none of them to
|
|
33
|
+
* `PRIOR_ART`: `packages/core/src/audit/catalog.test.ts`'s "every registered
|
|
34
|
+
* tool is credited" test fails on an entry no shipped rule cites yet, and
|
|
35
|
+
* neither TF001 nor TF101 cites anything. #2109 (TF002-TF005) is the first to
|
|
36
|
+
* draw from this list: `tflint-ruleset-terraform`, `terraform-sentinel-policies`,
|
|
37
|
+
* `hashicorp-style-guide` and `aws-terraform-prescriptive-guidance` are now in
|
|
38
|
+
* `PRIOR_ART`, spelled exactly as below. `checkov` and `kics` were already
|
|
39
|
+
* registered (other lexicons cite them); TF004 and TF005 are the first
|
|
40
|
+
* Terraform rules to. Each later rule issue adds the remaining entries it
|
|
41
|
+
* needs the first time a shipped rule cites them: #2112 (TF014, TF015, TF020,
|
|
42
|
+
* TF021) added `gcp-terraform-best-practices`, the last row of the table below
|
|
43
|
+
* that no shipped rule had cited yet.
|
|
44
|
+
*
|
|
45
|
+
* | Key | Name | URL | Licence | Kind | Note |
|
|
46
|
+
* |---|---|---|---|---|---|
|
|
47
|
+
* | `tflint` | tflint | https://github.com/terraform-linters/tflint | MPL-2.0 (its `terraform` package is BUSL-1.1; binaries are bound by both) | scanner | The plugin host; ships no language rules of its own. |
|
|
48
|
+
* | `tflint-ruleset-terraform` | tflint-ruleset-terraform | https://github.com/terraform-linters/tflint-ruleset-terraform | MPL-2.0 | scanner | The official language-level ruleset, bundled into tflint. |
|
|
49
|
+
* | `tflint-ruleset-redeploy` | tflint-ruleset-redeploy | https://github.com/RedeployAB/tflint-ruleset-redeploy | 0BSD | scanner | Community ruleset; richest source of language-level rules outside the official set. |
|
|
50
|
+
* | `tflint-ruleset-avm` | tflint-ruleset-avm | https://github.com/Azure/tflint-ruleset-avm | MIT | scanner | Azure Verified Modules conformance; two of its 33 rules are provider-agnostic. |
|
|
51
|
+
* | `tfsec` | tfsec | https://github.com/aquasecurity/tfsec | MIT | scanner | Historical: not archived but effectively frozen (no release since 2025-05); its docs site 404s above v0.61.x, so cite pinned repository paths (`github.com/aquasecurity/tfsec/blob/master/docs/checks/...`), not the docs site. |
|
|
52
|
+
* | `trivy-checks` | trivy-checks | https://github.com/aquasecurity/trivy-checks | MIT (not the trivy scanner's own Apache-2.0; cite the checks repo's licence, not the engine's) | scanner | Where tfsec's rules live today (tfsec -> defsec -> trivy + trivy-checks). Cite rules by `long_id` (stable, human-readable); `AVD-*` ids are aliases now. |
|
|
53
|
+
* | `terraform-sentinel-policies` | HashiCorp reference Sentinel policies | https://github.com/hashicorp/terraform-sentinel-policies | MPL-2.0 | specification | HashiCorp's own example policy set; ships zero rules baked into HCP Terraform itself, so treat a cited policy as a documented pattern (`specification`) unless a future entry runs it as a real check (`scanner`); the `kind` union allows either. |
|
|
54
|
+
* | `choudoufu` | choudoufu | https://github.com/INTENTIUS/choudoufu | MPL-2.0 | scanner | Its `internal/live/lint` package; experimental, AWS-only. |
|
|
55
|
+
* | `hashicorp-style-guide` | HashiCorp Terraform style guide | https://developer.hashicorp.com/terraform/language/style | n/a | specification | |
|
|
56
|
+
* | `gcp-terraform-best-practices` | Google Cloud Terraform best-practices series | https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure | n/a | specification | A multi-page series; cite the specific page's fragment. |
|
|
57
|
+
* | `aws-terraform-prescriptive-guidance` | AWS prescriptive guidance for Terraform | https://docs.aws.amazon.com/prescriptive-guidance/latest/terraform-aws-provider-best-practices/ | n/a | specification | |
|
|
58
|
+
*
|
|
59
|
+
* Deliberately excluded, per the survey: Terrascan and regula (both archived,
|
|
60
|
+
* do not build on dead projects); Snyk IaC (a product, not a rule source);
|
|
61
|
+
* semgrep (no SPDX id: its engine is LGPL-2.1 but its rules ship under the
|
|
62
|
+
* proprietary Semgrep Rules License v1.0, which forbids redistribution; cite
|
|
63
|
+
* it by name in prose if a rule's idea comes from it, never register it in
|
|
64
|
+
* `PRIOR_ART` with an SPDX licence).
|
|
65
|
+
*
|
|
66
|
+
* TF024 (#2103, live root declares a backend/cloud block) credits choudoufu's
|
|
67
|
+
* own `RuleStateBackend`, `internal/live/lint/issue.go` of
|
|
68
|
+
* github.com/INTENTIUS/choudoufu: the identical condition, checked the same
|
|
69
|
+
* way this fork checks it at `init` before any command runs. TF025 (a live
|
|
70
|
+
* root's non-default `terraform.workspace`) has no entry: its workspace half
|
|
71
|
+
* is refused at the CLI argument-parsing layer there
|
|
72
|
+
* (`internal/command/live_mode.go`), not by a dedicated lint rule under
|
|
73
|
+
* `internal/live/lint`, so there is no `Rule*` constant to point at.
|
|
74
|
+
*
|
|
75
|
+
* TF026 (#2106, a live root's `delete: "never"` against its `policy` block's
|
|
76
|
+
* `undeclared_tagged` verb) has no entry either, the same reason TF001 has
|
|
77
|
+
* none: choudoufu's own `internal/live/lint` package checks a policy block
|
|
78
|
+
* for internal validity (`RulePolicyVerb`: an unrecognized or wrong-quadrant
|
|
79
|
+
* verb; `RulePolicyScope`: `undeclared_untagged = "delete"` with no `scope`
|
|
80
|
+
* block), never against a `delete: "never" | "owned-only" | "gated"`
|
|
81
|
+
* classification, because choudoufu has no such classification — that
|
|
82
|
+
* vocabulary is chant's own, shared across lexicons (`lexicons/k8s/src/op/
|
|
83
|
+
* activities/kubectl.ts`'s `ApplyDeleteMode`, `packages/core/src/op/
|
|
84
|
+
* activities/apply.ts`'s `DeleteMode`), applied here to a tool that does not
|
|
85
|
+
* itself carry the concept. Ship with no lineage rather than invent a credit.
|
|
18
86
|
*/
|
|
19
87
|
import type { Lineage } from "@intentius/chant/audit/catalog";
|
|
20
88
|
|
|
21
|
-
|
|
89
|
+
const TFSEC_V061 = "https://github.com/aquasecurity/tfsec/blob/v0.61.3/docs/checks/general/secrets";
|
|
90
|
+
/** tflint's official language ruleset. */
|
|
91
|
+
const TFLINT_RULES = "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules";
|
|
92
|
+
/** The community ruleset with the richest set of language rules outside the official one. */
|
|
93
|
+
const REDEPLOY_RULES = "https://github.com/RedeployAB/tflint-ruleset-redeploy/blob/main/docs/rules";
|
|
94
|
+
|
|
95
|
+
export const terraformAuditLineage: Record<string, Lineage[]> = {
|
|
96
|
+
TF024: [
|
|
97
|
+
{
|
|
98
|
+
tool: "choudoufu",
|
|
99
|
+
rule: "RuleStateBackend",
|
|
100
|
+
url: "https://github.com/INTENTIUS/choudoufu/blob/main/internal/live/lint/issue.go",
|
|
101
|
+
relation: "equivalent",
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
TF002: [
|
|
105
|
+
{
|
|
106
|
+
tool: "tflint-ruleset-terraform",
|
|
107
|
+
rule: "terraform_required_providers",
|
|
108
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_required_providers.md",
|
|
109
|
+
relation: "equivalent",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
tool: "terraform-sentinel-policies",
|
|
113
|
+
rule: "require-all-providers-have-version-constraint",
|
|
114
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/tree/main/cloud-agnostic",
|
|
115
|
+
relation: "equivalent",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
tool: "aws-terraform-prescriptive-guidance",
|
|
119
|
+
rule: "Add automated version checks",
|
|
120
|
+
url: "https://docs.aws.amazon.com/prescriptive-guidance/latest/terraform-aws-provider-best-practices/version.html#add-automated-version-checks",
|
|
121
|
+
relation: "equivalent",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
// Sentinel's restrict-terraform-versions checks that required_version is
|
|
125
|
+
// within an allowed range, not that it is present at all: a narrower
|
|
126
|
+
// question than TF003's, so "overlaps" rather than "equivalent" (#2109).
|
|
127
|
+
TF003: [
|
|
128
|
+
{
|
|
129
|
+
tool: "tflint-ruleset-terraform",
|
|
130
|
+
rule: "terraform_required_version",
|
|
131
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_required_version.md",
|
|
132
|
+
relation: "equivalent",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
tool: "terraform-sentinel-policies",
|
|
136
|
+
rule: "restrict-terraform-versions",
|
|
137
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/tree/main/cloud-agnostic",
|
|
138
|
+
relation: "overlaps",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
tool: "hashicorp-style-guide",
|
|
142
|
+
rule: "Version pinning",
|
|
143
|
+
url: "https://developer.hashicorp.com/terraform/language/style#version-pinning",
|
|
144
|
+
relation: "overlaps",
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
TF004: [
|
|
148
|
+
{
|
|
149
|
+
tool: "tflint-ruleset-terraform",
|
|
150
|
+
rule: "terraform_module_version",
|
|
151
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_module_version.md",
|
|
152
|
+
relation: "equivalent",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
tool: "checkov",
|
|
156
|
+
rule: "CKV_TF_2",
|
|
157
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/module/generic/RevisionVersionTag.py",
|
|
158
|
+
relation: "equivalent",
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
// checkov's CKV_TF_1 accepts any `?ref=`/`&ref=` matching `[?&](ref=).*(\d\.\d).*`,
|
|
162
|
+
// which a branch literally named `v1.2-dev` passes; TF005 requires a full
|
|
163
|
+
// semver tag or a 40-hex SHA, so "extends" (a strict superset), not
|
|
164
|
+
// "equivalent". KICS's query only matches the `git::` prefix, not the
|
|
165
|
+
// github.com/bitbucket.org shorthands or scp-style sources TF005 also
|
|
166
|
+
// covers, so "overlaps" (#2109).
|
|
167
|
+
TF005: [
|
|
168
|
+
{
|
|
169
|
+
tool: "tflint-ruleset-terraform",
|
|
170
|
+
rule: "terraform_module_pinned_source",
|
|
171
|
+
url: "https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/terraform_module_pinned_source.md",
|
|
172
|
+
relation: "equivalent",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
tool: "checkov",
|
|
176
|
+
rule: "CKV_TF_1",
|
|
177
|
+
url: "https://github.com/bridgecrewio/checkov/blob/main/checkov/terraform/checks/module/generic/RevisionHash.py",
|
|
178
|
+
relation: "extends",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
tool: "kics",
|
|
182
|
+
rule: "3a81fc06-566f-492a-91dd-7448e409e2cd",
|
|
183
|
+
url: "https://docs.kics.io/latest/queries/terraform-queries/3a81fc06-566f-492a-91dd-7448e409e2cd/",
|
|
184
|
+
relation: "overlaps",
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
TF006: [
|
|
188
|
+
{
|
|
189
|
+
tool: "tflint-ruleset-avm",
|
|
190
|
+
rule: "avm_terraform_sensitive_variable_default_disallowed",
|
|
191
|
+
url: "https://github.com/Azure/tflint-ruleset-avm/blob/main/docs/basic/avm_terraform_sensitive_variable_default_disallowed.md",
|
|
192
|
+
relation: "equivalent",
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
TF007: [
|
|
196
|
+
{ tool: "tfsec", rule: "general-secrets-sensitive-in-variable", url: `${TFSEC_V061}/sensitive-in-variable.md`, relation: "equivalent" },
|
|
197
|
+
{ tool: "tfsec", rule: "general-secrets-sensitive-in-local", url: `${TFSEC_V061}/sensitive-in-local.md`, relation: "equivalent" },
|
|
198
|
+
],
|
|
199
|
+
TF008: [
|
|
200
|
+
{ tool: "checkov", rule: "CKV_AWS_41", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
|
|
201
|
+
{ 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" },
|
|
202
|
+
{
|
|
203
|
+
tool: "semgrep",
|
|
204
|
+
rule: "terraform.aws.security.aws-provider-static-credentials",
|
|
205
|
+
url: "https://github.com/semgrep/semgrep-rules/blob/develop/terraform/aws/security/aws-provider-static-credentials.yaml",
|
|
206
|
+
relation: "overlaps",
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
TF009: [
|
|
210
|
+
{
|
|
211
|
+
tool: "hashicorp-style-guide",
|
|
212
|
+
rule: "Variables: for sensitive variables, such as passwords and private keys, set the sensitive parameter to true",
|
|
213
|
+
url: "https://developer.hashicorp.com/terraform/language/style#variables",
|
|
214
|
+
relation: "equivalent",
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
TF010: [
|
|
218
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_typed_variables", url: `${TFLINT_RULES}/terraform_typed_variables.md`, relation: "equivalent" },
|
|
219
|
+
{ tool: "kics", rule: "fc5109bf-01fd-49fb-8bde-4492b543c34a", url: "https://docs.kics.io/latest/queries/terraform-queries/fc5109bf-01fd-49fb-8bde-4492b543c34a/", relation: "equivalent" },
|
|
220
|
+
],
|
|
221
|
+
TF011: [
|
|
222
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_documented_variables", url: `${TFLINT_RULES}/terraform_documented_variables.md`, relation: "equivalent" },
|
|
223
|
+
{ tool: "kics", rule: "2a153952-2544-4687-bcc9-cc8fea814a9b", url: "https://docs.kics.io/latest/queries/terraform-queries/2a153952-2544-4687-bcc9-cc8fea814a9b/", relation: "equivalent" },
|
|
224
|
+
{
|
|
225
|
+
tool: "terraform-sentinel-policies",
|
|
226
|
+
rule: "validate-variables-have-descriptions",
|
|
227
|
+
url: "https://github.com/hashicorp/terraform-sentinel-policies/blob/main/cloud-agnostic/validate-variables-have-descriptions.sentinel",
|
|
228
|
+
relation: "equivalent",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
TF012: [
|
|
232
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_documented_outputs", url: `${TFLINT_RULES}/terraform_documented_outputs.md`, relation: "equivalent" },
|
|
233
|
+
{ tool: "kics", rule: "59312e8a-a64e-41e7-a252-618533dd1ea8", url: "https://docs.kics.io/latest/queries/terraform-queries/59312e8a-a64e-41e7-a252-618533dd1ea8/", relation: "equivalent" },
|
|
234
|
+
],
|
|
235
|
+
TF013: [
|
|
236
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_ignore_changes_all", url: `${REDEPLOY_RULES}/terraform_ignore_changes_all.md`, relation: "equivalent" },
|
|
237
|
+
],
|
|
238
|
+
TF014: [
|
|
239
|
+
{
|
|
240
|
+
tool: "tflint-ruleset-avm",
|
|
241
|
+
rule: "avm_terraform_provider_block_disallowed",
|
|
242
|
+
url: "https://github.com/Azure/tflint-ruleset-avm/blob/main/docs/basic/avm_terraform_provider_block_disallowed.md",
|
|
243
|
+
relation: "equivalent",
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
tool: "gcp-terraform-best-practices",
|
|
247
|
+
rule: "Build reusable modules: modules must not configure providers",
|
|
248
|
+
url: "https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure#reusable-modules",
|
|
249
|
+
relation: "equivalent",
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
tool: "aws-terraform-prescriptive-guidance",
|
|
253
|
+
rule: "Structure: declare provider configurations in the root module",
|
|
254
|
+
url: "https://docs.aws.amazon.com/prescriptive-guidance/latest/terraform-aws-provider-best-practices/structure.html",
|
|
255
|
+
relation: "equivalent",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
// TF015 has no scanner to credit: no ruleset in the survey checks for a
|
|
259
|
+
// backend or cloud block inside a child module.
|
|
260
|
+
// tflint-ruleset-terraform's index has nothing about backends at all,
|
|
261
|
+
// Redeploy's has nothing either, and AVM's module rules stop at the
|
|
262
|
+
// provider block TF014 credits. What states the rule is Google Cloud's
|
|
263
|
+
// reusable-modules guide, so that document is the single credit here, and
|
|
264
|
+
// the page section says plainly that the credit is a document rather than
|
|
265
|
+
// a tool (#2112).
|
|
266
|
+
TF015: [
|
|
267
|
+
{
|
|
268
|
+
tool: "gcp-terraform-best-practices",
|
|
269
|
+
rule: "Build reusable modules: state configuration belongs to the root module",
|
|
270
|
+
url: "https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure#reusable-modules",
|
|
271
|
+
relation: "equivalent",
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
TF016: [
|
|
275
|
+
// `overlaps`, not `equivalent`: tflint reports the same `x = "${y}"` form
|
|
276
|
+
// and also the object-key case (`"${var.k}" = v`), which this rule leaves
|
|
277
|
+
// alone. See tf016.ts.
|
|
278
|
+
{ tool: "tflint-ruleset-terraform", rule: "terraform_deprecated_interpolation", url: `${TFLINT_RULES}/terraform_deprecated_interpolation.md`, relation: "overlaps" },
|
|
279
|
+
],
|
|
280
|
+
TF017: [
|
|
281
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_module_depends_on", url: `${REDEPLOY_RULES}/terraform_module_depends_on.md`, relation: "equivalent" },
|
|
282
|
+
],
|
|
283
|
+
TF018: [
|
|
284
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_output_resource", url: `${REDEPLOY_RULES}/terraform_output_resource.md`, relation: "equivalent" },
|
|
285
|
+
],
|
|
286
|
+
TF019: [
|
|
287
|
+
{ tool: "tflint-ruleset-redeploy", rule: "terraform_redundant_default", url: `${REDEPLOY_RULES}/terraform_redundant_default.md`, relation: "equivalent" },
|
|
288
|
+
],
|
|
289
|
+
TF020: [
|
|
290
|
+
// `overlaps`: tflint evaluates expressions and reports the same four
|
|
291
|
+
// kinds of declaration; chant's index is a string scan over hcl2json's
|
|
292
|
+
// preserved expression strings, so it is generous where tflint is exact
|
|
293
|
+
// (see hcl/references.ts and tf020.ts).
|
|
294
|
+
{
|
|
295
|
+
tool: "tflint-ruleset-terraform",
|
|
296
|
+
rule: "terraform_unused_declarations",
|
|
297
|
+
url: `${TFLINT_RULES}/terraform_unused_declarations.md`,
|
|
298
|
+
relation: "overlaps",
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
TF021: [
|
|
302
|
+
// Redeploy reports every `count` over a collection; TF021 reports only
|
|
303
|
+
// the ones that build an identity out of `count.index`, which is a
|
|
304
|
+
// strict subset, hence `overlaps` on both credits rather than
|
|
305
|
+
// `equivalent`. choudoufu's own rule is the survey's deepest reading of
|
|
306
|
+
// the identity-bearing question (#2112).
|
|
307
|
+
{
|
|
308
|
+
tool: "tflint-ruleset-redeploy",
|
|
309
|
+
rule: "terraform_prefer_for_each",
|
|
310
|
+
url: `${REDEPLOY_RULES}/terraform_prefer_for_each.md`,
|
|
311
|
+
relation: "overlaps",
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
tool: "choudoufu",
|
|
315
|
+
rule: "RuleCountIndex",
|
|
316
|
+
url: "https://github.com/INTENTIUS/choudoufu/blob/main/internal/live/lint/count_index.go",
|
|
317
|
+
relation: "overlaps",
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
tool: "hashicorp-style-guide",
|
|
321
|
+
rule: "Resources: use for_each for a dynamic resource count",
|
|
322
|
+
url: "https://developer.hashicorp.com/terraform/language/style#dynamic-resource-count",
|
|
323
|
+
relation: "overlaps",
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
TF022: [
|
|
327
|
+
// tfsec consolidated its three `general/secrets` checks into
|
|
328
|
+
// `no-plaintext-exposure` in v1, which is the one still in the repository's
|
|
329
|
+
// `master` docs; `overlaps` because that consolidation also covers the
|
|
330
|
+
// variable and locals cases TF007 reports separately.
|
|
331
|
+
{
|
|
332
|
+
tool: "tfsec",
|
|
333
|
+
rule: "general-secrets-no-plaintext-exposure",
|
|
334
|
+
url: "https://github.com/aquasecurity/tfsec/blob/master/docs/checks/general/secrets/no-plaintext-exposure/index.md",
|
|
335
|
+
relation: "overlaps",
|
|
336
|
+
},
|
|
337
|
+
{ tool: "kics", rule: "a88baa34-e2ad-44ea-ad6f-8cac87bc7c71", url: "https://docs.kics.io/latest/secrets/", relation: "overlaps" },
|
|
338
|
+
],
|
|
339
|
+
};
|
package/src/lint/audit.test.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
3
5
|
import { describe, expect, test } from "vitest";
|
|
4
6
|
import { auditFiles, type ChecksProvider, type EntitiesProvider } from "@intentius/chant/audit/core";
|
|
5
|
-
import { discoverByDetection, type DetectPlugin } from "@intentius/chant/audit/discover";
|
|
7
|
+
import { collectCandidates, discoverByDetection, type DetectPlugin } from "@intentius/chant/audit/discover";
|
|
8
|
+
import { auditTerraformState } from "@intentius/chant/audit/terraform-state";
|
|
6
9
|
import { terraformPlugin } from "../plugin";
|
|
10
|
+
import { postSynthChecks } from "./post-synth";
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* End-to-end audit test (#2085 acceptance): `chant audit` (`auditFiles` on top
|
|
@@ -43,3 +47,38 @@ describe("chant audit against a discovered terraform root module", () => {
|
|
|
43
47
|
expect(findings.filter((f) => f.checkId === "TF001")).toHaveLength(0);
|
|
44
48
|
});
|
|
45
49
|
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* TF023 (#2110) is audit-only: it reads the file list `chant audit` discovers,
|
|
53
|
+
* never a parsed root module, so it lives in core
|
|
54
|
+
* (`packages/core/src/audit/terraform-state.ts`) beside the other
|
|
55
|
+
* lexicon-independent families. These run the two halves it depends on, the
|
|
56
|
+
* real walk and the real check, over the committed fixture roots.
|
|
57
|
+
*/
|
|
58
|
+
describe("TF023: Terraform state committed to the repository", () => {
|
|
59
|
+
const tf023 = join(dirname(fileURLToPath(import.meta.url)), "post-synth", "fixtures", "TF023");
|
|
60
|
+
|
|
61
|
+
test("fires on a root whose state file is committed beside it", () => {
|
|
62
|
+
// The fixture ships its state as `state.json`: a tracked `terraform.tfstate`
|
|
63
|
+
// is exactly what this rule reports, and the repository's own push rules
|
|
64
|
+
// refuse the name. The test materialises the root the rule is written for.
|
|
65
|
+
const root = mkdtempSync(join(tmpdir(), "chant-tf023-"));
|
|
66
|
+
copyFileSync(join(tf023, "positive", "main.tf"), join(root, "main.tf"));
|
|
67
|
+
copyFileSync(join(tf023, "positive", "state.json"), join(root, "terraform.tfstate"));
|
|
68
|
+
const findings = auditTerraformState(collectCandidates(root));
|
|
69
|
+
rmSync(root, { recursive: true, force: true });
|
|
70
|
+
expect(findings).toHaveLength(1);
|
|
71
|
+
expect(findings[0].checkId).toBe("TF023");
|
|
72
|
+
expect(findings[0].file).toBe("terraform.tfstate");
|
|
73
|
+
expect(findings[0].lexicon).toBe("terraform");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("says nothing about a root that keeps its state remote", () => {
|
|
77
|
+
expect(auditTerraformState(collectCandidates(join(tf023, "negative")))).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("never runs during a build: it is not one of this lexicon's post-synth checks", () => {
|
|
81
|
+
expect(postSynthChecks.map((c) => c.id)).not.toContain("TF023");
|
|
82
|
+
expect(terraformPlugin.auditCatalog?.()).not.toHaveProperty("TF023");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
|
|
12
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
13
|
+
import { isResourceDeclarable } from "@intentius/chant/declarable";
|
|
14
|
+
import type { BlockBody, TerraformEntity } from "../../hcl/parse";
|
|
15
|
+
|
|
16
|
+
/** One parsed block, with the entity key the diagnostic reports against. */
|
|
17
|
+
export interface TerraformBlock {
|
|
18
|
+
/** `ctx.entities` key: `<root>/<address>`. */
|
|
19
|
+
key: string;
|
|
20
|
+
address: string;
|
|
21
|
+
body: BlockBody;
|
|
22
|
+
file: string;
|
|
23
|
+
root: string;
|
|
24
|
+
/** The raw text of the file this block came from (see `TerraformEntity.props`). */
|
|
25
|
+
source: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Every block of one entity type, in `ctx.entities` order. */
|
|
29
|
+
export function blocksOfType(ctx: PostSynthContext, entityType: string): TerraformBlock[] {
|
|
30
|
+
const blocks: TerraformBlock[] = [];
|
|
31
|
+
for (const [key, entity] of ctx.entities) {
|
|
32
|
+
if (entity.entityType !== entityType) continue;
|
|
33
|
+
if (!isResourceDeclarable(entity)) continue;
|
|
34
|
+
const props = (entity as TerraformEntity).props as Partial<TerraformEntity["props"]>;
|
|
35
|
+
blocks.push({
|
|
36
|
+
key,
|
|
37
|
+
address: typeof props.address === "string" ? props.address : key,
|
|
38
|
+
body: (typeof props.body === "object" && props.body !== null ? props.body : {}) as BlockBody,
|
|
39
|
+
file: typeof props.file === "string" ? props.file : "",
|
|
40
|
+
root: typeof props.root === "string" ? props.root : "",
|
|
41
|
+
source: typeof props.source === "string" ? props.source : "",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return blocks;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Blocks of several types at once, in the order the types are given. */
|
|
48
|
+
export function blocksOfTypes(ctx: PostSynthContext, entityTypes: string[]): TerraformBlock[] {
|
|
49
|
+
return entityTypes.flatMap((t) => blocksOfType(ctx, t));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The name half of a `var.<name>` / `output.<name>` address. */
|
|
53
|
+
export function blockName(address: string): string {
|
|
54
|
+
const dot = address.indexOf(".");
|
|
55
|
+
return dot === -1 ? address : address.slice(dot + 1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The bodies of a nested block (`lifecycle`, `validation`, a provider's
|
|
60
|
+
* `assume_role`). hcl2json encodes a repeatable block as an array of bodies
|
|
61
|
+
* and a singleton as a bare object, so both are normalized to an array here.
|
|
62
|
+
*/
|
|
63
|
+
export function nestedBodies(body: BlockBody, key: string): BlockBody[] {
|
|
64
|
+
const value = body[key];
|
|
65
|
+
if (Array.isArray(value)) return value.filter((v): v is BlockBody => typeof v === "object" && v !== null && !Array.isArray(v));
|
|
66
|
+
if (typeof value === "object" && value !== null) return [value as BlockBody];
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Is this attribute value a string that says nothing? */
|
|
71
|
+
export function isBlank(value: unknown): boolean {
|
|
72
|
+
return value === undefined || value === null || (typeof value === "string" && value.trim() === "");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** One scalar attribute found by {@link walkAttributes}. */
|
|
76
|
+
export interface Attribute {
|
|
77
|
+
/** The attribute's own name (the last path segment). */
|
|
78
|
+
name: string;
|
|
79
|
+
/** Dotted path from the block body, e.g. `connection.password`. */
|
|
80
|
+
path: string;
|
|
81
|
+
value: unknown;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Every scalar attribute in a block body, descending through nested blocks so
|
|
86
|
+
* a credential inside `connection {}` or `assume_role {}` is not invisible.
|
|
87
|
+
* Depth-limited: a Terraform block nests a handful of levels at most, and an
|
|
88
|
+
* unbounded walk over a large body buys nothing.
|
|
89
|
+
*/
|
|
90
|
+
export function walkAttributes(body: BlockBody, maxDepth = 3): Attribute[] {
|
|
91
|
+
const out: Attribute[] = [];
|
|
92
|
+
const visit = (value: unknown, path: string, name: string, depth: number): void => {
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
if (depth > maxDepth) return;
|
|
95
|
+
for (const item of value) visit(item, path, name, depth);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === "object" && value !== null) {
|
|
99
|
+
if (depth >= maxDepth) return;
|
|
100
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
101
|
+
visit(v, path === "" ? k : `${path}.${k}`, k, depth + 1);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (path !== "") out.push({ name, path, value });
|
|
106
|
+
};
|
|
107
|
+
visit(body, "", "", 0);
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_version = ">= 1.5.0"
|
|
3
|
+
|
|
4
|
+
backend "local" {
|
|
5
|
+
path = "terraform.tfstate"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
required_providers {
|
|
9
|
+
null = {
|
|
10
|
+
source = "hashicorp/null"
|
|
11
|
+
version = "~> 3.2"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
provider "null" {}
|
|
17
|
+
|
|
18
|
+
resource "null_resource" "first" {
|
|
19
|
+
triggers = {
|
|
20
|
+
name = "first"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
resource "null_resource" "second" {
|
|
25
|
+
triggers = {
|
|
26
|
+
name = "second"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_version = ">= 1.5.0"
|
|
3
|
+
|
|
4
|
+
required_providers {
|
|
5
|
+
null = {
|
|
6
|
+
source = "hashicorp/null"
|
|
7
|
+
version = "~> 3.2"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
provider "null" {}
|
|
13
|
+
|
|
14
|
+
resource "null_resource" "first" {
|
|
15
|
+
triggers = {
|
|
16
|
+
name = "first"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
resource "null_resource" "second" {
|
|
21
|
+
triggers = {
|
|
22
|
+
name = "second"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_version = ">= 1.5.0"
|
|
3
|
+
required_providers {
|
|
4
|
+
aws = {
|
|
5
|
+
source = "hashicorp/aws"
|
|
6
|
+
version = "~> 5.0"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
provider "aws" {
|
|
12
|
+
region = "us-east-1"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resource "aws_instance" "web" {
|
|
16
|
+
ami = "ami-123"
|
|
17
|
+
}
|