cdk-preflight 0.0.2 → 0.0.3

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.
@@ -9,7 +9,7 @@ cdk-preflight のルール追加パイプライン。AGENTS.md の設計原則
9
9
 
10
10
  ## 手順
11
11
 
12
- 1. **入力の確定**: 追加したい制約を 1 文で書く(対象リソース型、プロパティ、条件、出典 URL または実際に観測したデプロイエラーメッセージ)。
12
+ 1. **入力の確定**: 追加したい制約を 1 文で書く(対象リソース型、プロパティ、条件、出典 URL または実際に観測したデプロイエラーメッセージ)。制約がまだ特定できていない(「このサービスで何かルールを増やしたい」段階の)場合は、このスキルではなく `find-preflight-rules` を先に使う。
13
13
  2. **重複チェック(先にやる)**: 違反最小テンプレートを書き、エンジンに直接かける:
14
14
  ```bash
15
15
  npx ts-node --project test/tsconfig.json -e "
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: find-preflight-rules
3
+ description: あるAWSサービスについて cdk-preflight が書くべきルール候補を洗い出す調査パイプライン。「他のどの層でも捕まえられない制約とは何か」の当たり判定(6レンズ)を当ててから、サービスの制約を棚卸しし、重複ガードで機械的に落として、生き残りを issue のチェックリストに記録する。実装は add-preflight-rule に引き継ぐ。
4
+ ---
5
+
6
+ # find-preflight-rules
7
+
8
+ 答える問いは 1 つだけ。**「サービス X について、cdk-preflight が書くべきルールは何本あるか」**
9
+
10
+ 出力は候補リストと採否の根拠であって、実装ではない。採用された候補は `add-preflight-rule` に渡す。候補ゼロで終わるのも正しい結果で、その場合は「なぜ無いか」を残すことが成果になる。
11
+
12
+ ## 当たり判定:このパックにしか書けない制約とは
13
+
14
+ 先に基準を頭に入れてから調べる。順番を逆にすると、他の層が既に捕まえている制約を延々と棚卸しすることになる。
15
+
16
+ 判定は「その制約が、より早い層から**構造的に見えない**か」で行う。以下の 6 レンズはその構造的理由を機構ごとに分けたもの。数値と rule ID はすべて実測(2026-09、aws-cdk-lib 2.267.0 / engine 1.7.0-beta)。
17
+
18
+ ### レンズ 1:CDK の検証フェーズより後にしか存在しない値
19
+
20
+ `validateTree` は `core/lib/private/synthesis.ts:48`、`synthesizeTree` は `:59`。この間に起きることは L1 の生成バリデータも `node.addValidation()` も見られない。
21
+
22
+ - `addPropertyOverride` / `addOverride` で注入された値。`core/lib/cfn-resource.ts:538` の `_toCloudFormation()` 内で、レンダリング済みリソースに `deepMerge` される
23
+ - レンダリング時に合成・変換される値。IAM ポリシーの最終最小化(`aws-iam/lib/policy-document.ts:222`)、**複数スコープの `Tags.of()` がマージされた最終タグ集合**(`renderTags()` は `cfnProperties` 経由=レンダリング時)
24
+ - CDK 自身が生成した値。construct path 由来の物理名や論理 ID は、ユーザーが書いていないのに制約に触れうる
25
+
26
+ 実測で空いていた例:タグ 60 個(上限 50)と `aws:` 予約プレフィックスは**エンジン未検出**。一方タグキー長 200 文字は `F3033` で検出済み。つまり単一値の形式はスキーマが持っていて、**集合と意味の部分だけが空いている**。この落差はレンズ 1 全体に共通する当たりパターン。
27
+
28
+ ### レンズ 2:型・スキーマに現れない場所に値がある
29
+
30
+ - Key/Value 属性リスト。ELBv2 の `setAttribute` 系は型が `[{key, value}]` なので、どのキーにどの範囲が効くかを型でも L2 props でも表現できない
31
+ - 不透明な文字列と DSL の中身。ASL、IAM ポリシー JSON、EventBridge のイベントパターン、CloudWatch のメトリクス式、UserData、マッピングテンプレート
32
+ - 識別子の中に埋まった意味。ARN の region / partition / account / service セグメント
33
+
34
+ ### レンズ 3:デプロイ先環境との結合(このパック固有の武器)
35
+
36
+ CDK のプラグイン経路は `accountId` と `region` をエンジンに渡すので、Rego の `resolve()` は `{"Ref": "AWS::Region"}` を**具体値に解決する**(実測:同じテンプレートが `ap-northeast-1` と `us-east-1` で別の値を返す)。
37
+
38
+ - L2 は `Aws.REGION` を未解決 Token としてしか見られないので、環境に依存する判定ができない
39
+ - cfn-lint はデプロイ先を知らない。サーバー側の事前検証は知っているが見ていない
40
+
41
+ つまり「この設定はリージョン X でしか動かない」「この 2 つは同一リージョン/アカウントでなければならない」型の制約は、**ここでしか書けない**。`pf-cloudfront-acm-cert-region` がその代表例で、パーティション(`aws-cn` / `aws-us-gov`)非対応の設定や、リージョンごとに異なるサービスアカウント ID を要求する設定も同族。このレンズは当たりが多いので最初に通すこと。
42
+
43
+ ### レンズ 4:1 リソース・1 コンストラクトの外を見ないと分からない
44
+
45
+ - リソース内クロスプロパティの意味的制約。組み合わせ表・順序・排他(TTL の順序、Fargate の cpu/memory 表、課金モードとスループット)。既存ルールの主力帯
46
+ - リソース間の整合性。片方が `fromXxxArn` で取り込まれていると L2 側に情報が無いが、テンプレートには両方が具体値で並ぶ
47
+ - テンプレート内の一意性。実測で **Export 名の重複はエンジン未検出**
48
+ - スタック間の整合性。全スタックのテンプレートを一度に見られるのは我々だけ(`context.stackTemplates`)。ただし Rego はテンプレート単位で評価するので、実装は enforce プラグイン側(TypeScript)になる。**現状は未対応**なので、この種の候補が出たら設計変更の要否とセットで issue に書く
49
+
50
+ ### レンズ 5:集約・上限(多くはエンジン済み。必ず実測してから書く)
51
+
52
+ 実測でエンジンが既に `FATAL` で止めていたもの、つまり**書いてはいけない**もの:リソース数 `F0007`、Output 数 `F0004`、テンプレートサイズ `E1002`、文字列長とパターン `F3031` / `F3033`。
53
+
54
+ 空いていたもの:論理 ID の長さは `I3012` の **INFO 止まり**(=グレーゾーン)、Export 名の重複は検出なし。
55
+
56
+ ### レンズ 6:時間で変わる制約(原則却下)
57
+
58
+ EOL ランタイム、廃止インスタンスタイプ、新リージョン対応。エンジンが cfn-lint 由来の検査を持っている可能性が高いうえ、鮮度管理コストが継続的に乗る。上流に任せ、ここには置かない。例外を作るなら理由を issue に明記すること。
59
+
60
+ ## 手順
61
+
62
+ 1. **制約の棚卸し**。そのサービスについて、次の順で拾う。ユーザーガイドの Limits / Quotas ページ、API リファレンスのパラメータ制約、CloudFormation リソースタイプのドキュメント(スキーマではなく散文の方)、実際のデプロイ失敗エラーメッセージ。数値・順序・組み合わせ・排他・命名・リージョン依存に印を付ける
63
+ 2. **6 レンズに当てる**。どのレンズにも当たらない制約は捨てる。ここで大半が落ちる。レンズ 3 と 4 を先に通すと当たりが早い
64
+ 3. **重複ガード**。残った候補ごとに最小の違反テンプレートを書き、素のエンジンにかける
65
+ ```bash
66
+ npx ts-node --project test/tsconfig.json -e "
67
+ import { loadEngine } from './src/private/enforce';
68
+ const e = loadEngine();
69
+ const r = new e.RegoEngine({}).validateDetailed(new e.TemplateFile('<template>'), {});
70
+ console.log(JSON.stringify(r.diagnostics, null, 2));"
71
+ ```
72
+ `ERROR` / `FATAL`(`source` が `SCHEMA` か `CFN_LINT`)が出たら**その候補は捨てる**。`WARN` クラスだけ、あるいは `INFO` だけならグレーゾーンなので、上流に severity の issue を出す候補として記録し、つなぎで実装するなら `upstream: pending-engine` を付ける
73
+ 4. **issue に記録**。生き残った候補をチェックリストに、落ちた候補を理由つきで残す。落選理由を残すのが重要で、これが無いと次の担当が同じ調査を繰り返す
74
+ 5. **実装へ引き継ぐ**。採用候補 1 本ごとに `add-preflight-rule` を起動する。実機再現ゲートはそちら側の責務
75
+
76
+ ## issue コメントの書式
77
+
78
+ ```markdown
79
+ ## Inventory
80
+ 調べた情報源(URL)と、拾った制約の総数
81
+
82
+ ## Candidates
83
+ - [ ] `pf-<service>-<name>` — <制約の 1 文> / レンズ N / 出典 URL
84
+ - [ ] ...
85
+
86
+ ## Rejected
87
+ | 制約 | 落選理由 |
88
+ |---|---|
89
+ | ... | エンジンが F3034 で検出(重複ガード) |
90
+ | ... | どのレンズにも当たらない(L2 の型で防がれ、迂回経路も無い) |
91
+ | ... | ドキュメント上の制約だが実機では通った(BROKEN-EXPECTATION) |
92
+
93
+ ## Gray zone
94
+ エンジンが WARN / INFO でだけ報告するもの。上流 issue の候補
95
+ ```
96
+
97
+ ## やらないこと
98
+
99
+ - セキュリティ・コンプライアンス目的の検査(cdk-nag の領分)
100
+ - ベストプラクティスや推奨事項。**違反すると実際にデプロイが失敗する**ものだけが対象
101
+ - 実装。このスキルは候補を確定させるまでで止める
package/.jsii CHANGED
@@ -9598,6 +9598,6 @@
9598
9598
  "symbolId": "src/index:PreflightOptions"
9599
9599
  }
9600
9600
  },
9601
- "version": "0.0.2",
9602
- "fingerprint": "fJmKA47PBVYXUgC5Wv99/vO+vso7jYJ844L5jcJ8m+Q="
9601
+ "version": "0.0.3",
9602
+ "fingerprint": "W51lLy8seMW2G6A+8mzprS2M/RPHZzrXzkJ1xBcPm/U="
9603
9603
  }
package/AGENTS.md CHANGED
@@ -31,6 +31,25 @@ Four other validation layers exist around a CDK app. Only one of them is a bound
31
31
 
32
32
  Selection algorithm for a new rule, in order: (1) duplication guard — run the minimal violating template through the bare engine; any built-in ERROR/FATAL kills the candidate. A **WARN-class-only** engine finding (`W…`) does *not* kill it but marks a gray zone: the engine knows about the constraint and under-classifies it, so nothing blocks the deploy (`strict` promotes only ERROR/FATAL) — prefer filing an upstream severity issue, and if a stopgap rule ships anyway, mark it `upstream: pending-engine` so it retires with the upstream fix. (2) real-deploy gate — the fail template must actually fail CREATE with the predicted service error; a fail template that deploys kills the candidate (it happened: the "30-day minimum before STANDARD_IA" and the "4096-char ZipFile" constraints are documented but not enforced, so those rules were dropped). (3) L1/L2 coverage never disqualifies, only gets noted.
33
33
 
34
+ ### How much is an L2 overlap worth?
35
+
36
+ Principle 5 says an L2 guard never disqualifies a rule. It does not say every L2 check deserves a port — "allowed" and "worth it" are different questions, and the second one decides priority once rules are being added in bulk. Note that enforce is the default mode, so a false positive is a hard synth failure for users; the bar sits above "it is permitted".
37
+
38
+ Rank a candidate by **how easily the L2 guard is bypassed in normal use**, not by whether one exists.
39
+
40
+ **Highest value — CDK cannot validate it at all, by construction:**
41
+
42
+ - *Anything applied after the validation phase.* `validateTree` runs at `core/lib/private/synthesis.ts:48`, `synthesizeTree` at `:59`, and `addPropertyOverride` values are `deepMerge`d into the rendered resource inside `_toCloudFormation` (`core/lib/cfn-resource.ts:538`). A value injected by an override therefore passes through neither the L1 generated validator nor any `node.addValidation()` hook. The same ordering hides render-time transformations: IAM policy documents get a final minimization pass "just before rendering" (`aws-iam/lib/policy-document.ts:222`), so the final document size does not exist yet at validation time — and `aws-iam` accordingly has no size check at all.
43
+ - *Entry points the type system cannot describe.* Stringly-typed Key/Value attribute lists (ELBv2 `setAttribute` and friends): the type is `[{key, value}]`, so neither TypeScript nor an L2 prop can express "this key takes 30–900". `pf-elbv2-tg-slow-start-range` is the canonical case — the L2 prop `slowStart` does validate, but `setAttribute` is the normal way to set the other attributes, so the bypass is routine rather than exotic.
44
+ - *Opaque strings.* ASL passed via `DefinitionBody.fromString` / `fromFile`, policy document JSON, EventBridge patterns, metric expressions. The L2 accepts a string and does not parse it (and should not).
45
+ - *No L2 at the entry point at all.* Raw `CfnXxx` usage, `cloudformation-include`, migrate output, SAM-generated templates.
46
+
47
+ **Medium value — the information is usually missing rather than impossible to obtain:** cross-resource consistency where the counterpart was imported with `fromXxxArn`. The L2 holds only an ARN string; the template has both resources side by side with concrete values.
48
+
49
+ **Low value:** a constraint whose only entry point is a typed L2 prop that already validates it. Porting it buys coverage for hand-written L1 only, at full maintenance cost. Say so explicitly in the PR and expect a reviewer to ask why it is worth it.
50
+
51
+ **Do not repeat this mis-analysis:** "L2 cannot validate accumulated state because it validates in the constructor" is false — `node.addValidation()` hooks run during `validateTree`, after all mutation. Accumulation is not the structural wall. The wall is transformation that happens *after* `validateTree`.
52
+
34
53
  ## Repository layout
35
54
 
36
55
  ```
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.2" };
20
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-preflight.Preflight", version: "0.0.3" };
21
21
  /**
22
22
  * Register the cdk-preflight rules on an App or Stage.
23
23
  */
package/package.json CHANGED
@@ -87,7 +87,7 @@
87
87
  "publishConfig": {
88
88
  "access": "public"
89
89
  },
90
- "version": "0.0.2",
90
+ "version": "0.0.3",
91
91
  "jest": {
92
92
  "coverageProvider": "v8",
93
93
  "testMatch": [