From 9866707c0492a87fcaca8d9b99ba4de7f28d01b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 01:55:04 +0000 Subject: [PATCH 1/2] =?UTF-8?q?chore(security):=20JSON-LD=20=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E7=BB=9F=E4=B8=80=E8=B5=B0=20safeJsonLdStrin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 lib/json-ld.ts 把 JSON.stringify 输出里能闭合 攻击载荷不再出现在输出里 - 普通对象仍是合法 JSON(JSON.parse 能还原) - < > & 都被转义 - user-generated 字段往返保真 文档:docs/SECURITY_INVARIANTS.md 新增 INV-FE-001 条目,与 backend 仓库 SECURITY.md 共用同一套不变量编号空间(前端用 INV-FE-* 前缀避免冲突)。 历史:2026-05-07 三方 CR attack chain A 起点。配合 backend 仓库 PR #26 一同收口(admin→superadmin 提权阻断 / chat 越权写阻断 / 密码迁 bcrypt / user_follows 表补建 / compose 弱密码默认值收紧)。 --- app/[locale]/docs/[...slug]/page.tsx | 5 +-- app/[locale]/u/[username]/page.tsx | 3 +- app/layout.tsx | 5 +-- docs/SECURITY_INVARIANTS.md | 46 +++++++++++++++++++++++++ lib/json-ld.ts | 26 +++++++++++++++ tests/json-ld.test.ts | 50 ++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 docs/SECURITY_INVARIANTS.md create mode 100644 lib/json-ld.ts create mode 100644 tests/json-ld.test.ts diff --git a/app/[locale]/docs/[...slug]/page.tsx b/app/[locale]/docs/[...slug]/page.tsx index 2a13a16..7ca6b3d 100644 --- a/app/[locale]/docs/[...slug]/page.tsx +++ b/app/[locale]/docs/[...slug]/page.tsx @@ -1,4 +1,5 @@ import { source } from "@/lib/source"; +import { safeJsonLdString } from "@/lib/json-ld"; import { SITE_URL } from "@/lib/site-url"; import { DocsPage, DocsBody } from "fumadocs-ui/page"; import { notFound } from "next/navigation"; @@ -104,12 +105,12 @@ export default async function DocPage({ params }: Param) { "})` + 输出不能包含字面 ``,必须含 ``。 +- **为什么**:`JSON.stringify` 默认不转义 `<` `>` `&`,攻击者把 + `` + 写进任何 user-generated 字段(profile bio、displayName 等)即触发 stored XSS。 + satoken 存在 localStorage 且写入非 HttpOnly cookie(跨子域 pgAdmin 的设计取舍), + 一次 XSS 等于完整账户接管。 +- **历史**:2026-05-07 三方 CR attack chain A 起点(详见内部报告)。 diff --git a/lib/json-ld.ts b/lib/json-ld.ts new file mode 100644 index 0000000..2514e02 --- /dev/null +++ b/lib/json-ld.ts @@ -0,0 +1,26 @@ +/** + * 把任意对象序列化为可安全嵌入 + * JSON.stringify 默认不转义 `<`,攻击者文本作为合法 JSON 字符串嵌入 闭合 script 块,接着把后续 ` + * + * JSON.stringify 默认输出原文,浏览器看到 `` 就闭合 script block, + * 接着把后续 ` 不再出现在输出里", () => { + const payload = { + bio: ``, + }; + const out = safeJsonLdString(payload); + expect(out).not.toContain(""); + expect(out).not.toContain("载荷 with & 'quotes'` }; + const out = safeJsonLdString(original); + const parsed = JSON.parse(out); + expect(parsed.bio).toBe(original.bio); + }); +}); From 3e9285e4df96c438f70902e648159f190d949862 Mon Sep 17 00:00:00 2001 From: Siz Long Date: Fri, 8 May 2026 10:13:09 +0800 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/SECURITY_INVARIANTS.md | 8 ++++---- lib/json-ld.ts | 12 ++++++++++-- tests/json-ld.test.ts | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/SECURITY_INVARIANTS.md b/docs/SECURITY_INVARIANTS.md index 2dc5729..0c12026 100644 --- a/docs/SECURITY_INVARIANTS.md +++ b/docs/SECURITY_INVARIANTS.md @@ -1,7 +1,7 @@ # 前端安全不变量(Security Invariants) > 这是给维护者看的代码不变量清单。 -> 公开的 vulnerability disclosure policy 见 `frontend/SECURITY.md`。 +> 公开的 vulnerability disclosure policy 见 `SECURITY.md`。 本文档登记前端代码中**不可变更的安全保护点**。 每条不变量都对应一段 lint / 测试 / 代码模式,CI 应能捕获回归。 @@ -35,9 +35,9 @@ - 暂时通过 grep 巡查兜底: `rg -t tsx -t ts 'dangerouslySetInnerHTML' app/ | grep -v safeJsonLdString | grep "application/ld\\+json"` 应返回 0 行。建议未来加 ESLint 自定义规则。 - - 推荐补一个单元测试: - `safeJsonLdString({bio: ""})` - 输出不能包含字面 ``,必须含 ``。 + - 现有单元测试见:`tests/json-ld.test.ts` + 例如 `safeJsonLdString({bio: ""})` + 输出不能包含字面 `<` 或 ``,并且应包含转义后的 `\\u003c` 序列。 - **为什么**:`JSON.stringify` 默认不转义 `<` `>` `&`,攻击者把 `` 写进任何 user-generated 字段(profile bio、displayName 等)即触发 stored XSS。 diff --git a/lib/json-ld.ts b/lib/json-ld.ts index 2514e02..4fdd7bf 100644 --- a/lib/json-ld.ts +++ b/lib/json-ld.ts @@ -1,7 +1,7 @@ /** * 把任意对象序列化为可安全嵌入 ` 就闭合 script block, * 接着把后续 `