def test_renderAnswer_textplain(self): """If the request format specifies 'plain', we should return content with mimetype 'text/plain'. """ self.useBenignBridges() request = DummyRequest([self.pagename]) request.args.update({'format': ['plain']}) request.getClientIP = lambda: '4.4.4.4' request.method = b'GET' page = self.bridgesResource.render(request) self.assertTrue("html" not in str(page)) # We just need to strip and split it because it looks like: # # 94.235.85.233:9492 0d9d0547c3471cddc473f7288a6abfb54562dc06 # 255.225.204.145:9511 1fb89d618b3a12afe3529fd072127ea08fb50466 # # (Yes, there are two leading spaces at the beginning of each line) # bridgeLines = [line.strip() for line in page.strip().split('\n')] for bridgeLine in bridgeLines: bridgeLine = bridgeLine.split(' ') self.assertEqual(len(bridgeLine), 2) # Check that the IP and port seem okay: ip, port = bridgeLine[0].rsplit(':') self.assertIsInstance(ipaddr.IPv4Address(ip), ipaddr.IPv4Address) self.assertIsInstance(int(port), int) self.assertGreater(int(port), 0) self.assertLessEqual(int(port), 65535)
def test_render_GET_RTLlang(self): """Test rendering a request for plain bridges in Arabic.""" if 'ar' not in _langs.get_langs(): self.skipTest("'ar' language unsupported") self.useBenignBridges() request = DummyRequest([b"bridges?transport=obfs3"]) request.method = b'GET' request.getClientIP = lambda: '3.3.3.3' # For some strange reason, the 'Accept-Language' value *should not* be # a list, unlike all the other headers and args… request.headers.update({'accept-language': 'ar,en,en_US,'}) page = self.bridgesResource.render(request) self.assertSubstring(b"rtl.css", page) self.assertSubstring( # "I need an alternative way to get bridges!" "أحتاج إلى وسيلة بديلة للحصول على bridges".encode("utf-8"), page) for bridgeLine in self.parseBridgesFromHTMLPage(page): # Check that each bridge line had the expected number of fields: bridgeLine = bridgeLine.split(' ') self.assertEqual(len(bridgeLine), 2)
def test_render_GET_RTLlang_obfs3(self): """Test rendering a request for obfs3 bridges in Farsi.""" self.useBenignBridges() request = DummyRequest([b"bridges?transport=obfs3"]) request.method = b'GET' request.getClientIP = lambda: '3.3.3.3' request.headers.update({'accept-language': 'fa,en,en_US,'}) # We actually have to set the request args manually when using a # DummyRequest: request.args.update({'transport': ['obfs3']}) page = self.bridgesResource.render(request) self.assertSubstring("rtl.css", page) self.assertSubstring( # "How to use the above bridge lines" (since there should be # bridges in this response, we don't tell them about alternative # mechanisms for getting bridges) "چگونگی از پلهای خود استفاده کنید", page) for bridgeLine in self.parseBridgesFromHTMLPage(page): # Check that each bridge line had the expected number of fields: bridgeLine = bridgeLine.split(' ') self.assertEqual(len(bridgeLine), 3) self.assertEqual(bridgeLine[0], 'obfs3') # Check that the IP and port seem okay: ip, port = bridgeLine[1].rsplit(':') self.assertIsInstance(ipaddr.IPv4Address(ip), ipaddr.IPv4Address) self.assertIsInstance(int(port), int) self.assertGreater(int(port), 0) self.assertLessEqual(int(port), 65535)
def test_render_GET_vanilla(self): """Test rendering a request for normal, vanilla bridges.""" self.useBenignBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) # The response should explain how to use the bridge lines: self.assertTrue("How to start using your bridges" in str(page)) for b in self.parseBridgesFromHTMLPage(page): # Check that each bridge line had the expected number of fields: fields = b.split(' ') self.assertEqual(len(fields), 2) # Check that the IP and port seem okay: ip, port = fields[0].rsplit(':') self.assertIsInstance(ipaddress.ip_address(ip), ipaddress.IPv4Address) self.assertIsInstance(int(port), int) self.assertGreater(int(port), 0) self.assertLessEqual(int(port), 65535)
def test_renderAnswer_textplain(self): """If the request format specifies 'plain', we should return content with mimetype 'text/plain'. """ self.useBenignBridges() request = DummyRequest([self.pagename]) request.args.update({'format': ['plain']}) request.getClientIP = lambda: '4.4.4.4' request.method = b'GET' page = self.bridgesResource.render(request) self.assertTrue("html" not in str(page)) # We just need to strip and split it because it looks like: # # 94.235.85.233:9492 0d9d0547c3471cddc473f7288a6abfb54562dc06 # 255.225.204.145:9511 1fb89d618b3a12afe3529fd072127ea08fb50466 # # (Yes, there are two leading spaces at the beginning of each line) # bridgeLines = [line.strip() for line in page.strip().split(b'\n')] for bridgeLine in bridgeLines: bridgeLine = bridgeLine.split(b' ') self.assertEqual(len(bridgeLine), 2) # Check that the IP and port seem okay: ip, port = bridgeLine[0].rsplit(b':') self.assertIsInstance(ipaddress.ip_address(ip.decode("utf-8")), ipaddress.IPv4Address) self.assertIsInstance(int(port), int) self.assertGreater(int(port), 0) self.assertLessEqual(int(port), 65535)
def test_render_GET_RTLlang_obfs3(self): """Test rendering a request for obfs3 bridges in Farsi.""" self.useBenignBridges() request = DummyRequest([b"bridges?transport=obfs3"]) request.method = b'GET' request.getClientIP = lambda: '3.3.3.3' request.headers.update({'accept-language': 'fa,en,en_US,'}) # We actually have to set the request args manually when using a # DummyRequest: request.args.update({'transport': ['obfs3']}) page = self.bridgesResource.render(request) self.assertSubstring("rtl.css", page) self.assertSubstring( # "How to use the above bridge lines" (since there should be # bridges in this response, we don't tell them about alternative # mechanisms for getting bridges) "چگونگی از پلهای خود استفاده کنید", page) for bridgeLine in self.parseBridgesFromHTMLPage(page): # Check that each bridge line had the expected number of fields: bridgeLine = bridgeLine.split(' ') self.assertEqual(len(bridgeLine), 3) self.assertEqual(bridgeLine[0], 'obfs3') # Check that the IP and port seem okay: ip, port = bridgeLine[1].rsplit(':') self.assertIsInstance(ipaddr.IPv4Address(ip), ipaddr.IPv4Address) self.assertIsInstance(int(port), int) self.assertGreater(int(port), 0) self.assertLessEqual(int(port), 65535)
def test_render_GET_RTLlang(self): """Test rendering a request for obfs3 bridges in Hebrew.""" request = DummyRequest(["bridges?transport=obfs3"]) request.method = b'GET' request.getClientIP = lambda: '3.3.3.3' request.headers.update({'accept-language': 'he'}) # We actually have to set the request args manually when using a # DummyRequest: request.args.update({'transport': ['obfs2']}) page = self.optionsResource.render(request) self.assertSubstring("rtl.css", page) self.assertSubstring("מהם גשרים?", page)
def test_render_GET_RTLlang(self): """Test rendering a request for obfs3 bridges in Hebrew.""" request = DummyRequest(["bridges?transport=obfs3"]) request.method = b'GET' request.getClientIP = lambda: '3.3.3.3' request.headers.update({'accept-language': 'he'}) # We actually have to set the request args manually when using a # DummyRequest: request.args.update({'transport': ['obfs2']}) page = self.optionsResource.render(request) self.assertSubstring("rtl.css", page) self.assertSubstring("מהם גשרים?", page)
def test_render_GET_malicious_returnchar(self): """Test rendering a request when the some of the bridges returned have malicious (HTML, Javascript, etc., in their) PT arguments. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) self.assertTrue( 'eww=Bridge 1.2.3.4:1234' in str(page), "Return characters in bridge lines should be removed.")
def test_render_GET_malicious_newlines(self): """Test rendering a request when the some of the bridges returned have malicious (HTML, Javascript, etc., in their) PT arguments. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) self.assertTrue( 'bad=Bridge 6.6.6.6:6666 0123456789abcdef0123456789abcdef01234567' in str(page), "Newlines in bridge lines should be removed.")
def test_https_metrics(self): origFunc = metrics.resolveCountryCode metrics.resolveCountryCode = lambda _: "US" key1 = "https.obfs4.us.success.none" req1 = DummyRequest([b"bridges?transport=obfs4"]) # We have to set the request args manually when using a DummyRequest. req1.args.update({'transport': ['obfs4']}) req1.getClientIP = lambda: "3.3.3.3" self.metrix.recordValidHTTPSRequest(req1) self.assertTrue(self.metrix.hotMetrics[key1] == 1) key2 = "https.obfs4.us.fail.none" req2 = DummyRequest([b"bridges?transport=obfs4"]) # We have to set the request args manually when using a DummyRequest. req2.args.update({'transport': ['obfs4']}) req2.getClientIP = lambda: "3.3.3.3" self.metrix.recordInvalidHTTPSRequest(req2) self.assertTrue(self.metrix.hotMetrics[key2] == 1) metrics.resolveCountryCode = origFunc
def test_render_GET_malicious_returnchar(self): """Test rendering a request when the some of the bridges returned have malicious (HTML, Javascript, etc., in their) PT arguments. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) self.assertTrue( 'eww=Bridge 1.2.3.4:1234' in str(page), "Return characters in bridge lines should be removed.")
def test_render_GET_malicious_newlines(self): """Test rendering a request when the some of the bridges returned have malicious (HTML, Javascript, etc., in their) PT arguments. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) self.assertTrue( 'bad=Bridge 6.6.6.6:6666 0123456789abcdef0123456789abcdef01234567' in str(page), "Newlines in bridge lines should be removed.")
def test_render_GET_malicious_javascript(self): """Test rendering a request when the some of the bridges returned have malicious (HTML, Javascript, etc., in their) PT arguments. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) self.assertTrue( "evil=<script>alert('fuuuu');</script>" in str(page), ("The characters &, <, >, ', and \" in bridge lines should be " "replaced with their corresponding HTML special characters."))
def test_render_GET_malicious_javascript(self): """Test rendering a request when the some of the bridges returned have malicious (HTML, Javascript, etc., in their) PT arguments. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) self.assertTrue( "evil=<script>alert('fuuuu');</script>" in str(page), ("The characters &, <, >, ', and \" in bridge lines should be " "replaced with their corresponding HTML special characters."))
def test_renderAnswer_GET_textplain_malicious(self): """If the request format specifies 'plain', we should return content with mimetype 'text/plain' and ASCII control characters replaced. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.args.update({'format': ['plain']}) request.getClientIP = lambda: '4.4.4.4' request.method = b'GET' page = self.bridgesResource.render(request) self.assertTrue("html" not in str(page)) self.assertTrue( 'eww=Bridge 1.2.3.4:1234' in str(page), "Return characters in bridge lines should be removed.") self.assertTrue('bad=Bridge 6.6.6.6:6666' in str(page), "Newlines in bridge lines should be removed.")
def test_renderAnswer_textplain_error(self): """If we hit some error while returning bridge lines in text/plain format, then our custom plaintext error message (the hardcoded HTML in ``server.replaceErrorPage``) should be returned. """ self.useBenignBridges() request = DummyRequest([self.pagename]) request.args.update({'format': ['plain']}) request.getClientIP = lambda: '4.4.4.4' request.method = b'GET' # We'll cause a TypeError here due to calling '\n'.join(None) page = self.bridgesResource.renderAnswer(request, bridgeLines=None) # We don't want the fancy version: self.assertNotSubstring("Bad News Bears", page) self.assertSubstring("Sorry! Something went wrong with your request.", page)
def test_renderAnswer_textplain_error(self): """If we hit some error while returning bridge lines in text/plain format, then our custom plaintext error message (the hardcoded HTML in ``server.replaceErrorPage``) should be returned. """ self.useBenignBridges() request = DummyRequest([self.pagename]) request.args.update({'format': ['plain']}) request.getClientIP = lambda: '4.4.4.4' request.method = b'GET' # We'll cause a TypeError here due to calling '\n'.join(None) page = self.bridgesResource.renderAnswer(request, bridgeLines=None) # We don't want the fancy version: self.assertNotSubstring(b"Bad Request", page) self.assertSubstring(b"Sorry! Something went wrong with your request.", page)
def test_renderAnswer_GET_textplain_malicious(self): """If the request format specifies 'plain', we should return content with mimetype 'text/plain' and ASCII control characters replaced. """ self.useMaliciousBridges() request = DummyRequest([self.pagename]) request.args.update({'format': ['plain']}) request.getClientIP = lambda: '4.4.4.4' request.method = b'GET' page = self.bridgesResource.render(request) self.assertTrue("html" not in str(page)) self.assertTrue( 'eww=Bridge 1.2.3.4:1234' in str(page), "Return characters in bridge lines should be removed.") self.assertTrue( 'bad=Bridge 6.6.6.6:6666' in str(page), "Newlines in bridge lines should be removed.")
def test_render_GET_RTLlang(self): """Test rendering a request for plain bridges in Arabic.""" self.useBenignBridges() request = DummyRequest([b"bridges?transport=obfs3"]) request.method = b'GET' request.getClientIP = lambda: '3.3.3.3' # For some strange reason, the 'Accept-Language' value *should not* be # a list, unlike all the other headers and args… request.headers.update({'accept-language': 'ar,en,en_US,'}) page = self.bridgesResource.render(request) self.assertSubstring("rtl.css", page) self.assertSubstring( # "I need an alternative way to get bridges!" "أحتاج إلى وسيلة بديلة للحصول على bridges", page) for bridgeLine in self.parseBridgesFromHTMLPage(page): # Check that each bridge line had the expected number of fields: bridgeLine = bridgeLine.split(' ') self.assertEqual(len(bridgeLine), 2)
def test_render_GET_vanilla(self): """Test rendering a request for normal, vanilla bridges.""" self.useBenignBridges() request = DummyRequest([self.pagename]) request.method = b'GET' request.getClientIP = lambda: '1.1.1.1' page = self.bridgesResource.render(request) # The response should explain how to use the bridge lines: self.assertTrue("To enter bridges into Tor Browser" in str(page)) for b in self.parseBridgesFromHTMLPage(page): # Check that each bridge line had the expected number of fields: fields = b.split(' ') self.assertEqual(len(fields), 2) # Check that the IP and port seem okay: ip, port = fields[0].rsplit(':') self.assertIsInstance(ipaddr.IPv4Address(ip), ipaddr.IPv4Address) self.assertIsInstance(int(port), int) self.assertGreater(int(port), 0) self.assertLessEqual(int(port), 65535)