Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3de721a

Browse files
authored
Merge pull request astanin#26 from westurner/25_htmlescape_cells
BUG,SEC: html.escape cells to prevent XSS (astanin#25)
2 parents ae7e8b6 + 65a3464 commit 3de721a

File tree

2 files changed

+22
-11
lines changed

2 files changed

+22
-11
lines changed

tabulate.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ def _is_file(f):
5555
except ImportError:
5656
wcwidth = None
5757

58+
try:
59+
from html import escape as htmlescape
60+
except ImportError:
61+
from cgi import escape as htmlescape
62+
5863

5964
__all__ = ["tabulate", "tabulate_formats", "simple_separated_format"]
6065
__version__ = "0.8.7"
@@ -174,7 +179,7 @@ def _textile_row_with_attrs(cell_values, colwidths, colaligns):
174179

175180
def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore):
176181
# this table header will be suppressed if there is a header row
177-
return "\n".join(["<table>", "<tbody>"])
182+
return "<table>\n<tbody>"
178183

179184

180185
def _html_row_with_attrs(celltag, cell_values, colwidths, colaligns):
@@ -185,12 +190,13 @@ def _html_row_with_attrs(celltag, cell_values, colwidths, colaligns):
185190
"decimal": ' style="text-align: right;"',
186191
}
187192
values_with_attrs = [
188-
"<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""), c)
193+
"<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""),
194+
htmlescape(c))
189195
for c, a in zip(cell_values, colaligns)
190196
]
191-
rowhtml = "<tr>" + "".join(values_with_attrs).rstrip() + "</tr>"
197+
rowhtml = "<tr>{}</tr>".format("".join(values_with_attrs).rstrip())
192198
if celltag == "th": # it's a header row, create a new table header
193-
rowhtml = "\n".join(["<table>", "<thead>", rowhtml, "</thead>", "<tbody>"])
199+
rowhtml = "<table>\n<thead>\n{}\n</thead>\n<tbody>".format(rowhtml)
194200
return rowhtml
195201

196202

test/test_output.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -897,22 +897,27 @@ def test_moinmoin_headerless():
897897
assert_equal(expected, result)
898898

899899

900+
_test_table_html_headers = ["<strings>", "<&numbers&>"]
901+
_test_table_html = [["spam >", 41.9999], ["eggs &", 451.0]]
902+
assert_equal.__self__.maxDiff = None
903+
900904
def test_html():
901905
"Output: html with headers"
902906
expected = "\n".join(
903907
[
904908
"<table>",
905909
"<thead>",
906-
'<tr><th>strings </th><th style="text-align: right;"> numbers</th></tr>',
910+
'<tr><th>&lt;strings&gt; </th><th style="text-align: right;"> &lt;&amp;numbers&amp;&gt;</th></tr>',
907911
"</thead>",
908912
"<tbody>",
909-
'<tr><td>spam </td><td style="text-align: right;"> 41.9999</td></tr>',
910-
'<tr><td>eggs </td><td style="text-align: right;"> 451 </td></tr>',
913+
'<tr><td>spam &gt; </td><td style="text-align: right;"> 41.9999</td></tr>',
914+
'<tr><td>eggs &amp; </td><td style="text-align: right;"> 451 </td></tr>',
911915
"</tbody>",
912916
"</table>",
913917
]
914918
)
915-
result = tabulate(_test_table, _test_table_headers, tablefmt="html")
919+
result = tabulate(_test_table_html, _test_table_html_headers,
920+
tablefmt="html")
916921
assert_equal(expected, result)
917922

918923

@@ -922,13 +927,13 @@ def test_html_headerless():
922927
[
923928
"<table>",
924929
"<tbody>",
925-
'<tr><td>spam</td><td style="text-align: right;"> 41.9999</td></tr>',
926-
'<tr><td>eggs</td><td style="text-align: right;">451 </td></tr>',
930+
'<tr><td>spam &gt;</td><td style="text-align: right;"> 41.9999</td></tr>',
931+
'<tr><td>eggs &amp;</td><td style="text-align: right;">451 </td></tr>',
927932
"</tbody>",
928933
"</table>",
929934
]
930935
)
931-
result = tabulate(_test_table, tablefmt="html")
936+
result = tabulate(_test_table_html, tablefmt="html")
932937
assert_equal(expected, result)
933938

934939

0 commit comments

Comments
 (0)