docs(core): M240 复审 P2-1/P2-2/P2-3 —— 口径收窄、大写判据钉住、找回丢掉的负例

P2-1:「抹除能力没有下降」是绝对承诺,已改成准确口径——收窄的就是三类形状
(`=` 边界、填充不计段长、全小写点分串),每条都在 shape 模块文档里写明,并用
`known_narrowings_old_masked_new_keeps` 把「旧行为抹、新行为留」钉住。三条的旧行为
是用 M240 之前的真实代码(`git show 74be510:…` + rustc 探针)实测的,不是推断。

P2-2:「必须含大写」原先没有任何测试钉住(复审实测:删掉它 13 条仍全绿)。
查明原因:DNS 形状的全小写点分串早就被"首段是主机名"那条豁免救下,大写判据
只对**非 DNS 形状**的全小写点分串承担区分作用(标签带 `_`、标签超 63 字符、
`/` 前只有一段主机名)。选择保留(复审的 (b)),并补
`the_uppercase_requirement_is_load_bearing`(`sophisticated_backpacks.upgrades.reference`
等两条)——删掉判据 → 该测试与 `lowercase_dotted_names_are_spared` 红、其余 15 条绿。
同时改正事实错误:不是"第三段必为大写",而是头的 base64url **第三个字符**恒为
`ey` + `I/J/K/L`(`7B 22 xx` 的第三组 6 位 = `b1` 低 4 位 ++ `b2` 高 2 位 → 索引 8..11)。

P2-3:找回重构时丢掉的老负例 `dotted_token_rule_does_not_eat_normal_text`
(4 条用例 + 直接断言 `!has_dotted_token(...)`,含 `eyJhbGciOiJIUzI1NiJ9.short.abc`);
语料测试的 doc 注明它**只守一个方向**,不得当"正例仍被抹"的背书。

判据与阈值一个字没动(40 / 3×8 / 64),扫描器代码零改动,纯文档 + 测试。
This commit is contained in:
AskaEth committed 2026-11-11 10:15:49 +08:00
1 parent 43f77d8865
commit 66929786c0
3 files changed
+123 -11

No files matched your search

+6 -3
View File
@@ -19,12 +19,15 @@
//! 修法不是抬阈值,而是给每条规则加**必要条件**(详见 [`shape`] 的表):
//!
//! - `=` 不算 token 字符(赋值边界):`-D<key>=<品牌>` 不再被当成三段式 token;
//! - 带点 token 必须含大写字母、且首段不是主机名(JWT 头 `eyJ…` 必含大写、JWT 不含 `/`);
//! - 带点 token 必须含大写字母、且首段不是主机名(JWT 头的 base64url 第三个字符恒为
//! `I/J/K/L`,且 base64url 不含 `/`);
//! - ≥64 的不透明串若是「路径形状」(被 `/` 切开且每段都是词形名字)就整条保留,
//! 但**段本身**仍够长的照抹(`/…/objects/<64 位 hex>` 里只有那段消失)。
//!
//! 三条出口都只放行"整段不可能是凭据"的形状,[`Redactor::with_secret`] 登记过的明文值
//! 走的是第一条替换路径,与这些启发式无关——真 token 的抹除能力没有下降。
//! **口径(别读成"抹除能力没有下降")**:登记过明文值的那条替换路径没动,JWT / sha256 /
//! base64 的正例全部仍被抹;但有三类形状是「旧行为抹、新行为留」——`=` 边界、填充不计段长、
//! 全小写点分串。三条都在 [`shape`] 的「旧行为抹、新行为留」一节里逐条写明理由,
//! 前两条是本次要修的误杀,第三条是代价。
use std::borrow::Cow;
use std::path::Path;