DoS Vulnerability in URLEncodedFormDecoder in Vapor
Description
Vapor 4.61.0 and earlier have an unbounded stack growth vulnerability in URL-encoded form decoding, allowing remote DoS via crafted request.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Vapor 4.61.0 and earlier have an unbounded stack growth vulnerability in URL-encoded form decoding, allowing remote DoS via crafted request.
CVE-2022-31019 is a denial-of-service (DoS) vulnerability in the Vapor server-side Swift HTTP web framework, affecting versions prior to 4.61.1. The bug resides in the automatic content decoding feature, specifically within the URLEncodedFormData struct's recursive set(value:forPath:) method. When parsing deeply nested query parameters from an application/x-www-form-urlencoded request body, no recursion limit was enforced, leading to unbounded, attacker-controlled stack growth [1][2].
An attacker can exploit this by sending a specially crafted HTTP POST request, such as: curl -d "array[_0][0][array][_0][0][array]$(for f in $(seq 1100); do echo -n '[_0][0][array]'; done)[string][_0]=hello%20world" http://localhost:8080/foo. This causes the server to recursively process the deeply nested keys, consuming stack space until a stack overflow occurs, crashing the process [1][3]. No authentication is required, and the attack is remotely exploitable over the network.
The impact is a complete denial of service: the Vapor server process terminates unexpectedly, affecting all hosted services. There is no data exposure, but availability is severely compromised. The vulnerability has a CVSS v3.1 base score of 7.5 (High) [1].
Vapor 4.61.1, released on the same day as the advisory, fixes the issue by introducing a configurable maxRecursionDepth constant (set to 100) in the URLEncodedFormData struct, and modifying the recursive set method to throw a reachedNestingLimit error when the depth is exceeded [2][3]. As a workaround, administrators can disable URL-encoded form decoding entirely by configuring ContentConfiguration to use only JSON or other serializers [3].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/vapor/vaporSwiftURL | < 4.61.1 | 4.61.1 |
Affected products
2Patches
16c63226a4ab8Merge pull request from GHSA-qvxg-wjxc-r4gg
4 files changed · +20 −4
Sources/Vapor/URLEncodedForm/URLEncodedFormData.swift+6 −2 modified@@ -54,6 +54,7 @@ enum URLQueryFragment: ExpressibleByStringLiteral, Equatable { internal struct URLEncodedFormData: ExpressibleByArrayLiteral, ExpressibleByStringLiteral, ExpressibleByDictionaryLiteral, Equatable { var values: [URLQueryFragment] var children: [String: URLEncodedFormData] + let maxRecursionDepth = 100 var hasOnlyValues: Bool { return children.count == 0 @@ -90,7 +91,10 @@ internal struct URLEncodedFormData: ExpressibleByArrayLiteral, ExpressibleByStri self.children = Dictionary(uniqueKeysWithValues: dictionaryLiteral) } - mutating func set(value: URLQueryFragment, forPath path: [String]) { + mutating func set(value: URLQueryFragment, forPath path: [String], recursionDepth: Int) throws { + guard recursionDepth <= maxRecursionDepth else { + throw URLEncodedFormError.reachedNestingLimit + } guard let firstElement = path.first else { self.values.append(value) return @@ -101,7 +105,7 @@ internal struct URLEncodedFormData: ExpressibleByArrayLiteral, ExpressibleByStri } else { child = [] } - child.set(value: value, forPath: Array(path[1...])) + try child.set(value: value, forPath: Array(path[1...]), recursionDepth: recursionDepth + 1) self.children[firstElement] = child } }
Sources/Vapor/URLEncodedForm/URLEncodedFormError.swift+3 −0 modified@@ -1,6 +1,7 @@ /// Errors thrown while encoding/decoding `application/x-www-form-urlencoded` data. enum URLEncodedFormError: Error { case malformedKey(key: Substring) + case reachedNestingLimit } extension URLEncodedFormError: AbortError { @@ -12,6 +13,8 @@ extension URLEncodedFormError: AbortError { switch self { case .malformedKey(let path): return "Malformed form-urlencoded key encountered: \(path)" + case .reachedNestingLimit: + return "The data supplied is too nested" } } }
Sources/Vapor/URLEncodedForm/URLEncodedFormParser.swift+2 −2 modified@@ -14,11 +14,11 @@ internal struct URLEncodedFormParser { switch kv.count { case 1: let value = String(kv[0]) - result.set(value: .urlEncoded(value), forPath: []) + try result.set(value: .urlEncoded(value), forPath: [], recursionDepth: 0) case 2: let key = kv[0] let value = String(kv[1]) - result.set(value: .urlEncoded(value), forPath: try parseKey(key: Substring(key))) + try result.set(value: .urlEncoded(value), forPath: try parseKey(key: Substring(key)), recursionDepth: 0) default: //Empty `&&` continue
Tests/VaporTests/URLEncodedFormTests.swift+9 −0 modified@@ -574,6 +574,15 @@ final class URLEncodedFormTests: XCTestCase { ]) XCTAssertEqual(data, "test=%26%3B!$'(),/:%3D%3F@~") } + + func testHeavilyNestedArray() throws { + var body = "x" + body += String(repeating: "[]", count: 80000) + body += "=y" + struct Foo: Content {} + XCTAssertThrowsError(try URLEncodedFormDecoder().decode(Foo.self, from: body)) + XCTAssert(true, "We should not have crashed") + } } private struct User: Codable, Equatable {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-qvxg-wjxc-r4ggghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-31019ghsaADVISORY
- github.com/vapor/vapor/commit/6c63226a4ab82ce53730eb1afb9ca63866fcf033ghsax_refsource_MISCWEB
- github.com/vapor/vapor/security/advisories/GHSA-qvxg-wjxc-r4ggghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.