chore(security): JSON-LD 序列化统一走 safeJsonLdString#338
Merged
longsizhuo merged 2 commits intomainfrom May 8, 2026
Merged
Conversation
新增 lib/json-ld.ts 把 JSON.stringify 输出里能闭合 <script> 块的字符 (< > & U+2028 U+2029)替换成字面 \uXXXX 6 字符序列。JSON.parse 仍能 还原;浏览器 HTML 解析器看不到 < 自然不会闭合 script 块,阻断 stored XSS。 迁移点: - app/[locale]/u/[username]/page.tsx 的 personJsonLd(含用户 bio,攻击面最大) - app/[locale]/docs/[...slug]/page.tsx 的 articleJsonLd / breadcrumbJsonLd - app/layout.tsx 的 WebSite / Organization 结构化数据(当前都是常量但 defense-in-depth 一并迁移,避免未来加 user-generated 字段时漏改) 测试:tests/json-ld.test.ts 4 条覆盖 - </script> 攻击载荷不再出现在输出里 - 普通对象仍是合法 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 弱密码默认值收紧)。
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
本 PR 为前端 JSON-LD 注入点提供统一的安全序列化方法,修复将用户可控字段直接 JSON.stringify 嵌入 <script type="application/ld+json"> 导致的存储型 XSS 风险(INV-FE-001),并在关键页面完成迁移与回归测试。
Changes:
- 新增
safeJsonLdString:对 JSON.stringify 输出进行必要字符转义,确保可安全嵌入 JSON-LD<script>块 - 将 3 处 JSON-LD
dangerouslySetInnerHTML从JSON.stringify迁移为safeJsonLdString - 新增回归测试与维护者安全不变量文档(SECURITY_INVARIANTS)
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
lib/json-ld.ts |
新增 JSON-LD 安全序列化工具函数 safeJsonLdString |
app/layout.tsx |
站点级 WebSite / Organization JSON-LD 改用 safeJsonLdString |
app/[locale]/u/[username]/page.tsx |
个人主页 Person JSON-LD(含 bio)改用 safeJsonLdString |
app/[locale]/docs/[...slug]/page.tsx |
Docs 的 Article/Breadcrumb JSON-LD 改用 safeJsonLdString |
tests/json-ld.test.ts |
增加 INV-FE-001 回归测试覆盖 XSS payload 与往返保真 |
docs/SECURITY_INVARIANTS.md |
登记前端安全不变量 INV-FE-001 与检查/测试策略 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
2026-05-07 三方代码 review(backend / frontend / ChatBot)发现一条串成一条的 P0 攻击链。前端这一环是个人主页 JSON-LD 把用户 bio 直接
JSON.stringify嵌进<script type=\"application/ld+json\">——JSON.stringify默认不转义<,攻击者填一段</script><script>...</script>就触发存储型 XSS,配合 satoken 存 localStorage 等于完整账户接管。后端侧的修复见 InvolutionHell/involutionhell-backend#26(INV-001 ~ INV-005)。本 PR 处理前端这一环:INV-FE-001。
改动
新增
lib/json-ld.ts把会闭合 script 块的字符替换成字面 6 字符 \uXXXX 序列。
JSON.parse仍能正确还原;浏览器 HTML 解析器看不到<不会闭合 script。迁移点
app/[locale]/u/[username]/page.tsxapp/[locale]/docs/[...slug]/page.tsxapp/layout.tsx文档
docs/SECURITY_INVARIANTS.md登记 INV-FE-001(与公开的frontend/SECURITY.md漏洞披露政策不冲突,是给维护者看的内部不变量清单)。后端 SECURITY.md 用 INV-001/002/...,前端用 INV-FE-001/... 编号空间互不重叠。
测试
4 条覆盖:
</script><script>...</script>不再出现在输出里JSON.parse能还原)<>&都被转义为\\u003c等pre-commit hook 全套 23 测试通过、prettier 格式化、lockfile / pnpm 版本校验通过。
Test plan
pnpm vitest run tests/json-ld.test.ts4 / 4pnpm tsc --noEmit通过</script><script>alert(1)</script>,访问 profile 页确认 alert 不弹(输出里看到的是字面 \u003c 转义)