NIST deprecates RSA-2048 from 2030. Find every one of them before attackers do.
💎
💎 RubyGems
Not in CISA KEV
MEDIUM severity

Rack: Rack::Static header_rules bypass via dot-segment normalization mismatchGHSA-wvg4-6p58-3gh3

MEDIUMFix: rack/rack@18a07fe

GHSA-wvg4-6p58-3gh3 is a medium-severity (CVSS 5.3) CWE-180 vulnerability in rack. A fix is available for rack — see the affected versions and patch details below.

Also known asCVE-2026-125110
Published
Updated
Affected
3 pkgs
Patched
3 / 3
Exploits
None indexed
Exploitation data as of Nov 10, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

EPSS Exploitation Probability

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

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

Where this sits among everything scored

Real-World Exposure

3 pkgs affected
💎rack💎rack💎rack

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

Rack::Static normalizes the request path with Utils.clean_path_info when deciding which file to serve, but matches header_rules against a path that is only URL-decoded. A request containing dot segments, raw or percent-encoded, is served the normalized target file while the header rules for that file's location are not applied.

This allows an attacker to receive a protected static file without the security headers configured for it.

Details

Rack::Static#call computes the served path as Utils.clean_path_info(Utils.unescape_path(path)). Rack::Static#applicable_rules applies only Utils.unescape_path before testing string and regular expression rules. A request for /assets/../uploads/file.html or /assets/..%2fuploads/file.html is served as uploads/file.html, but the decoded path /assets/../uploads/file.html does not start with uploads, so a rule scoped to that directory never matches.

The fix for CVE-2026-34786 (GHSA-q4qf-9j86-f5mh) added URL decoding to rule matching but did not add dot segment normalization, so versions that include that fix remain affected.

Impact

Applications that use Rack::Static header_rules to attach security-relevant headers to particular paths or file types are affected. An unauthenticated attacker who knows the path of a protected file can request it through a dot segment path and receive it without those headers, for example without Content-Disposition: attachment or a restrictive Content-Security-Policy that prevents uploaded HTML from rendering inline. The file itself is served unchanged. Applications that do not use path-specific header_rules are not affected.

Mitigation

  • Update to a patched version of Rack in which header_rules are matched against the same normalized path used to serve the file.
  • Reject requests containing dot segments at a reverse proxy or application boundary.
  • Do not rely solely on Rack::Static header_rules to prevent inline rendering of untrusted uploads; serve them from a separate origin.

Credit

  • Reporter and Remediation developer

Affected Packages

3 total 3 fixed
EcosystemPackageVulnerable rangeFix
💎RubyGemsrackall versions2.2.25bundle update rack --conservative
💎RubyGemsrack≥ 3.0.0&&< 3.1.233.1.23bundle update rack --conservative
💎RubyGemsrack≥ 3.2.0&&< 3.2.83.2.8bundle update rack --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 rack, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update rack to 2.2.25 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-wvg4-6p58-3gh3 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.

Frequently Asked Questions

## Summary `Rack::Static` normalizes the request path with `Utils.clean_path_info` when deciding which file to serve, but matches `header_rules` against a path that is only URL-decoded. A request containing dot segments, raw or percent-encoded, is served the normalized target file while the header rules for that file's location are not applied. This allows an attacker to receive a protected static file without the security headers configured for it. ## Details `Rack::Static#call` computes the served path as `Utils.clean_path_info(Utils.unescape_path(path))`. `Rack::Static#applicable_rules`
O3 Security · Impact-Aware SCA

Is GHSA-wvg4-6p58-3gh3 in your dependencies?

Find it across RubyGems, including transitive dependencies.

Rack: Rack::Static header_rules bypass via dot-segment…