你以为正则里的 ^ 和 $ 永远只认字符串开头和结尾?Firefox 155 用两个锚点把它们彻底翻了一个面——multiline 模式下的行为从此不一样了
写过正则的人大概都踩过这个坑:写了一个 /^foo$/ 以为它只匹配整个字符串的 “foo”,结果加上 /m flag 之后,字符串里的每一行开头和结尾都被它认领了—— multiline 模式下的 ^ 和 $ 从「字符串边界」变成了「行首和行尾」,这个行为差异让很多安全相关的正则悄悄失效。
今天,Firefox 155 Nightly 带来了 RegExp Buffer Boundaries 提案的两个新锚点:A 和 z。它们的意义非常直接——永远只认整个输入的绝对开头和绝对结尾,不受 multiline flag 影响。
两个新锚点:A 和 z
这个提案(tc39.es/proposal-regexp-buffer-boundaries,Stage 3)在全球多数正则引擎(Perl、PCRE、.NET、Python re、Java、Go、Rust regex)中早已是标准功能,唯独 JavaScript 一直没有。现在 Firefox Nightly 155 率先实现了它们:
// 传统 ^/$ 在 multiline 模式下的行为
const re1 = /^foo$/mu;
re1.test("foo"); // true
re1.test("foo
bar"); // true ← 误匹配!$ 认了行尾
// A/z 永远只认整个输入的绝对边界
const re2 = /Afooz/mu;
re2.test("foo"); // true
re2.test("foo
bar"); // false ← 正确拒绝
对比一目了然:A 等价于「字符串最前面」,z 等价于「字符串最后面」,加不加 /m flag 都不会变。
为什么这件事重要
最典型的受害场景是输入验证。很多后端用这样的正则限制输入格式:
// 假设这是一个密码强度检查
const valid = /^[a-zA-Z0-9]+$/m.test(userInput);
如果攻击者输入 "safe <script>alert(1)</script>",multiline 模式下 ^ 和 $ 匹配了每一行的开头和结尾,这个恶意 payload 会通过验证——这在某些解析逻辑下会导致 XSS。
用 A...z 替换就能彻底封堵:
// 更安全的写法
const valid = /A[a-zA-Z0-9]+z/u.test(userInput);
同样的问题也出现在日志解析、数据格式校验、代码扫描等场景中。
语法细节
必须配合 /u 或 /v flag 使用,否则 A 和 z 在老版本里只是普通字符(字面量 “A” 和 “z”):
/Afooz/u.test("foo"); // true
/Afooz/.test("foo"); // false — 没有 u flag,A 是字符 "A"
z 匹配输入的最末端(无尾随换行),Z 在 2026 年 5 月重新加入提案(Stage 2.7),用于匹配可选尾随换行符的场景:
/endZ/u.test("The end
"); // true
/endz/u.test("The end
"); // false — z 不认尾随换行
浏览器支持现状
- Firefox 155 Nightly:已支持,需设置
javascript.options.experimental.regexp_buffer_boundaries = true - Chrome:未支持(tracking bug)
- Safari:未支持
- Node.js:未支持
这是一个典型的「语言层能力补全」,Stage 3 意味着规范文本已经完成,浏览器实现跟上后离正式发布不远。
迁移路径
渐进增强是最安全的写法:
function isValidInput(str) {
// 优先用原生 A/z
try {
return /A[a-zA-Z0-9]+z/iu.test(str);
} catch (e) {
// 降级:手动在首尾加 ^$
return /^[a-zA-Z0-9]+$/u.test(str) && !str.includes('
');
}
}
下一步: 检查你项目里所有带 /m flag 的正则,逐一确认 ^...$ 的语义是否真的是「匹配行首行尾」而非「匹配输入首尾」——如果是后者,换成 A...z 是五分钟就能完成的安全加固。
评论区
登录后可评论。