Back to skill

Security audit

Dispatch (Claude Code)

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly a local Claude Code dispatcher, but it gives background agents more control than the user-facing description makes clear.

Install only if you control the callers and runtime environment. Use strict allowlisted project and task names, avoid bypassPermissions, keep results/log directories private, and do not send sensitive prompts unless local plaintext storage is acceptable. This does not appear intentionally malicious, but it deserves review before use because it can run long-lived local agents and approve Claude safety prompts automatically.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/run_dispatch.sh:73
Finding

Path Traversal Through Unvalidated Project and Task Names

Content
View full analysis
``` 2. The script constructs: ```text ${REPOS_ROOT}/../../unintended-target ``` 3. `mkdir -p` creates the resolved directory if the process account has permission. 4. The dispatcher launches Claude Code with that unintended location as its working directory. 5. Claude Code can inspect or modify files available to the process ...[truncated 733 chars]
Remediation
View remediation
&2 exit 2 fi if [[ ! "$TASK_NAME" =~ ^[A-Za-z0-9._-]+$ ]] || [[ "$TASK_NAME" == "." || "$TASK_NAME" == ".." ]]; then echo "Invalid task name" >&2 exit 2 fi ``` 2. Reject path separators, traversal components, control characters, and newline characters. 3. Canonicalize the configured root and candidate path with `realpath`. 4. Verify that the canonical candidate begins with the canonical root followed by `/`. 5. Perform equivalent containment checks independently for work, result, and log paths. 6. Prefer an internally generated opaque run identifier rather than embedding user-controlled names in filenames. 7. Refuse to create a project directory automatically unless directory creation is an explicitly intended and authorized operation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/vendor/claude_code_run.py:203
Finding

Automatic Acceptance of Workspace Trust and Permission-Bypass Warnings

Content
View full analysis
Remediation
View remediation

other

Warning
Location
scripts/vendor/claude_code_run.py:340
Finding

Interactive tmux Mode Bypasses the Configured Runtime Timeout

Content
View full analysis
0: time.sleep(args.interactive_wait_s) try: snap = tmux_capture(socket_path, target, lines=200) print("\n--- tmux snapshot (last 200 lines) ---\n") print(snap) except subprocess.CalledProcessError: pass return 0 ``` Automatic mode selection routes slash-prefixed prompts into this path, where `max_seconds` is not used: ```python mode = args.mode if mode == "auto" and looks_like_slash_commands(args.prompt): mode = "interactive" if mode == "interactive": return run_interactive_tmux(args) cmd = build_headless_cmd(args) env = build_agent_teams_env(args) return run_with_pty(cmd, cwd=args.cwd, env=env, max_seconds=args.max_seconds) ``` ### Technical Analysis Timeout enforcement is implemented only in `run_with_pty`, which is used by headless mode. Interactive mode creates a detached tmux session, sends the command and prompt, and then returns success without supervising the tmux process. Because `auto` mode chooses interactive operation whenever a prompt line begins with `/`, a caller can trigger this behavior solely through prompt content. The parent dispatcher may then record successful completion while the Claude Code process continues running independently. This does not install a startup service or cross-reboot backdoor, so it is classified as ...[truncated 1044 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/vendor/dispatch.sh:111
Finding

Prompts, Session Metadata, and Model Output Stored in Plaintext Without Permission Hardening

Content
View full analysis
"$META_FILE" ``` The complete command, including the prompt argument, and all runner output are also logged: ```bash echo "🚀 Launching Claude Code..." echo " Command: ${CMD[*]}" echo "" "${CMD[@]}" 2>&1 | tee "$TASK_OUTPUT" ``` The outer launcher redirects this output to another log file: ```bash nohup "${CMD[@]}" >"$RUN_LOG" 2>&1 & ``` ### Technical Analysis The dispatcher persists full prompt text, callback identifiers, session information, command-line contents, and model output. It does not set a restrictive `umask`, explicitly assign secure file modes, redact sensitive values, or define retention and deletion behavior. Actual exposure depends on the permissions of the parent directories and the ambient process umask. If those controls are permissive, other local users or services may read task prompts and outputs. Printing `${CMD[*]}` duplicates the full prompt into the launch log because the prompt is included in the Python runner command. No code implementing network deli ...[truncated 1141 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (39)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as a straightforward async dispatcher, but the analyzed behavior includes callback configuration reads, persistent output/metadata storage, message-routing parameters, prompt augmentation, and possibly synchronous execution. Hidden persistence, routing, and prompt injection paths increase data exposure and make it easier for task content or results to be redirected or handled in ways the user did not knowingly approve.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill is presented as a straightforward async dispatcher, but the analyzed behavior includes callback configuration reads, persistent output/metadata storage, message-routing parameters, prompt augmentation, and possibly synchronous execution. Hidden persistence, routing, and prompt injection paths increase data exposure and make it easier for task content or results to be redirected or handled in ways the user did not knowingly approve.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 95)May include surrounding context.

python
def build_agent_teams_env(args: argparse.Namespace) -> dict[str, str]:
    """Build environment dict with Agent Teams support."""
    env = os.environ.copy()
    if args.agent_teams:
        env["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"] = "1"
    return env

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill invokes a shell script with user-supplied arguments and references environment-driven behavior, but it declares no tool scope or permission boundaries. That makes the skill's effective capabilities opaque to reviewers and enforcement layers, increasing the chance of unintended shell execution or environment misuse without explicit consent controls.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/run_dispatch.sh (reported line 120)May include surrounding context.

