@intentius/chant-lexicon-aws 0.124.43 → 0.124.45

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 (57) hide show
  1. package/dist/codegen/generate-lexicon.d.ts +2 -6
  2. package/dist/codegen/generate-lexicon.d.ts.map +1 -1
  3. package/dist/deep-observe.d.ts +6 -2
  4. package/dist/deep-observe.d.ts.map +1 -1
  5. package/dist/deep-observe.js +17 -7
  6. package/dist/import/live-export.d.ts.map +1 -1
  7. package/dist/import/live-export.js +8 -4
  8. package/dist/integrity.json +5 -4
  9. package/dist/lint/audit-catalog.d.ts.map +1 -1
  10. package/dist/lint/audit-catalog.js +1 -0
  11. package/dist/lint/audit-lineage.d.ts.map +1 -1
  12. package/dist/lint/audit-lineage.js +4 -0
  13. package/dist/lint/post-synth/index.d.ts.map +1 -1
  14. package/dist/lint/post-synth/index.js +2 -0
  15. package/dist/lint/post-synth/waw017.d.ts.map +1 -1
  16. package/dist/lint/post-synth/waw017.js +3 -2
  17. package/dist/lint/post-synth/waw071.d.ts +19 -0
  18. package/dist/lint/post-synth/waw071.d.ts.map +1 -0
  19. package/dist/lint/post-synth/waw071.js +76 -0
  20. package/dist/manifest.json +1 -1
  21. package/dist/meta.json +3618 -1206
  22. package/dist/okf/index.md +1 -0
  23. package/dist/okf/rules/WAW071.md +19 -0
  24. package/dist/okf/types/Key.md +1 -0
  25. package/dist/okf/types/Map.md +1 -0
  26. package/dist/okf/types/SsmParameter.md +4 -0
  27. package/dist/okf/types/Type.md +1 -0
  28. package/dist/okf/types/UserPool.md +1 -0
  29. package/dist/rules/waw017.ts +3 -2
  30. package/dist/rules/waw071.ts +85 -0
  31. package/dist/serializer.d.ts +12 -0
  32. package/dist/serializer.d.ts.map +1 -1
  33. package/dist/serializer.js +44 -10
  34. package/dist/spec/parse.d.ts +27 -5
  35. package/dist/spec/parse.d.ts.map +1 -1
  36. package/dist/spec/parse.js +56 -0
  37. package/dist/taggable.d.ts +28 -0
  38. package/dist/taggable.d.ts.map +1 -1
  39. package/dist/taggable.js +60 -9
  40. package/package.json +2 -2
  41. package/src/codegen/generate-lexicon.ts +2 -2
  42. package/src/deep-observe.test.ts +19 -0
  43. package/src/deep-observe.ts +17 -6
  44. package/src/generated/lexicon-aws.json +3618 -1206
  45. package/src/import/live-export.test.ts +18 -0
  46. package/src/import/live-export.ts +8 -4
  47. package/src/lint/audit-catalog.ts +1 -0
  48. package/src/lint/audit-lineage.ts +4 -0
  49. package/src/lint/post-synth/index.ts +2 -0
  50. package/src/lint/post-synth/waw017.ts +3 -2
  51. package/src/lint/post-synth/waw071.test.ts +50 -0
  52. package/src/lint/post-synth/waw071.ts +85 -0
  53. package/src/serializer-tag-shapes.test.ts +83 -0
  54. package/src/serializer.ts +46 -9
  55. package/src/spec/parse.test.ts +39 -0
  56. package/src/spec/parse.ts +83 -1
  57. package/src/taggable.ts +78 -11
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Output is a Record<string, LexiconEntry> keyed by generated class name.
5
5
  */
6
- import type { SchemaParseResult } from "../spec/parse.js";
6
+ import type { ResourceTagging, SchemaParseResult } from "../spec/parse.js";
7
7
  import type { NamingStrategy } from "./naming.js";
8
8
  import type { ExtensionConstraint } from "./extensions.js";
9
9
  import type { PropertyConstraints } from "@intentius/chant/codegen/json-schema";
