正则少一个 re.MULTILINE,我的安全闸门全程空转——而我以为修好了

一、起点:报告被 triage 判了 NA

我在 Bugcrowd 上投了个漏洞报告,隔天收到回复,核心两句:

unable to reproduce the described issue content appears to be low-quality or potentially automated (AI-generated)

翻译一下:复现不了 + 看着像 AI 生成的。

第一句我能忍,第二句真扎心。所以我回头做了一次系统复盘,给自己的流水线加了五道改造,其中一道是------AI 味检测器。

二、我"修好"了它,还有实测数据

检测器的逻辑很直白:扫描报告正文,命中模板化套话/模板标题就扣分,压不到 0.6 以上不许提交。

升级点有三个,我列出来是为了后面打自己的脸:

  1. 套话库补充(materially reduces the cost、collectively this、attack-surface map 这类 triager 一眼能识别的句式)
  2. 模板标题检测大小写不敏感 ,并且容忍 ## Summary 这种带 markdown 前缀的写法
  3. 加粗密度检测(满屏 **xxx** 是 AI 排版特征)

改完我跑了对照,数据漂亮:

稿件 分数
旧稿(AI 味重) 0.5 ✅ 被拦
新稿(改过的) 1.0,零命中 ✅ 过闸

我当时挺满意:"区分度出来了。"

三、然后有人让我复跑一遍

我同事(一个不会累、不会不好意思的家伙)说:报告里的数我不接受转述,重新跑一遍对账。

行,跑。跑完他问了一句:

四份稿件的"模板标题"命中数,全是 0。你觉得这正常吗?

我一看,还真是。连那份明明白白写着 ## Summary、## Impact、## Remediation 的旧稿,模板标题命中也是 0。

也就是说:我升级的那条规则,一分力都没出。 旧稿那个 0.5,完全是靠"套话短语 + 加粗密度"两项扣出来的。模板骨架检测------全程空转。

而新稿那个"1.0 零命中",是漏检漏出来的满分,不是干净。

四、根因:正则少一个 flag

代码长这样:

python 复制代码
_TEMPLATE_HEADER_PAT = re.compile(
    r"^[ \t#*\-]*(" + "|".join(re.escape(h) for h in AI_TEMPLATE_HEADERS) + r")[ \t]*:?\s*$",
    re.IGNORECASE,          # ← 我加了大小写不敏感
)

问题在 ^ 和 $。

Python 的 re 默认不开 re.MULTILINE 时,^ 和 $ 只锚定整个字符串的首尾。 而报告正文是一整篇多行文本------所以这个正则表示的是"整篇报告必须恰好只有 一个标题行",永远匹配不到夹在中间的 ## Summary。

我加对了 IGNORECASE,却漏了真正关键的那个。

实测三行,证据确凿:

sql 复制代码
正则 flags 含 MULTILINE        → False
正则在多行报告上 search()       → False   ← 漏检
同一正则手动补 re.M 后          → True    ← 本来能中

五、还藏着一个更辣的

修 MULTILINE 之前,我先把循环读了一遍:

python 复制代码
for h in AI_TEMPLATE_HEADERS:
    if _TEMPLATE_HEADER_PAT.search(text):
        cliche_hits.append("模板标题: " + h)

看着没问题?一旦正则会中了,这个循环会把名单里 14 个标题全部 append 一遍 ------因为它判断的是"文本里有没有任一模板标题",中的时候对每个 h 都为真。

也就是说:我要是不修 MULTILINE,直接补 flag,闸门会从"全程空转"变成"一碰就扣满 0.5"。两个 bug 互相掩着,一个装死、一个埋伏。

正确写法是对实际命中的结果去重:

python 复制代码
for _h in sorted({m.group(1).lower() for m in _TEMPLATE_HEADER_PAT.finditer(text)}):
    cliche_hits.append("模板标题: " + _h)

六、修完的复测

稿件 修前 修后
旧稿(AI 味重) 0.5(模板标题 0 条) 0.5(模板标题 3 条 + 套话 5 条)
我 10-12 的定稿 0.9 0.6
新稿 v2 1.0(假满分) 0.8(命中 2 条模板标题)

最后一行是重点:那个"1.0 零命中"是假的。 修好之后它掉到 0.8,因为它的 Suggested fix 和 What I found 两个标题本来就在黑名单里,只是检测器当时瞎了。

七、我学到的三件事

1. "改好了"这三个字,必须拿实测数据换,尤其是你自己写的校验逻辑。

我这次是怎么翻车的?不是没测,是测的时候只看了总分。0.5 和 1.0 都出来了,看着有区分度,就没拆开看"这分是谁扣的"。如果当时多打印一行命中明细,第一条规则空转当场就暴露了。

2. 一个"只会放行"的检查器,不是检查器。

它全程说 yes,你说它没问题。真正好用的闸门,得先证明它能说不------拿已知的坏样本去撞,撞得停才算数。后来我给这套检测器补了夹具:旧稿必须 0.5、v2 必须 1.0,每次改完跑一遍。

3. 两个 bug 会互相掩护。

空转的那个让埋伏的那个永远不暴露。只看"现在能不能用"是看不出来的,得看"每一条规则分别出了多少力"。


你们写校验逻辑的时候,会拆开看每一条规则各自的命中数吗? 还是只看最终分数?

我现在的要求是:任何"我修好了"的结论,必须附带一张"每条规则分别命中几条"的表。不然我自己都不信------这次就是活生生的例子。

相关推荐
柠檬味拥抱1 小时前
脑肿瘤检测数据集 | 4400张YOLO医学影像数据集
后端
柠檬味拥抱1 小时前
汽车损伤检测数据集 | 11700张YOLO车辆定损数据集
后端
柠檬味拥抱1 小时前
基于 YOLO11 的规模化禽舍鸡只行为视觉监测平台:从模型到 Streamlit 全流程实战
后端
柠檬味拥抱1 小时前
火灾智能检测预警系统:从 YOLO11n 训练到 Streamlit 全链路实战【实战源码】
后端
言午说数据1 小时前
Paimon把LSM搬进湖里的得与失
大数据·后端
故图1 小时前
JDBC 读 ResultSet 读到一半,报 stream closed
后端
武子康1 小时前
AI Agent 设备指令超时后,重试会执行两次吗?
人工智能·后端·agent
全栈弄潮儿1 小时前
Python实战第6期:列表与元组
后端·python·编程语言
用户817595220131 小时前
QNAP NAS 部署 Vaultwarden:使用 Cloudflare Tunnel 实现外网访问与手机同步
后端