T09 · Insecure Skill Coding Practices
- Location
SKILL.md:78- Finding
Unrestricted External Prompt Disclosure and Unsafe Shell Command Construction
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a coherent model-routing helper, but it can send full user prompts to external services without a clear confirmation or privacy warning.
Review before installing if you may route confidential prompts. Use it only when you are comfortable sending the prompt text to LLM Council and, if you follow the chaining example, to OpenRouter and downstream model providers. Avoid secrets, regulated data, proprietary code, or private files unless your organization has approved those disclosures and billing implications.
SKILL.md:78Unrestricted External Prompt Disclosure and Unsafe Shell Command Construction
The skill description says it will route prompts to the best model but does not clearly warn that user prompt content is transmitted to external services. This creates a transparency and consent problem: users may reveal sensitive data believing they are interacting locally, while the skill forwards their content to third-party APIs.
The invocation guidance is broad enough that the skill could activate on ordinary requests about choosing a model, causing user prompts to be sent externally in many routine contexts. Over-broad triggering increases the chance of unnecessary data sharing and provider calls, especially when users may only be asking for advice rather than consenting to API routing.
The curl example instructs sending the user's query directly to the LLM Council API, which is an external transmission of potentially sensitive text. While this is core to the feature, it remains a vulnerability in the absence of prominent user-facing notice and consent because ordinary prompts may contain secrets, proprietary data, or personal information.
curl -X POST https://clawbot.llmcouncil.ai/v1/route \
-H "Content-Type: application/json" \
-H "X-API-Key: $LLMCOUNCIL_API_KEY" \
-d '{"query": "USER_QUERY_HERE"}'
The skill is described as a router, but the documentation explicitly instructs chaining the routing result into a second provider call that executes the user's prompt on OpenRouter. This expands the trust boundary from 'model selection' to 'prompt transmission and execution by third-party services,' which can cause unintended data disclosure, extra billing, and broader capability activation than the user may expect from the skill name and description.
The code snippet at this line performs an HTTP POST to an external endpoint with the user's query, constituting external data exfiltration from the local trust boundary. In skill context, this is expected functionality, but it is still a security-relevant behavior because it can leak sensitive prompt content if not clearly disclosed and gated.
import requests, os
# Step 1: Get the best model from LLM Council
route = requests.post(
"https://clawbot.llmcouncil.ai/v1/route",
headers={"X-API-Key": os.environ["LLMCOUNCIL_API_KEY"]},
json={"query": "Write a Python web scraper"},
The code snippet at this line performs an HTTP POST to an external endpoint with the user's query, constituting external data exfiltration from the local trust boundary. In skill context, this is expected functionality, but it is still a security-relevant behavior because it can leak sensitive prompt content if not clearly disclosed and gated.
import requests, os
# Step 1: Get the best model from LLM Council
route = requests.post(
"https://clawbot.llmcouncil.ai/v1/route",
headers={"X-API-Key": os.environ["LLMCOUNCIL_API_KEY"]},
json={"query": "Write a Python web scraper"},
The code snippet at this line performs a POST to OpenRouter using a chosen model and forwards the user's message content for completion. This creates a second-hop disclosure path and broadens the operational scope from ranking to execution, increasing risk of sensitive data transfer, cost incurrence, and inadvertent use of a provider the user did not explicitly choose.
confidence = route["confidence"] # e.g. 0.85
# Step 2: Call that model via OpenRouter
answer = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
json={
The code snippet at this line performs a POST to OpenRouter using a chosen model and forwards the user's message content for completion. This creates a second-hop disclosure path and broadens the operational scope from ranking to execution, increasing risk of sensitive data transfer, cost incurrence, and inadvertent use of a provider the user did not explicitly choose.
confidence = route["confidence"] # e.g. 0.85
# Step 2: Call that model via OpenRouter
answer = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
json={
No suspicious patterns detected.