Back to skill

Security audit

Arya Model Router

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent local model-routing helper, but its documented shell integration can turn untrusted user text into command execution if copied literally.

Review this before installing if an agent or wrapper will invoke it automatically. Do not copy the README shell command with raw user text interpolation; call router.py with a structured argument array or pass text over stdin. Also decide whether automatic sub-agent/model escalation and local state.json persistence fit your cost and privacy expectations.

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

Error
Location
README.md:39
Finding
Shell Command Injection Through Unsafe User Message Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `README.md`, lines 39–41 **Vulnerability Type**: Shell command injection in documented agent integration **Risk Level**: High ### Vulnerable Code ```bash python3 skills/arya-model-router/router.py --text "<user message>" --context-chars 65000 ``` ### Technical Analysis The documented integration instructs an agent to substitute an untrusted user message directly into a shell command enclosed by double quotes. Double quotes do not prevent shell command substitution: constructs such as `$(command)` and backticks are evaluated by the shell before `router.py` receives the value of `--text`. For example, if a user message contains `$(id)`, direct substitution produces: ```bash python3 skills/arya-model-router/router.py --text "$(id)" --context-chars 65000 ``` The shell executes `id` first and passes its output to the Python script. More harmful commands could be substituted in the same way. The Python implementation does not itself execute the message, but it cannot protect against expansion that already occurred in the invoking shell. ### Attack Path 1. An attacker submits a message containing shell substitution syntax, such as `$(malicious_command)`. 2. An integrating agent or application follows the documented command and inserts the message into the quoted `--text` argument. 3. The command is executed through a shell. 4. The shell evaluates the attacker-controlled substitution before starting Python. 5. The injected command runs with the permissions and environment of the integrating agent. 6. Only the resulting command output is supplied to `router.py`, potentially concealing the original injection from application-level processing. This path applies when the documented placeholder is implemented through string interpolation followed by shell execution. Invocation through a correctly constructed argument array is not vulnerable to this issue. ### Impact Assessment Successful exploitation provides ...[truncated 628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not construct a shell command by interpolating the user message. Invoke the script with a structured argument array so no shell parses the message: ```python import subprocess subprocess.run( [ "python3", "skills/arya-model-router/router.py", "--text", user_message, "--context-chars", "65000", ], check=True, ) ``` Alternatively, redesign `router.py` to accept message content through standard input and pass it without shell interpolation. Update `README.md` to: 1. Warn that user-controlled messages must never be concatenated into a shell command. 2. Provide an argument-array integration example as the recommended method. 3. Avoid examples that imply double quoting is sufficient sanitization. 4. If a shell is unavoidable, pass data through a positional parameter rather than embedding it in command text; however, direct argument-array execution should remain the preferred approach. 5. Add a regression test using payloads containing `$(...)`, backticks, quotes, semicolons, and newlines to verify that they are delivered as literal text and never executed. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
th['heavy_score'] = heavy
    th['default_score'] = default
    rules['thresholds'] = th
    return rules


def score_text(text: str, rules: dict):
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
## Notes

- This router is safe: it does not execute arbitrary commands or call external services.
- It only emits decisions.
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Natural-language content throughout the file is presented in Spanish, including the description, objectives, approach, and usage examples. This effectively imposes a language choice on users without any opt-in, alternative language option, or justification for a Spanish-only scope.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The activation examples are very broad and do not define boundaries, exclusions, or a precise invocation format. In a routing skill that can influence model selection and potentially trigger sub-agents, ambiguous triggers can cause accidental activation on unrelated user text, leading to unintended delegation, higher-cost model use, or unexpected handling of sensitive context.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The docstring states "This script is local-only: it does not call models. It outputs a JSON decision," which implies a decision-only local router. However, the implementation also writes state changes to state.json for mode toggles, feedback updates, and lastDecision persistence. This is an active contradiction about side effects, not merely omitted detail.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This Python file contains multiple user-visible reason strings in Spanish, such as "texto largo", "patrones de matemáticas/cálculo", and "keywords pesadas detectadas". The file does not offer a language choice or document a justified Spanish-only locale restriction, which can violate the language/locale policy for skills expected to operate without forcing a specific language.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The example `@cheap resume esto` is a Spanish-language invocation, and the README does not indicate that language selection is optional or user-controlled. This can imply a fixed language/locale behavior without explicit opt-in, which falls under the language/locale policy check.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The config uses natural-language routing keywords in Spanish and English only, which can implicitly privilege those languages without offering user opt-in or documenting a justified locale restriction. This matches the language/locale policy concern because the skill behavior depends on fixed language assumptions embedded in config values.

Static analysis

No suspicious patterns detected.