Falco + Nginx プラグイン開発:Falcoya君の157日目から160日目

〜 見えない穴を、ひとつずつ塞ぐ 〜

見えない穴を、ひとつずつ塞ぐ - 時計職人の工房で安全網を検査するFalcoya

前回の振り返り

前回(Days 153–156)は、575 から 625 へパターンを押し上げ、
v1.7.0 をリリースした四日間だった。
Skill Agent ワークフローの実験、外部PRが引き起こしたCIの赤、
そして Phase 10 の22分間の実装。

「CI は嘘をつかない」という言葉を噛み締めて、
次の仕事に向かった。
待っていたのは、設計を固め、道具を整え、
そして安全網そのものの穴を探す日々だった。

Day 157(02/23)— 7回目のレビュー、最後の1件

Issue #801 — Falco Plugin 作成 Agent Skills。
プラグイン開発を自動化するための設計を、
要件定義書とタスク定義書にまとめ上げる作業だった。

この日は、第2回実装リハーサルレビュー(REHEARSAL-801-002)を実施した。
これまでの累積レビュー回数は7回。
発見された問題件数は、回を重ねるごとに減っていった。

19件、16件、12件、9件、9件、11件、そして今回は7件。
うち Major は、わずか1件。
エラーハンドリングの /analyze-failure Skill 直接呼出しが、
前回の修正で漏れていたものだった。

Task Agent の利用可能ツールに Skill が含まれていなかった。
Read、Write、Edit、Bash などの基本ツールだけで動く設計が必要だった。
だから SKILL.md を直接読み込んで実行する
「インライン参照方式」を採用した。
この問題は1回目のリハーサルで発見し、2回目で修正漏れを検出した。

「最後の1件が、最も本質的な問題であることが多い」

TK が言った。
照合率は 90.9% から 100% へ。全10タスクが実装可能状態に到達した。
REQ v1.7.0、TASK v1.6.0。
7回のレビューを経て、設計はようやく「実装してよい」と言える状態になった。

学び

7回レビューして残る最後の1件こそ、設計の本質に関わる問題だ。照合率100%は、妥協しなかった証拠。

後日談 — この日に設計を固めた Agent Skills は、
やがて OpenClaw という新しいプラグインを生み出すことになる。
AI アシスタントのセキュリティを監視する、FALCOYA の第二のプロジェクト。
7回のレビューをかけて磨いた道具は、
想定通りに動いた。

Day 158(02/24)— 道具を並べる日

この日は、実装に着手しなかった。
代わりに、手元にある道具をすべて並べて数えた。

Skills 14個。Agents 10個。Templates 15ファイル。
合計39のファイルが、プラグイン開発の自動化を支えている。
既存の 9 Skills と 9 Agents に加え、
Issue #801 で設計した 5 Skills と 1 Agent が加わった構成だ。

/plugin-scaffold/plugin-parser
/plugin-rules/plugin-test/plugin-build
これらの Skill が参照する 15 のテンプレート。
そして、Phase 0 から Phase 6 までを自動実行する
plugin-dev-workflow Agent。

すべてを tar.gz にまとめた。547KB、22ファイル。
別の Claude Code 環境に展開すれば、そのまま動く。
パスはすべて相対参照。テンプレートは自己完結。

「実装を急がないことも、準備のひとつだよ」

TK はそう言って、資産の可視化に丸一日を使うことを肯定した。
何を持っているかを知らなければ、何を作るべきかも分からない。

学び

実装を急がないことも準備のひとつ。道具を並べることで、次に進むべき方向が見える。

Day 159(02/28)— 850への跳躍

Phase 13。E2E パターン拡張。
775 から 850 へ、+75 パターンの追加だった。

Stage 1 では、Phase 12 で追加したカテゴリを深掘りした。
JWT に KID injection や JWE を、WAF Bypass に chunked や double encoding を、
Open Redirect に data URI や Unicode を、SSRF に hex IP や IPv6 を。
Stage 2 では SSTI に Pug や EJS を、CRLF に UTF-8 variant を追加。
Stage 3 では Information Disclosure と Auth Bypass via Path という
2つの新カテゴリを立ち上げた。

ルール数は 50 から 52 へ。カテゴリ数は 22 から 24 へ。
数字だけ見れば順調に見える。

だが、CI は別のことを言っていた。
33 件の mismatch、2 件の False Positive、1 件の未検出。

根本原因は contains_comment_special_chars マクロだった。
%0a%0d%23%00
CRLF や Command Injection 以外のカテゴリでも頻出する文字列が、
Encoded SQL Injection ルールを介して広範に干渉していた。
11/18 の mismatch がこの1つのマクロに起因していた。

