Use ' instead of backspace for escaping in csv export (#41163)

* Use ' instead of backspace for escaping

* Update tests

* Update dead link

* Update test case title

* Add changelog

* Fix typo in changelog

* Escape tab and carriage return chars too

---------

Co-authored-by: Anurag Bhandari <anurag@automattic.com>
This commit is contained in:
Ahmed 2023-11-15 11:29:55 +02:00 committed by GitHub
parent d4bcae78f9
commit 23df9a59e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 7 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Use single quote instead of tab for escaping in CSV exports.

View File

@ -1,2 +1,2 @@
export default `Date,Orders,Description,"Total sales",Refunds,Coupons,Taxes,Shipping,"Net sales","Negative number"
2018-04-29T00:00:00,30,"Lorem, ""ipsum""",200,19,19,100,19,200,"\t-123"`;
2018-04-29T00:00:00,30,"Lorem, ""ipsum""",200,19,19,100,19,200,"'-123"`;

View File

@ -20,10 +20,19 @@ function escapeCSVValue( value: string | number ) {
let stringValue = value.toString();
// Prevent CSV injection.
// See: http://www.contextis.com/resources/blog/comma-separated-vulnerabilities/
// See: https://owasp.org/www-community/attacks/CSV_Injection
// See: WC_CSV_Exporter::escape_data()
if ( [ '=', '+', '-', '@' ].includes( stringValue.charAt( 0 ) ) ) {
stringValue = '"\t' + stringValue + '"';
if (
[
'=',
'+',
'-',
'@',
String.fromCharCode( 0x09 ), // tab
String.fromCharCode( 0x0d ), // carriage return
].includes( stringValue.charAt( 0 ) )
) {
stringValue = '"\'' + stringValue + '"';
} else if ( stringValue.match( /[,"\s]/ ) ) {
stringValue = '"' + stringValue.replace( /"/g, '""' ) + '"';
}

View File

@ -33,9 +33,16 @@ describe( 'generateCSVDataFromTable', () => {
);
} );
it( 'should prefix tab character when the cell value starts with one of =, +, -, and @', () => {
[ '=', '+', '-', '@' ].forEach( ( val ) => {
const expected = 'value\n"\t' + val + 'test"';
it( 'should prefix single quote character when the cell value starts with one of =, +, -, @, tab, and carriage return', () => {
[
'=',
'+',
'-',
'@',
String.fromCharCode( 0x09 ), // tab
String.fromCharCode( 0x0d ), // carriage return
].forEach( ( val ) => {
const expected = 'value\n"\'' + val + 'test"';
const result = generateCSVDataFromTable(
[
{