feat(safety): classify run_background_job commands like shell - #4166
Merged
dgageot merged 3 commits intoSep 4, 2026
Conversation
run_background_job's cmd now goes through the same safe/destructive/unknown classifier as the shell tool. Session "always allow" grants are tool-scoped for both command tools, and BuildPermissionPattern emits `run_background_job:cmd=<word>*`. Assisted-By: docker-agent
…nd granting safety.CommandArg now applies the shell/background-job handlers' precedence (non-blank `cmd` wins, otherwise the `command` alias), so the safety label, the session-grant hardening and the "always allow" pattern all describe the command that actually runs. BuildPermissionPattern also honours the `command` alias instead of falling back to a whole-tool grant. Assisted-By: Claude
shell and run_background_job now resolve cmd/command via
safety.CommandArg over an exact-key map instead of encoding/json struct
tags, whose case-insensitive last-wins matching let
{"cmd":"ls","CMD":"rm -rf x"} run a command the runtime never
classified.
Permission rules and session grants for command tools are matched
against args with the executed command mirrored under the canonical
"cmd" key, so a `shell:cmd=sudo*` deny can no longer be dodged with the
"command" alias and "always allow" grants keep matching alias calls.
Assisted-By: Claude
trungutt
approved these changes
Sep 4, 2026
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.
Previously,
run_background_jobwas labelled from tool annotations alone, so itscmdargument was always treated asunknown. In balanced mode this meant every background job triggered a confirmation prompt regardless of how benign the command was; in restricted mode every job was denied outright. Worse, a destructive command likerm -rf /tmp/worklaunched in the background would lose its confirmation badge entirely, since no classifier ever inspected the actual command text.This change brings
run_background_jobunder the samepkg/safetyclassifier already used forshell. A newsafety.IsCommandTool()predicate covers both tools, andLabelToolCallnow classifies by command text for any command tool. Session "always allow" grants are tightened viacommandGrantCoversCall: ashell:cmd=…grant no longer covers a background job and vice versa.BuildPermissionPatternemitsrun_background_job:cmd=<word>*for background jobs, and the deprecatedsafer_shellhook uses the same predicate.The change also surfaces two pre-existing issues that would have widened to background jobs otherwise. First,
safety.CommandArgnow applies the same handler precedence — non-blankcmdwins, else thecommandalias — so the label always describes the command that actually runs. Second, handlers previously resolved args viaencoding/jsonstruct-tag matching, whose case-insensitive last-wins semantics let{"cmd":"ls","CMD":"rm -rf x"}execute a command the runtime had classified as safe; they now use an exact-key lookup over the samesafety.CommandArgpath. Permission rules and session grants are matched against a normalised view of the call args (call.permissionArgs()) that mirrors the executed command undercmd, soshell:cmd=sudo*deny rules cannot be bypassed with thecommandalias, and stored grants keep matching alias-style calls.The net effect by safety mode:
One known limitation is unchanged and out of scope: pressing T on a command-tool call with no extractable command still grants the whole tool, because hiding that option requires dialog layout work.