Back to skill

Security audit

Autoloop Controller

Security checks for vulnerabilities and agentic risk

Overview

This skill is not clearly malicious, but it deserves review because it can repeatedly run an automated skill-improvement subprocess, persist state, and schedule background runs while its cost limit is only an estimate.

Install only if you intentionally want an agent to run repeated skill-improvement cycles. Start with --dry-run, low --max-iterations, and a low --max-cost, and supervise initial runs. Treat --max-cost as an estimate rather than a hard spending limit. Avoid cron until you have reviewed run-eval.sh, chosen safe target and state paths, set appropriate permissions, and know how to remove the crontab entry.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/autoloop.py:159
Finding

Configured Cost Cap Does Not Reliably Enforce the Spending Limit

Content
View full analysis

Vulnerability Details

File Location: scripts/autoloop.py:159-164, 211-216, 269-291
Vulnerability Type: Inadequate resource and cost-limit enforcement
Risk Level: Medium

Vulnerable Code

python
proc = subprocess.run(cmd, capture_output=True, text=True)

duration = time.monotonic() - iter_start

if proc.returncode != 0:
    state.last_failure_trace = proc.stderr.strip() or proc.stdout.strip()
    state.status = "error"
    result = {
        "target": target,
        "attempts": 0,
        "max_retries": 0,
        "final_decision": "error",
        "final_candidate_id": None,
        "final_artifact_path": None,
        "error": state.last_failure_trace,
    }
    return state, result

result = _parse_orchestrator_output(proc.stdout, target)
cost = _estimate_cost(duration)
python
# Update cost
cost_val = cost if not dry_run else 0.0
state.total_cost_usd += cost_val
python
def _estimate_cost(duration_seconds: float, result: dict[str, Any] | None = None) -> float:
    """Estimate cost from token usage if available, else duration heuristic."""
    if result:
        summary_path = result.get("final_artifact_path")
        if summary_path:
            try:
                data = read_json(Path(summary_path))
                input_tokens = data.get("total_input_tokens", 0)
                output_tokens = data.get("total_output_tokens", 0)
                if input_tokens > 0 or output_tokens > 0:
                    cost = (input_tokens * 3.0 + output_tokens * 15.0) / 1_000_000
                    return round(cost, 4)
            except (OSError, KeyError, TypeError):
                pass
    return round(duration_seconds / 60.0 * 0.10, 4)

Technical Analysis

The application checks the cumulative cost before beginning an iteration, but it does not reserve the maximum possible cost of that iteration or enforce a limit while the ...[truncated 1880 chars]

Remediation
View remediation

Remediation Suggestions

  1. Collect authoritative token usage or provider billing data from every orchestrator run instead of relying primarily on elapsed time.
  2. Pass the parsed result to the estimator:
    python
    result = _parse_orchestrator_output(proc.stdout, target)
    cost = _estimate_cost(duration, result)
    
  3. Validate that the referenced summary artifact belongs to the current run before using its token counts.
  4. Before launching an iteration, reserve a conservative worst-case amount and refuse to start when the remaining budget cannot cover it.
  5. Where supported, propagate explicit token, request, and monetary limits to the downstream orchestrator or provider API.
  6. Add timeout and cancellation controls so a running iteration can be stopped when a resource threshold is reached.
  7. Clearly document whether --max-cost is a hard cap or an estimate. Rename it if strict enforcement cannot be provided.
  8. Add tests proving that token-based accounting is used, that an iteration cannot start without sufficient remaining budget, and that a single run cannot silently exceed its allocated limit.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description emphasizes continuous automated improvement via a persistent loop with convergence detection, cost control, and cross-session state persistence. The supplied code chunk does not implement those behaviors itself: it simply records logs and calls autoloop.py once with '--mode single-run'. That directly conflicts with the stated 'persistent loop' and makes the visible primary behavior a single-run wrapper rather than an autoloop manager. While some of the claimed functionality could exist inside autoloop.py, it is not evidenced in this chunk, and the explicit single-run mode suggests the opposite. Therefore the description does not accurately represent what this supplied code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied code does not implement any continuous improvement behavior. It is a simple test configuration helper for Python pytest that modifies sys.path to include a scripts directory for imports. This is materially different from the declared purpose of persistent automated skill improvement with looping, convergence detection, and state persistence. The declared triggers also do not correspond to the observed behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a high-level orchestration skill that continuously improves another skill in a persistent loop with convergence detection and state persistence. The supplied code does not implement such orchestration. Instead, it only contains tests for a cost tracker, specifically checking cost accumulation, iteration counting, immutability, over-budget status, and summary output. While cost control is mentioned in the description and the tested component relates to budgeting, that is only one supporting aspect and does not match the primary declared purpose. There is no evidence here of continuous improvement, looping, convergence detection, persistence, or trigger-related behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger "keep improving" is extremely vague and likely to occur in normal dialogue, making accidental invocation substantially more likely than with a narrowly scoped command. In this skill's context, accidental activation is more dangerous because the documented behavior includes repeated iterations, persistent state, shell usage, and potential ongoing spend.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documented behavior says single-run should perform one iteration and exit, but the implementation loops again unless a termination condition is reached. In an automation skill that invokes an improvement orchestrator and tracks cost, this mismatch can cause unintended repeated executions, excess spend, and repeated modification attempts when an operator expects a safe one-shot run.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The inline documentation explicitly promises one-shot behavior for single-run, yet the control flow continues iterating until stop criteria are met. In this skill context, that contradiction is dangerous because users may rely on the documented mode to bound runtime, costs, and automated changes, but the code silently behaves like a persistent loop.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill documents shell execution and file-writing behavior via the CLI and state/artifact generation, but it declares no explicit tool scope such as permissions or allowed-tools. In an orchestration skill that launches subprocesses and writes persistent state, missing capability declarations weakens containment and makes accidental or overbroad tool access more likely.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The trigger phrase "continuous improvement" is broad enough to match ordinary conversation and could invoke this skill unexpectedly. Because this skill is designed to run iterative orchestration, accidental activation could lead to unintended subprocess execution, file writes, and cost-incurring loops in contexts where the user did not intend to start automation.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

md
</example>

<anti-example>
Running continuous mode without checking cost cap:
$ python3 scripts/autoloop.py --target /path/to/skill --state-root ./state --mode continuous --max-cost 50.0
→ Default $50 cap applies. For overnight runs, explicitly set --max-cost.
→ Always verify cost cap BEFORE launching continuous mode.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

The documentation promotes creation of a persistent script under the user's skill directory and registration in cron, which establishes cross-session persistence. While persistence is part of the skill's purpose, documenting a persistence mechanism without guardrails can normalize long-lived automation that continues after the user forgets about it, especially if the script is later modified or points at unsafe targets.

Content

Scanner excerpt · references/scheduling-guide.md (reported line 6)May include surrounding context.

md
## Recommended: Shell Script + System Cron

### Setup
1. Create run script:
   ~/.claude/skills/autoloop-controller/scripts/run-eval.sh

2. Add to crontab:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The guide instructs users to schedule unattended execution of a local script via system cron, but it provides no warning to review the script, constrain its permissions, or understand the effects of repeated background execution. In the context of an autoloop controller that persistently modifies or evaluates skills, this increases the risk of unintended repeated actions, resource consumption, or persistence of unsafe behavior if the script or its inputs are tampered with.

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/autoloop.py (reported line 160)May include surrounding context.

python
"--state-root", str(state_root),
            "--auto",
        ]
        proc = subprocess.run(cmd, capture_output=True, text=True)

        duration = time.monotonic() - iter_start