コマ2 / テンプレートから最初の1本を通す
テンプレートから
プロトタイプを完成させる
このコマの目的は、完成度の高いエージェントを作ることではない。 テンプレートをペンテスト向けに差し替え、偵察 → 侵入 → 権限昇格の3つのサブエージェントが端から端まで連携する状態を作ること。
ここで作るのは、3人が並行開発に入るための土台。 共通スキーマ、安全機構、パイプラインの順序、サブエージェント間の受け渡しを先に固める。 個別エージェントの作り込みは、ゲートAを通過してから始める。
00このコマのゴール
コマ2の完了条件は、「型が確定して、サブエージェントが端から端まで連携する」こと。 まだ賢くなくてよい。まだ root を取れなくてよい。3人が同じ前提で開発できる形にする。
共通スキーマを確定する
Target、Finding、Foothold、PrivEscResult など、エージェント間で受け渡す型を決める。
サブエージェント3段を通す
agent01、agent02、agent03 をペンテスト用の名前と出力型に差し替え、パイプラインで順に実行できるようにする。
並行開発に入れる状態にする
共通ファイルをむやみに触らず、各自が自分の agentXX.py、テスト、評価だけを進められる状態にする。
プロンプトの細かい調整、攻撃ツールの本格実装、権限昇格ロジックの作り込みは後回し。 先に個別開発へ走ると、共通スキーマの解釈違いで統合時に作り直しになる。
01最初に決めること — RoE と設計合意
コードを書く前に、攻撃してよい範囲と、3人の分担境界を決める。
この合意は docs/RULES_OF_ENGAGEMENT.md と docs/DESIGN_AGREEMENT.md に書いて commit する。
交戦規則(RoE)
対象スコープ、禁止操作、承認ゲート、レート制限、認可の証跡を明文化する。 検証環境は z4.shino.club。開発中はここ以外にリクエストを出さない。
設計合意
エージェント分割、共通データモデル、安全機構の置き場所、実行方式、テスト方針、ファイル所有権を決める。
3人で合意する責務境界
| 担当 | エージェント | 責務 | 主な出力 |
|---|---|---|---|
| A | agent01 偵察 | ポート、サービス、エンドポイント、技術スタック、脆弱性候補を集める。攻撃ペイロードは送らない。 | AttackSurfaceFinding[] |
| B | agent02 侵入 | 候補を検証し、侵入の足がかりを得る。実行前に承認ゲートを通す。 | Foothold検証済み Finding[] |
| C | agent03 権限昇格 | Foothold を使って権限昇格を試み、到達権限と証拠を残す。 | PrivEscResult |
Foothold は agent02 から agent03 へ渡す「生きたアクセス」。
Cookie、Web シェル URL、SSH 接続情報、コマンド実行の口などをどう表現するかを先に決める。
ここが曖昧だと、agent03 は何もできない。
02共通基盤を先に作る
触る中心は config.py と core.py。
ここは1人が実装し、残り2人がレビューする。複数人で同時に触ると衝突しやすい。
| 対象 | やること | 完了の目安 |
|---|---|---|
config.py | 共通型を追加する。Target、Credential、Evidence、Finding、Foothold、PrivEscResult。 | 3人がレビュー済み |
config.py | スコープガード、監査ログ、承認ゲート、コマンド実行ラッパーを共通基盤に置く。 | 各エージェントが同じ口を使える |
agent01.py 〜 agent03.py | 名前、説明、出力型をペンテスト用に差し替える。まずは最小限のサブエージェントでよい。 | 単体で型どおりの JSON を返す |
core.py | run_pipeline() を偵察 → 侵入 → 権限昇格の順に配線する。 | 端から端まで実行できる |
core.py | Foothold.kind == "none" のとき agent03 をスキップする分岐を入れる。 | 侵入失敗ケースで止まらない |
.env.example | スコープ、モデル名、承認ゲート、タイムアウトなどの設定キーを追加する。 | 初期設定が共有できる |
# config.py に置く型の例。実装前に3人で確定する。
class Finding(AgentOutput):
id: str
title: str
severity: Literal["critical", "high", "medium", "low", "info"]
confidence: float
endpoint: str
evidence: list[Evidence] = []
verified: bool = False
class Foothold(AgentOutput):
id: str
kind: Literal["web_session", "web_shell", "rce", "ssh", "db", "none"]
host: str
user: str = ""
access_level: Literal["none", "user", "service", "admin", "root"] = "none"
handle: str = ""
credentials: list[Credential] = []
evidence: list[Evidence] = []
class PrivEscResult(AgentOutput):
start_level: str
final_level: str
path: list[str] = []
evidence: list[Evidence] = []
blocked_by: list[str] = []
03今日の作業手順
時間内に目指すのはゲートA。個別の賢さではなく、共通基盤とパイプラインの疎通を優先する。
STEP 0RoE を書く
docs/RULES_OF_ENGAGEMENT.md に書く。STEP 1設計合意を書く
docs/DESIGN_AGREEMENT.md に書く。STEP 2共通型を実装する
config.py に型と安全機構の入口を追加する。3人でレビューする。STEP 3サブエージェントを差し替える
agent01、agent02、agent03 をペンテスト用の出力型で返すようにする。STEP 4パイプラインを通す
04ゲートA — ここまでできたら並行開発へ
ゲートAは、個別開発を始めてよいかを判断する関所。 全員がここを通過するまで、各自の判断で共通型を変えない。
テストが通る
pytest の全テストが通る。最小限のサブエージェントでもよいので、最低限の単体テストがある。
パイプラインが最後まで走る
python core.py --pipeline "..." が3段を通り、最後に PrivEscResult まで返る。
失敗分岐がある
Foothold.kind == "none" のとき、agent03 がスキップされる。
WebUI で見える
3エージェント分の実行記録が WebUI の runs に表示される。
共通スキーマを3人が承認した
以降は個人の判断で勝手に変えない。変更が必要なら合意手順を踏む。
作業範囲が分かれている
A/B/C がそれぞれ触るファイルと、レビュー必須の共通ファイルが明確になっている。
05ゲートA後の分担
ゲートAを通過したら、3人は並行開発に入る。 担当者は自分のエージェント、テスト、評価を中心に触る。
| 担当 | 自由に触るファイル | レビュー必須 |
|---|---|---|
| A | agent01.py / tests/test_agent01.py / evals/eval_agent01.py | config.pycore.py.env.exampletemplates/static/ |
| B | agent02.py / tests/test_agent02.py / evals/eval_agent02.py | |
| C | agent03.py / tests/test_agent03.py / evals/eval_agent03.py |
前段の完成を待たない。B は AttackSurface と Finding[] の固定 JSON、
C は Foothold の固定 JSON を tests/fixtures/ に置いて開発を始める。
フィクスチャが、実装より先に共通の前提になる。
*このコマの要点
1. まず共通基盤
最初に触るのは agentXX.py ではなく config.py と core.py。
2. 完成度より接続
今日の目的は「賢い」ではなく「サブエージェントが端から端まで連携する」。
3. ゲートAまで個別開発しない
共通スキーマが確定してから、3人で並行開発に入る。