# 逐行代码走读审计完整规范 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/**` | `frontend/node_modules/**` | --- ## 三、单文件走读流程(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 | `