Back to skill

Security audit

Email Registration Scanner

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate email-audit purpose, but it handles mailbox credentials and scan results in ways users should review carefully before installing.

Install only if you are comfortable granting access to email account history. Prefer OAuth or app-specific passwords, avoid regular mailbox passwords, do not run plaintext IMAP except to a trusted local Proton Bridge, and delete any generated JSON results after use because they can reveal a map of your online accounts.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/imap_scan.py:167
Finding
Mailbox Credentials Exposed Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imap_scan.py:167-177`; invocation documented at `SKILL.md:70-76` **Vulnerability Type**: Credential exposure through process arguments **Risk Level**: Medium ### Vulnerable Code ```python def main(): parser = argparse.ArgumentParser(description="IMAP Registration Scanner") parser.add_argument("--host", required=True) parser.add_argument("--port", type=int, default=993) parser.add_argument("--user", required=True) parser.add_argument("--password", required=True) parser.add_argument("--output", required=True) parser.add_argument("--no-ssl", action="store_true") ``` The documented invocation explicitly places the secret in the command line: ```bash python3 "{baseDir}/scripts/imap_scan.py" \ --host "imap.mail.me.com" \ --port 993 \ --user "user@icloud.com" \ --password "app-specific-password" \ --output "/tmp/registration_scan_results.json" ``` ### Technical Analysis The script requires the mailbox password to be supplied through `--password`. Command-line arguments can be exposed through process inspection facilities, local monitoring or telemetry tools, shell history, debugging output, and orchestration logs. Although the script does not explicitly print the password, placing it in the process argument vector conflicts with the documented assertion that credentials are never logged or stored. The risk is especially significant where the provider guide permits use of a regular mailbox password instead of a narrowly scoped app password. ### Attack Path 1. A user or Agent launches the scanner using the documented command. 2. The mailbox password becomes part of the process argument vector. 3. A same-user monitoring process, privileged local process, execution wrapper, or logging system records or inspects the command. 4. The observer extracts the password before the process terminates or retrieves it from retained logs or shell history. 5. The attacker authen ...[truncated 604 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the `--password` command-line option. - Retrieve credentials directly from OpenClaw’s Secret Store where available. - For interactive use, read the password with `getpass.getpass()` so it is neither echoed nor placed in the argument vector. - For automated use, accept the secret through a protected inherited file descriptor or another platform-supported secret channel. - Avoid ordinary environment variables where possible because they may also be exposed through process inspection and diagnostic dumps. - Update `SKILL.md` so examples never contain a password argument. - Require app-specific, revocable, least-privilege credentials and clearly warn against regular account passwords. - Clear in-memory references to credentials as soon as authentication is complete, acknowledging that Python cannot guarantee secure memory erasure. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/imap_scan.py:121
Finding
Plaintext IMAP Authentication Allowed for Arbitrary Remote Hosts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imap_scan.py:121-129` and `scripts/imap_scan.py:173-184` **Vulnerability Type**: Unrestricted transmission of credentials over an unencrypted connection **Risk Level**: High ### Vulnerable Code ```python def connect_imap(host, port, user, password, use_ssl): """Connect and authenticate to IMAP server.""" if use_ssl: context = ssl.create_default_context() imap = imaplib.IMAP4_SSL(host, port, ssl_context=context) else: imap = imaplib.IMAP4(host, port) imap.login(user, password) return imap ``` ```python parser.add_argument("--host", required=True) parser.add_argument("--port", type=int, default=993) parser.add_argument("--user", required=True) parser.add_argument("--password", required=True) parser.add_argument("--output", required=True) parser.add_argument("--no-ssl", action="store_true") args = parser.parse_args() use_ssl = not args.no_ssl print(f"[scanner] Connecting to {args.host}:{args.port} ({'SSL' if use_ssl else 'plain'})...") try: imap = connect_imap(args.host, args.port, args.user, args.password, use_ssl) ``` ### Technical Analysis The `--host` argument accepts an arbitrary destination, while `--no-ssl` disables transport encryption without checking whether the destination is the local Proton Mail Bridge. When plaintext mode is selected, `imaplib.IMAP4.login()` transmits the supplied username and password over an unencrypted IMAP connection. The documentation intends plaintext mode only for Proton Bridge at `127.0.0.1:1143`, but that restriction is not enforced by the implementation. The TLS-enabled path uses `ssl.create_default_context()`, which appropriately enables certificate validation. The vulnerability is specifically the unrestricted plaintext fallback. ### Attack Path 1. A user or Agent is induced to run the scanner with an attacker-controlled or otherwise remote `--host`. 2. The co ...[truncated 1123 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject `--no-ssl` unless the destination is verified as loopback. - Parse the destination using the `ipaddress` module and require `ip_address(resolved_address).is_loopback`. - Validate every resolved address to prevent a hostname from resolving to an unexpected remote destination. - Prefer an explicit `--proton-bridge` mode that hardcodes an approved loopback host and expected port instead of exposing a general `--no-ssl` switch. - Require TLS for all non-loopback destinations and retain `ssl.create_default_context()` certificate and hostname verification. - Consider certificate pinning only if provider operational requirements justify the maintenance cost. - Display a clear error rather than silently permitting insecure remote authentication. - Add automated tests confirming that remote IPv4, remote IPv6, and non-loopback hostnames are rejected when plaintext mode is requested. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/imap_scan.py:228
Finding
Sensitive Mailbox-Derived Results Written Without Enforced Protection or Cleanup<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imap_scan.py:228-255`; cleanup is only instructed at `SKILL.md:132-137` **Vulnerability Type**: Unsafe handling of sensitive temporary output **Risk Level**: Medium ### Vulnerable Code ```python entry = { "service": service, "date": date_str, "from": headers["from"], "subject": subject, } # Keep only oldest entry per service if service not in service_map: service_map[service] = entry else: if date_str < service_map[service]["date"]: service_map[service] = entry if i % 50 == 0 and i > 0: print(f"[scanner] Processed {i}/{len(seen_ids)} emails...") time.sleep(0.05) imap.logout() final = sorted(service_map.values(), key=lambda x: x["date"], reverse=True) with open(args.output, "w", encoding="utf-8") as f: json.dump(final, f, ensure_ascii=False, indent=2) print(f"[scanner] Done. {len(final)} unique services found → {args.output}") ``` The Skill text places cleanup responsibility outside the script: ```markdown - Delete temp files (`/tmp/registration_scan_*.json`) after the session ends. ``` ### Technical Analysis The output includes sender identities, subjects, dates, and inferred service registrations. This data can reveal the user’s online accounts, financial or health-related services, personal interests, and other sensitive relationships. The script opens the caller-selected output path using the default process permissions. It does not enforce mode `0600`, does not create a private temporary directory, and does not protect against pre-existing paths or symbolic links. File confidentiality therefore depends on the ambient `umask` and filesystem configuration. The script also does not delete the output. Cleanup exists only as an Agent instruction, even though the README claims temporary files are deleted after e ...[truncated 1339 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create output files atomically with owner-only permissions, such as mode `0600`. - Use a private temporary directory created with `tempfile.TemporaryDirectory()` rather than a predictable shared `/tmp` filename. - Open files with exclusive-creation and no-follow protections where supported, rejecting pre-existing files and symbolic links. - Validate that the output destination is a regular file within an approved directory. - Implement cleanup in code using `try/finally` or a context-managed temporary directory. - If users choose to retain results, require explicit consent and move the data to a user-selected protected location. - Ensure partial output is removed when authentication, searching, parsing, or serialization fails. - Update documentation so retention and deletion behavior accurately match the implementation. - Consider minimizing the output by omitting full subjects unless the user explicitly requests them, since the service name, date, and sender domain may be sufficient. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The introductory description emphasizes convenience and outcomes but does not prominently warn that the skill will access and scan the user's email account contents. Because email inboxes contain highly sensitive personal and account data, insufficient disclosure can undermine informed consent and cause users to authorize broader access than they expected.

