CSV export: defuse formula injection in cells
Per Codex: source-controlled strings (name, feed_url, last_error, review_reason) could be read as formulas by spreadsheet apps if they start with = + - @. Add _csv_cell — prefixes such strings with an apostrophe; numbers pass through untouched (no risk, and avoids mangling negatives). Routed every exported cell through it. Test: a =HYPERLINK(...) source name is escaped, never bare. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -184,3 +184,17 @@ def test_export_audience_csv(tmp_path, monkeypatch):
|
||||
assert "metric,value" in body and "window_days,7" in body
|
||||
assert "date,visitors,opens" in body # daily time-series section
|
||||
assert TestClient(app).get("/api/admin/export/audience.csv").status_code == 401 # gated
|
||||
|
||||
|
||||
def test_export_sources_csv_escapes_formula_injection(tmp_path, monkeypatch):
|
||||
import os, sqlite3
|
||||
app, api = _make(tmp_path, monkeypatch, admin_email="boss@x.com")
|
||||
c = sqlite3.connect(os.environ["GOODNEWS_DB"])
|
||||
c.execute("UPDATE sources SET name = ?, review_flag = 1, review_reason = ? WHERE id = 1",
|
||||
('=HYPERLINK("http://bad")', '+danger'))
|
||||
c.commit(); c.close()
|
||||
tc = _signin(app, api, "boss@x.com")
|
||||
body = tc.get("/api/admin/export/sources.csv").text
|
||||
assert "'=HYPERLINK" in body # leading apostrophe defuses the formula (CSV may quote the cell)
|
||||
assert "'+danger" in body
|
||||
assert ",=HYPERLINK" not in body # never written as a bare, evaluable formula cell
|
||||
|
||||
Reference in New Issue
Block a user