Problem
_delimited_value() in lib/python/base_cli/output.py:256-265 sanitizes CSV/TSV cells against
terminal injection but not against spreadsheet formula injection (CWE-1236). Its docstring states
the intent:
Delimited output is commonly piped into another process. Keep the normal csv module's quoting
behavior, but remove ANSI/control sequences so a producer cannot inject terminal presentation
or unexpectedly split a record across physical lines.
It calls _table_cell(), which strips ANSI escapes and Cc/Cf category characters only. A cell
beginning with =, +, -, or @ is emitted verbatim; when the CSV is later opened in Excel,
LibreOffice, or Google Sheets, the value is evaluated as a formula. render_records() is the
public, documented way to emit csv/tsv, and resolve_output_format() makes tsv the
default for non-TTY output, so redirected output — the case most likely to be opened in a
spreadsheet or fed to another tool — is exactly the exposed path.
For the operations/SRE audience this matters because cell values routinely come from untrusted or
semi-trusted sources: cloud resource tags and names, Kubernetes annotations, git author fields,
ticket titles, alert payloads.
Verified evidence
Reviewed 2026-09-30 at a58ec109349fa3f3d03eae5b0de078b39ea361a2.
render_records(
[{"name": "=cmd|'/C calc'!A1", "note": "+1+1", "x": "-2+3", "y": "@SUM(A1)"}],
requested_format="csv",
columns=[("NAME","name"),("NOTE","note"),("X","x"),("Y","y")],
stream=buf,
)
Output:
"=cmd|'/C calc'!A1,+1+1,-2+3,@SUM(A1)\n"
Every one of the four standard formula-injection prefixes survives.
Proposal
Neutralize formula-leading cells in delimited output only (never in json, yaml, ndjson, or
the terminal table, where the concern does not apply and mangling values would be wrong):
- prefix cells whose first character is one of
= + - @ \t \r with a single ', or
- wrap them so the csv module quotes them and the leading character is no longer first.
Make the behaviour explicit and overridable — a formula_guard: bool = True keyword on
render_records() — so a consumer producing machine-only CSV for its own parser can opt out with
an audited decision. Document it in docs/output-contracts.md next to the existing ANSI note.
Acceptance criteria
csv and tsv output neutralizes leading =, +, -, @, tab, and CR in every cell,
including header cells.
json, yaml, ndjson, and terminal table rendering are byte-for-byte unchanged.
- A regression test covers each prefix and asserts round-trip through
csv.reader still yields
the guarded value.
- The guard is documented, including how to disable it and why that is a security decision.
Non-goals
- Do not alter the csv module's quoting rules.
- Do not apply the guard to structured formats where values are not interpreted as formulas.
Problem
_delimited_value()inlib/python/base_cli/output.py:256-265sanitizes CSV/TSV cells againstterminal injection but not against spreadsheet formula injection (CWE-1236). Its docstring states
the intent:
It calls
_table_cell(), which strips ANSI escapes andCc/Cfcategory characters only. A cellbeginning with
=,+,-, or@is emitted verbatim; when the CSV is later opened in Excel,LibreOffice, or Google Sheets, the value is evaluated as a formula.
render_records()is thepublic, documented way to emit
csv/tsv, andresolve_output_format()makestsvthedefault for non-TTY output, so redirected output — the case most likely to be opened in a
spreadsheet or fed to another tool — is exactly the exposed path.
For the operations/SRE audience this matters because cell values routinely come from untrusted or
semi-trusted sources: cloud resource tags and names, Kubernetes annotations, git author fields,
ticket titles, alert payloads.
Verified evidence
Reviewed 2026-09-30 at
a58ec109349fa3f3d03eae5b0de078b39ea361a2.Output:
Every one of the four standard formula-injection prefixes survives.
Proposal
Neutralize formula-leading cells in delimited output only (never in
json,yaml,ndjson, orthe terminal table, where the concern does not apply and mangling values would be wrong):
= + - @ \t \rwith a single', orMake the behaviour explicit and overridable — a
formula_guard: bool = Truekeyword onrender_records()— so a consumer producing machine-only CSV for its own parser can opt out withan audited decision. Document it in
docs/output-contracts.mdnext to the existing ANSI note.Acceptance criteria
csvandtsvoutput neutralizes leading=,+,-,@, tab, and CR in every cell,including header cells.
json,yaml,ndjson, and terminal table rendering are byte-for-byte unchanged.csv.readerstill yieldsthe guarded value.
Non-goals