Skip to content

Security: tui_gateway shell.exec inherits the approval denylist bypass → arbitrary command execution #36847

@dhyabi2

Description

@dhyabi2

Summary

The shell.exec JSON-RPC method in tui_gateway/server.py gates execution by calling tools.approval.detect_dangerous_command and, on a "dangerous" verdict, returning an error. Because that detector is the same bypassable regex denylist tracked in the sibling issue, every bypass payload passes the shell.exec gate and is handed straight to subprocess.run(cmd, shell=True, …). This is a distinct, network-relevant call path with the same root cause.

Affected code (current main, ef3a650f05d2)

tui_gateway/server.py:7815@method("shell.exec"):

@method("shell.exec")
def _(rid, params: dict) -> dict:
    cmd = params.get("command", "")
    ...
    from tools.approval import detect_dangerous_command
    is_dangerous, _, desc = detect_dangerous_command(cmd)
    if is_dangerous:
        return _err(rid, 4005, f"blocked: {desc}. ...")
    ...
    r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30, cwd=os.getcwd())

Note also the except ImportError: pass — if the import fails, the gate is skipped entirely (fail-open secondary path).

Reproduction

Any payload from the sibling denylist issue (r\m -rf …, $(echo rm) -rf …, ${0/x/r}m -rf …) sent as the command param of a shell.exec request returns no 4005 blocked error and executes under the hermes user. Confirmed against detect_dangerous_command on current main:

from tools.approval import detect_dangerous_command
detect_dangerous_command(r"r\m -rf /home/victim")   # → (False, None, None) → shell.exec proceeds

Impact

Arbitrary command execution via the TUI gateway protocol, bypassing the only safety check on this path. CWE-184. Severity follows the denylist root cause (Critical / RCE).

Suggested remediation

Fix the shared classifier (see sibling issue — structural shlex tokenization instead of regex). Additionally, replace except ImportError: pass with a fail-closed error so a missing approval module cannot silently disable the gate.


Prior private disclosure & status

Reported privately via GitHub Security Advisory on 2026-04-23 (GHSA-2jg5-795w-3rv5, "tui_gateway shell.exec inherits regex-denylist bypass", rated High). The advisory was closed on 2026-05-14 without a fix, CVE, or publication. The code path still reproduces on current main (ef3a650f05d2, 2026-06-01). Filing publicly so it has a tracked record. Shares a root cause with the tools/approval.py denylist issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Critical — data loss, security, crash loopcomp/gatewayGateway runner, session dispatch, deliverycomp/tuiTerminal UI (ui-tui/ + tui_gateway/)type/securitySecurity vulnerability or hardening

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions