Back to skill

Security audit

Email Manager

Security checks for vulnerabilities and agentic risk

Overview

This is a local email-management skill, but its documentation and implementation disagree in ways that matter for sensitive email and credential handling.

Review before installing. Use this only for non-sensitive local email data unless you are comfortable with plaintext JSON storage, do not place SMTP passwords or tokens in settings.json, and do not rely on the documented send, dry-run, read, delete, interactive compose, or programmatic API behavior until the documentation and implementation are corrected.

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

Warning
Location
email-manager.js:39
Finding

Plaintext Storage of Sensitive Email Data Without Restrictive File Permissions

Content
View full analysis

Vulnerability Details

File Location: email-manager.js:39-52, with sensitive data written at email-manager.js:130-145
Vulnerability Type: Plaintext sensitive-data storage and insecure default file permissions
Risk Level: Medium

Vulnerable Code

js
const DATA_DIR = path.join(WORKSPACE, 'memory', 'email');
const EMAILS_FILE = path.join(DATA_DIR, 'emails.json');
const SETTINGS_FILE = path.join(DATA_DIR, 'settings.json');

function ensureDir(dir) {
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

function loadJSON(file, fallback) {
  try {
    const data = fs.readFileSync(file, 'utf8');
    return JSON.parse(data);
  } catch { return fallback || {}; }
}

function saveJSON(file, data) {
  ensureDir(path.dirname(file));
  fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8');
}

The affected write path stores complete draft content:

js
// Get configured sender
const settings = loadJSON(SETTINGS_FILE, {});
email.from = settings.sender || 'configured@domain.com';

if (send) {
  // In production, would send via SMTP/API
  console.log(`[email-manager] Would send email:`);
  console.log(`  To: ${email.to}`);
  console.log(`  Subject: ${email.subject}`);
  console.log(`  Body: ${email.body.substring(0, 100)}...`);
} else {
  emails.push(email);
  saveJSON(EMAILS_FILE, emails);
  console.log(`[email-manager] Draft saved: ${email.id}`);
}

Technical Analysis

The application serializes email bodies, sender and recipient addresses, subjects, and metadata directly into an unencrypted JSON file. Directory creation does not specify a mode such as 0700, and file creation does not specify a mode such as 0600. Access therefore depends on the process umask and existing filesystem permissions.

The skill documentation also tells users that SMTP credentials may be stored in memory/email/settings.json. The current implementation reads only the sender property, but a user following that documentation could ...[truncated 1712 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the storage directory with owner-only permissions:
js
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
  1. Write sensitive files with mode 0600. Prefer an atomic write through a securely created temporary file in the same protected directory:
js
fs.writeFileSync(tempFile, JSON.stringify(data, null, 2), {
  encoding: 'utf8',
  mode: 0o600,
  flag: 'wx'
});
fs.renameSync(tempFile, file);
fs.chmodSync(file, 0o600);
  1. Validate ownership and permissions of existing directories and files before reading or writing them. Refuse operation or warn prominently if they are group- or world-accessible.

  2. Use lstatSync() and appropriate safe-open semantics to reject symbolic links and reduce the risk of writing through attacker-controlled filesystem objects.

  3. Do not store SMTP passwords or tokens in settings.json. Use an operating-system credential store, protected environment injection, or a dedicated secrets manager. Keep only non-secret settings such as the sender address in JSON.

  4. If email content requires protection from privileged local readers or backups, add encryption at rest with keys stored separately from the email files.

  5. Update the documentation to distinguish clearly between non-secret settings and credentials, and document the required ownership and permission checks.

Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The documentation materially misrepresents behavior by claiming immediate sending, mark-as-read, and delete workflows that are not actually callable, while also describing the skill as a local email manager with no external dependencies. In an agent setting, this can cause unsafe reliance on nonexistent actions, leading to missed approvals, incorrect assumptions about message state, or accidental disclosure if operators believe mail was sent or deleted when it was not.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This markdown file describes interactive email composition and later states that the store auto-saves on every mutation, meaning user data is written to disk. Although the behavior is documented, it is not presented as a warning or caution near the compose workflow, which could surprise users expecting a dry-run or temporary draft flow.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The Security section explicitly states 'Uses Node.js child_process with array args' and 'Email Manager uses execSync for CLI operations'. That contradicts the rest of the README's portrayal of a direct local JSON email manager CLI and indicates documentation asserting a subprocess-based implementation that is different from what the skill is described as doing.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill simultaneously states that send mode would only integrate with SMTP/API in the future and elsewhere documents outbound SMTP sending as if it is currently real. This inconsistency is dangerous because agents or users may treat a preview-only or draft-only action as a real transmission path, creating operational failures and possible disclosure if sensitive content is handled under false assumptions.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest and integration notes mix 'zero external dependencies' and 'local only' claims with language suggesting actual SMTP delivery, which obscures the trust and data-flow boundaries of the skill. In security-sensitive agent workflows, this can lead to incorrect approvals, underestimation of exfiltration risk, and deployment into environments that prohibit networked email operations.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The header documentation says --inbox --read <id> marks an email as read and --inbox --delete <id> deletes an email. In the actual CLI switch, inbox mode only calls showInbox(count, searchQuery === 'unread') and never calls markAsRead or deleteEmail, so the documented behavior contradicts what the code does.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The doc comment advertises --compose --send <to> <subject> → Send immediately, implying an actual send operation. However, when send is true, composeEmail only logs Would send email and does not transmit the email or save it to the sent store, which directly contradicts the documented intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The deleteEmail function performs an irreversible modification by removing an email from storage and writing the updated dataset back to disk. Although it logs after deletion, there is no prior confirmation prompt or warning before the destructive action occurs.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The test name 'drafts: compose output visible' suggests it is validating compose functionality, but the code merely invokes '--inbox 5' and checks for a subject string after replacing the backing JSON store. This is an active mismatch between the test's stated intent and what it actually exercises.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/run-self-tests.js:28

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/test-email-manager.js:28