T09 · Insecure Skill Coding Practices
- Location
src/tools/contacts-extra.ts:109- Finding
Single-contact deletion bypasses enforced user confirmation
- Content
View full analysis
Vulnerability Details
File Location:
src/tools/contacts-extra.ts, lines 109–117
Vulnerability Type: Missing authorization confirmation for a destructive operation
Risk Level: HighComplete Code Snippet
ts server.registerTool('gog_contacts_delete', { description: 'Delete a Google Contact by resource name.', annotations: { destructiveHint: true, openWorldHint: true }, inputSchema: z.object({ resourceName: z.string().describe('Contact resource name (people/...)'), account: accountParam, }), }, async ({ resourceName, account }) => { return runOrDiagnose(['contacts', 'delete', pos(resourceName), '--force'], { account }); // gog gates this op; without --force the runner's --no-input makes it refuse });Technical Analysis
The MCP tool accepts a contact resource name and immediately invokes the authenticated
gogclient withcontacts deleteand--force. The inline comment confirms that--forceis deliberately included to bypass the underlying command’s interactive safety gate, which would otherwise refuse deletion because the runner uses--no-input.The
destructiveHintannotation only describes the operation to MCP clients; it does not enforce approval. Unlike the batch-delete implementation in the same file, this handler does not callrequireDispatchConfirmation, require a one-time token, or otherwise prove that the user approved the exact account and contact being deleted.The relevant trust boundary is between an MCP caller or autonomous agent action and the user’s authenticated Google Contacts account. A caller able to invoke the tool can perform the deletion using the account credentials already available to the MCP server.
Attack Path
- The server is configured with an authenticated Google account.
- An MCP caller, including an agent acting on mistaken or untrusted instructions, obtains or selects a contact resource name.
- The caller invokes
gog_contacts_deletewith that resource nam ...[truncated 765 chars]
- Remediation
View remediation
Remediation Suggestions
Use
requireDispatchConfirmationbefore invoking the command, following the existing batch-delete pattern. Bind the confirmation to:- The exact contact resource name
- The selected Google account
- The delete action
- A short expiration period
- One-time token consumption
Only append
--forceafter the confirmation succeeds. If confirmation is unavailable, omit--forceso the underlying non-interactive command refuses the operation. Add tests proving that an initial unconfirmed call cannot reachrunOrDiagnose, that a valid token authorizes only the exact bound target, and that replayed or modified confirmations are rejected.
