Finds unescaped output in Rails ERB templates - raw, html_safe, <%==, script and href context - and names the escaping helper that fixes each line. Runs entirely in your browser — nothing is uploaded.
Same engine as the VS Code extension, byte for byte.
Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.
VS Code MarketplacenpmMCP server (npx)Docker Hubext install ReadyStack.erb-escape-audit npx @readystack/erb-escape-audit <file> npx @readystack/erb-escape-audit --mcp
Team? One key for every ReadyStack linter, 5 seats, $149 once
Sweep every .erb file in the workspace in one run and write a dated Markdown/CSV report you keep as the evidence artefact for a release note or a vulnerability-handling record.
One payment, one licence key for this tool. The key is shown right after payment.
A freelance Rails developer doing the same template review by hand bills $75-$150 an hour.
Buy the full version — $29· ReadyStack
Real numbers from this tool, line by line.
Fifteen unescaped-output lines in a thirty-two-line Rails product page: that is what a Rails developer gets back the first time they run ERB Escape Audit over a view an assistant helped write. Four of them critical, seven high, four medium.
The awkward part is that ERB is already safe: <%= value %> HTML-escapes what it prints, and nobody has to remember anything. So every XSS hole in a Rails view is a line where somebody turned the escaping off, or a line where HTML escaping was never the escaping that line needed.
There are only three ways to turn it off, and they all look reasonable in review. <%= raw @product.description_html %> is what you write when the admin stores rich text and the page renders <p> instead of a paragraph. <%= @product.blurb.html_safe %> is the same move spelled differently, and <%== @announcement.body %> is the same move again with nothing left to grep for. All three render correctly, which is why they survive a pull request.
The second family is harder, because nothing was switched off - the value was escaped for the wrong context. var name = "<%= @user.name %>"; inside a <script> block is HTML-escaped and still broken: a quote or a literal </script> in that name ends the string, or the tag. var cfg = <%= @config.to_json %>; fails the same way, because to_json leaves </script> intact. <a href="<%= @product.homepage %>"> is escaped and still clickable as javascript:alert(1) if the column holds that, and <img src=<%= @product.image_url %>> is escaped and still lets a value containing a space add onerror=.
This is the category linters miss, and the reason is structural: a Ruby linter parses Ruby. It sees the ERB tag, not the HTML wrapped around it - and the HTML is what decides whether the value needs j(), json_escape, a quoted attribute or a scheme check.
ERB Escape Audit reads the template as markup and as Ruby at the same time. Fifteen rules: the three ways to switch escaping off, render inline: (which is template injection rather than XSS), interpolation inside <script> and inside <style>, href and on* attributes, unquoted attributes, content_tag with its fourth argument set to false, sanitize() with no tags: allowlist, simple_format(..., sanitize: false), and a Content-Security-Policy meta tag carrying unsafe-inline. Every finding comes back with a line number, a severity, and the helper that makes the line safe: not "this is unsafe" but "wrap it in j()".
The fixtures ship with the extension, so the numbers are checkable. _fixtures/dirty.erb is a plausible product page in thirty-two lines and returns fifteen findings; _fixtures/clean.erb is the same page rewritten with sanitize(tags:), route helpers, j(), json_escape and a CSP nonce, and returns zero. Same engine in the editor and on the free web page.
There is also a date on this now. Since 11 September 2026 the EU Cyber Resilience Act has made an actively exploited vulnerability in a product with digital elements a reporting event: an early warning within 24 hours, a full notification within 72 hours, a final report within 14 days. A stored XSS in a customer-facing view is that kind of finding, and the clock starts when somebody outside your team notices it.
Auditing the file you have open is free and finished: every finding, every line, every fix, no cap, no watermark. The licensed half is a different axis - scope and ownership - sweeping every .erb file in the workspace and writing a dated report you keep. A freelance Rails developer doing that review by hand bills $75-$150 an hour, and a mid-sized app has hundreds of these files.
It reads Rails .erb templates and reports every line where HTML escaping was switched off or was the wrong escaping for the context: raw, .html_safe, the <%== tag, render inline:, interpolation inside script or style blocks, href and on-event attributes, unquoted attributes, content_tag with escape false, and sanitize without an allowlist. Fifteen rules, each naming the helper that fixes the line.
Rails developers who ship server-rendered views, and the person who reviews those views before a release. It is most useful on codebases where an AI assistant writes template code, because assistants reach for raw and html_safe to make markup render and the result looks correct in the browser either way.
Ruby linters parse Ruby, so they see the ERB tag but not the HTML around it. Whether a value needs j, json_escape, a quoted attribute or a scheme check depends on that surrounding markup. Static scanners that do know Rails flag raw and html_safe but not an unquoted src attribute or interpolation inside a style block.
Auditing the file you have open is free and complete: every finding, every line number, every fix, no cap and no watermark. The licence covers a different axis, scope and ownership. It sweeps every .erb file in the workspace in one run and writes a dated Markdown or CSV report you keep as evidence.
A freelance Rails developer doing the same template review bills $75 to $150 an hour on the open market, and a mid-sized app has hundreds of .erb files. The extension is $29 once, one licence key per person or CI seat,.
A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. ERB Escape Audit - Rails XSS lint checks the file you open against 15 rules from a rule set dated 2026-09-20, and points at the exact line. For a filing, an audit or a client you need that dated result on your own files.
One question, answered by the person who built it. Your email only if you want the answer sent.
Paste this code into your page. It runs in your visitor’s browser and stays up to date. Nothing your visitors type is stored.
For businesses: your name and logo instead of ours, and each visitor’s result + e-mail in your inbox (only when they tick consent) — Pro from $19/month. See Pro →