# 逐行代码走读审计规范（通用版）

> **版本**: 1.0  
> **适用**: 任何后端/全栈项目，AI 辅助逐行代码安全与质量审计  
> **来源**: 从 Nexus 6.0 全量审计实践中提炼

---

## 一、审计目的

逐行走读（Line Walk）不是 lint 或格式检查，而是对**每一行代码的意图、安全影响、正确性**进行人工+AI 联合审核：

- 发现逻辑缺陷、边界情况、静默失败
- 发现安全漏洞（注入、鉴权缺失、信息泄露、密钥暴露）
- 验证已声明的安全修复是否真正落地
- 识别设计接受 vs 必须修复的取舍

---

## 二、审计原则

| 原则 | 说明 |
|------|------|
| **不信任注释** | 只看代码实际行为，注释说"已修复"不等于代码真的修复了 |
| **逐行、不跳过** | 每个函数、每个 except、每个返回值都要过一遍 |
| **关注静默路径** | `except Exception: pass` / `return None` / 日志但不 raise 等 |
| **追踪数据流** | 用户输入 → 校验 → 存储 → 输出，每个环节都检查 |
| **修复当场** | 发现 BUG 立即修复，不记 TODO 延后 |
| **区分接受与修复** | 部分设计取舍明确接受并文档化，不强行修复 |

---

## 三、FINDING 分类体系

### 3.1 类型标签

| 标签 | 含义 |
|------|------|
| `VULN` | 安全漏洞：注入/越权/RCE/信息泄露/密钥暴露 |
| `BUG` | 逻辑错误：会导致功能错误或异常的代码缺陷 |
| `RISK` | 安全风险：当前不直接可利用，但设计上有隐患 |
| `EXT` | 外部化缺失：如缺少鉴权中间件、缺少全局错误处理 |
| `GAP` | 测试/覆盖缺口：功能实现但缺少验证 |
| `OPT` | 优化建议：非阻塞性改进（性能/可读性）|
| `STYLE` | 代码风格/一致性 |
| `SECRET` | 凭据/密钥出现在不该出现的地方（硬编码、日志、仓库）|

### 3.2 优先级

| 级别 | 定义 | 处置 |
|------|------|------|
| **P0** | 立即可利用的高危漏洞（RCE、越权、密钥泄露） | **当场修复，阻塞合并** |
| **P1** | 高风险缺陷，生产前必须修复 | 当前迭代修复 |
| **P2** | 中等缺陷，影响正确性或有利用路径 | 本批次修复 |
| **P3** | 低风险、边界情况、防御性改进 | 顺手修或记录 |
| **📋 ACCEPTED** | 已知取舍，明确设计决策，不修复 | 文档化接受原因 |
| **🟡 PARTIAL** | 有缓解但未彻底解决 | 文档化缓解措施 |

### 3.3 状态标签

```
✅ FIXED     — 代码已修复并验证
⏳ OPEN      — 尚未修复
📋 ACCEPTED  — 已接受设计取舍
🟡 PARTIAL   — 部分缓解
—  N/A       — 不适用（如代码路径已删除）
```

---

## 四、FINDING 记录格式

每条 FINDING 必须包含：

```markdown
| F<批次>-<序号> | `文件路径:行号` | <类型> | <优先级> | <状态> | <一句话描述> | <修复方案/原因> |
```

示例：

```markdown
| F2a-01 | `api/webssh.py:45` | VULN | P0 | ✅ | WebSSH 缺少 server_id 绑定 → IDOR | 新增 webssh-token 端点，token 绑定 server_id |
| F3b-01 | `background/scheduler.py:84` | BUG | P1 | ✅ | tz-aware - naive datetime → TypeError | 比较前统一剥离 tzinfo |
| F2f-01 | `agent/agent.py:119` | VULN | P1 | 📋 | 子机 shell 面无法消除 | 网络隔离 + 审计日志（架构接受） |
```

---

## 五、审计范围规划（批次划分）

按目录/主题拆批次，单批次不超过 **20 个文件**或 **1000 行**：

```
2a — server/api/（路由层）
2b — server/application/services/（业务逻辑层）
2c — server/infrastructure/（基础设施层）
2d — server/main.py + config.py + background/（入口与配置）
2e — web/app/（前端页面）
2f — web/agent/ + tests/ + scripts/（子机、测试、工具）
...
```

每批结束产出：
1. **走读报告**（`audit-phase-Xn-line-walk.md`）
2. **修复清单**更新到 FINDING 矩阵

---

## 六、高优先审查项清单（每次必过）

### 6.1 安全 Checklist

```
[ ] 所有 API 端点是否有鉴权（JWT/API Key）？
[ ] 有无鉴权绕过路径（PUBLIC_PREFIXES 是否过宽）？
[ ] 用户输入是否经过校验？（路径、命令、文件名）
[ ] SQL/Shell 注入：用户数据是否拼接进 SQL/命令？
[ ] 敏感字段是否加密存储？（密码、私钥、API Key）
[ ] API 响应是否泄露敏感字段？（password_hash、加密值、API Key）
[ ] 日志是否打印密钥、连接串、Token？
[ ] 外部通知（Telegram/Slack/Email）是否脱敏？
[ ] 硬编码密钥/密码是否存在？
[ ] 定时任务/后台任务是否有防重复执行机制？
```

### 6.2 正确性 Checklist

