@intentius/chant-lexicon-aws 0.56.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.
Files changed (61) hide show
  1. package/dist/agentcore/trace-fetch.d.ts +5 -4
  2. package/dist/agentcore/trace-fetch.d.ts.map +1 -1
  3. package/dist/condition.d.ts +27 -0
  4. package/dist/condition.d.ts.map +1 -0
  5. package/dist/generated/index.d.ts +1 -1
  6. package/dist/generated/index.d.ts.map +1 -1
  7. package/dist/import/generator.d.ts +19 -0
  8. package/dist/import/generator.d.ts.map +1 -1
  9. package/dist/import/parser.d.ts +19 -0
  10. package/dist/import/parser.d.ts.map +1 -1
  11. package/dist/index.d.ts +3 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/integrity.json +4 -3
  14. package/dist/intrinsics.d.ts +77 -3
  15. package/dist/intrinsics.d.ts.map +1 -1
  16. package/dist/lint/audit-catalog.d.ts.map +1 -1
  17. package/dist/lint/audit-lineage.d.ts +10 -0
  18. package/dist/lint/audit-lineage.d.ts.map +1 -0
  19. package/dist/lint/post-synth/index.d.ts.map +1 -1
  20. package/dist/lint/post-synth/waw069.d.ts +15 -0
  21. package/dist/lint/post-synth/waw069.d.ts.map +1 -0
  22. package/dist/manifest.json +29 -1
  23. package/dist/okf/index.md +1 -0
  24. package/dist/okf/rules/WAW069.md +11 -0
  25. package/dist/op/activities/aws-apply.d.ts.map +1 -1
  26. package/dist/op/activities/floci.d.ts +3 -2
  27. package/dist/op/activities/floci.d.ts.map +1 -1
  28. package/dist/op/builders.d.ts +4 -4
  29. package/dist/plugin.d.ts.map +1 -1
  30. package/dist/rules/waw069.ts +0 -0
  31. package/dist/serializer.d.ts.map +1 -1
  32. package/dist/types/index.d.ts +3 -3
  33. package/package.json +2 -2
  34. package/src/agentcore/trace-fetch.ts +5 -4
  35. package/src/codegen/docs.ts +3 -3
  36. package/src/codegen/generate-typescript.ts +3 -3
  37. package/src/codegen/generate.ts +1 -1
  38. package/src/condition.test.ts +129 -0
  39. package/src/condition.ts +40 -0
  40. package/src/deep-observe.test.ts +23 -15
  41. package/src/deep-topology.test.ts +7 -3
  42. package/src/generated/index.d.ts +3 -3
  43. package/src/generated/index.ts +1 -1
  44. package/src/import/generator.ts +131 -50
  45. package/src/import/parser.test.ts +96 -0
  46. package/src/import/parser.ts +86 -1
  47. package/src/import/roundtrip-fixtures.test.ts +50 -0
  48. package/src/index.ts +15 -3
  49. package/src/intrinsics.ts +130 -5
  50. package/src/lint/audit-catalog.ts +6 -1
  51. package/src/lint/audit-lineage.ts +159 -0
  52. package/src/lint/post-synth/index.ts +2 -0
  53. package/src/lint/post-synth/waw069.test.ts +94 -0
  54. package/src/lint/post-synth/waw069.ts +0 -0
  55. package/src/op/activities/aws-apply.ts +1 -3
  56. package/src/op/activities/floci.ts +3 -2
  57. package/src/op/builders.ts +4 -4
  58. package/src/plugin.test.ts +5 -1
  59. package/src/plugin.ts +4 -0
  60. package/src/serializer.ts +24 -3
  61. package/src/testdata/roundtrip/with-conditions.json +16 -0
@@ -3,6 +3,8 @@ import type {
3
3
  TemplateIR,
4
4
  ResourceIR,
5
5
  ParameterIR,
6
+ ConditionIR,
7
+ OutputIR,
6
8
  } from "@intentius/chant/import/parser";
7
9
  import { BaseValueParser } from "@intentius/chant/import/base-parser";
8
10
  import yaml from "js-yaml";
@@ -114,8 +116,19 @@ interface CFTemplate {
114
116
  AWSTemplateFormatVersion?: string;
115
117
  Description?: string;
116
118
  Parameters?: Record<string, CFParameter>;
119
+ Conditions?: Record<string, unknown>;
117
120
  Resources?: Record<string, CFResource>;
118
- Outputs?: Record<string, unknown>;
121
+ Outputs?: Record<string, CFOutput>;
122
+ }
123
+
124
+ /**
125
+ * CloudFormation output
126
+ */
127
+ interface CFOutput {
128
+ Value: unknown;
129
+ Description?: string;
130
+ Export?: { Name?: unknown };
131
+ Condition?: string;
119
132
  }
120
133
 
