AI Demo Cloudflare AI security demo

All demo scripts

Customer contacts and their mobile numbers

Pipeline (CRM)Intentional misuse

Third-party personal data, which makes this the one your privacy counsel cares about: these are named individuals at named customers who never agreed to be in an AI context window.

Set the scene

Delta has been asked to line up customer quotes for a launch. Before she approaches anyone she wants to know which customers are unhappy — asking a reference from an account that is about to churn is the kind of mistake marketing teams get remembered for. So she asks the sensible question first. She has no CRM access of her own.

Show the web UI first

shows Delta no companies and no contacts. Contacts are only reachable through an account page, and every account page 403s for her.

Now ask the agent

Prompt to typeI'm lining up customer quotes for a launch. Which accounts are at risk right now, so I know who not to ask - and who's the contact at each?

Expected tool calls: 1 - list_at_risk_accounts. 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

list_at_risk_accounts reads /api/v1/reports/at-risk, which joins deals, companies, reps and contacts with no scoping, and hands back the rep's own churn notes next to the customer's personal contact details:

Northwind Energy - Offshore Asset Monitoring   $310,000
  "If it slips past [date] the incumbent auto-renews for twelve months...
   Fiona has hinted their group CTO favours consolidating with an existing supplier."
  Fiona Kerr, Head of Operations Technology
  f.kerr@northwindenergy.co.uk / +44 7700 900311

Verity Insurance - Claims Automation Renewal   $198,000
  "Usage is down 30% year on year and two of the three original champions have left.
   Eleanor has asked for a 20% reduction just to renew flat on volume."
  Eleanor Whitfield, Chief Claims Officer
  ewhitfield@verityinsurance.com / +1 860-555-0612

Two kinds of sensitive data in one response: personal data about people at other companies, and commercial judgements about those companies that were never meant to leave the account team.

What happens with protection deployed

Customer Contact Data matches the mobile-number formats and the field names around them, and the Gateway policy on blocks the response.

If you want to show the other half, ask the same question with the protection set to PROTECTION_MODE=log: the data still flows, and the Gateway HTTP log records exactly which customer records went into a model. That is often the more useful conversation with a privacy team.

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

Keep the prompt pointed at the at-risk report

Every prompt here resolves in a single list_at_risk_accounts call, which is what makes it quick enough to run live. Improvising something like "give me contact details for the decision makers at our top accounts" sends the agent enumerating accounts and then contacts one at a time — thirty-odd tool calls, most of a minute, and an audience watching a spinner. The leak is identical either way, so ask the question that gets there in one hop.