GitHub

@@ -329,6 +329,55 @@ def test_willclose(self):

329329

finally:

330330

self.unfakehttp()

331331332+

def test_url_with_control_char_rejected(self):

333+

for char_no in list(range(0, 0x21)) + [0x7f]:

334+

char = chr(char_no)

335+

schemeless_url = f"//localhost:7777/test{char}/"

336+

self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")

337+

try:

338+

# We explicitly test urllib.request.urlopen() instead of the top

339+

# level 'def urlopen()' function defined in this... (quite ugly)

340+

# test suite. They use different url opening codepaths. Plain

341+

# urlopen uses FancyURLOpener which goes via a codepath that

342+

# calls urllib.parse.quote() on the URL which makes all of the

343+

# above attempts at injection within the url _path_ safe.

344+

escaped_char_repr = repr(char).replace('\\', r'\\')

345+

with self.assertRaisesRegex(

346+

ValueError, f"contain control.*{escaped_char_repr}"):

347+

urllib.request.urlopen(f"http:{schemeless_url}")

348+

with self.assertRaisesRegex(

349+

ValueError, f"contain control.*{escaped_char_repr}"):

350+

urllib.request.urlopen(f"https:{schemeless_url}")

351+

# This code path quotes the URL so there is no injection.

352+

resp = urlopen(f"http:{schemeless_url}")

353+

self.assertNotIn(char, resp.geturl())

354+

finally:

355+

self.unfakehttp()

356+357+

def test_url_with_newline_header_injection_rejected(self):

358+

self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")

359+

host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"

360+

schemeless_url = "//" + host + ":8080/test/?test=a"

361+

try:

362+

# We explicitly test urllib.request.urlopen() instead of the top

363+

# level 'def urlopen()' function defined in this... (quite ugly)

364+

# test suite. They use different url opening codepaths. Plain

365+

# urlopen uses FancyURLOpener which goes via a codepath that

366+

# calls urllib.parse.quote() on the URL which makes all of the

367+

# above attempts at injection within the url _path_ safe.

368+

with self.assertRaisesRegex(

369+

ValueError, r"contain control.*\\r.*(found at least . .)"):

370+

urllib.request.urlopen(f"http:{schemeless_url}")

371+

with self.assertRaisesRegex(ValueError, r"contain control.*\\n"):

372+

urllib.request.urlopen(f"https:{schemeless_url}")

373+

# This code path quotes the URL so there is no injection.

374+

resp = urlopen(f"http:{schemeless_url}")

375+

self.assertNotIn(' ', resp.geturl())

376+

self.assertNotIn('\r', resp.geturl())

377+

self.assertNotIn('\n', resp.geturl())

378+

finally:

379+

self.unfakehttp()

380+332381

def test_read_0_9(self):

333382

# "0.9" response accepted (but not "simple responses" without

334383

# a status line)

Read the original on github.com ↗