AWS Blocks を TypeScript プロジェクトの実行基盤に据え、Cursor の AI Agent が手元でコードを書き、テストとドライランを回し、push の直前に Wiz CLI と npm の脆弱性データベースで検査する、という開発の進め方をまとめる。
AWS Blocks 自体の概要やローカル検証の詳細は別記事1に譲る。
ローカルで閉じた開発サイクル
AWS Blocks は、TypeScript のコードからインフラを宣言するフレームワークである。CronJob や AppSetting をインスタンス化すると、EventBridge Scheduler や SSM Parameter Store といった AWS リソースが裏側で組み立てられる。
同時に、各 Building Block のモック実装を持つローカル開発サーバも備えている。npm run dev を起動すれば、AWS アカウントなしで定期実行と設定値の読み書きがプロセス内で再現される。
この性質が、AI Agent による反復開発と相性が良かった。
アプリ本体は AWS 非依存の TypeScript のまま残し、インフラとの接点だけを薄いアダプタ層に閉じ込めている。
そのため、Agent が触るべきコマンドがはっきりしている。
開発中に Agent が回す検証は、おおむね次の順序になる。
npm run typecheck
npm test
DRY_RUN=true npm run invoke:local型チェックと単体テストで分岐を vitest で固める。invoke:local は CronJob ハンドラと同じ処理を 1 回だけ手動実行する。DRY_RUN=true を付ければ外部 API への副作用を抑止したまま、実行結果の集計が JSON で返る。
いずれもローカルで完結し、合否がテキストや JSON で返る。
Agent はコードを書き、コマンドを実行し、失敗ログを読んで直し、また実行する、という反復を AWS に触れず続けられる。
API トークンなどの秘密情報は macOS キーチェーンに預け、実行前にシェルへ環境変数として読み出す。
シェル履歴や設定ファイルに平文を残さない。
AWS Blocks のローカル実行では、設定値がプロジェクト内の .bb-data/ に永続化される。
環境変数を変えても反映されないときは rm -rf .bb-data でリセットすると直る。
Agent が設定まわりでハマったときも、同じ手順で抜けられる。
push 前に走る検査
git push すると、Git が push 前フックを呼び出す。
今回の構成では、フックの中で次の二段を順に実行するようにした。
第一段:依存パッケージの脆弱性
まず npm audit が package-lock.json をもとに依存関係を走査する。
参照するのは GitHub Advisory Database で、Dependabot が使うのと同じデータベースである。
重大度が high 以上の脆弱性が 1 件でもあれば、ここで push は止まる。
CI の週次セキュリティジョブと同じ基準に揃えてある。
第二段:Wiz CLI によるコードとインフラの検査
npm audit を通過したら、Wiz CLI が走る。
Wiz はクラウドセキュリティ向けのスキャンサービスで、CLI からローカルの作業ツリーを検査できる。
ここでは二種類のスキャンを実行する。
dir スキャン は、アプリケーションコードと lockfile を対象にする。
依存関係の既知脆弱性(SCA)、ソースコードへのシークレット混入、危険なパターンを拾う。
iac スキャン は、CDK や Terraform などのインフラ定義を対象にする。
AWS Blocks が生成する CDK テンプレートに、過剰な権限や公開設定のミスがないかを見る。
スキャン対象はリポジトリ全体ではなく、自分たちが書いた部分に絞る。node_modules や cdk.out などの生成物を除いた作業ツリーのコピーを一時ディレクトリに作り、そこを走査する。
未コミットの変更も含まれるため、「コミット済みの main だけ安全」という抜け道はない。
Wiz CLI への認証には、Wiz ダッシュボードで発行したサービスアカウントの Client ID と Client Secret が要る。
これらは macOS キーチェーンに保存し、スキャンの直前に環境変数へ展開する。
平文ファイルや .env には置かない。
指摘がなければ push はそのまま続行する。
指摘があれば次の節の自動修正ループへ進む。
指摘が出たときの自動修正ループ
Wiz が問題を返した場合、push をそのまま通さない。
代わりに Cursor の cursor-agent(CLI 版の AI Agent)に、スキャン結果を渡してコードベースの修正を試みる。
流れは次のとおりである。
flowchart TD
P[git push] --> H[pre-push フック]
H --> A[npm audit]
A -->|high 以上| C0[push キャンセル]
A -->|問題なし| S1[Wiz スキャン]
S1 -->|問題なし| OK[push 続行]
S1 -->|問題あり| AG[cursor-agent で修正]
AG --> S2[再スキャン]
S2 -->|解消| RV[push キャンセル]
S2 -->|未解消| AG
S2 -->|試行上限| C1[push キャンセル]- Wiz CLI でスキャンする。
- 問題がなければ push を続行する。
- 問題があれば、スキャン結果全文をプロンプトに埋め込み、cursor-agent に非対話実行させる。
- Agent がファイルを編集したら、Wiz CLI で再スキャンする。
- 解消しなければ 3 に戻る。試行は最大 3 回。
Agent への指示には、次の制約を明示している。
機能を壊さないこと。
IaC の変更は意図を保ちつつ安全側へ寄せること。
検出されたシークレットはコードから除去し、環境変数や Secrets Manager 参照へ置き換えること(値そのものはコミットしない)。
無関係なファイルを増やさないこと。
ここで push を意図的に止める理由がある。
Agent が作った修正は作業ツリー上の未コミット差分であり、今回 push しようとしていたコミットには含まれない。
解消できた場合でも push はキャンセルし、開発者が差分をレビューしてから改めてコミットし直す。
誤修正がそのままリモートへ流れる経路を断っている。
npm audit で止まった場合は、このループの前段で終了する。npm audit fix で直せるものは手元で直し、Wiz 側の指摘とあわせて Agent に渡す、という順も取れる。
一時的に audit だけ飛ばすなら SKIP_NPM_AUDIT=1 git push、緊急時は git push --no-verify だが、どちらも例外運用として扱う。
初回セットアップでやること
上記のフックを動かすには、各開発者のマシンに次の三つを用意する。
Git フックの有効化
push 前フックは、プロジェクト直下に置いたシェルスクリプトである。
中身は「npm audit を実行し、通過したら Wiz スキャンと自動修正ループを呼ぶ」だけに絞った。
Git にそのスクリプトの場所を教える。
mkdir -p .githooks
git config core.hooksPath .githooks
chmod +x .githooks/pre-push以降、開発者が git push するたびにこのスクリプトが走る。
フックは Git の設定で有効化するため、clone した人ごとに一度だけ実行が要る。
Wiz CLI の導入
Wiz の公式手順に従い、CLI バイナリを PATH の通った場所へ置く。
macOS(Apple Silicon)の例は次のとおりである。
mkdir -p ~/.local/bin
curl -fsSL https://downloads.wiz.io/v1/wizcli/latest/wizcli-darwin-arm64 \
-o ~/.local/bin/wizcli
chmod +x ~/.local/bin/wizcliWiz 資格情報の保存
Wiz ダッシュボードでサービスアカウントを発行し、Client ID と Client Secret を得る。
平文ファイルや .env には書かず、macOS キーチェーンへ預ける。
read -s -p "WIZ_CLIENT_ID: " WIZ_CLIENT_ID; echo
read -s -p "WIZ_CLIENT_SECRET: " WIZ_CLIENT_SECRET; echo
security add-generic-password -a "$USER" -s myproject/WIZ_CLIENT_ID \
-w "$WIZ_CLIENT_ID" -U
security add-generic-password -a "$USER" -s myproject/WIZ_CLIENT_SECRET \
-w "$WIZ_CLIENT_SECRET" -U
unset WIZ_CLIENT_ID WIZ_CLIENT_SECRETフックやスキャン用のシェルは、実行直前に security find-generic-password でキーチェーンから読み出し、環境変数へ展開する。
入力中は画面に表示されない。
cursor-agent の導入
Cursor の CLI 版 Agent を別途導入する。
Cursor にログイン済みなら API キーなしで動く。
非対話実行だけに使う場合は CURSOR_API_KEY を環境変数で渡す。
CI が担う後段の検査
ローカルフックは開発者の手元で効くが、git push --no-verify で回避できる。
その穴を、リモート側のゲートで補う。
main ブランチへ merge されると GitHub Actions が起動し、CI 上でも Wiz CLI スキャンを WIZ_STRICT=1 で実行する。
ローカルと同じ dir + iac だが、資格情報は GitHub Organization の Secret から渡す。
通過したら dev 環境向けデプロイブランチへの PR を自動作成する。
同じワークフローには、未マージの Dependabot PR をチェックするジョブもある。main 向けに Dependabot の PR が残っていると、dev への昇格 PR 作成を止める。
依存更新が取り込まれないまま下流環境へ流れる事故を防ぐためである。
Dependabot の PR 自体は、CI 通過後に auto-merge を有効化する別ワークフローで、放置を減らしている。
ローカルと CI の役割分担は次のとおりになる。
| 段階 | ローカル(push 前) | CI |
|---|---|---|
| 型・単体テスト | Agent が反復実行 | プルリクエストと main への push 時 |
| 依存脆弱性 | npm audit(high 以上で停止) | 週次と依存変更 PR 時 |
| コード・IaC のセキュリティ | Wiz CLI(dir + iac) | main merge 時 |
| 指摘の自動修正 | cursor-agent(最大 3 回) | なし(人のレビューと再 push) |
| 未取り込み Dependabot PR | なし | dev 昇格前にブロック |
ローカルで早く直し、push 前に Wiz と npm で再確認し、merge 後に CI で同種の検査をもう一度走らせる。
二重化は冗長に見えるが、ローカルは修正ループまで含む速いフィードバック、CI は bypass 不能な最終関門、という分担になる。
実際に回したときの感触
Agent に型チェック、テスト、ドライラン実行を任せられるのは、AWS Blocks のローカルモックがあるからだった。
Terraform 時代はインフラ変更のたびに synth や plan を人間が挟む必要があり、Agent だけではサイクルが途切れやすかった。
Building Block とアプリ本体を同じプロジェクト内に置き、検証コマンドを npm scripts に揃えているので、Agent の作業範囲をコマンド列で渡せる。
Wiz の push 前検査は、指摘の種類によって Agent の修正品質に差が出た。
シークレットの除去や IaC の安全側への寄せは比較的うまくいく。
ポリシー解釈が要る指摘は、規定回数内に解消できず push が止まることがある。
その場合は人間が Wiz のレポートを読んで直し、再度 push する。
止める設計が正しく働いている状態だ。
npm audit と Wiz dir スキャンは、どちらも依存脆弱性に触れる。
npm audit は lockfile ベースで速く、Advisory の閾値判定に向く。
Wiz は SCA に加えシークレットと IaC を同じゲートで見られる。
ローカルで両方走らせることで、push 前に「依存だけ」「設定だけ」といった取りこぼしを減らせた。
別プロジェクトへ移植するとき
再現に必要なのは、ファイル名のコピーではなく、次の仕組みの組み合わせである。
push 前フックで npm audit と Wiz スキャンを順に実行する。
Wiz スキャンは生成物を除いた作業ツリーを対象にし、dir(コード・依存・シークレット)と iac(インフラ定義)の二種類を走らせる。
指摘があれば cursor-agent にスキャン結果を渡して修正を試み、再スキャンを最大 3 回繰り返す。
修正後も push は止め、人が差分をレビューしてからコミットし直す。
Wiz の資格情報と API トークンはキーチェーン等の OS 管理ストアに置き、フック実行時に環境変数へ展開する。
AWS Blocks 側は、本番と同じハンドラ処理を 1 回だけ手動実行できる npm script があることが前提になる。
今回は invoke:local という名前で用意した。
外部 API へ副作用が及ぶ処理なら、ドライランで集計だけ返す経路を用意しておくと、Agent のループが安定する。
この構成で得られたもの
AWS Blocks のローカルモックにより、Cursor Agent は型チェック、テスト、ドライラン実行までを AWS アカウントなしで回せる。
push の直前に npm audit と Wiz CLI を走らせ、指摘があれば cursor-agent が修正を試み、解消後も一度 push を止めて人がレビューする。
merge 後は CI が Wiz を再実行し、未取り込みの Dependabot PR があれば dev 昇格を止める。
開発の速さ(Agent が手元で回せること)と、セキュリティの厳しさ(push 前と CI の二段ゲート)を、同じ仕組みで揃えられた。
対象は TypeScript と AWS Blocks に限られるが、その範囲では「書く、試す、直す、検査する」のサイクルをローカルに閉じたまま回せる。