Skip to content

Fixed issue when CSV data wasn't properly escaped in HTML output #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 14, 2018
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ cd csv-to-html-table
##### Custom formatting
If you want to do custom formatting for one or more column, you can pass in an array of arrays containing the index of the column and a custom function for formatting it. You can pass in multiple formatters and they will be executed in order.

The custom functions must take in one parameter (the value in the cell) and return a string:
The custom functions must take in one parameter (the value in the cell) and return a HTML string:

Example:

Expand All @@ -69,6 +69,9 @@ Example:
</script>
```

Note that you should take care about HTML escaping to avoid [XSS](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)) or broken layout.
jQuery has a nice function [text()](https://api.jquery.com/text/) which safely escapes HTML from value.

#### 4. Run it

You can run this locally using this handy python command:
Expand Down
104 changes: 54 additions & 50 deletions js/csv_to_html_table.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,59 @@ var CsvToHtmlTable = CsvToHtmlTable || {};

CsvToHtmlTable = {
init: function (options) {

options = options || {};
var csv_path = options.csv_path || "";
var el = options.element || "table-container";
var allow_download = options.allow_download || false;
var csv_options = options.csv_options || {};
var datatables_options = options.datatables_options || {};
var custom_formatting = options.custom_formatting || [];

$("#" + el).html("<table class='table table-striped table-condensed' id='" + el + "-table'></table>");

$.when($.get(csv_path)).then(
function(data){
var csv_data = $.csv.toArrays(data, csv_options);

var table_head = "<thead><tr>";

for (head_id = 0; head_id < csv_data[0].length; head_id++) {
table_head += "<th>" + csv_data[0][head_id] + "</th>";
}

table_head += "</tr></thead>";
$('#' + el + '-table').append(table_head);
$('#' + el + '-table').append("<tbody></tbody>");

for (row_id = 1; row_id < csv_data.length; row_id++) {
var row_html = "<tr>";

//takes in an array of column index and function pairs
if (custom_formatting != []) {
$.each(custom_formatting, function(i, v){
var col_idx = v[0]
var func = v[1];
csv_data[row_id][col_idx]= func(csv_data[row_id][col_idx]);
})
}

for (col_id = 0; col_id < csv_data[row_id].length; col_id++) {
row_html += "<td>" + csv_data[row_id][col_id] + "</td>";
}

row_html += "</tr>";
$('#' + el + '-table tbody').append(row_html);
}

$('#' + el + '-table').DataTable(datatables_options);

if (allow_download)
$("#" + el).append("<p><a class='btn btn-info' href='" + csv_path + "'><i class='glyphicon glyphicon-download'></i> Download as CSV</a></p>");
options = options || {};
var csv_path = options.csv_path || "";
var el = options.element || "table-container";
var allow_download = options.allow_download || false;
var csv_options = options.csv_options || {};
var datatables_options = options.datatables_options || {};
var custom_formatting = options.custom_formatting || [];
var customTemplates = {};
$.each(custom_formatting, function (i, v) {
var colIdx = v[0];
var func = v[1];
customTemplates[colIdx] = func;
});

var $table = $("<table class='table table-striped table-condensed' id='" + el + "-table'></table>");
var $containerElement = $("#" + el);
$containerElement.empty().append($table);

$.when($.get(csv_path)).then(
function (data) {
var csvData = $.csv.toArrays(data, csv_options);
var $tableHead = $("<thead></thead>");
var csvHeaderRow = csvData[0];
var $tableHeadRow = $("<tr></tr>");
for (var headerIdx = 0; headerIdx < csvHeaderRow.length; headerIdx++) {
$tableHeadRow.append($("<th></th>").text(csvHeaderRow[headerIdx]));
}
$tableHead.append($tableHeadRow);

$table.append($tableHead);
var $tableBody = $("<tbody></tbody>");

for (var rowIdx = 1; rowIdx < csvData.length; rowIdx++) {
var $tableBodyRow = $("<tr></tr>");
for (var colIdx = 0; colIdx < csvData[rowIdx].length; colIdx++) {
var $tableBodyRowTd = $("<td></td>");
var cellTemplateFunc = customTemplates[colIdx];
if (cellTemplateFunc) {
$tableBodyRowTd.html(cellTemplateFunc(csvData[rowIdx][colIdx]));
} else {
$tableBodyRowTd.text(csvData[rowIdx][colIdx]);
}
$tableBodyRow.append($tableBodyRowTd);
$tableBody.append($tableBodyRow);
}
}
$table.append($tableBody);

$table.DataTable(datatables_options);

if (allow_download) {
$containerElement.append("<p><a class='btn btn-info' href='" + csv_path + "'><i class='glyphicon glyphicon-download'></i> Download as CSV</a></p>");
}
});
}
}
};