Session Persistence

Medium
Category
Rogue Agent
Content
https://github.com/fatihbtw/email-registration-scanner

# Option 2 – manual
mkdir -p ~/.openclaw/skills/email-registration-scanner
cp -r . ~/.openclaw/skills/email-registration-scanner
```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The invocation phrases are broad and include natural-language requests like 'Where am I registered?' and 'Scan my emails for registrations,' which could be triggered during ordinary conversation. In the context of a skill that accesses mailbox contents, accidental invocation can lead to unintended scanning of sensitive email data and privacy exposure.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill instructs the agent to use network access and write temporary files, but it does not declare any explicit tool scope or allowed-tools restrictions. For a skill that handles mailbox credentials and scans large volumes of sensitive email metadata, missing scope boundaries increases the chance of over-privileged execution, unintended tool use, or unsafe file/network operations beyond what the user expects.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrases are broad enough to match common account-management requests like 'show me all my accounts,' which could invoke a full mailbox scan when the user may have intended something narrower. Because this skill can access highly sensitive historical registration data across multiple providers, over-triggering creates a meaningful privacy and consent risk.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill scans entire mailboxes for historical registrations, which can reveal a detailed inventory of the user's online accounts and associated timelines, but the description does not prominently warn about that privacy impact up front. In this context, insufficient disclosure undermines informed consent and makes it easier for users to authorize a highly sensitive scan without understanding the breadth of exposure.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script accepts raw mailbox credentials as command-line arguments and immediately uses them for IMAP authentication, but provides no explicit warning or safer handling path for these sensitive secrets. Command-line passwords can be exposed via shell history, process listings, logs, or orchestration layers, making this especially risky for a skill designed to access an entire email account.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script writes a detailed inventory of the user's registrations, including sender addresses, subjects, and dates, to a local JSON file without any explicit consent prompt, warning, or retention guidance. In the context of a mailbox-registration scanner, this output is highly privacy-sensitive because it reveals a broad map of the user's online accounts and could be exposed through insecure temp paths, shared systems, or later collection by other components.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The description states the skill scans email accounts for registration, welcome, and confirmation emails to build a historical list of all services a user has signed up for, but it does not document any trigger boundaries, mailbox scope limits, or explicit user-consent constraints. Because email history is highly sensitive, broad activation language can cause over-collection or unexpected invocation beyond what a user reasonably intended.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
This reference mixes English instructions with provider-specific German phrases such as "POP3/IMAP abrufen," which can impose a language/locale assumption on users. Because the file does not state that these steps are specific to German-localized interfaces or provide alternative locale guidance, it may violate the language/locale policy criterion.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
Line L003 instructs users to run query batches for different languages as a fixed sequence, and the remainder of the file defines a predetermined set of supported locales. Because the file does not explain that these are optional examples or provide a way to choose/add locales, it embeds a locale policy that may exclude other languages without user opt-in.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The description promises comprehensive scanning of a user's email history to reconstruct registrations across services, which creates a privacy risk because it implies large-scale review of sensitive personal communications without any visible limitation or opt-in wording. In this skill context, the data being inferred can reveal the user's accounts, habits, health, finances, and other sensitive affiliations, making the lack of scope and consent language more dangerous.

Static analysis

No suspicious patterns detected.