Skip to content

Commit 1f2c733

Browse files
authored
Add more cases to escapeFormulae and allow to pass RegExp (#904)
1 parent 26a86fd commit 1f2c733

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

Diff for: docs/docs.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ <h5>Unparse Config Options</h5>
343343
<code>escapeFormulae</code>
344344
</td>
345345
<td>
346-
If <code>true</code>, field values that begin with <code>=</code>, <code>+</code>, <code>-</code>, or <code>@</code>, will be prepended with a <code>'</code> to defend against <a href="https://www.contextis.com/en/blog/comma-separated-vulnerabilities" target="_blank" rel="noopener">injection attacks</a>, because Excel and LibreOffice will automatically parse such cells as formulae.
346+
If <code>true</code>, field values that begin with <code>=</code>, <code>+</code>, <code>-</code>, <code>@</code>, <code>\t</code>, or <code>\r</code>, will be prepended with a <code>'</code> to defend against <a href="https://owasp.org/www-community/attacks/CSV_Injection" target="_blank" rel="noopener">injection attacks</a>, because Excel and LibreOffice will automatically parse such cells as formulae. You can override those values by setting this option to a regular expression
347347
</td>
348348
</tr>
349349
</table>

Diff for: papaparse.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,11 @@ License: MIT
367367
_escapedQuote = _config.escapeChar + _quoteChar;
368368
}
369369

370-
if (typeof _config.escapeFormulae === 'boolean')
371-
_escapeFormulae = _config.escapeFormulae;
370+
if (typeof _config.escapeFormulae === 'boolean' || _config.escapeFormulae instanceof RegExp) {
371+
_escapeFormulae = _config.escapeFormulae instanceof RegExp ? _config.escapeFormulae : /^[=+\-@\t\r].*$/;
372+
}
372373
}
373374

374-
375375
/** The double for loop that iterates the data and writes out a CSV string including header row */
376376
function serialize(fields, data, skipEmptyLines)
377377
{
@@ -444,13 +444,17 @@ License: MIT
444444
if (str.constructor === Date)
445445
return JSON.stringify(str).slice(1, 25);
446446

447-
if (_escapeFormulae === true && typeof str === "string" && (str.match(/^[=+\-@].*$/) !== null)) {
447+
var needsQuotes = false;
448+
449+
if (_escapeFormulae && typeof str === "string" && _escapeFormulae.test(str)) {
448450
str = "'" + str;
451+
needsQuotes = true;
449452
}
450453

451454
var escapedQuoteStr = str.toString().replace(quoteCharRegex, _escapedQuote);
452455

453-
var needsQuotes = (typeof _quotes === 'boolean' && _quotes)
456+
needsQuotes = needsQuotes
457+
|| _quotes === true
454458
|| (typeof _quotes === 'function' && _quotes(str, col))
455459
|| (Array.isArray(_quotes) && _quotes[col])
456460
|| hasAny(escapedQuoteStr, Papa.BAD_DELIMITERS)

Diff for: tests/test-cases.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,7 @@ var UNPARSE_TESTS = [
18811881
description: "Escape formulae",
18821882
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
18831883
config: { escapeFormulae: true },
1884-
expected: 'Col1,Col2,Col3\r\n\'=danger,\'@danger,safe\r\nsafe=safe,\'+danger,"\'-danger, danger"\r\n\'+safe,\'@safe,"safe, safe"'
1884+
expected: 'Col1,Col2,Col3\r\n"\'=danger","\'@danger",safe\r\nsafe=safe,"\'+danger","\'-danger, danger"\r\n\'+safe,\'@safe,"safe, safe"'
18851885
},
18861886
{
18871887
description: "Don't escape formulae by default",
@@ -1898,14 +1898,39 @@ var UNPARSE_TESTS = [
18981898
description: "Escape formulae with single-quote quoteChar and escapeChar",
18991899
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
19001900
config: { escapeFormulae: true, quoteChar: "'", escapeChar: "'" },
1901-
expected: 'Col1,Col2,Col3\r\n\'\'=danger,\'\'@danger,safe\r\nsafe=safe,\'\'+danger,\'\'\'-danger, danger\'\r\n\'\'+safe,\'\'@safe,\'safe, safe\''
1901+
expected: 'Col1,Col2,Col3\r\n\'\'\'=danger\',\'\'\'@danger\',safe\r\nsafe=safe,\'\'\'+danger\',\'\'\'-danger, danger\'\r\n\'\'+safe,\'\'@safe,\'safe, safe\''
19021902
},
19031903
{
19041904
description: "Escape formulae with single-quote quoteChar and escapeChar and forced quotes",
19051905
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
19061906
config: { escapeFormulae: true, quotes: true, quoteChar: "'", escapeChar: "'" },
19071907
expected: '\'Col1\',\'Col2\',\'Col3\'\r\n\'\'\'=danger\',\'\'\'@danger\',\'safe\'\r\n\'safe=safe\',\'\'\'+danger\',\'\'\'-danger, danger\'\r\n\'\'\'+safe\',\'\'\'@safe\',\'safe, safe\''
19081908
},
1909+
// new escapeFormulae values:
1910+
{
1911+
description: "Escape formulae with tab and carriage-return",
1912+
input: [{ "Col1": "\tdanger", "Col2": "\rdanger,", "Col3": "safe\t\r" }],
1913+
config: { escapeFormulae: true },
1914+
expected: 'Col1,Col2,Col3\r\n"\'\tdanger","\'\rdanger,","safe\t\r"'
1915+
},
1916+
{
1917+
description: "Escape formulae with tab and carriage-return, with forced quotes",
1918+
input: [{ "Col1": " danger", "Col2": "\rdanger,", "Col3": "safe\t\r" }],
1919+
config: { escapeFormulae: true, quotes: true },
1920+
expected: '"Col1","Col2","Col3"\r\n"\'\tdanger","\'\rdanger,","safe\t\r"'
1921+
},
1922+
{
1923+
description: "Escape formulae with tab and carriage-return, with single-quote quoteChar and escapeChar",
1924+
input: [{ "Col1": " danger", "Col2": "\rdanger,", "Col3": "safe, \t\r" }],
1925+
config: { escapeFormulae: true, quoteChar: "'", escapeChar: "'" },
1926+
expected: 'Col1,Col2,Col3\r\n\'\'\'\tdanger\',\'\'\'\rdanger,\',\'safe, \t\r\''
1927+
},
1928+
{
1929+
description: "Escape formulae with tab and carriage-return, with single-quote quoteChar and escapeChar and forced quotes",
1930+
input: [{ "Col1": " danger", "Col2": "\rdanger,", "Col3": "safe, \t\r" }],
1931+
config: { escapeFormulae: true, quotes: true, quoteChar: "'", escapeChar: "'" },
1932+
expected: '\'Col1\',\'Col2\',\'Col3\'\r\n\'\'\'\tdanger\',\'\'\'\rdanger,\',\'safe, \t\r\''
1933+
},
19091934
];
19101935

19111936
describe('Unparse Tests', function() {

0 commit comments

Comments
 (0)