# Security model

> How OpenWAF Tasks isolates your script and the agent, keeps the model credential out of reach and handles prompt injection from web pages.

A task runs untrusted inputs on purpose: your code, a model, and web pages written by anyone. The design assumes any of them can misbehave.

## The sandbox

Each run's script or agent loop runs in its own container that:

- has **no network** at all; it reaches the browser, the runner and the model only through local sockets the runner serves;
- has a **read-only file system** with a small temporary directory;
- drops **all Linux capabilities**, cannot gain privileges and runs as an unprivileged user;
- has limits on CPU, memory, processes and time (`max_run_minutes`);
- holds **no secrets**: no API token, browser key or provider credential.

The DevTools connection it gets cannot close the browser or open further debugging channels. The read-only guard sits on a separate connection it cannot reach.

## The model credential

An agent task calls its model through [OpenWAF Egress](https://developers.openwaf.dev/egress/cloud/agents/). The task has its own Egress agent; its token is rotated when a run starts and again when it ends, so it is only valid for that run and is never stored. The runner adds it to the model requests outside the container. Your provider credential stays in Egress and never reaches the runner. Egress applies its injection and data-leak checks to every call.

## Prompt injection

Page content is data. The agent receives it between explicit untrusted-content delimiters, and its instructions tell it never to follow instructions found in pages. That is the first layer, and it can fail. The others do not depend on the model:

1. The browser is read-only while the agent runs: a write it is tricked into making is blocked and logged.
2. The browser only reaches the task's allowed domains.
3. Whatever the agent proposes waits for a person, who sees the exact steps and the target.
4. Only the approved steps run, by the runner, on the action's domain.

We test this against a page that tells agents to ignore their goal and post elsewhere: the request is blocked, the proposal stays pending, and a rejected proposal never runs.

## What you can do

- Keep `allow` as narrow as the task needs.
- Read every step before approving, not only the summary. Check the target domain.
- Use a dedicated browser profile per task, logged in only where the task needs it.
- Set stricter `action_limits` for sites where a mistake would be costly.
