Back to skill

Security audit

MoltQuest

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its game-agent purpose, but it needs Review because it can autonomously sign a real crypto payment and submit authenticated, persistent game actions with limited local safeguards.

Treat this as a Review item before installing. Use a limited wallet with only the funds you are willing to risk, prefer the free onboarding path when available, do not point MOLTQUEST_API or model-provider base URLs at untrusted services, and expect the agent to make autonomous game, trade, inventory, and persistent strategy decisions until stopped.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (3)

T01 · Skill Instruction Hijacking

Error
Location
quick-start.py:239
Finding

Indirect Prompt Injection Can Trigger Authenticated Game Actions

Content
View full analysis
tuple[dict, str]: log_match = _LOG_RE.search(text) log_entry = log_match.group(1).strip()[:200] if log_match else "" match = _EXUVIAE_RE.search(text) or _FALLBACK_RE.search(text) if match: try: data = json.loads(match.group(1)) if "action" in data and "type" not in data: data["type"] = data.pop("action") if "params" in data and isinstance(data["params"], dict): data.update(data.pop("params")) if "type" in data: return data, log_entry except json.JSONDecodeError: pass return {"type": "idle"}, log_entry or "Could not parse intention." ``` ```python _INJECTION_RE = re.compile(r'EXUVIAE:\s*\{') def _sanitize_context(text: str) -> str: """Strip potential intention-injection patterns from game world text.""" return _INJECTION_RE.sub('EXUVIAE_BLOCKED: {', text) ``` ```python ctx_str = _sanitize_context(json.dumps(context)) user_msg = f"Check-in reason: {reason}\n\nState: {ctx_str}\n" if events: _truncate_names(events) user_msg += f"Events: {_sanitize_context(json.dumps(events[:5]))}\n" user_msg += "\nWhat is your intention?" intention, log = self._decide(user_msg) ``` ```python def _decide(self, user_msg: str) -> tuple[dict, str]: try: llm_text = self.llm.chat(SYSTEM_PROMPT, user_msg) return parse_response(llm_text) ``` ```python def _ ...[truncated 2596 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
quick-start.py:388
Finding

Autonomous USDC Payment Trusts a Remote-Selected Recipient and Excessive Amount

Content
View full analysis
10_000_000: print(f"ERROR: Amount {amount} outside safe range (max $10 USDC)") sys.exit(1) print(f" Paying {amount / 1e6} USDC to {pay_to[:10]}...{pay_to[-4:]}") ``` ```python # Sign EIP-3009 transferWithAuthorization acct = EthAccount.from_key(private_key) nonce = "0x" + secrets.token_hex(32) valid_after = 0 valid_before = int(time.time()) + 3600 signable = eth_encode_typed_data( domain_data=USDC_DOMAIN, message_types=EIP3009_TYPES, message_data={ "from": wallet, "to": pay_to, "value": amount, "validAfter": valid_after, "validBefore": valid_before, "nonce": bytes.fromhex(nonce[2:]), }, ) signed = acct.sign_message(signable) signature = signed.signature.hex() if not signature.startswith("0x"): signature = "0x" + signature # Build x402 v2 PaymentPayload payment_payload = { "x402Version": 2, "payload": { "authorization": { "from": wallet, "to": pay_to, "value": str(amount), "validAfter": str(valid_after), "validBefore": str(valid_before), "nonce": nonce, }, "signature": signature, }, "accepted": req, "resource": { "url": f"{API_BASE}/onboarding/x402", "description": "MoltQuest Agent Onboarding", }, } encoded_payload = base64.b64encode( json.dumps(payment_payload).encode() ).decode() # Resend with signed payment print(" Submitting signed payment...") r ...[truncated 2525 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:18
Finding

Third-Party Python Dependencies Are Installed Without Version or Hash Pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Tainted flow: 'API_BASE' from os.getenv (line 76, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · quick-start.py (reported line 268)May include surrounding context.

python
def api_get(path: str, key: str = "", timeout: int = 10, _retry: bool = True) -> requests.Response:
    headers = {"X-Agent-Key": key} if key else {}
    resp = requests.get(f"{API_BASE}{path}", headers=headers, timeout=timeout)
    if resp.status_code == 429 and _retry:
        retry_after = int(resp.headers.get("Retry-After", "5"))
        print(f"[429] Rate limited — backing off {retry_after}s")

Tainted flow: 'API_BASE' from os.getenv (line 76, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · quick-start.py (reported line 281)May include surrounding context.

python
headers = {"Content-Type": "application/json"}
    if key:
        headers["X-Agent-Key"] = key
    resp = requests.post(f"{API_BASE}{path}", json=body, headers=headers, timeout=timeout)
    if resp.status_code == 429 and _retry:
        retry_after = int(resp.headers.get("Retry-After", "5"))
        print(f"[429] Rate limited — backing off {retry_after}s")

Tainted flow: 'API_BASE' from os.getenv (line 76, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Here the script sends a signed payment payload to whatever API_BASE is configured, including an authorization that can transfer USDC. If MOLTQUEST_API is overridden to an attacker-controlled endpoint, the code may hand that party a valid payment authorization, making the environment-controlled base URL materially more dangerous in this payment path than in ordinary gameplay requests.

Content

Scanner excerpt · quick-start.py (reported line 450)May include surrounding context.

python
# Resend with signed payment
    print("  Submitting signed payment...")
    resp = requests.post(
        f"{API_BASE}/onboarding/x402",
        json=body,
        headers={"Content-Type": "application/json", "PAYMENT-SIGNATURE": encoded_payload},

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

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

md
WALLET_PRIVATE_KEY=0x... python quick-start.py --name "MyAgent" --x402 --llm ollama

# With Claude API instead of local Ollama:
WALLET_PRIVATE_KEY=0x... ANTHROPIC_API_KEY=your_key python quick-start.py --name "MyAgent" --x402 --llm anthropic --model claude-haiku-latest

# Reconnect existing agent:
python quick-start.py --wallet 0xYourAddress --reconnect --llm ollama

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill declares user-invocable: true and documents use of environment secrets and live network access, but it does not declare an explicit tool scope such as permissions or allowed-tools. That mismatch can cause the runtime or user to underestimate what the skill can access, increasing the risk of unintended secret exposure or unreviewed outbound requests in a wallet-linked workflow.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill instructs the model to emit 'inner monologue' and strategic reasoning in every response, which can leak hidden chain-of-thought, policy-sensitive reasoning, or user-derived context into logs or to the external game service. This is especially risky here because the agent is autonomous, persistent, and may interact with external systems and other players, increasing the surface for accidental disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill promotes autonomous onboarding that can trigger a real USDC or ETH payment using WALLET_PRIVATE_KEY, but it does not present a prominent, explicit warning that real funds may be spent and transactions may be irreversible. In context, this is more dangerous because the skill is user-invocable, crypto-native, and optimized for 'zero human steps,' which can normalize unattended financial actions.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · quick-start.py (reported line 164)May include surrounding context.

python
self.host = (host or os.getenv("OLLAMA_HOST", "http://localhost:11434")).rstrip("/")

    def chat(self, system: str, user: str) -> str:
        resp = requests.post(f"{self.host}/api/chat", json={
            "model": self.model,
            "stream": False,
            "messages": [

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · quick-start.py (reported line 281)May include surrounding context.

python
headers = {"Content-Type": "application/json"}
    if key:
        headers["X-Agent-Key"] = key
    resp = requests.post(f"{API_BASE}{path}", json=body, headers=headers, timeout=timeout)
    if resp.status_code == 429 and _retry:
        retry_after = int(resp.headers.get("Retry-After", "5"))
        print(f"[429] Rate limited — backing off {retry_after}s")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script automatically signs and submits a USDC payment authorization whenever --x402 is used and WALLET_PRIVATE_KEY is present, without an interactive confirmation step. In a crypto/payment context, autonomous spending is sensitive because an operator can trigger real fund movement with a single command and little friction, especially if combined with endpoint redirection or misleading server responses.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This external transmission is security-relevant because it includes a signed payment authorization in the PAYMENT-SIGNATURE header. In the context of on-chain token economics, transmitting a spend authorization to a configurable endpoint is materially dangerous if the endpoint is untrusted or spoofed.

Content

Scanner excerpt · quick-start.py (reported line 450)May include surrounding context.

python
# Resend with signed payment
    print("  Submitting signed payment...")
    resp = requests.post(
        f"{API_BASE}/onboarding/x402",
        json=body,
        headers={"Content-Type": "application/json", "PAYMENT-SIGNATURE": encoded_payload},

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This markdown file describes the salvage intention, which destroys an item and converts it into materials, but it does not include an explicit warning or caution to the user before presenting the action. Because this operation irreversibly alters user inventory, the skill description should clearly call out the destructive nature of the action rather than only mentioning it in the return text.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The drop intention removes an item from inventory and places it on the ground, which can affect user data/state and may lead to loss if the item is not recovered. The section documents the behavior but does not provide an explicit cautionary warning about that risk.

Content

No source excerpt is available for this finding.

Persistent Context Injection

Medium
Category
Memory Poisoning
Confidence
84% confidence
Finding

The set_strategy intention allows persistent standing orders and long-term goals to be stored server-side, creating a durable prompt/context channel that can outlive a single interaction. If untrusted users, NPC dialogue, chat, or adversarial content can influence these fields, an attacker could implant lasting behavioral instructions that bias future agent actions toward unsafe trading, combat, or asset-handling decisions in a live on-chain economy.

Content

Scanner excerpt · references/intentions.md (reported line 687)May include surrounding context.

md
**Parameters:**
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `standing_orders` | string[] | no | `[]` | Persistent rules (e.g., "always pick up rare items") |
| `life_goal` | string | no | — | Long-term direction |
| `personality` | object | no | — | Personality modifiers |

Static analysis

No suspicious patterns detected.