Back to skill

Security audit

Autonomous Skill

Security checks for vulnerabilities and agentic risk

Overview

This skill is designed for autonomous work, but it gives unattended child agents broad default authority and persistent loop behavior that users should review carefully before installing.

Review this skill before installing. Use it only in isolated workspaces, set explicit max session or iteration limits, avoid bypassPermissions unless you intentionally want unattended broad access, and expect local task logs/transcripts under .autonomous plus hook state under .claude. Do not run it on untrusted prompts or repositories without sandboxing.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/run-session.sh:26
Finding
Autonomous headless sessions bypass permission checks by default<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run-session.sh:26`, `scripts/run-session.sh:38-40`, and `scripts/run-session.sh:243-249` **Vulnerability Type**: Default permission bypass and removal of nested-session protection **Risk Level**: High ### Vulnerable Code ```bash DEFAULT_PERMISSION_MODE="bypassPermissions" ``` ```bash # Allow spawning claude -p from within an interactive Claude Code session. # Without this, Claude Code refuses to launch nested sessions. unset CLAUDECODE 2>/dev/null || true ``` ```bash build_claude_args() { local -a args=() args+=(--output-format stream-json --verbose) args+=(--model "$opt_model") args+=(--effort "$opt_effort") args+=(--max-budget-usd "$opt_max_budget") args+=(--permission-mode "$opt_permission_mode") args+=(--no-session-persistence) [ -n "$opt_fallback_model" ] && args+=(--fallback-model "$opt_fallback_model") [ -n "$opt_add_dir" ] && args+=(--add-dir "$opt_add_dir") echo "${args[@]}" } ``` ### Technical Analysis The headless runner defaults to Claude Code's `bypassPermissions` mode. This removes interactive authorization checks from autonomous tool operations rather than requiring users to opt into that behavior explicitly. The script also unsets `CLAUDECODE`, which disables the environment-based protection that ordinarily prevents a Claude Code process from launching another nested Claude Code session. Each resulting child session may receive up to 100 turns, and the outer session loop is unlimited unless the caller explicitly supplies a limit. These behaviors combine to create an execution environment in which task instructions can cause commands and file operations to run repeatedly without user approval. This is especially dangerous when the task incorporates untrusted repository content, issue descriptions, generated tracking files, or other prompt-injection-capable material. The documented behavior is inconsistent with the implementation: `SKI ...[truncated 1849 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the default to a restrictive permission mode, such as the documented `auto` mode: ```bash DEFAULT_PERMISSION_MODE="auto" ``` 2. Require an explicit, prominently documented opt-in flag before permitting `bypassPermissions`. 3. Display a clear warning and request interactive confirmation when bypass mode is selected. 4. Do not unset `CLAUDECODE` automatically. If nested execution is essential, require a separate explicit opt-in and explain the security consequences. 5. Apply a finite default session limit and require an explicit flag for unlimited operation. 6. Constrain accessible directories and tools to the minimum needed for the task. 7. Avoid exposing secrets in the runner's environment, and execute autonomous sessions inside a sandbox or isolated container where possible. 8. Update `SKILL.md` so its documented default permission mode exactly matches the implementation. 9. Add tests that verify the safe permission mode and finite session limit remain the defaults. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup-loop.sh:51
Finding
Unvalidated task name permits filesystem path traversal in hook setup<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup-loop.sh:51-54`, `scripts/setup-loop.sh:77-89` **Vulnerability Type**: Path traversal through an unvalidated task name **Risk Level**: Medium ### Vulnerable Code ```bash --task-name) [[ -z "${2:-}" ]] && { echo "Error: --task-name requires a name" >&2; exit 1; } TASK_NAME="$2"; shift 2 ;; ``` ```bash # Generate task name if not provided if [[ -z "$TASK_NAME" ]]; then TASK_NAME=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | cut -c1-30 | sed 's/^-//' | sed 's/-$//') [[ -z "$TASK_NAME" ]] && TASK_NAME="task-$(date +%Y%m%d-%H%M%S)" fi TASK_DIR=".autonomous/$TASK_NAME" # Set up task directory for structured mode if [[ "$MODE" == "structured" ]]; then mkdir -p "$TASK_DIR/sessions" fi ``` ### Technical Analysis An explicitly supplied `--task-name` is accepted without validating path separators, `..` components, control characters, or an allowlisted name format. The value is directly concatenated with `.autonomous/` and then used as a filesystem path. For example, a task name such as `../../outside-target` makes `TASK_DIR` resolve outside the intended `.autonomous` tracking directory. In structured mode, the script immediately creates the resulting directory hierarchy with `mkdir -p`. The unsafe path is also written into `.claude/autonomous-loop.local.md`. The Stop hook later trusts the stored `task_dir` value when checking `task_list.md` and reporting progress. This extends the effect beyond initial directory creation and causes later hook activity to reference an attacker-selected location. The headless runner contains a `validate_task_name` function, but equivalent validation is missing from `setup-loop.sh`. ### Attack Path 1. An attacker or unsafe wrapper supplies a crafted argument such as: ```bash bash scripts/setup-loop.sh "Perform task" \ --mode structured \ --task-name ../../outside-target ``` 2. The script a ...[truncated 1285 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reuse or centralize the task-name validation already present in `run-session.sh`. 2. Enforce a strict allowlist, for example: ```bash if [[ ! "$TASK_NAME" =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$ ]]; then echo "Error: Invalid task name" >&2 exit 1 fi ``` 3. Reject `..`, `/`, `\`, whitespace, newlines, leading hyphens, and control characters. 4. Resolve the canonical `.autonomous` root and candidate task path, then verify the candidate remains beneath the root before creating directories. 5. Use `mkdir -- "$TASK_DIR/sessions"` after validation to terminate option parsing defensively. 6. Validate the `task_dir` field again in `stop-hook.sh`; do not trust the persisted state file merely because this setup script created it. 7. Add regression tests covering absolute paths, traversal paths, encoded separators, newlines, and leading-hyphen names. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup-loop.sh:44
Finding
Unescaped arguments allow hook-state frontmatter injection and corruption<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup-loop.sh:44-54` and `scripts/setup-loop.sh:92-116` **Vulnerability Type**: Unsafe serialization of user-controlled data into YAML-like frontmatter **Risk Level**: Medium ### Vulnerable Code ```bash --max-iterations) [[ -z "${2:-}" ]] && { echo "Error: --max-iterations requires a number" >&2; exit 1; } MAX_ITERATIONS="$2"; shift 2 ;; --completion-promise) [[ -z "${2:-}" ]] && { echo "Error: --completion-promise requires text" >&2; exit 1; } COMPLETION_PROMISE="$2"; shift 2 ;; --mode) [[ -z "${2:-}" ]] && { echo "Error: --mode requires structured or lightweight" >&2; exit 1; } MODE="$2"; shift 2 ;; --task-name) [[ -z "${2:-}" ]] && { echo "Error: --task-name requires a name" >&2; exit 1; } TASK_NAME="$2"; shift 2 ;; ``` ```bash # Quote values for YAML if [[ -n "$COMPLETION_PROMISE" ]]; then PROMISE_YAML="\"$COMPLETION_PROMISE\"" else PROMISE_YAML="null" fi cat > .claude/autonomous-loop.local.md <<EOF --- active: true iteration: 1 max_iterations: $MAX_ITERATIONS completion_promise: $PROMISE_YAML mode: $MODE task_name: "$TASK_NAME" task_dir: "$TASK_DIR" started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --- $PROMPT EOF ``` The generated state is later parsed by `hooks/stop-hook.sh:20-26`: ```bash FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$STATE_FILE") ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//') MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//') COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/') MODE=$(echo "$FRONTMATTER" | grep '^mode:' | sed 's/mode: *//') TASK_DIR=$(echo "$FRONTMATTER" | grep '^task_dir:' | sed 's/task_dir: *//' | sed 's/^"\(.*\)"$/\1/') ``` ### Technical Analysis Although the file uses YAML-style frontmatter, user-controlled values are inserted using a shell heredoc rather than a YAML-a ...[truncated 2682 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate all option values before writing the state file: - Require `MAX_ITERATIONS` to match `^[0-9]+$`. - Require `MODE` to be exactly `structured` or `lightweight`. - Apply a strict task-name allowlist. - Reject newlines and control characters in completion promises. 2. Serialize state with a YAML- or JSON-aware tool rather than interpolating raw values into a heredoc. JSON with `jq --arg` is suitable because the hook already depends on `jq`. 3. Prefer a dedicated JSON state file, for example: ```bash jq -n \ --argjson iteration 1 \ --argjson max_iterations "$MAX_ITERATIONS" \ --arg completion_promise "$COMPLETION_PROMISE" \ --arg mode "$MODE" \ --arg task_name "$TASK_NAME" \ --arg task_dir "$TASK_DIR" \ '{ active: true, iteration: $iteration, max_iterations: $max_iterations, completion_promise: $completion_promise, mode: $mode, task_name: $task_name, task_dir: $task_dir }' > .claude/autonomous-loop.local.json ``` 4. Parse the state with `jq` and reject missing, duplicate, malformed, or incorrectly typed values. 5. Treat the state file as untrusted inside `stop-hook.sh`; revalidate numeric ranges, enums, safe paths, and string lengths before use. 6. Store the prompt separately from structured control state so that prompt delimiters cannot alter configuration parsing. 7. Create the state file with restrictive permissions and an atomic temporary-file replacement. 8. Add tests for embedded quotes, multiline strings, duplicate keys, frontmatter delimiters, invalid modes, and nonnumeric iteration limits. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
`.claude/autonomous-loop.local.md` and blocks exit until the promise is detected
or max iterations reached.