121
134
  /**
@@ -135,6 +148,7 @@ interface CFResource {
135
148
  Properties?: Record<string, unknown>;
136
149
  Metadata?: Record<string, unknown>;
137
150
  DependsOn?: string | string[];
151
+ Condition?: string;
138
152
  }
139
153
 
140
154
  /**
@@ -156,11 +170,17 @@ export class CFParser extends BaseValueParser implements TemplateParser {
156
170
  const template = parsed as CFTemplate;
157
171
 
158
172
  const parameters = this.parseParameters(template.Parameters ?? {});
173
+ const conditions = this.parseConditions(template.Conditions ?? {});
159
174
  const resources = this.parseResources(template.Resources ?? {});
175
+ const outputs = this.parseOutputs(template.Outputs ?? {});
176
+ const warnings = this.collectDroppedSectionWarnings(template as unknown as Record<string, unknown>);
160
177
 
161
178
  return {
162
179
  parameters,
180
+ conditions: conditions.length > 0 ? conditions : undefined,
163
181
  resources,
182
+ outputs: outputs.length > 0 ? outputs : undefined,
183
+ warnings: warnings.length > 0 ? warnings : undefined,
164
184
  metadata: {
165
185
  version: template.AWSTemplateFormatVersion ?? "2010-09-09",
166
186
  description: template.Description,
@@ -168,6 +188,64 @@ export class CFParser extends BaseValueParser implements TemplateParser {
168
188
  };
169
189
  }
170
190
 
191
+ /**
192
+ * Template sections import carries. Anything else is named in a warning
193
+ * rather than dropped silently (#2069).
194
+ */
195
+ private static readonly CARRIED_SECTIONS = new Set([
196
+ "AWSTemplateFormatVersion",
197
+ "Description",
198
+ "Parameters",
199
+ "Conditions",
200
+ "Resources",
201
+ "Outputs",
202
+ ]);
203
+
204
+ private collectDroppedSectionWarnings(template: Record<string, unknown>): string[] {
205
+ const warnings: string[] = [];
206
+ for (const key of Object.keys(template)) {
207
+ if (!CFParser.CARRIED_SECTIONS.has(key)) {
208
+ warnings.push(`Template section "${key}" is not carried by import — it is dropped from the generated source`);
209
+ }
210
+ }
211
+ return warnings;
212
+ }
213
+
214
+ /**
215
+ * Parse the Conditions section (#2069). Inside a condition expression the
216
+ * single-key `{ "Condition": "<name>" }` form references another declared
217
+ * condition; `inConditionExpression` scopes that dispatch to this section
218
+ * so a resource property that happens to hold a single-key `Condition`
219
+ * object is left alone.
220
+ */
221
+ private inConditionExpression = false;
222
+
223
+ private parseConditions(conditions: Record<string, unknown>): ConditionIR[] {
224
+ return Object.entries(conditions).map(([name, expression]) => {
225
+ this.inConditionExpression = true;
226
+ try {
227
+ return { name, expression: this.parseValue(expression) };
228
+ } finally {
229
+ this.inConditionExpression = false;
230
+ }
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Parse the Outputs section (#2069).
236
+ */
237
+ private parseOutputs(outputs: Record<string, CFOutput>): OutputIR[] {
238
+ return Object.entries(outputs)
239
+ .filter(([_, output]) => typeof output === "object" && output !== null)
240
+ .map(([name, output]) => ({
241
+ name,
242
+ value: this.parseValue(output.Value),
243
+ description: output.Description,
244
+ exportName: output.Export?.Name !== undefined ? this.parseValue(output.Export.Name) : undefined,
245
+ condition: typeof output.Condition === "string" ? output.Condition : undefined,
246
+ }));
247
+ }
248
+
171
249
  /**
172
250
  * Parse parameters section
173
251
  */
@@ -192,6 +270,7 @@ export class CFParser extends BaseValueParser implements TemplateParser {
192
270
  type: resource.Type,
193
271
  properties: this.parseProperties(resource.Properties ?? {}),
194
272
  metadata: resource.Metadata,
273
+ condition: typeof resource.Condition === "string" ? resource.Condition : undefined,
195
274
  }));
196
275
  }
197
276
 
@@ -212,6 +291,12 @@ export class CFParser extends BaseValueParser implements TemplateParser {
212
291
  * CFN-specific intrinsic dispatch table.
213
292
  */
214
293
  protected dispatchIntrinsic(key: string, value: unknown, _obj: Record<string, unknown>): unknown | null {
294
+ // `{ "Condition": "<name>" }` — only valid inside the Conditions section
295
+ // (#2069); see parseConditions for the scoping.
296
+ if (key === "Condition" && this.inConditionExpression && typeof value === "string") {
297
+ return { __intrinsic: "ConditionRef", name: value };
298
+ }
299
+
215
300
  if (key === "Ref") {
216
301
  return { __intrinsic: "Ref", name: value };
217
302
  }
@@ -112,6 +112,56 @@ describe("parameters.json build roundtrip", () => {
112
112
  });
113
113
  });
114
114
 
115
+ describe("with-conditions.json build roundtrip (#2069)", () => {
116
+ test("Conditions, Condition keys, and the conditioned output round-trip exactly", async () => {
117
+ const content = readFileSync(join(roundtripDir, "with-conditions.json"), "utf-8");
118
+ const source = JSON.parse(content);
119
+
120
+ const ir = parser.parse(content);
121
+ const files = generator.generate(ir);
122
+ const mainFile = files.find((f) => f.path === "main.ts")!;
123
+
124
+ // The generated source declares both conditions and keeps the references
125
+ expect(mainFile.content).toContain("new Condition(Equals(Ref(Cutover), \"true\"))");
126
+ expect(mainFile.content).toContain("new Condition(Not(DoCutover))");
127
+ expect(mainFile.content).toContain("{ Condition: DoCutover }");
128
+ expect(mainFile.content).toContain("stackOutput(Ref(Rule), { condition: DoCutover })");
129
+
130
+ const dir = mkdtempSync(join(import.meta.dirname, "../../.roundtrip-tmp-"));
131
+ try {
132
+ const srcDir = join(dir, "src");
133
+ mkdirSync(srcDir);
134
+ writeFileSync(join(srcDir, "main.ts"), mainFile.content);
135
+
136
+ const result = await build(srcDir, [awsSerializer]);
137
+ expect(result.errors).toHaveLength(0);
138
+
139
+ const template = JSON.parse(result.outputs.get("aws") as string);
140
+
141
+ // The Conditions section round-trips exactly, {Condition: ...} ref included
142
+ expect(template.Conditions).toEqual(source.Conditions);
143
+ // The resource keeps its Condition key and its Fn::If
144
+ expect(template.Resources.Rule.Condition).toBe("DoCutover");
145
+ expect(template.Resources.Rule.Properties.LogGroupName).toEqual(
146
+ source.Resources.Rule.Properties.LogGroupName,
147
+ );
148
+ // The conditioned output round-trips exactly
149
+ expect(template.Outputs).toEqual(source.Outputs);
150
+ } finally {
151
+ rmSync(dir, { recursive: true, force: true });
152
+ }
153
+ });
154
+
155
+ test("a section import cannot carry is named in a warning, never dropped silently", () => {
156
+ const template = JSON.parse(readFileSync(join(roundtripDir, "with-conditions.json"), "utf-8"));
157
+ template.Mappings = { RegionMap: { "us-east-1": { AMI: "ami-123" } } };
158
+ const ir = parser.parse(JSON.stringify(template));
159
+ expect(ir.warnings).toEqual([
160
+ 'Template section "Mappings" is not carried by import — it is dropped from the generated source',
161
+ ]);
162
+ });
163
+ });
164
+
115
165
  describe("SAM roundtrip fixtures", () => {
116
166
  const fixtures = readdirSync(samDir).filter(
117
167
  (f) => f.endsWith(".yaml") || f.endsWith(".yml"),
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  // Parameter
2
2
  export { Parameter } from "./parameter";
3
3
 
4
+ // Condition (#2068) — the Conditions-section declarable
5
+ export { Condition, isCondition, CONDITION_ENTITY_TYPE } from "./condition";
6
+
4
7
  // Default Tags
5
8
  export { defaultTags, isDefaultTags, DEFAULT_TAGS_MARKER } from "./default-tags";
6
9
  export type { DefaultTags, TagEntry } from "./default-tags";
@@ -25,9 +28,9 @@ export type { AwsReceiptStoreOptions, ReceiptRowObservation } from "./receipt-st
25
28
  // Typed Op step-builder wrappers (chant #1288 Stage 2) — awsApply/awsDelete/
26
29
  // flociUp/flociDown with authoring-time types derived from this lexicon's
27
30
  // own *Args interfaces (see ./op/builders.ts's module doc for why these live
28
- // here rather than in core or the temporal barrel). Opt-in:
29
- // `@intentius/chant-lexicon-temporal`'s same-named exports are core's
30
- // original untyped builders, unchanged, for cloud-agnostic authoring.
31
+ // here rather than in core). Opt-in: `@intentius/chant/op`'s same-named
32
+ // exports are core's original untyped builders, unchanged, for
33
+ // cloud-agnostic authoring.
31
34
  export { awsApply, awsDelete, flociUp, flociDown } from "./op/builders";
32
35
 
33
36
  // Serializer
@@ -105,6 +108,10 @@ export {
105
108
  Split,
106
109
  Base64,
107
110
  GetAZs,
111
+ Equals,
112
+ And,
113
+ Or,
114
+ Not,
108
115
  SubIntrinsic,
109
116
  RefIntrinsic,
110
117
  GetAttIntrinsic,
@@ -114,7 +121,12 @@ export {
114
121
  SplitIntrinsic,
115
122
  Base64Intrinsic,
116
123
  GetAZsIntrinsic,
124
+ EqualsIntrinsic,
125
+ AndIntrinsic,
126
+ OrIntrinsic,
127
+ NotIntrinsic,
117
128
  } from "./intrinsics";
129
+ export type { ConditionOperand } from "./intrinsics";
118
130
 
119
131
  // Pseudo-parameters
120
132
  export {
package/src/intrinsics.ts CHANGED
@@ -2,6 +2,25 @@ import { INTRINSIC_MARKER, resolveIntrinsicValue, isIntrinsic, type Intrinsic }
2
2
  import { buildInterpolatedString, defaultInterpolationSerializer } from "@intentius/chant/intrinsic-interpolation";
3
3
  import { type Declarable } from "@intentius/chant/declarable";
4
4
  import { getLogicalName } from "@intentius/chant/utils";
5
+ import { isCondition, type Condition } from "./condition";
6
+
7
+ /**
8
+ * An operand allowed where CloudFormation expects a condition (#2068): a
9
+ * nested condition intrinsic (`Equals`/`And`/`Or`/`Not`), the `Condition`
10
+ * declarable itself, or a condition name string. The latter two both emit
11
+ * the `{ "Condition": "<name>" }` reference form.
12
+ */
13
+ export type ConditionOperand = Intrinsic | Condition | string;
14
+
15
+ function resolveConditionOperand(operand: ConditionOperand): unknown {
16
+ if (typeof operand === "string") {
17
+ return { Condition: operand };
18
+ }
19
+ if (isCondition(operand)) {
20
+ return { Condition: getLogicalName(operand) };
21
+ }
22
+ return resolveIntrinsicValue(operand);
23
+ }
5
24
 
6
25
  /**
7
26
  * Fn::Sub intrinsic function implementation
@@ -99,29 +118,135 @@ export function GetAtt(logicalName: string, attribute: string): GetAttIntrinsic
99
118
  */
100
119
  export class IfIntrinsic implements Intrinsic {
101
120
  readonly [INTRINSIC_MARKER] = true as const;
102
- private conditionName: string;
121
+ private conditionName: string | Condition;
103
122
  private valueIfTrue: unknown;
104
123
  private valueIfFalse: unknown;
105
124
 
106
- constructor(conditionName: string, valueIfTrue: unknown, valueIfFalse: unknown) {
125
+ constructor(conditionName: string | Condition, valueIfTrue: unknown, valueIfFalse: unknown) {
107
126
  this.conditionName = conditionName;
108
127
  this.valueIfTrue = valueIfTrue;
109
128
  this.valueIfFalse = valueIfFalse;
110
129
  }
111
130
 
112
131
  toJSON(): { "Fn::If": [string, unknown, unknown] } {
113
- return { "Fn::If": [this.conditionName, resolveIntrinsicValue(this.valueIfTrue), resolveIntrinsicValue(this.valueIfFalse)] };
132
+ const name = typeof this.conditionName === "string" ? this.conditionName : getLogicalName(this.conditionName);
133
+ return { "Fn::If": [name, resolveIntrinsicValue(this.valueIfTrue), resolveIntrinsicValue(this.valueIfFalse)] };
114
134
  }
115
135
  }
116
136
 
117
137
 
118
138
  /**
119
- * Create an If intrinsic
139
+ * Create an If intrinsic. The condition is a name string, or the `Condition`
140
+ * declarable itself (resolved to its logical name at serialization, #2068).
120
141
  */
121
- export function If(conditionName: string, valueIfTrue: unknown, valueIfFalse: unknown): IfIntrinsic {
142
+ export function If(conditionName: string | Condition, valueIfTrue: unknown, valueIfFalse: unknown): IfIntrinsic {
122
143
  return new IfIntrinsic(conditionName, valueIfTrue, valueIfFalse);
123
144
  }
124
145
 
146
+ /**
147
+ * Fn::Equals condition intrinsic (#2068).
148
+ * Compares two values; each may be a literal or a value intrinsic (Ref etc.).
149
+ */
150
+ export class EqualsIntrinsic implements Intrinsic {
151
+ readonly [INTRINSIC_MARKER] = true as const;
152
+ private left: unknown;
153
+ private right: unknown;
154
+
155
+ constructor(left: unknown, right: unknown) {
156
+ this.left = left;
157
+ this.right = right;
158
+ }
159
+
160
+ toJSON(): { "Fn::Equals": [unknown, unknown] } {
161
+ return { "Fn::Equals": [resolveIntrinsicValue(this.left), resolveIntrinsicValue(this.right)] };
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Create an Equals condition intrinsic: `Equals(Ref(cutover), "true")`.
167
+ */
168
+ export function Equals(left: unknown, right: unknown): EqualsIntrinsic {
169
+ return new EqualsIntrinsic(left, right);
170
+ }
171
+
172
+ /**
173
+ * Fn::And condition intrinsic (#2068).
174
+ */
175
+ export class AndIntrinsic implements Intrinsic {
176
+ readonly [INTRINSIC_MARKER] = true as const;
177
+ private conditions: ConditionOperand[];
178
+
179
+ constructor(conditions: ConditionOperand[]) {
180
+ this.conditions = conditions;
181
+ }
182
+
183
+ toJSON(): { "Fn::And": unknown[] } {
184
+ return { "Fn::And": this.conditions.map(resolveConditionOperand) };
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Create an And condition intrinsic over 2–10 operands, each a nested
190
+ * condition intrinsic, a `Condition` declarable, or a condition name.
191
+ */
192
+ export function And(...conditions: ConditionOperand[]): AndIntrinsic {
193
+ if (conditions.length < 2 || conditions.length > 10) {
194
+ throw new Error("And(...conditions): Fn::And takes between 2 and 10 conditions");
195
+ }
196
+ return new AndIntrinsic(conditions);
197
+ }
198
+
199
+ /**
200
+ * Fn::Or condition intrinsic (#2068).
201
+ */
202
+ export class OrIntrinsic implements Intrinsic {
203
+ readonly [INTRINSIC_MARKER] = true as const;
204
+ private conditions: ConditionOperand[];
205
+
206
+ constructor(conditions: ConditionOperand[]) {
207
+ this.conditions = conditions;
208
+ }
209
+
210
+ toJSON(): { "Fn::Or": unknown[] } {
211
+ return { "Fn::Or": this.conditions.map(resolveConditionOperand) };
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Create an Or condition intrinsic over 2–10 operands, each a nested
217
+ * condition intrinsic, a `Condition` declarable, or a condition name.
218
+ */
219
+ export function Or(...conditions: ConditionOperand[]): OrIntrinsic {
220
+ if (conditions.length < 2 || conditions.length > 10) {
221
+ throw new Error("Or(...conditions): Fn::Or takes between 2 and 10 conditions");
222
+ }
223
+ return new OrIntrinsic(conditions);
224
+ }
225
+
226
+ /**
227
+ * Fn::Not condition intrinsic (#2068).
228
+ */
229
+ export class NotIntrinsic implements Intrinsic {
230
+ readonly [INTRINSIC_MARKER] = true as const;
231
+ private condition: ConditionOperand;
232
+
233
+ constructor(condition: ConditionOperand) {
234
+ this.condition = condition;
235
+ }
236
+
237
+ toJSON(): { "Fn::Not": [unknown] } {
238
+ return { "Fn::Not": [resolveConditionOperand(this.condition)] };
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Create a Not condition intrinsic over a nested condition intrinsic, a
244
+ * `Condition` declarable, or a condition name.
245
+ */
246
+ export function Not(condition: ConditionOperand): NotIntrinsic {
247
+ return new NotIntrinsic(condition);
248
+ }
249
+
125
250
  /**
126
251
  * Fn::Join intrinsic function
127
252
  * Joins values with a delimiter
@@ -5,7 +5,8 @@
5
5
  * #350); core keeps only the generic catalog machinery + cross-cutting ids.
6
6
  */
7
7
 
8
- import { auditRule, type RuleMeta, type Authority } from "@intentius/chant/audit/catalog";
8
+ import { auditRule, type RuleMeta, type Authority, applyLineage } from "@intentius/chant/audit/catalog";
9
+ import { awsAuditLineage } from "./audit-lineage";
9
10
 
10
11
  /** AWS Well-Architected Security Pillar — the authority chant cites for AWS security findings. */
11
12
  const AWS_SEC: Authority = {
@@ -88,4 +89,8 @@ export const awsAuditCatalog: Record<string, RuleMeta> = {
88
89
  WAW066: auditRule("WAW066", "merge-worthy", "guidance", "Private subnet's route table has no working default route", "Add a 0.0.0.0/0 route to a NAT gateway/Transit Gateway that exists in the template.", { category: "correctness" }),
89
90
  WAW067: auditRule("WAW067", "report-only", "guidance", "Single-AZ NAT gateway serves multi-AZ private subnets", "Add one NAT gateway per Availability Zone and point each AZ's subnets at its own.", { category: "best-practice" }),
90
91
  WAW068: auditRule("WAW068", "report-only", "guidance", "VPN Gateway or Transit Gateway has only one attached VPN Connection", "Attach a second VPNConnection (ideally to a separate Customer Gateway) for redundancy.", { category: "best-practice" }),
92
+ WAW069: auditRule("WAW069", "merge-worthy", "guidance", "Template references a condition it never declares", "Declare the condition in the Conditions section (new Condition(...)), or fix the referenced name.", { category: "correctness" }),
91
93
  };
94
+
95
+ // Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
96
+ applyLineage(awsAuditCatalog, awsAuditLineage);
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Prior art for these audit rules: the open-source tools whose checks cover the
3
+ * same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
4
+ * the registry, the relation vocabulary, and why this is credit rather than
5
+ * authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
6
+ * when a credited tool's index no longer lists a rule cited here.
7
+ */
8
+ import type { Lineage } from "@intentius/chant/audit/catalog";
9
+
10
+ export const awsAuditLineage: Record<string, Lineage[]> = {
11
+ WAW010: [
12
+ { tool: "cfn-lint", rule: "W3005", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#W3005", relation: "equivalent" },
13
+ ],
14
+ WAW011: [
15
+ { tool: "cfn-lint", rule: "W2531", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#W2531", relation: "equivalent" },
16
+ { tool: "checkov", rule: "CKV_AWS_363", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
17
+ ],
18
+ WAW017: [
19
+ { tool: "kics", rule: "Lambda Function Without Tags", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/8df8e857-bd59-44fa-9f4c-d77594b95b46/", relation: "extends" },
20
+ { tool: "kics", rule: "EFS Without Tags", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/08e39832-5e42-4304-98a0-aa5b43393162/", relation: "extends" },
21
+ ],
22
+ WAW018: [
23
+ { tool: "guard-rules-registry", rule: "S3_BUCKET_LEVEL_PUBLIC_ACCESS_PROHIBITED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_s3/s3_bucket_level_public_access_prohibited.guard", relation: "equivalent" },
24
+ { tool: "checkov", rule: "CKV_AWS_53", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "extends" },
25
+ { tool: "checkov", rule: "CKV_AWS_56", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "extends" },
26
+ ],
27
+ WAW019: [
28
+ { tool: "guard-rules-registry", rule: "RESTRICTED_INCOMING_TRAFFIC", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_ec2/restricted_common_ports.guard", relation: "overlaps" },
29
+ { tool: "kics", rule: "EC2 Sensitive Port Is Publicly Exposed", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/494b03d3-bf40-4464-8524-7c56ad0700ed/", relation: "overlaps" },
30
+ { tool: "checkov", rule: "CKV_AWS_24", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "extends" },
31
+ ],
32
+ WAW020: [
33
+ { tool: "checkov", rule: "CKV_AWS_63", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
34
+ { tool: "cfn-nag", rule: "F4", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/IamPolicyWildcardActionRule.rb", relation: "overlaps" },
35
+ { tool: "guard-rules-registry", rule: "IAM_ROLE_NO_WILDCARD_ACTIONS_ON_PERMISSIONS", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/iam/iam_role_no_wildcard_actions_on_permissions.guard", relation: "overlaps" },
36
+ ],
37
+ WAW021: [
38
+ { tool: "cfn-nag", rule: "F27", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/RDSDBInstanceStorageEncryptedRule.rb", relation: "equivalent" },
39
+ { tool: "checkov", rule: "CKV_AWS_16", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
40
+ { tool: "guard-rules-registry", rule: "RDS_STORAGE_ENCRYPTED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_rds/rds_storage_encrypted.guard", relation: "equivalent" },
41
+ ],
42
+ WAW022: [
43
+ { tool: "cfn-nag", rule: "W89", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/LambdaFunctionInsideVPCRule.rb", relation: "equivalent" },
44
+ { tool: "checkov", rule: "CKV_AWS_117", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
45
+ { tool: "guard-rules-registry", rule: "LAMBDA_INSIDE_VPC", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/lambda/lambda_inside_vpc.guard", relation: "equivalent" },
46
+ ],
47
+ WAW023: [
48
+ { tool: "checkov", rule: "CKV_AWS_68", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
49
+ { tool: "kics", rule: "CloudFront Without WAF", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/0f139403-303f-467c-96bd-e717e6cfd62d/", relation: "equivalent" },
50
+ ],
51
+ WAW024: [
52
+ { tool: "cfn-nag", rule: "W52", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/ElasticLoadBalancerV2AccessLoggingRule.rb", relation: "equivalent" },
53
+ { tool: "checkov", rule: "CKV_AWS_91", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
54
+ { tool: "kics", rule: "ELBv2 ALB Access Log Disabled", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/c62e8b7d-1fdf-4050-ac4c-76ba9e1d9621/", relation: "equivalent" },
55
+ ],
56
+ WAW025: [
57
+ { tool: "cfn-nag", rule: "W47", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/SnsTopicKmsMasterKeyIdRule.rb", relation: "equivalent" },
58
+ { tool: "checkov", rule: "CKV_AWS_26", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
59
+ { tool: "guard-rules-registry", rule: "SNS_ENCRYPTED_KMS", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_sns/sns_encrypted_kms.guard", relation: "equivalent" },
60
+ ],
61
+ WAW026: [
62
+ { tool: "checkov", rule: "CKV_AWS_27", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
63
+ { tool: "kics", rule: "SQS With SSE Disabled", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/12726829-93ed-4d51-9cbe-13423f4299e1/", relation: "equivalent" },
64
+ { tool: "cfn-nag", rule: "W48", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/SqsQueueKmsMasterKeyIdRule.rb", relation: "overlaps" },
65
+ ],
66
+ WAW027: [
67
+ { tool: "cfn-nag", rule: "W78", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/DynamoDBBackupRule.rb", relation: "equivalent" },
68
+ { tool: "checkov", rule: "CKV_AWS_28", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
69
+ { tool: "guard-rules-registry", rule: "DYNAMODB_PITR_ENABLED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/dynamodb/dynamodb_pitr_enabled.guard", relation: "equivalent" },
70
+ ],
71
+ WAW028: [
72
+ { tool: "cfn-nag", rule: "F1", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/EbsVolumeHasSseRule.rb", relation: "equivalent" },
73
+ { tool: "checkov", rule: "CKV_AWS_3", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
74
+ { tool: "guard-rules-registry", rule: "ENCRYPTED_VOLUMES", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_ec2/encrypted_volumes.guard", relation: "equivalent" },
75
+ ],
76
+ WAW029: [
77
+ { tool: "cfn-lint", rule: "E3005", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3005", relation: "equivalent" },
78
+ ],
79
+ WAW032: [
80
+ { tool: "checkov", rule: "CKV_AWS_97", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
81
+ { tool: "kics", rule: "EFS Volume With Disabled Transit Encryption", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/c1282e03-b285-4637-aee7-eefe3a7bb658/", relation: "equivalent" },
82
+ ],
83
+ WAW038: [
84
+ { tool: "cfn-nag", rule: "F22", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/RDSInstancePubliclyAccessibleRule.rb", relation: "equivalent" },
85
+ { tool: "checkov", rule: "CKV_AWS_17", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
86
+ { tool: "kics", rule: "RDS DB Instance Publicly Accessible", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/de38e1d5-54cb-4111-a868-6f7722695007/", relation: "equivalent" },
87
+ ],
88
+ WAW039: [
89
+ { tool: "cfn-nag", rule: "W75", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/RDSInstanceBackupRetentionPeriodRule.rb", relation: "equivalent" },
90
+ { tool: "guard-rules-registry", rule: "DB_INSTANCE_BACKUP_ENABLED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_rds/db_instance_backup_enabled.guard", relation: "equivalent" },
91
+ { tool: "kics", rule: "RDS With Backup Disabled", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/8c415f6f-7b90-4a27-a44a-51047e1506f9/", relation: "equivalent" },
92
+ ],
93
+ WAW040: [
94
+ { tool: "cfn-nag", rule: "F80", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/RDSInstanceDeletionProtectionRule.rb", relation: "equivalent" },
95
+ { tool: "guard-rules-registry", rule: "RDS_INSTANCE_DELETION_PROTECTION_ENABLED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_rds/rds_instance_deletion_protection_enabled.guard", relation: "equivalent" },
96
+ { tool: "kics", rule: "RDS DB Instance With Deletion Protection Disabled", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/2c161e58-cb52-454f-abea-6470c37b5e6e/", relation: "equivalent" },
97
+ ],
98
+ WAW042: [
99
+ { tool: "guard-rules-registry", rule: "S3_BUCKET_SSL_REQUESTS_ONLY", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_s3/s3_bucket_ssl_requests_only.guard", relation: "equivalent" },
100
+ { tool: "kics", rule: "S3 Bucket Without SSL In Write Actions", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/38c64e76-c71e-4d92-a337-60174d1de1c9/", relation: "overlaps" },
101
+ ],
102
+ WAW043: [
103
+ { tool: "cfn-nag", rule: "F19", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/KMSKeyRotationRule.rb", relation: "equivalent" },
104
+ { tool: "checkov", rule: "CKV_AWS_7", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
105
+ { tool: "guard-rules-registry", rule: "CMK_BACKING_KEY_ROTATION_ENABLED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/aws_kms/cmk_backing_key_rotation_enabled.guard", relation: "equivalent" },
106
+ ],
107
+ WAW044: [
108
+ { tool: "checkov", rule: "CKV_AWS_2", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "overlaps" },
109
+ { tool: "cfn-nag", rule: "W56", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/ElasticLoadBalancerV2ListenerProtocolRule.rb", relation: "overlaps" },
110
+ ],
111
+ WAW045: [
112
+ { tool: "cfn-nag", rule: "W55", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/ElasticLoadBalancerV2ListenerSslPolicyRule.rb", relation: "equivalent" },
113
+ { tool: "checkov", rule: "CKV_AWS_103", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
114
+ { tool: "guard-rules-registry", rule: "ELBV2_LISTENER_SSL_POLICY_RULE", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/elastic_load_balancing_v2/elbv2_listener_ssl_policy_rule.guard", relation: "equivalent" },
115
+ ],
116
+ WAW049: [
117
+ { tool: "cfn-nag", rule: "W2", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/SecurityGroupIngressOpenToWorldRule.rb", relation: "overlaps" },
118
+ { tool: "guard-rules-registry", rule: "EC2_SECURITY_GROUP_INGRESS_OPEN_TO_WORLD_RULE", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/amazon_ec2/ec2_security_group_ingress_open_to_world_rule.guard", relation: "overlaps" },
119
+ { tool: "kics", rule: "Unrestricted Security Group Ingress", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/4a1e6b34-1008-4e61-a5f2-1f7c276f8d14/", relation: "overlaps" },
120
+ ],
121
+ WAW052: [
122
+ { tool: "cfn-nag", rule: "F78", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/CognitoUserPoolMfaConfigurationOnorOptionalRule.rb", relation: "overlaps" },
123
+ { tool: "guard-rules-registry", rule: "COGNITO_USER_POOL_MFA_CONFIGURATION_RULE", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/aws_cognito/cognito_user_pool_mfa_configuration_rule.guard", relation: "overlaps" },
124
+ { tool: "kics", rule: "Cognito UserPool Without MFA", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/74a18d1a-cf02-4a31-8791-ed0967ad7fdc/", relation: "overlaps" },
125
+ ],
126
+ WAW053: [
127
+ { tool: "cfn-nag", rule: "W79", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/ECRRepositoryScanOnPushRule.rb", relation: "equivalent" },
128
+ { tool: "checkov", rule: "CKV_AWS_163", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
129
+ { tool: "guard-rules-registry", rule: "ECR_REPO_SCAN_ON_PUSH", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/aws_ecr/ecr_repo_scan_on_push_rule.guard", relation: "equivalent" },
130
+ ],
131
+ WAW054: [
132
+ { tool: "checkov", rule: "CKV_AWS_51", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
133
+ { tool: "kics", rule: "ECR Image Tag Not Immutable", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/33f41d31-86b1-46a4-81f7-9c9a671f59ac/", relation: "equivalent" },
134
+ ],
135
+ WAW055: [
136
+ { tool: "cfn-nag", rule: "W86", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/LogsLogGroupRetentionRule.rb", relation: "equivalent" },
137
+ { tool: "checkov", rule: "CKV_AWS_66", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "equivalent" },
138
+ { tool: "guard-rules-registry", rule: "CW_LOGGROUP_RETENTION_PERIOD_CHECK", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/cloudwatch/cw_loggroup_retention_period_check.guard", relation: "equivalent" },
139
+ ],
140
+ WAW058: [
141
+ { tool: "guard-rules-registry", rule: "MULTI_REGION_CLOUD_TRAIL_ENABLED", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/cloudtrail/multi_region_cloud_trail_enabled.guard", relation: "overlaps" },
142
+ { tool: "checkov", rule: "CKV_AWS_67", url: "https://www.checkov.io/5.Policy%20Index/cloudformation.html", relation: "overlaps" },
143
+ { tool: "kics", rule: "CloudTrail Logging Disabled", url: "https://docs.kics.io/latest/queries/cloudformation-queries/aws/5c0b06d5-b7a4-484c-aeb0-75a836269ff0/", relation: "overlaps" },
144
+ ],
145
+ WAW059: [
146
+ { tool: "cfn-nag", rule: "W12", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/IamPolicyWildcardResourceRule.rb", relation: "overlaps" },
147
+ { tool: "cfn-nag", rule: "W11", url: "https://github.com/stelligent/cfn_nag/blob/master/lib/cfn-nag/custom_rules/IamRoleWildcardResourceOnPermissionsPolicyRule.rb", relation: "overlaps" },
148
+ { tool: "guard-rules-registry", rule: "IAM_POLICYDOCUMENT_NO_WILDCARD_RESOURCE", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry/blob/main/rules/aws/iam/iam_policydocument_no_wildcard_resource.guard", relation: "overlaps" },
149
+ ],
150
+ WAW061: [
151
+ { tool: "cfn-lint", rule: "E3059", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3059", relation: "equivalent" },
152
+ ],
153
+ WAW062: [
154
+ { tool: "cfn-lint", rule: "E3019", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3019", relation: "overlaps" },
155
+ ],
156
+ WAW069: [
157
+ { tool: "cfn-lint", rule: "E8002", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E8002", relation: "equivalent" },
158
+ ],
159
+ };
@@ -60,6 +60,7 @@ import { waw065 } from "./waw065";
60
60
  import { waw066 } from "./waw066";
61
61
  import { waw067 } from "./waw067";
62
62
  import { waw068 } from "./waw068";
63
+ import { waw069 } from "./waw069";
63
64
 
64
65
  export const postSynthChecks: PostSynthCheck[] = [
65
66
  cor020,
@@ -122,4 +123,5 @@ export const postSynthChecks: PostSynthCheck[] = [
122
123
  waw066,
123
124
  waw067,
124
125
  waw068,
126
+ waw069,
125
127
  ];