Back to skill

Security audit

mail-imap-sync

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to sync email as advertised, but it handles private mailbox data and passwords with enough weak safeguards to require careful review before use.

Use this only with mailboxes you intentionally want copied to local plaintext files. Prefer an app-specific, easily revocable IMAP password, keep config.json out of version control, restrict local file permissions or use disk encryption, use simple account names such as letters/digits/hyphens only, and pin or preinstall reviewed dependencies instead of relying on run.sh as written.

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 (3)

T08 · Insecure Dependencies

Warning
Location
run.sh:8
Finding

Unpinned Dependencies Are Installed Automatically at Runtime

Content
View full analysis

Vulnerability Details

File Location: run.sh:8-10
Vulnerability Type: Supply-chain exposure through unpinned runtime package installation
Risk Level: Medium

Vulnerable Code

bash
# Install dependencies on every execution
pip install python-dateutil html2text >/dev/null 2>&1

Technical Analysis

The launcher installs python-dateutil and html2text without pinning versions or verifying package hashes. The packages are obtained from whichever Python package index is configured in the execution environment.

Consequently, the reviewed source does not uniquely determine the code that will execute. A compromised package release, package-index account, index mirror, or locally configured package source could provide malicious or unexpectedly modified dependencies. Suppressing standard output and error output also conceals installation failures and security-relevant warnings.

Because main.py imports both dependencies, malicious code placed in an installed package can execute when the application starts. Source distributions may also execute build-related code during installation, depending on pip's build process and package metadata.

Attack Path

  1. An attacker compromises an upstream dependency release, configured package index, or package-index mirror.
  2. The attacker publishes or serves a malicious version under one of the requested package names.
  3. A user invokes ./run.sh.
  4. The unrestricted pip install command downloads and installs the malicious version.
  5. Malicious code executes during package installation or when main.py imports the installed dependency.
  6. The code runs with the same operating-system privileges and environment access as the user executing the Skill.

Impact Assessment

Successful exploitation provides arbitrary Python code execution with the runner's privileges. This can expose mailbox credentials, downloaded messages, local files accessible to ...[truncated 315 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin reviewed dependency versions in a dedicated requirements or lock file.
  2. Record and enforce cryptographic hashes:
    bash
    python3 -m pip install --require-hashes -r requirements.txt
    
  3. Install dependencies into a dedicated virtual environment during a controlled setup phase rather than on every execution.
  4. Use a trusted, explicitly configured package index and restrict fallback indexes.
  5. Retain installation diagnostics instead of redirecting all output to /dev/null.
  6. Perform dependency vulnerability and provenance scanning as part of the release process.
  7. Run the synchronization process under a least-privileged operating-system account.

T09 · Insecure Skill Coding Practices

Warning
Location
main.py:207
Finding

Mailbox Credentials Are Loaded Directly from a Plaintext Configuration File

Content
View full analysis

Vulnerability Details

File Location: main.py:207-210 and config.json:2-9
Vulnerability Type: Plaintext sensitive credential storage
Risk Level: Medium

Vulnerable Code

python
name = acc["name"]
key = acc["user"]

acc_state = state["accounts"].setdefault(key, {
    "last_uid": 0,
    "initialized": False
})

client = IMAPClient(acc["imap_host"], acc["user"], acc["pass"])

The configuration schema places the password directly in the account object:

json
{
  "accounts": [
    {
      "name": "gmail",
      "imap_host": "imap.gmail.com",
      "user": "xxx@gmail.com",
      "pass": "[application-specific password]",
      "mode": "recent",
      "recent_days": 7
    }
  ]
}

Technical Analysis

The application expects an IMAP password to be stored as a directly readable JSON string and passes it from config.json into the IMAP client. It does not support a protected secret store, perform a file-permission check, or warn when the configuration is broadly readable.

Although the checked-in value is only a placeholder and is not a confirmed live secret, the documented deployment workflow directs users to replace it with a real application password. That real credential can then be exposed through repository commits, project archives, backups, support bundles, permissive filesystem permissions, or another local process operating under an account that can read the project directory.

