Back to skill

Security audit

Arc Free Worker Dispatch 1.1.0

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent OpenRouter task dispatcher, but it has real safety gaps that could send sensitive prompts externally, overwrite files in batch mode, or use paid models despite its free-only promise.

Install only if you are comfortable sending delegated task content to OpenRouter and downstream models. Do not use it for secrets, proprietary code, customer data, regulated records, or sensitive business context. Avoid batch files from untrusted sources, avoid --output in batch mode until validation is fixed, and verify model names manually because some command paths can bypass the advertised free-model restriction.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dispatch.py:196
Finding
Arbitrary File Overwrite Through Batch Output Path## Vulnerability Details **File Location**: `scripts/dispatch.py:196-198` **Vulnerability Type**: Unrestricted file write **Risk Level**: Medium ```python if args.output: with open(args.output, "w") as f: json.dump(results, f, indent=2) ``` ### Technical Analysis The `batch` command opens the user-supplied `--output` path in truncating write mode without calling `_validate_output_path()`. This differs from `cmd_task()`, which attempts to validate its output destination before writing. Consequently, batch mode can overwrite any file writable by the process. The written content is JSON containing model responses, but an attacker may influence those responses through supplied prompts. Even without precise content control, opening an existing file with mode `"w"` immediately truncates it. ### Attack Path 1. An attacker influences a Skill invocation or convinces the user or agent to run the `batch` command. 2. The attacker supplies an existing user-writable configuration, source, or shell initialization file as `--output`. 3. The batch tasks are sent to OpenRouter. 4. The script opens the chosen file in truncating mode without validating its location. 5. The original file is replaced with batch-result JSON, causing corruption or potentially introducing attacker-influenced content. ### Impact Assessment The vulnerability grants write and overwrite capability within the permissions of the user running the Skill. It does not directly elevate operating-system privileges, but it may: - Destroy or corrupt user-owned files. - Cause denial of service by overwriting required configuration or project files. - Modify application behavior if a writable configuration file is targeted. - Contribute to later code execution if an executable or automatically loaded file can be replaced with usable attacker-controlled content. The scope is limited to paths writable by the current process account.
Remediation
## Remediation Suggestions - Call `_validate_output_path(args.output)` before opening the batch output file. - Replace the existing string-prefix containment check with `os.path.commonpath()` to enforce directory boundaries reliably. - Resolve and validate the parent directory and reject unsafe symbolic-link destinations where appropriate. - Restrict output to an explicitly designated output directory rather than the entire home directory. - Use atomic writes through a temporary file in the validated destination directory followed by `os.replace()`. - Consider requiring explicit confirmation before overwriting an existing file.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dispatch.py:183
Finding
Free-Model Allowlist Bypass in Batch Dispatch## Vulnerability Details **File Location**: `scripts/dispatch.py:183-190` **Vulnerability Type**: Missing model authorization validation **Risk Level**: Medium ```python for i, task in enumerate(tasks): prompt = task.get("prompt", "") task_type = task.get("type", "general") model = task.get("model") or MODEL_MAP.get(task_type, "openrouter/free") print(f"[{i+1}/{len(tasks)}] Dispatching to {model}...", file=sys.stderr) result = call_openrouter(prompt, model) ``` ### Technical Analysis The single-task command calls `_validate_model(model)` before contacting OpenRouter. Batch mode derives the model directly from attacker-controllable JSON and passes it to `call_openrouter()` without allowlist validation. This bypasses the Skill's declared free-model restriction. Any OpenRouter model identifier accepted by the associated account can be supplied through the batch file, including paid models. ### Attack Path 1. An attacker creates or modifies a batch JSON file. 2. A task entry specifies a paid or otherwise unauthorized model in its `model` property. 3. The user or agent invokes `dispatch.py batch --file` with that JSON file. 4. `cmd_batch()` reads the arbitrary model identifier without calling `_validate_model()`. 5. The identifier is sent to OpenRouter using the user's API key. 6. If the account permits that model, the request is processed and may incur charges. ### Impact Assessment This flaw can consume the user's OpenRouter quota or credits and violates the least-privilege requirement of a free-only dispatcher. A malicious batch containing many tasks could amplify the financial impact. The attacker does not obtain the OpenRouter API key or local system privileges. The principal impact is unauthorized use of API-account capabilities, unexpected billing, and bypass of the Skill's stated model policy.
Remediation
## Remediation Suggestions - Call `_validate_model(model)` during every batch iteration before `call_openrouter()`. - Validate that the parsed JSON root is a list and that every item is an object with expected field types. - Reject unknown task types rather than silently routing them to a default model. - Enforce the free-model restriction centrally inside `call_openrouter()` so no command path can bypass it. - Apply limits to the number of batch entries and maximum prompt size to reduce quota-abuse risk.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dispatch.py:159
Finding
Free-Model Allowlist Bypass in Model Status Check## Vulnerability Details **File Location**: `scripts/dispatch.py:159-163` **Vulnerability Type**: Missing model authorization validation **Risk Level**: Medium ```python def cmd_status(args): model = args.model or "openrouter/free" print(f"Checking {model}...") try: result = call_openrouter("Say 'OK' and nothing else.", model, max_tokens=10) ``` ### Technical Analysis The `status` command accepts an arbitrary model identifier and sends a live completion request without invoking `_validate_model()`. Although the request limits the response to ten tokens, it still authorizes a request against any model available to the user's OpenRouter account. This contradicts the code's stated invariant that only models in `ALLOWED_MODELS` should be used. ### Attack Path 1. An attacker influences a command invocation or supplies a crafted model argument. 2. The user or agent runs `dispatch.py status --model <paid-model>`. 3. `cmd_status()` passes that model directly to `call_openrouter()`. 4. OpenRouter processes the request using the user's API key if the account permits the selected model. 5. The account may incur an unauthorized charge. ### Impact Assessment The direct cost per invocation is constrained by the short fixed prompt and ten-token output limit, but repeated calls can consume credits and bypass the free-only policy. No API-key disclosure, local privilege escalation, or arbitrary prompt transmission occurs through this specific path.
Remediation
## Remediation Suggestions - Invoke `_validate_model(model)` before making the status request. - Move allowlist enforcement into `call_openrouter()` so it applies uniformly to task, batch, and status operations. - If paid-model status checks are intentionally supported, require an explicit opt-in flag and document the potential billing impact. - Add regression tests confirming that every command rejects model identifiers outside `ALLOWED_MODELS`.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (9)

