T09 · Insecure Skill Coding Practices
- Location
- SKILL.md:295
- Finding
- Unauthenticated Webhook Can Trigger Wallet-Funded Purchases## Vulnerability Details **File Location**: `SKILL.md`, lines 295–312 **Vulnerability Type**: Unauthenticated webhook with attacker-controlled payment destination **Risk Level**: High ### Vulnerable Code ```js app.use(express.json()); app.post('/hooks/alpha', async (req, res) => { const { alpha } = req.body; res.sendStatus(200); // Acknowledge immediately if (alpha.group_score < 30) return; // Skip low-trust groups // Free trial if available (new group, no track record yet) if (alpha.trial_url) { const trial = await fetch(`https://www.gatedalpha.xyz${alpha.trial_url}`); const preview = await trial.json(); // Evaluate preview — if not interesting, return early if (!isWorthBuying(preview)) return; } // Buy the full alpha const res2 = await paidFetch(alpha.paid_url); // paidFetch from Step 4 ``` ### Technical Analysis The recommended webhook handler accepts arbitrary JSON from any network client without verifying a webhook signature, shared secret, sender identity, timestamp, nonce, or subscription identifier. It then trusts security-sensitive fields taken directly from the request body. In particular, `alpha.group_score` is used as the principal trust check even though a caller can assign it any value. More critically, `alpha.paid_url` is passed directly to `paidFetch`. Earlier in the documented flow, `paidFetch` is connected to an x402 client backed by a Base wallet signer. It can therefore respond to a payment challenge by producing a signed payment authorization. The subscription-level `max_price_usdc` setting does not secure this handler. That setting is enforced by the external provider before legitimate webhook delivery, while an attacker can contact the public webhook endpoint directly and bypass the provider. The handler also lacks: - An allowlist restricting `paid_url` to the exact Gated Alpha HTTPS origin and expected path format. - Independent validat ...[truncated 2419 chars]
- Remediation
- ## Remediation Suggestions 1. **Authenticate every webhook** - Require the provider to sign the raw request body using HMAC or an asymmetric signature. - Verify the signature before parsing or acting on the payload. - Compare signatures using a constant-time operation. - Reject unsigned requests and requests signed with an unknown key. 2. **Prevent replay attacks** - Include a signed timestamp, event identifier, and subscription identifier. - Reject requests outside a short time window. - Store processed event identifiers and reject duplicates. 3. **Validate the complete payload** - Enforce a strict schema and reject unknown, missing, incorrectly typed, or oversized fields. - Confirm that `subscription_id`, wallet, alpha identifier, chain, and other metadata match locally stored subscription data. - Do not treat request-supplied `group_score` as proof of trust. 4. **Constrain outbound destinations** - Parse URLs with a standard URL parser. - Require HTTPS. - Allow only the exact trusted hostname `www.gatedalpha.xyz`. - Require the expected `/alpha/{validated-id}` path format. - Reject credentials, unexpected ports, redirects to other origins, and ambiguous or encoded hostnames. - Prefer constructing the purchase URL locally from a validated alpha ID rather than accepting a complete URL from the webhook. 5. **Enforce payment policy locally** - Independently retrieve and verify the payment amount, recipient, token contract, chain ID, resource identifier, and challenge expiry. - Enforce strict per-transaction, daily, and cumulative spending limits. - Reject payments above the locally configured ceiling regardless of subscription settings. - Require explicit approval for purchases unless narrowly scoped automatic purchasing has been intentionally enabled. 6. **Reduce wallet privileges** - Use a dedicated low-balance wallet rather than a general-purp ...[truncated 730 chars]
