Back to skill

Security audit

Handwrytten

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Handwrytten API wrapper, but it can submit paid physical-mail orders and mutate account data without enforced confirmation safeguards.

Review this before installing. Use it only with an API key/account where the assistant is allowed to spend credits, send physical mail, process recipient addresses, and change saved Handwrytten assets. Require human review in your client before live sends, bulk sends, gift-card orders, basket submission, and deletions; consider using a limited or test account where possible.

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

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]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The public description frames the skill narrowly as sending handwritten notes, but the documented capabilities also include recipient and sender address-book management, order history, QR code lifecycle actions, custom image upload, custom card creation, and geographic prospecting. This mismatch can mislead users and reviewers about the true data access and action surface, increasing the chance of unintended PII handling, broader account modification, or costly side effects.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The public description frames the skill narrowly as sending handwritten notes, but the documented capabilities also include recipient and sender address-book management, order history, QR code lifecycle actions, custom image upload, custom card creation, and geographic prospecting. This mismatch can mislead users and reviewers about the true data access and action surface, increasing the chance of unintended PII handling, broader account modification, or costly side effects.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README promotes tools that can trigger real-world actions with cost and privacy consequences—sending physical mail, handling recipient addresses, and supporting bulk sends—without any user-facing warnings, confirmation guidance, or notice about charges and sensitive personal data handling. In an MCP context, this is dangerous because an AI assistant may operationalize these actions quickly at scale, increasing the risk of accidental mailing, exposure of PII, or unintended spend.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares access to an environment secret (`HANDWRYTTEN_API_KEY`) and requires `node`, but it does not declare any explicit tool scope such as allowed tools or permissions. That weakens least-privilege guarantees and makes it harder for a host or reviewer to constrain what the skill may invoke or access at runtime.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This package advertises sending handwritten notes through an API, which necessarily involves transmitting user-provided message content and recipient details to an external service. Failing to disclose that data leaves the local environment can mislead users and agents, increasing the chance of unintended disclosure of personal or sensitive information.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The skill description frames the capability as simply sending handwritten notes, but the implementation exposes a much broader administrative surface including account inspection, address-book management, QR code creation/deletion, custom-card design, gift card handling, basket submission, and prospecting. This scope mismatch is dangerous because an AI agent or user may grant trust based on the narrow description while the server can perform materially different and more sensitive actions, increasing the chance of unintended data access or destructive operations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The delete_qr_code tool performs irreversible deletion but its user-facing description is a minimal 'Delete a QR code' with no warning, confirmation, or friction. In an agent setting, ambiguous destructive tools can be invoked accidentally or through prompt manipulation, causing permanent loss of configuration or assets.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The delete_recipient tool can remove one or many saved recipient addresses, yet the interface does not clearly communicate permanence or require confirmation for batch deletion. Because address-book entries are sensitive operational data, accidental or manipulated invocation can disrupt mailing workflows and cause loss of recipient records.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The delete_sender tool permanently removes saved sender/return addresses without any explicit warning or confirmation semantics. Loss of sender addresses can break fulfillment workflows and may be triggered unintentionally by an autonomous agent that does not appreciate the destructive nature of the operation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The send_order tool triggers real-world effects: it spends account resources and causes physical mail to be sent to recipients, potentially at scale. Without explicit confirmation language or execution safeguards, an agent could be induced to place unwanted orders, leak sensitive message/address data to a third party, or incur financial and reputational harm through accidental mailing.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The basket_send tool submits all queued orders for live processing, which can amplify mistakes because a single call may trigger multiple real mailings and charges. Although testMode exists, the tool does not make safe default behavior explicit, so an agent may execute a production send without presenting the user with the operational and financial consequences.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The delete_custom_image tool removes uploaded design assets with no explicit indication that the action is irreversible or may affect dependent custom cards. In an agent workflow, this can lead to unintended asset loss and broken card designs if the deletion is performed speculatively or under prompt confusion.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The delete_custom_card tool permanently deletes a custom card design without warning or confirmation. Because custom designs may represent business-critical templates, accidental invocation by an AI agent can result in operational disruption and loss of work product.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The calculate_targets tool adds geographic prospecting functionality that is not necessary for basic note-sending and expands the skill into lead-generation and location-based targeting. In this context, that extra capability increases abuse potential because an assistant authorized to send mail can also be used to profile or target geographic areas without that power being clearly implied by the skill's stated purpose.

Vague Triggers

Low
Confidence
82% confidence
Finding
This manifest file includes a high-level description of sending handwritten notes via AI assistants, but it does not specify any concrete trigger phrases, scope boundaries, or exclusion conditions. In manifest files, such broad capability descriptions can contribute to ambiguous activation behavior if used by tooling to infer when the skill should be invoked.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"prepublishOnly": "npm run build"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "handwrytten": "^1.0.0",
    "zod": "^3.24.4"
  },
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: @modelcontextprotocol/sdk has 3 known advisory(ies) (CVE-2026-25536 (@modelcontextprotocol/sdk has cross-client data leak via shared server/transport); CVE-2026-0621 (Anthropic's MCP TypeScript SDK has a ReDoS vulnerability); CVE-2025-66414 (Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protec)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding
The manifest allows a floating @modelcontextprotocol/sdk version while known advisories exist for this package family, including issues relevant to MCP servers such as cross-client data leakage and DNS rebinding protections. Because this skill handles user content and may connect agents to external actions, inability to verify the resolved SDK version meaningfully increases the risk of deploying a vulnerable build.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "handwrytten": "^1.0.0",
    "zod": "^3.24.4"
  },
  "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "handwrytten": "^1.0.0",
    "zod": "^3.24.4"
  },
  "devDependencies": {
    "@types/node": "^22.15.3",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: zod has 1 known advisory(ies) (CVE-2023-4316 (Zod denial of service vulnerability)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"zod": "^3.24.4"
  },
  "devDependencies": {
    "@types/node": "^22.15.3",
    "tsup": "^8.4.0",
    "tsx": "^4.19.4",
    "typescript": "^5.8.3"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/node": "^22.15.3",
    "tsup": "^8.4.0",
    "tsx": "^4.19.4",
    "typescript": "^5.8.3"
  },
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: tsup has 1 known advisory(ies) (CVE-2024-53384 (tsup DOM Clobbering vulnerability)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
    "@types/node": "^22.15.3",
    "tsup": "^8.4.0",
    "tsx": "^4.19.4",
    "typescript": "^5.8.3"
  },
  "engines": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@types/node": "^22.15.3",
    "tsup": "^8.4.0",
    "tsx": "^4.19.4",
    "typescript": "^5.8.3"
  },
  "engines": {
    "node": ">=18.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.