Tainted flow: 'req' from os.environ.get (line 58, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
start = time.time()
    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            result = json.loads(resp.read().decode("utf-8"))
            elapsed = time.time() - start
            content = result["choices"][0]["message"]["content"]
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill routes prompts and task content to OpenRouter, but the description does not clearly warn that user data will be transmitted to an external service. This is dangerous because users or upstream agents may supply confidential business data, source code, credentials-adjacent context, or personal information under the assumption the task remains within the local/primary agent environment.

Credential Access

High
Category
Privilege Escalation
Content
def call_openrouter(prompt, model, system_prompt=None, max_tokens=4096):
    """Call OpenRouter API with a prompt and model."""
    if not OPENROUTER_API_KEY:
        print("ERROR: OPENROUTER_API_KEY not set. Set it in env or credentials.txt", file=sys.stderr)
        sys.exit(1)

    messages = []
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
if not real.startswith(os.path.realpath(home)):
        print(f"ERROR: Output path must be within home directory", file=sys.stderr)
        sys.exit(1)
    sensitive = ['.ssh', '.aws', '.env', '.bashrc', '.profile', '.gitconfig', 'credentials']
    for s in sensitive:
        if s in real:
            print(f"ERROR: Cannot write to sensitive path containing '{s}'", file=sys.stderr)
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises and enables capabilities that involve environment-variable access, writing files, and making network requests, but it does not declare any tool scope or permission boundaries. This creates a transparency and governance gap: agents or users may invoke a skill with external-network and file-output behavior without an explicit permission contract, increasing the chance of unintended data disclosure or filesystem side effects.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The description is broad enough to match many common tasks like writing, research, and code generation, which increases the likelihood of over-invocation. In this skill's context, over-invocation is more dangerous because each invocation can transmit user prompts or task data to an external third-party service, potentially exposing sensitive information and bypassing the user's expectation that work stays local or with the primary model.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The status command accepts any model string and passes it to call_openrouter without _validate_model, despite the code's stated goal of preventing paid-model abuse. This enables a user or calling agent to trigger requests to non-free or unexpected models, creating cost exposure and weakening policy controls around outbound AI usage.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
Batch mode writes to args.output without applying the _validate_output_path protections used by the task command. A caller can therefore overwrite arbitrary files writable by the current user, including shell startup files or project configuration, which can lead to persistence, data corruption, or follow-on code execution in some environments.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This is the same underlying issue as SDI-2: batch mode writes JSON results to a user-controlled path with no safety validation. In an agent context, that makes the skill more dangerous because another component may pass attacker-influenced paths, enabling arbitrary file overwrite within the agent's privileges.

Static analysis

No suspicious patterns detected.