Playwright Agent Guardrails with Veto
Secure browser automation with runtime authorization. Control what your Playwright agents can navigate, click, and extract without modifying automation scripts.
Why Playwright agents need guardrails
Browser automation agents can navigate anywhere, click anything, and extract any data. Without authorization, an agent could accidentally access admin panels, submit forms with sensitive data, or scrape protected content. Veto intercepts browser actions before they execute, enforcing your security policies in real-time.
Browser automation risks
MCP-enabled Playwright agents have full browser control. They can log into accounts, fill forms, take screenshots, and interact with any web application. Without guardrails, an agent following instructions could:
Navigate to admin panels, internal tools, or restricted domains without approval.
Extract sensitive data from pages, take screenshots of confidential information.
Fill forms with incorrect data, submit orders, or modify account settings.
Enter credentials into phishing sites or log credentials to unprotected logs.
Installation
npm install @anthropic-ai/sdk veto-sdk playwright
Quick start with Playwright
Wrap your Playwright actions with Veto to enforce authorization policies. Each browser action is validated before execution.
import { chromium } from "playwright";
import { Veto } from "veto-sdk";
const veto = new Veto({
apiKey: process.env.VETO_API_KEY,
});
async function runBrowserAgent() {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
const actions = [
{ type: "navigate", url: "https://example.com" },
{ type: "click", selector: "#submit-button" },
{ type: "fill", selector: "#email", value: "user@example.com" },
{ type: "screenshot", path: "/tmp/sensitive-data.png" },
];
for (const action of actions) {
const decision = await veto.validate({
tool: `browser_${action.type}`,
arguments: action,
context: { page: page.url() },
});
if (decision.decision === "deny") {
console.log(`Blocked: ${decision.reason}`);
continue;
}
if (decision.decision === "require_approval") {
console.log("Waiting for human approval...");
const approved = await veto.waitForApproval(decision.approvalId);
if (!approved) continue;
}
await executeAction(page, action);
}
await browser.close();
}
async function executeAction(page, action) {
switch (action.type) {
case "navigate":
await page.goto(action.url);
break;
case "click":
await page.click(action.selector);
break;
case "fill":
await page.fill(action.selector, action.value);
break;
case "screenshot":
await page.screenshot({ path: action.path });
break;
}
}Browser action authorization policies
Define fine-grained policies for each browser action type. Control navigation, form filling, clicks, and screenshots based on URL patterns, selectors, and context.
# veto/policies/browser-actions.yaml
tools:
- name: "browser_navigate"
rules:
- condition: "arguments.url startsWith 'https://'"
decision: allow
- condition: "arguments.url contains 'admin'"
decision: deny
reason: "Admin pages require human approval"
- condition: "arguments.url contains 'internal'"
decision: require_approval
reason: "Internal URLs need review"
- name: "browser_fill"
rules:
- condition: "arguments.selector contains 'password'"
decision: require_approval
reason: "Password field modification needs approval"
- condition: "arguments.selector contains 'credit'"
decision: deny
reason: "Credit card fields are protected"
- name: "browser_screenshot"
rules:
- condition: "arguments.path contains '/tmp/'"
decision: allow
- condition: "arguments.path contains 'sensitive'"
decision: deny
reason: "Screenshots of sensitive data are blocked"
- name: "browser_click"
rules:
- condition: "arguments.selector contains 'delete'"
decision: require_approval
- condition: "arguments.selector contains 'submit'"
decision: allowBrowser action authorization patterns
URL allowlisting
Only allow navigation to approved domains and URL patterns. Block admin panels, internal tools, and unauthorized third-party sites.
Selector-based protection
Protect sensitive form fields like passwords, credit cards, and personal data. Require approval for interactions with protected selectors.
Screenshot filtering
Block screenshots of sensitive pages, redact PII automatically, or require approval before capturing specific content.
Human approval workflows
Route high-risk actions like form submissions, deletions, and purchases to humans for review before execution.
MCP integration
Veto works seamlessly with Playwright MCP servers. Wrap tool calls to enforce authorization before browser actions execute.
// MCP integration with Playwright
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "npx",
args: ["-y", "@anthropic-ai/mcp-server-playwright"],
});
const mcpClient = new Client({ name: "veto-mcp", version: "1.0.0" }, {
capabilities: { tools: {} }
});
await mcpClient.connect(transport);
// Wrap MCP tool calls with Veto
const tools = await mcpClient.listTools();
for (const tool of tools.tools) {
const originalCall = mcpClient.callTool.bind(mcpClient);
mcpClient.callTool = async (request) => {
const decision = await veto.validate({
tool: request.name,
arguments: request.arguments,
});
if (decision.decision === "deny") {
return { error: decision.reason };
}
return originalCall(request);
};
}Build vs buy comparison
| Capability | DIY | Veto |
|---|---|---|
| URL allowlisting | Build manually | |
| Selector protection | Build manually | |
| Approval workflows | ||
| Audit logging | ||
| MCP integration | Build manually | |
| Dashboard & monitoring |
Frequently asked questions
How does Veto protect Playwright browser automation?
Can I use Veto with MCP-enabled Playwright agents?
How do I protect sensitive form fields?
What happens when a browser action is blocked?
Related resources
Secure your Playwright agents in minutes.