diff --git a/packages/js/csv-export/changelog/add-change-csv-export-escape-char b/packages/js/csv-export/changelog/add-change-csv-export-escape-char new file mode 100644 index 00000000000..d28efd31c6d --- /dev/null +++ b/packages/js/csv-export/changelog/add-change-csv-export-escape-char @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Use single quote instead of tab for escaping in CSV exports. diff --git a/packages/js/csv-export/src/__mocks__/mock-csv-data.js b/packages/js/csv-export/src/__mocks__/mock-csv-data.js index 950b794beaa..af0507b8fa4 100644 --- a/packages/js/csv-export/src/__mocks__/mock-csv-data.js +++ b/packages/js/csv-export/src/__mocks__/mock-csv-data.js @@ -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"`; diff --git a/packages/js/csv-export/src/index.ts b/packages/js/csv-export/src/index.ts index 8dddb8b4afe..5bef351999c 100644 --- a/packages/js/csv-export/src/index.ts +++ b/packages/js/csv-export/src/index.ts @@ -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, '""' ) + '"'; } diff --git a/packages/js/csv-export/src/test/index.ts b/packages/js/csv-export/src/test/index.ts index 0a6b83753df..ec6a4c19760 100644 --- a/packages/js/csv-export/src/test/index.ts +++ b/packages/js/csv-export/src/test/index.ts @@ -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( [ {