Skip to main content

ViewComponent CVE-2026-44837

| EUVDEUVD-2026-31971 HIGH
Path Traversal (CWE-22)
2026-05-08 https://github.com/ViewComponent/view_component GHSA-hg3h-g7xc-f7vp
7.5
CVSS 3.1 · NVD
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Reach test-mode app route
  2. Delivery
    Send file param with ../sibling path
  3. Exploit
    realpath resolves outside temp dir
  4. Execution
    start_with? prefix check passes
  5. Persist
    Rails renders out-of-bounds file
  6. Impact
    Disclose 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.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.0% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv3 – 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)
v2 Jul 23, 2026 - 11:48 cvss changed CVSS 7.5

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.

v1 May 09, 2026 - 00:00 CVSS 5.9

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

view_component 3.0.0, < 4.9.0

Affected Packages

rubygems view_component fix: 4.9.0

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

NVD PRIMARY 7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
vuln.today AI 5.9 MEDIUM

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.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
4.0 AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

Primary rating from NVD.

CVSS VectorNVD

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

Lifecycle Timeline

5
Analysis Updated
Jul 23, 2026 - 11:49 vuln.today
v3 - CVSS changed
Analysis Updated
Jul 23, 2026 - 11:48 vuln.today
v2 - CVSS changed
Severity Changed
Jul 23, 2026 - 11:22 NVD
MEDIUM HIGH
CVSS changed
Jul 23, 2026 - 11:22 NVD
5.9 (MEDIUM) 7.5 (HIGH)
CVE Published
May 08, 2026 - 23:33 nvd
MEDIUM 5.9

DescriptionNVD

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

Executive ViewAI

Why It Matters

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.

Business Impact: Medium
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

EPSS Score
0.0%
Top 99% exploitable
Prevalence
Medium
popular Rails view library, but far from ubiquitous
Threat Intel
1
1 source
Data Quality
6 sources
Confidence: High

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.

SignalValue
Severity HIGH
CISA KEV No
Public POC No
EPSS 0.0%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
None
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact
Medium

Priority Score

38
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +38
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Path traversal exploits occur when applications use user-controlled input to construct file system paths without proper validation.

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy