Writing tests

Secrets the AI agent never sees

A step that signs in writes its credentials as {env:...} tokens. The values stay on your machine, in the environment or a git-ignored .env, and no AI provider ever receives them, not even while the agent records:

The step as you write it When I enter "{env:ADMIN_PASSWORD}" in the password field AI PROVIDER: TOKENS ONLY AI agent Claude, Codex, Antigravity, Cursor Saffron browser tools every browser call passes here Real browser your app, on your machine the prompt keeps the token browser_type text= '{env:ADMIN_PASSWORD}' 1 The recording and the trace keep the token 2 The value is put in; guards check the real URL or path fill('••••••••') the real value, here only snapshot, code, console, network, evaluate, downloads: the value inside 3 Every spelling of the value becomes the token again textbox "Password": {env:ADMIN_PASSWORD} The recording you commit "value": "{env:ADMIN_PASSWORD}" reports and traces: the token too Environment or .env The only place the value lives. .env is git-ignored; a real variable always wins. Every later run Replay reads the value at each action. No AI, no provider: nothing leaves your machine.
Agent-blind secrets: only tokens reach the AI provider; the value goes from your environment to the browser and nowhere else
  1. The prompt gives the agent the step as written, token included.
  2. The agent types the token: browser_type text='{env:ADMIN_PASSWORD}'. A token counts only where the browser types, picks, opens or uploads it; in browser_evaluate code it is refused. A page the agent opens with a token in its URL must be on the app's origin, or one the scenario itself writes: a page's text cannot lead the agent to send a secret anywhere else. A credential an API hands out during the session is shown to the agent as its {captured:name} token from then on.
  3. Every browser call, from Claude, Codex, Antigravity or Cursor alike, passes through Saffron's browser tools. There the recording and the trace take the call as the agent wrote it, with the token. Then the value is put in, and the safety checks judge the real value: a URL must be a web page, and an upload a file inside the project.
  4. The browser types the real value.
  5. Whatever comes back (the page snapshot, the code Playwright echoes, console messages, network requests and bodies, browser_evaluate results, Saffron's own replies about a download) has every spelling of the value turned back into the token before the agent sees it: as written, percent-encoded, JSON- and HTML-escaped, and quoted in code. Screenshots never reach the agent. The prompt's base URL, and a heal or refine session's quotes of replay's failure and the page's address, are masked the same way.

The recording therefore holds the token, and replay reads the value from the environment at each action, with no AI involved. Section 3 lists what masking cannot cover: values under three characters, a value the page shows transformed, and the same text elsewhere masked along with it.