Skip to content

Commit f5a4140

Browse files
justin808claude
andcommitted
Address PR #3934 review feedback on canary baseline and nonce escaping
- strict_csp.spec.ts: wait for the streamed content (header + both Suspense branches at level 0) to be fully delivered before asserting the zero-violation baseline in the enforcement-canary test. Previously the baseline ran right after the load event, when streamed chunks could still be in transit, making the pre-canary assertion vacuous. - application_controller.rb: HTML-escape the CSP nonce before interpolating it into the script tag attribute. Safe today with Rails' base64/session generators, but the raw interpolation was a copyable pattern that a custom nonce generator could break out of. The docs page already shows only the safe helper-based patterns (javascript_tag/javascript_include_tag with nonce: true), so no docs change is needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a65c757 commit f5a4140

2 files changed

Lines changed: 13 additions & 1 deletion

File tree

react_on_rails_pro/spec/dummy/app/controllers/application_controller.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ def stream_prerender_error_response
5252
# The strict CSP (config/initializers/content_security_policy.rb) blocks
5353
# inline scripts without the per-request nonce, so this late-streamed error
5454
# script must carry it. The meta refresh below stays as a no-JS fallback.
55+
# html_escape guards against a custom nonce generator emitting characters
56+
# (e.g. `"` or `>`) that could break out of the attribute; Rails' built-in
57+
# base64/session generators are already safe.
5558
nonce = content_security_policy_nonce
56-
nonce_attribute = nonce.present? ? %( nonce="#{nonce}") : ""
59+
nonce_attribute = nonce.present? ? %( nonce="#{ERB::Util.html_escape(nonce)}") : ""
5760
js_redirect = <<~JAVASCRIPT
5861
<script#{nonce_attribute}>
5962
document.getElementById('page-container').innerHTML = #{ActiveSupport::JSON.encode(error_message)};

react_on_rails_pro/spec/dummy/e2e-tests/strict_csp.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ test.describe('Strict CSP (script-src self + per-request nonce, no unsafe-inline
135135
test('the policy is enforced: a nonce-less inline script is blocked (canary)', async ({ page }) => {
136136
await page.goto(STREAMED_RSC_PAGE);
137137

138+
// Wait for the streamed content (including the late Suspense boundaries)
139+
// to be fully delivered before asserting the empty baseline — otherwise
140+
// chunks still in transit could make the pre-canary assertion vacuous.
141+
await expect(page.getByText('Header for AsyncComponentsTreeForTesting')).toBeVisible({
142+
timeout: 30000,
143+
});
144+
await expect(page.getByText('branch1 (level 0)')).toBeVisible({ timeout: 30000 });
145+
await expect(page.getByText('branch2 (level 0)')).toBeVisible({ timeout: 30000 });
146+
138147
// The page itself must be violation-free before the canary.
139148
expect(await getUnexpectedCspViolations(page)).toEqual([]);
140149

0 commit comments

Comments
 (0)