fix(cli): guard CSV cells against formula injection via jsonguard - #8
Merged
Conversation
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>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
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. |
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
marked this pull request as ready for review
August 4, 2026 04:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
cli/src/lib.rsrendered every CSV cell through a privatecsv_escapethat 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
\rnever triggered quoting (the predicate tested,,"and\nonly), 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.jsonlandxlsxoutput 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:GREEN
a9f9597— migrate tojsonguard::csv_field; add thejsonguard = "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