Sample XLSX file (formula injection)
Workbook whose cells contain =, +, - and @ prefixed strings and a HYPERLINK formula.
↓ Download formula-injection.xlsx| Filename | formula-injection.xlsx |
| Format | XLSX |
| Size | 1.7 KB (1,733 bytes) |
| MIME type | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| SHA-256 | f3a7e7c4d1b67e339a5e918da7a81a3ac6bdb52f9581f63586774e1dcf8ed10b |
Handle it in code
Each snippet below was run against this exact file with Python 3.14.4 and Node 24.15.0 when this page was built, and the output shown is what it printed.
Python
Text cells starting with =, +, - or @ are harmless inside Excel, but become live formulas when your app exports them to CSV. Scan the sheet XML directly: an .xlsx is a ZIP, and cell text is either inline or in the shared-strings table.
import re
import zipfile
import xml.etree.ElementTree as ET
M = "{http://schemas.openxmlformats.org/spreadsheetml/2006/main}"
TRIGGERS = ("=", "+", "-", "@", "\t", "\r")
def text_of(el):
return "".join(t.text or "" for t in el.iter(M + "t"))
found = 0
with zipfile.ZipFile("formula-injection.xlsx") as z:
names = z.namelist()
shared = []
if "xl/sharedStrings.xml" in names:
shared = [text_of(si) for si in ET.fromstring(z.read("xl/sharedStrings.xml")).iter(M + "si")]
for sheet in sorted(n for n in names if re.fullmatch(r"xl/worksheets/sheet\d+\.xml", n)):
for c in ET.fromstring(z.read(sheet)).iter(M + "c"):
ref, kind, formula = c.get("r"), c.get("t"), c.find(M + "f")
if formula is not None:
print(f"{ref}: formula ={formula.text} (runs when the file is opened)")
continue
if kind == "s":
text = shared[int(c.find(M + "v").text)]
elif kind == "inlineStr":
text = text_of(c)
else:
continue
if text.startswith(TRIGGERS):
print(f"{ref}: text {text!r} turns into a formula when exported to CSV")
found += 1
print(f"{found} text cell{'s' * (found != 1)} to neutralise before a CSV export" if found else "no formula-like text")Run against this file, it prints: B2: text '=1+1' turns into a formula when exported to CSV
B3: text '+1+1' turns into a formula when exported to CSV
B4: text '@SUM(1+1)' turns into a formula when exported to CSV
B5: formula =HYPERLINK("https://example.test","click") (runs when the file is opened)
3 text cells to neutralise before a CSV export