fix(agent): keep strict permissions server-side, not in repo
The previous commit put .claude/settings.json IN THE REPO, which would
have applied its strict deny rules to ANY Claude Code invocation from
this cwd — including the human user's interactive dev sessions on their
own machine. That's wrong; the production agent's lockdown should not
constrain the developer.
Remove the committed file and gitignore .claude/ entirely. The repo is
permission-neutral now.
Strict permissions for the production agent come from two server-only
sources:
1. CLI flags in agent/claude_wrapper.py (--allowed-tools +
--disallowed-tools, passed by the systemd-spawned subprocess only)
2. /var/lib/overlord-agent/.claude/settings.json (the agent's own HOME
— separate from any user's .claude/)
Also bumps claude_wrapper.py with the explicit --disallowed-tools list
of meta-tools (ToolSearch, Monitor, TodoWrite, TaskOutput, Skill, cron
tools, etc.) that the --allowed-tools whitelist does not block on its
own. Verified empirically: with only --allowed-tools, ToolSearch was
still callable; --disallowed-tools is required.
This commit is contained in:
parent
f894399165
commit
e780f249d1
3 changed files with 54 additions and 23 deletions
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"WebFetch(domain:acpedia.org)"
|
|
||||||
],
|
|
||||||
"deny": [
|
|
||||||
"Bash",
|
|
||||||
"Write",
|
|
||||||
"Edit",
|
|
||||||
"Read",
|
|
||||||
"Glob",
|
|
||||||
"Grep",
|
|
||||||
"NotebookEdit",
|
|
||||||
"WebSearch"
|
|
||||||
],
|
|
||||||
"ask": []
|
|
||||||
},
|
|
||||||
"enableAllProjectMcpServers": true
|
|
||||||
}
|
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -3,7 +3,9 @@ __pycache__
|
||||||
static/v2/
|
static/v2/
|
||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
|
|
||||||
# Claude Code per-machine permissions (do NOT deploy to server — production
|
# Claude Code config — never commit. The production agent's strict
|
||||||
# agent must run with the strict permissions in committed .claude/settings.json)
|
# permissions live server-side at /var/lib/overlord-agent/.claude/
|
||||||
.claude/settings.local.json
|
# (and via CLI flags in agent/claude_wrapper.py). The repo stays
|
||||||
.claude/settings.local.json.*
|
# permission-neutral so devs can `claude` interactively here without
|
||||||
|
# inheriting production-agent restrictions.
|
||||||
|
.claude/
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,50 @@ async def ask_claude(message: str, session_id: str) -> ClaudeResult:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# CRITICAL: Claude Code's built-in meta-tools (ToolSearch, Monitor, etc.)
|
||||||
|
# bypass the --allowed-tools whitelist. They come from Anthropic's tool
|
||||||
|
# registry rather than from local MCP servers. We must explicitly DISALLOW
|
||||||
|
# them — confirmed by testing that ToolSearch was reachable even with
|
||||||
|
# `--permission-mode dontAsk` and a tight --allowed-tools list.
|
||||||
|
disallowed_tools = ",".join(
|
||||||
|
[
|
||||||
|
# File / shell / search built-ins (defense in depth — already not
|
||||||
|
# in allow list, but if someone toggles permission-mode this
|
||||||
|
# belt-and-suspenders the deny side).
|
||||||
|
"Bash",
|
||||||
|
"Write",
|
||||||
|
"Edit",
|
||||||
|
"Read",
|
||||||
|
"Glob",
|
||||||
|
"Grep",
|
||||||
|
"NotebookEdit",
|
||||||
|
# Network built-ins
|
||||||
|
"WebSearch",
|
||||||
|
# Tool / session meta-tools — these can list, load, or chain
|
||||||
|
# into other tools and must NOT be reachable.
|
||||||
|
"ToolSearch",
|
||||||
|
"Monitor",
|
||||||
|
"TaskOutput",
|
||||||
|
"TaskStop",
|
||||||
|
"TodoWrite",
|
||||||
|
"Skill",
|
||||||
|
"EnterPlanMode",
|
||||||
|
"ExitPlanMode",
|
||||||
|
"EnterWorktree",
|
||||||
|
"ExitWorktree",
|
||||||
|
"AskUserQuestion",
|
||||||
|
"ListMcpResourcesTool",
|
||||||
|
"ReadMcpResourceTool",
|
||||||
|
"PushNotification",
|
||||||
|
# Scheduling / cron — the agent must never schedule itself.
|
||||||
|
"CronCreate",
|
||||||
|
"CronList",
|
||||||
|
"CronDelete",
|
||||||
|
"ScheduleWakeup",
|
||||||
|
"RemoteTrigger",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Pick --session-id (creates) vs --resume (continues) based on whether
|
# Pick --session-id (creates) vs --resume (continues) based on whether
|
||||||
# the session JSONL already exists on disk.
|
# the session JSONL already exists on disk.
|
||||||
is_new = not _session_exists(session_id)
|
is_new = not _session_exists(session_id)
|
||||||
|
|
@ -105,6 +149,10 @@ async def ask_claude(message: str, session_id: str) -> ClaudeResult:
|
||||||
"json",
|
"json",
|
||||||
"--allowed-tools",
|
"--allowed-tools",
|
||||||
allowed_tools,
|
allowed_tools,
|
||||||
|
# Built-in meta-tools that --allowed-tools does NOT block — must
|
||||||
|
# be explicitly listed here.
|
||||||
|
"--disallowed-tools",
|
||||||
|
disallowed_tools,
|
||||||
# CRITICAL: dontAsk auto-DENIES anything outside --allowed-tools.
|
# CRITICAL: dontAsk auto-DENIES anything outside --allowed-tools.
|
||||||
# Do NOT use bypassPermissions here — that mode ignores the whitelist
|
# Do NOT use bypassPermissions here — that mode ignores the whitelist
|
||||||
# entirely and lets the model call Bash/Write/Edit/etc. (verified
|
# entirely and lets the model call Bash/Write/Edit/etc. (verified
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue