Class: RuboCop::Cop::Heatwave::SafeBufferInAttribute
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Heatwave::SafeBufferInAttribute
- Extended by:
- AutoCorrector
- Defined in:
- lib/rubocop/cop/heatwave/safe_buffer_in_attribute.rb
Overview
Flags <%= EXPR %> ERB interpolations that emit html_safe /
ActiveSupport::SafeBuffer content (fa_icon, tag.*, content_tag,
link_to, button_to, any *_tag helper, safe_join, safe_concat,
sanitize, simple_format, highlight, raw, render,
<expr>.html_safe, or a h(...) / html_escape(...) /
ERB::Util.html_escape(...) wrapper — these are no-ops on SafeBuffer
and don't actually fix the bug) inside an HTML attribute value when
the expression is not wrapped in html_escape_once(...).
Rails ERB's auto-escape is a no-op on SafeBuffer, so the inner "
characters from the helper's HTML markup leak through and close the
attribute prematurely. html_escape_once forces the escape while
leaving any already-encoded entities in the safe content alone, which
is what you want when the value will later be read out (e.g. via
dataset.foo for Turbo's data-turbo-submits-with swap).
The check is intentionally conservative — it only fires on a fixed
list of known-html_safe call patterns plus a .html_safe trailer, so
plain string interpolations like placeholder="<%= label %>" are not
flagged.
Constant Summary collapse
- MSG =
Msg.
'`html_safe` content interpolated into an HTML attribute value ' \ 'is not auto-escaped by Rails — wrap the expression in ' \ '`html_escape_once(...)` so the inner `"` characters do not ' \ 'close the attribute prematurely.'
- DOUBLE_QUOTED_ATTR =
An attribute (
name="…"orname='…') whose value contains at least
one<%= … %>interpolation. The surrounding quote char is excluded
from the value content so we don't accidentally span across
neighbouring attributes; the<%= … %>body is.*?so it can carry
the opposite quote (e.g.'spinner'inside a double-quoted attr). /\b([a-zA-Z_][\w:-]*)\s*=\s*"((?:[^"]*?<%=.*?%>)+[^"]*?)"/m- SINGLE_QUOTED_ATTR =
/\b([a-zA-Z_][\w:-]*)\s*=\s*'((?:[^']*?<%=.*?%>)+[^']*?)'/m- ERB_INTERPOLATION =
A single ERB output tag inside an attribute value. Capture group 1
is the trimmed expression, used both for the html-safe heuristic and
the autocorrect'shtml_escape_once(...)wrap. /<%=\s*(.+?)\s*%>/m- HTML_SAFE_HEAD =
Heuristic for "this expression returns html_safe / SafeBuffer".
Matches at the START of the expression so we don't false-positive on
helpers.fa_icon(already covered byRedundantHelpersFaIcon) or
arbitrary strings that happen to mention these names.h/html_escape/ERB::Util.html_escapeare intentionally
included: they preserve the SafeBuffer flag (no-op when input is
already html_safe) and therefore do not fix the attribute-quote-
collision bug. Wrappingh(fa_icon(...))inhtml_escape_once(...)
is the correct fix even though it looks redundant. /\A(?: fa_icon | tag (?: \. \w+ )? | content_tag | link_to | button_to | [a-z_]\w*_tag | safe_join | safe_concat | sanitize | simple_format | highlight | raw | render | h | html_escape | ERB::Util\.html_escape )\b/x- HTML_SAFE_TRAIL =
Trailing
.html_safeanywhere — coversfoo.html_safe,
bar.baz.html_safe,(stuff).html_safe. /\.html_safe\b/- ALREADY_ESCAPED =
Already wrapped in
html_escape_once(...)— skip. Note that
h(...)/ERB::Util.html_escape(...)are not treated as
"already escaped" — they're no-ops on SafeBuffer and don't fix the
bug, so they're flagged viaHTML_SAFE_HEADabove. /\Ahtml_escape_once\s*[(\s]/- SCRIPT_BLOCK =
Skip matches inside
<script>…</script>blocks — those are JS
string-literal contents, not real HTML attributes. %r{<script\b[^>]*>(.*?)</script>}im
Instance Method Summary collapse
-
#on_new_investigation ⇒ void
Invoked by RuboCop when investigating a Ruby-parseable file.
-
#on_other_file ⇒ void
Invoked by RuboCop when investigating a non-Ruby file (e.g. ERB).
Instance Method Details
#on_new_investigation ⇒ void
This method returns an undefined value.
Invoked by RuboCop when investigating a Ruby-parseable file.
100 101 102 |
# File 'lib/rubocop/cop/heatwave/safe_buffer_in_attribute.rb', line 100 def on_new_investigation scan_source end |
#on_other_file ⇒ void
This method returns an undefined value.
Invoked by RuboCop when investigating a non-Ruby file (e.g. ERB).
106 107 108 |
# File 'lib/rubocop/cop/heatwave/safe_buffer_in_attribute.rb', line 106 def on_other_file scan_source end |