@intentius/chant-lexicon-terraform 0.101.0 → 0.102.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.
Files changed (114) hide show
  1. package/dist/codegen/package.d.ts +1 -1
  2. package/dist/config.d.ts +3 -3
  3. package/dist/hcl/jsonencode.d.ts +106 -0
  4. package/dist/hcl/jsonencode.d.ts.map +1 -0
  5. package/dist/hcl/marker.d.ts.map +1 -1
  6. package/dist/integrity.json +11 -2
  7. package/dist/lint/audit-catalog.d.ts.map +1 -1
  8. package/dist/lint/audit-lineage.d.ts +1 -1
  9. package/dist/lint/audit-lineage.d.ts.map +1 -1
  10. package/dist/lint/post-synth/flat-attributes.d.ts +69 -0
  11. package/dist/lint/post-synth/flat-attributes.d.ts.map +1 -0
  12. package/dist/lint/post-synth/index.d.ts.map +1 -1
  13. package/dist/lint/post-synth/tf030.d.ts +51 -0
  14. package/dist/lint/post-synth/tf030.d.ts.map +1 -0
  15. package/dist/lint/post-synth/tf031.d.ts +55 -0
  16. package/dist/lint/post-synth/tf031.d.ts.map +1 -0
  17. package/dist/lint/post-synth/tf032.d.ts +71 -0
  18. package/dist/lint/post-synth/tf032.d.ts.map +1 -0
  19. package/dist/lint/post-synth/tf033.d.ts +50 -0
  20. package/dist/lint/post-synth/tf033.d.ts.map +1 -0
  21. package/dist/lint/post-synth/tf034.d.ts +25 -0
  22. package/dist/lint/post-synth/tf034.d.ts.map +1 -0
  23. package/dist/lint/post-synth/tf035.d.ts +33 -0
  24. package/dist/lint/post-synth/tf035.d.ts.map +1 -0
  25. package/dist/lint/post-synth/tf036.d.ts +33 -0
  26. package/dist/lint/post-synth/tf036.d.ts.map +1 -0
  27. package/dist/lint/post-synth/tf037.d.ts +31 -0
  28. package/dist/lint/post-synth/tf037.d.ts.map +1 -0
  29. package/dist/manifest.json +1 -1
  30. package/dist/okf/index.md +8 -0
  31. package/dist/okf/rules/TF030.md +11 -0
  32. package/dist/okf/rules/TF031.md +11 -0
  33. package/dist/okf/rules/TF032.md +11 -0
  34. package/dist/okf/rules/TF033.md +11 -0
  35. package/dist/okf/rules/TF034.md +11 -0
  36. package/dist/okf/rules/TF035.md +11 -0
  37. package/dist/okf/rules/TF036.md +11 -0
  38. package/dist/okf/rules/TF037.md +11 -0
  39. package/dist/op/activity-contracts.d.ts +18 -18
  40. package/dist/op/builders.d.ts.map +1 -1
  41. package/dist/rules/flat-attributes.ts +146 -0
  42. package/dist/rules/tf030.ts +290 -0
  43. package/dist/rules/tf031.ts +370 -0
  44. package/dist/rules/tf032.ts +226 -0
  45. package/dist/rules/tf033.ts +160 -0
  46. package/dist/rules/tf034.ts +63 -0
  47. package/dist/rules/tf035.ts +74 -0
  48. package/dist/rules/tf036.ts +119 -0
  49. package/dist/rules/tf037.ts +101 -0
  50. package/package.json +3 -3
  51. package/src/hcl/jsonencode.test.ts +226 -0
  52. package/src/hcl/jsonencode.ts +454 -0
  53. package/src/lint/audit-catalog.ts +148 -0
  54. package/src/lint/audit-lineage.ts +155 -1
  55. package/src/lint/post-synth/estate-2284.test.ts +68 -0
  56. package/src/lint/post-synth/fixtures/TF030/negative-rules.tf +45 -0
  57. package/src/lint/post-synth/fixtures/TF030/negative.tf +55 -0
  58. package/src/lint/post-synth/fixtures/TF030/not-determined-port.tf +28 -0
  59. package/src/lint/post-synth/fixtures/TF030/not-determined-standalone.tf +32 -0
  60. package/src/lint/post-synth/fixtures/TF030/not-determined-vpc-rule.tf +21 -0
  61. package/src/lint/post-synth/fixtures/TF030/not-determined.tf +39 -0
  62. package/src/lint/post-synth/fixtures/TF030/positive-ipv6.tf +44 -0
  63. package/src/lint/post-synth/fixtures/TF030/positive-mixed.tf +36 -0
  64. package/src/lint/post-synth/fixtures/TF030/positive-standalone.tf +25 -0
  65. package/src/lint/post-synth/fixtures/TF030/positive.tf +43 -0
  66. package/src/lint/post-synth/fixtures/TF031/negative.tf +89 -0
  67. package/src/lint/post-synth/fixtures/TF031/not-determined.tf +139 -0
  68. package/src/lint/post-synth/fixtures/TF031/positive-data-source.tf +57 -0
  69. package/src/lint/post-synth/fixtures/TF031/positive-heredoc.tf +149 -0
  70. package/src/lint/post-synth/fixtures/TF031/positive-jsonencode.tf +115 -0
  71. package/src/lint/post-synth/fixtures/TF032/negative-heredoc.tf +20 -0
  72. package/src/lint/post-synth/fixtures/TF032/negative-references.tf +214 -0
  73. package/src/lint/post-synth/fixtures/TF032/negative.tf +99 -0
  74. package/src/lint/post-synth/fixtures/TF032/positive-heredoc.tf +22 -0
  75. package/src/lint/post-synth/fixtures/TF032/positive-sidecar.tf +197 -0
  76. package/src/lint/post-synth/fixtures/TF032/positive.tf +54 -0
  77. package/src/lint/post-synth/fixtures/TF033/negative.tf +39 -0
  78. package/src/lint/post-synth/fixtures/TF033/not-determined-aurora.tf +25 -0
  79. package/src/lint/post-synth/fixtures/TF033/positive-absent.tf +93 -0
  80. package/src/lint/post-synth/fixtures/TF033/positive.tf +50 -0
  81. package/src/lint/post-synth/fixtures/TF034/negative.tf +24 -0
  82. package/src/lint/post-synth/fixtures/TF034/positive-absent.tf +36 -0
  83. package/src/lint/post-synth/fixtures/TF034/positive.tf +14 -0
  84. package/src/lint/post-synth/fixtures/TF035/negative-absent.tf +43 -0
  85. package/src/lint/post-synth/fixtures/TF035/negative.tf +54 -0
  86. package/src/lint/post-synth/fixtures/TF035/positive.tf +37 -0
  87. package/src/lint/post-synth/fixtures/TF036/negative.tf +61 -0
  88. package/src/lint/post-synth/fixtures/TF036/not-determined.tf +28 -0
  89. package/src/lint/post-synth/fixtures/TF036/positive.tf +38 -0
  90. package/src/lint/post-synth/fixtures/TF037/negative-exclusion.tf +63 -0
  91. package/src/lint/post-synth/fixtures/TF037/negative.tf +27 -0
  92. package/src/lint/post-synth/fixtures/TF037/not-determined.tf +27 -0
  93. package/src/lint/post-synth/fixtures/TF037/positive-absent.tf +15 -0
  94. package/src/lint/post-synth/fixtures/TF037/positive.tf +26 -0
  95. package/src/lint/post-synth/fixtures/estate-2284/clean/main.tf +132 -0
  96. package/src/lint/post-synth/fixtures/estate-2284/violating/main.tf +112 -0
  97. package/src/lint/post-synth/flat-attributes.ts +146 -0
  98. package/src/lint/post-synth/index.ts +16 -0
  99. package/src/lint/post-synth/tf030.test.ts +219 -0
  100. package/src/lint/post-synth/tf030.ts +290 -0
  101. package/src/lint/post-synth/tf031.test.ts +229 -0
  102. package/src/lint/post-synth/tf031.ts +370 -0
  103. package/src/lint/post-synth/tf032.test.ts +127 -0
  104. package/src/lint/post-synth/tf032.ts +226 -0
  105. package/src/lint/post-synth/tf033.test.ts +126 -0
  106. package/src/lint/post-synth/tf033.ts +160 -0
  107. package/src/lint/post-synth/tf034.test.ts +71 -0
  108. package/src/lint/post-synth/tf034.ts +63 -0
  109. package/src/lint/post-synth/tf035.test.ts +68 -0
  110. package/src/lint/post-synth/tf035.ts +74 -0
  111. package/src/lint/post-synth/tf036.test.ts +87 -0
  112. package/src/lint/post-synth/tf036.ts +119 -0
  113. package/src/lint/post-synth/tf037.test.ts +88 -0
  114. package/src/lint/post-synth/tf037.ts +101 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * TF036: an EBS volume does not ask for encryption (chant #2288, epic #2284).
3
+ *
4
+ * The Terraform counterpart of the aws lexicon's WAW028, written natively.
5
+ *
6
+ * Absence is not determined for this resource. `aws_ebs_volume.encrypted` is
7
+ * optional, computed and force-new (provider `internal/service/ec2/
8
+ * ebs_volume.go`, v6.67.0). Whether a volume created without it is encrypted
9
+ * depends on the Region's EBS encryption by default
10
+ * (https://docs.aws.amazon.com/ebs/latest/userguide/encryption-by-default.html),
11
+ * which is account state the parse cannot see, and a volume built from
12
+ * `snapshot_id` also follows its snapshot. So an unset `encrypted` emits one
13
+ * `info` diagnostic, not a finding.
14
+ *
15
+ * One configuration does settle absence: the volume `depends_on` an
16
+ * `aws_ebs_encryption_by_default` in the same module whose `enabled` is
17
+ * absent (the provider default is `true`) or literal `true`, on the same
18
+ * `provider` and `region`. The `depends_on` matters: without it Terraform may
19
+ * create the volume before it turns the default on, and the default "has no
20
+ * effect on existing EBS volumes".
21
+ *
22
+ * `encrypted = false` is a finding even though encryption by default would
23
+ * override it. The literal is wrong either way: with the default off the
24
+ * volume is unencrypted, and with it on AWS encrypts the volume anyway ("you
25
+ * cannot disable it for individual volumes"), the volume no longer matches
26
+ * the configuration, and because `encrypted` forces replacement Terraform
27
+ * plans to replace it on every apply.
28
+ *
29
+ * Scope: root and child modules alike (#2112).
30
+ */
31
+ import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
32
+ export declare const tf036: PostSynthCheck;
33
+ //# sourceMappingURL=tf036.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tf036.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/tf036.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AA0B9G,eAAO,MAAM,KAAK,EAAE,cA6DnB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * TF037: an ECR repository lets an image tag be repointed (chant #2288, epic
3
+ * #2284).
4
+ *
5
+ * The Terraform counterpart of the aws lexicon's WAW054, written natively.
6
+ *
7
+ * Absence is insecure for this resource, and the rule reports it.
8
+ * `aws_ecr_repository.image_tag_mutability` (provider
9
+ * `internal/service/ecr/repository.go`, v6.67.0) is optional with
10
+ * `Default: MUTABLE`, so a repository that does not set it lets anyone with
11
+ * push access move a tag (`prod`, `v1.4.2`) to different image content after
12
+ * it was deployed, scanned or signed.
13
+ *
14
+ * Provider v6 accepts four values (AWS added the two `_WITH_EXCLUSION` modes
15
+ * in 2025), and the rule reads them as follows:
16
+ *
17
+ * - `IMMUTABLE`: secure.
18
+ * - `IMMUTABLE_WITH_EXCLUSION`: secure. Tags matching an
19
+ * `image_tag_mutability_exclusion_filter` stay mutable, a written-down
20
+ * exception such as `latest`. A filter that is nothing but `*` excludes
21
+ * every tag, so it is a finding; a filter from an expression is not
22
+ * determined.
23
+ * - `MUTABLE`, absent, and `MUTABLE_WITH_EXCLUSION` (every tag mutable except
24
+ * those the filters name): a finding.
25
+ * - an expression: not determined.
26
+ *
27
+ * Scope: root and child modules alike (#2112).
28
+ */
29
+ import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
30
+ export declare const tf037: PostSynthCheck;
31
+ //# sourceMappingURL=tf037.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tf037.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/tf037.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAmC9G,eAAO,MAAM,KAAK,EAAE,cAoCnB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terraform",
3
- "version": "0.101.0",
3
+ "version": "0.102.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Terraform"
6
6
  }
package/dist/okf/index.md CHANGED
@@ -32,4 +32,12 @@ okf_version: '0.2'
32
32
  * [TF027](/rules/TF027.md) - Live root's policy block sets undeclared_untagged = "delete", which deletes resources chant does not own
33
33
  * [TF028](/rules/TF028.md) - A live root is watched by a TerraformWatchOp built without live: true
34
34
  * [TF029](/rules/TF029.md) - Live root names its estate both in chant.config and in its own HCL
35
+ * [TF030](/rules/TF030.md) - Security group rule allows unrestricted ingress on a sensitive port
36
+ * [TF031](/rules/TF031.md) - IAM policy allows a wildcard Action or Resource
37
+ * [TF032](/rules/TF032.md) - ECS container definition passes a credential as a plaintext environment value
38
+ * [TF033](/rules/TF033.md) - RDS DB instance or cluster storage is not encrypted
39
+ * [TF034](/rules/TF034.md) - SNS topic has no server-side encryption
40
+ * [TF035](/rules/TF035.md) - SQS queue turns server-side encryption off
41
+ * [TF036](/rules/TF036.md) - EBS volume does not ask for encryption
42
+ * [TF037](/rules/TF037.md) - ECR repository allows mutable image tags
35
43
  * [TF101](/rules/TF101.md) - terraformApply's planFile must reference a preceding terraformPlan step.
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF030
4
+ description: Security group rule allows unrestricted ingress on a sensitive port
5
+ id: TF030
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ Security group rule allows unrestricted ingress on a sensitive port
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF031
4
+ description: IAM policy allows a wildcard Action or Resource
5
+ id: TF031
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ IAM policy allows a wildcard Action or Resource
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF032
4
+ description: ECS container definition passes a credential as a plaintext environment value
5
+ id: TF032
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ ECS container definition passes a credential as a plaintext environment value
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF033
4
+ description: RDS DB instance or cluster storage is not encrypted
5
+ id: TF033
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ RDS DB instance or cluster storage is not encrypted
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF034
4
+ description: SNS topic has no server-side encryption
5
+ id: TF034
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ SNS topic has no server-side encryption
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF035
4
+ description: SQS queue turns server-side encryption off
5
+ id: TF035
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ SQS queue turns server-side encryption off
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF036
4
+ description: EBS volume does not ask for encryption
5
+ id: TF036
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ EBS volume does not ask for encryption
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: post-synth-check
3
+ title: TF037
4
+ description: ECR repository allows mutable image tags
5
+ id: TF037
6
+ severity: error
7
+ category: post-synth
8
+ lexicon: terraform
9
+ docs: https://intentius.io/chant/lexicons/terraform/rules/
10
+ ---
11
+ ECR repository allows mutable image tags
@@ -31,60 +31,63 @@
31
31
  */
32
32
  export declare const terraformInitContract: import("@intentius/chant/op").ActivityContract<{
33
33
  root: string;
34
+ cwd?: string | undefined;
34
35
  upgrade?: boolean | undefined;
35
36
  reconfigure?: boolean | undefined;
36
- cwd?: string | undefined;
37
37
  }, {
38
38
  dir: string;
39
39
  workspace?: string | undefined;
40
40
  }>;
41
41
  export declare const terraformPlanContract: import("@intentius/chant/op").ActivityContract<{
42
42
  root: string;
43
+ cwd?: string | undefined;
43
44
  planFile?: string | undefined;
44
45
  destroy?: boolean | undefined;
45
- cwd?: string | undefined;
46
46
  }, {
47
+ adds: number;
48
+ changes: number;
49
+ destroys: number;
47
50
  changed: boolean;
48
51
  planFile: string;
49
52
  dir: string;
50
53
  json: unknown;
51
54
  text: string;
52
55
  planDigest: string;
53
- adds: number;
54
- changes: number;
55
- destroys: number;
56
56
  }>;
57
57
  export declare const terraformApplyContract: import("@intentius/chant/op").ActivityContract<{
58
58
  root: string;
59
- planFile?: string | undefined;
60
59
  cwd?: string | undefined;
60
+ planFile?: string | undefined;
61
61
  }, {
62
+ planFile?: string | undefined;
62
63
  dir: string;
63
64
  applied: boolean;
64
- planFile?: string | undefined;
65
65
  refused?: "approval-mismatch" | "wrong-estate" | undefined;
66
66
  refusal?: string | undefined;
67
67
  }>;
68
68
  export declare const terraformShowContract: import("@intentius/chant/op").ActivityContract<{
69
69
  root: string;
70
- planFile?: string | undefined;
71
70
  cwd?: string | undefined;
71
+ planFile?: string | undefined;
72
72
  }, {
73
+ adds: number;
74
+ changes: number;
75
+ destroys: number;
73
76
  source: "plan" | "state";
74
77
  json: unknown;
75
78
  text: string;
76
79
  dir: string;
77
- adds: number;
78
- changes: number;
79
- destroys: number;
80
80
  planFile?: string | undefined;
81
81
  }>;
82
82
  export declare const choudoufuLivePlanContract: import("@intentius/chant/op").ActivityContract<{
83
83
  root: string;
84
+ cwd?: string | undefined;
84
85
  estate?: string | undefined;
85
86
  adoptionOnly?: boolean | undefined;
86
- cwd?: string | undefined;
87
87
  }, {
88
+ adds: number;
89
+ changes: number;
90
+ destroys: number;
88
91
  unowned: number;
89
92
  adoptable: number;
90
93
  drift: boolean;
@@ -102,15 +105,12 @@ export declare const choudoufuLivePlanContract: import("@intentius/chant/op").Ac
102
105
  dir: string;
103
106
  documentPath: string;
104
107
  estate: string;
105
- adds: number;
106
- changes: number;
107
- destroys: number;
108
108
  }>;
109
109
  export declare const choudoufuLiveLsContract: import("@intentius/chant/op").ActivityContract<{
110
110
  root: string;
111
+ cwd?: string | undefined;
111
112
  estate?: string | undefined;
112
113
  consistent?: boolean | undefined;
113
- cwd?: string | undefined;
114
114
  }, {
115
115
  json: unknown;
116
116
  dir: string;
@@ -126,14 +126,14 @@ export declare const choudoufuLiveCheckContract: import("@intentius/chant/op").A
126
126
  dir: string;
127
127
  }>;
128
128
  export declare const choudoufuAdoptContract: import("@intentius/chant/op").ActivityContract<{
129
+ root: string;
130
+ cwd?: string | undefined;
129
131
  adoptions: {
130
132
  [x: string]: unknown;
131
133
  }[];
132
- root: string;
133
134
  contested?: {
134
135
  [x: string]: unknown;
135
136
  }[] | undefined;
136
- cwd?: string | undefined;
137
137
  }, {
138
138
  mechanism: "tag-write";
139
139
  adopted: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAEhC,kFAAkF;AAClF,KAAK,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC/D,iBAGF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAClE,iBAOF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAChE,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KACnE,iBAOF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,MAAM,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAEhC,kFAAkF;AAClF,KAAK,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAGF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC/D,iBAGF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,SACtB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAClE,iBAOF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,SACpB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAChE,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SACvB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KACnE,iBAOF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,QACN,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,SACL,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9D,iBAOF,CAAC"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Shared reading helpers for TF033-TF037, the five flat encryption and
3
+ * immutability rules (chant #2288, epic #2284).
4
+ *
5
+ * Each of those rules reads one or two scalar attributes of one resource type
6
+ * and answers a three-valued question: the literal proves the resource
7
+ * secure, the literal proves it insecure, or the parse cannot tell. This
8
+ * module is the reading half; each rule file holds its own answer for
9
+ * absence, because that answer differs per resource (see each rule's doc
10
+ * comment and its section of `docs/pages/lint-rules.mdx`). It exports no
11
+ * `PostSynthCheck`, so the generated barrel skips it.
12
+ */
13
+
14
+ import type { PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
15
+ import { RESOURCE_TYPE, type BlockBody } from "../../hcl/parse";
16
+ import { attr } from "../../hcl/value";
17
+ import { blocksOfType, nestedBodies, type TerraformBlock } from "./blocks";
18
+
19
+ /** What one attribute read settles. `reason` says why a value could not be read. */
20
+ export type Read<T> = { kind: "known"; value: T } | { kind: "absent" } | { kind: "unknown"; reason: string };
21
+
22
+ /** Every `resource` block of one provider type (`aws_sqs_queue`), in entity order. */
23
+ export function resourcesOfType(ctx: PostSynthContext, type: string): TerraformBlock[] {
24
+ return blocksOfType(ctx, RESOURCE_TYPE).filter((b) => b.address.startsWith(`${type}.`));
25
+ }
26
+
27
+ /** One expression, for a message: `var.encrypted`, or the template text. */
28
+ function describe(name: string, raw: unknown, refs: string[] | undefined, kind: "reference" | "template"): string {
29
+ return kind === "reference"
30
+ ? `\`${name}\` is an expression (${refs?.[0] ?? String(raw)})`
31
+ : `\`${name}\` is a template string ("${String(raw)}")`;
32
+ }
33
+
34
+ /**
35
+ * A bool attribute. Terraform converts the strings `"true"` and `"false"` to
36
+ * bool, so those read as the bool they convert to. Any other literal (a
37
+ * number, a list) is a plan-time type error, not this rule's concern, and
38
+ * reads as unknown.
39
+ */
40
+ export function readBool(body: BlockBody, name: string): Read<boolean> {
41
+ const a = attr(body, name);
42
+ if (a.kind === "absent") return { kind: "absent" };
43
+ if (a.kind === "reference" || a.kind === "template") {
44
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, a.kind) };
45
+ }
46
+ if (typeof a.value === "boolean") return { kind: "known", value: a.value };
47
+ if (a.value === "true" || a.value === "false") return { kind: "known", value: a.value === "true" };
48
+ return { kind: "unknown", reason: `\`${name}\` is not a bool (${JSON.stringify(a.value)})` };
49
+ }
50
+
51
+ /** A string attribute, literal or not. */
52
+ export function readString(body: BlockBody, name: string): Read<string> {
53
+ const a = attr(body, name);
54
+ if (a.kind === "absent") return { kind: "absent" };
55
+ if (a.kind === "reference" || a.kind === "template") {
56
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, a.kind) };
57
+ }
58
+ if (typeof a.value === "string") return { kind: "known", value: a.value };
59
+ return { kind: "unknown", reason: `\`${name}\` is not a string (${JSON.stringify(a.value)})` };
60
+ }
61
+
62
+ /** Expression roots that name an input or a computed value, not a managed object. */
63
+ const VALUE_ROOTS = new Set(["var", "local", "module", "each", "count", "path", "terraform", "self"]);
64
+
65
+ /** `aws_kms_key.sns.arn`, `data.aws_kms_alias.sqs.target_key_arn`, with an optional index or splat. */
66
+ const OBJECT_ATTRIBUTE = /^(?:data\.)?([a-z][a-z0-9_]*)\.[A-Za-z_][\w-]*(?:\[[^\]]*\])?(?:\.[A-Za-z_][\w-]*|\[[^\]]*\])+$/;
67
+
68
+ /** Every `${...}` span in a template, as `attr()` finds them. */
69
+ const SPANS = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
70
+
71
+ /**
72
+ * Whether a key-like argument (`kms_master_key_id`) is set to something.
73
+ *
74
+ * - absent, or `null`: `absent`.
75
+ * - a literal: `known`, with the string (`""` is a value, and a rule decides
76
+ * what an empty key means).
77
+ * - an attribute of a managed resource or data source (`aws_kms_key.x.arn`):
78
+ * `known` and non-empty. The provider computes a key's id or ARN; it is
79
+ * never the empty string, so the key is set whatever its value turns out to
80
+ * be.
81
+ * - a template with literal text (`"alias/${var.env}-sns"`): `known` and
82
+ * non-empty, for the same reason.
83
+ * - anything else (`var.kms_key`, `local.key`, a conditional, a function
84
+ * call): `unknown`. A variable can be `null` or `""`.
85
+ */
86
+ export function readKeySet(body: BlockBody, name: string): Read<string> {
87
+ const a = attr(body, name);
88
+ if (a.kind === "absent") return { kind: "absent" };
89
+ if (a.kind === "literal") {
90
+ return typeof a.value === "string"
91
+ ? { kind: "known", value: a.value }
92
+ : { kind: "unknown", reason: `\`${name}\` is not a string (${JSON.stringify(a.value)})` };
93
+ }
94
+ const raw = String(a.raw);
95
+ if (a.kind === "template") {
96
+ if (raw.replace(SPANS, "").length > 0) return { kind: "known", value: raw };
97
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, "template") };
98
+ }
99
+ const expr = (a.refs?.[0] ?? "").trim();
100
+ const m = OBJECT_ATTRIBUTE.exec(expr);
101
+ if (m && !VALUE_ROOTS.has(m[1])) return { kind: "known", value: raw };
102
+ return { kind: "unknown", reason: describe(name, a.raw, a.refs, "reference") };
103
+ }
104
+
105
+ /**
106
+ * Whether an argument that names a source (`snapshot_identifier`,
107
+ * `replicate_source_db`) or a nested block (`restore_to_point_in_time`) is
108
+ * given. The provider reads these with `GetOk`, so a literal `""` is the same
109
+ * as leaving the argument out; anything else, a reference included, is given.
110
+ */
111
+ export function isGiven(body: BlockBody, name: string): boolean {
112
+ const a = attr(body, name);
113
+ if (a.kind === "absent") return false;
114
+ if (a.kind === "literal") {
115
+ if (a.value === "" || a.value === false) return false;
116
+ if (Array.isArray(a.value)) return a.value.length > 0;
117
+ }
118
+ return true;
119
+ }
120
+
121
+ /** The bodies of a nested block, re-exported for the rule files. */
122
+ export { nestedBodies };
123
+
124
+ /** A finding the literal proves. */
125
+ export function finding(
126
+ checkId: string,
127
+ severity: "error" | "warning",
128
+ block: TerraformBlock,
129
+ message: string,
130
+ ): PostSynthDiagnostic {
131
+ return { checkId, severity, message: `"${block.address}" ${message}`, entity: block.key, lexicon: "terraform" };
132
+ }
133
+
134
+ /**
135
+ * The one `info` diagnostic a rule emits when it cannot read the value it
136
+ * needs. The message starts `Not determined:`, as #2284 fixed for TF030-TF037.
137
+ */
138
+ export function notDetermined(checkId: string, block: TerraformBlock, reason: string, tail: string): PostSynthDiagnostic {
139
+ return {
140
+ checkId,
141
+ severity: "info",
142
+ message: `Not determined: "${block.address}" ${reason}. ${tail}`,
143
+ entity: block.key,
144
+ lexicon: "terraform",
145
+ };
146
+ }