@intentius/chant-lexicon-terraform 0.105.1 → 0.106.1

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "9bcc2f2626fed67d9687c013e667c8a648f5a0af7cc46b256145f84472b12c3c",
4
+ "manifest.json": "88dd9c9a9374f8880f3ec2028254efa12d37a931f7ab2dd40313a08687844670",
5
5
  "meta.json": "ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
6
6
  "types/index.d.ts": "022927e6a7b351cc4cf52d5f973f60ccd140e5810e17b32fb8868965aa1db161",
7
7
  "rules/plan-before-apply.ts": "31b1b3176799fd365b2583fe3947edd187c2a863e3dc0094ed9e91ece2464c99",
@@ -31,7 +31,7 @@
31
31
  "rules/tf019.ts": "ae1841a358e5b7a3f42c484f0b5cf1c1f5aa845a50516969a46d65b13e282951",
32
32
  "rules/tf020.ts": "2210815c355e37459c34de0fe622f0b87b78010ee7c444135cf5abf7e048adf7",
33
33
  "rules/tf021.ts": "ad3cd813a289437d33da866927697077e7a5b20f3c05341325193d286b57bbd5",
34
- "rules/tf022.ts": "87483762b02e947aba94ec9b79fb9bf4dd6442287d602836306bc66b4a239a35",
34
+ "rules/tf022.ts": "5466797cd88fd5489f6c5abd1e796d7c5d3d0e977d0e9e043d253f6ecf369cdc",
35
35
  "rules/tf024.ts": "c0c101f913bd3d0fdb5403ee57888e80d8fc612e363f0eb43d7ae759ac1d5651",
36
36
  "rules/tf025.ts": "0c31be3885e940e2b3e9ac8e023067d8d6e2b42c04f628890405c9fc6db7d1ce",
37
37
  "rules/tf026.ts": "8cf1e61bc9563aa2d3c149f243ffa02ad4f183eebb672f861cd6c4e6fba1d310",
@@ -56,5 +56,5 @@
56
56
  "rules/tf045.ts": "760b1f4f385fbe7a86897efe3078fdeac11bcc9bfb806285d63db50488174b7b",
57
57
  "skills/chant-terraform.md": "d9ca7b7c28aed0038ce775a3491150298492eed2858fd3651ed9bfca0e577ae8"
58
58
  },
59
- "composite": "d5a10541b312d0d4af01f0fef77394bcbaae24b5ad2a6b973dd189ec237c6c5f"
59
+ "composite": "88c99f7360817f2de33f2eef527c4778e223db4679dd3cebde3649dd53652631"
60
60
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terraform",
3
- "version": "0.105.1",
3
+ "version": "0.106.1",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Terraform"
6
6
  }
@@ -50,7 +50,7 @@ export const tf022: PostSynthCheck = {
50
50
  "characters, redacted here). The value is in git history, in state, and in plan output. " +
51
51
  "Replace it with a reference: a `sensitive` variable, or a data source that reads the " +
52
52
  "value from a secret manager at apply time.",
53
- entity: `${block.key}.${attr.path}`,
53
+ entity: block.key,
54
54
  lexicon: "terraform",
55
55
  });
56
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-terraform",
3
- "version": "0.105.1",
3
+ "version": "0.106.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/",
@@ -67,7 +67,7 @@
67
67
  "access": "public"
68
68
  },