To cancel an active hook-mode loop: `rm .claude/autonomous-loop.local.md`

## Workflow Detail
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
release_lock() {
    local task_dir="$1"
    rm -f "$task_dir/run.lock"
}

# ── Promise detection ─────────────────────────────────────────────────
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

External Model or Provider Selection

High
Category
Excessive Agency
Content
claude_args=$(build_claude_args)

    local exit_code=0
    claude -p "Task: $task_desc
Task Name: $task_name
Task Directory: $task_dir
Completion Promise: $opt_completion_promise
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

External Model or Provider Selection

High
Category
Excessive Agency
Content
claude_args=$(build_claude_args)

    local exit_code=0
    claude -p "Continue working on the task.
Task Name: $task_name
Task Directory: $task_dir
Completion Promise: $opt_completion_promise
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

External Model or Provider Selection

High
Category
Excessive Agency
Content
claude_args=$(build_claude_args)

    local exit_code=0
    claude -p "$task_desc

---
You are in iteration $session_num of an iterative development loop.
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Agent Config Directory Access

High
Category
Agent Snooping
Content
PROMISE_YAML="null"
fi

cat > .claude/autonomous-loop.local.md <<EOF
---
active: true
iteration: 1
Confidence
88% confidence
Finding
This script writes directly into the agent configuration directory by creating `.claude/autonomous-loop.local.md`, which can alter agent behavior for the current repository or session. In the context of an autonomous, long-running skill, this is especially sensitive because the file enables persistent loop state and can influence future agent execution without additional user confirmation.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The invocation guidance uses broad phrases like "keep working on this," "batch process," and "iterate until done," which can match many ordinary user requests and cause the skill to activate in situations where unattended execution was not explicitly intended. In this skill's context, that increases the chance of autonomous background actions, repeated sessions, and persistent logging being initiated without sufficiently informed user consent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill describes headless mode operationally but does not clearly warn that it spawns unattended child sessions and writes persistent state and transcript logs under project directories. In an autonomous-execution skill, omission of that warning is safety-relevant because users may unknowingly allow background actions and storage of potentially sensitive task content.

