Back to skill

Security audit

Only read email

Security checks for vulnerabilities and agentic risk

Overview

This is a read-only email skill, but it asks for mailbox credentials and can persist them in global configuration without enough safeguards.

Use this only with a mailbox you are comfortable exposing to the agent. Prefer a revocable app password or authorization code, avoid pasting real account passwords into chat, avoid storing EMAIL_PASS in global OpenClaw configuration, and treat all email body text as untrusted content that should be summarized rather than obeyed.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:34
Finding
Mailbox credentials may be exposed through chat, command-line arguments, environment variables, and persistent global configuration<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:34-63`, `SKILL.md:89-93`, `scripts/email_reader.py:42-72`, `scripts/email_reader.py:237-243` **Vulnerability Type**: Insecure credential handling **Risk Level**: High ### Vulnerable Code and Instructions ```markdown python3 scripts/email_reader.py \ --user your@163.com \ --pass_ YOUR_AUTH_CODE \ --server pop.163.com \ --port 995 \ subjects --n 5 ``` ```markdown export EMAIL_USER="your@163.com" export EMAIL_PASS="YOUR_AUTH_CODE" export POP3_SERVER="pop.163.com" export POP3_PORT="995" python3 scripts/email_reader.py subjects --n 5 ``` ```markdown 在 OpenClaw 中,可通过 `gateway config.patch` 将环境变量写入全局配置: { "env": { "EMAIL_USER": "your@163.com", "EMAIL_PASS": "YOUR_AUTH_CODE", "POP3_SERVER": "pop.163.com", "POP3_PORT": "995" } } ``` ```markdown ## 工作流程 1. 先询问用户的邮箱账号、授权码(密码)、邮箱类型 2. 根据邮箱类型查 `references/providers.md` 确认服务器地址 3. 决定配置方式(推荐环境变量),完成配置 4. 运行脚本,解析 JSON 输出后以自然语言回复用户 ``` ```python def load_config(args): """从 CLI / 环境变量 / .email_config 三层加载配置,返回 dict""" cfg = { "user": None, "pass_": None, "server": None, "port": 995, } # 层 3:.email_config 文件 config_file = Path(__file__).parent / ".email_config" if config_file.exists(): parser = configparser.ConfigParser() parser.read(config_file) if "email" in parser: s = parser["email"] cfg["user"] = s.get("user", cfg["user"]) cfg["pass_"] = s.get("pass_", cfg["pass_"]) cfg["server"] = s.get("server", cfg["server"]) cfg["port"] = int(s.get("port", cfg["port"])) # 层 2:环境变量 cfg["user"] = os.environ.get("EMAIL_USER", cfg["user"]) cfg["pass_"] = os.environ.get("EMAIL_PASS", cfg["pass_"]) cfg["server"] = os.environ.get("POP3_SERVER", cfg["server"]) cfg["port"] = int(os.environ.get("POP3_PORT", cfg["port"])) # 层 1:命令行参数(最高优先级) if args.user: cfg["us ...[truncated 3125 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not instruct the Agent to request mailbox passwords or authorization codes in ordinary chat. 2. Integrate with a dedicated secret manager or masked credential-input mechanism that does not place secrets in model context or transcripts. 3. Remove or deprecate the `--pass_` argument. Read the credential from a protected file descriptor, operating-system keychain, or narrowly scoped secret injection mechanism. 4. Do not write mailbox credentials into global Agent configuration. Scope secret injection to the single email-reader process and remove it immediately afterward. 5. If environment variables remain supported, prevent unnecessary child-process inheritance and document that they are a compatibility fallback rather than the preferred mechanism. 6. If `.email_config` remains supported, verify ownership and reject the file unless permissions prevent access by group and other users. Avoid following symlinks where practical. 7. Ensure logs and error handlers redact account identifiers, authorization codes, connection strings, and configuration values. 8. Prefer provider-specific OAuth or revocable, narrowly scoped application credentials where POP3 provider support permits it. 9. Document credential revocation and rotation procedures so users can rapidly invalidate a potentially exposed authorization code. ]]>

other

