T09 · Insecure Skill Coding Practices
Warning
- Location
scripts/cat-food-core.mjs:342- Finding
Spreadsheet Formula Injection in CSV Exports
- Content
View full analysis
[pet.id, pet])); const petIds = new Set([ ...backup.pets.map((pet) => pet.id), ...backup.feed_records.map((record) => record.pet_id), ...backup.weight_records.map((record) => record.pet_id), ...backup.water_records.map((record) => record.pet_id), ]); const rows = []; for (const petId of [...petIds].sort()) { const pet = petById.get(petId) ?? { id: petId, name: '' }; ``` ```js rows.push({ pet_id: petId, pet_name: pet.name, date, ``` ```js export function toCSV(rows) { const headers = rows.length === 0 ? SUMMARY_HEADERS : Object.keys(rows[0]); const lines = rows.map((row) => headers.map((header) => JSON.stringify(row[header] ?? '')).join(',')); return [headers.join(','), ...lines].join('\n'); } ``` ### Technical Analysis The backup schema accepts arbitrary strings for fields such as `pet.id` and `pet.name`. These values are copied into summary rows and then serialized as CSV cells using `JSON.stringify`. JSON quoting provides delimiter and quote escaping, but it is not spreadsheet formula neutralization. Spreadsheet applications may interpret a cell beginning with `=`, `+`, `-`, or `@` as a formula even when that cell is quoted in the CSV file. Consequently, a crafted backup can inject spreadsheet expressions into `pet_id` or `pet_name`. The issue becomes exploitable when a generated CSV file is opened in software that evaluates formulas from imported CSV cells. Actual behavior depends on the spreadsheet product and its security settings. ### Attack Path 1. An attacker creates or modifies a backup containing ...[truncated 1679 chars]- Remediation
View remediation
{ if (typeof value !== 'string') return value; return /^[\s]*[=+\-@]/.test(value) ? `'${value}` : value; }; export function toCSV(rows) { const headers = rows.length === 0 ? SUMMARY_HEADERS : Object.keys(rows[0]); const lines = rows.map((row) => headers .map((header) => JSON.stringify(neutralizeSpreadsheetFormula(row[header] ?? ''))) .join(',') ); return [headers.join(','), ...lines].join('\n'); } ``` If preserving the exact original text is required, consider offering separate machine-oriented CSV and spreadsheet-safe CSV modes, with the safe mode used by default. ]]>
