defense-in-depth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Defense-in-Depth Validation

纵深防御验证

Overview

概述

When you fix a bug caused by invalid data, adding validation at one place feels sufficient. But that single check can be bypassed by different code paths, refactoring, or mocks.
Core principle: Validate at EVERY layer data passes through. Make the bug structurally impossible.
当你修复一个由无效数据引发的Bug时,在单一位置添加验证看似足够。但这种单一检查可能会被不同的代码路径、重构或模拟(mock)绕过。
核心原则: 在数据流经的每一层都进行验证,从结构上让这类Bug无法出现。

When to Use

适用场景

Use when:
  • Invalid data caused a bug deep in the call stack
  • Data crosses system boundaries (API → service → storage)
  • Multiple code paths can reach the same vulnerable code
  • Tests mock intermediate layers (bypassing validation)
Don't use when:
  • Pure internal function with single caller (validate at caller)
  • Data already validated by framework/library you trust
  • Adding validation would duplicate identical checks at adjacent layers
适用情况:
  • 无效数据导致调用栈深层出现Bug
  • 数据跨系统边界流转(API → 服务 → 存储)
  • 多条代码路径可到达同一易受攻击的代码
  • 测试中模拟了中间层(绕过了验证)
不适用情况:
  • 仅单一调用方的纯内部函数(在调用方处验证即可)
  • 数据已由你信任的框架/库完成验证
  • 在相邻层级添加完全相同的验证检查

The Four Layers

四个验证层级

Layer 1: Entry Point Validation

层级1:入口点验证

Purpose: Reject invalid input at API/system boundary
typescript
function createProject(name: string, workingDirectory: string) {
  if (!workingDirectory?.trim()) {
    throw new Error('workingDirectory cannot be empty');
  }
  if (!existsSync(workingDirectory)) {
    throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
  }
  // ... proceed
}
When needed: Always. This is your first line of defense.
目的: 在API/系统边界处拒绝无效输入
typescript
function createProject(name: string, workingDirectory: string) {
  if (!workingDirectory?.trim()) {
    throw new Error('workingDirectory cannot be empty');
  }
  if (!existsSync(workingDirectory)) {
    throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
  }
  // ... proceed
}
适用时机: 始终需要,这是你的第一道防线。

Layer 2: Business Logic Validation

层级2:业务逻辑验证

Purpose: Ensure data makes sense for this specific operation
typescript
function initializeWorkspace(projectDir: string, sessionId: string) {
  if (!projectDir) {
    throw new Error('projectDir required for workspace initialization');
  }
  // ... proceed
}
When needed: When business rules differ from entry validation, or when mocks might bypass Layer 1.
目的: 确保数据对当前特定操作有意义
typescript
function initializeWorkspace(projectDir: string, sessionId: string) {
  if (!projectDir) {
    throw new Error('projectDir required for workspace initialization');
  }
  // ... proceed
}
适用时机: 当业务规则与入口验证不同,或模拟可能绕过层级1时。

Layer 3: Environment Guards

层级3:环境防护

Purpose: Prevent dangerous operations in specific contexts
typescript
async function gitInit(directory: string) {
  if (process.env.NODE_ENV === 'test') {
    const normalized = normalize(resolve(directory));
    if (!normalized.startsWith(tmpdir())) {
      throw new Error(`Refusing git init outside temp dir in tests: ${directory}`);
    }
  }
  // ... proceed
}
When needed: When operation is destructive/irreversible, especially in test environments.
目的: 防止在特定上下文执行危险操作
typescript
async function gitInit(directory: string) {
  if (process.env.NODE_ENV === 'test') {
    const normalized = normalize(resolve(directory));
    if (!normalized.startsWith(tmpdir())) {
      throw new Error(`Refusing git init outside temp dir in tests: ${directory}`);
    }
  }
  // ... proceed
}
适用时机: 当操作具有破坏性/不可逆性时,尤其是在测试环境中。

Layer 4: Debug Instrumentation

层级4:调试埋点

