Back to skill

Security audit

super-imap-smtp-email

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent email tool, but its file access safeguards and setup handling are weak enough to require careful review before installation.

Install only if you are comfortable giving this skill direct access to your email account and selected local directories. Keep allowed read/write directories narrow, avoid writable shared paths and symlinked directories, review the setup script before reconfiguring accounts, and prefer a version that fixes the path-validation and temporary-credential issues.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/imap.js:14
Finding

Attachment download whitelist can be bypassed through symbolic-link directories

Content
View full analysis
path.resolve(d.replace(/^~/, os.homedir())) ); const allowed = allowedDirs.some(dir => resolved === dir || resolved.startsWith(dir + path.sep) ); if (!allowed) { throw new Error(`Access denied: '${dirPath}' is outside allowed write directories`); } return resolved; } ``` The validated directory is then used for attachment writes: ```javascript const resolvedDir = validateWritePath(outputDir); if (!fs.existsSync(resolvedDir)) { fs.mkdirSync(resolvedDir, { recursive: true }); } const downloaded = []; for (const attachment of parsed.attachments) { if (specificFilename && attachment.filename !== specificFilename) { continue; } if (attachment.content) { const filePath = path.join(resolvedDir, sanitizeFilename(attachment.filename)); fs.writeFileSync(filePath, attachment.content); downloaded.push({ filename: attachment.filename, path: filePath, size: attachment.size, }); } } ``` `path.resolve()` normalizes path components but does not resolve symbolic links. Consequently, a path that lexically appears beneath an allowed directory may resolve through a symbolic link to an arbitrary directory outside the whitelist. The attachment filename is reduced to a basename, which prevents direct `../` traversal but does not address a symbolic link in the directory path. It also doe ...[truncated 1658 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smtp.js:14
Finding

File-read whitelist is vulnerable to symbolic-link race conditions

Content
View full analysis
path.resolve(d.replace(/^~/, os.homedir())) ); const allowed = allowedDirs.some(dir => realPath === dir || realPath.startsWith(dir + path.sep) ); if (!allowed) { throw new Error(`Access denied: '${inputPath}' is outside allowed read directories`); } return realPath; } ``` However, callers discard the validated canonical path and subsequently use the original attacker-controlled path: ```javascript function readAttachment(filePath) { validateReadPath(filePath); if (!fs.existsSync(filePath)) { throw new Error(`Attachment file not found: ${filePath}`); } return { filename: path.basename(filePath), path: path.resolve(filePath), }; } ``` The same issue affects subject and message body files: ```javascript if (options['subject-file']) { validateReadPath(options['subject-file']); options.subject = fs.readFileSync(options['subject-file'], 'utf8').trim(); } if (options['body-file']) { validateReadPath(options['body-file']); const content = fs.readFileSync(options['body-file'], 'utf8'); if (options['body-file'].endsWith('.html') || options.html) { options.html = content; } else { options.text = content; } } else if (options['html-file']) { validateReadPath(options['html-file']); options. ...[truncated 1771 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:235
Finding

Reconfiguration creates a potentially world-readable temporary copy of account credentials

Content
View full analysis
"$TEMP_FILE.named" 2>/dev/null || true cat > "$TEMP_FILE" << EOF $ACCOUNT_VARS # File access whitelist (security) ALLOWED_READ_DIRS=${ALLOWED_READ_DIRS:-$HOME/Downloads,$HOME/Documents} ALLOWED_WRITE_DIRS=${ALLOWED_WRITE_DIRS:-$HOME/Downloads} EOF # Append retained named-account lines if any if [ -s "$TEMP_FILE.named" ]; then echo "" >> "$TEMP_FILE" echo "# Named accounts" >> "$TEMP_FILE" cat "$TEMP_FILE.named" >> "$TEMP_FILE" fi mv "$TEMP_FILE" "$CONFIG_FILE" rm -f "$TEMP_FILE.named" ``` `mktemp` securely creates `TEMP_FILE`, normally with owner-only permissions. It does not create or protect `"$TEMP_FILE.named"`. The shell creates that second file according to the process umask. With a common umask of `022`, it can be created with mode `0644`. The `.named` file contains retained named-account variables, including `IMAP_PASS` and `SMTP_PASS`. It resides in the system temporary directory rather than the protected `~/.config/imap-smtp-email` directory. Its random base name makes prediction difficult, but temporary directory entries can commonly be enumerated by local users while setup is running. ### Attack Path 1. A user runs `setup.sh` and chooses to reconfigure the default account while named accounts already exist. 2. The script copies all named IMAP and SMTP variables, including passwords, into `/tmp/.named`. 3. Under a permissive umask, the copied file is readable by other local users. 4. A local attacker monitors the temporary directory ...[truncated 788 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
package.json:10
Finding

Dependency graph is not reproducibly pinned

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description mostly matches the IMAP-related behavior: the code can check unread/all mail, fetch full messages, search mailboxes, mark read/unread, list mailboxes, and handle attachments. However, the declared purpose explicitly includes sending email via SMTP and sending attachments, but this code chunk contains no SMTP logic or send capability at all. Additionally, the code can download attachments to local disk, which is a meaningful filesystem-write capability not stated in the description. The description also claims multiple-account support; while the code can list configured accounts, this chunk does not show switching between or operating on multiple accounts. Overall, there is a material description/behavior mismatch primarily because a major declared capability (sending mail) is absent from the actual code shown.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The description presents a combined IMAP/SMTP email skill with both inbound and outbound mail management. However, this code chunk is limited to SMTP sending features and account listing. It creates an SMTP transporter with nodemailer, verifies connectivity, sends messages with optional CC/BCC/HTML/attachments, can send a self-test email, and lists configured accounts. It also reads local files for subject/body/HTML/attachments subject to configured directory restrictions. There is no IMAP logic, no mailbox access, and no message state/search handling in the supplied code. Therefore the declared description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 89)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 133)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 145)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 152)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 164)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 182)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 183)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 190)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 197)May include surrounding context.

md
node scripts/imap.js --account work check

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 90)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 198)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 209)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 229)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 232)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 235)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 238)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 245)May include surrounding context.

md
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/config.js (reported line 9)May include surrounding context.

js
const dotenv = require('dotenv');

// Config file locations
const PRIMARY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

// Find the .env file: primary location first, then fallback

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/config.js (reported line 10)May include surrounding context.

js
const dotenv = require('dotenv');

// Config file locations
const PRIMARY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

// Find the .env file: primary location first, then fallback

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/imap.js (reported line 95)May include surrounding context.

js
const dotenv = require('dotenv');

// Config file locations
const PRIMARY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

// Find the .env file: primary location first, then fallback

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/smtp.js (reported line 67)May include surrounding context.

js
const dotenv = require('dotenv');

// Config file locations
const PRIMARY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

// Find the .env file: primary location first, then fallback

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/config.js (reported line 12)May include surrounding context.

js
const PRIMARY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

// Find the .env file: primary location first, then fallback
function findEnvPath() {
  if (fs.existsSync(PRIMARY_ENV_PATH)) return PRIMARY_ENV_PATH;
  if (fs.existsSync(FALLBACK_ENV_PATH)) return FALLBACK_ENV_PATH;

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/config.js (reported line 67)May include surrounding context.

js
const PRIMARY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');

// Find the .env file: primary location first, then fallback
function findEnvPath() {
  if (fs.existsSync(PRIMARY_ENV_PATH)) return PRIMARY_ENV_PATH;
  if (fs.existsSync(FALLBACK_ENV_PATH)) return FALLBACK_ENV_PATH;

Static analysis

No suspicious patterns detected.