feat(security): deny irreversible destructive commands in every posture - #23
Open
JordanTheJet wants to merge 314 commits into
Open
feat(security): deny irreversible destructive commands in every posture#23JordanTheJet wants to merge 314 commits into
JordanTheJet wants to merge 314 commits into
Conversation
…eroclaw-labs#9014) - e530dd4 ci(release): notarize + staple the macOS .dmg for offline validation - da1be7e fix(ci): publish the notarized macOS DMG deterministically - 649a013 test(ci): keep desktop release guards mergeable - 40bca67 fix(ci): keep zero-DMG failure portable - be2fd5c Merge branch 'master' into ci-staple-macos-dmg
…zeroclaw-labs#9133) - b3d021f docs(architecture): record channel plugin and gateway process targets - c8c7fd6 Merge branch 'master' into codex/adr-006-007-targets
…law-labs#9130) - cec0fb1 fix(config): restore generated descriptions and code contracts - e0e0a7e fix(gateway): restore pending reload contract description - 13ebdba Merge origin/master into codex/restore-8901-contracts - a39f4ad Merge branch 'master' into codex/restore-8901-contracts
…claw-labs#9026) - b0a7138 feat(gateway/acp): select session agent via ?agent= query param - d163820 fix(gateway/acp): reject disabled agents at shared alias boundary - 28aba77 docs(acp): replace prose em-dashes in ?agent= section - 306178a fix(gateway/acp): keep ?agent= out of session restore rebind - bfcde2f Merge branch 'master' into feat/acp-agent-query-param
…9146) The hardware peripherals design doc linked to a non-existent GitHub repository. Replace it with the canonical kevinmehall/nusb repository verified through crates.io.
…8s (zeroclaw-labs#9147) Replace the nonexistent zeroclaw-templates repository link with the in-repo deploy-k8s sample manifests and correct the Helm wording.
…labs#9148) Remove optional angle brackets from two CommonMark link destinations to match the surrounding documentation style.
…9150) Remove the trailing space inside the Mattermost voice-message code span so markdownlint MD038 passes.
…rt (zeroclaw-labs#9151) Escape the line-leading zeroclaw-labs#6960 reference so markdownlint does not parse it as an ATX heading.
…zeroclaw-labs#8622) - 595c61b feat(channels): emit structured login lifecycle events for QR pairing - 5032f0e fix(log): keep QR pairing credentials off the persisted JSONL trace - 480dbd7 fix(log): flush writer before reading persisted trace in ephemeral-attrs test - 2f355cd fix(log,gateway): keep QR pairing credentials off verbose stderr and unauthenticated SSE - a6a0792 Merge upstream/master into feat/channel-login-events - 13a6205 fix(gateway,log,runtime): hold the pairing-credential boundary across every broadcast consumer - 497a22c Merge upstream/master into feat/channel-login-events - 0a2f53b style(gateway,log,runtime): rustfmt the credential-boundary test helpers - 9b9fe0d Merge upstream/master into feat/channel-login-events
…9122) - ee12041 refactor(plugins): establish canonical instance scope - 1f22dc3 fix(plugins): require adapter opt-in for HTTP - 150c82a fix(plugins): preserve callable tool attribution - caed50f docs(plugins): require grant and adapter opt-in - 03e7153 Merge branch 'master' into feat/plugin-instance-context
…zeroclaw-labs#9094) - 18b4c32 fix(zerocode): separate the literal Ctrl label from the key on darwin - 576fbc6 Merge branch 'master' into fix/darwin-ctrl-chord-display-assertion
…oclaw-labs#9165) The 'Determine version tag' step in docs-deploy.yml expanded ${{ github.event.inputs.tag }} directly into the run: script, one line before the regex that validates it. GitHub performs template interpolation before the shell executes, so a crafted workflow_dispatch tag input (e.g. '"; <cmd>; "') would run arbitrary commands before the validation could reject it — an Actions script-injection (CWE-94) in a job that holds contents: write. Pass github.event_name / event.inputs.tag / github.ref / github.ref_name through env vars and reference them as shell variables, so template values reach the shell as data and can never be interpreted as code. The validation regex and all downstream behavior are unchanged. Exploitability is low (workflow_dispatch requires repo write access and the regex bounds valid values), but this is the canonical Actions injection anti-pattern and the fix is free.
…eroclaw-labs#9152) resolve_sops_dir() only handled the None/empty case relative to the workspace; an explicit relative sops_dir (the shape shown in the documented <workspace>/sops layout) fell through to PathBuf::from(), which resolves against whatever directory the process happens to be running in rather than the workspace. Path::join already replaces the base when the joined path is absolute, so workspace_dir.join(expanded) is a strict superset: absolute and ~-prefixed values behave exactly as before, relative values now resolve against the workspace as documented.
…claw-labs#9043) - 33e3a05 docs(install): align first-run guidance with installer behavior - 41e27d4 docs(install): qualify supported prebuilt targets - 60b366 docs(install): qualify Cargo bin destination
Owner
Author
|
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.
Summary
masteris_command_allowedreturnstrueimmediately whenallowed_commandscontains*andblock_high_risk_commandsisfalse. That is the documented operator opt-out from command screening — but today it also opts out of the handful of operations that leave no system to recover on. This adds a deny tier that no configuration can disable.mkfs(andmkfs.<fstype>),ddwriting to a raw block device,rmnaming the filesystem root, and the classic fork bomb.ls && mkfs.ext4 /dev/sdb1is caught. Reports a distinct error rather than the generic policy refusal, so an operator can tell "you turned screening off but this is still denied" from "your allowlist rejected this".command_risk_level, to approval flow, or to whatblock_high_risk_commandsdoes for everything else. This does not attempt general shell-syntax normalization — the existing guards already reject backticks,$(), and process substitution outside the wildcard path.SecurityPolicy::is_command_allowed/validate_command_execution, i.e. the shell tool and anything reusing the policy gate. Behavior changes only for the four patterns above; ordinary work is unaffected and covered by a negative test.type:feat,risk:medium,size:STesting (required)
How you can test
surface=cli(shell tool via the security policy)allowed_commands = ["*"],block_high_risk_commands = false,autonomy = "full"— the most permissive posture available.mkfs.ext4 /dev/sdb1(use a device that does not exist if you would rather not find out).master(before): The policy permits it and the command reaches the shell.How I tested
Four tests written before the implementation. At red, three failed and
catastrophic_deny_does_not_capture_ordinary_workalready passed — establishing that the negative cases were allowed before the change and still are after it.catastrophic_commands_are_denied_in_the_most_permissive_posture— nine variants, asserting both theis_command_allowedrefusal and the distinct error textcatastrophic_deny_does_not_capture_ordinary_work—dd if=input.iso of=output.img,dd of=./disk.img,rm -rf ./build,rm -rf /tmp/scratch,echo ':(){ :|:& };:',grep -r mkfs docs/catastrophic_deny_survives_a_high_risk_command_being_explicitly_listed— listingddunlocksdd if=a.img of=b.imgbut notof=/dev/sdacatastrophic_deny_applies_to_every_segment_of_a_chainzeroclaw-config, which owns the policy gate; the full 1245-test policy suite is the regression surface for a shared predicate.strip_windows_exe_suffixis a no-op off Windows, somkfsmatching is validated on Unix naming only. The four patterns are Unix-shaped by nature.echo ':(){ :|:& };:'stays allowed) and that/dev/nulland/dev/zeroare excluded from the raw-block-device prefixes so output suppression keeps working. I did NOT run any of these commands against real hardware.Security & Privacy Impact (required)
Yes, describe the risk and mitigation: N/A. The intended risk trade-off is stated plainly: an operator who deliberately set["*"]loses the ability to run four specific patterns. That is the point of the tier, and the error message says so rather than failing as a generic policy refusal.