69
69
  "peerDependencies": {
70
- "@intentius/chant": "^0.105.1",
70
+ "@intentius/chant": "^0.106.1",
71
71
  "typescript": "^5.9.3",
72
72
  "zod": "^4.3.6"
73
73
  },
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * The plans are the real fixtures the adapter tests pin: chant's `lifecycle
10
10
  * plan --json` against floci (`packages/core/src/__fixtures__/change-set`),
11
- * choudoufu 0.16.0's `show -json` and the warden's reconcile change set. The
11
+ * choudoufu 0.16.0's `show -json` and the change sets github-warden's
12
+ * `reconcile --plan-json` wrote (`warden.plan.json`, github-warden#66). The
12
13
  * two planner activities that need a cloud are stand-ins returning what the
13
14
  * real ones return for those plans. `composeChangeSet` and
14
15
  * `readChangeSetPart` are the real activities. The scenario is #2300's on a
@@ -56,3 +56,29 @@ describe("changedRoots", () => {
56
56
  expect(changedRoots(project, ROOTS, ["README.md", "roots/netsplit/main.tf"])).toEqual([]);
57
57
  });
58
58
  });
59
+
60
+ describe("changedRoots in a workspace member (#3465)", () => {
61
+ // The project is a member at infra/network. Its root calls a module that
62
+ // lives outside the member, and the changed files come relative to the
63
+ // member, so a file outside it reads `../...`.
64
+ const repo = mkdtempSync(join(tmpdir(), "chant-changed-roots-member-"));
65
+ afterAll(() => rmSync(repo, { recursive: true, force: true }));
66
+ const put = (path: string, text: string): void => {
67
+ mkdirSync(dirname(join(repo, path)), { recursive: true });
68
+ writeFileSync(join(repo, path), text);
69
+ };
70
+ put("infra/network/roots/vpc/main.tf", 'module "base" {\n source = "../../../../shared/modules/base"\n}\n');
71
+ put("shared/modules/base/main.tf", "");
72
+ put("apps/web/main.tf", "");
73
+ const member = join(repo, "infra/network");
74
+ const roots = { vpc: { dir: "roots/vpc" } };
75
+
76
+ test("a module outside the member is watched by a path that leaves it", () => {
77
+ expect(rootWatchPaths(member, roots.vpc)).toEqual(["../../shared/modules/base", "roots/vpc"]);
78
+ });
79
+
80
+ test("a change to that module touches the root; a change in another member touches nothing", () => {
81
+ expect(changedRoots(member, roots, ["../../shared/modules/base/main.tf"])).toEqual(["vpc"]);
82
+ expect(changedRoots(member, roots, ["../../apps/web/main.tf"])).toEqual([]);
83
+ });
84
+ });
@@ -243,9 +243,10 @@ describe("TF022: credential-named resource attribute holding a literal", () => {
243
243
  test("flags the plaintext password", async () => {
244
244
  const diags = tf022.check(await loadFixture("TF022", "positive"));
245
245
  expect(diags).toHaveLength(1);
246
- expect(diags[0].entity).toBe("TF022/aws_db_instance.app.password");
246
+ expect(diags[0].entity).toBe("TF022/aws_db_instance.app");
247
247
  expect(diags[0].message).not.toContain("Pr0dDbP4ssw0rd");
248
248
  expect(diags[0].message).toContain("reference");
249
+ expect(diags[0].message).toContain("`password`");
249
250
  });
250
251
 
251
252
  test("passes a password read from a data source", async () => {
@@ -254,7 +255,8 @@ describe("TF022: credential-named resource attribute holding a literal", () => {
254
255
 
255
256
  test("descends into a nested block", async () => {
256
257
  const diags = tf022.check(await loadFixture("TF022", "positive-nested"));
257
- expect(diags.map((d) => d.entity)).toEqual(["TF022/aws_instance.web.connection.password"]);
258
+ expect(diags.map((d) => d.entity)).toEqual(["TF022/aws_instance.web"]);
259
+ expect(diags[0].message).toContain("`connection.password`");
258
260
  });
259
261
 
260
262
  test("a locator attribute is the remediation, not the finding", async () => {
@@ -50,7 +50,7 @@ export const tf022: PostSynthCheck = {
50
50
  "characters, redacted here). The value is in git history, in state, and in plan output. " +
51
51
  "Replace it with a reference: a `sensitive` variable, or a data source that reads the " +
52
52
  "value from a secret manager at apply time.",
53
- entity: `${block.key}.${attr.path}`,
53
+ entity: block.key,
54
54
  lexicon: "terraform",
55
55
  });
56
56
  }
@@ -102,6 +102,24 @@ describe("the shared normalization vectors", () => {
102
102
  expect(grouped).toEqual([]);
103
103
  });
104
104
 
105
+ test("red first: keeping a sensitive value or a read's result splits the sensitive and read must-group rows", () => {
106
+ const keepValues = (root: Json): Json => {
107
+ const r = clone(root);
108
+ for (const rc of r.plan.resource_changes) {
109
+ delete rc.change.before_sensitive;
110
+ delete rc.change.after_sensitive;
111
+ if (rc.change.actions.length === 1 && rc.change.actions[0] === "read") rc.change.actions = ["create"];
112
+ }
113
+ return r;
114
+ };
115
+ const rows = vectors.rootPairs.filter((r) => r.group && ["sensitive attribute with different values", "read entries whose results differ"].includes(r.name));
116
+ expect(rows.map((r) => r.name).sort()).toEqual(["read entries whose results differ", "sensitive attribute with different values"]);
117
+ for (const row of rows) {
118
+ expect(rootsGroup(row.a, row.b)).toBe(true);
119
+ expect(rootsGroup(keepValues(row.a), keepValues(row.b))).toBe(false);
120
+ }
121
+ });
122
+
105
123
  test("red first: blanking every string falsely groups exactly the named must-not pairs", () => {
106
124
  const falselyGrouped = vectors.rootPairs.filter((r) => !r.group && rootsGroup(blankStrings(r.a), blankStrings(r.b))).map((r) => r.name);
107
125
  expect(falselyGrouped.sort()).toEqual([...vectors.overEagerFalselyGroups].sort());