Defense-in-Depth Validation(纵深防御式校验)是一种用于防止无效数据引发缺陷的工程实践。它的核心原则是:在数据流经的每一层都进行校验,让缺陷在结构上变得不可能发生。
用途
当修复一个由无效数据导致的缺陷时,只在某一处添加校验往往感觉已经足够。但这一处检查可能被不同的代码路径、重构或 mock 绕过。该方法的目标不是“修复了这个缺陷”,而是“让这个缺陷不可能发生”。不同层能捕获不同情况:入口校验捕获大多数缺陷,业务逻辑捕获边界情况,环境守卫防止特定上下文中的危险,调试日志在其它层失效时提供帮助。
四层校验
第一层:入口点校验
目的:在 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
}
第二层:业务逻辑校验
目的:确保数据对当前操作是有意义的。
示例代码:
function initializeWorkspace(projectDir: string, sessionId: string) {
if (!projectDir) {
throw new Error('projectDir required for workspace initialization');
}
// ... proceed
}
第三层:环境守卫
目的:在特定上下文中阻止危险操作。
示例代码:
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
}
第四层:调试插桩
目的:为事后取证捕获上下文。
示例代码:
async function gitInit(directory: string) {
const stack = new Error().stack;
logger.debug('About to git init', {
directory,
cwd: process.cwd(),
stack,
});
// ... proceed
}
使用方式
当发现一个缺陷时,按以下步骤应用该模式:
- 追踪数据流——坏值从哪里产生?在哪里被使用?
- 映射所有检查点——列出数据经过的每一个点。
- 在每一层添加校验——入口、业务、环境、调试。
- 测试每一层——尝试绕过第一层,验证第二层能否捕获。
示例
资料中给出的会话示例:
缺陷:空的 projectDir 导致 git init 在源码目录中执行。
数据流:
- 测试设置 → 空字符串
- Project.create(name, '')
- WorkspaceManager.createWorkspace('')
- git init 在 process.cwd() 中运行
添加的四层:
- 第一层:Project.create() 校验非空、存在、可写
- 第二层:WorkspaceManager 校验 projectDir 非空
- 第三层:WorktreeManager 在测试中拒绝在 tmpdir 之外执行 git init
- 第四层:在 git init 前记录堆栈跟踪日志
结果:全部 1847 个测试通过,缺陷无法复现。
关键洞察
四层都是必要的。在测试过程中,每一层都捕获了其它层遗漏的缺陷:
- 不同代码路径绕过了入口校验
- mock 绕过了业务逻辑检查
- 不同平台上的边界情况需要环境守卫
- 调试日志识别出结构性误用
不要只停留在一个校验点。在每一层都添加检查。
适用场景与限制
该方法适用于需要处理外部输入、跨多个模块传递数据、或在测试与生产等不同环境中执行危险操作的软件系统。资料未提供关于性能开销、具体语言限制或平台兼容性的进一步说明。