fix(agent): the allowlist covers the whole line, and coder test stops being the way around it - #1531
Merged
Merged
Conversation
… being the way around it Strict mode checked the first word of a command and nothing else. A line is a sequence of invocations, so that made any allowed command a passphrase for the rest of it: ls followed by an ampersand pair carried an arbitrary command straight past the gate. "Only commands from the allowlist can be executed" was not true of any line with an operator in it. Every segment is held to the same rule now, decomposed by the shell parser that was already in the tree, so a quoted operator stays a quoted operator and is not mistaken for a chain. Two things kept that from becoming a regression. A line the parser cannot read falls back to the previous single-command check rather than failing closed, because a host whose shell is not bash would otherwise lose every command. And the navigation builtins joined the list: cd, pwd, pushd and their neighbours carry no capability of their own, and refusing them once every segment is checked would have broken a directory change followed by a build while withholding nothing. Nineteen commands the documentation lists as allowed were not on the list at all, so strict mode refused npx, base64, openssl, poetry, zig, istioctl and the rest while the page said otherwise. They are on it now. Two settings did nothing when written the way they were documented. The custom allowlist split on commas while the documented example used semicolons, which registered one command with semicolons in its name; extra read paths split on the native separator while the documented example used semicolons, which produced one path that exists nowhere. Both accept either spelling now, except that a colon is still never a separator on Windows, where it separates a drive letter from its path. The coder test subcommand takes an arbitrary command and ran it through none of the guards its sibling applies: not the dangerous-pattern check, not the sandbox, and not the upstream gate, which only ever looked at exec. The same payload the exec path refuses executed through test. It is guarded now, with the same escape hatches, so a suite that legitimately needs them is not newly refused, and the upstream gate covers both subcommands rather than the one that happens to be named after running commands.
diillson
force-pushed
the
feat/agent-exec-hardening
branch
from
September 5, 2026 19:18
886353a to
472cdeb
Compare
Contributor
Quality GateResult: ✅ all floors passed
Config: .github/quality-gate.yml. Workflow: |
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.
Third PR from the
features/securityaudit. This one is mostly fixes to protections that existed but did not hold, rather than missing features.The allowlist checked one word
extractBaseCommandtruncated at the first|,&&,||,;or&, so strict mode authorised the leading command and let the rest of the line through. Proven before the fix:"Only commands from the allowlist can be executed" was untrue of any line containing an operator. Every segment is now held to the same rule, decomposed with the shell parser already in the tree — so
echo "a && b"stays one command and is not mistaken for a chain.Two deliberate choices keep this from becoming a regression:
mvdan.cc/shcannot parse is one the executor's shell is unlikely to run either. The denylist layer still applies.cd,pwd,pushd,popd,dirs,wait,read,shift,jobs,:,[). My own test caught this: withoutcd, checking every segment would have refusedcd sub && go build. They carry no capability of their own.Nineteen documented commands were not on the list
The docs list
npx,base64,openssl,poetry,zig,kotlinc,argocd,flux,istioctl,xmllint,csvtool,ag,cmp,cal,clear,reset,tput,stty,lookas allowed. Strict mode refused all nineteen. They are on the list now, and a test pins the documented set.Two settings did nothing when written as documented
CHATCLI_AGENT_ALLOWLISTsplit on commas; the documented example used semicolons, so"mycli;internal-tool;company-deploy"registered one command with semicolons in its name.CHATCLI_AGENT_EXTRA_READ_PATHSsplit on the native separator; the documented example used semicolons, so it produced one path that exists nowhere.Both accept either spelling now. A colon is still never a separator on Windows — it separates a drive letter from its path.
@coder testwas an unguarded path to the same shellhandleTesttakes an arbitrary--cmdand ran it through no guard: notIsUnsafeCommand, not the sandbox, and not the upstream gate, which only ever looked atsub == "exec". Proven by running the engine:It now applies the same check and the same sandbox, and carries the same
--allow-unsafe/--allow-sudoescape hatches so a suite that legitimately needs them is not newly refused. The upstream gate covers both subcommands.Verification
15 new tests: the bypass shapes, legitimate chains that must keep working, quoted operators, the sudo prefix, the documented command set, both separator spellings, and
@coder testparity withexecin both directions.Patch coverage 93.1%. Full suite,
go vetgreen.