{
  "id": "web.browser.v1",
  "name": "Browser Access Policy",
  "description": "Pre-action governance for browser automation and web interactions. Enforces URL allowlists, blocked domains, action restrictions (click, type, navigate), screenshot limits, and interaction patterns to prevent unauthorized web activity and data exfiltration.",
  "version": "1.0.0",
  "status": "active",
  "requires_capabilities": ["web.browser"],
  "min_assurance": "L0",
  "limits_required": ["allowed_domains"],
  "required_fields": ["action"],
  "optional_fields": ["url", "selector", "value", "screenshot"],
  "enforcement": {
    "domain_allowlist_enforced": true,
    "blocked_domains_enforced": true,
    "action_restrictions_enforced": true,
    "screenshot_rate_enforced": true,
    "form_submission_enforced": true
  },
  "mcp": {
    "require_allowlisted_if_present": true
  },
  "advice": [
    "Use domain allowlists to restrict browser navigation to trusted sites only",
    "Block known phishing, malware, and data exfiltration domains",
    "Restrict browser actions (e.g., block form submissions for read-only agents)",
    "Limit screenshot frequency to prevent excessive data capture",
    "Monitor for suspicious patterns (e.g., automated form filling to many sites)",
    "Log all browser actions with URLs and selectors for audit trail",
    "Consider blocking authentication-related actions (login forms, password fields)",
    "Block navigation to private IP ranges and localhost",
    "Subscribe to status webhooks for instant suspend on suspicious activity",
    "Use action-specific limits (e.g., max screenshots per hour, max navigations)",
    "Audit logs should capture action type, URL, selector, and timestamp"
  ],
  "required_context": {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": ["action"],
    "properties": {
      "action": {
        "type": "string",
        "enum": ["navigate", "click", "type", "screenshot", "scroll", "wait", "extract", "close"],
        "description": "Browser action to perform"
      },
      "url": {
        "type": "string",
        "minLength": 1,
        "maxLength": 8192,
        "description": "URL to navigate to (for navigate action)"
      },
      "selector": {
        "type": "string",
        "maxLength": 1024,
        "description": "CSS selector for element interaction"
      },
      "value": {
        "type": "string",
        "maxLength": 10000,
        "description": "Value to type or submit (for type/submit actions)"
      },
      "screenshot": {
        "type": "boolean",
        "description": "Whether to capture screenshot after action"
      },
      "timeout": {
        "type": "integer",
        "minimum": 0,
        "maximum": 300000,
        "description": "Action timeout in milliseconds (max 5 minutes)"
      },
      "mcp_servers": {
        "type": "array",
        "items": { "type": "string" },
        "description": "MCP servers being used in this request"
      },
      "mcp_tools": {
        "type": "array",
        "items": { "type": "string" },
        "description": "MCP tools being used in this request"
      },
      "mcp_server": {
        "type": "string",
        "description": "Single MCP server being used (backward compatibility)"
      },
      "mcp_tool": {
        "type": "string",
        "description": "Single MCP tool being used (backward compatibility)"
      },
      "mcp_session": {
        "type": "string",
        "description": "MCP session identifier for audit trail (optional)"
      }
    }
  },
  "evaluation_rules_version": "1.0",
  "evaluation_rules": [
    {
      "name": "domain_allowlist",
      "type": "expression",
      "condition": "!context.url || limits.allowed_domains.includes('*') || limits.allowed_domains.some(d => context.url.includes(d))",
      "deny_code": "oap.domain_not_allowed",
      "description": "Browser navigation domain must be in allowed list",
      "message": "Domain not in allowed list. Configure passport limits.allowed_domains to grant access."
    },
    {
      "name": "blocked_domains",
      "type": "custom_validator",
      "validator": "validateBrowserBlockedDomains",
      "deny_code": "oap.domain_blocked",
      "description": "URL must not be in blocked domains (phishing, malware, data exfiltration)",
      "message": "Navigation to this domain is blocked (potential security risk)."
    },
    {
      "name": "private_ip_check",
      "type": "custom_validator",
      "validator": "validateBrowserPrivateIP",
      "deny_code": "oap.private_ip_blocked",
      "description": "Navigation to private IP ranges blocked (if configured)",
      "message": "Navigation to private IP addresses (localhost, 192.168.*, 10.*, etc.) is blocked."
    },
    {
      "name": "action_restriction",
      "type": "expression",
      "condition": "!limits.allowed_actions || limits.allowed_actions.includes(context.action)",
      "deny_code": "oap.action_not_allowed",
      "description": "Browser action must be in allowed list (if configured)",
      "message": "Browser action not allowed. Configure passport limits.web.browser.allowed_actions to grant access."
    },
    {
      "name": "form_submission_check",
      "type": "custom_validator",
      "validator": "validateBrowserFormSubmission",
      "deny_code": "oap.form_submission_blocked",
      "description": "Form submissions blocked if limits.allow_form_submission is false",
      "message": "Form submissions are not allowed for this agent."
    },
    {
      "name": "screenshot_rate_limit",
      "type": "custom_validator",
      "validator": "validateBrowserScreenshotRate",
      "deny_code": "oap.rate_limit_exceeded",
      "description": "Screenshot captures must not exceed rate limit",
      "message": "Screenshot rate limit exceeded. Too many screenshots in the current time window."
    },
    {
      "name": "navigation_rate_limit",
      "type": "custom_validator",
      "validator": "validateBrowserNavigationRate",
      "deny_code": "oap.rate_limit_exceeded",
      "description": "Browser navigations must not exceed rate limit (navigations per hour)",
      "message": "Browser navigation rate limit exceeded. Too many page loads in the current time window."
    }
  ],
  "cache": {
    "default_ttl_seconds": 60,
    "suspend_invalidate_seconds": 30
  },
  "deprecation": null,
  "created_at": "2026-03-01T00:00:00Z",
  "updated_at": "2026-03-01T00:00:00Z"
}
