fix(audit): H10 — neutralize CSV formula injection in expense + audit exports
Adds sanitizeCsvCell() (prefixes a quote when a cell starts with = + - @ tab/CR) and applies it to the audit-export escape() and the user-controlled free-text columns of the expense export before Papa.unparse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Papa from 'papaparse';
|
||||
import { eq, and, gte, lte, isNull, or, ilike } from 'drizzle-orm';
|
||||
|
||||
import { sanitizeCsvCell } from '@/lib/csv/sanitize-csv-cell';
|
||||
import { db } from '@/lib/db';
|
||||
import { expenses } from '@/lib/db/schema/financial';
|
||||
import { ports } from '@/lib/db/schema/ports';
|
||||
@@ -63,17 +64,21 @@ export async function exportCsv(portId: string, query: ListExpensesInput): Promi
|
||||
// quotes, newlines, BOM) that the hand-rolled escape-and-quote version
|
||||
// missed. Keyed objects let us define column order via `columns` and
|
||||
// get matching headers for free.
|
||||
// Neutralize spreadsheet formula triggers on user-controlled free-text
|
||||
// fields before papaparse serializes them (papaparse has no built-in
|
||||
// CSV-injection guard). Numeric/derived columns are not attacker-seeded
|
||||
// free text, so they keep their native values and formatting.
|
||||
return Papa.unparse(
|
||||
rows.map((r) => ({
|
||||
Date: r.expenseDate ? new Date(r.expenseDate).toISOString().split('T')[0] : '',
|
||||
Establishment: r.establishmentName ?? '',
|
||||
Category: r.category ?? '',
|
||||
Establishment: sanitizeCsvCell(r.establishmentName ?? ''),
|
||||
Category: sanitizeCsvCell(r.category ?? ''),
|
||||
Amount: r.amount,
|
||||
Currency: r.currency,
|
||||
'Amount USD': r.amountUsd ?? 'N/A',
|
||||
'Payment Status': r.paymentStatus ?? '',
|
||||
'Payment Method': r.paymentMethod ?? '',
|
||||
Description: r.description ?? '',
|
||||
'Payment Status': sanitizeCsvCell(r.paymentStatus ?? ''),
|
||||
'Payment Method': sanitizeCsvCell(r.paymentMethod ?? ''),
|
||||
Description: sanitizeCsvCell(r.description ?? ''),
|
||||
})),
|
||||
{
|
||||
columns: [
|
||||
|
||||
Reference in New Issue
Block a user