Mercurial > p > roundup > code
diff test/test_cgi.py @ 6681:ab2ed11c021e
issue2551205: Add support for specifying valid origins for api: xmlrpc/rest
We now have an allow list to filter the hosts allowed to do api
requests. An element of this allow list must match the http ORIGIN
header exactly or the rest/xmlrpc CORS request will result in an
error.
The tracker host is always allowed to do a request.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Tue, 17 May 2022 17:18:51 -0400 |
| parents | da6c9050a79e |
| children | 9a1f5e496e6c |
line wrap: on
line diff
--- a/test/test_cgi.py Tue May 17 14:09:00 2022 -0400 +++ b/test/test_cgi.py Tue May 17 17:18:51 2022 -0400 @@ -955,7 +955,7 @@ # need to set SENDMAILDEBUG to prevent # downstream issue when email is sent on successful # issue creation. Also delete the file afterwards - # just tomake sure that someother test looking for + # just to make sure that some other test looking for # SENDMAILDEBUG won't trip over ours. if 'SENDMAILDEBUG' not in os.environ: os.environ['SENDMAILDEBUG'] = 'mail-test1.log' @@ -1157,6 +1157,18 @@ del(out[0]) del(cl.env['HTTP_REFERER']) + + # test by setting allowed api origins to * + # this should not redirect as it is not an API call. + cl.db.config.WEB_ALLOWED_API_ORIGINS = " * " + cl.env['HTTP_ORIGIN'] = 'https://baz.edu' + cl.inner_main() + match_at=out[0].find('Invalid Origin https://baz.edu') + print("result of subtest invalid origin:", out[0]) + self.assertEqual(match_at, 36) + del(cl.env['HTTP_ORIGIN']) + cl.db.config.WEB_ALLOWED_API_ORIGINS = "" + del(out[0]) # clean up from email log if os.path.exists(SENDMAILDEBUG): @@ -1239,6 +1251,106 @@ self.assertEqual(response,expected) del(out[0]) + + # rest has no form content + cl.db.config.WEB_ALLOWED_API_ORIGINS = "https://bar.edu http://bar.edu" + form = cgi.FieldStorage() + form.list = [ + cgi.MiniFieldStorage('title', 'A new issue'), + cgi.MiniFieldStorage('status', '1'), + cgi.MiniFieldStorage('@pretty', 'false'), + cgi.MiniFieldStorage('@apiver', '1'), + ] + cl = client.Client(self.instance, None, + {'REQUEST_METHOD':'POST', + 'PATH_INFO':'rest/data/issue', + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', + 'HTTP_ORIGIN': 'https://bar.edu', + 'HTTP_X_REQUESTED_WITH': 'rest', + 'HTTP_AUTHORIZATION': 'Basic YWRtaW46YWRtaW4=', + 'HTTP_REFERER': 'http://whoami.com/path/', + 'HTTP_ACCEPT': "application/json;version=1" + }, form) + cl.db = self.db + cl.base = 'http://whoami.com/path/' + cl._socket_op = lambda *x : True + cl._error_message = [] + cl.request = MockNull() + h = { 'content-type': 'application/json', + 'accept': 'application/json' } + cl.request.headers = MockNull(**h) + + cl.write = wh # capture output + + # Should return explanation because content type is text/plain + # and not text/xml + cl.handle_rest() + answer='{"data": {"link": "http://tracker.example/cgi-bin/roundup.cgi/bugs/rest/data/issue/2", "id": "2"}}\n' + # check length to see if pretty is turned off. + self.assertEqual(len(out[0]), 99) + + # compare as dicts not strings due to different key ordering + # between python versions. + response=json.loads(b2s(out[0])) + expected=json.loads(answer) + self.assertEqual(response,expected) + del(out[0]) + + ##### + cl = client.Client(self.instance, None, + {'REQUEST_METHOD':'POST', + 'PATH_INFO':'rest/data/issue', + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', + 'HTTP_ORIGIN': 'httxs://bar.edu', + 'HTTP_AUTHORIZATION': 'Basic YWRtaW46YWRtaW4=', + 'HTTP_REFERER': 'http://whoami.com/path/', + 'HTTP_ACCEPT': "application/json;version=1" + }, form) + cl.db = self.db + cl.base = 'http://whoami.com/path/' + cl._socket_op = lambda *x : True + cl._error_message = [] + cl.request = MockNull() + h = { 'content-type': 'application/json', + 'accept': 'application/json' } + cl.request.headers = MockNull(**h) + + cl.write = wh # capture output + + # Should return explanation because content type is text/plain + # and not text/xml + cl.handle_rest() + self.assertEqual(b2s(out[0]), "<class 'roundup.exceptions.Unauthorised'>: Invalid Origin httxs://bar.edu\n") + del(out[0]) + + + cl.db.config.WEB_ALLOWED_API_ORIGINS = " * " + cl = client.Client(self.instance, None, + {'REQUEST_METHOD':'POST', + 'PATH_INFO':'rest/data/issue', + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', + 'HTTP_ORIGIN': 'httxs://bar.edu', + 'HTTP_X_REQUESTED_WITH': 'rest', + 'HTTP_AUTHORIZATION': 'Basic YWRtaW46YWRtaW4=', + 'HTTP_REFERER': 'http://whoami.com/path/', + 'HTTP_ACCEPT': "application/json;version=1" + }, form) + cl.db = self.db + cl.base = 'http://whoami.com/path/' + cl._socket_op = lambda *x : True + cl._error_message = [] + cl.request = MockNull() + h = { 'content-type': 'application/json', + 'accept': 'application/json' } + cl.request.headers = MockNull(**h) + + cl.write = wh # capture output + + # create third issue + cl.handle_rest() + self.assertIn('"id": "3"', b2s(out[0])) + del(out[0]) + def testXmlrpcCsrfProtection(self): # set the password for admin so we can log in. passwd=password.Password('admin') @@ -1663,12 +1775,11 @@ # need to set SENDMAILDEBUG to prevent # downstream issue when email is sent on successful # issue creation. Also delete the file afterwards - # just tomake sure that someother test looking for + # just to make sure that some other test looking for # SENDMAILDEBUG won't trip over ours. if 'SENDMAILDEBUG' not in os.environ: os.environ['SENDMAILDEBUG'] = 'mail-test1.log' SENDMAILDEBUG = os.environ['SENDMAILDEBUG'] - # missing opaqueregister cl = self._make_client({'username':'new_user1', 'password':'secret', @@ -1727,7 +1838,7 @@ # need to set SENDMAILDEBUG to prevent # downstream issue when email is sent on successful # issue creation. Also delete the file afterwards - # just tomake sure that someother test looking for + # just to make sure that some other test looking for # SENDMAILDEBUG won't trip over ours. if 'SENDMAILDEBUG' not in os.environ: os.environ['SENDMAILDEBUG'] = 'mail-test1.log'
