cdk-preflight 0.0.169 → 0.0.171

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/.jsii CHANGED
@@ -9600,6 +9600,6 @@
9600
9600
  "symbolId": "src/index:PreflightOptions"
9601
9601
  }
9602
9602
  },
9603
- "version": "0.0.169",
9604
- "fingerprint": "IE1+ptdzBXn/2xOMLLAWbC+hDC40fi4VT/ojoRRLku4="
9603
+ "version": "0.0.171",
9604
+ "fingerprint": "Ev/tD8od13W6K3Ql3HpDPDnXKMG1f2l48gVkSvQYFJM="
9605
9605
  }
package/AGENTS.md CHANGED
@@ -201,7 +201,8 @@ If you are a subagent running one of these phases, do not spawn further agents.
201
201
  - **`json.unmarshal` and `json.is_valid` exist** (measured 2026-09-03, 1.7.0-beta). Opaque JSON-in-a-string properties (CloudWatch `DashboardBody`) can be parsed and validated structurally in Rego — this is the lens-2 workhorse. Reference implementations: `pf-cloudwatch-dashboard-*`.
202
202
  - **The Rego regex builtins exist** (`regex.match`, `regex.is_valid`, `regex.find_n`, `regex.replace`) — but the engine is Rust, so its regex flavour is **not** the one AWS's published patterns assume. Two traps, both measured 2026-09-04, both of which shipped broken rules before they were caught:
203
203
  - *Character classes are Unicode-aware.* `\w`, `\d` and `\s` match non-ASCII — `regex.match("[\\w]+", "テスト用")` is **true**. AWS API patterns like Cognito's `[\w\s+=,.@-]+` are ASCII-semantics, so copying one verbatim yields a rule that never fires. Write explicit ranges (`^[A-Za-z0-9_ \t+=,.@-]+$`).
