T09 · Insecure Skill Coding Practices
- Location
scripts/mail_cli.py:138- Finding
Arbitrary File Write Through Untrusted Attachment Filenames
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This mail skill mostly matches its stated email-management purpose, but it has unsafe file handling and high-impact mail actions without enough safeguards.
Review carefully before installing. Use only with a dedicated mail account or app password, restrict filesystem permissions on the mail_data directory, avoid autonomous send/delete/export actions, and do not fetch untrusted attachments until filename containment is fixed. Treat verification-code summaries as sensitive authentication material.
scripts/mail_cli.py:138Arbitrary File Write Through Untrusted Attachment Filenames
scripts/mail_cli.py:208Task Identifier Path Traversal Permits Unauthorized JSON File Disclosure
requirements.txt:1Unpinned and Unhashed Third-Party Dependencies Prevent Reproducible Installation
This finding shows the skill claims broad email indexing, search, reply, and summarization features that are not consistently represented in the described implementation. Such overclaiming can mislead agents into relying on nonexistent safeguards or workflows and can mask the true handling of sensitive email content, especially when summarization and indexing imply additional retention and processing of private communications.
This finding shows the skill claims broad email indexing, search, reply, and summarization features that are not consistently represented in the described implementation. Such overclaiming can mislead agents into relying on nonexistent safeguards or workflows and can mask the true handling of sensitive email content, especially when summarization and indexing imply additional retention and processing of private communications.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
os.makedirs(TASKS_DIR, exist_ok=True)
def load_config():
"""Load configuration from .env file"""
# Look for .env in the parent directory of scripts
env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '.env')
if os.path.exists(env_path):
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
os.makedirs(TASKS_DIR, exist_ok=True)
def load_config():
"""Load configuration from .env file"""
# Look for .env in the parent directory of scripts
env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '.env')
if os.path.exists(env_path):
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_config():
"""Load configuration from .env file"""
# Look for .env in the parent directory of scripts
env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '.env')
if os.path.exists(env_path):
load_dotenv(env_path)
else:
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
}
# Parse accounts
for key, value in os.environ.items():
if key.startswith('MAIL_ACCOUNT_') and key.endswith('_EMAIL'):
account_prefix = key[:-6] # Remove _EMAIL
account_id = account_prefix.split('_')[-1]
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_client(config, email_account=None):
if not config['ACCOUNTS']:
raise ValueError("No mail accounts configured in .env")
if email_account and email_account in config['ACCOUNTS']:
account_config = config['ACCOUNTS'][email_account]
The summarizer deliberately extracts and highlights verification codes from emails, effectively surfacing authentication secrets in a compact report. In an agent skill, this increases the chance that one-time codes or other credentials are exposed to unintended recipients, logs, downstream tools, or prompt-injected workflows.
The README describes local storage behavior and even mentions absolute attachment paths, but it does not explicitly warn that fetched emails, attachments, and metadata may be exposed to the agent itself and to other local users or processes with filesystem access. In a mail-management skill, this omission is security-relevant because email content commonly contains credentials, invoices, personal data, and sensitive business information, so users may underestimate the confidentiality risks of enabling the skill.
The skill advertises and instructs use of capabilities that access environment secrets, local files, persistent storage, and networked email services, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, missing scope makes it easier for the skill to be invoked with overly broad privileges, increasing the risk of unauthorized mailbox access, credential exposure, or unintended file writes.
The activation description is so broad that the skill may trigger for many ordinary email-related requests without clear limits, causing an agent to use a high-privilege mail tool when a narrower or read-only action would suffice. In context, this is risky because the skill includes sensitive and destructive operations over live mailboxes, local storage, and outbound sending.
The skill documents destructive actions like move and delete without requiring confirmation or warning about irreversible consequences. In an email-management context, accidental or prompt-induced deletion/movement can cause loss of important records, disrupt business workflows, or hide evidence of communications.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# Save EML
eml_filename = "".join([c for c in email_data['message_id'] if c.isalpha() or c.isdigit() or c in '-_.']).rstrip() + '.eml'
eml_path = os.path.join(paths['eml_path'], eml_filename)
with open(eml_path, 'wb') as f:
f.write(bytes(email_data['raw_email']))
email_data['local_path_eml'] = eml_path
Attachment filenames are written directly with os.path.join(att_dir, att.filename) and no sanitization or path normalization check. A malicious email attachment name containing traversal sequences or absolute paths could escape the attachment directory and overwrite arbitrary files accessible to the process.
for att in email_data['attachments']:
if att.filename:
att_path = os.path.join(att_dir, att.filename)
with open(att_path, 'wb') as f:
f.write(att.payload)
db_attachments.append({
'filename': att.filename,
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
if 'date' in json_data and isinstance(json_data['date'], datetime):
json_data['date'] = json_data['date'].isoformat()
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(json_data, f, ensure_ascii=False, indent=2)
email_data['local_path_json'] = json_path
The send command can transmit arbitrary email bodies and local attachments immediately without any confirmation gate or warning. In an LLM-agent setting, prompt injection or misunderstanding could cause unintended outbound messages or attachment exfiltration on the user's behalf.
Deletion is destructive and irreversible from the local database perspective, yet the command performs server-side deletion immediately with no confirmation prompt. In an autonomous skill context, this raises the risk of accidental or induced data loss through prompt injection, ambiguity, or model error.
The export command allows dumping locally indexed emails to any user-specified path, including sensitive metadata and potentially full contents depending on search_emails output. In an agent skill context, this materially expands exfiltration capability beyond ordinary mail interaction and can persist sensitive mailbox data to attacker-chosen locations.
The export command writes sensitive mailbox data to an arbitrary file path without warning or confirmation. In an agent-driven environment, this can facilitate silent data staging or exfiltration to locations later read by other tools or users.
The summarize command emits status text, headings, and guidance in Chinese only, which imposes a specific language on users without offering a choice. This is a natural-language locale policy issue because the skill does not provide opt-in, configuration, or documented justification for the forced language.
This code assembles outbound email content, recipients, and optional file attachments, then sends them over SMTP. Although the function has a docstring, it does not provide any user-facing disclosure, confirmation prompt, or visible logging that data and local files may be transmitted externally.
The method deletes emails from the selected folder, and the docstring notes deletion may be permanent depending on server behavior. However, there is no confirmation prompt, visible user disclosure, or user-facing log at the point of deletion to warn about possible irreversible effects.
The README presents the skill instructions and usage information entirely in Chinese, which imposes a specific language on users without indicating an option to select another language. This can violate language/locale policy when no user opt-in or justified locale limitation is documented.
The dependency specification for imap-tools uses a lower-bound-only version constraint, which allows future releases to be installed without review. This creates a supply-chain risk because a later incompatible or compromised release could be pulled into the skill unexpectedly.
imap-tools>=1.5.0
python-dotenv>=1.0.0
beautifulsoup4>=4.12.0
jinja2>=3.1.0
python-dotenv is not pinned and is allowed to resolve to any future version greater than or equal to 1.0.0. In a skill that may load local configuration and secrets, unpinned dependency resolution increases supply-chain exposure and makes it harder to determine whether vulnerable versions are in use.
imap-tools>=1.5.0
python-dotenv>=1.0.0
beautifulsoup4>=4.12.0
jinja2>=3.1.0
No suspicious patterns detected.