Skip to content

Commit 69651a5

Browse files
justin808claude
andcommitted
Add tests and security documentation for CSP nonce support
- Add TypeScript tests for: - buildConsoleReplay() with nonce parameter - consoleReplay() returns JS without script tags - Empty string handling with nonce - Add security comments explaining: - Why html_safe is safe (content pre-sanitized via scriptSanitizedVal) - CSP nonce availability (Rails 5.2+) - Add blank line before wrap_console_script_with_nonce method for consistency All tests passing (100 passed in react-on-rails, 30 passed in pro). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 059a19e commit 69651a5

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

lib/react_on_rails/helper.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,16 +437,20 @@ def build_react_component_result_for_server_rendered_hash(
437437
)
438438
end
439439

440+
# Wraps console replay JavaScript code in a script tag with CSP nonce if available.
441+
# The console_script_code is already sanitized by scriptSanitizedVal() in the JavaScript layer,
442+
# so using html_safe here is secure.
440443
def wrap_console_script_with_nonce(console_script_code)
441444
return "" if console_script_code.blank?
442445

443-
# Get the CSP nonce if available
446+
# Get the CSP nonce if available (Rails 5.2+)
444447
nonce = content_security_policy_nonce(:script) if respond_to?(:content_security_policy_nonce)
445448

446449
# Build the script tag with nonce if available
447450
script_options = { id: "consoleReplayLog" }
448451
script_options[:nonce] = nonce if nonce.present?
449452

453+
# Safe to use html_safe because content is pre-sanitized via scriptSanitizedVal()
450454
content_tag(:script, console_script_code.html_safe, script_options)
451455
end
452456

packages/react-on-rails/tests/buildConsoleReplay.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,36 @@ console.warn.apply(console, ["other message","{\\"c\\":3,\\"d\\":4}"]);
7575

7676
expect(actual).toEqual(expected);
7777
});
78+
79+
it('buildConsoleReplay adds nonce attribute when provided', () => {
80+
console.history = [{ arguments: ['test message'], level: 'log' }];
81+
const actual = buildConsoleReplay(undefined, 0, 'abc123');
82+
83+
expect(actual).toContain('nonce="abc123"');
84+
expect(actual).toContain('<script id="consoleReplayLog" nonce="abc123">');
85+
expect(actual).toContain('console.log.apply(console, ["test message"]);');
86+
});
87+
88+
it('buildConsoleReplay returns empty string when no console messages', () => {
89+
console.history = [];
90+
const actual = buildConsoleReplay(undefined, 0, 'abc123');
91+
92+
expect(actual).toEqual('');
93+
});
94+
95+
it('consoleReplay returns only JavaScript without script tags', () => {
96+
console.history = [
97+
{ arguments: ['message 1'], level: 'log' },
98+
{ arguments: ['message 2'], level: 'error' },
99+
];
100+
const actual = consoleReplay();
101+
102+
// Should not contain script tags
103+
expect(actual).not.toContain('<script');
104+
expect(actual).not.toContain('</script>');
105+
106+
// Should contain the JavaScript code
107+
expect(actual).toContain('console.log.apply(console, ["message 1"]);');
108+
expect(actual).toContain('console.error.apply(console, ["message 2"]);');
109+
});
78110
});

0 commit comments

Comments
 (0)