204
- - *No lookahead/lookbehind.* ELBv2 publishes `(?!^-)(?!.*-$)^[A-Za-z0-9-]+$` for `targetGroupName`; that pattern does not compile here, and `regex.match` then returns false for **every** input — the rule fires on all values, which is worse than not firing. Rewrite without lookaround: `^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?$`.
204
+ - *No lookahead/lookbehind.* ELBv2 publishes `(?!^-)(?!.*-$)^[A-Za-z0-9-]+$` for `targetGroupName`; that pattern does not compile here, and `regex.match` is then undefined for **every** input — so the usual `not regex.match(...)` shape is `not undefined` = true and the rule fires on all values, which is worse than not firing. Rewrite without lookaround: `^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?$`.
205
+ - **A Unicode class under a large bounded repeat is rejected by the regex engine, and the rule then breaks whichever way it was written** (measured 2026-09-27). AWS publishes Grafana's `workspaceDescription` as `^[\p{L}\p{Z}\p{N}\p{P}]{0,2048}$`; handed to `regex.match` verbatim it does not compile. The bound itself is not the story — `[a-z]{0,100000}` compiles, while four `\p` classes already break at `{0,500}` and a single `\p{L}` at `{0,2048}` — so what blows up is the size of the compiled pattern (class width × repeat count), not a repeat-count cap; do not port Go's "repeat count over 1000" rule of thumb to this engine. On a rejected pattern `regex.match` is **undefined, not false** (binding it kills the body; nothing is thrown and the rest of the pack keeps firing), exactly like the lookaround trap above — so which way the rule breaks depends on how it is written: `not regex.match(p, v)`, the shape these charset checks almost always take, is `not undefined` = true and fires on **every** value (`pass: <id> fired`), while a positive `regex.match(p, v)` guard goes **silent** (`fail: own rule silent`). `regex.is_valid(p) == false` catches both and is the one-line self-check. Split the length off and leave it to the schema or `count()`: `pf-grafana-ws-description-charset` matches the charset with `*` and drops the 2048.
205
206
  - **The absence sentinel must not be a string.** The `object.get(props, "<Key>", "__pf_absent")` idiom is only safe when the next step is `== "__pf_absent"`. If the next step is `is_string(v)` followed by a format check, an *absent* key yields the sentinel string and the rule fires on every resource that omits the property. Use `null` as the default in that case — `is_string(null)` is false, so the rule skips. (This produced three false-positive rules in the 2026-09-04 batch, visible only because other rules' pass fixtures went red.)
206
207
  - **`not <undefined>` is true, so a helper that declines makes the rule fire on everything.** Rego's negation succeeds when its operand is undefined, so `not a in _helper(x)` passes whenever `_helper(x)` is undefined — not just when `a` is missing from it. Helpers guard their input (`is_array`, `is_object`, an unresolved `Fn::If` marker) precisely so they decline on the values a rule should skip, and writing the negation inline turns that guard inside out into a false positive on every such resource. Bind first — `defined := _helper(x)` on its own line, then `not a in defined` — so the body goes undefined and the rule stays silent when the helper declines. **Neither fixture catches this**: both templates carry a value the helper accepts, so only a false-positive sweep over unrelated templates finds it. Measured twice on 2026-09-25: the OpenSearch Serverless policy rules fired on the `Fn::Join`+`Ref` policies CDK ordinarily emits (#268), and the Network Firewall custom-action rules fired on every stateless rule group (#280).
207
208
  - **Two bare `some x` declarations in one rule body take the whole pack down** (measured 2026-09-09, 1.7.0-beta): the engine defines the second one's local slot once per binding of the first, so as soon as the outer iteration yields two values the custom package dies with `duplicated definition of local variable <name>` — every rule stops firing, for every stack, and `exclude` cannot work around it because the package fails as a whole. One filter entry passed, two did not (issue #150). Write the second iteration as `some k, v in coll` (guarded by `is_array` / `is_object`, since `some .. in` on a scalar is itself a hard error); converting either of the two is enough. `test/structure.test.ts` fails the build on any body with two bare `some`s. Reference: `pf-wafv2-logging-filter-condition`.
@@ -229,6 +230,7 @@ If you are a subagent running one of these phases, do not spawn further agents.
229
230
  - **`some` cannot re-declare a variable that is already bound in the same body or an enclosing one** ("var `name` used before definition below"). Iterating the same tuple set twice needs fresh names plus equality checks (`some [nm, ii, m, l2, c2] in set; nm == name; ii == i`), and a comprehension inside a body must not reuse the body's variable names.
230
231
  - **`sprintf` with the wrong arity silently disables the rule**, like a bare `%`: `sprintf("%d %s", [n], extra)` never evaluates and nothing warns (measured 2026-09-05). Keep every argument inside the array.
231
232
  - **`%q` is not a supported `sprintf` verb** (measured 2026-09-24): the policy compiles, but the body never evaluates and no diagnostic fires — the same silent death as a bare `%`. Use `'%s'` (quote it yourself) or `%v`.
233
+ - **A `sprintf` format string with fewer verbs than array elements dies the same way, and a generator is what produces it** (measured 2026-09-27): run the format string through a `%`-style templater on the way into the `.rego` file and `%v` comes out as `%%v` — a literal percent followed by a `v`, so the format carries zero verbs while the array still carries one element, the body never evaluates and nothing warns (`sprintf("a %%v b", ["x"])` is silent; `sprintf("a %%v b", [])` yields `a %v b`). This is not the wrong-arity trap above — there the `sprintf` *call* is malformed; here the call is well-formed and only the verb count is off. Keep rego in raw string literals and pass it through no `%` formatting at all: in #290 one slice built its message with Python's `%` operator and silenced a rule, and the slice that kept the rego raw was 9/9 `ok` on the first run. `rule-check`'s `fail: own rule silent` is the only thing that shows it.
232
234
  - **`meta.fixtureRegion` moves a rule's fixture evaluation off the harness default (us-east-1)**, in both harnesses (`test/rules.test.ts` and `scripts/rule-check.ts`; it wins over `PF_REGION`). Needed for rules about the deploy region itself (`pf-wafv2-scope-region`: CLOUDFRONT scope only in us-east-1) whose fail template cannot fire where the harness runs.
233
235
  - **WAFv2 names are unique per scope and region, so parallel bench runs collide on fixture names.** The WAFv2 generators suffix every `Name` with the rule id (`uniq()` in the scratch `gen-lib.js`); do the same for any service whose entity names are account-unique.
234
236
  - **The CLI's service model can lag the API** (measured 2026-09-05: `aws wafv2` had no `Monetize` action and no `PreParseTextTransformations`). Fields the CLI rejects with ParamValidation need a stack for triage; everything else is cheaper through `create-*` calls whose rejections create nothing (WAF: ~290 calls, 13 stacks for the whole survey). `aws wafv2 check-capacity` is the oracle for the WCU estimate in `pf-wafv2-capacity`.
package/lib/index.js CHANGED
@@ -17,7 +17,7 @@ const rules_generated_1 = require("./rules.generated");
17
17
  * Preflight.apply(app);
18
18
  */
19
19
  class Preflight {
20
- static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-preflight.Preflight", version: "0.0.169" };
20
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-preflight.Preflight", version: "0.0.171" };
21
21
  /**
22
22
  * Register the cdk-preflight rules on an App or Stage.
23
23
  */