Skip to content

Gate exec writes on a sensitive default set, add disable + deny vars#128

Merged
andrewm4894 merged 4 commits into
mainfrom
posthog-code/exec-gate-default-deny-set
Jun 27, 2026
Merged

Gate exec writes on a sensitive default set, add disable + deny vars#128
andrewm4894 merged 4 commits into
mainfrom
posthog-code/exec-gate-default-deny-set

Conversation

@andrewm4894

@andrewm4894 andrewm4894 commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

The MCP exec write gate previously prompted on every write call. That's noisy on remote devboxes and Claude Cloud runs, where there's no one to answer the prompt.

This flips the default so the gate only prompts for a curated sensitive subset of writes (grounded in the live MCP tool registry), and adds two env vars to tune it:

  • Default deny set*feature-flag*, *delete*, *destroy*, plus the high-blast-radius rollout/user-facing lifecycle ops experiment-launch, experiment-ship-variant, experiment-reset, survey-launch, workflows-enable. Everything else (routine insight-create, dashboard-update, experiment-pause, etc.) runs silently.
  • POSTHOG_MCP_EXEC_GATE_DISABLE — set to a non-empty value (e.g. 1) to turn the gate off entirely. Intended for devboxes / Cloud runs.
  • POSTHOG_MCP_EXEC_GATE_DENY — comma-separated bash globs that override the default set. Use * to restore the old "prompt on every write" behaviour.
  • POSTHOG_MCP_EXEC_GATE_ALLOW (unchanged) still wins over the deny set.

The write-verb regex stays as a prefilter, so deny globs like *feature-flag* never catch read tools (feature-flag-get-all, feature-flags-status-retrieve stay silent).

Grounding

The deny globs were validated against the live PostHog MCP tools registry — *feature-flag* / *delete* / *destroy* match ~50 real tools (create/update/delete-feature-flag, the bulk FF ops, insight-delete, persons-bulk-delete, notebooks-destroy, …). The registry also surfaced sensitive non-delete ops that would otherwise run silently (experiment-launch, experiment-ship-variant, survey-launch, workflows-enable), which is why those are in the default set.

Deliberately left silent by default (lower blast radius / easily reverted): routine create & update, experiment-pause/resume/end, survey-stop, persons-property-set, error-tracking issue merge/split. Easy to add via POSTHOG_MCP_EXEC_GATE_DENY.

Why

Per the Slack thread: the exec confirmation prompt fires constantly in remote devboxes and Claude Cloud runs and is hard to disable there. We wanted the default to only kick in on sensitive-looking calls (feature-flag writes were the motivating example) plus an actual off switch.

Note for reviewers

The default deny set is a judgement call. If you'd rather keep it strictly to feature-flag writes + deletes, drop the lifecycle entries from DEFAULT_DENY in hooks/gate-exec-write.sh — one line. Happy to adjust the scope either way.

Test plan

  • bash tests/test_gate_exec_write.sh — 39/39 pass (covers the default set, the rollout-lifecycle entries, DISABLE, DENY override, and allow-wins-over-deny).

Created with PostHog from a Slack thread

The exec write gate previously prompted on every write `call`, which is
noisy on remote devboxes and Claude Cloud runs where the prompt can't be
answered.

Changes to `hooks/gate-exec-write.sh`:
- Default the prompt to a curated sensitive subset (`*feature-flag*`,
  `*delete*`, `*destroy*`) instead of every write.
- Add `POSTHOG_MCP_EXEC_GATE_DISABLE` to turn the gate off entirely.
- Add `POSTHOG_MCP_EXEC_GATE_DENY` (comma-separated globs) to override the
  default set — set to `*` to restore prompting on every write.
- Existing `POSTHOG_MCP_EXEC_GATE_ALLOW` still wins over the deny set.

Generated-By: PostHog Code
Task-Id: e017e801-e550-40d7-8730-75ced98d0b85
@andrewm4894 andrewm4894 self-assigned this Jun 25, 2026
@andrewm4894 andrewm4894 marked this pull request as ready for review June 25, 2026 12:44
Validated the deny globs against the live PostHog MCP `tools` registry and
documented the real tools each glob covers. The `*feature-flag*`, `*delete*`,
and `*destroy*` globs match ~50 real tools.

The registry surfaced a gap: high-blast-radius rollout/user-facing writes that
aren't deletes would have run silently. Added them explicitly to the default
set: experiment-launch, experiment-ship-variant, experiment-reset,
survey-launch, workflows-enable.

Documented what's deliberately left silent (routine create/update,
experiment-pause/resume/end, survey-stop, persons-property-set, issue
merge/split) and added test coverage for the new entries.

Generated-By: PostHog Code
Task-Id: e017e801-e550-40d7-8730-75ced98d0b85
Bump the patch version across all four plugin manifests (claude, cursor,
codex, gemini), matching the bump the sync-skills workflow applies on any
content change.

Generated-By: PostHog Code
Task-Id: e017e801-e550-40d7-8730-75ced98d0b85
A PreToolUse hook that exits 2 is a hard block of the user's tool call.
The only thing that could make this gate exit 2 was a parse-time syntax
error; a runtime crash (unbound var under `set -u`, failed builtin, old
bash) exited 1 with noisy stderr.

The "ask" decision is delivered entirely via the stdout JSON, never the
exit code, so forcing every exit path to 0 is fully compatible:

- Add `trap 'exit 0' EXIT` so any runtime failure falls through to normal
  permission flow instead of surfacing as a hook error — and can never
  reach Claude Code as exit 2.
- Guard the one case a trap can't catch (syntax errors) with a `bash -n`
  check in the test suite, plus assertions that no input — empty, garbage,
  truncated, or a real sensitive write — ever exits 2.

Tests: 45/45 pass.

Generated-By: PostHog Code
Task-Id: a511847d-af3b-4083-a675-d94d9500e11c
@andrewm4894 andrewm4894 requested a review from skoob13 June 25, 2026 15:36
@skoob13

skoob13 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

@andrewm4894 looks good to me except for ungating update actions. From the PostHog AI experience, feedback on auto-updating was quite negative, and customers wanted to see first what was going on before the action was taken. I believe this applies to the MCP as well, i.e., I care if agents update a crafted dashboard without explaining it to me.

@skoob13 skoob13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore the comment above. I misunderstood the description...

@andrewm4894 andrewm4894 merged commit 3b60fdd into main Jun 27, 2026
11 checks passed
@andrewm4894 andrewm4894 deleted the posthog-code/exec-gate-default-deny-set branch June 27, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants