T09 · Insecure Skill Coding Practices
Error
- Location
- src/index.ts:633
- Finding
- Paid and Irreversible Order Submission Lacks Enforced User Confirmation<![CDATA[ ## Vulnerability Details **File Locations**: - `SKILL.md:58` - `src/index.ts:577-637` - `src/index.ts:738-750` **Vulnerability Type**: Missing server-side authorization for paid and irreversible actions **Risk Level**: High ### Vulnerable Code The Skill documentation requires confirmation before submitting a paid order: ```markdown - **Confirm before ordering.** Sending a note costs money and results in a physical card being mailed. Always confirm the details with the user before calling `send_order`. ``` However, the `send_order` handler immediately forwards tool arguments to the external API: ```ts server.tool( "send_order", "Send a real handwritten note via Handwrytten. This is the primary tool — it places " + "an order that results in a physical card being written by a robot with a real pen " + "and mailed to the recipient. Use list_cards and list_fonts first to get valid IDs. " + "The recipient can be an inline address object or a saved address ID number. " + "For bulk sends, pass an array of recipients.", { cardId: z.string().describe("Card template ID (from list_cards)"), font: z.string().describe("Handwriting font ID or label (from list_fonts)"), message: z.string().optional().describe("The handwritten message body"), wishes: z.string().optional().describe("Closing wishes (e.g. 'Best,\\nThe Team')"), recipient: z .union([ AddressSchema, z.number().describe("Saved recipient address ID"), z.array( z.union([ AddressSchema.extend({ message: z.string().optional().describe("Per-recipient message override"), wishes: z.string().optional().describe("Per-recipient wishes override"), }), z.number().describe("Saved recipient address ID"), ]) ), ]) .describe("Recipient — an address object, saved address ID, or array for bulk"), sender: z .union([ AddressSchema, ...[truncated 4402 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Introduce a mandatory preview phase** - Add a server-side operation such as `preview_order`. - Resolve card, font, recipients, inserts, gift cards, scheduling, quantity, and estimated cost. - Return a canonical summary for explicit user review. 2. **Use server-issued approval tokens** - Generate a cryptographically random, short-lived token after preview creation. - Bind the token to a hash of the exact order parameters, authenticated account, recipient count, and estimated cost. - Require this token in both `send_order` and `basket_send`. - Reject expired, reused, missing, or mismatched tokens. 3. **Prevent post-approval mutation** - Do not accept independently editable order details during final submission. - Load the approved parameters from server-side state using the preview identifier. - Invalidate approval if recipients, messages, card selection, gift-card value, inserts, schedule, or other material details change. 4. **Apply controls to every submission path** - Enforce the same approval process for direct orders and basket submissions. - Do not allow `basket_send` to bypass the controls applied to `send_order`. - Default `testMode` to `true` until a valid approval token is supplied. 5. **Add financial and volume safeguards** - Configure maximum recipients per order, maximum gift-card value, and per-transaction or daily spending limits. - Require stronger or repeated confirmation for bulk sends and transactions exceeding configurable thresholds. - Refuse unexpectedly large recipient arrays before contacting the API. 6. **Improve confirmation contents** - Present the final card, font, complete message, sender, recipients, recipient count, attached items, gift-card value, scheduled date, and estimated total cost. - Require an explicit user decision tied to this exact summary. 7. **Add auditability and replay protection** - Record preview creation, approval ...[truncated 540 chars]
