--- name: Role-Gate All Zapier Writes tags: - zapier - role-gate-writes - ingress - soc2 - gdpr-ccpa publishedAt: 2026-07-12 description: | # zapier / role-gate-writes **Direction:** ingress (`tool_pre_invoke`) **Default:** deny writes for non-approved groups, allow reads for everyone **Package:** `zapier.ingress.role_gate_writes` ## What it does Zapier MCP is an aggregator: one connector proxies actions across 9,000+ apps, and every create/update/delete/send funnels through a small, predictable naming surface. In agentic (dynamic tool discovery) mode, **all writes go through a single meta-tool**, `execute_zapier_write_action` — a Gmail delete and a Salesforce record update both arrive as the same tool name. In classic (manual configuration) mode, each enabled action is its own `_` tool whose name carries a write verb (`send_`, `create_`, `update_`, `delete_`, `remove_`) — e.g. `gmail_send_email`, `google_sheets_create_row`. This policy denies all of them unless the caller's IdP `groups` claim includes `automation-writers`, while reads (`execute_zapier_read_action`, discovery/list meta-tools, and classic `find_`/`get_` tools) stay open to everyone. The result is a **read-only-by-default posture** for the whole aggregator: one rule fences writes across every proxied app. Missing identity claims fail closed: a caller with no `groups` claim (or no claims at all) is not an approved writer and is denied all writes — reads remain available. ## Compliance alignment - **SOC 2 CC6.1 / CC6.3** — supports logical access security and role-based least privilege: write access to every app behind the Zapier connector is granted only to an authorized IdP group, evaluated per call. **CC6.2** — supports credential de-provisioning effect: removal from the IdP group revokes write access on the next call, with no per-app work. - **PCI DSS 7.2.1 / 7.2.2** — supports a least-privilege access model over the aggregator's reach into cardholder-adjacent apps (payment, invoicing, commerce actions all transit the same write funnel). **7.2.5** — supports least privilege for the application account: the broad per-app OAuth grants held Zapier-side are narrowed to read-only on the MCP path for non-writers. - **HIPAA §164.308(a)(4)** — supports information access management for connectors that can reach PHI-bearing apps; **§164.502(b)/§164.514(d)** — supports minimum-necessary, role-based limits (reads only, unless the role warrants writes); **§164.312(a)(1)** — supports access control decided on per-call, per-user identity. **§164.308(a)(3)** — supports workforce-security termination effect via live IdP claims. - **GDPR Art. 25** — supports data protection by default on the agent channel: the aggregator's default capability is read-only. **Art. 29 / 32(4)** — supports processing only on the controller's instructions: agents acting for unapproved users cannot mutate personal data in downstream processors. **CCPA §1798.100(e)** — supports reasonable security over consumer data reachable through the connector. - **SOX (ITGC — access to programs and data)** — supports least-privilege access to financial systems reachable through Zapier (accounting, billing, ERP actions); **SoD (COSO P10)** — supports initiate/approve separation by keeping record-mutation ability out of unapproved hands. ## Tool name matching Case-insensitive, on the tool-name suffix (the DTwo gateway prefixes tool names with the configured MCP server name, e.g. `zapier-mcp-execute_zapier_write_action`, and that prefix is not standardized — suffix matching keeps the policy portable): - **Agentic write funnel** — name ends with `execute_zapier_write_action` (exact meta-tool name, verified against Zapier's official MCP docs). - **Classic writes** — name contains a write-verb substring: `send_`, `create_`, `update_`, `delete_`, `remove_`. Everything else passes: `execute_zapier_read_action`, `list_enabled_zapier_actions`, `discover_zapier_actions`, `list_zapier_skills`, `get_zapier_skill`, `get_configuration_url`, and classic `find_`/`get_` tools (e.g. `quickbooks_online_find_customer`). Note the verb-substring rule also catches the agentic skill meta-tools (`create_zapier_skill`, `update_zapier_skill`, `delete_zapier_skill`) and `send_feedback` — intentional, since all four are writes (skill changes persist instructions future sessions auto-load). See Known limitations for the composition consequence. Verify the exact names your gateway sends with the dump-input debug technique before relying on this in production. ## Argument shape None. This policy decides purely on the tool name and the caller's identity claims — it never inspects `input.payload.args`, so it is immune to argument-shape drift in Zapier's tools (including the underdocumented `execute_zapier_*_action` envelope). Identity is read via `object.get(input.subject, "claims", {})` and `object.get(claims, "groups", [])`; the `groups` claim is expected to be an **array of strings** as emitted by the tenant's IdP. ## Examples ### Allowed — read funnel, any caller (no claims needed) ```jsonc { "input": { "action": "tool_pre_invoke", "resource": { "name": "zapier-mcp-execute_zapier_read_action", "type": "tool" }, "subject": { "sub": "auth0|analyst", "claims": {} }, "payload": { "name": "zapier-mcp-execute_zapier_read_action", "args": {} } } } ``` `allow = true`, no reason. ### Denied — non-writer hits the agentic write funnel ```jsonc { "input": { "action": "tool_pre_invoke", "resource": { "name": "zapier-mcp-execute_zapier_write_action", "type": "tool" }, "subject": { "sub": "auth0|analyst", "claims": { "groups": ["engineering"] } }, "payload": { "name": "zapier-mcp-execute_zapier_write_action", "args": { "instructions": "email the report to the team" } } } } ``` `allow = false`, `reason = "Zapier write actions are restricted (...)"`. ### Allowed — approved writer sends via a classic-mode tool ```jsonc { "input": { "action": "tool_pre_invoke", "resource": { "name": "zapier-mcp-gmail_send_email", "type": "tool" }, "subject": { "sub": "auth0|ops", "claims": { "groups": ["engineering", "automation-writers"] } }, "payload": { "name": "zapier-mcp-gmail_send_email", "args": { "to": "team@example.com", "subject": "report" } } } } ``` `allow = true`, no reason. ## Composition This policy fences *who* may write; it does not constrain *what* a permitted write contains or which apps it reaches. Useful companions: - [`zapier/freeze-toolset`](../freeze-toolset/policy.md) — stops the agent from widening its own toolset (`enable_zapier_action`, `auto_provision_mcp`, `write_code_action`, skill persistence); this policy assumes the toolset is what admins configured. - [`zapier/guard-external-send`](../guard-external-send/policy.md) — content controls on the writes that approved writers do make. - [`zapier/default-deny-unknown-tools`](../default-deny-unknown-tools/policy.md) — classic mode's tool inventory is per-account; an allowlist catches write actions whose names use a verb this policy does not list. - [`zapier/mask-pan-egress`](../mask-pan-egress/policy.md) — reads stay open under this policy, so pair with egress redaction for what those reads return. ## Known limitations - **Group name is a placeholder.** Replace `automation-writers` with your IdP's real group name at import time, and confirm your IdP actually emits a `groups` claim in the access token (many IdPs require explicit configuration to do so). Callers whose tokens carry no `groups` claim are denied all writes — including would-be writers. - **`groups` must be an array.** If your IdP emits `groups` as a single string or a space-delimited string, the membership check never matches and every caller is denied writes (fail closed). Adjust `is_approved_writer` if your IdP uses a non-array shape. - **The verb list is not exhaustive.** Classic-mode action names are per-account and only a handful are verified from public docs; a write action whose name carries a different verb (e.g. `add_`, `post_`, `move_`, `archive_`) would pass as a "read". Extend `write_verbs` for your inventory, or pair with `default-deny-unknown-tools` so unlisted names are denied by default. - **Agentic write meta-tools overlap `freeze-toolset`.** `create_/update_/delete_zapier_skill` match the verb rule, so when both policies are attached, changing skills requires being in *both* the admin and writer groups. The toolset self-modification meta-tools — `enable_zapier_action`, `disable_zapier_action`, `auto_provision_mcp`, and `write_code_action` — carry no listed verb and are **not** gated here; they are writes-of-capability, not writes-of-data, and are out of this policy's scope by design. Attach `freeze-toolset` to cover them (note `freeze-toolset`'s own candidate list omits `disable_zapier_action`, so confirm it enumerates every self-modification tool your server exposes). - **Unidentifiable calls fail closed.** A request whose `resource.name` is missing, `null`, or an empty string cannot be classified as a read or a write, so it does not match the read-open branch and falls through to `default allow := false` (denied). Real gateway traffic always carries a tool name; this guard only removes a fail-open corner and never blocks a legitimately named read. - **Verb matching sees the whole prefixed name.** A gateway server name containing a verb substring (e.g. a server named `zapier-send_prod`) would classify every tool on it as a write. Name servers without `send_`/`create_`/`update_`/`delete_`/`remove_` substrings. - **Reads are open by design.** `execute_zapier_read_action` still proxies reads across every enabled app, and Zapier's server-side AI resolves `instructions` after the gateway passes the call. This policy is a write fence, not a data-access control — compose with egress redaction and app-scoping policies for read-side exposure. - **Tool names verified against Zapier's official MCP docs** (docs.zapier.com, mid-2026) for the agentic meta-tools; classic-mode examples (`gmail_send_email`, `google_sheets_create_row`, `quickbooks_online_find_customer`) come from third-party client docs — treat other classic names as unverified until observed on a live server. > **Compliance note.** This policy supports alignment with the cited framework controls **on the MCP path only**. No policy or bundle makes an organization compliant with any framework; web-UI, native-API, and in-app access are outside the gateway's reach by design. Validate against your own compliance program before relying on it. direction: ingress apps: - zapier industries: [] bundles: - soc2 - gdpr-ccpa experimental: true schemaVersion: 1.0.0 minimumGatewayVersion: 1.0.0b24 --- ```rego package zapier.ingress.role_gate_writes # Deny-by-default: only the explicit allow rules below permit the request. default allow := false # IdP group approved to perform Zapier writes. PLACEHOLDER — map to your # tenant's real IdP group name at import time. writer_group := "automation-writers" # Classic-mode write verbs. Zapier classic (manual configuration) tools are # named `_` (e.g. gmail_send_email, google_sheets_create_row); # a name carrying one of these substrings is treated as a write. The list is # intentionally conservative — extend it for per-account action inventories # that use other verbs (add_, post_, ...), or pair with a # default-deny-unknown-tools allowlist to catch what this misses. write_verbs := [ "send_", # externally visible sends (email, chat, social posts) "create_", # record/row/page/event creation "update_", # record mutation "delete_", # destructive; Zapier offers no undo "remove_", # destructive; Zapier offers no undo ] # Agentic mode: every write across 9,000+ proxied apps funnels through this # single meta-tool. Matched by suffix because the gateway prefixes tool names # with the configured MCP server name # (e.g. `zapier-mcp-execute_zapier_write_action`). is_agentic_write if { endswith(lower(input.resource.name), "execute_zapier_write_action") } # Classic mode: per-action tools whose name carries a write verb. This also # intentionally catches the agentic skill meta-tools (create_/update_/ # delete_zapier_skill) and send_feedback — all of them are writes. is_classic_write if { name := lower(input.resource.name) some verb in write_verbs contains(name, verb) } is_write_tool if { is_agentic_write } is_write_tool if { is_classic_write } # Caller is an approved writer. Fails closed: if `subject`, `claims`, or # `groups` is missing (or `groups` is not an array), no membership is found # and the caller is not approved. is_approved_writer if { claims := object.get(input.subject, "claims", {}) groups := object.get(claims, "groups", []) some group in groups group == writer_group } # A resolvable, non-empty tool name is required before anything is treated as # a read. Without this guard a call whose `resource.name` is missing, null, or # empty would make `is_write_tool` undefined/false and sail through the # read-open branch below — a fail-open on tool identity. Requiring the name to # be a non-empty string makes an unidentifiable call fall through to # `default allow := false` (fail closed). has_tool_name if { is_string(input.resource.name) input.resource.name != "" } # Reads stay open to everyone: execute_zapier_read_action, discovery/list # meta-tools, and classic find_/get_ tools carry no write verb. allow if { has_tool_name not is_write_tool } # Approved writers may write. allow if { is_write_tool is_approved_writer } reasons contains "Zapier write actions are restricted to members of the automation-writers group; read actions remain available to everyone. Ask your IdP administrator to add you to automation-writers if your role requires write access through this connector. If you believe this is a false positive, contact your InfoSec team." if { is_write_tool not is_approved_writer } reason := joined if { count(reasons) > 0 reason_list := sort([r | some r in reasons]) joined := concat("; ", reason_list) } ```