From e6f1204b088f364d8367f0cb1d8b47699b2d7319 Mon Sep 17 00:00:00 2001 From: Yusrizal Ahmad Date: Mon, 22 Jun 2026 04:56:18 +0000 Subject: [PATCH] fix(#7224): sanitize CSV formula injection in export --- rustchain_export.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/rustchain_export.py b/rustchain_export.py index 561c0544f..ccfc10ea3 100644 --- a/rustchain_export.py +++ b/rustchain_export.py @@ -297,16 +297,35 @@ def db_exports(options: ExportOptions) -> dict[str, list[dict[str, Any]]]: } +_FORMULA_LEADING = frozenset(("=", "+", "-", "@")) + + +def _sanitize_csv_value(value: Any) -> str: + """Neutralize spreadsheet formula-leading text values.""" + if not isinstance(value, str): + return value + stripped = value.strip() + if stripped and stripped[0] in _FORMULA_LEADING: + return "'" + value + # Also catch tab/control-prefixed variants + for ch in ("\t", "\r", "\n", "\x00", "\x1b"): + if ch in stripped: + return "'" + value + return value + + def write_csv(path: Path, rows: list[dict[str, Any]], default_headers: list[str] | None = None) -> None: if rows: fieldnames = sorted({key for row in rows for key in row.keys()}) else: fieldnames = sorted(default_headers) if default_headers else [] + # Sanitize formula-leading values + sanitized = [{k: _sanitize_csv_value(v) for k, v in row.items()} for row in rows] with path.open("w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter(handle, fieldnames=fieldnames) if fieldnames: writer.writeheader() - writer.writerows(rows) + writer.writerows(sanitized) def write_json(path: Path, rows: list[dict[str, Any]]) -> None: