[P1/高] DeleteFileTool TOCTOU 竞态,存在检查与删除之间可被替换 #20

Open
opened 2026-07-21 21:55:49 +08:00 by thzxx · 0 comments
Owner

问题类型

安全漏洞 / 高 / 工具系统

文件位置

electron/harness/tools/built-in/filesystem.ts (DeleteFileTool)

问题描述

DeleteFileTool 执行流程:

  1. stat(path) 检查文件存在与类型
  2. validatePath(path) 校验工作空间内
  3. unlink(path) / rmdir(path) 删除

步骤 1-2 与步骤 3 之间存在 TOCTOU(time-of-check to time-of-use)窗口:

  • 攻击者可在校验后用符号链接替换文件
  • path 校验通过后,实际 unlink 的可能是符号链接指向的任意文件

影响

  • 删除工作空间外的任意文件
  • 数据丢失

建议修复

  1. 使用 file descriptor:通过 open(path, O_NOFOLLOW) 打开 fd,再对 fd 操作
  2. realpath 二次校验:删除前再次 realpath 校验
import { open, unlink } from 'fs/promises';

async safeDelete(path: string) {
  const realBefore = realpathSync(path);
  if (!isInWorkspace(realBefore)) throw new Error('Path escape');

  // 使用 O_NOFOLLOW 防止符号链接
  const fd = await open(path, 'r');
  const stat = await fd.stat();
  if (stat.isDirectory()) {
    await fd.close();
    throw new Error('Cannot delete directory');
  }
  await fd.close();

  // 再次校验(防止 fd 关闭后被替换)
  const realAfter = realpathSync(path);
  if (realAfter !== realBefore) throw new Error('TOCTOU detected');

  await unlink(path);
}
## 问题类型 安全漏洞 / 高 / 工具系统 ## 文件位置 `electron/harness/tools/built-in/filesystem.ts` (DeleteFileTool) ## 问题描述 DeleteFileTool 执行流程: 1. `stat(path)` 检查文件存在与类型 2. `validatePath(path)` 校验工作空间内 3. `unlink(path)` / `rmdir(path)` 删除 步骤 1-2 与步骤 3 之间存在 TOCTOU(time-of-check to time-of-use)窗口: - 攻击者可在校验后用符号链接替换文件 - `path` 校验通过后,实际 unlink 的可能是符号链接指向的任意文件 ## 影响 - 删除工作空间外的任意文件 - 数据丢失 ## 建议修复 1. **使用 file descriptor**:通过 `open(path, O_NOFOLLOW)` 打开 fd,再对 fd 操作 2. **realpath 二次校验**:删除前再次 realpath 校验 ```ts import { open, unlink } from 'fs/promises'; async safeDelete(path: string) { const realBefore = realpathSync(path); if (!isInWorkspace(realBefore)) throw new Error('Path escape'); // 使用 O_NOFOLLOW 防止符号链接 const fd = await open(path, 'r'); const stat = await fd.stat(); if (stat.isDirectory()) { await fd.close(); throw new Error('Cannot delete directory'); } await fd.close(); // 再次校验(防止 fd 关闭后被替换) const realAfter = realpathSync(path); if (realAfter !== realBefore) throw new Error('TOCTOU detected'); await unlink(path); } ```
thzxx added the ??????????? labels 2026-07-21 21:55:49 +08:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MetonaTeam/metona-ai-desktop#20