Skip to content

Fix CSV export field escaping and injection protection#956

Open
Ridanshi wants to merge 1 commit into
Charushi06:mainfrom
Ridanshi:fix/csv-export-escaping
Open

Fix CSV export field escaping and injection protection#956
Ridanshi wants to merge 1 commit into
Charushi06:mainfrom
Ridanshi:fix/csv-export-escaping

Conversation

@Ridanshi
Copy link
Copy Markdown

Closes #361

Root cause

downloadData in backend/controllers/csvDownload.controller.js built each CSV row by interpolating raw field values and joining them with commas. Only the notes column had any escaping (a manual double-quote wrapper). The other seven exported fields — id, subject_name, title, due_at, status, priority, and confidence_score — were written without quoting, so:

  • a comma in any value split it across extra columns,
  • a newline broke the row boundary,
  • values beginning with =, +, -, or @ could be executed as spreadsheet formulas.

Changes

backend/controllers/csvDownload.controller.js

Adds escapeCSVField(value), a single reusable helper that implements RFC 4180 serialization:

  • null / undefined"" (empty quoted field)
  • Every value is wrapped in double quotes.
  • Embedded double quotes are doubled ("""), satisfying RFC 4180 §2.7.
  • Values whose first character is a known formula trigger (=, +, -, @, tab, carriage return) are prefixed with a tab character before quoting, preventing spreadsheet applications from interpreting the content as a formula.

downloadData now passes every field through escapeCSVField via a .map() call instead of the previous manual mix of raw values and a hand-rolled notes escaper. Row separators are changed from \n to \r\n per RFC 4180.

tests/exportCsv.test.js (new file)

26 tests using Node's built-in node:test runner (same runner used by the existing exportIcs suite) covering:

  • plain values, numbers, dates
  • null, undefined, empty string
  • commas, newlines, CRLF inside fields
  • embedded double quotes
  • all formula-prefix characters (=, +, -, @, tab, carriage return)
  • hyperlink formula injection example from the issue
  • correct column count with comma-containing fields
  • correct row count with newline-containing fields
  • CRLF row separation
  • regression cases for each previously unescaped field (id, subject_name, title, due_at, notes)

All 26 new tests pass. The 5 existing ICS tests are unaffected.

Verification

node --test tests/exportCsv.test.js
# pass 26  fail 0

node --test tests/exportIcs.test.js
# pass 5   fail 0

All exported fields are now serialized through a shared escapeCSVField
helper that applies RFC 4180 quoting rules: every value is wrapped in
double quotes, embedded double quotes are doubled, and null/undefined
produce an empty quoted field so commas and newlines can never split
columns or rows.

Values whose first character is a spreadsheet formula trigger (=, +,
-, @, tab, carriage return) are prefixed with a tab before quoting so
spreadsheet applications treat the content as plain text rather than
executing it as a formula.

Previously only the notes column had any escaping; the other seven
columns (id, subject_name, title, due_at, status, priority,
confidence_score) were interpolated raw into the CSV output.

Row separators are changed from LF to CRLF in line with RFC 4180.

Adds tests/exportCsv.test.js with 26 tests covering normal values,
commas, newlines, double quotes, all formula-prefix characters, null,
undefined, empty strings, multi-row output, and regression cases for
each previously unescaped field.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: CSV export corrupts fields containing commas, newlines, or formula characters

1 participant