Back to skill

Security audit

Imap Smtp Email

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its email purpose, but unsafe setup migration and file-write handling create real local security risks that warrant Review before installation.

Review this skill before installing. It needs access to your email account and can send mail, read private messages, change read/unread state, and save attachments. Avoid running the legacy migration path unless you trust the existing legacy .env file, keep the credential file private, restrict ALLOWED_READ_DIRS and ALLOWED_WRITE_DIRS narrowly, and update dependencies before use if 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
setup.sh:49
Finding
Legacy configuration is executed as shell code during migration<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:49-52` **Vulnerability Type**: Shell command injection through unsafe configuration evaluation **Risk Level**: High ### Vulnerable Code ```bash if [ "$MIGRATE" = true ]; then # Source the legacy .env to get variables set -a source "$LEGACY_CONFIG_FILE" 2>/dev/null set +a ``` ### Technical Analysis The migration process treats `~/.config/imap-smtp-email/.env` as executable Bash code by loading it with `source`. An environment file should be parsed strictly as data, but `source` evaluates all shell syntax in the file. Consequently, command substitutions, function invocations, redirections, expansions, and arbitrary shell commands embedded in the legacy configuration are executed with the privileges of the user running `setup.sh`. Restricting the file to `KEY=value` by convention does not provide any security because Bash does not enforce that convention. The vulnerable behavior is only necessary for extracting configuration values, not for the Skill's declared IMAP/SMTP functionality. It therefore exceeds the minimum behavior required for migration. ### Attack Path 1. An attacker gains the ability to create or modify `~/.config/imap-smtp-email/.env`, such as through insecure permissions, another vulnerable local application, a malicious backup, or a manipulated configuration package. 2. The attacker adds executable shell syntax, for example: ```bash IMAP_HOST=imap.example.com IMAP_USER=user@example.com IMAP_PASS="$(malicious-command)" ``` 3. The user runs `bash setup.sh`. 4. The script detects the legacy configuration and offers migration. 5. The user selects the migration option. 6. `source "$LEGACY_CONFIG_FILE"` evaluates the attacker's shell syntax. 7. The malicious command runs with the setup user's privileges before migration continues. ### Impact Assessment Successful exploitation provides arbitrary command execution as the local user running the setup script ...[truncated 311 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never load `.env` files with `source`, `.`, or another shell evaluation mechanism. - Parse the file with a non-evaluating parser, such as `dotenv.parse()` in Node.js. - Permit only explicitly supported keys and reject malformed names, duplicate keys, NUL bytes, shell syntax, and unexpected multiline values. - Transfer parsed values through a safe serialization format rather than interpolating them into shell commands. - Perform the entire migration in Node.js where configuration values can be handled as strings without a second shell parser. - Verify that the legacy file is a regular file owned by the current user and is not writable by group or others before processing it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:109
Finding
Configuration-derived values are evaluated a second time with eval<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:109-118` **Vulnerability Type**: Shell command injection through `eval` **Risk Level**: Medium ### Vulnerable Code ```bash if [[ "$key" =~ ^([A-Z0-9]+)_IMAP_HOST$ ]]; then NP="${BASH_REMATCH[1]}" NPROVIDER=$(node -e " const { detectProvider } = require('$SKILL_DIR/scripts/providers'); const provider = detectProvider('$value'); console.log(provider || 'custom'); ") NPUSER=$(eval echo "\$${NP}_IMAP_USER") NPPASS=$(eval echo "\$${NP}_IMAP_PASS") if [ "$NPROVIDER" = "custom" ]; then NPHOST="$value" NPPORT=$(eval echo "\$${NP}_IMAP_PORT") ``` The same pattern continues for additional account fields: ```bash NPTLS=$(eval echo "\$${NP}_IMAP_TLS") NPSMTP=$(eval echo "\$${NP}_SMTP_HOST") NPSMTPPORT=$(eval echo "\$${NP}_SMTP_PORT") NPSMTPSEC=$(eval echo "\$${NP}_SMTP_SECURE") ``` ### Technical Analysis The script uses `eval` to retrieve dynamically named account variables. `eval` concatenates its arguments and sends the resulting string back through the Bash parser. Configuration-derived content can therefore receive a second round of shell interpretation. Although the account prefix is constrained by the regular expression to uppercase letters and digits, the value stored in the dynamically selected variable is not safely isolated from the second evaluation. Shell metacharacters or command substitutions retained in a configuration value may become executable syntax when the generated command is reparsed. This issue compounds the unsafe `source` operation but should also be removed independently. Dynamic variable lookup does not require shell evaluation. ### Attack Path 1. An attacker modifies a named-account entry in the legacy configuration. 2. The attacker places shell syntax in a field that is later retrieved through `eval`, such as a named password or SMTP field. 3. The user starts setup and selects legacy migration. 4. The migration loop detects the named acc ...[truncated 680 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove every use of `eval`. - If Bash must be retained, use indirect expansion without reparsing values: ```bash variable_name="${NP}_IMAP_USER" NPUSER="${!variable_name}" ``` - Validate the generated variable name against a strict allowlist before indirect lookup. - Prefer migrating the file entirely in Node.js with `dotenv.parse()` and ordinary object property access: ```javascript const username = parsed[`${prefix}_IMAP_USER`]; ``` - Treat every configuration value as opaque data and never insert it into executable shell or JavaScript source. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:353
Finding
Named-account credentials are copied into a potentially world-readable temporary file<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:353-370` **Vulnerability Type**: Unsafe temporary file containing plaintext credentials **Risk Level**: Medium ### Vulnerable Code ```bash "reconfigure") TEMP_FILE=$(mktemp) grep -E '^[A-Z0-9]+_(PROVIDER|USERNAME|PASSWORD|IMAP_|SMTP_)' "$CONFIG_FILE" > "$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 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" ``` ### Technical Analysis `mktemp` securely creates `$TEMP_FILE`, but it does not create `$TEMP_FILE.named`. The shell redirection: ```bash > "$TEMP_FILE.named" ``` creates that second file according to the process umask. Under a common `022` umask, the file may be created with mode `0644`, allowing other local users to read it. The `grep` expression deliberately copies named-account configuration fields, including `PASSWORD`, IMAP credentials, and SMTP credentials, into this file. The file is deleted later, but it remains exposed during reconfiguration and may be recoverable through backups, monitoring software, or filesystem snapshots. ### Attack Path 1. The system has multiple local users or an untrusted process capable of reading files permitted by the setup user's umask. 2. The victim has one or more named email accounts in the shared configuration. 3. The victim runs setup and chooses to reconfigure the default account. 4. The script creates `$TEMP_FILE.named` through ordinary shell redirection. 5. Named-account credentials are written into that temporary file. 6. Before deletion, the attacker discovers and reads the file from the temporary directory. 7. The attacker o ...[truncated 531 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Set a restrictive umask before creating any configuration or temporary files: ```bash umask 077 ``` - Create every temporary file independently with `mktemp`; do not derive a new filename by appending a suffix to an existing temporary path. - Register a cleanup trap so temporary files are removed on normal exit, errors, and signals: ```bash trap 'rm -f "$TEMP_FILE" "$NAMED_TEMP_FILE"' EXIT HUP INT TERM ``` - Avoid copying plaintext credentials into multiple files. Parse and rewrite the configuration in memory where practical. - Explicitly apply mode `0600` to the final configuration and any unavoidable intermediate files before writing secrets. - Store long-lived credentials in an operating-system credential manager where available. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/imap.js:14
Finding
Attachment write whitelist can be bypassed through symlinked directories<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imap.js:14-31` and `scripts/imap.js:348-362` **Vulnerability Type**: Symlink-based path validation bypass and unintended file overwrite **Risk Level**: Medium ### Vulnerable Code ```javascript function validateWritePath(dirPath) { if (!config.allowedWriteDirs.length) { throw new Error('ALLOWED_WRITE_DIRS not set in .env. Attachment download is disabled.'); } const resolved = path.resolve(dirPath.replace(/^~/, os.homedir())); const allowedDirs = config.allowedWriteDirs.map(d => 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 path is later 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 is provided, only download matching attachment if (specificFilename && attachment.filename !== specificFilename) { continue; } if (attachment.content) { const filePath = path.join(resolvedDir, sanitizeFilename(attachment.filename)); fs.writeFileSync(filePath, attachment.content); ``` ### Technical Analysis The write whitelist checks only lexically normalized paths using `path.resolve()`. It does not canonicalize existing filesystem objects using `fs.realpathSync()` and does not reject symlinks. A path can therefore appear to be under an allowed directory while resolving through a symlink to a location outside that directory. For example, `~/Downloads/link` passes a whitelist permitting `~/Downloads`, even if `link` points to another directory. `fs.writeFileSync()` follows symlinks and overwrites ...[truncated 1615 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Canonicalize the existing destination directory with `fs.realpathSync()` before comparing it with canonicalized allowed directories. - Reject destination paths containing symlink components by checking each component with `fs.lstatSync()`. - Revalidate the destination immediately before opening the file to reduce time-of-check/time-of-use exposure. - Open destination files using exclusive and no-follow semantics where supported, such as `O_CREAT | O_EXCL | O_NOFOLLOW`. - Reject an existing destination if `lstat()` reports that it is a symbolic link. - Avoid overwriting existing files by default. Require explicit user confirmation or generate a collision-safe filename. - For stronger isolation, open the approved directory once and perform descriptor-relative file operations so path components cannot be replaced after validation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (48)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The code substantially matches the email-reading portions of the description: it connects to IMAP, checks messages, fetches full emails, searches, marks messages read/unread, lists mailboxes, and supports account listing/config-based multi-account handling. However, the declared purpose explicitly includes sending email via SMTP and sending emails with attachments, and this code chunk contains no SMTP logic or outbound email capability at all. Additionally, the code can write attachments to local disk, which is an extra resource-access capability not mentioned in the description. Therefore the description does not accurately represent the supplied code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The description promises both IMAP and SMTP capabilities, especially mailbox access and state changes. However, this code only creates an SMTP transporter with nodemailer, sends messages, optionally attaches local files, verifies/test-sends through SMTP, and lists configured accounts. There is no IMAP connection, mailbox enumeration, message retrieval, unread checking, searching, or read/unread flag management in the provided chunk. Therefore the declared description materially overstates what this code actually does.

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/imap.js --account work check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/smtp.js --account work send --to foo@bar.com --subject Hi --body Hello
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Known Vulnerable Dependency: linkify-it==5.0.0 — 2 advisory(ies): CVE-2026-48801 (LinkifyIt#match scan loop has quadratic algorithmic complexity); CVE-2026-59887 (linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on at)

High
Category
Supply Chain
Confidence
96% confidence
Finding
The lockfile includes linkify-it 5.0.0, which is flagged for quadratic-complexity denial-of-service issues during crafted input scanning, including mailto parsing. In an email skill, this package is reached through mailparser and may process attacker-controlled email content, so malformed messages could cause excessive CPU use and service degradation.

Known Vulnerable Dependency: nodemailer==8.0.5 — 8 advisory(ies): CVE-2026-82661 (Nodemailer: CRLF injection in Nodemailer List-* header comments allows arbitrary); GHSA-2x7j-588g-ccc2 (Nodemailer: Quadratic (O(n²)) time complexity in addressparser allows remote den); GHSA-8m3c-c648-2xjj (Nodemailer: resolveContent() on a MailMessage bypasses disableFileAccess/disable) +5 more

High
Category
Supply Chain
Confidence
98% confidence
Finding
The lockfile includes transitive nodemailer 8.0.5 via mailparser, and the advisories listed include header injection and content-resolution bypass classes of issues. In an email-processing skill, mail-related libraries handle attacker-influenced addresses, headers, and MIME structures, so a vulnerable version can increase risk of denial of service, malformed message abuse, or unsafe content handling depending on how the package is exercised.

Known Vulnerable Dependency: nodemailer==7.0.13 — 10 advisory(ies): CVE-2026-82661 (Nodemailer: CRLF injection in Nodemailer List-* header comments allows arbitrary); GHSA-2x7j-588g-ccc2 (Nodemailer: Quadratic (O(n²)) time complexity in addressparser allows remote den); GHSA-8m3c-c648-2xjj (Nodemailer: resolveContent() on a MailMessage bypasses disableFileAccess/disable) +7 more

High
Category
Supply Chain
Confidence
99% confidence
Finding
The primary dependency set includes nodemailer 7.0.13, which is reported with multiple high-severity advisories including CRLF/header injection and denial-of-service weaknesses. Because this skill sends email and may construct messages from user-provided fields such as recipients, subjects, headers, or attachments, the vulnerable dependency is directly security-relevant and could enable spoofed headers, mail manipulation, or resource exhaustion.

Known Vulnerable Dependency: semver==5.3.0 — 1 advisory(ies): CVE-2022-25883 (semver vulnerable to Regular Expression Denial of Service)

High
Category
Supply Chain
Confidence
83% confidence
Finding
The lockfile includes semver 5.3.0, which has a known ReDoS issue. Here it appears only as a transitive dependency of utf7 used by imap, so exploitability is more limited unless untrusted input is passed into semver evaluation paths; however, it remains a genuine vulnerable component in the dependency tree.

Known Vulnerable Dependency: nodemailer==7.0.13 — 10 advisory(ies): CVE-2026-82661 (Nodemailer: CRLF injection in Nodemailer List-* header comments allows arbitrary); GHSA-2x7j-588g-ccc2 (Nodemailer: Quadratic (O(n²)) time complexity in addressparser allows remote den); GHSA-8m3c-c648-2xjj (Nodemailer: resolveContent() on a MailMessage bypasses disableFileAccess/disable) +7 more

High
Category
Supply Chain
Confidence
98% confidence
Finding
The manifest references nodemailer 7.0.13, which is flagged with multiple known advisories including header injection, denial-of-service, and content resolution security bypass issues. In an email-sending skill, this is especially dangerous because untrusted message fields, addresses, headers, or attachments may be processed directly, increasing the likelihood that a vulnerable mail library could be abused to send malformed emails, inject headers, access unintended content, or disrupt service.

Credential Access

High
Category
Privilege Escalation
Content
const { PROVIDERS } = require('./providers');

// Config file locations
const LEGACY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.env');
const SHARED_ENV_PATH = path.join(os.homedir(), '.config', 'mail-skills', '.env');
const FALLBACK_ENV_PATH = path.resolve(__dirname, '../.env');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

No suspicious patterns detected.