```
[ ] timezone-aware vs naive datetime 混用 → TypeError？
[ ] float()/int() 对外部数据无保护 → ValueError？
[ ] dict ** 解包用在了 list 上？
[ ] 循环中修改被迭代的集合？
[ ] asyncio.gather(return_exceptions=True) 吞掉的异常有无处理？
[ ] 非原子操作（INCR + EXPIRE / 读-改-写 race）？
[ ] DB 列宽是否足够（String(N) vs Text，加密后长度）？
[ ] cron 表达式解析是否防 division by zero？
[ ] 字符串替换是否原子（密码含 $VAR 导致嵌套替换）？
[ ] shlex.quote 在双引号上下文中是否产生混合引号问题？
```

### 6.3 数据流 Checklist

```
[ ] 用户命令 → 危险命令检测 → 执行，顺序是否正确？
[ ] 密钥 → 使用 → 审计，是否每步可追溯？
[ ] 长任务回调：job_id+secret 是否一次性消费？
[ ] Rate Limit：INCR+EXPIRE 是否原子（Redis pipeline/Lua）？
```

---

## 七、报告结构

### 7.1 单批次走读报告

```markdown
# Phase Xn 走读报告

**批次**: Xn | **文件数**: N | **FINDING**: M

## FINDING 列表
| ID | 文件:行 | 类 | 级 | 状态 | 描述 | 修复 |

## CLEAN 文件
- file1.py — 无 FINDING，关键路径 A/B/C 正确
- file2.py — CLEAN

## 待修复代码（附 diff）
...
```

### 7.2 全量 FINDING 矩阵

维护一张总表，字段：

```
ID | 文件:行 | 类型 | 原始级别 | 当前状态 | 描述 | 备注/修复
```

每次修复后更新状态，始终保持「OPEN 代码待办 = 0」的目标。

### 7.3 闭环声明

走读结束产出一份闭环声明，包含：
- 覆盖范围（批次 → 文件 → 行数）
- FINDING 统计（总数 / 已修复 / 接受 / 部分）
- 代码待办数（目标 0）
- 遗留观察（不需修复的设计说明）

---

## 八、与 AI 协作的提示语模板

### 开始走读

```
请对以下文件进行逐行代码走读审计：
文件：[文件路径]

要求：
1. 逐行阅读，不跳过任何函数、异常处理、返回值
2. 按以下分类发现问题：VULN/BUG/RISK/SECRET/GAP
3. 按 P0/P1/P2/P3 标注优先级
4. 每个 FINDING 格式：ID | 文件:行 | 类型 | 级别 | 描述 | 修复建议
5. 对于可以立即修复的，直接给出修复代码
6. 对于设计取舍，标注 📋 ACCEPTED + 原因
7. CLEAN 文件明确说明"无 FINDING"

禁止：
- 跳过 except/finally 块
- 忽略返回值为 None 的路径
- 忽略 datetime timezone 问题
- 忽略字符串拼接命令的路径
```

### 修复验证

```
以下 FINDING 已修复，请验证：
FINDING ID: [ID]
原代码：[原始代码]
修复后：[修复代码]

请确认：
1. 修复是否正确覆盖了原始问题
2. 修复是否引入了新问题
3. 是否有边界情况未处理
```

### 闭环声明

```
请根据以下走读记录，生成 Phase X 闭环声明：
- 覆盖批次：[2a/2b/...]
- FINDING 矩阵：[附表]

闭环声明应包含：
1. 覆盖范围（文件列表）
2. FINDING 统计
3. CLEAN 文件列表
4. 代码待办数
5. 遗留观察
```

---

## 九、常见陷阱速查

| 陷阱 | 典型代码 | 问题 |
|------|---------|------|
| Datetime 混用 | `now - record.created_at` | MySQL 返回 naive，`now = datetime.now(tz=utc)` 是 aware → TypeError |
| 非原子 Rate Limit | `redis.incr(k); redis.expire(k, 60)` | 崩溃在 incr 和 expire 之间 → key 永久存在 |
| list `**` 解包 | `{**d, **some_list, "k": v}` | list 不是 mapping → TypeError（被 gather 静默吞掉） |
| shlex + 引号混用 | `f'"{shlex.quote(path)}.pid"'` | quote 加单引号，外层双引号使其变字面量 |
| 替换非原子 | `cmd.replace("$DB_PASS", pw)` | 若 pw 含 `$DB_USER` 则二次替换 |
| float() 无保护 | `float(agent_data["cpu"])` | Agent 传非数字串 → 500 |
| DB 列宽 | `String(500)` 存加密私钥 | RSA 4096 Fernet 后 ~4300 chars → DataError |
| cron 除零 | `value % step == 0` | `step=0` → ZeroDivisionError |
| 死锁自身 | lock 内 `await self._evict()` 再次 acquire lock | asyncio 单线程，但嵌套 acquire 会死锁 |
| 日志泄密 | `logger.info(f"Redis: {settings.REDIS_URL}")` | URL 含密码 → 日志泄露 |

---

## 十、审计完成标准

| 条件 | 验收 |
|------|------|
| 所有 P0/P1 FINDING | 代码已修复且验证 |
| OPEN 代码待办 | **= 0** |
| ACCEPTED 项 | 每条有明确理由和文档 |
| 走读闭环声明 | 已出具并入库 |
| 修复 commit | 已推送到仓库 |

---

*本规范可直接粘贴给任何 AI，作为逐行走读任务的系统提示词前缀或参考框架。*