Bundled hooks can execute when matching lifecycle events occur.

Medium
Category
Bundled Execution Surface
Confidence
95% confidence
Finding
This hook configuration causes a shell command to execute automatically on the Stop lifecycle event, which creates an implicit code-execution path whenever the skill is installed and used. Because the command points to a bundled script within the skill package, any malicious or unsafe logic in that script would run without an additional explicit user review at trigger time, making this a real execution-surface risk rather than a false positive.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The script defaults Claude Code to `bypassPermissions`, which grants the autonomous agent elevated access without requiring the user to explicitly opt in. In a long-running autonomous skill, that meaningfully increases the blast radius of prompt injection, task abuse, or accidental destructive actions because the agent can act without normal approval gates.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script performs autonomous shell-driven model execution with a permissive default permission mode and no meaningful user-facing warning at runtime. Users may invoke it expecting ordinary task automation, while the agent actually runs with elevated privileges that can modify files or access more data than necessary.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script intentionally unsets the `CLAUDECODE` guard so it can launch nested headless sessions from inside an interactive Claude Code session. That bypasses a safety boundary designed to prevent recursive or unattended agent spawning, making it easier for untrusted task content to escalate into additional autonomous executions.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This hook performs file deletion of the autonomous loop state file as part of several control paths, including corruption handling and normal stop conditions. Although some paths print status messages, there is no user confirmation before removing the file, and the deletion behavior is not otherwise disclosed in this file beyond implementation comments.

Static analysis

No suspicious patterns detected.