cdk-preflight 0.0.28 → 0.0.29
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/.claude/skills/find-preflight-rules/SKILL.md +13 -3
- package/.jsii +2 -2
- package/AGENTS.md +14 -1
- package/lib/index.js +1 -1
- package/package.json +2 -1
|
@@ -51,11 +51,21 @@ CDK のプラグイン経路は `accountId` と `region` をエンジンに渡
|
|
|
51
51
|
- テンプレート内の一意性。実測で **Export 名の重複はエンジン未検出**
|
|
52
52
|
- スタック間の整合性。全スタックのテンプレートを一度に見られるのは我々だけ(`context.stackTemplates`)。ただし Rego はテンプレート単位で評価するので、実装は enforce プラグイン側(TypeScript)になる。**現状は未対応**なので、この種の候補が出たら設計変更の要否とセットで issue に書く
|
|
53
53
|
|
|
54
|
-
### レンズ 5
|
|
54
|
+
### レンズ 5:集約・上限、および文字種・長さ(プロパティ単位で必ず実測)
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
テンプレート全体に効く集約はエンジンが持っている:リソース数 `F0007`、Output 数 `F0004`、テンプレートサイズ `E1002`。論理 ID の長さは `I3012` の **INFO 止まり**(=グレーゾーン)、Export 名の重複は検出なし。
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
**文字種と長さ(`F3031` / `F3033`)を「エンジン済み」と一般化してはいけない**(2026-09-04、57 テンプレートで実測)。カバレッジは**スキーマがそのプロパティに pattern / maxLength を書いているかどうか**で決まるので、同じリソースの中でも非対称になる:
|
|
59
|
+
|
|
60
|
+
| エンジンが止める | 素通り(=候補) |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `SecurityGroup.GroupDescription`(F3031) | **`SecurityGroupIngress[].Description`** |
|
|
63
|
+
| DynamoDB GSI `IndexName`(F3031) | **`Table.TableName`** |
|
|
64
|
+
| `IAM Role.Description`、`Lambda FunctionName`、`Events Rule.Name`、`S3 BucketName`、`Firehose DeliveryStreamName` | `ECS ClusterName/ServiceName/Family`、`ELBv2 Name`(LB/TG とも)、`Cognito UserPoolName/ClientName`、`Batch` の各名前、`SNS TopicName`、`SQS QueueName`、`RDS DBSubnetGroupName`、`CloudWatch Namespace`、`LaunchTemplateName` |
|
|
65
|
+
|
|
66
|
+
しかも `SecurityGroupIngress[].Description` は CDK で日本語が最も入りやすい場所(`addIngressRule(peer, port, '説明')` の第 3 引数)。**プロパティ 1 つずつ重複ガードに掛けること。**
|
|
67
|
+
|
|
68
|
+
そして「日本語なら落ちる」も成立しない。同じ「説明」でも RDS は non-printable 扱いで落ち、IAM ManagedPolicy・Lambda・CloudWatch AlarmDescription は通る。実機ゲートまで通して初めて候補が確定する。
|
|
59
69
|
|
|
60
70
|
### レンズ 6:時間で変わる制約(対象。ただし重複ガードを先に)
|
|
61
71
|
|
package/.jsii
CHANGED
|
@@ -9598,6 +9598,6 @@
|
|
|
9598
9598
|
"symbolId": "src/index:PreflightOptions"
|
|
9599
9599
|
}
|
|
9600
9600
|
},
|
|
9601
|
-
"version": "0.0.
|
|
9602
|
-
"fingerprint": "
|
|
9601
|
+
"version": "0.0.29",
|
|
9602
|
+
"fingerprint": "56ha27pDnOg2btypvxrAXUJx727KVabcfRb2GtCwFuA="
|
|
9603
9603
|
}
|
package/AGENTS.md
CHANGED
|
@@ -60,9 +60,16 @@ The ordering is by **how easily the L2 guard is bypassed in normal use**, not by
|
|
|
60
60
|
|
|
61
61
|
## Rule lifecycle: add fast, retire without ceremony
|
|
62
62
|
|
|
63
|
+
The two failure modes are watched separately: **staleness** (the constraint disappeared at AWS — the fail template now deploys clean) is caught by the monthly real-deploy run and reported as BROKEN; **obsolescence** (the engine caught up — the rule is now a duplicate) is caught by the duplication guard and the redundancy scan and reported as REDUNDANT. Both land in the monthly issue.
|
|
64
|
+
|
|
63
65
|
**Adding.** Two gates, both mechanical, and nothing else can reject a candidate: the duplication guard (`test/rules.test.ts`) and the real-deploy gate (`bench/verify-rule.sh`). The real-deploy gate is the one thing that did *not* get looser with the widened scope, and deliberately so — `enforce` is the default mode, so a false positive is a hard synth failure in someone's CI. The CloudFront survey is the standing evidence: 3 of 9 documented constraints deployed clean (see "A doc sentence is a hypothesis" above). Volume comes from running the bench over more services, never from lowering that bar.
|
|
64
66
|
|
|
65
|
-
**Retiring.** The trigger is the bundled engine (or CloudFormation's server-side validation) starting to block the constraint — never an L2 construct, never cfn-lint. You do not have to watch for it
|
|
67
|
+
**Retiring.** The trigger is the bundled engine (or CloudFormation's server-side validation) starting to block the constraint — never an L2 construct, never cfn-lint. You do not have to watch for it; two mechanisms report it:
|
|
68
|
+
|
|
69
|
+
- The jest duplication guard turns red on its own the first time an `aws-cdk-lib` bump brings an engine that reports ERROR/FATAL on a rule's fail template, and its failure message names the rule to delete.
|
|
70
|
+
- `npx projen redundancy-scan` lists every such rule at once (no AWS account, no cost — it just re-runs each fail template through the bare engine). The monthly `monthly-verify` workflow runs it and files the results under a **REDUNDANT** heading in the same issue that reports drift, so obsolescence and staleness arrive on one page.
|
|
71
|
+
|
|
72
|
+
Then:
|
|
66
73
|
|
|
67
74
|
1. `rm -rf rules/<service>/<rule-id>/` — delete outright, no tombstone. `upstream: retired` exists for a rule mid-flight, not for a corpse.
|
|
68
75
|
2. `npx projen bundle-rules` (regenerates `src/rules.generated.ts` and `docs/rules.md`).
|
|
@@ -128,4 +135,10 @@ bench/ # real-deploy verification (needs an AWS account; no
|
|
|
128
135
|
- **`net.cidr_*` builtins do not exist** in the engine's Rego build ("could not find func", measured 2026-09-03 on `net.cidr_contains` / `net.cidr_intersects` / `net.cidr_is_valid`). CIDR math must be hand-rolled: `split`, `to_number`, a 33-entry power-of-two table, and `floor` (which does exist). The gap matters less than it looks: the engine already ships cross-resource CIDR checks — subnet-not-within-VPC is `E3059` and sibling-subnet overlap is `E3060` (both fire on `Ref`-wired resources in one template).
|
|
129
136
|
- The engine validates instance/node types **against the deploy region** when one is supplied via `validateDetailed(tpl, { pseudoParameterOverrides: { region } })` — `E3628` ('trn1.32xlarge' is not valid for region 'ap-northeast-1'), measured 2026-09-03. Without a region it validates against the union of all regions and emits `I9003`. The enforce plugin passes the region whenever the app's env is concrete, so region-availability rules for instance types belong to the engine, not this pack.
|
|
130
137
|
- **`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-*`.
|
|
138
|
+
- **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:
|
|
139
|
+
- *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+=,.@-]+$`).
|
|
140
|
+
- *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])?$`.
|
|
141
|
+
- **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.)
|
|
142
|
+
- **Tuple destructuring in `some [a, b] in <array literal>` does not schedule** ("statements not scheduled in query", measured 2026-09-04). Iterating a table of pairs inline is not available; build a set with one helper rule per case and destructure that set (`some [name, path, v] in _pf_x_bad`), which does work.
|
|
143
|
+
- **Schema pattern/length coverage is per property, not per resource or per service.** `AWS::EC2::SecurityGroup.GroupDescription` carries a pattern (`F3031`) while the rule-level `SecurityGroupIngress[].Description` on the same resource carries none; DynamoDB's GSI `IndexName` has one while the table's own `TableName` does not. Never generalise "the engine handles names/lengths" from a sample — probe the exact property (measured 2026-09-04 across 57 templates).
|
|
131
144
|
- **Probing the raw engine with custom rules**: the rule set must be named `violation` (`data.<pkg>.violation` is looked up; a `diagnostics` set fails with "not a valid rule path"), and hand-built diagnostic objects need lowercase severities (`error`, not `ERROR`/`CUSTOM` — those are display labels added by the wrapper). Pack rules never see this because `make_diag_full` handles the shape.
|
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.
|
|
20
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-preflight.Preflight", version: "0.0.29" };
|
|
21
21
|
/**
|
|
22
22
|
* Register the cdk-preflight rules on an App or Stage.
|
|
23
23
|
*/
|
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"post-compile": "projen post-compile",
|
|
28
28
|
"post-upgrade": "projen post-upgrade",
|
|
29
29
|
"pre-compile": "projen pre-compile",
|
|
30
|
+
"redundancy-scan": "projen redundancy-scan",
|
|
30
31
|
"release": "projen release",
|
|
31
32
|
"test": "projen test",
|
|
32
33
|
"test:watch": "projen test:watch",
|
|
@@ -87,7 +88,7 @@
|
|
|
87
88
|
"publishConfig": {
|
|
88
89
|
"access": "public"
|
|
89
90
|
},
|
|
90
|
-
"version": "0.0.
|
|
91
|
+
"version": "0.0.29",
|
|
91
92
|
"jest": {
|
|
92
93
|
"coverageProvider": "v8",
|
|
93
94
|
"testMatch": [
|