CVE-2026-54498 Overview
CVE-2026-54498 is a cross-site scripting (XSS) vulnerability in view_component, a framework for building reusable, testable, and encapsulated view components in Ruby on Rails. The flaw affects versions 4.0.0 through versions prior to 4.12.0. The ViewComponent::Base#around_render method can return HTML-unsafe strings that bypass the escaping applied to normal #call return values. ViewComponent::Collection#render_in amplifies the issue by joining per-item results and marking the entire output html_safe, converting raw unsafe output into an ActiveSupport::SafeBuffer. The maintainers fixed the issue in version 4.12.0.
Critical Impact
Attackers can inject arbitrary HTML or JavaScript into rendered pages when applications use around_render to wrap content that includes user-controlled data, enabling session theft, credential capture, and account takeover.
Affected Products
- ViewComponent gem versions 4.0.0 through 4.11.x
- Ruby on Rails applications using ViewComponent::Base#around_render
- Applications using ViewComponent::Collection#render_in with unsafe wrapping logic
Discovery Timeline
- 2026-07-17 - CVE-2026-54498 published to NVD
- 2026-07-23 - Last updated in NVD database
Technical Details for CVE-2026-54498
Vulnerability Analysis
The vulnerability stems from an output-safety gap in the ViewComponent rendering pipeline [CWE-79]. Normal #call return values pass through Rails' HTML escaping logic, which converts user-controlled data into safe HTML entities. The around_render hook, however, allowed applications to substitute the rendered string with an arbitrary return value that bypassed this escaping step.
Downstream applications commonly use around_render to wrap, replace, instrument, or conditionally return rendered content. When developers concatenated user-controlled data with the inner rendered template inside this hook, the resulting string was returned verbatim without HTML escaping. ViewComponent::Collection#render_in compounded the risk by calling .join(...).html_safe on the aggregated output, promoting attacker-controlled bytes to an ActiveSupport::SafeBuffer that Rails renders directly to the client.
Root Cause
The root cause is inconsistent HTML-safety enforcement between #call and around_render return paths in lib/view_component/base.rb. The framework treated the block return value as authoritative without verifying that the string returned from around_render retained the html_safe? marker. In lib/view_component/collection.rb, the use of String#join followed by html_safe discarded per-item safety information and blanket-marked the joined result as safe.
Attack Vector
Exploitation requires an application that uses around_render to compose output containing user-controlled data, such as request parameters, database fields sourced from other users, or headers. An attacker submits a payload containing HTML or JavaScript to a form field, URL parameter, or API endpoint whose value is later rendered inside a component wrapped by around_render. Because the escape step is skipped, the browser interprets the payload as active markup.
value = nil
@output_buffer.with_buffer do
- rendered_template =
- around_render do
- render_template_for(@__vc_requested_details).to_s
- end
+ inner_rendered_template = nil
+ around_rendered_template = around_render do
+ inner_rendered_template = render_template_for(@__vc_requested_details).to_s
+ end
+
+ # If `around_render` returned the same object the block yielded, the inner
+ # template's escaping is authoritative and we can trust the result. If the
+ # user replaced/wrapped the value, re-check HTML safety to prevent
+ # bypassing the escaping applied to normal `#call` return values
+ # (GHSA-97jw-64cj-jc58).
+ rendered_template = if around_rendered_template.equal?(inner_rendered_template)
+ around_rendered_template
+ else
+ __vc_safe_around_render_output(around_rendered_template)
+ end
Source: GitHub Commit 48e5fd2
The patch introduces __vc_safe_around_render_output to re-check HTML safety whenever the returned object differs from the yielded inner template. The collection fix replaces join(...).html_safe with safe_join, which escapes non-safe strings before concatenation:
def render_in(view_context, **_, &block)
- components.map do |component|
+ rendered = components.map do |component|
component.render_in(view_context, &block)
- end.join(rendered_spacer(view_context)).html_safe
+ end
+ safe_join(rendered, rendered_spacer(view_context))
end
Source: GitHub Commit 48e5fd2
Detection Methods for CVE-2026-54498
Indicators of Compromise
- Unexpected <script> tags, on*= event handlers, or javascript: URIs appearing in server-rendered HTML responses from ViewComponent-based routes
- Application logs showing HTML metacharacters (<, >, ", ') in request parameters that reach controllers rendering components with around_render hooks
- Outbound requests from client browsers to unfamiliar domains immediately after loading pages that render user-generated content through ViewComponent
Detection Strategies
- Perform a code audit of every around_render implementation in the application and verify that any string interpolation uses html_safe-aware helpers or explicitly escapes user input
- Scan the Gemfile.lock across all repositories for view_component versions between 4.0.0 and 4.11.x
- Enable a Content Security Policy (CSP) in report-only mode to surface inline script executions that indicate injection attempts
Monitoring Recommendations
- Monitor web application firewall (WAF) logs for XSS payload signatures targeting endpoints backed by ViewComponent renderers
- Alert on CSP violation reports containing script-src or inline violations originating from authenticated user sessions
- Track browser telemetry for unexpected DOM mutations or script execution on pages rendered by component collections
How to Mitigate CVE-2026-54498
Immediate Actions Required
- Upgrade the view_component gem to version 4.12.0 or later in all Rails applications
- Audit every custom around_render implementation and confirm that returned values are either html_safe by construction or explicitly escaped with ERB::Util.html_escape
- Review any code that calls .html_safe on aggregated component output and replace it with safe_join
Patch Information
The fix is available in ViewComponent v4.12.0. The patch adds __vc_safe_around_render_output to re-validate HTML safety when around_render replaces the block's return value, and replaces the unsafe join(...).html_safe in ViewComponent::Collection#render_in with safe_join from ActionView::Helpers::OutputSafetyHelper. Full technical details are available in the GHSA-97jw-64cj-jc58 advisory.
Workarounds
- Remove or refactor around_render hooks that interpolate user-controlled data until the upgrade is deployed
- Wrap any user input inside around_render with ERB::Util.html_escape(value) before concatenation with the inner rendered template
- Apply a strict Content-Security-Policy header that disallows inline scripts and untrusted sources to reduce the impact of successful injection
# Update Gemfile constraint
bundle update view_component --conservative
# Verify installed version
bundle info view_component | grep -i version
# Confirm no vulnerable versions remain
grep -R "view_component" Gemfile.lock | grep -E "4\.(0|1|2|3|4|5|6|7|8|9|10|11)\."
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