Purpose: Capture context for forensics when other layers fail
typescript
async function gitInit(directory: string) {
  logger.debug('git init', { directory, cwd: process.cwd(), stack: new Error().stack });
  // ... proceed
}
When needed: When debugging is difficult, or when you need to trace how bad data arrived.
目的: 当其他层级验证失败时,捕获用于取证的上下文信息
typescript
async function gitInit(directory: string) {
  logger.debug('git init', { directory, cwd: process.cwd(), stack: new Error().stack });
  // ... proceed
}
适用时机: 当调试难度大,或需要追踪无效数据的来源时。

Decision Heuristic

决策启发式

SituationLayers Needed
Public API, simple validation1 only
Data crosses multiple services1 + 2
Destructive operations (delete, init, write)1 + 2 + 3
Chasing a hard-to-reproduce bug1 + 2 + 3 + 4
Tests mock intermediate layersAt minimum: 1 + 3
场景需要启用的层级
公开API,简单验证仅层级1
数据跨多个服务流转层级1 + 层级2
破坏性操作(删除、初始化、写入)层级1 + 层级2 + 层级3
排查难以复现的Bug层级1 + 层级2 + 层级3 + 层级4
测试中模拟中间层至少:层级1 + 层级3

Applying the Pattern

模式应用步骤

When you find a bug caused by invalid data:
  1. Trace the data flow - Where does the bad value originate? Where is it used?
  2. Map checkpoints - List every function/layer the data passes through
  3. Decide which layers - Use heuristic above
  4. Add validation - Entry → business → environment → debug
  5. Test each layer - Verify Layer 2 catches what bypasses Layer 1
当你发现由无效数据引发的Bug时:
  1. 追踪数据流 - 无效值的来源在哪里?在哪里被使用?
  2. 标记检查点 - 列出数据流经的所有函数/层级
  3. 确定验证层级 - 参考上述启发式规则
  4. 添加验证 - 按入口→业务→环境→调试的顺序添加
  5. 测试每个层级 - 验证层级2能捕获绕过层级1的情况

Quick Reference

快速参考

LayerQuestion It AnswersTypical Check
EntryIs input valid?Non-empty, exists, correct type
BusinessDoes it make sense here?Required for this operation, within bounds
EnvironmentIs this safe in this context?Not in prod, inside temp dir, etc.
DebugHow did we get here?Log stack, cwd, inputs
层级解决的问题典型检查内容
入口输入是否有效?非空、存在、类型正确
业务在此场景下是否合理?当前操作必需、在有效范围内
环境在此上下文执行是否安全?非生产环境、在临时目录内等
调试我们是如何走到这一步的?记录调用栈、当前工作目录、输入参数

Common Mistakes

常见错误

MistakeFix
One validation point, call it doneAdd at least entry + business layers
Identical checks at adjacent layersMake each layer check something different
Environment guards only in prodAdd them in test too (prevent test pollution)
Skipping debug loggingAdd it during the bug hunt, keep it
Validation but no useful error messageInclude the bad value and expected format
错误做法修复方案
仅添加一处验证就认为完成至少添加入口+业务两层验证
在相邻层级添加完全相同的检查让每个层级检查不同的内容
仅在生产环境添加环境防护在测试环境也添加(防止测试污染)
跳过调试日志在排查Bug时添加,并保留下来
添加了验证但错误信息无用包含无效值和预期格式

Key Insight

核心洞见

During testing, each layer catches bugs the others miss:
  • Different code paths bypass entry validation
  • Mocks bypass business logic checks
  • Edge cases need environment guards
  • Debug logging identifies structural misuse
Don't stop at one validation point. The bug isn't fixed until it's impossible.
在测试过程中,每个层级都会捕获其他层级遗漏的Bug:
  • 不同代码路径会绕过入口验证
  • 模拟会绕过业务逻辑检查
  • 边缘情况需要环境防护
  • 调试日志可识别结构性误用
不要仅停留在单一验证点。 只有当Bug从结构上无法出现时,才算真正修复。