Skip to content

Linter Rule: Disallow unescaped HTML entities

Rule: html-no-unescaped-entities

Description

Disallow unescaped special characters in HTML attribute values and text content. Characters like <, >, and & have special meaning in HTML and should be replaced with their corresponding character references when used as literal values.

This rule checks both attribute values and text content for unescaped <, >, and & characters. ERB output tags are ignored, as the escaping responsibility belongs to the Ruby layer. Only the static (literal) portions of mixed attribute values are checked.

Content inside raw text elements (<script> and <style>) is not checked, as character references are not decoded in those contexts per the HTML spec. Content inside escapable raw text elements (<textarea> and <title>) is still checked.

The & detection validates named references against the full HTML spec character reference list, so &amp;, &lt;, &copy;, etc. are recognized as valid, while &notavalidname; would be flagged.

Rationale

Unescaped special characters in HTML can cause parsing ambiguities, rendering issues, and security vulnerabilities. Browsers may interpret unescaped < and > as tag boundaries, leading to broken layouts or unintended element creation. Unescaped & characters can be misinterpreted as the start of a character reference, producing unexpected characters in the output.

Using proper character references ensures that the document is parsed correctly and consistently across all browsers.

CharacterEntityContext
<&lt;Attribute values and text content
>&gt;Attribute values and text content
&&amp;Attribute values and text content

Autofix

This rule provides an unsafe autofix that replaces unescaped characters with their corresponding character references. The autofix is considered unsafe because replacing characters may change the intended behavior in some contexts (e.g., URLs with query parameters).

Examples

✅ Good

html
<div class="hello"></div>
html
<div data-html="&lt;br&gt;"></div>
html
<a href="/path?a=1&amp;b=2">Link</a>
html
<div data-char="&#60;&#62;"></div>
html
<div data-char="&#x3C;&#x3E;"></div>
html
<div data-html="&lt;div class=&quot;test&quot;&gt;content&lt;/div&gt;"></div>
erb
<div class="<%= value %>"></div>
erb
<div data-value="prefix-<%= value %>-suffix"></div>
html
<div>Tom &amp; Jerry</div>
html
<script>var x = a & b;</script>
Missing a `nonce` attribute on `<script>` tag. Use `request.content_security_policy_nonce`. (html-require-script-nonce)
html
<style>.foo { content: "a & b"; }</style>

🚫 Bad

html
<div data-html="<br>"></div>
Attribute `data-html` contains an unescaped `>` character. Use `&gt;` instead. (html-no-unescaped-entities)
Attribute `data-html` contains an unescaped `<` character. Use `&lt;` instead. (html-no-unescaped-entities)
html
<div data-expr="a > b"></div>
Attribute `data-expr` contains an unescaped `>` character. Use `&gt;` instead. (html-no-unescaped-entities)
html
<a href="/path?a=1&b=2">Link</a>
Attribute `href` contains an unescaped `&` character. Use `&amp;` instead. (html-no-unescaped-entities)
html
<div data-html="<b>bold & italic</b>"></div>
Attribute `data-html` contains an unescaped `&` character. Use `&amp;` instead. (html-no-unescaped-entities)
Attribute `data-html` contains an unescaped `>` character. Use `&gt;` instead. (html-no-unescaped-entities)
Attribute `data-html` contains an unescaped `>` character. Use `&gt;` instead. (html-no-unescaped-entities)
Attribute `data-html` contains an unescaped `<` character. Use `&lt;` instead. (html-no-unescaped-entities)
Attribute `data-html` contains an unescaped `<` character. Use `&lt;` instead. (html-no-unescaped-entities)
html
<div>Tom & Jerry</div>
Text content contains an unescaped `&` character. Use `&amp;` instead. (html-no-unescaped-entities)
html
<p>Hello this is < content</p>
Text content contains an unescaped `<` character. Use `&lt;` instead. (html-no-unescaped-entities)
html
<p>a > b</p>
Text content contains an unescaped `>` character. Use `&gt;` instead. (html-no-unescaped-entities)

References

Released under the MIT License.