Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

csvfend

Catch CSV / formula injection before your export ships.

A CSV export feels like the safest feature you can build — you are just writing rows of text. But the moment a user opens that file in Excel or Google Sheets, any cell whose value starts with =, +, -, @ (or a tab / carriage return) is executed as a formula. An attacker who can influence one field — a display name, a note, a company — can smuggle in =HYPERLINK(...) to exfiltrate the row, =IMPORTXML(...) to phone home, or a DDE payload that runs a shell command on the machine that opens the file. This is CSV / formula injection (OWASP CSV Injection, CWE-1236), and it has real CVEs.

csvfend is a zero-config, language-agnostic static gate: it flags code that writes CSV/spreadsheet output but never neutralizes those trigger characters.

$ csvfend .

● 1 formula-injectable CSV write(s):
  exporters/users.py:14  CSV written from dynamic data with no formula-injection neutralization in this file.
      ↳ Any cell whose value starts with = + - @ (or a tab/carriage return) is treated as a
        formula when the file is opened in Excel or Google Sheets — an attacker can exfiltrate
        data via HYPERLINK/IMPORTXML or run commands via DDE. Prefix any such cell with a single
        quote ('), or reject it, before writing. [OWASP CSV Injection / CWE-1236]
      [CF001 Python/csv]

1 blocker · 0 warnings

Exit code 1 on a finding, so it drops straight into pre-commit or CI. It runs no code, makes no network calls, needs no configuration, and ships as one static Go binary.

How it works

The detection is file-scoped and honest about its heuristic. For each source file csvfend asks two questions:

  1. Does this file write CSV or spreadsheet output? — it contains a high-signal writer token: Python csv.writer / writerow / DictWriter / .to_csv(, Go csv.NewWriter / .WriteAll(, Node csv-stringify / fast-csv / papaparse / json2csv, PHP fputcsv, Java CSVWriter / writeNext (opencsv), Ruby CSV.open / CSV.generate.
  2. Does it neutralize formula-trigger characters? — it contains an explicit hardening marker: an escape_formula / sanitize_csv-style helper, a hand-rolled check for a leading = + - @ (e.g. startswith('=')), or a char-class of the trigger characters.

If (1) is true and (2) is false, that's the finding. The safe marker is a short-circuit: the instant csvfend sees neutralization, it trusts the whole file and stays quiet. Quoting is deliberately not treated as safe — QUOTE_ALL only wraps a cell in quotes; a leading = still executes.

Rules

Rule Severity What
CF001 blocker CSV written from dynamic data with no formula neutralization in the file
CF002 warning A spreadsheet (.to_excel / xlsx) write path with no neutralization — same bug class, often narrower exposure

Install

go install github.com/jay-tank/csvfend@latest

Or build from source: go build -o csvfend .

Usage

csvfend                 # scan the current directory
csvfend ./exporters     # scan a path
csvfend --strict        # treat CF002 warnings as failures too
csvfend --json          # machine-readable output

In CI

- run: go run github.com/jay-tank/csvfend@latest ./ --strict

Exit codes: 0 clean · 1 a formula-injectable CSV write (or any finding under --strict) · 2 usage error.

Suppressing false positives

csvfend reads each file on its own, so it can be wrong when your neutralization lives in a shared helper module. Two ways to suppress:

  • Per file: add a csvfend:ignore comment anywhere in the file.
  • Per path: list a path substring in a .csvfendignore file in the scanned root (one per line, # for comments).

Scope & honesty

csvfend answers one question well — "does this CSV/spreadsheet writer neutralize formula-trigger characters at all?" — across languages, as a fast static gate. It is a substring/line scanner, not a data-flow engine: it does not prove the written data is attacker-controlled, and because the safe marker is file-scoped, a file that neutralizes one writer is trusted for all writers in that file. That is a deliberate trade favoring low false-positives. When it is wrong, tell it so and move on.

License

MIT © Jay Tank

About

A zero-config, language-agnostic gate that statically flags CSV/formula injection - code writing user-controlled data to CSV/spreadsheet output without neutralizing the formula-trigger characters (= + - @ tab CR) that let a cell execute in Excel/Sheets. Single Go binary. Grounded in OWASP CSV Injection / CWE-1236.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages