现象
hasRegexLookaround(backend/internal/service/openai_responses_tool_schema.go)在字符类内遇到 -[ 时,把 [ 当作嵌套类的开启;Rust regex 则把它当作范围运算符 - 的右端点字面量。
于是类被读得比实际更长,可能把类之外的一个真 lookaround 吞进类里,判成"无 lookaround"、原样转发,最终撞 upstream 400。
实测例(Rust 判为含 lookaround,我们判为不含):
[b=-[]\xa0\[(?<!\((?!
(?<n>( ?= --(?:#[])-[:alpha:](?<=
根因
Rust 里 - 是否为范围运算符依赖上下文(首位 - 是字面量、-- 是集合差、] 前的 - 是字面量),只有当它确实是范围运算符时,后面的 [ 才是字面端点。核对示例:
[a-[b]] → invalid character class range, the start must be <= the end(说明 a-[ 被解析成 a 到 [ 的范围)
[-[a]] → ok(首位 - 是字面量,[a] 是嵌套类)
[a--[b]] → ok(-- 是集合差,[b] 是嵌套类)
影响面(实测)
以 regex 1.13.1 / regex-syntax 0.8.11 为 oracle 做差分,两组独立种子共 28 万条随机 pattern(语料刻意向 [、-、#、(?x) 这些决策点倾斜):
- 假阳性(删掉 Rust 能接受的 pattern)= 0,两组种子都是 0。
- 假阴性 12 条,占全部 lookaround pattern 的 0.0083% / 0.0341%,全部根因为
-[。
偏差方向恒定:把 [ 当成嵌套类开启只会让字符类向后延伸,因此只可能产生假阴性(可见的 upstream 400),不可能产生假阳性(静默删掉合法约束)。这与本模块"宁可不改也不猜"的失败方向一致。
为什么不在 PR #488 里修
修它必须在字符类扫描主循环里引入"当前 - 是不是范围运算符"的状态判断(要同时处理首位 -、-- 集合差、] 前的 -)。那个主循环正是"假阳性 = 0"这一实测性质所依赖的代码;判断只要反向出错一次,就会让类提前闭合,把类内容读成 lookaround,从而把一个可见的 400 变成一次静默的约束删除——这是本模块唯一真正危险的失效模式。
用 0.008%–0.034%(且是在刻意构造的对抗语料下)的漏判率,去赌已经测出来的安全性质,不划算。
如果要修
必须满足:
- 重跑差分(至少两组独立种子、各 ≥10 万条),假阳性仍为 0;
- 为
[-[a]]、[a--[b]]、[!-[]、[a-[b](?=c)] 这几种形状各加具名用例;
- 在
openai_responses_tool_schema_test.go 的突变体注册表里新增对应突变体,并确认新用例能杀死它。
差分 oracle 与突变对账的做法记录在 docs/openai_responses_tool_schema_sanitizer.md 的"与 Rust regex 的差分核对结果"一节。
现象
hasRegexLookaround(backend/internal/service/openai_responses_tool_schema.go)在字符类内遇到-[时,把[当作嵌套类的开启;Rustregex则把它当作范围运算符-的右端点字面量。于是类被读得比实际更长,可能把类之外的一个真 lookaround 吞进类里,判成"无 lookaround"、原样转发,最终撞 upstream 400。
实测例(Rust 判为含 lookaround,我们判为不含):
根因
Rust 里
-是否为范围运算符依赖上下文(首位-是字面量、--是集合差、]前的-是字面量),只有当它确实是范围运算符时,后面的[才是字面端点。核对示例:[a-[b]]→invalid character class range, the start must be <= the end(说明a-[被解析成a到[的范围)[-[a]]→ ok(首位-是字面量,[a]是嵌套类)[a--[b]]→ ok(--是集合差,[b]是嵌套类)影响面(实测)
以
regex 1.13.1/regex-syntax 0.8.11为 oracle 做差分,两组独立种子共 28 万条随机 pattern(语料刻意向[、-、#、(?x)这些决策点倾斜):-[。偏差方向恒定:把
[当成嵌套类开启只会让字符类向后延伸,因此只可能产生假阴性(可见的 upstream 400),不可能产生假阳性(静默删掉合法约束)。这与本模块"宁可不改也不猜"的失败方向一致。为什么不在 PR #488 里修
修它必须在字符类扫描主循环里引入"当前
-是不是范围运算符"的状态判断(要同时处理首位-、--集合差、]前的-)。那个主循环正是"假阳性 = 0"这一实测性质所依赖的代码;判断只要反向出错一次,就会让类提前闭合,把类内容读成 lookaround,从而把一个可见的 400 变成一次静默的约束删除——这是本模块唯一真正危险的失效模式。用 0.008%–0.034%(且是在刻意构造的对抗语料下)的漏判率,去赌已经测出来的安全性质,不划算。
如果要修
必须满足:
[-[a]]、[a--[b]]、[!-[]、[a-[b](?=c)]这几种形状各加具名用例;openai_responses_tool_schema_test.go的突变体注册表里新增对应突变体,并确认新用例能杀死它。差分 oracle 与突变对账的做法记录在
docs/openai_responses_tool_schema_sanitizer.md的"与 Rustregex的差分核对结果"一节。