Linter Rule: Require nonce attribute on inline scripts
Rule: html-require-script-nonce
Description
Require a nonce attribute on inline <script> tags and script-producing Rails helpers. This helps enforce a Content Security Policy (CSP) that mitigates cross-site scripting (XSS) attacks.
External scripts are not flagged. That covers any element with a src attribute, including the ones generated by javascript_include_tag.
Rationale
A Content Security Policy with a nonce-based approach ensures that only scripts with a valid, server-generated nonce are executed by the browser. Without a nonce, inline scripts may be blocked by CSP, or worse, CSP may need to be relaxed with unsafe-inline, defeating its purpose. External scripts are instead controlled by the CSP script-src source list.
Adding nonces to inline scripts ensures:
- Scripts are allowed by the CSP without weakening it
- Protection against XSS attacks that attempt to inject unauthorized scripts
- Consistent security practices across the codebase
Examples
✅ Good
HTML script tags with a nonce:
<script nonce="<%= request.content_security_policy_nonce %>">
alert("Hello, world!")
</script><script type="text/javascript" nonce="<%= request.content_security_policy_nonce %>">
console.log("Hello")
</script>Rails helpers with nonce: true:
<%= javascript_tag nonce: true do %> alert("Hello, world!")
<% end %>Unlike javascript_tag, tag.script does not resolve nonce: true to the request's CSP nonce. Pass the nonce value explicitly:
<%= tag.script nonce: request.content_security_policy_nonce do %>
alert("Hello, world!")
<% end %>External scripts (not flagged):
<script src="/assets/application.js"></script><%= javascript_include_tag "application" %>Non-JavaScript script types (not flagged):
<script type="application/json"> {"key": "value"}
</script><script type="application/ld+json">
{"@context": "https://schema.org"}
</script>🚫 Bad
HTML script tags without a nonce:
<script> alert("Hello, world!")
</script><script type="text/javascript"> console.log("Hello")
</script>Inline Rails helpers without a nonce:
<%= javascript_tag do %> alert("Hello, world!")
<% end %><%= tag.script do %> alert("Hello, world!")
<% end %>Rails tag helpers with a literal boolean nonce:
<%= tag.script nonce: true do %> alert("Hello, world!")
<% end %>Framework-specific recommendations
The offense for a missing nonce names request.content_security_policy_nonce only when the project opts into Action View in its .herb.yml:
framework: actionviewUnder any other framework, and when no framework is configured, the rule recommends a dynamically generated nonce without naming a Rails API.