AI Demo Cloudflare AI security demo

All demo scripts

Pay against performance, for every rep

WorkWeek (HR)Pipeline (CRM)Cross-appIntentional misuse

Neither app can answer this on its own, and neither would be allowed to. The agent has both, so it joins them.

Set the scene

Delta has been asked to write an internal piece about how the sales team performed this year. Someone suggests "just ask the assistant to pull the numbers".

Show the web UI first

In Delta sees no compensation but her own. In she sees no deals at all. There is no report anywhere in the company that puts the two together - the only people who could build one are the CFO and the VP of People, and they would do it in a spreadsheet they password-protect.

Now ask the agent

Prompt to typeTake the three reps who have closed the most this year and compare each one's base salary to what they closed.

Expected tool calls: 5 - the pipeline summary, the employee list, then one file read per rep. Far more than that means the agent has started enumerating - stop it and re-read the prompt.

Variations that work the same way:

What happens with no protection

The agent does this in three steps, all of them reasonable-looking on their own:

  1. crm_list_reps and crm_get_pipeline_summary for who the reps are and what they have closed, per owner.
  2. hr_list_employees to match those people to employee records.
  3. hr_get_employee_file per person for the compensation history.

Out comes a table that has never existed inside this company:

Rep                Base salary   Closed YTD    Ratio
Edison Schaden     $166,079      $780,000      4.7x
Nestor Herzog      $118,400      $265,000      2.2x
Imani Hauck        $121,600      $520,000      4.3x
Ramona Flatley     $109,750      $0            0.0x   <- contractor, EMEA
...
Why this is the one executives react to

Each source was "just" a leaky endpoint. The output is a performance-and-pay ranking of named employees, produced by someone in Marketing, in about nine seconds. Ask the room what happens when that table gets pasted into a channel.

What happens with protection deployed

Two independent policies fire, at different hostnames, in the same conversation: the rule on the forecast data, and the rule on the employee files. Either one alone prevents the join; together they make the point that the control is per-source, so you do not have to anticipate the combination.

If the model has already seen fragments from an earlier turn, AI Gateway DLP catches the completion as it is written, because a table of names against salaries matches Employee PII on its way back.

Watch the tool stream while it runs

Leave the client's tool list expanded rather than waiting for the answer. Each blocked call is marked the moment it happens, so a slow prompt becomes an asset: the audience watches the control fire repeatedly, in real time, instead of staring at a spinner and then reading a conclusion.

Execute  const employees = await tools["ai-demo"].hr_list_employees({ query: 'Rozella Lynch' });
Execute  const employees = await tools["ai-demo"].hr_list_employees({ query: 'Rozella' });
         ! Blocked by Cloudflare Gateway
           this MCP tool call carried data matching a DLP profile for this demo.
           rule_id: 1cbf53fd-2bc2-49b7-8618-26943aa72b5d
           request_id: 31763e3ad30000c552a6f34400000001
Execute  const employees = await tools["ai-demo"].hr_list_employees({ query: 'Lynch' });
         ! Blocked by Cloudflare Gateway
Execute  const employees = await tools["ai-demo"].hr_list_employees({});
         ! Blocked by Cloudflare Gateway

Point at three things in that stream:

The block text is yours

That wording comes from the Gateway rule's block reason, set in scripts/protection-payloads.mjs - it is not a Cloudflare default. Each of the four rules says what was blocked and that the application itself was not changed, because this is one of the few places the audience reads your words inside the attacker's tooling.

Then expand the agent's own reasoning

When the run finishes, expand the model's thinking - most clients hide it behind a Thought or Reasoning toggle. The model narrates the control working, in its own words. It is persuasive precisely because nobody wrote it: the audience is reading the agent explain why it failed.

I have already tried to use hr_get_employee and hr_get_employee_file for employee ID 1 (Nikita Crist) — but these calls were blocked by a Cloudflare Gateway DLP (Data Loss Prevention) rule. […] The DLP rules are clearly working to prevent me from accessing and returning this sensitive information.

Verbatim from a run of the first script, with the model's tool names intact.

Three things to draw out of whatever your run produces:

Careful what you promise here

Reasoning text is generated, not a log. A model can describe a block it did not experience, or stay silent about one it did, and some models expose no reasoning at all. Show it because it is vivid, then move to the Gateway and portal logs for the record that is actually authoritative.

Where to show the evidence