T09 · Insecure Skill Coding Practices
- Location
index.js:139- Finding
CSV Formula Injection in Feedback Export
- Content
View full analysis
Vulnerability Details
File Location:
index.js, lines 139–153
Vulnerability Type: CSV formula injection
Risk Level: MediumVulnerable Code
javascript exportFeedback(format = 'json') { if (format === 'json') { return JSON.stringify(this.feedbackStore, null, 2); } else if (format === 'csv') { const headers = ['id', 'timestamp', 'conversationId', 'rating', 'query', 'response']; const rows = this.feedbackStore.map(f => [ f.id, f.timestamp, f.conversationId, f.rating, `"${(f.query || '').replace(/"/g, '""')}"`, `"${(f.response || '').replace(/"/g, '""')}"` ]); return [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); } return this.feedbackStore; }Technical Analysis
The CSV exporter serializes attacker-controlled feedback fields without neutralizing spreadsheet formula prefixes. Values beginning with
=,+,-, or@may be interpreted as formulas when the resulting CSV file is opened in spreadsheet applications.Escaping quotation marks and enclosing some fields in double quotes only provides CSV syntax escaping; it does not reliably prevent formula evaluation. Moreover,
conversationIdandratingare neither quoted nor escaped, which can also allow record or column structure manipulation through commas and line breaks.Because
collectFeedback()stores input fields without validation, an untrusted user can place a spreadsheet payload inquery,response,conversationId, orrating, and the payload will flow intoexportFeedback('csv').Attack Path
- An attacker submits feedback containing a formula-prefixed value, such as a payload beginning with
=, in a query, response, conversation identifier, or rating. FeedbackCollector.collectFeedback()stores the supplied value without validation or neutralization.- An operator exports the collected records by calling
exportFeedback('csv'). - The operator opens the generated CSV ...[truncated 822 chars]
- An attacker submits feedback containing a formula-prefixed value, such as a payload beginning with
- Remediation
View remediation
Remediation Suggestions
- Neutralize every string cell whose first non-whitespace character is
=,+,-, or@. Prefix such values with a single quote or another mitigation appropriate for the target spreadsheet software. - Apply the protection to all exported fields, including
id,timestamp,conversationId,rating,query, andresponse. - Use a maintained CSV serialization library that correctly handles quotation marks, commas, carriage returns, and line feeds.
- Validate structured fields such as
ratingagainst an explicit allowlist and constrain identifiers to an expected format. - Treat CSV exports as untrusted data and warn operators not to enable active content or external connections when opening them.
- Add regression tests covering formula prefixes, leading whitespace before formula characters, embedded commas, quotation marks, carriage returns, and line feeds.
A defensive cell-normalization function could follow this pattern:
javascript function safeSpreadsheetCell(value) { const text = String(value ?? ''); const neutralized = /^[\t\r\n ]*[=+\-@]/.test(text) ? `'${text}` : text; return `"${neutralized.replace(/"/g, '""')}"`; } const rows = this.feedbackStore.map(f => [ safeSpreadsheetCell(f.id), safeSpreadsheetCell(f.timestamp), safeSpreadsheetCell(f.conversationId), safeSpreadsheetCell(f.rating), safeSpreadsheetCell(f.query), safeSpreadsheetCell(f.response) ]);- Neutralize every string cell whose first non-whitespace character is
