Back to skill

Security audit

ollama-task-orchestrator

Security checks for vulnerabilities and agentic risk

Overview

This skill is coherent, but it gives agents broad SSH-based shell, file-write, and process-control power on a worker machine without strong guardrails.

Install only on a dedicated, low-privilege worker account with isolated non-production project directories. Treat this as remote shell access for any agent that can call it, disable or remove exec and NL exec unless truly needed, keep DEFAULT_PROJECT tightly controlled, review file writes before use, and avoid using shared workers or accounts with SSH keys, cloud credentials, or sensitive repositories.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
runner/run_task.sh:232
Finding
Unrestricted Remote Shell Command Execution Through the Exec Task<![CDATA[ ## Vulnerability Details **File Location**: `runner/run_task.sh`, lines 232-243 and 562-564 **Vulnerability Type**: Arbitrary command execution without authorization or confinement **Risk Level**: Critical ### Vulnerable Code ```bash run_exec() { local cmd="$1" if [ -z "$cmd" ]; then echo "ERROR: no command provided" exit 1 fi if [ "$DRY_RUN" = true ]; then echo "DRY RUN: would run shell command" echo "Command: $cmd" return 0 fi eval "$cmd" } ``` The task dispatcher exposes the function directly: ```bash exec) CMD="${*}" run_exec "$CMD" ;; ``` The Python entry point sends user-controlled task arguments to this runner over SSH: ```python def run_task(self, task_command: str) -> str: """Run a task on the worker Mac with exclusivity locking.""" with self.lock: cmd = [f"{self.runner_path}/run_task.sh", *shlex.split(task_command)] stdout, stderr, code = self._ssh(cmd) ``` ### Technical Analysis The `exec` task intentionally accepts an arbitrary command string and evaluates it with Bash `eval`. The implementation has no command allowlist, authorization check, confirmation requirement, working-directory restriction, sandbox, or project-level confinement. Although remote command execution is documented as an available feature, it exceeds the privileges required for the Skill's primary queue-management, code-generation, controlled file-writing, and test-running functions. Any caller that can cause the Skill to process an `ollama run exec` request effectively obtains a shell under the SSH worker account. Using `eval` also causes shell syntax in the supplied value to be interpreted, including command substitutions, redirections, pipelines, variable expansion, and command separators. Quoting performed by the Python SSH layer does not make this safe because the runner deliberately re-evaluates the reconstructed string. ### Attack Path 1. An attacker supplies or induces an Agent to pro ...[truncated 1047 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `exec` task unless unrestricted shell access is an essential and explicitly approved requirement. 2. Replace arbitrary commands with narrowly scoped, structured operations such as fixed test, build, lint, and status subcommands. 3. If command execution must remain available: - Disable it by default through an administrator-controlled setting. - Add caller authentication and per-action authorization. - Require an explicit human confirmation for dangerous operations. - Use a strict executable and argument allowlist. - Invoke commands through argument arrays rather than `eval` or `bash -c`. - Run commands in a dedicated, minimally privileged service account or sandbox. - Restrict filesystem and network access to the required project resources. 4. Ensure untrusted natural-language content cannot select the direct `exec` action. 5. Add audit logging that records the authenticated caller, exact executable and arguments, time, working directory, and result. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
runner/run_task.sh:162
Finding
Generated File Writes Can Escape the Intended Project Boundary<![CDATA[ ## Vulnerability Details **File Location**: `runner/run_task.sh`, lines 162-196 **Vulnerability Type**: Insufficient path validation and symlink traversal **Risk Level**: High ### Vulnerable Code ```bash run_write() { local rel_file="$1" local instruction="$2" if [ -z "$rel_file" ] || [ -z "$instruction" ]; then echo "ERROR: missing file path or instruction" echo "Usage: run_task.sh write <relative/path/file.ext> <instruction>" exit 1 fi require_default_project case "$rel_file" in /*|../*|*/../*|..) echo "ERROR: file path must stay inside the project directory." exit 1 ;; esac local target_file="$PROJECTS_DIR/$DEFAULT_PROJECT/$rel_file" if [ "$DRY_RUN" = true ]; then echo "DRY RUN: would write generated code" echo "Target: $target_file" echo "Instruction: $instruction" return 0 fi local tmp_file tmp_file=$(mktemp /tmp/ollama_output_XXXXXX) mkdir -p "$(dirname "$target_file")" ollama_generate "$instruction" true > "$tmp_file" strip_to_code "$tmp_file" > "$target_file" rm -f "$tmp_file" echo "WROTE: $target_file" } ``` ### Technical Analysis The code validates only the caller-supplied `rel_file` value using lexical shell patterns. It does not validate `DEFAULT_PROJECT`, canonicalize the final destination, or verify that the resolved path remains under `PROJECTS_DIR`. A `DEFAULT_PROJECT` value containing traversal components can therefore move the final destination outside the configured projects directory. In addition, shell redirection follows symbolic links. A malicious or compromised project can place a symlink at the target location and cause generated output to overwrite a file outside the project. The check against `../` in `rel_file` does not prevent either condition because confinement is never enforced against the canonical final path. ### Attack Path #### Configuration traversal 1. An attacker gains control over or influences the worker's `DEFAULT ...[truncated 1373 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict `DEFAULT_PROJECT` to a single project-directory name using a conservative allowlist such as letters, digits, underscores, and hyphens. 2. Canonicalize `PROJECTS_DIR`, the selected project directory, and the final destination using `realpath`. 3. Verify that the canonical destination begins with the canonical project root followed by a path separator. 4. Reject any destination when an existing path component is a symbolic link. 5. Open the destination using a mechanism that refuses symlink following, such as `openat2` with appropriate resolution flags on supported systems. 6. Ensure the selected project itself is a real directory directly beneath `PROJECTS_DIR`. 7. Write to a temporary file inside the validated project directory and replace the destination atomically only after all checks pass. 8. Run the writer under a dedicated account that has write access only to approved project directories. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
runner/queue_status.sh:69
Finding
Queue Cleanup Can Terminate Unrelated Worker Processes<![CDATA[ ## Vulnerability Details **File Location**: `runner/queue_status.sh`, lines 69-71 and 115-150 **Vulnerability Type**: Overbroad process matching and unconditional force termination **Risk Level**: High ### Vulnerable Code The script identifies processes using broad command-line substring matching: ```bash RUNNERS=$(pgrep -f "run_task.sh" 2>/dev/null | tr '\n' ' ') CURLS=$(pgrep -f "curl.*11434" 2>/dev/null | tr '\n' ' ') ``` The Ollama restart path kills every process whose command line contains `ollama`: ```bash if [ "$OLLAMA_BUSY" = true ] && [ "$FORCE" = "--kill-ollama" ]; then echo -e "${RED}--kill-ollama: stopping Ollama server to cancel active generation...${NC}" pkill -9 -f "ollama" 2>/dev/null sleep 3 echo "Restarting Ollama server..." nohup ollama serve > /tmp/ollama.log 2>&1 & sleep 5 echo -e "${GREEN}Ollama restarted.${NC}" ``` The ordinary cleanup path also force-kills all broadly matched runners and curl processes: ```bash # Kill stuck runner bash processes (force kill) if [ -n "$RUNNERS" ]; then echo "Killing run_task.sh processes: $RUNNERS" kill -9 $RUNNERS 2>/dev/null fi # Kill curl processes if [ -n "$CURLS" ]; then echo "Killing curl processes: $CURLS" kill -9 $CURLS 2>/dev/null fi ``` ### Technical Analysis `pgrep -f` and `pkill -f` match against entire command lines. The patterns are not tied to the lock PID, a known parent process, a specific executable path, or a process created by this Skill. Consequently, any process with `run_task.sh`, `curl` followed by `11434`, or `ollama` in its command line may be selected. The script then uses `SIGKILL`, which prevents graceful cleanup and cannot be handled by the target. The cleanup operation also kills all matched runner processes rather than limiting termination to a verified stale task. This behavior exceeds the minimum privilege required to remove a stale lock and can interfere with unrelated Ollama users or other a ...[truncated 1294 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Record the exact PID and process start time for every runner started by this Skill. 2. Before terminating a process, verify: - The PID matches protected runner state. - The process is owned by the expected worker account. - The executable path is the expected runner or Ollama binary. - The process start time matches the recorded value, preventing PID-reuse errors. 3. Limit stale-lock cleanup to the process identified by the lock metadata rather than all matching processes. 4. Send `SIGTERM` first, wait for a bounded grace period, and use `SIGKILL` only after identity is revalidated and graceful termination fails. 5. Replace `pkill -f "ollama"` with service-specific lifecycle management or an exact, verified Ollama server PID. 6. Separate lock removal from process termination. Removing a demonstrably stale lock should not automatically kill unrelated runners. 7. Require explicit human confirmation for terminating an active verified job, including in noninteractive integrations. 8. Update the documentation so `clean --force` is not recommended merely because Ollama is busy; it should be reserved for a verified stale task. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (19)

Credential Access

High
Category
Privilege Escalation
Content
Host <your-worker-host-alias>
  HostName <your-worker-ip-or-hostname>
  User <your-username>
  IdentityFile ~/.ssh/id_ed25519
```

### Tell Claude Code about the runner
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Host <your-worker-host-alias>
  HostName <your-worker-ip-or-hostname>
  User <your-username>
  IdentityFile ~/.ssh/id_ed25519
```

### Tell Claude Code about the runner
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

External Script Fetching

High
Category
Supply Chain
Content
print_status() {
  local active
  local api_base="${OLLAMA_URL%/api/generate}"
  active=$(curl -s --max-time 3 "$api_base/api/ps" 2>/dev/null | python3 -c \
    "import sys,json; m=json.load(sys.stdin).get('models',[]); print(m[0]['name']+' (busy)' if m else 'idle')" 2>/dev/null || echo "unknown")
  local lock_state="clear"
  if [ -d "$LOCK_DIR" ]; then
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
local json_prompt
  json_prompt=$(printf '%s' "$prompt" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')

  curl -s --max-time "$OLLAMA_TIMEOUT" "$OLLAMA_URL" -d "{
    \"model\": \"$OLLAMA_MODEL\",
    \"prompt\": $json_prompt,
    \"stream\": false
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Missing User Warnings

High
Confidence
97% confidence
Finding
In run_nl, once routing resolves to exec, the command is echoed and immediately passed to run_exec with no confirmation, sandboxing, or policy enforcement. Because routing may be rule-based or LLM-based, this enables arbitrary shell execution from natural-language input, which is especially risky in an agent/automation context where prompts may be attacker-influenced.

Intent-Code Divergence

High
Confidence
95% confidence
Finding
The script presents the nl command as a safe natural-language router, but its implementation can dispatch to exec and run arbitrary shell commands when ALLOW_NL_EXEC=true. This creates a dangerous trust boundary failure: a user may provide a natural-language request expecting constrained behavior, yet the router can translate that request into direct command execution without a separate explicit approval step.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This markdown file explicitly instructs the agent to use a `write` command that writes generated output directly to a specified file. Although the behavior is described later in the notes, there is no nearby warning about overwriting or modifying user files, which is relevant because markdown files should warn about behaviors affecting user data or system integrity.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The documentation tells Codex to use a forceful cleanup command to clear stale locks, but does not warn that forcibly removing locks can disrupt in-progress jobs or affect runner state. For markdown files, potentially integrity-affecting actions should be disclosed to the user.

Session Persistence

Medium
Category
Rogue Agent
Content
An [OpenClaw](https://openclaw.ai) skill that lets your AI agents queue and execute tasks on a local Ollama instance running on a worker Mac (or any machine accessible via SSH).

Agents can check Ollama's queue health, run code generation tasks, write files, run tests, or execute arbitrary shell commands — all through a simple `ollama status` / `ollama run` interface.

---
Confidence
87% confidence
Finding
The skill is designed to provide persistent remote task execution capability via SSH, including arbitrary command execution and file modification on a worker machine. In an agent ecosystem, this meaningfully expands the blast radius of prompt injection or agent misuse because a compromised or overly trusted agent can repeatedly act on the remote host, not just within the local chat context.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly advertises that agents can write files and execute arbitrary shell commands over SSH, but it does not include any safety boundaries, trust assumptions, or warnings about system and data impact. In the context of an agent skill, this can normalize highly dangerous capabilities and lead operators to deploy it without understanding that prompt-driven actions may modify projects, access sensitive files, or damage the worker host.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly offers a `write` task that generates and writes code to a file, but the specification does not warn that invoking it can modify files on a remote worker or project workspace. In agent settings, lack of an explicit modification warning can cause users or higher-level agents to trigger state-changing actions without informed consent, increasing the risk of unintended overwrite or persistence of unsafe generated code.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The `exec` task advertises arbitrary shell-command execution with no accompanying safety notice, guardrails, or restriction language. In an agent skill, this effectively exposes remote command execution capability; without prominent warnings and constraints, it can be abused to run destructive commands, access sensitive data, or pivot further on the worker machine.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "Runner target: $RUNNER_DIR"
echo ""

mkdir -p "$RUNNER_DIR"

cp "$SCRIPT_DIR/runner/run_task.sh" "$RUNNER_DIR/run_task.sh"
cp "$SCRIPT_DIR/runner/queue_status.sh" "$RUNNER_DIR/queue_status.sh"
Confidence
60% 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.

Session Persistence

Medium
Category
Rogue Agent
Content
pkill -9 -f "ollama" 2>/dev/null
    sleep 3
    echo "Restarting Ollama server..."
    nohup ollama serve > /tmp/ollama.log 2>&1 &
    sleep 5
    echo -e "${GREEN}Ollama restarted.${NC}"
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.

External Transmission

Medium
Category
Data Exfiltration
Content
kill -9 $RUNNERS 2>/dev/null
  fi

  # Kill curl processes
  if [ -n "$CURLS" ]; then
    echo "Killing curl processes: $CURLS"
    kill -9 $CURLS 2>/dev/null
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
local json_prompt
  json_prompt=$(printf '%s' "$prompt" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')

  curl -s --max-time "$OLLAMA_TIMEOUT" "$OLLAMA_URL" -d "{
    \"model\": \"$OLLAMA_MODEL\",
    \"prompt\": $json_prompt,
    \"stream\": false
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
remote_command = command
        else:
            remote_command = shlex.join(command)
        result = subprocess.run(
            ["ssh", self.worker_host, remote_command],
            capture_output=True,
            text=True,
Confidence
96% confidence
Finding
This SSH invocation executes a remote shell command constructed from user-influenced input. In this skill, both `queue_status(args)` and `run_task(task_command)` accept untrusted text, transform it with `shlex.split/join`, and pass the resulting string as the command argument to `ssh`, which is interpreted by the remote shell; shell metacharacters, option injection, or downstream script argument abuse can therefore lead to arbitrary command execution on the worker host. The skill’s purpose is remote task orchestration, which makes this more dangerous because it is explicitly designed to run commands on another machine.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The configuration section explains DEFAULT_PROJECT functionally but does not clearly warn that it defines the target project context for generated writes and tests. That omission increases the chance of operators pointing the skill at a sensitive or production repository and unintentionally authorizing AI-driven modifications there.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The overview states that the skill manages a remote worker over SSH for task execution and code generation, but it does not clearly warn about remote system impact or that actions occur on another machine. This omission increases the chance that operators underestimate the consequences of commands, including changing remote state, consuming resources, killing processes, or affecting shared project environments.

Static analysis

No suspicious patterns detected.