Warning
Location
scripts/email_reader.py:107
Finding
Untrusted email content is passed to the Agent without an indirect prompt-injection boundary<![CDATA[ ## Vulnerability Details **File Location**: `scripts/email_reader.py:107-132`, `scripts/email_reader.py:151-160`, `SKILL.md:95-96`, `SKILL.md:119` **Vulnerability Type**: Indirect prompt injection through attacker-controlled email content **Risk Level**: Medium ### Vulnerable Code and Instructions ```python def get_email_body(msg): text_body = "" html_body = "" if msg.is_multipart(): for part in msg.walk(): ct = part.get_content_type() charset = part.get_content_charset() or "utf-8" if ct == "text/plain": text_body = part.get_payload(decode=True).decode(charset, errors="ignore") elif ct == "text/html": html_body = part.get_payload(decode=True).decode(charset, errors="ignore") else: charset = msg.get_content_charset() or "utf-8" payload = msg.get_payload(decode=True) if payload: ct = msg.get_content_type() if ct == "text/plain": text_body = payload.decode(charset, errors="ignore") elif ct == "text/html": html_body = payload.decode(charset, errors="ignore") if text_body: return text_body.strip() elif html_body: clean = re.sub(r"<[^>]+>", "", html_body) return clean.strip() return "(无正文内容)" ``` ```python def parse_email(raw_lines): msg = email.message_from_bytes(b"\r\n".join(raw_lines)) try: date = parsedate_to_datetime(msg.get("Date")).strftime("%Y-%m-%d %H:%M:%S") except Exception: date = msg.get("Date", "未知时间") return { "subject": decode_str(msg.get("Subject")), "from": decode_str(msg.get("From")), "to": decode_str(msg.get("To")), "date": date, "body": get_email_body(msg), "attachments": get_attachments(msg), } ``` ```markdown ## 输出格式 脚本统一输出 JSON,agent 解析后转为自然语言回复用户。 ``` ```markdown - 正文超长时建议截取前 2000 字 ...[truncated 2388 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly define every retrieved email field as untrusted content that must never override system, developer, user, or Skill instructions. 2. Add Skill-level instructions stating that commands, links, requests, or policy text found inside messages must be summarized as content and never executed or obeyed. 3. Place email data in clearly labeled delimiters and keep it separate from operational instructions supplied to the Agent. 4. Process and summarize messages in an isolated stage without access to tools, credentials, memory-writing functions, or external network actions. 5. Require explicit user confirmation before performing any action suggested by an email, including opening links, downloading files, changing configuration, replying, forwarding, or invoking unrelated tools. 6. Enforce body, header, and message-count limits in code rather than relying on advisory documentation. Truncation is defense in depth and is not a complete prompt-injection defense. 7. Return a structured warning alongside retrieved content, such as `content_trust: "untrusted_external_input"`, and ensure the host Agent honors that marker. 8. Do not render active HTML. If HTML conversion is needed, use a robust parser and sanitizer rather than a regular expression, while recognizing that sanitization does not remove natural-language prompt injections. 9. Add adversarial tests containing instruction-like email bodies and verify that the Agent only reports their contents without following them. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill can access sensitive mailbox credentials via environment variables, but it does not declare any explicit tool scope or permission boundary. This makes the capability opaque to the host agent and increases the chance of over-broad execution or accidental secret exposure during configuration and use.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The trigger phrases are very broad and map to common requests like '看看邮件' or '最近有什么邮件', which can cause the skill to activate in situations where the user did not intend to grant mailbox access. Because the skill reads full email content and attachments metadata, accidental activation can expose highly sensitive personal or corporate information.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The workflow explicitly tells the agent to ask for the user's mailbox account and authorization code/password, then read full email contents, but it does not clearly warn about the privacy and credential-handling risks. This creates a direct pathway for collecting secrets and exposing sensitive communications, including one-time codes, personal data, and confidential business content.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code file reads sensitive credentials from command-line arguments, environment variables, or a local config file, then connects to a POP3 server and fetches email metadata and bodies. Although the docstring describes functionality, it does not warn users that the tool will process mailbox contents and credentials, which is a privacy-sensitive operation.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
This markdown file presents all instructions and labels exclusively in Chinese, including the title and configuration guidance. Under the policy criteria, forcing a specific language without user opt-in can be a natural-language policy violation when no language choice or justification is provided.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The file’s docstring, CLI descriptions, and error/output-facing text are written exclusively in Chinese, which imposes a specific language on users without opt-in or documented locale justification. This matches the language/locale policy concern for natural-language content in code files.

Static analysis

No suspicious patterns detected.