Mercurial > p > roundup > code
comparison test/test_cgi.py @ 4880:ca692423e401
Different approach to fix XSS in issue2550817
Encapsulate the error/ok message append method as add_ok_message and
add_error_message. The new approach escapes the messages when appending
-- at a point in the code where we still know where the message comes
from. Escaping is the default but can bei turned off. This also fixes
issue2550836 where certain messages may contain links.
Another advantage of the new fix is that users don't need to change
installed trackers and are secure by default.
| author | Ralf Schlatterbeck <rsc@runtux.com> |
|---|---|
| date | Mon, 31 Mar 2014 18:19:23 +0200 |
| parents | 24b8011cd2dc |
| children | 3b9252085ba9 |
comparison
equal
deleted
inserted
replaced
| 4879:302c967d710c | 4880:ca692423e401 |
|---|---|
| 38 form.list.append(x) | 38 form.list.append(x) |
| 39 else: | 39 else: |
| 40 form.list.append(cgi.MiniFieldStorage(k, v)) | 40 form.list.append(cgi.MiniFieldStorage(k, v)) |
| 41 return form | 41 return form |
| 42 | 42 |
| 43 cm = client.clean_message | 43 cm = client.add_message |
| 44 class MessageTestCase(unittest.TestCase): | 44 class MessageTestCase(unittest.TestCase): |
| 45 # Note: We used to allow some html tags in error message. Now *only* | 45 # Note: Escaping is now handled on a message-by-message basis at a |
| 46 # newlines are allowed and messages are split at newlines. | 46 # point where we still know what generates a message. In this way we |
| 47 # Note that tags are no longer escaped, see doc/upgrading.txt for | 47 # can decide when to escape and when not. We test the add_message |
| 48 # the changes needed in the templates (Migrating from 1.5.0 to 1.5.1) | 48 # routine here. |
| 49 def testCleanMessageOK(self): | 49 # Of course we won't catch errors in judgement when to escape here |
| 50 self.assertEqual(cm('a'), ['a']) | 50 # -- but at the time of this change only one message is not escaped. |
| 51 self.assertEqual(cm('a\nb'), ['a','b']) | 51 def testAddMessageOK(self): |
| 52 self.assertEqual(cm('a\nb\nc\n'), ['a','b','c','']) | 52 self.assertEqual(cm([],'a\nb'), ['a<br />\nb']) |
| 53 self.assertEqual(cm([],'a\nb\nc\n'), ['a<br />\nb<br />\nc<br />\n']) | |
| 54 | |
| 55 def testAddMessageBAD(self): | |
| 56 self.assertEqual(cm([],'<script>x</script>'), | |
| 57 ['<script>x</script>']) | |
| 58 self.assertEqual(cm([],'<iframe>x</iframe>'), | |
| 59 ['<iframe>x</iframe>']) | |
| 60 self.assertEqual(cm([],'<<script >>alert(42);5<</script >>'), | |
| 61 ['<<script >>alert(42);5<</script >>']) | |
| 62 self.assertEqual(cm([],'<a href="y">x</a>'), | |
| 63 ['<a href="y">x</a>']) | |
| 64 self.assertEqual(cm([],'<A HREF="y">x</A>'), | |
| 65 ['<A HREF="y">x</A>']) | |
| 66 self.assertEqual(cm([],'<br>x<br />'), ['<br>x<br />']) | |
| 67 self.assertEqual(cm([],'<i>x</i>'), ['<i>x</i>']) | |
| 68 self.assertEqual(cm([],'<b>x</b>'), ['<b>x</b>']) | |
| 69 self.assertEqual(cm([],'<BR>x<BR />'), ['<BR>x<BR />']) | |
| 70 self.assertEqual(cm([],'<I>x</I>'), ['<I>x</I>']) | |
| 71 self.assertEqual(cm([],'<B>x</B>'), ['<B>x</B>']) | |
| 72 | |
| 73 def testAddMessageNoEscape(self): | |
| 74 self.assertEqual(cm([],'<i>x</i>',False), ['<i>x</i>']) | |
| 75 self.assertEqual(cm([],'<i>x</i>\n<b>x</b>',False), | |
| 76 ['<i>x</i><br />\n<b>x</b>']) | |
| 53 | 77 |
| 54 class FormTestCase(unittest.TestCase): | 78 class FormTestCase(unittest.TestCase): |
| 55 def setUp(self): | 79 def setUp(self): |
| 56 self.dirname = '_test_cgi_form' | 80 self.dirname = '_test_cgi_form' |
| 57 # set up and open a tracker | 81 # set up and open a tracker |
| 670 if nodeid is not None: | 694 if nodeid is not None: |
| 671 cl.nodeid = nodeid | 695 cl.nodeid = nodeid |
| 672 cl.db = self.db | 696 cl.db = self.db |
| 673 cl.userid = userid | 697 cl.userid = userid |
| 674 cl.language = ('en',) | 698 cl.language = ('en',) |
| 675 cl.error_message = [] | 699 cl._error_message = [] |
| 676 cl.template = template | 700 cl.template = template |
| 677 return cl | 701 return cl |
| 678 | 702 |
| 679 def testClassPermission(self): | 703 def testClassPermission(self): |
| 680 cl = self._make_client(dict(username='bob')) | 704 cl = self._make_client(dict(username='bob')) |
| 741 cl = self._make_client({'username':'new_user', 'password':'secret', | 765 cl = self._make_client({'username':'new_user', 'password':'secret', |
| 742 '@confirm@password':'secret', 'address':'new_user@bork.bork', | 766 '@confirm@password':'secret', 'address':'new_user@bork.bork', |
| 743 'roles':'Admin'}, nodeid=None, userid='4') | 767 'roles':'Admin'}, nodeid=None, userid='4') |
| 744 self.assertRaises(exceptions.Unauthorised, | 768 self.assertRaises(exceptions.Unauthorised, |
| 745 actions.NewItemAction(cl).handle) | 769 actions.NewItemAction(cl).handle) |
| 746 self.assertEqual(cl.error_message,[]) | 770 self.assertEqual(cl._error_message,[]) |
| 747 # this should work | 771 # this should work |
| 748 cl = self._make_client({'username':'new_user', 'password':'secret', | 772 cl = self._make_client({'username':'new_user', 'password':'secret', |
| 749 '@confirm@password':'secret', 'address':'new_user@bork.bork'}, | 773 '@confirm@password':'secret', 'address':'new_user@bork.bork'}, |
| 750 nodeid=None, userid='4') | 774 nodeid=None, userid='4') |
| 751 self.assertRaises(exceptions.Redirect, | 775 self.assertRaises(exceptions.Redirect, |
| 752 actions.NewItemAction(cl).handle) | 776 actions.NewItemAction(cl).handle) |
| 753 self.assertEqual(cl.error_message,[]) | 777 self.assertEqual(cl._error_message,[]) |
| 754 # don't allow changing (my own) username (in this example) | 778 # don't allow changing (my own) username (in this example) |
| 755 cl = self._make_client(dict(username='new_user42'), userid='4') | 779 cl = self._make_client(dict(username='new_user42'), userid='4') |
| 756 self.assertRaises(exceptions.Unauthorised, | 780 self.assertRaises(exceptions.Unauthorised, |
| 757 actions.EditItemAction(cl).handle) | 781 actions.EditItemAction(cl).handle) |
| 758 cl = self._make_client(dict(username='new_user42'), userid='4', | 782 cl = self._make_client(dict(username='new_user42'), userid='4', |
| 887 self.assertEqual([x.id for x in h.batch()],['1', '2', '3']) | 911 self.assertEqual([x.id for x in h.batch()],['1', '2', '3']) |
| 888 | 912 |
| 889 def testEditCSV(self): | 913 def testEditCSV(self): |
| 890 form = dict(rows='id,name\n1,newkey') | 914 form = dict(rows='id,name\n1,newkey') |
| 891 cl = self._make_client(form, userid='1', classname='keyword') | 915 cl = self._make_client(form, userid='1', classname='keyword') |
| 892 cl.ok_message = [] | 916 cl._ok_message = [] |
| 893 actions.EditCSVAction(cl).handle() | 917 actions.EditCSVAction(cl).handle() |
| 894 self.assertEqual(cl.ok_message, ['Items edited OK']) | 918 self.assertEqual(cl._ok_message, ['Items edited OK']) |
| 895 k = self.db.keyword.getnode('1') | 919 k = self.db.keyword.getnode('1') |
| 896 self.assertEqual(k.name, 'newkey') | 920 self.assertEqual(k.name, 'newkey') |
| 897 form = dict(rows=u'id,name\n1,\xe4\xf6\xfc'.encode('utf-8')) | 921 form = dict(rows=u'id,name\n1,\xe4\xf6\xfc'.encode('utf-8')) |
| 898 cl = self._make_client(form, userid='1', classname='keyword') | 922 cl = self._make_client(form, userid='1', classname='keyword') |
| 899 cl.ok_message = [] | 923 cl._ok_message = [] |
| 900 actions.EditCSVAction(cl).handle() | 924 actions.EditCSVAction(cl).handle() |
| 901 self.assertEqual(cl.ok_message, ['Items edited OK']) | 925 self.assertEqual(cl._ok_message, ['Items edited OK']) |
| 902 k = self.db.keyword.getnode('1') | 926 k = self.db.keyword.getnode('1') |
| 903 self.assertEqual(k.name, u'\xe4\xf6\xfc'.encode('utf-8')) | 927 self.assertEqual(k.name, u'\xe4\xf6\xfc'.encode('utf-8')) |
| 904 | 928 |
| 905 def testRoles(self): | 929 def testRoles(self): |
| 906 cl = self._make_client({}) | 930 cl = self._make_client({}) |
