@intentius/chant-lexicon-terraform 0.100.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.
- package/dist/codegen/package.d.ts +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/hcl/jsonencode.d.ts +106 -0
- package/dist/hcl/jsonencode.d.ts.map +1 -0
- package/dist/hcl/marker.d.ts.map +1 -1
- package/dist/integrity.json +11 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts +1 -1
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/flat-attributes.d.ts +69 -0
- package/dist/lint/post-synth/flat-attributes.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/tf030.d.ts +51 -0
- package/dist/lint/post-synth/tf030.d.ts.map +1 -0
- package/dist/lint/post-synth/tf031.d.ts +55 -0
- package/dist/lint/post-synth/tf031.d.ts.map +1 -0
- package/dist/lint/post-synth/tf032.d.ts +71 -0
- package/dist/lint/post-synth/tf032.d.ts.map +1 -0
- package/dist/lint/post-synth/tf033.d.ts +50 -0
- package/dist/lint/post-synth/tf033.d.ts.map +1 -0
- package/dist/lint/post-synth/tf034.d.ts +25 -0
- package/dist/lint/post-synth/tf034.d.ts.map +1 -0
- package/dist/lint/post-synth/tf035.d.ts +33 -0
- package/dist/lint/post-synth/tf035.d.ts.map +1 -0
- package/dist/lint/post-synth/tf036.d.ts +33 -0
- package/dist/lint/post-synth/tf036.d.ts.map +1 -0
- package/dist/lint/post-synth/tf037.d.ts +31 -0
- package/dist/lint/post-synth/tf037.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +8 -0
- package/dist/okf/rules/TF030.md +11 -0
- package/dist/okf/rules/TF031.md +11 -0
- package/dist/okf/rules/TF032.md +11 -0
- package/dist/okf/rules/TF033.md +11 -0
- package/dist/okf/rules/TF034.md +11 -0
- package/dist/okf/rules/TF035.md +11 -0
- package/dist/okf/rules/TF036.md +11 -0
- package/dist/okf/rules/TF037.md +11 -0
- package/dist/op/activity-contracts.d.ts +18 -18
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/rules/flat-attributes.ts +146 -0
- package/dist/rules/tf030.ts +290 -0
- package/dist/rules/tf031.ts +370 -0
- package/dist/rules/tf032.ts +226 -0
- package/dist/rules/tf033.ts +160 -0
- package/dist/rules/tf034.ts +63 -0
- package/dist/rules/tf035.ts +74 -0
- package/dist/rules/tf036.ts +119 -0
- package/dist/rules/tf037.ts +101 -0
- package/package.json +3 -3
- package/src/hcl/jsonencode.test.ts +226 -0
- package/src/hcl/jsonencode.ts +454 -0
- package/src/lint/audit-catalog.ts +148 -0
- package/src/lint/audit-lineage.ts +155 -1
- package/src/lint/post-synth/estate-2284.test.ts +68 -0
- package/src/lint/post-synth/fixtures/TF030/negative-rules.tf +45 -0
- package/src/lint/post-synth/fixtures/TF030/negative.tf +55 -0
- package/src/lint/post-synth/fixtures/TF030/not-determined-port.tf +28 -0
- package/src/lint/post-synth/fixtures/TF030/not-determined-standalone.tf +32 -0
- package/src/lint/post-synth/fixtures/TF030/not-determined-vpc-rule.tf +21 -0
- package/src/lint/post-synth/fixtures/TF030/not-determined.tf +39 -0
- package/src/lint/post-synth/fixtures/TF030/positive-ipv6.tf +44 -0
- package/src/lint/post-synth/fixtures/TF030/positive-mixed.tf +36 -0
- package/src/lint/post-synth/fixtures/TF030/positive-standalone.tf +25 -0
- package/src/lint/post-synth/fixtures/TF030/positive.tf +43 -0
- package/src/lint/post-synth/fixtures/TF031/negative.tf +89 -0
- package/src/lint/post-synth/fixtures/TF031/not-determined.tf +139 -0
- package/src/lint/post-synth/fixtures/TF031/positive-data-source.tf +57 -0
- package/src/lint/post-synth/fixtures/TF031/positive-heredoc.tf +149 -0
- package/src/lint/post-synth/fixtures/TF031/positive-jsonencode.tf +115 -0
- package/src/lint/post-synth/fixtures/TF032/negative-heredoc.tf +20 -0
- package/src/lint/post-synth/fixtures/TF032/negative-references.tf +214 -0
- package/src/lint/post-synth/fixtures/TF032/negative.tf +99 -0
- package/src/lint/post-synth/fixtures/TF032/positive-heredoc.tf +22 -0
- package/src/lint/post-synth/fixtures/TF032/positive-sidecar.tf +197 -0
- package/src/lint/post-synth/fixtures/TF032/positive.tf +54 -0
- package/src/lint/post-synth/fixtures/TF033/negative.tf +39 -0
- package/src/lint/post-synth/fixtures/TF033/not-determined-aurora.tf +25 -0
- package/src/lint/post-synth/fixtures/TF033/positive-absent.tf +93 -0
- package/src/lint/post-synth/fixtures/TF033/positive.tf +50 -0
- package/src/lint/post-synth/fixtures/TF034/negative.tf +24 -0
- package/src/lint/post-synth/fixtures/TF034/positive-absent.tf +36 -0
- package/src/lint/post-synth/fixtures/TF034/positive.tf +14 -0
- package/src/lint/post-synth/fixtures/TF035/negative-absent.tf +43 -0
- package/src/lint/post-synth/fixtures/TF035/negative.tf +54 -0
- package/src/lint/post-synth/fixtures/TF035/positive.tf +37 -0
- package/src/lint/post-synth/fixtures/TF036/negative.tf +61 -0
- package/src/lint/post-synth/fixtures/TF036/not-determined.tf +28 -0
- package/src/lint/post-synth/fixtures/TF036/positive.tf +38 -0
- package/src/lint/post-synth/fixtures/TF037/negative-exclusion.tf +63 -0
- package/src/lint/post-synth/fixtures/TF037/negative.tf +27 -0
- package/src/lint/post-synth/fixtures/TF037/not-determined.tf +27 -0
- package/src/lint/post-synth/fixtures/TF037/positive-absent.tf +15 -0
- package/src/lint/post-synth/fixtures/TF037/positive.tf +26 -0
- package/src/lint/post-synth/fixtures/estate-2284/clean/main.tf +132 -0
- package/src/lint/post-synth/fixtures/estate-2284/violating/main.tf +112 -0
- package/src/lint/post-synth/flat-attributes.ts +146 -0
- package/src/lint/post-synth/index.ts +16 -0
- package/src/lint/post-synth/tf030.test.ts +219 -0
- package/src/lint/post-synth/tf030.ts +290 -0
- package/src/lint/post-synth/tf031.test.ts +229 -0
- package/src/lint/post-synth/tf031.ts +370 -0
- package/src/lint/post-synth/tf032.test.ts +127 -0
- package/src/lint/post-synth/tf032.ts +226 -0
- package/src/lint/post-synth/tf033.test.ts +126 -0
- package/src/lint/post-synth/tf033.ts +160 -0
- package/src/lint/post-synth/tf034.test.ts +71 -0
- package/src/lint/post-synth/tf034.ts +63 -0
- package/src/lint/post-synth/tf035.test.ts +68 -0
- package/src/lint/post-synth/tf035.ts +74 -0
- package/src/lint/post-synth/tf036.test.ts +87 -0
- package/src/lint/post-synth/tf036.ts +119 -0
- package/src/lint/post-synth/tf037.test.ts +88 -0
- package/src/lint/post-synth/tf037.ts +101 -0
- package/src/op/activities/terraform.test.ts +2 -2
|
@@ -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"}
|
package/dist/manifest.json
CHANGED
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,
|
|
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
|
+
}
|