Skip to content

Commit 71d7f7f

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 #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 ec48f4f commit 71d7f7f

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift

+6
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,12 @@ internal final class HttpBinHandler: ChannelInboundHandler {
495495
headers.add(name: "Location", value: "/redirect/infinite1")
496496
self.resps.append(HTTPResponseBuilder(status: .found, headers: headers))
497497
return
498+
case "/redirect/target":
499+
var headers = self.responseHeaders
500+
let targetURL = req.headers["X-Target-Redirect-URL"].first ?? ""
501+
headers.add(name: "Location", value: targetURL)
502+
self.resps.append(HTTPResponseBuilder(status: .found, headers: headers))
503+
return
498504
case "/percent%20encoded":
499505
if req.method != .GET {
500506
self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed))

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTests.swift

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

348348
response = try localClient.get(url: self.defaultHTTPBinURLPrefix + "redirect/https?port=\(httpsBin.port)").wait()
349349
XCTAssertEqual(response.status, .ok)
350+
351+
XCTAssertNoThrow(try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { httpSocketPath in
352+
XCTAssertNoThrow(try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { httpsSocketPath in
353+
let socketHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(httpSocketPath))
354+
let socketHTTPSBin = HTTPBin(ssl: true, bindTarget: .unixDomainSocket(httpsSocketPath))
355+
defer {
356+
XCTAssertNoThrow(try socketHTTPBin.shutdown())
357+
XCTAssertNoThrow(try socketHTTPSBin.shutdown())
358+
}
359+
360+
// From HTTP or HTTPS to HTTP+UNIX should fail to redirect
361+
var targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
362+
var request = try Request(url: self.defaultHTTPBinURLPrefix + "redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
363+
364+
var response = try localClient.execute(request: request).wait()
365+
XCTAssertEqual(response.status, .found)
366+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
367+
368+
request = try Request(url: "https://localhost:\(httpsBin.port)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
369+
370+
response = try localClient.execute(request: request).wait()
371+
XCTAssertEqual(response.status, .found)
372+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
373+
374+
// From HTTP or HTTPS to HTTPS+UNIX should also fail to redirect
375+
targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
376+
request = try Request(url: self.defaultHTTPBinURLPrefix + "redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
377+
378+
response = try localClient.execute(request: request).wait()
379+
XCTAssertEqual(response.status, .found)
380+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
381+
382+
request = try Request(url: "https://localhost:\(httpsBin.port)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
383+
384+
response = try localClient.execute(request: request).wait()
385+
XCTAssertEqual(response.status, .found)
386+
XCTAssertEqual(response.headers.first(name: "Location"), targetURL)
387+
388+
// ... while HTTP+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed
389+
targetURL = self.defaultHTTPBinURLPrefix + "ok"
390+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
391+
392+
response = try localClient.execute(request: request).wait()
393+
XCTAssertEqual(response.status, .ok)
394+
395+
targetURL = "https://localhost:\(httpsBin.port)/ok"
396+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
397+
398+
response = try localClient.execute(request: request).wait()
399+
XCTAssertEqual(response.status, .ok)
400+
401+
targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
402+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
403+
404+
response = try localClient.execute(request: request).wait()
405+
XCTAssertEqual(response.status, .ok)
406+
407+
targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
408+
request = try Request(url: "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
409+
410+
response = try localClient.execute(request: request).wait()
411+
XCTAssertEqual(response.status, .ok)
412+
413+
// ... and HTTPS+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed
414+
targetURL = self.defaultHTTPBinURLPrefix + "ok"
415+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
416+
417+
response = try localClient.execute(request: request).wait()
418+
XCTAssertEqual(response.status, .ok)
419+
420+
targetURL = "https://localhost:\(httpsBin.port)/ok"
421+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
422+
423+
response = try localClient.execute(request: request).wait()
424+
XCTAssertEqual(response.status, .ok)
425+
426+
targetURL = "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
427+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
428+
429+
response = try localClient.execute(request: request).wait()
430+
XCTAssertEqual(response.status, .ok)
431+
432+
targetURL = "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok"
433+
request = try Request(url: "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", method: .GET, headers: ["X-Target-Redirect-URL": targetURL], body: nil)
434+
435+
response = try localClient.execute(request: request).wait()
436+
XCTAssertEqual(response.status, .ok)
437+
})
438+
})
350439
}
351440

352441
func testHttpHostRedirect() {

0 commit comments

Comments
 (0)