Defense-in-Depth Validation について
Defense-in-Depth Validationは、無効なデータによるバグに対処する開発者向けのスキルです。入口、ビジネスロジック、環境ごとに検証し、デバッグ情報も記録して検証の回避を防ぎます。
主な機能
- 01入口での入力検証
- 02ビジネスロジックの制約確認
- 03環境依存の操作をガード
- 04デバッグ情報の記録
- 05検証レイヤーごとのテスト
活用シーン
- 01空のパスによるgit initの防止
- 02データフローの検証回避対策
- 03デバッグ情報を使った誤用の診断
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ obra/ superpowers-skills --skill defense-in-depth-validationインストール後、Claude Code で/defense-in-depth-validationを入力して読み込まれたか確認します。
詳細
多層防御による検証
概要
無効なデータが原因のバグを修正するとき、1か所に検証を追加すれば十分だと感じるかもしれません。しかし、そのチェックは、異なるコードパスやリファクタリング、モックによって回避される可能性があります。
基本原則: データが通過するすべての層で検証します。バグが構造上起こり得ないようにします。
複数の層が必要な理由
単一の検証:「バグを修正しました」 複数の層:「バグが起こり得ないようにしました」
層ごとに検出できるケースが異なります。
- エントリーポイントでの検証は、ほとんどのバグを検出します
- ビジネスロジックでの検証は、エッジケースを検出します
- 環境ガードは、特定のコンテキストにおける危険を防ぎます
- デバッグログは、ほかの層が機能しなかった場合の調査に役立ちます
4つの層
レイヤー 1:エントリーポイントでの検証
目的: API 境界で明らかに無効な入力を拒否します
function createProject(name: string, workingDirectory: string) {
if (!workingDirectory || workingDirectory.trim() === '') {
throw new Error('workingDirectory cannot be empty');
}
if (!existsSync(workingDirectory)) {
throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
}
if (!statSync(workingDirectory).isDirectory()) {
throw new Error(`workingDirectory is not a directory: ${workingDirectory}`);
}
// ... proceed
}
レイヤー 2:ビジネスロジックでの検証
目的: データがこの操作に適していることを確認します
function initializeWorkspace(projectDir: string, sessionId: string) {
if (!projectDir) {
throw new Error('projectDir required for workspace initialization');
}
// ... proceed
}
レイヤー 3:環境ガード
目的: 特定のコンテキストで危険な操作を防ぎます
async function gitInit(directory: string) {
// In tests, refuse git init outside temp directories
if (process.env.NODE_ENV === 'test') {
const normalized = normalize(resolve(directory));
const tmpDir = normalize(resolve(tmpdir()));
if (!normalized.startsWith(tmpDir)) {
throw new Error(
`Refusing git init outside temp dir during tests: ${directory}`
);
}
}
// ... proceed
}
レイヤー 4:デバッグ用インストルメンテーション
目的: 調査用のコンテキストを記録します
async function gitInit(directory: string) {
const stack = new Error().stack;
logger.debug('About to git init', {
directory,
cwd: process.cwd(),
stack,
});
// ... proceed
}
パターンの適用
バグを見つけた場合:
- データフローを追跡する - 不正な値はどこで発生し、どこで使われていますか?
- すべてのチェックポイントを洗い出す - データが通過するすべての箇所を列挙します
- 各層に検証を追加する - エントリーポイント、ビジネスロジック、環境、デバッグ
- 各層をテストする - レイヤー 1 を回避し、レイヤー 2 で検出できることを確認します
セッションでの例
バグ:空の projectDir によって、ソースコード内で git init が実行されました
データフロー:
- テストのセットアップ → 空文字列
Project.create(name, '')WorkspaceManager.createWorkspace('')git initがprocess.cwd()で実行される
追加した4つの層:
- レイヤー 1:
Project.create()で空でないこと、存在すること、書き込み可能であることを検証 - レイヤー 2:
WorkspaceManagerで projectDir が空でないことを検証 - レイヤー 3:テスト中に
WorktreeManagerが tmpdir 外での git init を拒否 - レイヤー 4:git init の前にスタックトレースを記録
結果: 1847 個すべてのテストが成功し、バグを再現できなくなりました
重要なポイント
4つの層はすべて必要でした。テスト中、各層がほかの層では検出できなかったバグを検出しました。
- 異なるコードパスによって、エントリーポイントでの検証が回避されました
- モックによって、ビジネスロジックのチェックが回避されました
- プラットフォームごとのエッジケースには、環境ガードが必要でした
- デバッグログによって、構造上の誤用を特定できました
検証を1か所だけで終わらせないでください。 すべての層にチェックを追加します。
コメント
Markdown 対応