Skip to content

fix(exporters): neutralize CSV formula injection (#1300) - #1302

Closed
saidai-bhuvanesh wants to merge 1 commit into
Nitya-003:mainfrom
saidai-bhuvanesh:fix/1300-csv-formula-injection
Closed

fix(exporters): neutralize CSV formula injection (#1300)#1302
saidai-bhuvanesh wants to merge 1 commit into
Nitya-003:mainfrom
saidai-bhuvanesh:fix/1300-csv-formula-injection

Conversation

@saidai-bhuvanesh

@saidai-bhuvanesh saidai-bhuvanesh commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

The supply-chain CSV export escaped commas and quotes but did not protect cells whose values start with a spreadsheet formula trigger character (=, +, -, @, tab, or CR). A malicious or accidental field value such as a crop type, farmer name, or stage note of =HYPERLINK("http://bad.test","Click") would be executed as a formula when the exported CSV was opened in Excel / Google Sheets / LibreOffice (CWE-1236 — CSV formula injection). This PR adds formula-injection protection to the CSV export path.

Problem

generateBatchCSVString in frontend/src/utils/exporters.ts built each cell by hand as "${value}" with only quote-escaping. It never inspected the first character of the value. Spreadsheet applications treat a leading =, +, -, @, tab, or carriage return as the start of a formula, so any user-controlled field that began with one of those characters would be evaluated on open rather than displayed as text. Every user-controlled cell in the export was affected: batch fields (cropType, farmerName, farmerAddress, origin, quantity, harvestDate, status, currentStage, certifications, description) and every supply-chain timeline field (stage, location, actor, temperature, humidity, txHash, notes).

Fix

Introduce a sanitizeCsvCell(value) guard that detects values beginning with a formula trigger character and prefixes them with a single quote ('). Spreadsheets treat a leading single quote as the literal-text marker and silently strip it on display, so the value renders identically to the user but can never be executed as a formula. sanitizeCsvCell is wrapped by a shared csvCell(value) helper that also performs the existing quote/escaping, and every dynamic cell in generateBatchCSVString now goes through csvCell instead of being quoted by hand. Static header strings are not user-controlled and are unchanged.

Behaviour preserved:

  • Quote-escaping still doubles embedded " (Jane "Farmer""Jane ""Farmer""").
  • null/undefined cells render as empty, numbers are coerced normally (0 is kept, not dropped).
  • Values that merely contain =/@ mid-string (e.g. a=b, hello @user) are not affected — only values that start with a trigger character are neutralized.

Verification

  • Added frontend/src/utils/exporters.test.ts with 8 tests: trigger-character prefixing for = + - @ \t \r, benign-value pass-through, nullish/number coercion, mid-string non-triggering, embedded-quote escaping round-trip, and a payload test that asserts no cell in the generated CSV begins (after its opening quote) with a trigger character across every user-controlled field including the timeline.
  • The pre-existing frontend/src/test/exporters.test.ts (3 tests) still passes.
  • npx vitest run src/utils/exporters.test.ts8/8 passing.

Note on the issue's referenced paths

The issue lists apps/web/src/lib/comparisonExport.ts and apps/web/tests/compare-pricing.test.tsx, which do not exist in this repository (the frontend lives under frontend/, not apps/web/, and the compare page itself has no CSV export). The actual CSV export utility is frontend/src/utils/exporters.ts (used by the batch journey export), and that is the path fixed here. The same sanitization pattern is applicable to any future CSV export added to the compare page.

Changes

  • frontend/src/utils/exporters.ts — add sanitizeCsvCell + csvCell, route every dynamic cell through csvCell, export sanitizeCsvCell.
  • frontend/src/utils/exporters.test.ts — new regression tests (8 tests).

Closes #1300

generateBatchCSVString quoted/escaped cells but did not protect values
starting with =, +, -, @, tab or CR, so a malicious field value could
be executed as a spreadsheet formula on CSV open (CWE-1236). Add a
sanitizeCsvCell guard that prefixes trigger-leading values with a
single quote, applied via a shared csvCell helper to every dynamic
cell. Add regression tests.

Closes Nitya-003#1300
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

@openhands-agent is attempting to deploy a commit to the Nitya Gosain's projects Team on Vercel.

A member of the Team first needs to authorize it.

@saidai-bhuvanesh saidai-bhuvanesh changed the title probe fix(exporters): neutralize CSV formula injection (#1300) Aug 10, 2026
@github-actions github-actions Bot added the merge conflicts PR has merge conflicts label Aug 16, 2026
@github-actions

Copy link
Copy Markdown

@saidai-bhuvanesh, please resolve the commit so that it will be merged soon ......

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:ai-slop merge conflicts PR has merge conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Compare CSV export can execute spreadsheet formulas

3 participants