fix(approval): de-obfuscate shell idioms in dangerous-command detection#25795
Open
GodsBoy wants to merge 1 commit into
Open
fix(approval): de-obfuscate shell idioms in dangerous-command detection#25795GodsBoy wants to merge 1 commit into
GodsBoy wants to merge 1 commit into
Conversation
Extend the approval gate's pre-match normalization to de-obfuscate shell
idioms that hide an otherwise-matched dangerous command, and widen/add
dangerous-pattern entries for idiom classes the regex tables did not model.
Follow-up to GHSA-6cjf-cff6-j9mg per SECURITY.md section 3.2's invitation to
harden the in-process heuristic.
Normalization (_normalize_command_for_detection) now runs a bounded
fixed-point de-obfuscation loop covering ${IFS} expansion, ANSI-C $'...'
quoting, empty-quote splits, and eval/command/builtin wrapper-prefix
stripping. Non-idempotent rules are drained to a local fixed point per
iteration so single-rule nesting of any depth collapses regardless of the
iteration cap; input that still does not converge is surfaced for approval
rather than trusted as a clean no-match.
Pattern-table changes: chmod octal-prefixed world-writable modes, alternate
shell binaries (dash/ash) via -c, base64-decode piped to a shell, and shell
execution of a script in a world-writable or transient path. The base64 and
script-indirection patterns are command-position anchored so a shell binary
name appearing as a non-command token does not false-positive in the
tui_gateway shell.exec hard gate.
Also passes the normalized command to the smart-approval LLM path so it
assesses the de-obfuscated form, and gives two gateway-protection patterns
distinct descriptions (a shared description silently cross-approved them).
No change to the security posture or the OS-isolation boundary; this is a
heuristic extension of the cooperative-mode catch. Adds regression tests
covering each idiom class with true-positive and false-positive guards.
Contributor
Author
|
CI note: the
All other checks are green: ruff enforcement, ruff + ty diff, check-attribution, build-amd64, build-arm64, nix (macOS + Ubuntu), e2e, Windows footguns, and the supply-chain scan. |
This was referenced May 16, 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.
What does this PR do?
Extends the approval gate's pre-match normalization to de-obfuscate shell idioms that hide an otherwise-matched dangerous command, and widens or adds
DANGEROUS_PATTERNSentries for idiom classes the regex tables did not model. This is the RG-4 follow-up to advisoryGHSA-6cjf-cff6-j9mg, filed per SECURITY.md section 3.2's invitation to harden the in-process heuristic. It does not change the security posture: the boundary remains OS-level isolation (section 2.2); this makes the cooperative-mode catch more complete.Related Issue
Fixes #25793
Type of Change
Changes Made
tools/approval.py:_normalize_command_for_detectionnow runs a bounded fixed-point de-obfuscation loop (_deobfuscate) covering${IFS}/$IFSexpansion, ANSI-C$'...'quoting (hex / octal / named / unicode escapes), empty and adjacent quote splits, andeval/command/builtinwrapper-prefix stripping. Non-idempotent rules are drained to a local fixed point per iteration (_drain), so single-rule nesting of any depth collapses regardless of the iteration cap. Input that still does not converge is surfaced for approval rather than trusted as a clean no-match.DANGEROUS_PATTERNS: widened the chmod world-writable pattern to accept octal-prefixed modes (0777/0666) and the shell--cpattern to includedash/ash; added base64-decode-piped-to-shell and world-writable-path script-execution patterns. The two new patterns are command-position anchored, so a shell binary name appearing as a non-command token does not false-positive in thetui_gatewayshell.exechard gate.check_all_command_guardspasses the normalized command to the smart-approval LLM path, so it assesses the de-obfuscated form.tests/tools/test_approval.py:shell.exec-path coverage.How to Test
scripts/run_tests.sh tests/tools/test_approval.py: 238 tests pass (new suites:TestShellObfuscationBypass,TestProngBPatternWidening,TestProngBStructuralPatterns,TestObfuscationHardeningConsolidation,TestSmartApproveNormalization).scripts/run_tests.sh tests/tools/ tests/test_tui_gateway_server.py tests/gateway/test_approve_deny_commands.py: 595 pass.Checklist
Code
fix(approval):)tools/approval.py,tests/tools/test_approval.py)scripts/run_tests.sh(the repo's canonical runner) and all relevant tests passDocumentation & Housekeeping
Post-Deploy Monitoring & Validation
This is an in-process detection-heuristic change; there is no runtime service to deploy.
DANGEROUS_PATTERNSentries are command-position anchored specifically to avoid false-positive hard-blocks intui_gatewayshell.exec, and every idiom class ships a false-positive guard test._deobfuscateemits alogger.debugline when the iteration cap is hit without converging; that log indicates pathologically nested input reaching the normalizer.Notes for reviewers
$(...)or backticks is deliberately not attempted, consistent with the existing narrowkill $(pgreppatterns.command <hardline-word>consistency. Because wrapper-stripping exposes the inner token at a command-start position,command reboot-helperhardline-matches the same way; reboot-helperalready does today. This is a documented, consistent extension of pre-existing hardline-pattern behavior, not a new defect; tightening the hardline anchors was deliberately kept out of scope (it touches the unconditional-block tier).