Repeat pattern: unilateral destructive agent action on external systems in bypassPermissions mode (credentials, Firebase)
Summary
Two independent incidents, three+ months apart, in two different local projects, show Claude Code — while running in bypassPermissions mode — taking irreversible, unilateral action against a real external system it was never explicitly authorized to touch in the specific way it touched it. In both cases, the agent's own attempt to fix or clean up the situation introduced a second, separate, unrequested consequence. This is filed as one issue because the shared shape is the point: the failure isn't "the agent occasionally makes mistakes," it's that in bypassPermissions mode, authorization for a general action (e.g. "deploy to Firebase," "authenticate to GitHub") is treated as authorization for any specific decision needed to carry it out — including decisions with real, irreversible, external blast radius.
---
Incident 1 — Credential hardcoded and pushed to remote git history (May 22–24, 2026)
Asked to set up a routine requiring GitHub authentication, the agent hardcoded a live GitHub Personal Access Token and an SMTP app password directly into instruction files (INSTRUCTIONS.md, PR_REVIEW_INSTRUCTIONS.md), committed them, and pushed to the remote. The token sat exposed in git history for ~39 hours before I found it via manual audit.
I had authorized the agent to authenticate to GitHub. I had not authorized how that credential would be persisted — hardcoding a live value into version-controlled files and pushing them, instead of using an environment variable or placeholder, was a decision the agent made unilaterally.
Remediation required revoking the token, rewriting 30 commits with git filter-branch, force-pushing clean history, and auditing all 53 of my repos to confirm scope was contained to one.
This was previously reported via HackerOne and via the model bug bounty email address. The HackerOne report was closed as Informative, citing the bypassPermissions-mode program exclusion. No response was received on a follow-up reopen challenge or via the bug bounty email. Filing here instead, since this is a product-behavior/agentic-safety issue rather than a classic CVSS vulnerability, and the repeat occurrence below suggests it's a pattern worth product-team visibility rather than a one-off.
Incident 2 — Production Firebase project silently selected and then disabled (Sep 5, 2026)
Asked to deploy two demo HTML files to Firebase Hosting, the agent ran firebase projects:list, which returned 6 existing projects — I had named none of them. The agent silently selected ppradyoth, my personal portfolio project, reasoning (per its own response) that it was "most suitable for a personal demo." It then wrote .firebaserc/firebase.json and ran firebase deploy --only hosting, overwriting my live portfolio site at ppradyoth.web.app.
When I objected ("NEVER USE MY PORTFOLIO WEBSITE"), the agent's own remediation was a second unconfirmed destructive command: firebase hosting:disable --project ppradyoth --force, which took the portfolio offline entirely rather than just reverting the deploy. I had to instruct the agent to create a new, unambiguous Firebase project and redeploy there, and separately restore the portfolio myself.
Full session log: available on request (contains project IDs / URLs I'd rather not paste verbatim into a public issue, happy to share privately if useful).
---
Side-by-side
| | Incident 1 — Credentials | Incident 2 — Firebase |
|---|---|---|
| External system touched | Remote git history (GitHub) | Remote cloud infra (Firebase Hosting) |
| Action actually authorized | Authenticate to GitHub | Deploy files to Firebase Hosting |
| Decision made unilaterally | How to persist a live credential | Which existing production project to target, then to disable |
| Was the target/method named by the user? | No | No — 6 projects existed, none named |
| Irreversibility | Secret exposed in git history until manually rewritten | Production site taken offline; manual restoration required |
| Agent's own "fix" caused a second problem | No — caught before any fix was attempted | Yes — hosting:disable --force was the agent's own unrequested "cleanup," and was the more damaging action |
Why this doesn't fit the existing bypassPermissions carve-out
I recognize the bug bounty program's stated position that "in auto-accept-edits mode Claude Code is able to modify all files in the CWD... this behavior is the responsibility of the user that enables auto-accept-edits mode." That's a reasonable position for local file modification. Both incidents here go further: they're irreversible actions against remote, external, shared systems (a GitHub remote; a live production hosting project) that the user cannot simply revert locally. The gap isn't that bypassPermissions removes prompts — that's the accepted tradeoff — it's that the agent has no heuristic for treating an ambiguous choice among pre-existing external resources, or an unrequested destructive remediation, with elevated caution even when per-tool prompts are off.
Suggested mitigations
- When an agent-initiated action targets an existing external/cloud resource (Firebase project, GCP project, AWS profile, Vercel project, etc.) and the user did not explicitly name it, surface the list and ask — regardless of permission mode. Heuristic: if listing existing resources returns more than one result and the user named none, ask, don't pick.
- Treat any action that modifies or disables a previously untouched production resource — even one taken as a corrective step — as requiring the same confirmation bar as the original ambiguous action. "I made a mistake" is not authorization for a second unilateral destructive action.
- For credential handling specifically: warn (regardless of permission mode) when a known credential pattern (
ghp_,AKIA,sk-, etc.) is about to be written to a file, and document a standard placeholder/env-var convention for routines.
Environment
- Claude Code: claude-sonnet-4-6, desktop app (Incident 2); similar era build for Incident 1
- OS: macOS
- Permission mode: bypassPermissions (both incidents)
Disclosure
Both incidents were discovered and fully remediated by me during ordinary live use. No malicious intent alleged on any side — filing for product improvement, given the repeat occurrence across unrelated projects and external systems.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
The second incident is the one I would push on. firebase hosting:disable --force against a project the user never named is two failures stacked: the target got resolved ambiguously, then an irreversible action ran as cleanup.
Your first proposal is the right shape. I would make it narrower so it is cheaper to land: gate on reversibility, not on permission mode. If an action cannot be undone without an external restore, it needs a confirm even in bypass mode. "Listing returned more than one result" is a good trigger, but it misses the case where there is exactly one match, and that match is production.
I maintain a runtime policy layer for agent tool calls, so some notes from that side:
Reversibility can be static metadata on the tool. You do not have to infer intent. hosting:disable --force sits in a different tier from list or read, and that tier is knowable before the call.
The approval hop only works if it is synchronous. If the gate is async, the agent has already moved on by the time anyone looks.
The credential ask is two asks. Masking a secret value inside a tool-call payload is one check at the tool boundary. Catching "the agent decided to write a live token into a file it is about to commit" is a different check, and it lives at the write. Worth splitting those in the issue; they land on different teams.
On HackerOne closing incident 1 as Informative: I think that is the wrong read. The finding is not that bypass mode permitted a write. It is that the agent chose to put a live credential somewhere durable. That is not a permissions question, so a permissions-mode exclusion should not dispose of it.
Repo if any of the policy shape is useful: https://github.com/sattyamjjain/agent-airlock
Hi Sattyam,
Agree on both counts, and the reversibility framing is sharper.
Gating on reversibility instead of permission mode is the right axis.
If an action can't be undone without an external restore, it confirms regardless of mode. hosting:disable --force sits in a different tier from list or read, and that tier is known before the call.
On the credential finding, splitting it makes sense. Masking a secret in a tool-call payload is a check at the tool boundary. Catching the agent write a live token into a file it's about to commit is a different check at the write. Different enforcement points.
Hackerone programs usually frame narratives to push back all the time. Anthropic should consider these issues as they considerably help improve claude code.
Built the reversibility-gate slice of this out as a plugin, narrowed per the framing above: static reversibility-tier matching on
Bashcommands (force pushes, history rewrites,firebase hosting:disable/projects:delete,terraform destroy,kubectl delete, cloud delete ops,DROP/TRUNCATE, broadrm -rf), returningpermissionDecision: "ask"so it confirms synchronously regardless of permission mode, including bypass. Doesn't attempt the target-resolution piece (single-match-but-production) — that needs session state a stateless per-call hook doesn't have, so left that out rather than fake it. 34 tests, all passing.Branch/diff: https://github.com/ppradyoth/claude-code/compare/main...ppradyoth:claude-code:reversibility-gate-plugin
I can't open a PR against this repo directly — "An owner of this repository has limited the ability to open a pull request to users that are collaborators on this repository." Could someone either pull the branch in or add me as a collaborator so I can open it myself?
Separately, #62099 (credential-guard — the write-boundary check for the credential-exposure incident here) has been open since May with no review yet, if that's easier to look at first.