Back to skill

Security audit

Openrouter Models Pricing

Security checks for vulnerabilities and agentic risk

Overview

This skill fetches and analyzes OpenRouter model catalog data with narrow, disclosed behavior, though users should note the optional API key handling.

Reasonable to install for OpenRouter model-price and capability comparison. Before running fetch_models.py, unset OPENROUTER_API_KEY if you want the public catalog request to be anonymous; the skill does not send prompts to models or perform generation, but its cached catalog includes descriptions of router and web-enabled models that may have different privacy implications if used separately.

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

Note
Location
scripts/fetch_models.py:27
Finding

Unnecessary Automatic Transmission of an API Credential

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_models.py, lines 27–33 and 53–62
Vulnerability Type: Automatic credential use without explicit per-run consent
Risk Level: Low

Vulnerable Code

python
def fetch(api_key: str | None) -> dict:
    headers = {}
    if api_key:
        headers["Authorization"] = f"Bearer {api_key}"
    req = urllib.request.Request(API_URL, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            body = resp.read().decode("utf-8")
python
parser.add_argument(
    "--api-key",
    default=os.environ.get("OPENROUTER_API_KEY"),
    help="Optional. OpenRouter API key, or set OPENROUTER_API_KEY. "
         "Endpoint is public; key only affects rate limits / attribution.",
)
parser.add_argument("--out", type=Path, default=DEFAULT_OUT)
args = parser.parse_args()

data = fetch(args.api_key)

Technical Analysis

The OpenRouter model-catalog endpoint is documented by the Skill as public and does not require authentication. Nevertheless, the default argument value is automatically populated from OPENROUTER_API_KEY. If that environment variable exists, an ordinary invocation of python3 scripts/fetch_models.py silently adds the credential to an Authorization: Bearer header and sends it to the configured OpenRouter endpoint.

This behavior exceeds the minimum privilege needed for the declared catalog-retrieval function. Although the credential is sent over HTTPS to the declared OpenRouter domain rather than an unrelated host, authentication should require explicit opt-in when the operation works anonymously. The alternative --api-key KEY interface also places a secret in command-line arguments, where it may be retained in shell history or exposed through process inspection.

Attack Path

  1. A user has a valid OpenRouter credential stored in OPENROUTER_API_KEY.
  2. The user or agent follows t ...[truncated 1280 chars]
Remediation
View remediation

Remediation Suggestions

  1. Change the default value of --api-key to None so public retrieval is anonymous by default.
  2. Require an explicit option such as --authenticated or --use-env-api-key before reading OPENROUTER_API_KEY.
  3. Avoid accepting secrets directly as command-line values. If authenticated requests are necessary, read the credential from the environment or a permission-restricted file only after explicit user consent.
  4. Clearly report when authenticated mode is active without printing the credential.
  5. Update SKILL.md to explain that authentication is unnecessary for normal operation and document the exact circumstances in which a credential will be transmitted.
  6. Consider separating anonymous and authenticated retrieval paths so the normal quick-start flow cannot access credentials.

A safer interface would resemble:

python
parser.add_argument(
    "--use-env-api-key",
    action="store_true",
    help="Explicitly authenticate using OPENROUTER_API_KEY.",
)
args = parser.parse_args()

api_key = os.environ.get("OPENROUTER_API_KEY") if args.use_env_api_key else None
if args.use_env_api_key and not api_key:
    parser.error("OPENROUTER_API_KEY is not set")

data = fetch(api_key)
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个既能拉取又能分析 OpenRouter 模型目录的技能,且明确支持多种定价维度、上下文长度、模态信息以及面向用户的问题式比较/筛选场景。但给出的代码块只实现了基础的数据获取:向 OpenRouter /api/v1/models 发起 HTTP 请求、解析 JSON、保存到本地文件并打印抓取数量。它没有任何针对 8 项定价字段、上下文长度、输入输出模态的解析分析逻辑,也没有实现比较、过滤、聚合、回答查询或生成分析结果的能力。因此,代码实际行为仅覆盖“拉取”中的一小部分,未覆盖声明中的核心“分析”用途,构成明显描述与行为不匹配。

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
`models_raw.json`. Re-run `fetch_models.py` to refresh.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest says this skill is for pulling and analyzing OpenRouter model directory data for comparison/pricing questions and explicitly says it is not for generation or dialogue tasks. However, the raw catalog embedded here includes models whose documented purpose is image generation, audio generation, roleplay/chat, deep research, and code-application routers, indicating the skill’s actual data scope is much broader than the narrow use-case description suggests.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The model description says it autonomously searches, reads, and evaluates external sources, and failing to warn users about that behavior can expose sensitive prompts or derived queries to external systems and broaden the data exposure surface. In this skill context, where users may believe they are only comparing catalog metadata, undisclosed autonomous retrieval is more dangerous because it changes the trust boundary and may trigger external network actions unexpectedly.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description says a user's prompt will be processed and routed to one of dozens of models, without any narrowing conditions, trigger constraints, or exclusion examples. In a manifest-style JSON file, this is the kind of broad invocation description that can overlap with ordinary usage and lead to unintended skill selection.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The router entry explicitly states that prompts are first processed by a meta-model and then routed to one of many downstream models, but if the skill omits a user warning about this behavior, users may unknowingly disclose sensitive data to additional providers. In a skill focused on model analysis and pricing, hidden multi-hop processing increases privacy and data-governance risk because requests may be sent beyond the provider the user expects.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manifest specifically enumerates eight pricing dimensions: prompt, completion, cache_read, cache_write, web_search, reasoning, image, and audio. This dataset also includes pricing keys such as internal_reasoning, which does not match the declared category naming and suggests the actual analyzed schema is broader or different than what the manifest promises.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.