Mercurial > p > roundup > code
comparison test/test_cgi.py @ 6190:15fd91fd3c4c
Quote all exported CSV data
Quote all non-numeric data in csv export functions. Report that a
title like '=a2+b3' could be interpreted as a function in Excel and
executed. csv.writer now includes quoting=csv.QUOTE_NONNUMERIC to
generate quoted values for all fields. This should make the string
starting with = be interpreted as a string and not a formula.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 08 Jun 2020 16:18:21 -0400 |
| parents | f74d078cfd9a |
| children | bdcccd2b2141 |
comparison
equal
deleted
inserted
replaced
| 6189:7458211ca6f3 | 6190:15fd91fd3c4c |
|---|---|
| 1790 output = io.BytesIO() | 1790 output = io.BytesIO() |
| 1791 cl.request = MockNull() | 1791 cl.request = MockNull() |
| 1792 cl.request.wfile = output | 1792 cl.request.wfile = output |
| 1793 # call export version that outputs names | 1793 # call export version that outputs names |
| 1794 actions.ExportCSVAction(cl).handle() | 1794 actions.ExportCSVAction(cl).handle() |
| 1795 #print(output.getvalue()) | 1795 should_be=(s2b('"id","title","status","keyword","assignedto","nosy"\r\n' |
| 1796 should_be=(s2b('id,title,status,keyword,assignedto,nosy\r\n' | 1796 '"1","foo1","deferred","","Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n' |
| 1797 '1,foo1,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n' | 1797 '"2","bar2","unread","keyword1;keyword2","Bork, Chef","Bork, Chef"\r\n' |
| 1798 '2,bar2,unread,keyword1;keyword2,"Bork, Chef","Bork, Chef"\r\n' | 1798 '"3","baz32","need-eg","","",""\r\n')) |
| 1799 '3,baz32,need-eg,,,\r\n')) | |
| 1800 #print(should_be) | 1799 #print(should_be) |
| 1801 #print(output.getvalue()) | 1800 print(output.getvalue()) |
| 1802 self.assertEqual(output.getvalue(), should_be) | 1801 self.assertEqual(output.getvalue(), should_be) |
| 1803 output = io.BytesIO() | 1802 output = io.BytesIO() |
| 1804 cl.request = MockNull() | 1803 cl.request = MockNull() |
| 1805 cl.request.wfile = output | 1804 cl.request.wfile = output |
| 1806 # call export version that outputs id numbers | 1805 # call export version that outputs id numbers |
| 1807 actions.ExportCSVWithIdAction(cl).handle() | 1806 actions.ExportCSVWithIdAction(cl).handle() |
| 1807 should_be = s2b('"id","title","status","keyword","assignedto","nosy"\r\n' | |
| 1808 "\"1\",\"foo1\",\"2\",\"[]\",\"4\",\"['3', '4', '5']\"\r\n" | |
| 1809 "\"2\",\"bar2\",\"1\",\"['1', '2']\",\"3\",\"['3']\"\r\n" | |
| 1810 '\"3\","baz32",\"4\","[]","None","[]"\r\n') | |
| 1811 #print(should_be) | |
| 1808 print(output.getvalue()) | 1812 print(output.getvalue()) |
| 1809 self.assertEqual(s2b('id,title,status,keyword,assignedto,nosy\r\n' | 1813 self.assertEqual(output.getvalue(), should_be) |
| 1810 "1,foo1,2,[],4,\"['3', '4', '5']\"\r\n" | |
| 1811 "2,bar2,1,\"['1', '2']\",3,['3']\r\n" | |
| 1812 '3,baz32,4,[],None,[]\r\n'), | |
| 1813 output.getvalue()) | |
| 1814 | 1814 |
| 1815 def testCSVExportCharset(self): | 1815 def testCSVExportCharset(self): |
| 1816 cl = self._make_client( | 1816 cl = self._make_client( |
| 1817 {'@columns': 'id,title,status,keyword,assignedto,nosy'}, | 1817 {'@columns': 'id,title,status,keyword,assignedto,nosy'}, |
| 1818 nodeid=None, userid='1') | 1818 nodeid=None, userid='1') |
| 1825 output = io.BytesIO() | 1825 output = io.BytesIO() |
| 1826 cl.request = MockNull() | 1826 cl.request = MockNull() |
| 1827 cl.request.wfile = output | 1827 cl.request.wfile = output |
| 1828 # call export version that outputs names | 1828 # call export version that outputs names |
| 1829 actions.ExportCSVAction(cl).handle() | 1829 actions.ExportCSVAction(cl).handle() |
| 1830 should_be=(b'id,title,status,keyword,assignedto,nosy\r\n' | 1830 should_be=(b'"id","title","status","keyword","assignedto","nosy"\r\n' |
| 1831 b'1,foo1\xc3\xa4,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n') | 1831 b'"1","foo1\xc3\xa4","deferred","","Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n') |
| 1832 self.assertEqual(output.getvalue(), should_be) | 1832 self.assertEqual(output.getvalue(), should_be) |
| 1833 | 1833 |
| 1834 output = io.BytesIO() | 1834 output = io.BytesIO() |
| 1835 cl.request = MockNull() | 1835 cl.request = MockNull() |
| 1836 cl.request.wfile = output | 1836 cl.request.wfile = output |
| 1837 # call export version that outputs id numbers | 1837 # call export version that outputs id numbers |
| 1838 actions.ExportCSVWithIdAction(cl).handle() | 1838 actions.ExportCSVWithIdAction(cl).handle() |
| 1839 print(output.getvalue()) | 1839 print(output.getvalue()) |
| 1840 self.assertEqual(b'id,title,status,keyword,assignedto,nosy\r\n' | 1840 self.assertEqual(b'"id","title","status","keyword","assignedto","nosy"\r\n' |
| 1841 b"1,foo1\xc3\xa4,2,[],4,\"['3', '4', '5']\"\r\n", | 1841 b"\"1\",\"foo1\xc3\xa4\",\"2\",\"[]\",\"4\",\"['3', '4', '5']\"\r\n", |
| 1842 output.getvalue()) | 1842 output.getvalue()) |
| 1843 | 1843 |
| 1844 # again with ISO-8859-1 client charset | 1844 # again with ISO-8859-1 client charset |
| 1845 cl.charset = 'iso8859-1' | 1845 cl.charset = 'iso8859-1' |
| 1846 output = io.BytesIO() | 1846 output = io.BytesIO() |
| 1847 cl.request = MockNull() | 1847 cl.request = MockNull() |
| 1848 cl.request.wfile = output | 1848 cl.request.wfile = output |
| 1849 # call export version that outputs names | 1849 # call export version that outputs names |
| 1850 actions.ExportCSVAction(cl).handle() | 1850 actions.ExportCSVAction(cl).handle() |
| 1851 should_be=(b'id,title,status,keyword,assignedto,nosy\r\n' | 1851 should_be=(b'"id","title","status","keyword","assignedto","nosy"\r\n' |
| 1852 b'1,foo1\xe4,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n') | 1852 b'"1","foo1\xe4","deferred","","Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n') |
| 1853 self.assertEqual(output.getvalue(), should_be) | 1853 self.assertEqual(output.getvalue(), should_be) |
| 1854 | 1854 |
| 1855 output = io.BytesIO() | 1855 output = io.BytesIO() |
| 1856 cl.request = MockNull() | 1856 cl.request = MockNull() |
| 1857 cl.request.wfile = output | 1857 cl.request.wfile = output |
| 1858 # call export version that outputs id numbers | 1858 # call export version that outputs id numbers |
| 1859 actions.ExportCSVWithIdAction(cl).handle() | 1859 actions.ExportCSVWithIdAction(cl).handle() |
| 1860 print(output.getvalue()) | 1860 print(output.getvalue()) |
| 1861 self.assertEqual(b'id,title,status,keyword,assignedto,nosy\r\n' | 1861 self.assertEqual(b'"id","title","status","keyword","assignedto","nosy"\r\n' |
| 1862 b"1,foo1\xe4,2,[],4,\"['3', '4', '5']\"\r\n", | 1862 b"\"1\",\"foo1\xe4\",\"2\",\"[]\",\"4\",\"['3', '4', '5']\"\r\n", |
| 1863 output.getvalue()) | 1863 output.getvalue()) |
| 1864 | 1864 |
| 1865 def testCSVExportBadColumnName(self): | 1865 def testCSVExportBadColumnName(self): |
| 1866 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, | 1866 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, |
| 1867 userid='1') | 1867 userid='1') |
| 1901 # used to be self.assertRaises(exceptions.Unauthorised, | 1901 # used to be self.assertRaises(exceptions.Unauthorised, |
| 1902 # but not acting like the column name is not found | 1902 # but not acting like the column name is not found |
| 1903 | 1903 |
| 1904 actions.ExportCSVAction(cl).handle() | 1904 actions.ExportCSVAction(cl).handle() |
| 1905 #print(output.getvalue()) | 1905 #print(output.getvalue()) |
| 1906 self.assertEqual(s2b('id,username,address,password\r\n' | 1906 self.assertEqual(s2b('"id","username","address","password"\r\n' |
| 1907 '1,admin,[hidden],[hidden]\r\n' | 1907 '"1","admin","[hidden]","[hidden]"\r\n' |
| 1908 '2,anonymous,[hidden],[hidden]\r\n' | 1908 '"2","anonymous","[hidden]","[hidden]"\r\n' |
| 1909 '3,Chef,[hidden],[hidden]\r\n' | 1909 '"3","Chef","[hidden]","[hidden]"\r\n' |
| 1910 '4,mary,[hidden],[hidden]\r\n' | 1910 '"4","mary","[hidden]","[hidden]"\r\n' |
| 1911 '5,demo,demo@test.test,%s\r\n'%(passwd)), | 1911 '"5","demo","demo@test.test","%s"\r\n'%(passwd)), |
| 1912 output.getvalue()) | 1912 output.getvalue()) |
| 1913 | 1913 |
| 1914 def testCSVExportWithId(self): | 1914 def testCSVExportWithId(self): |
| 1915 cl = self._make_client({'@columns': 'id,name'}, nodeid=None, | 1915 cl = self._make_client({'@columns': 'id,name'}, nodeid=None, |
| 1916 userid='1') | 1916 userid='1') |
| 1917 cl.classname = 'status' | 1917 cl.classname = 'status' |
| 1918 output = io.BytesIO() | 1918 output = io.BytesIO() |
| 1919 cl.request = MockNull() | 1919 cl.request = MockNull() |
| 1920 cl.request.wfile = output | 1920 cl.request.wfile = output |
| 1921 actions.ExportCSVWithIdAction(cl).handle() | 1921 actions.ExportCSVWithIdAction(cl).handle() |
| 1922 self.assertEqual(s2b('id,name\r\n1,unread\r\n2,deferred\r\n3,chatting\r\n' | 1922 self.assertEqual(s2b('"id","name"\r\n"1","unread"\r\n"2","deferred"\r\n"3","chatting"\r\n' |
| 1923 '4,need-eg\r\n5,in-progress\r\n6,testing\r\n7,done-cbb\r\n' | 1923 '"4","need-eg"\r\n"5","in-progress"\r\n"6","testing"\r\n"7","done-cbb"\r\n' |
| 1924 '8,resolved\r\n'), | 1924 '"8","resolved"\r\n'), |
| 1925 output.getvalue()) | 1925 output.getvalue()) |
| 1926 | 1926 |
| 1927 def testCSVExportWithIdBadColumnName(self): | 1927 def testCSVExportWithIdBadColumnName(self): |
| 1928 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, | 1928 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, |
| 1929 userid='1') | 1929 userid='1') |
