Back to skill

Security audit

Moltpho

Security checks for vulnerabilities and agentic risk

Overview

This shopping skill is coherent but needs Review because it can spend funds and place real-world orders with weak local consent and credential-safety controls.

Install only if you are comfortable giving this skill authority to use stored Moltpho credentials for real purchases and shipping-profile updates. Before enabling it, turn off proactive purchasing unless you explicitly want inferred-need buying, require confirmations for orders, set tight per-order and daily caps in the portal, prefer portal entry for sensitive shipping/payment details, and protect or rotate the local credentials file if the machine is shared or compromised.

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

T09 · Insecure Skill Coding Practices

Error
Location
lib/moltpho.py:586
Finding
Unvalidated API Base URL Can Redirect Bearer Credentials and Sensitive Data<![CDATA[ ## Vulnerability Details **File Location**: `lib/moltpho.py:143-144` and `lib/moltpho.py:586-598` **Vulnerability Type**: Unvalidated credential destination / sensitive-data exfiltration **Risk Level**: High ### Vulnerable Code ```python @classmethod def from_dict(cls, data: dict) -> Credentials: return cls( agent_id=data["agent_id"], api_key_id=data["api_key_id"], api_key_secret=data["api_key_secret"], api_base_url=data.get("api_base_url", API_BASE_URL), wallet_address=data["wallet_address"], ) ``` ```python base_url = creds.api_base_url if creds else API_BASE_URL url = f"{base_url}{endpoint}" request_headers = {"Content-Type": "application/json"} if creds: request_headers.update(_get_auth_headers(creds)) if headers: request_headers.update(headers) session = _create_session() response = session.request( method=method, url=url, params=params, json=data, headers=request_headers, timeout=timeout, ) ``` The authentication headers contain the API secret: ```python return { "Authorization": f"Bearer {creds.api_key_secret}", "X-Moltpho-Key-Id": creds.api_key_id, "Content-Type": "application/json", } ``` ### Technical Analysis The API destination is read directly from the local credentials JSON and used without validating its scheme or origin. Authenticated requests then attach the bearer secret and API key ID to that destination. Although newly registered credentials use the intended constant `https://api.moltpho.com`, subsequently loaded credentials may contain any `api_base_url`. The HTTP session also mounts an adapter for both HTTP and HTTPS, so a modified credential file can select an unencrypted HTTP endpoint. The configurable base URL is not required for the declared production shopping functionality and exceeds the minimum trust necessary for credential use. It turns a local configuration field into a credential-exfiltration sink. ### Attack Path ...[truncated 1355 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not deserialize the production API origin from the credentials file. Use the fixed `API_BASE_URL` constant for all authenticated requests. - If alternate environments are necessary, maintain an explicit allowlist of exact origins and require a separate, trusted deployment configuration. - Require HTTPS and reject HTTP, user-information components, fragments, unexpected ports, and non-allowlisted hosts. - Disable automatic redirects for authenticated requests or ensure authorization headers are never forwarded when the origin changes. - Validate the complete credentials schema before use. - Consider separating credentials by environment so production credentials cannot be sent to staging or development hosts. - Add tests proving that modified `api_base_url` values, cross-origin redirects, and HTTP destinations are rejected before any request is sent. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
lib/moltpho.py:1322
Finding
Exported Purchase Flow Does Not Enforce Local Authorization, Confirmation, or Budget Policies<![CDATA[ ## Vulnerability Details **File Location**: `lib/moltpho.py:1322-1440` **Vulnerability Type**: Missing authorization checks at a financial transaction boundary **Risk Level**: High ### Vulnerable Code ```python def purchase( asin: str, quantity: int, shipping_profile_id: Optional[str] = None, creds: Optional[Credentials] = None, ) -> Order: if creds is None: creds = load_credentials() if creds is None: raise MoltphoError( code="NOT_AUTHENTICATED", message="No credentials found. Please register first.", ) # Get shipping profile if not provided if shipping_profile_id is None: default_profile = get_default_shipping_profile(creds) if default_profile is None: raise MoltphoError( code=MoltphoErrorCode.INVALID_SHIPPING_PROFILE, message="No shipping profile set. Please add a shipping address.", status_code=422, ) shipping_profile_id = default_profile.id # Generate idempotency key for the entire purchase flow purchase_idempotency_key = str(uuid.uuid4()) original_price_cents: Optional[int] = None for attempt in range(MAX_QUOTE_RETRIES): quote_idempotency_key = f"{purchase_idempotency_key}-quote-{attempt}" try: quote = create_quote( asin=asin, quantity=quantity, shipping_profile_id=shipping_profile_id, creds=creds, idempotency_key=quote_idempotency_key, ) except MoltphoError: raise if original_price_cents is None: original_price_cents = quote.total_cents else: price_change_bps = abs( (quote.total_cents - original_price_cents) * 10000 // original_price_cents ) if price_change_bps > QUOTE_RETRY_PRICE_T ...[truncated 3672 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement one mandatory transaction-authority function that: 1. Fetches the current balance and complete credit policy. 2. Validates autonomous or proactive purchasing mode. 3. Verifies the exact quoted total against available credit and all caps. 4. Enforces the owner allowlist, denylist, and immutable system blocklist. 5. Enforces conversation confidence for proactive purchases. 6. Requires and verifies confirmation when `confirmation_required` is true. 7. Binds the approved ASIN, quantity, shipping profile, quote ID, total, and expiry into an immutable authorization object. - Require that authorization object in both signing and final order submission. - Revalidate policy and quote values immediately before requesting the payment signature. - Make `request_x402_signature()`, `create_order()`, and `_create_order_with_signature()` internal implementation details rather than public exports. - Do not rely exclusively on server-side enforcement; enforce the same invariants on both client and server. - Add negative tests showing that disabled purchasing modes, denied categories, missing confirmation, insufficient budget, and stale authorization prevent signing and order creation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/moltpho.py:296
Finding
Security-Critical Purchasing Policy Fields Fail Open When Missing<![CDATA[ ## Vulnerability Details **File Location**: `lib/moltpho.py:296-302` and `lib/proactive.py:457-459` **Vulnerability Type**: Fail-open authorization defaults **Risk Level**: Medium ### Vulnerable Code ```python return cls( target_limit_cents=data["target_limit_cents"], per_order_cap_cents=data.get("per_order_cap_cents"), daily_cap_cents=data.get("daily_cap_cents"), autonomous_purchasing_enabled=data.get( "autonomous_purchasing_enabled", True ), proactive_purchasing_enabled=data.get( "proactive_purchasing_enabled", True ), confirmation_required=data.get("confirmation_required", False), ) ``` The proactive decision function repeats the fail-open behavior: ```python # 1. Check if proactive purchasing is enabled if not credit_policy.get("proactive_purchasing_enabled", True): audit.rule_path = "proactive_disabled" audit.blocklist_check = "skipped" return False, "Proactive purchasing is disabled", 0.0 ``` The audit function contains the same default: ```python if not credit_policy.get("proactive_purchasing_enabled", True): audit.rule_path = "proactive_disabled" audit.blocklist_check = "skipped" return audit ``` ### Technical Analysis Missing policy fields are interpreted as permission to perform autonomous and proactive purchases, while missing confirmation configuration is interpreted as confirmation not being required. Financial authorization controls should fail closed. An incomplete API response, version mismatch, parsing issue, test fixture, or caller-provided partial dictionary should not silently broaden purchasing authority. This behavior is particularly risky because the proactive decision function accepts an ordinary dictionary rather than a fully validated policy object. A caller that omits `proactive_purchasing_enabled` automatically receives the permissive default. ### Attack Path 1. The policy API returns a partial response, an intermediary strips fields, or a ...[truncated 967 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require all security-critical policy fields to be present and of the exact expected Boolean type. - Default `autonomous_purchasing_enabled` and `proactive_purchasing_enabled` to `False`. - Default `confirmation_required` to `True`. - Refuse to purchase when policy retrieval, parsing, schema validation, or freshness checks fail. - Change proactive decision functions to accept a validated `CreditPolicy` instance rather than an arbitrary partial dictionary. - Version the policy schema and reject unsupported versions. - Add tests for missing, null, string-valued, and malformed policy fields to confirm that every such case blocks purchases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/moltpho.py:968
Finding
Daily Spending Cap Uses a Weekly Average Instead of Actual Daily Spend<![CDATA[ ## Vulnerability Details **File Location**: `lib/moltpho.py:968-977` **Vulnerability Type**: Incorrect security-control calculation **Risk Level**: Medium ### Vulnerable Code ```python # Check daily cap if policy.daily_cap_cents: # Would need to sum today's orders + this amount # For now, just check if there's headroom today_spent = balance.total_spent_week_cents // 7 # Rough approximation if today_spent + amount_cents > policy.daily_cap_cents: allowed = False reasons.append( f"Would exceed daily cap: ${policy.daily_cap_cents/100:.2f}" ) ``` ### Technical Analysis The function calculates current-day spending by dividing weekly spending by seven. A weekly average is not an authorization-safe estimate of spending since midnight in the owner's configured timezone. When actual spending today is greater than the weekly average, this calculation undercounts daily usage and can approve an amount that exceeds the owner's daily limit. It can also overcount on a low-spend day and improperly deny a legitimate purchase. The source comment explicitly acknowledges that exact daily spending is unavailable. A financial limit must not be enforced using an approximation that can fail open. ### Attack Path 1. The account spends a substantial amount on the current day while spending little or nothing on earlier days of the week. 2. `total_spent_week_cents // 7` produces a value far below the amount actually spent today. 3. A further purchase is checked using this underestimated value. 4. `budget_check()` returns `allowed: True`, even though actual daily spending plus the new amount exceeds `daily_cap_cents`. 5. If the caller relies on this result and the remote service does not independently enforce the exact limit, the additional order proceeds. For example, if $70 was spent today and nothing was spent on the previous six days, the function estimates today's spend as $10. With a $100 daily cap, it may approv ...[truncated 509 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Obtain authoritative current-day spending from the server, calculated using the owner's configured timezone and exact accounting period. - Alternatively, query orders from the start of the owner's current day and sum only settled spending according to a documented status policy. - Make the server perform the final daily-cap check atomically with quote reservation and order placement to avoid time-of-check/time-of-use races. - If exact daily usage cannot be obtained, return a blocked or indeterminate result rather than approving the purchase. - Include the current daily spend in the balance or policy response so clients do not reconstruct it approximately. - Add tests for concentrated same-day spending, timezone boundaries, refunds, active reservations, and concurrent purchases. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
lib/moltpho.py:441
Finding
Credential File Is Written Before Restrictive Permissions Are Applied<![CDATA[ ## Vulnerability Details **File Location**: `lib/moltpho.py:441-451` **Vulnerability Type**: Non-atomic secret-file creation and unsafe filesystem handling **Risk Level**: Low ### Vulnerable Code ```python try: # Ensure parent directory exists path.parent.mkdir(parents=True, exist_ok=True) # Write credentials with open(path, "w") as f: json.dump(creds.to_dict(), f, indent=2) # Set permissions to 600 (owner read/write only) # On Windows, this is a no-op but doesn't fail if platform.system() != "Windows": os.chmod(path, stat.S_IRUSR | stat.S_IWUSR) ``` The credentials include the bearer secret: ```python def to_dict(self) -> dict: return { "agent_id": self.agent_id, "api_key_id": self.api_key_id, "api_key_secret": self.api_key_secret, "api_base_url": self.api_base_url, "wallet_address": self.wallet_address, } ``` ### Technical Analysis The code opens and writes the credential file using permissions derived from the process umask, then changes the permissions to mode `0600` only after the secret has been fully written. With a permissive umask, the file may temporarily be readable by other local users. The operation is also non-atomic, follows symbolic links, and truncates an existing file before the write succeeds. These properties create a local race window and can leave corrupted credentials after interruption. The environment-controlled `MOLTPHO_CREDENTIALS_PATH` increases exposure when it points into a shared or attacker-influenced directory. Access to the Skill's own credential store is necessary, but secure secret-file creation must not rely on a later `chmod()`. ### Attack Path 1. The Skill runs on a multi-user system with a permissive umask, or the credential path is placed in a shared or attacker-observable directory. 2. Registration or credential rotation invokes `save_credentials()`. 3. `open(path, "w")` creates or truncates the file using ...[truncated 898 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create secret files with mode `0600` at creation time using `os.open()` and secure flags such as `O_CREAT`, `O_EXCL`, and, where available, `O_NOFOLLOW`. - Write to a securely created temporary file in the same trusted directory, flush and `fsync()` it, then atomically replace the destination. - Validate that the destination and parent directory are not symbolic links and are owned by the expected user. - Restrict the parent directory to mode `0700`. - Reject credential override paths located in world-writable directories unless explicit secure validation succeeds. - Preserve secure permissions during updates and handle Windows ACLs explicitly instead of treating permission hardening as a no-op. - Add tests using permissive umasks, existing symbolic links, interrupted writes, and shared directories. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description claims a full autonomous Amazon shopping skill that can search products, manage credit, and purchase using mUSD on Base. This code chunk does not perform any marketplace interaction, blockchain/payment activity, or actual order placement. Instead, it is a policy engine for deciding whether a proposed proactive purchase should be allowed based on conversation signals, item metadata, and credit-policy constraints. While this could be a supporting component of such a shopping skill, the chunk’s actual behavior is materially narrower and different from the declared end-user capability.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill prominently advertises autonomous and proactive purchasing, with proactive purchasing default ON, but does not present an equally prominent upfront warning about financial charges, shipment of physical goods, and the need for explicit owner consent. Because the capability affects money and real-world deliveries, insufficient disclosure materially increases the risk of unsafe or unauthorized use.

Credential Access

High
Category
Privilege Escalation
Content
| Platform | Path |
|----------|------|
| Linux/macOS | `~/.config/moltpho/credentials.json` |
| Windows | `%APPDATA%\moltpho\credentials.json` |
| Override | `MOLTPHO_CREDENTIALS_PATH` environment variable |
Confidence
90% confidence
Finding
The skill stores long-lived API credentials, including a secret key, in a predictable local file path and instructs the agent to read/write that file during bootstrap. If the host is compromised, backups are exposed, logs leak paths, or the override path is abused, these credentials could be stolen and used to query account data or initiate purchases on behalf of the owner.

Credential Access

High
Category
Privilege Escalation
Content
| Platform | Path |
|----------|------|
| Linux/macOS | `~/.config/moltpho/credentials.json` |
| Windows | `%APPDATA%\moltpho\credentials.json` |
| Override | `MOLTPHO_CREDENTIALS_PATH` environment variable |

### Registration Process
Confidence
90% confidence
Finding
The Windows credentials path is likewise a predictable location for a file containing reusable API secrets. In a skill that can place orders and access credit-backed balances, theft of this file could enable unauthorized purchases, account probing, or impersonation of the agent.

Vague Triggers

High
Confidence
96% confidence
Finding
The proactive monitoring logic uses broad conversational triggers such as 'I need' and 'we're out of' and allows autonomous purchases when confidence is deemed high. In a real-world shopping skill, this can misinterpret ordinary conversation as authorization to spend money and place physical orders, creating unintended financial and delivery consequences.

Credential Access

High
Category
Privilege Escalation
Content
@dataclass
class Credentials:
    """Agent credentials from credentials.json (Appendix A)."""

    agent_id: str
    api_key_id: str
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
@dataclass
class Credentials:
    """Agent credentials from credentials.json (Appendix A)."""

    agent_id: str
    api_key_id: str
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
@dataclass
class Credentials:
    """Agent credentials from credentials.json (Appendix A)."""

    agent_id: str
    api_key_id: str
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
@dataclass
class Credentials:
    """Agent credentials from credentials.json (Appendix A)."""

    agent_id: str
    api_key_id: str
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
1. MOLTPHO_CREDENTIALS_PATH environment variable
    2. Platform-specific default:
       - Linux/macOS: ~/.config/moltpho/credentials.json
       - Windows: %APPDATA%/moltpho/credentials.json
    """
    if env_path := os.environ.get("MOLTPHO_CREDENTIALS_PATH"):
        return Path(env_path)
Confidence
73% confidence
Finding
Allowing the credentials path to be overridden by the MOLTPHO_CREDENTIALS_PATH environment variable creates a path-injection/trust-boundary issue if an attacker can influence the process environment. The skill may then read secrets from or write secrets to an attacker-chosen location, potentially enabling credential substitution, unintended disclosure, or tampering with the account used for purchases.

Missing User Warnings

High
Confidence
96% confidence
Finding
This logic can authorize real purchases based on inferred conversational cues such as 'we're out of' or 'broke' without any mandatory user-facing confirmation at the point of purchase. In a shopping skill connected to spendable credit and on-chain funds, false positives, ambiguous context, prompt-manipulated conversations, or attacker-influenced dialogue could directly trigger unauthorized purchases.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
---

### DELETE /v1/quotes/{id}

Cancel a quote and release the soft reservation.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Missing User Warnings

High
Confidence
94% confidence
Finding
The policy explicitly permits both autonomous and proactive purchasing, including completing payment and placing orders, but does not include a prominent user-facing warning in the policy about real financial consequences or the risk of unintended purchases. In a shopping skill tied to available credit and on-chain payment flow, this increases the chance of unauthorized or surprising transactions, especially for proactive purchases inferred from conversation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill requires powerful capabilities including file read/write, environment-variable access, network access, browser interaction, and likely shell-like operations, but it declares no explicit tool scope or permission boundaries. In a purchasing skill that stores API secrets locally and can trigger browser actions and financial transactions, missing least-privilege declarations increases the chance of overbroad tool use or abuse if the runtime grants defaults.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `agent_display_name`
   - `agent_description`
   - No shipping profile required at registration
3. **Save credentials** with `chmod 600` permissions
4. **Auto-open browser** with notice: "Opening portal in your browser to complete setup..."
5. Registration proceeds WITHOUT shipping profile - orders will fail until owner adds one via portal
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `agent_display_name`
   - `agent_description`
   - No shipping profile required at registration
3. **Save credentials** with `chmod 600` permissions
4. **Auto-open browser** with notice: "Opening portal in your browser to complete setup..."
5. Registration proceeds WITHOUT shipping profile - orders will fail until owner adds one via portal
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `agent_display_name`
   - `agent_description`
   - No shipping profile required at registration
3. **Save credentials** with `chmod 600` permissions
4. **Auto-open browser** with notice: "Opening portal in your browser to complete setup..."
5. Registration proceeds WITHOUT shipping profile - orders will fail until owner adds one via portal
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `agent_display_name`
   - `agent_description`
   - No shipping profile required at registration
3. **Save credentials** with `chmod 600` permissions
4. **Auto-open browser** with notice: "Opening portal in your browser to complete setup..."
5. Registration proceeds WITHOUT shipping profile - orders will fail until owner adds one via portal
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill instructs the agent to collect full name, street address, email, and phone number and transmit them to an API, but it does not include a clear privacy notice, retention explanation, or guidance to minimize collection in-chat. This increases the risk of oversharing sensitive personal data and handling regulated contact information without adequate user awareness.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The instructions require validation of US addresses only and state that international addresses are not supported in v1. This is a locale/region restriction expressed in natural language, but the document does not frame the skill as a region-specific tool or provide a user choice or explicit justification beyond version limitation.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The shipping-profile upsert function transmits sensitive personal data including full name, street address, email, and phone number to a remote API without any user-facing disclosure or consent mechanism in this file. While HTTPS is used, the privacy risk remains because an agent or higher layer could send PII silently or under prompt manipulation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This file exposes a complete purchase flow that can autonomously create quotes, request payment signatures, and place orders without any built-in confirmation or user-consent checkpoint. In a shopping skill, that materially increases the risk of unintended or prompt-induced purchases because the dangerous action is one function call away and there is no local safeguard before spending funds.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest describes the skill as autonomously shopping on Amazon via Moltpho, specifically searching products, managing credit, and purchasing items. This file additionally creates and lists support tickets for returns/lost packages/other issues, which is a separate customer-support workflow not mentioned in the manifest description.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
Check if item matches any keyword in an allowlist.

    Returns:
        True if item matches allowlist (or allowlist is empty/None).
    """
    if not allowlist:
        return True  # Empty allowlist means all allowed
Confidence
70% 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.

External Transmission

Medium
Category
Data Exfiltration
Content
## Base URL

```
https://api.moltpho.com/v1
```

## Authentication
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.