sh
exit 0
fi

nohup "${CMD[@]}" >"$RUN_LOG" 2>&1 &
PID=$!

# Quick startup sanity check: allow immediate successful completion,

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The wrapper explicitly supports interactive tmux sessions and slash-command handling, which conflicts with the skill's stated purpose of non-blocking headless dispatch. This scope expansion is dangerous because it introduces persistent operator-like interaction paths and richer command surfaces than consumers of the manifest would reasonably expect.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Agent Teams support allows multi-agent orchestration beyond the dispatch skill's described purpose. In a security review, this matters because it can amplify resource usage, coordination complexity, and the number of autonomous actions performed under one invocation.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
89% confidence
Finding

This wrapper executes a user-controlled Claude CLI command and, when script is present, passes a single shell command string via script -c. Although arguments are shell-quoted, the code still launches an external program based on user/environment-controlled inputs (claude_bin, prompt/options, extra args), so this is a real command-execution sink with meaningful security impact in an agent skill context.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 112)May include surrounding context.

python
script_bin = which("script")
    proc_args = [script_bin, "-q", "-c", cmd_str, "/dev/null"] if script_bin else cmd

    proc = subprocess.Popen(
        proc_args,
        cwd=cwd,
        text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 160)May include surrounding context.

python
def tmux_capture(socket_path: str, target: str, lines: int = 200) -> str:
    out = subprocess.check_output(
        tmux_cmd(socket_path, "capture-pane", "-p", "-J", "-t", target, "-S", f"-{lines}"),
        text=True,
    )

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_output (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 160)May include surrounding context.

python
def tmux_capture(socket_path: str, target: str, lines: int = 200) -> str:
    out = subprocess.check_output(
        tmux_cmd(socket_path, "capture-pane", "-p", "-J", "-t", target, "-S", f"-{lines}"),
        text=True,
    )

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Persistent tmux session management is an unjustified capability for a dispatch helper meant to launch headless async jobs. It enables long-lived hidden execution contexts, post-launch interaction, prompt automation, and cross-session interference, all of which materially increase attack surface and reduce transparency.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 192)May include surrounding context.

python
session = args.tmux_session
    target = f"{session}:0.0"

    subprocess.run(tmux_cmd(socket_path, "kill-session", "-t", session), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    subprocess.check_call(tmux_cmd(socket_path, "new", "-d", "-s", session, "-n", "shell"))

    cwd = args.cwd or os.getcwd()

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 192)May include surrounding context.

python
session = args.tmux_session
    target = f"{session}:0.0"

    subprocess.run(tmux_cmd(socket_path, "kill-session", "-t", session), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    subprocess.check_call(tmux_cmd(socket_path, "new", "-d", "-s", session, "-n", "shell"))

    cwd = args.cwd or os.getcwd()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 193)May include surrounding context.

python
target = f"{session}:0.0"

    subprocess.run(tmux_cmd(socket_path, "kill-session", "-t", session), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    subprocess.check_call(tmux_cmd(socket_path, "new", "-d", "-s", session, "-n", "shell"))

    cwd = args.cwd or os.getcwd()

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_call (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 193)May include surrounding context.

python
target = f"{session}:0.0"

    subprocess.run(tmux_cmd(socket_path, "kill-session", "-t", session), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    subprocess.check_call(tmux_cmd(socket_path, "new", "-d", "-s", session, "-n", "shell"))

    cwd = args.cwd or os.getcwd()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 199)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_call (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 199)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 200)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

    claude_parts = [args.claude_bin]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 224)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

    claude_parts = [args.claude_bin]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 245)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

    claude_parts = [args.claude_bin]

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_call (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 200)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

    claude_parts = [args.claude_bin]

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_call (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 224)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

    claude_parts = [args.claude_bin]

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_call (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 245)May include surrounding context.

python
# Set Agent Teams env var inside tmux session if enabled
    if args.agent_teams:
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", "export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"))
        subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))
        time.sleep(0.3)

    claude_parts = [args.claude_bin]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The code assembles a shell command string (cd ... && ...) and sends it into a tmux shell for execution. Even though components are quoted, this intentionally converts user-influenced values (cwd, claude_bin, flags, extra args) into shell-interpreted text, making the tmux shell an execution sink and significantly increasing the blast radius if inputs or upstream trust assumptions fail.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 223)May include surrounding context.

python
claude_parts += args.extra

    launch = f"cd {shlex.quote(cwd)} && " + " ".join(shlex.quote(p) for p in claude_parts)
    subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", launch))
    subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))

    # Workspace trust prompt (first run in a new folder).

Tainted flow: 'socket_path' from os.environ.get (line 187, credential/environment) → subprocess.check_call (code execution)

Medium
Category
Data Flow
Confidence
84% confidence
Finding

The tainted socket path can redirect where the shell-launch command is sent, so an attacker controlling the environment may cause this wrapper to type and execute commands in an unintended tmux server/session. Because line 223 sends an executable shell command, misrouting it has real integrity and confidentiality implications.

Content

Scanner excerpt · scripts/vendor/claude_code_run.py (reported line 223)May include surrounding context.

python
claude_parts += args.extra

    launch = f"cd {shlex.quote(cwd)} && " + " ".join(shlex.quote(p) for p in claude_parts)
    subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "-l", "--", launch))
    subprocess.check_call(tmux_cmd(socket_path, "send-keys", "-t", target, "Enter"))

    # Workspace trust prompt (first run in a new folder).

Static analysis

No suspicious patterns detected.