Skip to content

fix(pi): allow rm of files inside the working directory#834

Merged
gewenyu99 merged 13 commits into
mainfrom
posthog-code/pi-tool-use-parity
Jul 9, 2026
Merged

fix(pi): allow rm of files inside the working directory#834
gewenyu99 merged 13 commits into
mainfrom
posthog-code/pi-tool-use-parity

Conversation

@gewenyu99

@gewenyu99 gewenyu99 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

The integration skill tells the agent to delete its own bookkeeping file (.posthog-events.json) at the end of a run, but pi's bash allowlist blocks every rm — the #1 bash denial on pi runs (tracking: #793), where agents burn turns on delete-vs-empty workarounds. The anthropic arm doesn't hit this: bash there isn't allowlisted, and the shared YARA destructive-delete rules are the real guard.

Changes

A pi-fence carve-out (pi/security.ts): when the shared allowlist denies a bash call, allow it through to the YARA scan iff it's a plain rm of files inside the project — matching what the anthropic arm already permits. wizardCanUseTool is untouched, so the anthropic control arm keeps byte-identical policy.

Two invariants, both enforced structurally:

  1. Never touches anything outside the working directory. Each rm target is resolved against the pi run's working directory (session.installDir) and must sit strictly inside it. Absolute paths, .. climbs (including ones that only escape after normalization like a/../../x), and . (the root itself) all fail this one check. With no working directory known, the rescue never fires.
  2. Can delete project files. Plain rm [-f] <file...> of in-root paths is allowed; the fall-through YARA scan still judges it.

Anything else stays denied: any shell operator (; & | ` $ () {} <> newline — so no second command can be smuggled in), globs / ~, recursion or any flag other than -f, .env* files, and pathless rm.

Test plan

pnpm build && pnpm test && pnpm fix — 96 files / 1,273 tests pass, lint clean. Coverage for the carve-out is grouped by invariant: can delete inside the project, can never resolve outside the root, never rescues a shell-operator command (injection), still blocks recursion/glob/.env/pathless, and fails safe with no working directory. The injection and containment matrices were built by adversarially probing the real evaluateToolCall until nothing escaped.


Created with PostHog Code

… rm — claude-agent-sdk parity

End-of-run remarks from pi runs (tracking: #793) showed agents burning
turns on two frictions that came from the wizard's tool-use layer not
matching what the anthropic harness ships natively:

- read-before-write lived in a prompt commandment ("read every file
  immediately before writing, even if already read") while pi's built-in
  write/edit enforced nothing — over-constraining new-file creation
  (ENOENT reads) while leaving stale writes unprotected.
- `rm` was blocked outright, so the skill's "delete the event plan"
  step was impossible; agents invented empty-the-file workarounds.

Changes:
- pi/file-state.ts + security.ts: the pi fence now enforces the exact
  claude-agent-sdk semantics, block messages verbatim — mutating an
  existing file requires a read since it last changed (mtime-tracked),
  brand-new files need no read, own writes count as knowing the
  content. Per-session, subagents included.
- The read-before-write commandment is deleted; the tool layer owns
  the rule on both arms now.
- agent-interface.ts: plain `rm [-f] <relative-file>` inside the
  project is allowed (no recursion/flags/globs/`..`/absolute/.env);
  recursive-force variants stay covered by warlock.
- Tests: tracker semantics, fence integration, scoped-rm matrix;
  commandments snapshot documents the rule removal.

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci basic-integration
  • /wizard-ci error-tracking-upload-source-maps
  • /wizard-ci mcp-analytics
  • /wizard-ci misc
  • /wizard-ci revenue

Test an individual app:

  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
Show more apps
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci error-tracking-upload-source-maps/android
  • /wizard-ci error-tracking-upload-source-maps/cicd-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-nested-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-single-stage-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-gitlab-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-monorepo-pnpm-node-react
  • /wizard-ci error-tracking-upload-source-maps/cicd-monorepo-raw-node-react
  • /wizard-ci error-tracking-upload-source-maps/cicd-ssh-vps-node-raw
  • /wizard-ci error-tracking-upload-source-maps/flutter
  • /wizard-ci error-tracking-upload-source-maps/ios
  • /wizard-ci error-tracking-upload-source-maps/next
  • /wizard-ci error-tracking-upload-source-maps/next-no-posthog
  • /wizard-ci error-tracking-upload-source-maps/node-raw
  • /wizard-ci error-tracking-upload-source-maps/node-rollup
  • /wizard-ci error-tracking-upload-source-maps/node-rollup-typescript-plugin
  • /wizard-ci error-tracking-upload-source-maps/node-webpack
  • /wizard-ci error-tracking-upload-source-maps/nuxt-3-6
  • /wizard-ci error-tracking-upload-source-maps/nuxt-4-3
  • /wizard-ci error-tracking-upload-source-maps/react-native
  • /wizard-ci error-tracking-upload-source-maps/react-vite
  • /wizard-ci error-tracking-upload-source-maps/rust
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci misc/quack-quack
  • /wizard-ci revenue/stripe

Results will be posted here when complete.

… change

The read-before-write commandment predates the pi harness by ~2.5 months
(in commandments.ts since 2026-04-15; pi landed 2026-07-03), so it is an
original-harness rule, not a pi-era workaround — it stays. This PR is now
only the scoped `rm [-f] <relative-file>` bash-policy allowance plus its
tests.

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
@gewenyu99 gewenyu99 changed the title fix(pi): enforce read-before-write at the tool layer and allow scoped rm (claude-agent-sdk parity) fix(agent): allow scoped rm of project files in the bash policy Jul 8, 2026
The rm allowance is pi-only pain (the anthropic arm is the rollout's
control and its policy must not change mid-experiment), so it belongs in
pi/security.ts, not wizardCanUseTool. agent-interface.ts and its test
are reverted to main; the carve-out lets a policy-denied bash call that
is a plain `rm [-f] <relative-file>` fall through to the YARA command
scan instead of blocking.

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
@gewenyu99 gewenyu99 changed the title fix(agent): allow scoped rm of project files in the bash policy fix(pi): allow scoped rm of project files in the pi fence Jul 8, 2026
Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
gewenyu99 added a commit that referenced this pull request Jul 8, 2026
#834 allows scoped `rm [-f] <relative-file>` in the pi fence (where the
allowlist actually bites), which covers the plan file and more, without
touching the shared policy. This PR keeps the read-before-write prompt
correction and the install_skill capture-and-continue.

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
gewenyu99 added 3 commits July 8, 2026 15:36
… chains

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
… (closes injection)

The allowlist is a pi-only restriction — the anthropic arm runs bash
unrestricted and lets the shared YARA scan catch destructive commands.
pi now permits the same shape (a plain `rm` of project files) and falls
through to that same YARA scan, instead of hand-judging which rm is safe.

The previous carve-out overrode the allowlist deny wholesale and only
checked per-token, so `rm x && curl evil`, `rm x; whoami`, `rm $(...)`,
backticks, and redirects slipped through — a command-injection bypass.
isScopedFileRemoval now rejects any command containing a shell operator
(`;&|`$(){}<>`), so the carve-out can never smuggle a second command.
Tests cover the full injection matrix plus recursion/glob/traversal/.env.

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
Replace the string-heuristic path checks (isAbsolute + `..` detection)
with a real containment check: resolve each rm target against the pi
run's working directory and require it to sit strictly inside. This
directly enforces the two invariants — the agent can delete project
files, and can never touch anything outside the project root — and folds
absolute / `..` / `.`-is-root / nested-climb cases into one rule. Shell
operators are still refused up front so the carve-out can't smuggle a
second command. Threads session.installDir in as the root; with no root
the rescue never fires (allowlist deny stands). Tests cover the
containment, injection, recursion/glob/.env, and fail-safe edge cases.