@@ -25,11 +25,7 @@ export interface LexiconEntry {
25
25
  inferredDeprecations?: string[];
26
26
  conditionalCreateOnly?: string[];
27
27
  replacementStrategy?: "delete_then_create" | "create_then_delete";
28
- tagging?: {
29
- taggable: boolean;
30
- tagOnCreate: boolean;
31
- tagUpdatable: boolean;
32
- };
28
+ tagging?: ResourceTagging;
33
29
  runtimeDeprecations?: Record<string, string>;
34
30
  }
35
31
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"generate-lexicon.d.ts","sourceRoot":"","sources":["../../src/codegen/generate-lexicon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAOhF,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC1D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,oBAAoB,GAAG,oBAAoB,CAAC;IAClE,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAC,EAC/C,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GACjD,MAAM,CA+CR;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYlE"}
1
+ {"version":3,"file":"generate-lexicon.d.ts","sourceRoot":"","sources":["../../src/codegen/generate-lexicon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAOhF,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC1D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,oBAAoB,GAAG,oBAAoB,CAAC;IAClE,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAC,EAC/C,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GACjD,MAAM,CA+CR;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYlE"}
@@ -197,8 +197,12 @@ export interface CloudControlResource {
197
197
  identifier: string;
198
198
  properties: Record<string, unknown>;
199
199
  }
200
- /** True when the live property tree carries chant's ownership marker tag. */
201
- export declare function hasOwnershipMarker(properties: Record<string, unknown>): boolean;
200
+ /**
201
+ * True when the live property tree carries chant's ownership marker tag. With
202
+ * a `type`, the tags are read from that type's own tag property
203
+ * (`UserPoolTags`); either way a list or a map reads (#4350).
204
+ */
205
+ export declare function hasOwnershipMarker(properties: Record<string, unknown>, type?: string): boolean;
202
206
  export interface AwsDeepObserveOptions {
203
207
  environment: string;
204
208
  entityNames: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../src/deep-observe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../src/deep-observe.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,7 @@
45
45
  import { AwsReadError, describeStackResources, getResource, listResources, } from "./api/read-client.js";
46
46
  import { declaredIdentifier } from "./identity-observe.js";
47
47
  import { AWS_TAG_OWNERSHIP_KEYS } from "./ownership.js";
48
+ import { readResourceTags, tagPlacementFor } from "./taggable.js";
48
49
  import { AWS_DEEP_BLIND_SPOTS, EC2_TOPOLOGY_GENERATED_NAMES, EC2_TOPOLOGY_SERVICE_DEFAULTS, EC2_TOPOLOGY_SOURCES, } from "./deep-topology.js";
49
50
  import { applyAwsEndpointArgv } from "./components/cloud-executor.js";
50
51
  import { toIngressRules } from "./dependencies.js";
@@ -304,6 +305,9 @@ export const awsDeepNormalizationHooks = {
304
305
  // still compared against what is live.
305
306
  if (isOwnershipTag(node.value))
306
307
  return true;
308
+ // The same marker as a map entry, on a map-tagged type (#4350).
309
+ if (OWNERSHIP_TAG_KEYS.has(node.key) && isMapTagEntry(node.entityType, node.pattern, node.key))
310
+ return true;
307
311
  // A name the service generated because source did not supply one.
308
312
  if (AWS_GENERATED_NAMES[node.entityType]?.has(node.pattern))
309
313
  return true;
@@ -412,12 +416,18 @@ const OWNERSHIP_TAG_KEYS = new Set(Object.values(AWS_TAG_OWNERSHIP_KEYS));
412
416
  function isOwnershipTag(value) {
413
417
  return isRecord(value) && typeof value.Key === "string" && OWNERSHIP_TAG_KEYS.has(value.Key);
414
418
  }
415
- /** True when the live property tree carries chant's ownership marker tag. */
416
- export function hasOwnershipMarker(properties) {
417
- const tags = properties.Tags;
418
- if (!Array.isArray(tags))
419
- return false;
420
- return tags.some((t) => isRecord(t) && t.Key === AWS_TAG_OWNERSHIP_KEYS.managedBy);
419
+ /** True when `pattern` is `<tagProperty>.<key>` on a type whose tags are a map. */
420
+ function isMapTagEntry(type, pattern, key) {
421
+ const placement = tagPlacementFor(type);
422
+ return placement?.shape === "map" && pattern === `${placement.property}.${key}`;
423
+ }
424
+ /**
425
+ * True when the live property tree carries chant's ownership marker tag. With
426
+ * a `type`, the tags are read from that type's own tag property
427
+ * (`UserPoolTags`); either way a list or a map reads (#4350).
428
+ */
429
+ export function hasOwnershipMarker(properties, type) {
430
+ return readResourceTags(type, properties)[AWS_TAG_OWNERSHIP_KEYS.managedBy] !== undefined;
421
431
  }
422
432
  /**
423
433
  * Read the live property tree for each declared entity via Cloud Control.
@@ -530,7 +540,7 @@ export async function observeResourcesDeepAws(options) {
530
540
  // thin path this one can answer the ownership question (#1015's open note).
531
541
  // A resource withheld by the filter is `filtered`, never absent: it exists,
532
542
  // it just isn't chant's.
533
- const owned = hasOwnershipMarker(properties);
543
+ const owned = hasOwnershipMarker(properties, type);
534
544
  if (options.owned && !owned) {
535
545
  unobserved[entityName] = {
536
546
  type,
@@ -1 +1 @@
1
- {"version":3,"file":"live-export.d.ts","sourceRoot":"","sources":["../../src/import/live-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAKnF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,OAAO,EACrB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,KAAK,CAAC,EAAE,OAAO,GACd,gBAAgB,CAoBlB"}
1
+ {"version":3,"file":"live-export.d.ts","sourceRoot":"","sources":["../../src/import/live-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAMnF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,OAAO,EACrB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,KAAK,CAAC,EAAE,OAAO,GACd,gBAAgB,CAuBlB"}
@@ -1,5 +1,6 @@
1
- import { hasOwnershipMarker, tagArrayToMap } from "@intentius/chant/ownership";
2
- import { AWS_TAG_OWNERSHIP_KEYS } from "../ownership.js";
1
+ import { hasOwnershipMarker } from "@intentius/chant/ownership";
2
+ import { AWS_TAG_OWNERSHIP_KEYS, OWNERSHIP_METADATA_KEY } from "../ownership.js";
3
+ import { readResourceTags, readTagValue } from "../taggable.js";
3
4
  import { CFParser } from "./parser.js";
4
5
  /**
5
6
  * Map a live CloudFormation template body to full-fidelity export IR.
@@ -24,8 +25,11 @@ export function parseStackTemplate(templateBody, selector, owned) {
24
25
  if (selector?.name !== undefined && r.logicalId !== selector.name)
25
26
  return false;
26
27
  if (owned) {
27
- const tags = r.properties.Tags;
28
- if (!hasOwnershipMarker(tagArrayToMap(tags), AWS_TAG_OWNERSHIP_KEYS))
28
+ // The marker sits in the type's own tag property, in either shape, or
29
+ // in the resource's Metadata for a type whose tags can't be stamped (#4350).
30
+ const tags = readResourceTags(r.type, r.properties);
31
+ const marker = readTagValue(r.metadata?.[OWNERSHIP_METADATA_KEY]);
32
+ if (!hasOwnershipMarker(tags, AWS_TAG_OWNERSHIP_KEYS) && !hasOwnershipMarker(marker, AWS_TAG_OWNERSHIP_KEYS))
29
33
  return false;
30
34
  }
31
35
  return true;
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "4d860e44d1e4e6e356a9d88757706b0a5baf926d1056848631845a7eca1d4893",
5
- "meta.json": "3e18663eb9eb09920882f8014020432330a74ccd480689e7384c2022cbb10f39",
4
+ "manifest.json": "4e87272580ed7978235350ce2c7631c83b575c7bc765f3c27002e63bc02c18b6",
5
+ "meta.json": "6f48557ed4203cbdeafdb47e7270fdf3533f6d6e6189987246301ed8e72170e9",
6
6
  "types/index.d.ts": "8651f9ae166668f4311eea1fe61c3fe235f345947951c1d23d1df24a4fd3384f",
7
7
  "rules/hardcoded-region.ts": "957eadcaa9f39cc17d21437565d7a94607d9b082cf28918b178fa067f07498a9",
8
8
  "rules/iam-wildcard.ts": "10f5065fa2bbf2c8ce23dfb475a56df33f280df87784e44fec641b2584cbf333",
@@ -16,7 +16,7 @@
16
16
  "rules/waw014.ts": "889f1b06899b50f98941d05b029954cf375ac8a130933b98c9586079096216aa",
17
17
  "rules/waw015.ts": "071c940fb6432cd8b42a44d083921e9491d5063417839109c10b2bd13dd2032e",
18
18
  "rules/waw016.ts": "2c4e230c98056a03ecdbfa0e408e36a31733e83cc96419910ae3ec9fd4b7fd9e",
19
- "rules/waw017.ts": "658554008954f71036e23b514dece7063142198fbdca125f11d09026f1d21ab2",
19
+ "rules/waw017.ts": "cb7386f8c91285da46e55ba28191ba2ce38eb070e3bed037d2419352f3b9d152",
20
20
  "rules/waw018.ts": "a4aa83a25104f039a6c5e0f3e1f5b8b65e493b0ba162c985988190a726b90d2f",
21
21
  "rules/waw019.ts": "f14ebf024a3cce7005d54c762e4e0979537d6a854fb123344107d40d92a94b5c",
22
22
  "rules/waw020.ts": "3958b724a4b6e2c954e6f85a611a928003fd13282632d6bae8f560d805b97f8b",
@@ -70,9 +70,10 @@
70
70
  "rules/waw068.ts": "adbfe84e9b3cdafc072346d87a5df81a97ab07bc5171f3d4abb824c29e9a66a0",
71
71
  "rules/waw069.ts": "4792bb3943d5e08039f45cbf554f45263703288acf4ca01d88ad778083023d27",
72
72
  "rules/waw070.ts": "35bd2281ab072a1d6138e0db9f9d6f4f0b6c5af4deacb28aa693177c654db66a",
73
+ "rules/waw071.ts": "d0d6e82aa7b347c1a3f41803c53906ea31a085bf04efca7f9bd7894e180b2846",
73
74
  "skills/chant-aws.md": "4d65eb160e001f2af42eb7c622a5b498c2fd1f5afbd6c8155615c010fb265c63",
74
75
  "skills/chant-aws-eks.md": "8789255709ff004ad0a875fd5999edcdc66fc6e33d710db058d9f42703bcfdfe",
75
76
  "skills/chant-aws-carve-terraform.md": "8780b94ce60aded935938d5ef20ea667738354051da1bf9ca6d70d9886f65d21"
76
77
  },
77
- "composite": "e42eeece34dd3644f68145fae8204a7fc97d2414d8f8dbab29f62c49bc5f4f88"
78
+ "composite": "6e6034cc52fcfa710c8daa010fd67a0f510a61a93cf862910a0b85791d67d96f"
78
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAa,KAAK,QAAQ,EAAgC,MAAM,gCAAgC,CAAC;AASxG,6CAA6C;AAC7C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA4EpD,CAAC"}
1
+ {"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAa,KAAK,QAAQ,EAAgC,MAAM,gCAAgC,CAAC;AASxG,6CAA6C;AAC7C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA6EpD,CAAC"}
@@ -83,6 +83,7 @@ export const awsAuditCatalog = {
83
83
  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" }),
84
84
  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" }),
85
85
  WAW070: auditRule("WAW070", "merge-worthy", "guidance", "Ref or Fn::GetAtt names something the template doesn't declare", "Reference a resource or parameter in this template. A value from another lexicon's resource arrives as a parameter.", { category: "correctness" }),
86
+ WAW071: auditRule("WAW071", "merge-worthy", "guidance", "Tags are in the wrong property or the wrong shape for the resource type", "Write the tags in the property the type uses and in the shape it takes, which is a list of Key and Value pairs for most types and a map for some, such as UserPoolTags on AWS::Cognito::UserPool.", { category: "correctness" }),
86
87
  };
87
88
  // Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
88
89
  applyLineage(awsAuditCatalog, awsAuditLineage);
@@ -1 +1 @@
1
- {"version":3,"file":"audit-lineage.d.ts","sourceRoot":"","sources":["../../src/lint/audit-lineage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAyJrD,CAAC"}
1
+ {"version":3,"file":"audit-lineage.d.ts","sourceRoot":"","sources":["../../src/lint/audit-lineage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CA6JrD,CAAC"}
@@ -151,4 +151,8 @@ export const awsAuditLineage = {
151
151
  { tool: "cfn-lint", rule: "E1012", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E1012", relation: "equivalent" },
152
152
  { tool: "cfn-lint", rule: "E1010", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E1010", relation: "overlaps" },
153
153
  ],
154
+ WAW071: [
155
+ { tool: "cfn-lint", rule: "E3002", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3002", relation: "overlaps" },
156
+ { tool: "cfn-lint", rule: "E3012", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3012", relation: "overlaps" },
157
+ ],
154
158
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAgEvE,eAAO,MAAM,eAAe,EAAE,cAAc,EA+D3C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAiEvE,eAAO,MAAM,eAAe,EAAE,cAAc,EAgE3C,CAAC"}
@@ -60,6 +60,7 @@ import { waw067 } from "./waw067.js";
60
60
  import { waw068 } from "./waw068.js";
61
61
  import { waw069 } from "./waw069.js";
62
62
  import { waw070 } from "./waw070.js";
63
+ import { waw071 } from "./waw071.js";
63
64
  export const postSynthChecks = [
64
65
  cor020,
65
66
  ext001,
@@ -123,4 +124,5 @@ export const postSynthChecks = [
123
124
  waw068,
124
125
  waw069,
125
126
  waw070,
127
+ waw071,
126
128
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"waw017.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/waw017.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAI9G;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GACpB,mBAAmB,EAAE,CA0BvB;AAED,eAAO,MAAM,MAAM,EAAE,cAOpB,CAAC"}
1
+ {"version":3,"file":"waw017.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/waw017.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAI9G;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GACpB,mBAAmB,EAAE,CA2BvB;AAED,eAAO,MAAM,MAAM,EAAE,cAOpB,CAAC"}
@@ -5,7 +5,7 @@
5
5
  * Encourages adding tags for cost allocation and compliance.
6
6
  */
7
7
  import { parseCFTemplate } from "./cf-refs.js";
8
- import { loadTaggableResources } from "../../taggable.js";
8
+ import { loadTaggableResources, tagPlacementFor } from "../../taggable.js";
9
9
  /**
10
10
  * Core detection logic — exported for direct testing with synthetic data.
11
11
  */
@@ -21,7 +21,8 @@ export function checkMissingTags(ctx, taggable) {
21
21
  if (!taggable.has(resource.Type))
22
22
  continue;
23
23
  const props = resource.Properties ?? {};
24
- if (!("Tags" in props)) {
24
+ const tagProperty = tagPlacementFor(resource.Type)?.property ?? "Tags";
25
+ if (!(tagProperty in props)) {
25
26
  diagnostics.push({
26
27
  checkId: "WAW017",
27
28
  severity: "warning",
@@ -0,0 +1,19 @@
1
+ /**
2
+ * WAW071: Tags are in the wrong property or the wrong shape for the type
3
+ *
4
+ * Each taggable type names its tag property and shape in its schema
5
+ * (`tagging.tagProperty`): a `[{Key, Value}]` list for most types, a map for
6
+ * types such as `AWS::SSM::Parameter`, and a property other than `Tags` for
7
+ * types such as `AWS::Cognito::UserPool` (`UserPoolTags`). CloudFormation
8
+ * rejects a template that puts tags anywhere else. chant stamped the ownership
9
+ * marker as a `Tags` list on every type until #4350; this check catches the
10
+ * next such template.
11
+ */
12
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
13
+ import { type TagPlacement } from "../../taggable.js";
14
+ /**
15
+ * Core detection logic — exported for direct testing with synthetic data.
16
+ */
17
+ export declare function checkTagShapes(ctx: PostSynthContext, placements: Map<string, TagPlacement | null>): PostSynthDiagnostic[];
18
+ export declare const waw071: PostSynthCheck;
19
+ //# sourceMappingURL=waw071.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waw071.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/waw071.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE9G,OAAO,EAAqC,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAiBtF;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,gBAAgB,EACrB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC,GAC3C,mBAAmB,EAAE,CAsCvB;AAED,eAAO,MAAM,MAAM,EAAE,cAOpB,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * WAW071: Tags are in the wrong property or the wrong shape for the type
3
+ *
4
+ * Each taggable type names its tag property and shape in its schema
5
+ * (`tagging.tagProperty`): a `[{Key, Value}]` list for most types, a map for
6
+ * types such as `AWS::SSM::Parameter`, and a property other than `Tags` for
7
+ * types such as `AWS::Cognito::UserPool` (`UserPoolTags`). CloudFormation
8
+ * rejects a template that puts tags anywhere else. chant stamped the ownership
9
+ * marker as a `Tags` list on every type until #4350; this check catches the
10
+ * next such template.
11
+ */
12
+ import { parseCFTemplate } from "./cf-refs.js";
13
+ import { isTagIntrinsic, loadTagPlacements } from "../../taggable.js";
14
+ function isPlainObject(value) {
15
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16
+ }
17
+ /** Why `value` doesn't fit `placement`, or undefined when it does. */
18
+ function shapeProblem(value, placement) {
19
+ if (isTagIntrinsic(value))
20
+ return undefined;
21
+ if (placement.shape === "map") {
22
+ return isPlainObject(value) ? undefined : "a list, but the type takes a key-to-value map";
23
+ }
24
+ if (!Array.isArray(value))
25
+ return "a map, but the type takes a [{Key, Value}] list";
26
+ const bad = value.some((t) => !isTagIntrinsic(t) && !(isPlainObject(t) && "Key" in t && "Value" in t));
27
+ return bad ? "a list with an entry that has no Key and Value" : undefined;
28
+ }
29
+ /**
30
+ * Core detection logic — exported for direct testing with synthetic data.
31
+ */
32
+ export function checkTagShapes(ctx, placements) {
33
+ const diagnostics = [];
34
+ for (const [lexicon, output] of ctx.outputs) {
35
+ if (lexicon !== "aws")
36
+ continue;
37
+ const template = parseCFTemplate(output);
38
+ if (!template?.Resources)
39
+ continue;
40
+ for (const [logicalId, resource] of Object.entries(template.Resources)) {
41
+ const placement = placements.get(resource.Type);
42
+ if (!placement)
43
+ continue;
44
+ const props = resource.Properties ?? {};
45
+ if (placement.property !== "Tags" && "Tags" in props) {
46
+ diagnostics.push({
47
+ checkId: "WAW071",
48
+ severity: "error",
49
+ message: `Resource "${logicalId}" (${resource.Type}) has Tags, but the type takes its tags in ${placement.property}`,
50
+ entity: logicalId,
51
+ lexicon: "aws",
52
+ });
53
+ }
54
+ if (!(placement.property in props))
55
+ continue;
56
+ const problem = shapeProblem(props[placement.property], placement);
57
+ if (problem) {
58
+ diagnostics.push({
59
+ checkId: "WAW071",
60
+ severity: "error",
61
+ message: `Resource "${logicalId}" (${resource.Type}) has ${placement.property} as ${problem}`,
62
+ entity: logicalId,
63
+ lexicon: "aws",
64
+ });
65
+ }
66
+ }
67
+ }
68
+ return diagnostics;
69
+ }
70
+ export const waw071 = {
71
+ id: "WAW071",
72
+ description: "Tags are in the wrong property or the wrong shape for the resource type",
73
+ check(ctx) {
74
+ return checkTagShapes(ctx, loadTagPlacements());
75
+ },
76
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws",
3
- "version": "0.124.43",
3
+ "version": "0.124.45",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "AWS",
6
6
  "intrinsics": [