Integrations/Playwright

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.

Playwright agent guardrails, Playwright MCP security, Playwright automation authorization, browser automation security, AI agent browser control

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:

Unauthorized navigation

Navigate to admin panels, internal tools, or restricted domains without approval.

Data exfiltration

Extract sensitive data from pages, take screenshots of confidential information.

Form manipulation

Fill forms with incorrect data, submit orders, or modify account settings.

Credential exposure

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: allow

Browser action authorization patterns

1

URL allowlisting

Only allow navigation to approved domains and URL patterns. Block admin panels, internal tools, and unauthorized third-party sites.

2

Selector-based protection

Protect sensitive form fields like passwords, credit cards, and personal data. Require approval for interactions with protected selectors.

3

Screenshot filtering

Block screenshots of sensitive pages, redact PII automatically, or require approval before capturing specific content.

4

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);
  };
}
Full MCP integration guide

Build vs buy comparison

CapabilityDIYVeto
URL allowlistingBuild manually
Selector protectionBuild manually
Approval workflows
Audit logging
MCP integrationBuild manually
Dashboard & monitoring

Frequently asked questions

How does Veto protect Playwright browser automation?
Veto intercepts browser actions before they execute. Each navigate, click, fill, or screenshot call is validated against your authorization policies. Actions can be allowed, blocked, or routed to human approval based on URL patterns, selectors, and context.
Can I use Veto with MCP-enabled Playwright agents?
Yes. Veto integrates directly with MCP tool calls. Wrap your MCP client to validate tool requests before execution. The agent receives a clear response when actions are blocked or require approval.
How do I protect sensitive form fields?
Define selector-based policies that match sensitive fields like passwords, credit cards, and personal data. Configure rules to deny, allow, or require approval for interactions with protected selectors. The agent receives the policy decision without knowing the underlying logic.
What happens when a browser action is blocked?
The action is intercepted before execution. The agent receives a configurable response explaining why the action was blocked. You can provide fallback values, error messages, or route to human approval. All decisions are logged with full context for audit trails.

Related resources

Secure your Playwright agents in minutes.