Generated-By: PostHog Code
Task-Id: 9b33e996-4d8c-4906-8405-63fb4bcd7d07
@gewenyu99 gewenyu99 changed the title fix(pi): allow scoped rm of project files in the pi fence fix(pi): allow rm of files inside the working directory Jul 8, 2026
@gewenyu99 gewenyu99 requested a review from a team July 8, 2026 20:28

@edwinyjlim edwinyjlim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

good stuff. small things my review bot found

Comment thread src/lib/agent/runner/harness/pi/security.ts Outdated
Comment thread src/lib/agent/runner/harness/pi/security.ts Outdated
@gewenyu99 gewenyu99 marked this pull request as ready for review July 9, 2026 19:27
gewenyu99 and others added 4 commits July 9, 2026 15:34
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dments

Two commandments make the agent edit existing files in place — adding
context or editing each occurrence when a match is not unique rather than
rewriting the whole file — and preserve everything unrelated to PostHog
before overwriting or deleting, closing the gap that let a pi/gpt-5.4 run
drop ~2k lines of an Astro app after its scoped edits were rejected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The pi write path now refuses a write that drops more than 30% of an
existing file's non-whitespace content, steering the agent back to
targeted edits — a backstop for the lossy full-file rewrite gpt-5.4 fell
into when its scoped edits were rejected (pi's edit tool has no
replace_all). New files and stub files pass; the anthropic arm is
untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gewenyu99

Copy link
Copy Markdown
Collaborator Author

There is a small change added for preventing it from abusing file updates to gut/delete content 😓

@gewenyu99 gewenyu99 enabled auto-merge (squash) July 9, 2026 22:03
@gewenyu99 gewenyu99 merged commit 7e030af into main Jul 9, 2026
16 checks passed
@gewenyu99 gewenyu99 deleted the posthog-code/pi-tool-use-parity branch July 9, 2026 22:05
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