Attack Path

  1. A user replaces the placeholder password in config.json with a valid IMAP or application-specific password.
  2. The configuration file is accidentally committed, copied into an archive, included in a backup, or left readable by another local account or process.
  3. An attacker obtains the plaintext configuration.
  4. The attacker extracts the mailbox username, IMAP host, and password.
  5. The attacker authenticates directly to the IMAP service, subject to th ...[truncated 654 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove mailbox passwords from the ordinary JSON configuration schema.
  2. Retrieve credentials from an operating-system keyring, managed secret store, or protected environment variable.
  3. Store only a secret reference in config.json, for example:
    json
    {
      "password_env": "MAIL_IMAP_PASSWORD"
    }
    
  4. Provide a credential-free config.example.json and exclude the real configuration from version control.
  5. Validate that any local secret file has restrictive ownership and permissions before reading it.
  6. Use application-specific credentials with the narrowest available permissions and support prompt credential revocation.
  7. Add secret scanning to repository and release workflows.
  8. If a real credential has ever been committed or distributed, revoke and rotate it rather than merely deleting it from the latest revision.

T09 · Insecure Skill Coding Practices

Warning
Location
main.py:151
Finding

Unsanitized Account Name Allows Writes Outside the Email Storage Directory

Content
View full analysis

Vulnerability Details

File Location: main.py:151-183
Vulnerability Type: Path traversal through a configuration-controlled directory component
Risk Level: Medium

Vulnerable Code

python
def save_email(data, uid, account):
    dt = data["time"]["dt"]

    path = os.path.join(BASE_DIR, account, str(dt.year), f"{dt.month:02d}", f"{dt.day:02d}")
    os.makedirs(path, exist_ok=True)

    title = sanitize(data["subject"] or "untitled")
    msg_id = sanitize(data["message_id"] or str(uid))

    filename = f"{uid}_{msg_id}_{title}.md"
    full = os.path.join(path, filename)

    if os.path.exists(full):
        return None

    content = f"""# {data['subject']}

From: {data['from']}
Date(raw): {data['date']}
Date(local): {data['time']['readable']}
Date(ISO): {data['time']['iso']}
Timestamp: {data['time']['timestamp']}

UID: {uid}
Message-ID: {data['message_id']}
Account: {account}

---

{data['body']}
"""

    with open(full, "w", encoding="utf-8") as f:
        f.write(content)

Technical Analysis

The account argument comes from the configurable account name. It is inserted directly into a path under BASE_DIR without sanitization or canonical containment validation.

os.path.join() does not prevent traversal. Values containing .. components can resolve outside BASE_DIR, while an absolute account name can cause earlier path components to be discarded. The program subsequently creates directories and writes a Markdown file at the resulting location.

The subject and message ID receive limited filename sanitization, but the account component does not. The existing-file check prevents overwriting a file with the exact generated name, and the year/month/day plus generated filename structure constrains the destination shape. These restrictions reduce impact but do not ensure that writes remain inside the intended email directory.

Attack Path

  1. An attac ...[truncated 1383 chars]
Remediation
View remediation

Remediation Suggestions

  1. Restrict account names to a conservative identifier format, such as letters, digits, underscores, and hyphens:
    python
    if not re.fullmatch(r"[A-Za-z0-9_-]+", account):
        raise ValueError("Invalid account name")
    
  2. Reject absolute paths, empty names, . components, and .. components.
  3. Resolve and verify the destination before creating directories or files:
    python
    base = os.path.realpath(BASE_DIR)
    target = os.path.realpath(
        os.path.join(base, account, str(dt.year), f"{dt.month:02d}", f"{dt.day:02d}")
    )
    
    if os.path.commonpath([base, target]) != base:
        raise ValueError("Destination escapes the email storage directory")
    
  4. Consider generating the storage directory from a stable internal account identifier rather than a user-provided display name.
  5. Account for symbolic-link attacks where the project directory may be writable by another user. Open files using secure descriptor-based APIs where the deployment threat model requires protection from concurrent local attackers.
  6. Run the service with filesystem permissions limited to its dedicated storage directory.
  7. Treat saved email content as untrusted when consumed by Markdown renderers, automation, or AI agents.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (7)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill explicitly synchronizes and locally stores full email contents, but the documentation does not clearly warn users about the privacy and data-handling risks of pulling potentially sensitive messages onto local disk. This can lead to unintended exposure of personal, corporate, or regulated data because users may enable the skill without understanding retention, access-control, or downstream AI-processing implications.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The code uses supplied IMAP credentials to connect to a remote mailbox and retrieve message contents, but there is no visible in-code disclosure, confirmation flow, or scope limitation around that network access. In a skill context, silent mailbox access is sensitive because it grants broad access to private communications and can surprise users or be repurposed to collect data beyond what they expected.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill persists full email content, headers, and identifiers to local markdown files under predictable directories without any access-control, consent, minimization, or retention safeguards visible in the code. Because email bodies commonly contain credentials, reset links, invoices, and personal data, local plaintext storage increases the risk of sensitive-data exposure if the host is shared, compromised, backed up insecurely, or indexed by other tools.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The natural-language description states that timestamps are unified to Asia/ShangHai, and later sections repeat that all email times are converted to that timezone. This is a locale-specific behavior presented as mandatory rather than optional, which can violate language/locale policy when no opt-in or justification is provided.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code fixes the local timezone to Asia/Taipei, and elsewhere also normalizes dates to Asia/Shanghai, imposing region-specific locale behavior without any user choice or documented justification. This can violate language/locale policy expectations when users are outside that locale or expect their own regional settings to be respected.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The inline comment says the code is converting to Beijing time, but the rest of the program defines LOCAL_TZ as Asia/Taipei and uses that for recent-message cutoff logic. This creates an intent/documentation inconsistency about what 'local' time the skill is actually normalizing email timestamps to.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The function converts parsed email dates into Asia/Shanghai time regardless of the user's locale or account preferences. This enforces a specific regional time interpretation without opt-in, which is a natural-language policy concern for locale handling.

Content

No source excerpt is available for this finding.