docs: line-walk audit standard v2.0 - full spec integrating mdc rules + Phase 1-4 lessons
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
# 逐行代码走读审计完整规范 v2.0
|
||||
|
||||
> **版本**: 2.0 · 2026-05-23
|
||||
> **适用**: Python 后端 / 前端 / Shell 任意项目,AI 辅助全量逐行审计
|
||||
> **来源**: audit-line-review.mdc + perfect-implementation.mdc + Nexus Phase 1–4 实战提炼
|
||||
|
||||
---
|
||||
|
||||
## 核心原则(不可妥协)
|
||||
|
||||
**逐行走读 ≠ 攻击面抽查。** 未满足本规范 DoD 的文件不得标记「已审计」。
|
||||
|
||||
**完美实现原则**(来自 perfect-implementation.mdc):
|
||||
- ❌ 不允许发现问题后绕过 — 必须根治
|
||||
- ❌ 不允许静默吞错 — `except Exception: pass` 须证明合理
|
||||
- ❌ 不允许留 TODO/FIXME — 当场能修必须当场修
|
||||
- ❌ 不允许无行号 FINDING — 每条必须 `文件:行号`
|
||||
- ✅ 无法完美修复时必须停下,告知用户,确认方案后再动
|
||||
|
||||
---
|
||||
|
||||
## 一、术语
|
||||
|
||||
| 术语 | 定义 |
|
||||
|------|------|
|
||||
| **走读** | 对单文件按行号顺序阅读全部源码,对命中规则做上下文分析 |
|
||||
| **触达** | 仅 grep / 打开文件,未满足走读 DoD,**不算完成** |
|
||||
| **规则命中** | §四 规则表在某行产生匹配,计入列表 H |
|
||||
| **Closure** | 对一条命中的判定:`SAFE`(附依据)或 `FINDING`(附行号+理由) |
|
||||
| **DoD** | 单文件完成定义,见 §六 |
|
||||
| **ACCEPTED** | 已知设计取舍,明确接受并文档化,不修复 |
|
||||
|
||||
---
|
||||
|
||||
## 二、适用范围
|
||||
|
||||
| 包含 | 排除 |
|
||||
|------|------|
|
||||
| `server/**/*.py`(所有后端) | `docs/**`(仅做密钥扫描) |
|
||||
| `web/app/*.{html,js}`(前端页面) | 第三方 minified JS(vendor/) |
|
||||
| `web/agent/**`(子机 Agent) | 图片、字体、.pyc |
|
||||
| `scripts/**`、`deploy/**`、`tests/**` | `.cursor/skills/**` |
|
||||
|
||||
---
|
||||
|
||||
## 三、单文件走读流程(8 步,每步必做)
|
||||
|
||||
```
|
||||
Step 1 登记:文件路径、语言、总行数 N(wc -l 或工具读取)
|
||||
Step 2 全文 Read 至 EOF(每段 ≤200 行;末段 offset ≥ N-5,须覆盖末行)
|
||||
Step 3 规则扫描 → 命中列表 H(§四 规则表)
|
||||
Step 4 对 H 每条 Read ±15 行上下文 → Closure 表(SAFE 或 FINDING)
|
||||
Step 5 API / WebSocket 文件:写入口表(方法 / 路径 / 鉴权方式)
|
||||
Step 6 标注外部输入 → sink(SQL / shell / 文件路径 / innerHTML / 出站 HTTP)
|
||||
Step 7 八类归类核对(§七)
|
||||
Step 8 满足 DoD → 标「逐行完成 ✓」
|
||||
```
|
||||
|
||||
**单会话上限**:最多 **5 个文件** 完成 8 步(防止上下文过长导致漏报)。
|
||||
|
||||
---
|
||||
|
||||
## 四、规则表
|
||||
|
||||
### 4.1 Python 后端
|
||||
|
||||
| ID | 匹配模式(示意) | 审查要点 |
|
||||
|----|-----------------|----------|
|
||||
| PY-01 | f-string + exec / shell / ssh / sysctl / curl / sudo / bash | 是否经 shell?整段 `key=value` 是否安全引用?`shlex.quote(f"{k}={v}")` vs 仅 quote value |
|
||||
| PY-02 | `subprocess.` / `create_subprocess_shell` / `os.system` | `shell=True`?命令来源是否可控? |
|
||||
| PY-03 | `exec_ssh_command` / `conn.run(` / `asyncssh` | 远程 shell 拼接?命令是否用户可控? |
|
||||
| PY-04 | `text(` / `execute(` / `raw(` / 字符串拼 SQL | SQL 注入?是否用参数绑定? |
|
||||
| PY-05 | `@router.` / `@app.` / `websocket` / `@app.post` | 是否有 JWT / API Key 鉴权?PUBLIC 白名单是否过宽? |
|
||||
| PY-06 | 字面量 `API_KEY` / `SECRET_KEY` / `password =` / `token =` | 硬编码凭据? |
|
||||
| PY-07 | `pickle` / `yaml.load(` / `eval(` / `exec(` | 危险反序列化 / 动态执行 |
|
||||
| PY-08 | API Key `==` / `!=` | 应用 `secrets.compare_digest`(防时序攻击) |
|
||||
| PY-09 | `except` 后直接 `pass` / `return None` / `logger.xxx` 无 re-raise | 是否静默吞掉安全异常?静默失败是否可接受? |
|
||||
| PY-10 | `open(` + 路径变量 / `pathlib.Path(` + 用户输入 | 路径遍历?白名单校验? |
|
||||
| PY-11 | `datetime.now()` 不带 `timezone.utc` / 与 `now(utc)` 相减 | tz-aware vs naive 混用 → TypeError |
|
||||
| PY-12 | `float(x)` / `int(x)` 对外部数据无 try/except | 非数字输入 → ValueError → 500 |
|
||||
| PY-13 | `{**d1, **some_list, ...}` dict 解包 | list 不是 mapping → TypeError(常被 gather 静默捕获)|
|
||||
| PY-14 | `redis.incr(k)` 后 `redis.expire(k, t)` 两步分开 | 非原子:崩溃在中间 → key 无 TTL 永久存在 |
|
||||
| PY-15 | `String(N)` 列存加密后数据 / 大型字段 | 加密后长度是否仍 ≤ N?RSA 4096 Fernet 后 ~4300 chars |
|
||||
| PY-16 | `command.replace("$VAR", value)` 多次循环替换 | 密码含 `$VAR` 模式 → 嵌套二次替换 |
|
||||
| PY-17 | `shlex.quote(x)` 后再包进 `f'"{q}.suffix"'` | 单引号结果被双引号包裹 → 文件名含字面单引号 |
|
||||
| PY-18 | cron field `step = int(part[2:])` 后 `value % step` | step=0 → ZeroDivisionError |
|
||||
| PY-19 | `logger.info(f"... {settings.REDIS_URL}")` / `{settings.DATABASE_URL}` | 连接串含密码 → 日志泄露 |
|
||||
| PY-20 | `send_telegram(f"...{e}...")` / `{result}` 等外部通知 | DB/Redis 详情泄露到 Telegram/Slack |
|
||||
|
||||
### 4.2 前端(HTML / JS)
|
||||
|
||||
| ID | 匹配模式 | 审查要点 |
|
||||
|----|---------|----------|
|
||||
| FE-01 | `innerHTML` / `outerHTML` | 每个 `${}` 是否经 `esc()` / `escAttr()`? |
|
||||
| FE-02 | `localStorage.setItem` + token / key | JWT / API Key 存 localStorage?CSP 是否有 |
|
||||
| FE-03 | `WebSocket` + `?token=` / `?t=` in URL | Token 在 URL → 日志 / Referer 泄露 |
|
||||
| FE-04 | 裸 `fetch(` 不经封装函数 | 是否绕过统一 `apiFetch`(含鉴权 / CSRF)? |
|
||||
| FE-05 | `onclick="…${` / `href="javascript:${` | JS 属性注入(XSS) |
|
||||
| FE-06 | `<script src="https://cdn.` / `<link href="https://` | 是否有 SRI(integrity)? |
|
||||
| FE-07 | `data-xxx="${` / 后端数据直插 HTML 属性 | 属性注入?须 `escAttr` |
|
||||
|
||||
### 4.3 Shell / Deploy / SQL / Config
|
||||
|
||||
| ID | 范围 | 要点 |
|
||||
|----|------|------|
|
||||
| SH-01 | `*.sh` | 变量引用是否加 `""`、curl 是否含密钥、路径是否变量拼接 |
|
||||
| DP-01 | `nginx*.conf` | CSP 头、TLS 配置、是否暴露 `.env` |
|
||||
| SQL-01 | `*.sql` | `GRANT ALL`、`'%'`(任意主机)、明文密码 |
|
||||
| CFG-01 | `.env.example` / `config.json` | 是否含真实密钥? |
|
||||
|
||||
**H=0 规则**:即使无规则命中,仍须完成 Step 2 全文 Read,Closure 表写明「规则零命中,全文确认 CLEAN」。
|
||||
|
||||
---
|
||||
|
||||
## 五、Closure 表格式
|
||||
|
||||
```markdown
|
||||
| 文件 | 行号 | 规则 | 判定 | 严重度 | 理由 / 修复建议 |
|
||||
|------|------|------|------|--------|-----------------|
|
||||
| sync_engine_v2.py | 243 | PY-01 | FINDING | P2 | config value 含换行符拼入 echo → 多行注入;剥离 \n\r\x00 |
|
||||
| sync_engine_v2.py | 95 | PY-01 | SAFE | — | rsync 两端路径均 shlex.quote,不经用户可控拼接 |
|
||||
| agent.py | 69 | PY-08 | SAFE | — | compare_digest(provided, stored) 已使用 |
|
||||
| auth.py | 116 | PY-09 | ACCEPTED | — | /logout 无 JWT 是设计决策:access token 无状态,仅清 refresh |
|
||||
```
|
||||
|
||||
- **SAFE**:必须写明依据(argv 不经 shell / Pydantic 校验 / 常量输入 / 白名单)
|
||||
- **FINDING**:对应八类 + 行号 + 一行修复建议
|
||||
- **ACCEPTED**:写明接受理由,不得留空
|
||||
|
||||
---
|
||||
|
||||
## 六、单文件 DoD(缺一不可)
|
||||
|
||||
```
|
||||
[ ] Step 2:Read 覆盖 1..N(报告写明 N 与实际 Read 范围)
|
||||
[ ] Step 4:len(H) == Closure 行数(含 SAFE)
|
||||
[ ] Step 5:有 @router / websocket → 有入口表;纯 __init__ 可 N/A
|
||||
[ ] Step 6:已标注外部输入→sink 或写明「无外部输入」
|
||||
[ ] Step 7:八类已勾选
|
||||
[ ] 无「Top 15」「已大体审计」「整体 CLEAN」等笼统结论
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、八类分类与严重度
|
||||
|
||||
| 类 | 代号 | P0 例 | P1 例 | P2/P3 例 |
|
||||
|----|------|-------|-------|---------|
|
||||
| 安全漏洞 | VULN | 未授权 RCE / IDOR Shell | XSS / 命令注入 / 越权 | 信息泄露 / 会话固定 |
|
||||
| 安全风险 | RISK | 仓库内生产 API Key | 明文 HTTP 传凭据 | 非原子 rate limit |
|
||||
| 功能缺陷 | BUG | 加密列宽不足 → 截断 | tz-aware/naive 相减 → TypeError | float() 无保护 → 500 |
|
||||
| 外部化缺失 | EXT | 无全局鉴权中间件 | 无危险命令拦截 | 缺日志 |
|
||||
| 规范违规 | STYLE | — | 未 esc 的 innerHTML | 过宽 public 路径 |
|
||||
| 性能 | PERF | — | 心跳每次写 MySQL | N+1 查询 |
|
||||
| 可用性 | UX | — | 错误无提示静默失败 | 缺进度状态 |
|
||||
| 优化点 | OPT | — | — | 重复逻辑 / 死代码 |
|
||||
|
||||
**KPI 约定**:P0 漏报 -100;P1 漏报 -50(用于 AI 质量评估)。
|
||||
|
||||
---
|
||||
|
||||
## 八、严重度定义
|
||||
|
||||
| 级别 | 定义 | 处置 |
|
||||
|------|------|------|
|
||||
| **P0** | 未授权可利用:RCE / IDOR / 仓库密钥 / 任意用户越权 | **阻塞合并,当场修复** |
|
||||
| **P1** | 认证后可利用或高影响缺陷:命令注入 / XSS / 凭据泄露 / 关键路径 TypeError | 当前批次修复 |
|
||||
| **P2** | 正确性或安全加固:float 无保护 / tz 混用 / 非原子操作 / 列宽不足 | 本迭代修复 |
|
||||
| **P3** | 低风险 / 防御性改进 / 代码整洁 | 顺手修或记录 |
|
||||
| **📋 ACCEPTED** | 设计取舍,已知且接受 | 文档化理由 |
|
||||
| **🟡 PARTIAL** | 有缓解但未彻底 | 文档化缓解措施 |
|
||||
|
||||
---
|
||||
|
||||
## 九、批次规划建议
|
||||
|
||||
| 批次 | 目录 / 主题 | 文件数上限 |
|
||||
|------|------------|-----------|
|
||||
| 1a | 路由层(API handlers) | 15 |
|
||||
| 1b | 业务层(Services) | 10 |
|
||||
| 1c | 基础设施层(DB / Redis / SSH / 外部服务) | 20 |
|
||||
| 1d | 入口与配置(main / config / background) | 8 |
|
||||
| 1e | 前端页面(HTML / JS) | 15 |
|
||||
| 1f | 子机 / 脚本 / 测试 | 20 |
|
||||
| 2a | 修复后重读(Phase 1 修改行数最多的文件) | 10 |
|
||||
| 2b | 遗留文件 / 新增文件补充走读 | 按需 |
|
||||
|
||||
每批结束产出:
|
||||
1. **走读报告** `docs/reports/audit-phase-Xn-line-walk.md`
|
||||
2. **FINDING 矩阵**更新(全量状态表)
|
||||
3. **修复 commit** 推送
|
||||
|
||||
---
|
||||
|
||||
## 十、全量 FINDING 矩阵格式
|
||||
|
||||
维护一张总表:
|
||||
|
||||
```markdown
|
||||
| ID | 文件:行号 | 类 | 级 | 状态 | 描述 | 修复/备注 |
|
||||
|----|-----------|----|----|------|------|---------|
|
||||
| F1a-01 | api/auth.py:45 | VULN | P0 | ✅ | ... | ... |
|
||||
| F1b-03 | services/sync.py:187 | BUG | P2 | ⏳ | ... | ... |
|
||||
| F1c-02 | infra/ssh.py:92 | RISK | P1 | 📋 | ... | 网络隔离接受 |
|
||||
```
|
||||
|
||||
每次修复后更新状态。**代码待办 = 0 是合并前硬性要求。**
|
||||
|
||||
---
|
||||
|
||||
## 十一、文档纪律(来自 perfect-implementation.mdc)
|
||||
|
||||
```
|
||||
修复流程:发现 FINDING → 当场修复(P0/P1)或记录(P2/P3)→ 写 changelog → commit
|
||||
```
|
||||
|
||||
每次有意义的修复必须:
|
||||
|
||||
| 文档 | 路径 | 必含内容 |
|
||||
|------|------|---------|
|
||||
| Changelog | `docs/changelog/YYYY-MM-DD-<主题>.md` | 日期 / 变更摘要 / 动机 / 涉及文件 / 验证方式 |
|
||||
| 走读报告 | `docs/reports/audit-phase-Xn-line-walk.md` | 文件清单 / Closure 全表 / Findings / 走读签字 |
|
||||
| FINDING 矩阵 | `docs/reports/audit-findings-matrix.md` | 全量状态,持续更新 |
|
||||
|
||||
**禁止**:把 changelog 只写在 commit message 里代替文档。
|
||||
|
||||
---
|
||||
|
||||
## 十二、禁止行为清单
|
||||
|
||||
```
|
||||
❌ 无 `文件:行号` 的 FINDING
|
||||
❌ `rg 命中 ≠ Closure 数` 仍标「完成」
|
||||
❌ 子代理 Top 15 代替 Closure 全表
|
||||
❌ 单回复声称整个 server/ 或 web/ 已完成
|
||||
❌ 逐行审计与修代码混做(除非用户明确要求同步修复)
|
||||
❌ SAFE 无依据(不写为何 SAFE,等于没做)
|
||||
❌ FINDING 无修复建议(至少一行说明如何修)
|
||||
❌ 发现问题留 TODO,不当场修复(来自 perfect-implementation.mdc)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十三、常见高频漏报(必须覆盖)
|
||||
|
||||
| 陷阱 | 典型错误代码 | 正确做法 |
|
||||
|------|-------------|---------|
|
||||
| shlex 引号混用 | `f'"{shlex.quote(path)}.pid"'` | `f'{shlex.quote(path)}.pid'`(单引号直接用) |
|
||||
| tz 混用 | `naive_dt - datetime.now(utc)` | 比较前统一:`naive.replace(tzinfo=None)` 或全用 utc |
|
||||
| list `**` 解包 | `{**d, **some_list}` | `{"key": some_list}` |
|
||||
| 非原子 rate limit | `incr(k); expire(k, t)` | `pipeline: incr + expire` 或 Lua 脚本 |
|
||||
| DB 列宽 | `String(500)` 存加密 SSH 私钥 | `Text`(RSA 4096 Fernet 后 ~4300 chars)|
|
||||
| 变量替换嵌套 | `cmd.replace("$PASS", pw)` + pw 含 `$USER` | 先全部替换为 sentinel,再用真值替换 sentinel |
|
||||
| cron 除零 | `value % step` 未判 step==0 | `if step == 0: return False` |
|
||||
| 日志泄密 | `logger.info(settings.DATABASE_URL)` | 脱敏后再 log(仅显示 host:port) |
|
||||
| 命令已执行再检测 | shell.stdin.write(cmd) → check_dangerous(cmd) | 顺序错误;WebSSH 层无法阻断,只能记录 |
|
||||
| gather 吞异常 | `gather(*tasks, return_exceptions=True)` 未检查结果 | 遍历结果,对 isinstance(r, Exception) 单独处理 |
|
||||
|
||||
---
|
||||
|
||||
## 十四、给 AI 的执行提示词
|
||||
|
||||
### 14.1 开始走读(粘贴给 AI)
|
||||
|
||||
```
|
||||
请按以下规范对 [文件/目录] 进行逐行代码走读审计:
|
||||
|
||||
【规范】
|
||||
1. 单文件 8 步:登记行数 → 全文 Read 至 EOF → 规则扫描 → Closure 表 → 入口表 → 输入→sink → 八类核对 → DoD
|
||||
2. 每段 Read ≤200 行;末段须覆盖末行(offset ≥ N-5)
|
||||
3. 规则命中列表 H:对每条 Read ±15 行上下文
|
||||
4. Closure 表每行必须有:文件 | 行号 | 规则 | SAFE/FINDING | 严重度 | 理由
|
||||
5. FINDING 格式:ID | 文件:行号 | 类型(VULN/BUG/RISK...) | P0/P1/P2/P3 | 描述 | 修复建议
|
||||
6. H=0 仍须全文 Read,Closure 表注明「规则零命中,全文 CLEAN」
|
||||
7. 单回复最多 5 个文件完成 8 步
|
||||
|
||||
【禁止】
|
||||
- 无行号的 FINDING
|
||||
- 仅 grep 不全文 Read 就标「已审计」
|
||||
- 声称整个目录一次完成
|
||||
|
||||
【重点规则】(必检)
|
||||
- PY-01: f-string 命令拼接
|
||||
- PY-08: API Key == 比较(应用 compare_digest)
|
||||
- PY-09: except 静默
|
||||
- PY-11: tz-aware/naive 混用
|
||||
- PY-14: redis INCR+EXPIRE 非原子
|
||||
- PY-15: String(N) 列宽不足
|
||||
- FE-01: innerHTML 未 esc
|
||||
|
||||
【产出】
|
||||
- Closure 全表(含 SAFE)
|
||||
- FINDING 列表(含行号)
|
||||
- 入口表(API 文件)
|
||||
- 走读签字:文件 | N | H 数 | FINDING 数
|
||||
```
|
||||
|
||||
### 14.2 修复验证
|
||||
|
||||
```
|
||||
以下 FINDING 已修复,请验证修复是否完整:
|
||||
|
||||
FINDING: [ID] [文件:行号] [描述]
|
||||
修复前:
|
||||
[原代码]
|
||||
修复后:
|
||||
[新代码]
|
||||
|
||||
请确认:
|
||||
1. 修复是否覆盖了原始问题
|
||||
2. 修复是否引入新问题
|
||||
3. 是否有边界情况遗漏
|
||||
4. 若确认正确,将状态更新为 ✅
|
||||
```
|
||||
|
||||
### 14.3 闭环声明生成
|
||||
|
||||
```
|
||||
请根据以下走读记录生成 Phase X 走读闭环声明:
|
||||
|
||||
覆盖批次:[Xa, Xb, ...]
|
||||
FINDING 矩阵:[附表]
|
||||
|
||||
闭环声明必须包含:
|
||||
1. 覆盖范围(批次 + 文件列表 + 大约行数)
|
||||
2. FINDING 统计(总数 / ✅已修复 / 📋接受 / 🟡部分 / ⏳待修)
|
||||
3. CLEAN 文件列表(附简要理由)
|
||||
4. 代码待办数(目标 = 0)
|
||||
5. 遗留观察(不修复的设计说明)
|
||||
6. 签字:Phase X 走读已闭环,可进入 [下一阶段]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十五、闭环声明模板
|
||||
|
||||
```markdown
|
||||
# Phase X 走读闭环声明
|
||||
|
||||
**日期**: YYYY-MM-DD
|
||||
**标准**: 本规范 v2.0
|
||||
|
||||
## 覆盖范围
|
||||
| 批次 | 目录 | 文件数 | 总行数 |
|
||||
...
|
||||
|
||||
## FINDING 统计
|
||||
- 总计: N 条
|
||||
- ✅ 已修复: N
|
||||
- 📋 接受: N
|
||||
- 🟡 部分缓解: N
|
||||
- ⏳ 代码待办: **0**(合并前硬性要求)
|
||||
|
||||
## CLEAN 文件(无 FINDING)
|
||||
- `file1.py` — 鉴权正确,命令均 shlex.quote,无外部输入 sink
|
||||
- ...
|
||||
|
||||
## 遗留观察(不修复)
|
||||
- 子机 shell 执行面(F1f-01):📋 架构接受,网络隔离 + 审计日志
|
||||
|
||||
## 签字
|
||||
Phase X 走读已闭环。代码待办 0。可进入 [首次部署 / 下一功能迭代]。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*本规范完全通用,可直接粘贴给任何 AI 执行逐行走读任务。路径、批次编号按实际项目调整。*
|
||||
Reference in New Issue
Block a user