Skip to content

Commit ee07ddf

Browse files
Added tests that ensure redirects to unix socket paths from a regular HTTP server are disallowed.
Motivation: Currently, redirects to any supported URL scheme will always be allowed, despite code being in place to seemingly prevent it. See swift-server#230. Modifications: - Added a method to HTTPBin to redirect to the specified target. - Added failing tests that perform redirects from a regular server to a socket-based server and vice versa. Result: Failing tests that show that the existing redirect checks were inadequate.
1 parent 606ab0e commit ee07ddf

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift

+6
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,12 @@ internal final class HttpBinHandler: ChannelInboundHandler {
490490
headers.add(name: "Location", value: "/redirect/infinite1")
491491
self.resps.append(HTTPResponseBuilder(status: .found, headers: headers))
492492
return
493+
case "/redirect/target":
494+
var headers = self.responseHeaders
495+
let targetURL = req.headers["X-Target-Redirect-URL"].first ?? ""
496+
headers.add(name: "Location", value: targetURL)
497+
self.resps.append(HTTPResponseBuilder(status: .found, headers: headers))
498+
return
493499
case "/percent%20encoded":
494500
if req.method != .GET {
495501
self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed))

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

+89
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,95 @@ class HTTPClientTests: XCTestCase {
198198

199199
response = try localClient.get(url: self.defaultHTTPBinURLPrefix + "redirect/https?port=\(httpsBin.port)").wait()
200200
XCTAssertEqual(response.status, .ok)
201+
202+
XCTAssertNoThrow(try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { httpSocketPath in
203+
XCTAssertNoThrow(try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { httpsSocketPath in
204+
let socketHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(httpSocketPath))
205+
let socketHTTPSBin = HTTPBin(ssl: true, bindTarget: .unixDomainSocket(httpsSocketPath))
206+
defer {
207+
XCTAssertNoThrow(try socketHTTPBin.shutdown())
208+
XCTAssertNoThrow(try socketHTTPSBin.shutdown())
209+
}
210+
211+
// From HTTP or HTTPS to HTTP+UNIX should fail to redirect
212+
var targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
213+
var request = try Request(url: self.defaultHTTPBinURLPrefix + "redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
214+
215+
var response = try localClient.execute(request: request).wait()
216+
XCTAssertEqual(response.status, .found)
217+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
218+
219+
request = try Request(url: "https://localhost:\(httpsBin.port)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
220+
221+
response = try localClient.execute(request: request).wait()
222+
XCTAssertEqual(response.status, .found)
223+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
224+
225+
// From HTTP or HTTPS to HTTPS+UNIX should also fail to redirect
226+
targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
227+
request = try Request(url: self.defaultHTTPBinURLPrefix + "redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
228+
229+
response = try localClient.execute(request: request).wait()
230+
XCTAssertEqual(response.status, .found)
231+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
232+
233+
request = try Request(url: "https://localhost:\(httpsBin.port)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
234+
235+
response = try localClient.execute(request: request).wait()
236+
XCTAssertEqual(response.status, .found)
237+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
238+
239+
// ... while HTTP+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed
240+
targetURL = self.defaultHTTPBinURLPrefix + "ok"
241+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
242+
243+
response = try localClient.execute(request: request).wait()
244+
XCTAssertEqual(response.status, .ok)
245+
246+
targetURL = "https://localhost:\(httpsBin.port)/ok"
247+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
248+
249+
response = try localClient.execute(request: request).wait()
250+
XCTAssertEqual(response.status, .ok)
251+
252+
targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
253+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
254+
255+
response = try localClient.execute(request: request).wait()
256+
XCTAssertEqual(response.status, .ok)
257+
258+
targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
259+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
260+
261+
response = try localClient.execute(request: request).wait()
262+
XCTAssertEqual(response.status, .ok)
263+
264+
// ... and HTTPS+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed
265+
targetURL = self.defaultHTTPBinURLPrefix + "ok"
266+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
267+
268+
response = try localClient.execute(request: request).wait()
269+
XCTAssertEqual(response.status, .ok)
270+
271+
targetURL = "https://localhost:\(httpsBin.port)/ok"
272+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
273+
274+
response = try localClient.execute(request: request).wait()
275+
XCTAssertEqual(response.status, .ok)
276+
277+
targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
278+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
279+
280+
response = try localClient.execute(request: request).wait()
281+
XCTAssertEqual(response.status, .ok)
282+
283+
targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
284+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
285+
286+
response = try localClient.execute(request: request).wait()
287+
XCTAssertEqual(response.status, .ok)
288+
})
289+
})
201290
}
202291

203292
func testHttpHostRedirect() {

0 commit comments

Comments
 (0)