| Version | Supported |
|---|---|
| 1.x | ✅ |
| < 1.0 | ❌ |
扩展由 .github/workflows/release.yml 从打标签的版本发布,只维护最新一条版本线。
The extension is published from tagged releases; only the latest version line is maintained.
它拿的是 <all_urls> 主机权限,能为任意页面代发请求并注入请求头,也能把页面 fetch / XMLHttpRequest / WebSocket 改道到另一个环境。也就是说,一个规则注入校验漏洞、一条让被阻断请求真的发出去的回归、或一处把跨源消息当成可信来源的判断失误,都可能变成目标环境 token 泄露或请求被悄悄改写。这类问题请不要开公开 Issue。
It holds the <all_urls> host permission, can issue requests on behalf of any page and inject headers, and can redirect page fetch / XHR / WebSocket traffic elsewhere. A flaw in header validation, in block-rule enforcement, or in message-origin checks could leak a target-environment token or silently rewrite requests. Please don't open a public issue for that.
- 首选:仓库首页 Security → Report a vulnerability(私密漏洞报告),填写复现步骤即可,只有你与维护者能看到。该入口需要仓库所有者先在
Settings → General → Advanced勾选 Privately report a security vulnerability(见 GITHUB.md §6)。 Preferred: use Security → Report a vulnerability on the repository home page. It is private to you and the maintainers. The owner must first enable Privately report a security vulnerability underSettings → General → Advanced. - 入口未开启时,开一个标题只写
security的最小 Issue 并@维护者,不要在正文里贴漏洞细节,约到私密渠道再展开。 If that entry point is off, open a minimal issue titled onlysecuritywithout any details and ask for a private channel. - 复现材料里请用
fat-api.example.com/uat-api.example.com这类示例域名,不要贴真实内网域名、token 或抓包数据——Issue 正文是公开的,仓库与商店审核都会看到。 Use*.example.comdomains in reports. Never paste real internal hostnames, tokens or captured traffic: issue text is public.
- 收到后先确认并给出是否复现的结论与临时规避手段(单人维护,不承诺固定 SLA)。 We acknowledge first and come back with reproduction status plus a workaround; it is a solo-maintained project, so no fixed SLA is promised.
- 确认的问题优先出补丁版本并按 RELEASING.md 走商店提审;商店审核时长不受本仓库控制,会在补丁发布时一并说明。 Confirmed issues ship as a patch release through the store; store review latency is outside our control and gets called out in the release notes.
- 修复会写进 CHANGELOG.md;是否署名致谢完全尊重报告者意愿,不公开未修复的细节。
Fixes are listed in
CHANGELOG.md; attribution follows the reporter's preference, and unpatched details stay undisclosed.
请求头名校验且拒绝 CRLF、状态码钳制在 200–599、请求体 10 MB 上限(按 UTF-8 字节量最终出站 body,规则覆盖的 body 同样计入)、被规则拒绝的请求在本地日志留下原因、用户正则的嵌套量词(ReDoS)筛查、送给网络层的正则必须过 Chrome 的 RE2 校验且替换引用越界检查、跨 world 消息一律以 window.location.origin 为目标源、跨 world 的代理响应在桥接层整形(失败信封补齐 requestId 与数值 status,后台内部错误文案不外泄给页面)、响应体改写的路径写入拒绝 __proto__ / constructor / prototype 键名、修改状态的消息需过 isTrustedSender、导入的 HAR/cURL/JSON 在边界处过滤非法项(导入与抓包路径上的头映射还会逐条丢弃非法项)、导出默认剔除凭据(Authorization / Cookie 等头,配置导出另含 token 类查询参数覆盖,取消勾选才做原样本地备份)、搜索高亮按文本节点分段渲染(此前的 v-html 逃生通道已删除)、无 eval / 远程代码、无遥测与埋点。
Header name validation with CRLF rejected, status codes clamped to 200–599, a 10 MB body cap (measured in UTF-8 bytes on the final outbound body, including one injected by a rule), rejected requests leaving a reason in the local log, ReDoS screening on user regexes, RE2 validation plus substitution bounds-checking for rules bound to the network layer, window.location.origin as the target origin for every cross-world message, cross-world proxy responses normalized at the bridge (failure envelopes get a requestId and a numeric status, and internal error text is never forwarded to the page), response-body path writes rejecting __proto__ / constructor / prototype keys, isTrustedSender on state-changing messages, boundary filtering for imported HAR/cURL/JSON (header maps on the import and capture path additionally drop invalid entries one by one), exports that strip credentials by default — Authorization / Cookie style headers, plus token-like query-parameter overrides on the config side, unticked only for a verbatim local backup — search highlighting rendered as text-node segments (the former v-html escape hatch is removed), no eval / remote code, and no telemetry.
上面这些基线里,ReDoS 筛查、导入规则过滤、响应体路径写入的原型链拒绝、跨 world 响应信封整形、查询参数编码保持、请求头与 CRLF 校验(isValidHeaderEntry / filterIncomingHeaders / validateRuleHeaders)、请求体上限(exceedsBodyCap)与状态码钳制(clampResponseStatus)已有直接的 Vitest 用例(tests/security-and-optimization.test.ts、tests/round2-regression.test.ts、tests/channel-consistency.test.ts、tests/proxyResponseGuard.test.ts、tests/requestGuard.test.ts、tests/highlight.test.ts、tests/exportSanitize.test.ts)。头名/CRLF 判据本轮从后台抽到 utils/headerValidation.ts,由规则表单的保存侧与导入侧共用,并新增 findInvalidHeaderNames(保存前逐项报出问题头名,只报名不回值)与 sanitizeImportedHeaderMap(导入路径逐条清洗)两个出口;exceedsBodyCap 与 clampResponseStatus 则是 entrypoints/background/proxyHandler.ts 导出的行为不变的可见性调整。isTrustedSender 与它守的那道 gate 由 tests/messageRouter.test.ts 直接对真实现断言(vi.stubGlobal + 动态 import,不比对实现副本):sender.url 必须是本扩展 URL 根的前缀——同前缀伪 ID、其他扩展 ID、非扩展协议、空 URL、把根串塞在 URL 中间都判不可信,且只认 url 一个字段;每一个状态修改类消息在不可信 sender 下都同步回 Unauthorized sender、不触达存储,且 gate 先于参数校验;任何消息类型在不可信 sender 下都不许产生写入(新加写 handler 却忘了登记进 gate 清单时靠这条兜住)。只读消息与 PROXY_REQUEST 不经 gate 是当前实现的既有事实,测试把它钉成契约:内容脚本的 sender.url 就是页面 URL,加 gate 会让代理与配置回放当场失效。本节此前记录的「isTrustedSender 无单测入口」一项已随之闭合;按上文「处理承诺」,其余未修复项的细节仍不在此公开。
Of these, ReDoS screening, imported-rule filtering, prototype-chain rejection in response-body path writes, cross-world response envelope normalization, query-parameter encoding fidelity, header/CRLF validation (isValidHeaderEntry / filterIncomingHeaders / validateRuleHeaders), the body cap (exceedsBodyCap) and status clamping (clampResponseStatus) all have direct Vitest cases (security-and-optimization, round2-regression, channel-consistency, proxyResponseGuard, requestGuard, highlight, exportSanitize). The header/CRLF predicates were lifted out of the background into utils/headerValidation.ts this round, so the rule form's save path and the import path share one set, and they gained two exports: findInvalidHeaderNames (lists offending header names before anything is saved, names only, never values) and sanitizeImportedHeaderMap (per-entry dropping on the import path). exceedsBodyCap and clampResponseStatus remain a visibility-only export from proxyHandler.ts. isTrustedSender and the gate it guards are asserted against the real implementation by tests/messageRouter.test.ts (vi.stubGlobal plus a dynamic import; nothing compares against a copy of the code): sender.url must start with this extension's URL root — same-prefix spoofed IDs, other extension IDs, non-extension schemes, empty URLs and a root string embedded mid-URL all count as untrusted, and url is the only field consulted; every state-changing message type answers an untrusted sender synchronously with Unauthorized sender, never reaches storage, and is gated before any argument validation; and no message type at all may produce a write for an untrusted sender, which is what catches a future write handler that forgot to register in the gate list. That read-only messages and PROXY_REQUEST bypass the gate is a fact of the current implementation, which the test pins as a contract: a content script's sender.url is the injected page's URL, so gating them would break proxying and config replay outright. The item previously recorded in this section — "isTrustedSender has no unit-test entry point" — is closed with this; per the disclosure commitment above, details of anything still unfixed stay undisclosed.