Skip to content

fix(cli): guard CSV cells against formula injection via jsonguard - #8

Merged
h4x0r merged 3 commits into
mainfrom
fix/csv-formula-guard
Aug 4, 2026
Merged

fix(cli): guard CSV cells against formula injection via jsonguard#8
h4x0r merged 3 commits into
mainfrom
fix/csv-formula-guard

Conversation

@h4x0r

@h4x0r h4x0r commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Sequencing: this PR alone does not close the CR bypass. It pins jsonguard = "0.2", which resolves to the published 0.2.4. That version guards =, +, - and @ in the first position, but not a lead-in hidden behind leading whitespace — "\r=1+1" still reaches the cell unguarded. That gap is fixed in jsonguard#5 and arrives here only once it merges and release-plz publishes 0.2.5. Merging this PR first is correct and strictly improves matters; just don't read it as closing the whole class.

The defect

cli/src/lib.rs rendered every CSV cell through a private csv_escape that applied RFC 4180 quoting and nothing else. Its 26 call sites feed it values read straight out of the evidence file — carved column values, fragment cells, snapshot labels, anomaly notes. A stored value beginning with =, +, - or @ therefore reached the examiner's spreadsheet as a live formula.

Two smaller gaps in the same function: a lone \r never triggered quoting (the predicate tested ,, " and \n only), and bidi-override and C0 control characters passed straight through into the cell.

The fix

Adopt jsonguard::csv_field — the fleet's shared sanitizer — instead of re-implementing the guard. jsonl and xlsx output are untouched.

Commits

  • RED c70d3f5 — tests asserting no carve-CSV or audit-CSV cell begins with a formula lead-in. Confirmed failing before the fix:

    unguarded formula cell "=cmd|'/c calc'!A1" in row:
    3,128,1,freelist-page,0.90,=cmd|'/c calc'!A1
    
    unguarded formula cell "=cmd|'/c calc'!A1" in row:
    LOW,SQLITE-FREELIST-NONEMPTY,free_pages=4,=cmd|'/c calc'!A1
    
  • GREEN a9f9597 — migrate to jsonguard::csv_field; add the jsonguard = "0.2" workspace dependency.

Gate

cargo build, cargo test (542 tests, 70 suites), cargo clippy --all-targets -- -D warnings, cargo fmt --check — all clean.

🤖 Generated with Claude Code

h4x0r and others added 2 commits August 2, 2026 07:57
sqlite4n6's private csv_escape applies RFC 4180 quoting only; it never
neutralizes a spreadsheet formula lead-in. Every cell it renders comes out
of the evidence file — carved column values, fragment cells, anomaly
notes — and is therefore attacker-controlled.

A carved value beginning with =, +, - or @ reaches the examiner's
spreadsheet as a live formula.

These two tests fail on the current implementation:

    unguarded formula cell "=cmd|'/c calc'!A1" in row:
    3,128,1,freelist-page,0.90,=cmd|'/c calc'!A1

    unguarded formula cell "=cmd|'/c calc'!A1" in row:
    LOW,SQLITE-FREELIST-NONEMPTY,free_pages=4,=cmd|'/c calc'!A1

RED commit: tests only, no implementation change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace the private csv_escape body with the fleet's shared
jsonguard::csv_field, which applies RFC 4180 quoting *and* neutralizes a
leading =, +, - or @ with an apostrophe. It also closes two gaps the local
escaper had: a lone CR never triggered quoting, and bidi-override and C0
control characters passed straight through into the cell.

Every value on this path is carved out of the evidence file — column
values, fragment cells, anomaly notes — so all of it is attacker
controlled.

Adopting the shared sanitizer rather than re-implementing the guard keeps
one definition of "safe cell" across the fleet. The jsonl and xlsx views
are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@socket-security

socket-security Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​jsonguard@​0.2.5N/AN/AN/AN/AN/A

View full report

@socket-security

socket-security Bot commented Aug 2, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

The lock pinned jsonguard 0.2.4, whose formula guard fires only on character 0.
A value leading with whitespace or an invisible format character therefore
reached the cell unguarded -- " =cmd" got neither quoting nor an apostrophe,
and the zero-width family (U+200B, U+200C, U+200D, U+2060, U+FEFF, U+180E,
U+00AD) behaved the same.

0.2.5 keys the guard on the first VISIBLE character, skipping Unicode
whitespace and the whole General_Category Cf range. The requirement was already
jsonguard = "0.2", a caret that admits 0.2.5, so this is a lock refresh with no
manifest change.

Full suite passes unchanged -- no expected-CSV fixture shifted, which was the
risk worth checking: values leading with whitespace now receive an apostrophe
they previously did not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@h4x0r
h4x0r marked this pull request as ready for review August 4, 2026 04:01
@h4x0r
h4x0r merged commit 7258e98 into main Aug 4, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant