Back to skill

Security audit

Imap Smtp Email Fixed

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to provide the promised email features, but it ships insecure defaults and credential-handling flaws that could expose mailbox access or affect local files.

Review this skill carefully before installing. It needs mailbox credentials and can read, send, and modify email state. Do not use the bundled config as-is: enable certificate verification, replace the hardcoded account settings, restrict allowed read/write directories, and avoid running setup where terminal output is logged until the password-printing fallback is fixed.

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
config.env:6
Finding
TLS Certificate Verification Disabled for IMAP and SMTP<![CDATA[ ## Vulnerability Details **File Location**: `config.env:6-13` **Vulnerability Type**: Improper certificate validation **Risk Level**: High ### Vulnerable Code ```env IMAP_REJECT_UNAUTHORIZED=false IMAP_MAILBOX=INBOX SMTP_HOST=smtp.mail.yahoo.com SMTP_PORT=465 SMTP_SECURE=true SMTP_USER=aabjj93@yahoo.com SMTP_FROM=aabjj93@yahoo.com SMTP_REJECT_UNAUTHORIZED=false ``` ### Technical Analysis The active bundled configuration explicitly disables certificate verification for both IMAP and SMTP. Although encryption is enabled, setting `rejectUnauthorized` to `false` causes the clients to accept certificates that are self-signed, expired, issued for a different host, or signed by an untrusted authority. These values are consumed by `scripts/config.js` and passed into the IMAP and Nodemailer TLS configurations. Consequently, the clients do not authenticate the identity of the remote mail server. The ability to configure custom mail servers and self-signed certificates is consistent with the Skill's functionality, but disabling verification in the distributed default configuration is not necessary and exceeds an acceptable security baseline. ### Attack Path 1. A user runs the Skill with the bundled configuration. 2. The user connects through a network controlled or influenced by an attacker, such as a malicious access point, compromised router, or poisoned DNS resolver. 3. The attacker redirects the IMAP or SMTP connection to an attacker-controlled endpoint. 4. The endpoint supplies an invalid or attacker-generated certificate. 5. Because certificate verification is disabled, the Skill accepts the certificate. 6. The client authenticates to the malicious endpoint, exposing the email username and password or app password. 7. The attacker can also observe or modify incoming and outgoing email data. ### Impact Assessment A network-positioned attacker could obtain email credentials, read intercepted email content, capture outgoing messages and attachments ...[truncated 152 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Set both certificate-verification options to secure defaults: ```env IMAP_REJECT_UNAUTHORIZED=true SMTP_REJECT_UNAUTHORIZED=true ``` - Do not distribute active configuration with certificate verification disabled. - For private servers requiring a custom certificate, install the relevant private CA certificate and configure the client to trust that CA instead of disabling all verification. - If an insecure override must remain available, require an explicit per-account opt-in and display a prominent warning describing the credential interception risk. - Consider rejecting insecure settings for public mail providers such as Yahoo, Gmail, and Outlook. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:303
Finding
Setup Script Prints Email Credentials to Terminal Output<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:190`, `setup.sh:235-238`, and `setup.sh:303-307` **Vulnerability Type**: Plaintext exposure of credentials **Risk Level**: Medium ### Vulnerable Code The password is initially collected without terminal echo: ```bash read -s -p "Password / App Password / Authorization Code: " PASSWORD ``` It is then embedded into a multiline variable: ```bash SECRET_VARS="# IMAP/SMTP ${ACCOUNT_NAME:-default} account ${ACCOUNT_PREFIX}IMAP_PASS=$PASSWORD ${ACCOUNT_PREFIX}SMTP_PASS=$PASSWORD" ``` If the expected OpenClaw environment file does not exist, that variable is printed: ```bash else echo "⚠️ OC .env not found at $OC_ENV — add credentials manually:" echo "$SECRET_VARS" fi ``` ### Technical Analysis The setup script uses `read -s`, indicating that the credential is intended to remain hidden during entry. That protection is defeated when `~/.openclaw/.env` does not exist because the complete IMAP and SMTP password assignments are subsequently emitted to standard output. Terminal output may be retained in scrollback, CI/CD logs, orchestration logs, shell-session recordings, support transcripts, or remote terminal monitoring systems. The password is also duplicated into both `IMAP_PASS` and `SMTP_PASS` output lines. Reading an email credential is necessary for the declared functionality. Printing it is not necessary and violates minimum-disclosure principles. ### Attack Path 1. The user runs `bash setup.sh` on a system where `~/.openclaw/.env` does not exist. 2. The setup script prompts for an email password, app password, or authorization code. 3. The user enters the secret through the hidden-input prompt. 4. The fallback branch prints the secret in plaintext as `IMAP_PASS` and `SMTP_PASS`. 5. Another local user, logging service, session recorder, or operator with access to captured output retrieves the credential. 6. The exposed credential is used to access the user's mailbox or send email as ...[truncated 340 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never print credential values, even as a manual-configuration fallback. - Create the credential directory and file securely when absent: ```bash mkdir -p "$HOME/.openclaw" chmod 700 "$HOME/.openclaw" touch "$HOME/.openclaw/.env" chmod 600 "$HOME/.openclaw/.env" ``` - Write secrets directly to the protected file using restrictive permissions. - If automatic creation is not appropriate, abort and provide instructions containing only variable names, not values. - Clear the shell variable after use where practical: ```bash unset PASSWORD SECRET_VARS ``` - Ensure error handling cannot include the credential in debug traces or command output. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/imap.js:16
Finding
Attachment Download Allowlist Can Be Bypassed Through Symbolic Links<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imap.js:16-31` and `scripts/imap.js:333-347` **Vulnerability Type**: Symbolic-link path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code The output directory is checked using only lexical path normalization: ```js 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 subsequently used for unrestricted file creation or overwrite: ```js 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); ``` ### Technical Analysis `path.resolve()` normalizes `.` and `..` components but does not resolve symbolic links. The validation therefore confirms only that the textual path begins with an allowed directory. It does not confirm that the filesystem object ultimately reached by that path remains inside the allowlist. For example, an allowed directory could contain a symlink named `out` that points to a directory outside the allowlist. A path such as `~/Downloads/out` passes the prefix check even though writes occur at ...[truncated 1535 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Resolve the canonical destination directory with `fs.realpathSync()` and compare it against canonical allowed directories. - Reject any symlink in the destination path. - After creating a directory, canonicalize and validate it again before writing. - Reject existing destination files that are symbolic links by using `fs.lstatSync()`. - Open output files with exclusive and no-follow semantics where supported, such as `O_CREAT | O_EXCL | O_NOFOLLOW`. - Avoid silently overwriting existing attachments; use unique filenames or require explicit overwrite confirmation. - Keep filename sanitization as defense in depth, but do not treat it as protection against filesystem symlinks. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smtp.js:15
Finding
File Read Allowlist Is Vulnerable to Check-to-Use Races<![CDATA[ ## Vulnerability Details **File Location**: `scripts/smtp.js:15-38`, `scripts/smtp.js:117-125`, and `scripts/smtp.js:235-252` **Vulnerability Type**: TOCTOU file access and allowlist bypass **Risk Level**: Medium ### Vulnerable Code The validation function computes and returns a canonical path: ```js function validateReadPath(inputPath) { let realPath; try { realPath = fs.realpathSync(inputPath); } catch { realPath = path.resolve(inputPath); } if (!config.allowedReadDirs.length) { throw new Error('ALLOWED_READ_DIRS not set in .env. File read operations are disabled.'); } const allowedDirs = config.allowedReadDirs.map(d => 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; } ``` For attachments, the validated canonical path is discarded and the original path is resolved again: ```js 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), }; } ``` Body and subject files are also validated and then reopened by their original names: ```js 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.html = fs.readFileSync(options['html-file'], 'u ...[truncated 1899 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Canonicalize each allowed directory with `fs.realpathSync()` before comparison. - Open each validated file exactly once and consume the resulting file descriptor or buffer instead of reopening the pathname. - Use no-follow flags where the platform supports them, and verify the opened file with `fstat`. - For attachments, pass a validated buffer or read stream created from the already-open descriptor to Nodemailer rather than passing the original pathname. - Remove the fallback from `realpathSync()` to `path.resolve()` for files that must already exist. - Verify that the opened object is a regular file and impose reasonable file-size limits before loading or transmitting it. ]]>
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 (42)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The supplied code substantially matches the IMAP/read side of the description: it connects to IMAP, checks unread/all messages, fetches full content, searches, marks read/unread, lists mailboxes, parses attachments, and downloads attachments. However, the declared purpose explicitly includes sending email via SMTP and sending emails with attachments, and there is no SMTP logic or send-mail function in this code chunk. Additionally, the code has a concrete filesystem write capability for attachment downloads, which is an operational behavior not called out explicitly in the description, though it is closely related to attachment handling. The claim of multiple-account support is only partially supported here through account listing/config display, not through clear per-command account selection or simultaneous account operations in this chunk. Overall, this is a real description/behavior mismatch because a major declared capability—sending mail—is absent from the provided code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a combined IMAP/SMTP skill with substantial mailbox-reading and message-management features. The supplied code chunk is specifically an SMTP CLI: it creates an SMTP transporter, verifies connectivity, sends mail, supports attachments, reads subject/body content from local files, runs a connection test that sends a test message, and lists configured accounts. There is no evidence in this chunk of IMAP connections, inbox access, unread-message detection, message retrieval, search, or read/unread state changes. While sending email with attachments and multi-account-related configuration are consistent with part of the description, the actual behavior shown is materially narrower than the declared purpose, so this is a mismatch.

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
94% confidence
Finding
The lockfile pins linkify-it 5.0.0, which has reported quadratic-complexity denial-of-service issues in its matching logic, including mailto: handling. In this skill, mail content is attacker-controlled input, so parsing crafted email bodies or headers could consume excessive CPU and degrade or block processing.

Known Vulnerable Dependency: nodemailer==8.0.4 — 9 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) +6 more

High
Category
Supply Chain
Confidence
97% confidence
Finding
The bundled transitive nodemailer 8.0.4 under mailparser has multiple advisories, including CRLF/header injection and content resolution weaknesses. Because this skill sends and parses email and may handle untrusted addresses, headers, and message parts, exploitation could enable message/header manipulation, unauthorized content inclusion, or denial of service depending on reachable code paths.

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 top-level dependency nodemailer 7.0.13 is flagged by multiple advisories affecting header parsing, content resolution, and denial-of-service behavior. This is especially relevant in an IMAP/SMTP skill because it directly constructs outbound email from potentially user-controlled fields like recipients, subject, headers, and attachments.

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
semver 5.3.0 is affected by a regular-expression denial-of-service issue, but here it appears only as a transitive dependency of utf7 used by imap. The practical risk is lower because exploitation requires attacker influence over semver parsing inputs, which is less obvious in normal email-processing flows than the other findings.

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 package declares nodemailer 7.0.13, which is reported as affected by multiple advisories including CRLF/header injection, denial-of-service, and bypass issues. In the context of an IMAP/SMTP skill, this is especially dangerous because the library is central to composing and sending email, so crafted inputs could potentially manipulate headers, bypass safeguards, or disrupt service.

Credential Access

High
Category
Privilege Escalation
Content
const dotenv = require('dotenv');

// Split config: secrets in OC .env, connection settings in skill config.env
const OC_ENV_PATH = path.join(os.homedir(), '.openclaw', '.env');
const SKILL_CONFIG_PATH = path.resolve(__dirname, '../config.env');
const LEGACY_ENV_PATH = path.join(os.homedir(), '.config', 'imap-smtp-email', '.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.