Skip to content
Permalink
Browse files Browse the repository at this point in the history
Merge pull request from GHSA-qvxg-wjxc-r4gg
* Add test for crash

* The sensible option

* Crude fix

* Throw an error if we hit a nesting limit

* Catch error in test
  • Loading branch information
0xTim committed Jun 6, 2022
1 parent d2d1910 commit 6c63226
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 4 deletions.
8 changes: 6 additions & 2 deletions Sources/Vapor/URLEncodedForm/URLEncodedFormData.swift
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}
}
3 changes: 3 additions & 0 deletions Sources/Vapor/URLEncodedForm/URLEncodedFormError.swift
@@ -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 {
Expand All @@ -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"
}
}
}
4 changes: 2 additions & 2 deletions Sources/Vapor/URLEncodedForm/URLEncodedFormParser.swift
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions Tests/VaporTests/URLEncodedFormTests.swift
Expand Up @@ -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 {
Expand Down

0 comments on commit 6c63226

Please sign in to comment.