File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -65,6 +65,7 @@ import { UserIdentityBadges } from "~/components/UserIdentityBadges";
6565import { externalUrlLabel , githubProfileUrl , safeExternalUrl } from "~/lib/public-profile" ;
6666import { getTopicActionPresentation } from "~/lib/topic-action-presentation" ;
6767import type { TopicReplyDTO } from "~/lib/api-types" ;
68+ import { useCspNonce } from "~/lib/csp-nonce" ;
6869import {
6970 Empty ,
7071 EmptyContent ,
@@ -201,10 +202,12 @@ export default function TopicDetail({ loaderData }: Route.ComponentProps) {
201202 const headings = extractMarkdownHeadings ( topic . content || "" ) . slice ( 0 , 12 ) ;
202203 const toc = headings . length >= 4 ? < TopicToc headings = { headings } /> : null ;
203204 const jsonLd = discussionForumPostingJsonLd ( topic ) ;
205+ const nonce = useCspNonce ( ) ;
204206
205207 return (
206208 < Layout >
207209 < script
210+ nonce = { nonce }
208211 type = "application/ld+json"
209212 dangerouslySetInnerHTML = { { __html : JSON . stringify ( jsonLd ) . replace ( / < / g, "\\u003c" ) } }
210213 />
Original file line number Diff line number Diff line change @@ -120,5 +120,8 @@ describe("Web document security headers", () => {
120120 expect ( rootSource ) . toContain ( "<ScrollRestoration nonce={nonce} />" ) ;
121121 expect ( rootSource ) . toContain ( "<Scripts nonce={nonce} />" ) ;
122122 expect ( rootSource . match ( / < s c r i p t \s + n o n c e = \{ n o n c e \} / g) ) . toHaveLength ( 2 ) ;
123+
124+ const topicSource = readFileSync ( resolve ( import . meta. dirname , "../app/routes/topic.$tid.tsx" ) , "utf8" ) ;
125+ expect ( topicSource ) . toContain ( '<script\n nonce={nonce}\n type="application/ld+json"' ) ;
123126 } ) ;
124127} ) ;
Original file line number Diff line number Diff line change 1111- [x] 2.1 实现同源 CSP 报告资源路由,仅接受规定内容类型和受限请求体,解析兼容的报告 envelope 并返回无正文响应。
1212- [x] 2.2 对 CSP 报告使用字段白名单和 URL 脱敏,只记录 directive、去除查询参数与片段的来源摘要、同源文档路径和策略阶段;拒绝记录原始正文、Cookie、请求头或代码片段。
1313- [x] 2.3 为报告入口接入适用的匿名限流边界,并添加有效报告、格式错误、内容类型错误、超限、跨源 URL 和敏感查询参数测试。
14- - [ ] 2.4 在候选 CSP 中配置报告目标,并通过浏览器 smoke test 验证首页、登录、话题详情、管理入口和 Turnstile 相关页面在 Report-Only 下可渲染、hydration、导航、请求 API 和加载当前头像来源。
14+ - [x ] 2.4 在候选 CSP 中配置报告目标,并通过浏览器 smoke test 验证首页、登录、话题详情、管理入口和 Turnstile 相关页面在 Report-Only 下可渲染、hydration、导航、请求 API 和加载当前头像来源。
1515
1616## 3. Feature-Complete:部署与强制切换
1717
18- - [ ] 3.1 核实生产 Caddy 和其他入口的现有 header 配置,为 CSP、HSTS 及其他安全头指定唯一所有者,并确认最终客户端响应不存在冲突、重复或策略放宽。
18+ - [x ] 3.1 核实生产 Caddy 和其他入口的现有 header 配置,为 CSP、HSTS 及其他安全头指定唯一所有者,并确认最终客户端响应不存在冲突、重复或策略放宽。
1919- [x] 3.2 增加无需重建镜像即可切换 CSP 阶段和 HSTS ` max-age ` 的安全运行时配置,校验未知值时采用安全、可用且可观测的默认行为。
2020- [ ] 3.3 定义并执行 Report-Only 观测门槛:约定窗口内无未解释第一方违规、关键浏览器测试通过、必要来源均有评审记录,然后切换强制 CSP。
2121- [x] 3.4 为发布后探测增加首页、交互页面和 HTML 错误响应检查,确认预期 CSP 阶段、HSTS、安全头、hydration 和资源加载,且探测输出不包含 Cookie、凭据或报告原文。
You can’t perform that action at this time.
0 commit comments