Mercurial > p > roundup > code
diff test/test_cgi.py @ 5218:44f7e6b958fe
Added tests for csrf with xmlrpc.
Fixed the code for xmlrpc csrf defense:
raise UsageError if X-REQUESTED-WITH header is required and missing.
if HTTP_AUTHORIZATION is used, properly seed the random number
generator using the password.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 27 Mar 2017 22:37:30 -0400 |
| parents | 7da56980754d |
| children | 14d8f61e6ef2 |
line wrap: on
line diff
--- a/test/test_cgi.py Thu Mar 23 21:08:30 2017 -0400 +++ b/test/test_cgi.py Mon Mar 27 22:37:30 2017 -0400 @@ -12,6 +12,7 @@ from roundup.cgi import client, actions, exceptions from roundup.cgi.exceptions import FormError +from roundup.exceptions import UsageError from roundup.cgi.templating import HTMLItem, HTMLRequest, NoTemplate, anti_csrf_nonce from roundup.cgi.templating import HTMLProperty, _HTMLItem from roundup.cgi.form_parser import FormParser @@ -1046,6 +1047,61 @@ os.remove(SENDMAILDEBUG) #raise ValueError + def testXmlrpcCsrfProtection(self): + # set the password for admin so we can log in. + passwd=password.Password('admin') + self.db.user.set('1', password=passwd) + + out = [] + def wh(s): + out.append(s) + + # xmlrpc has no form content + form = {} + cl = client.Client(self.instance, None, + {'REQUEST_METHOD':'POST', + 'PATH_INFO':'xmlrpc', + 'CONTENT_TYPE': 'text/plain', + 'HTTP_AUTHORIZATION': 'Basic YWRtaW46YWRtaW4=', + 'HTTP_REFERER': 'http://whoami.com/path/', + 'HTTP_X-REQUESTED-WITH': "XMLHttpRequest" + }, form) + cl.db = self.db + cl.base = 'http://whoami.com/path/' + cl._socket_op = lambda *x : True + cl._error_message = [] + cl.request = MockNull() + cl.write = wh # capture output + + # Should return explanation because content type is text/plain + # and not text/xml + cl.handle_xmlrpc() + self.assertEqual(out[0], "This is the endpoint of Roundup <a href='http://www.roundup-tracker.org/docs/xmlrpc.html'>XML-RPC interface</a>.") + del(out[0]) + + # Should return admin user indicating auth works and + # header checks succeed (REFERER and X-REQUESTED-WITH) + cl.env['CONTENT_TYPE'] = "text/xml" + # ship the form with the value holding the xml value. + # I have no clue why this works but .... + cl.form = MockNull(file = True, value = "<?xml version='1.0'?>\n<methodCall>\n<methodName>display</methodName>\n<params>\n<param>\n<value><string>user1</string></value>\n</param>\n<param>\n<value><string>username</string></value>\n</param>\n</params>\n</methodCall>\n" ) + answer ="<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><struct>\n<member>\n<name>username</name>\n<value><string>admin</string></value>\n</member>\n</struct></value>\n</param>\n</params>\n</methodResponse>\n" + cl.handle_xmlrpc() + print out + self.assertEqual(out[0], answer) + del(out[0]) + + # remove the X-REQUESTED-WITH header and get a failure. + del(cl.env['HTTP_X-REQUESTED-WITH']) + self.assertRaises(UsageError,cl.handle_xmlrpc) + + # change config to not require X-REQUESTED-WITH header + cl.db.config['WEB_CSRF_ENFORCE_HEADER_X-REQUESTED-WITH'] = 'logfailure' + cl.handle_xmlrpc() + print out + self.assertEqual(out[0], answer) + del(out[0]) + # # SECURITY # @@ -1481,6 +1537,7 @@ result = self.client.renderContext() print result + # sha1sum of classic tracker user.forgotten.template must be found self.assertNotEqual(-1, result.index('<!-- SHA: eb5dd0bec7a57d58cb7edbeb939fb0390ed1bf74 -->')) @@ -1495,6 +1552,7 @@ result = self.client.renderContext() print result + # sha1sum of classic tracker user.item.template must be found self.assertNotEqual(-1, result.index('<!-- SHA: 3b7ce7cbf24f77733c9b9f64a569d6429390cc3f -->'))