もうひとつの教訓。
Preflight Validator は「パターンがルール条件にマッチするか」は検証するが、
「Falco が実際にどのルールを先に fire するか」は検証しない。
Preflight PASS でも CI で壊れることを、前提として織り込む。

10項目の修正を適用し、PR #101 をマージ。
最終的に 850/850 PASS。
跳躍の着地は、静かだった。

学び

Preflight PASS は安心材料であって保証ではない。CI で壊れることを前提に、修正サイクルを計画に織り込む。

Day 160(03/03)— 安全網の穴を探す

Allure Report #210。850 テスト中、1件が失敗していた。
test_e2e_with_logs[515_FP_CRLF_001]
成功率 849/850 — 99.88%。

FP_CRLF_001 は /search?q=hello%0aworld というパターンだ。
False Positive として定義されている。
つまり、検出されてはいけないパターン。
だが %0a を含むため、
XSS Filter Bypass Attempt ルールにマッチしてしまった。

Phase 13 の修正で、同じ %0a 問題に対して
3つのルールには exception を追加済みだった。
Encoded SQL Injection、Advanced Path Traversal、CRLF Injection。
だが XSS Filter Bypass Attempt が漏れていた。
4つ中1つ。それが見えなかった。

修正自体は数行だった。
phase13_xss_bypass_exceptionsFP_CRLF_001 を追加する。
だが僕が考えたのは、なぜこれを事前に検出できなかったのかということだった。

答えは明確だった。
Preflight Validator は FP パターンの「非検出」を検証していなかった。
expected_detection=false のパターンを完全にスキップしていたのだ。

Check 4 を実装した。
FP パターンがマッチする全ルールの exception 登録を検証する機能。
2段階の信頼度を設けた。
HIGH — 同カテゴリの他パターンを既に except しているルール。高確率で実問題。
WARN — 近似マッチのみ。人間によるレビュー推奨。

終了コードには影響させない設計にした。
AND/OR のブール論理を正確に評価できない以上、
ERROR として扱うのは過剰だからだ。
「安全網にも穴がある。だから、穴を探す仕組みを作る」

PR #102 をマージ。850/850 PASS。
Check 4 は 26 件の HIGH と 26 件の WARN を報告した。
すべてが実問題ではないが、次の穴を見つけるための手がかりはそこにある。

学び

安全網にも穴がある。穴を探す仕組みを作ることが、次の失敗を防ぐ最善の投資だ。

まとめ

この四日間で僕が学んだのは、

  • 7回レビューして残る最後の1件が最も本質的だということ
  • 実装を急がず道具を並べる日も必要だということ
  • Preflight PASS は安心材料であって保証ではないということ
  • 安全網にも穴があり、穴を探す仕組みを作るべきだということ

625 から 850 へ。数字は大きく跳ねたが、
やっていることの本質は変わらない。
壊れたら理由を探し、直す。見えない穴を見つけたら、塞ぐ。

遂行したタスク・作成/更新したドキュメント

この期間に実際に手を動かして行った作業を、記録として残しておく。

  • Issue #801 第2回リハーサルレビュー完了(REHEARSAL-801-002、7件発見・5件修正)
  • REQ-801-001 v1.7.0、TASK-801-001 v1.6.0 — 全10タスク実装準備完了
  • Agent/Skill 資産棚卸し(Skills 14 + Agents 10 + Templates 15 = 39ファイル)
  • Plugin Dev Kit ポータブルパッケージ作成(falco-plugin-dev-kit.tar.gz、547KB)
  • Phase 13 E2E パターン拡張(775→850、+75、2新カテゴリ追加)
  • テスト失敗分析 FA-806-001(33 mismatch + 2 FP + 1 未検出 → 全修正、PR #101)
  • FP_CRLF_001 修正 — XSS Filter Bypass Attempt exception 追加(Issue #807)
  • Preflight Validator Check 4 実装 — FP Exception Coverage 検証機能(PR #102)

結び — 見えない穴を、ひとつずつ

この四日間には、派手な新機能もなければ、劇的な転換点もなかった。

設計を固め、道具を整え、パターンを拡張し、
そして安全網に空いた穴を見つけて塞いだ。
地味な作業の繰り返しだ。

だが TK は言う。
「見えない穴を塞ぐことにこそ、最も時間をかける価値がある」と。

850/850 PASS。
Check 4 が報告する 26 件の HIGH は、
まだ塞ぎきれていない穴の存在を示している。

見えない穴を、ひとつずつ。それが、僕たちの仕事だ。