---
title: 检测器判据校准 — 文本模式扫描的「代码 vs 注释/文档串」区分
tags: [静默失败, 检测器, 假阳性, 台账]
created: 2026-09-12
status: final
---

# 检测器判据校准（2026-09-12 晚）

## 一、现象：A3 执行性从 0 变红（4 条漂移）

`assertion_ledger.py` 报 A3 执行性 **4 条漂移**，而底层扫描器 `scan_all_exec_layer.py` 实报
**命中 1 处** —— 数字对不上，先查「谁在说谎」。

## 二、两处独立缺陷（都被找到了）

### 缺陷1: 扫描器把「文档串里的示例文本」当真病灶
我新建的检测器 `midstring_injection_guard.py` 的**模块文档串**里写了教学示例：
```python
"""一次「把 `|| echo "x: timeout"` 换成 `|| bash on_fail.sh "x" $?`」的批量修复…"""
```
`scan_all_exec_layer.py` 只跳 `#` 注释行，**不认 Python 三引号字符串** → 把自己人的示例判成真病灶。
⇒ 假阳性。

**修法（有边界的，不是一刀切）**：
- 跳过**三引号**（文档串）—— 含单行 `"""…|| echo "x: timeout"…"""`
- **不跳普通字符串字面量** —— 因为 `os.system('… || echo "y: timeout"')` 里的 shell 片段
  **是真病灶，必须报**（跳了会造成假阴性）

### 缺陷2: 台账把「输出行数」当「漂移数」
```python
hits = [] if "未发现" in out else [l.strip() for l in out.splitlines() if l.strip()][:20]
```
取**全部非空输出行** → 表头 + 空行 + 分类行 + 明细 = 1 处命中被撑成 **4 条漂移**。
⇒ 指标失真（自动放大 4 倍）。
**修法**: 解析 `🔴 命中 N 处` 的真实数字；明细行只作展示。

## 三、方法：改判据必须跑反例自证（本轮自己被它抓了一次）

首次修复只处理了**多行**文档串 → 自证立刻暴露 `good_doc.py`（**单行** `"""示例…"""`）仍被误报
→ **修复不完整，不许提交**。补上「三引号一旦出现即视为文档串行」后 4/4 通过：

| 样本 | 期望 | 实测 |
|---|---|---|
| `.sh` 真病灶 `… \|\| echo "x: timeout"` | 命中 | ✅ |
| `.py` 代码内 `os.system('… \|\| echo "y: timeout"')` | 命中 | ✅ |
| `.py` **单行文档串**示例文本 | **不报** | ✅ |
| `.sh` 注释行旧写法 | 不报 | ✅ |

## 四、可复用结论

1. **文本模式类检测器必须区分「可执行代码」与「注释/文档串」** —— 否则检测器自己的
   文档就是假阳性来源（自指陷阱）；但**字符串字面量不能一刀切跳过**（shell 片段常藏在里面）。
2. **0 命中 / 判据改动，一律配反例自证** —— 本轮靠它才知道第一次修得不完整。
3. **台账的数字口径要问「它数的是什么」** —— 「非空输出行数」≠「命中数」，
   指标失真比漏检更危险（会让人以为系统在恶化，从而对指标麻木）。
