Back to skill

Security audit

Proton Bridge Email

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Proton Bridge email purpose, but it needs review because its sender can decrypt email credentials and use insecure or non-local SMTP settings.

Install only if you are comfortable giving the skill access to your Proton Bridge credentials and outgoing email content. Keep SMTP_HOST limited to 127.0.0.1 or another verified loopback address, avoid SMTP_SECURITY=NONE, remove temporary plaintext env files immediately, and review send_email.py before using it for automated mail.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send_email.py:52
Finding

SMTP Certificate Verification Is Disabled and Plaintext Authentication Is Permitted

Content
View full analysis

Vulnerability Details

File Location: scripts/send_email.py, lines 52–98
Vulnerability Type: Improper TLS certificate validation and permitted plaintext SMTP authentication
Risk Level: High

Vulnerable Code

python
smtp_host = conf.get("SMTP_HOST", "127.0.0.1")
smtp_port = int(conf.get("SMTP_PORT", "1025"))
smtp_user = conf.get("PROTON_BRIDGE_USER") or conf.get("PROTON_EMAIL")
smtp_pass = conf.get("PROTON_BRIDGE_PASS")
sec = (conf.get("SMTP_SECURITY") or "").upper()
python
# Proton Bridge uses a local cert; for localhost automation we allow it.
local_ctx = ssl._create_unverified_context()

if "SSL" in sec and "START" not in sec:
    with smtplib.SMTP_SSL(smtp_host, smtp_port, context=local_ctx, timeout=30) as s:
        if smtp_user and smtp_pass:
            s.login(smtp_user, smtp_pass)
        s.send_message(msg)
elif "START" in sec:
    with smtplib.SMTP(smtp_host, smtp_port, timeout=30) as s:
        s.ehlo()
        s.starttls(context=local_ctx)
        s.ehlo()
        if smtp_user and smtp_pass:
            s.login(smtp_user, smtp_pass)
        s.send_message(msg)
else:
    with smtplib.SMTP(smtp_host, smtp_port, timeout=30) as s:
        s.ehlo()
        if smtp_user and smtp_pass:
            s.login(smtp_user, smtp_pass)
        s.send_message(msg)

Technical Analysis

The SMTP host and security mode are loaded from the encrypted configuration without enforcing the documented localhost-only trust boundary. TLS connections use ssl._create_unverified_context(), which disables certificate-chain and hostname validation. Consequently, encryption may be established with an attacker-controlled endpoint without authenticating that endpoint.

The final branch also permits SMTP authentication when TLS is not enabled. If SMTP_SECURITY is empty, unrecognized, or explicitly configured as NONE, s.login() can transmit the Bridge username and pass ...[truncated 2127 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce the intended local-only boundary by accepting only literal loopback addresses or hostnames that resolve exclusively to loopback addresses. Reject all other destinations unless an explicit secure remote mode is implemented.
  2. Replace ssl._create_unverified_context() with ssl.create_default_context() for authenticated TLS.
  3. If Proton Bridge requires a self-signed certificate, trust a dedicated local CA or pin the expected Bridge certificate or public key instead of disabling verification globally.
  4. Reject unknown SMTP_SECURITY values. Use an explicit allowlist such as STARTTLS and SSL.
  5. Prohibit SMTP_SECURITY=NONE whenever credentials or sensitive message content are transmitted. If plaintext SMTP must remain available for a narrowly defined loopback deployment, require an explicit opt-in and verify that the destination is loopback.
  6. Validate all required configuration values before opening a connection and fail closed when the security mode is absent or malformed.
  7. Document certificate enrollment or pin rotation procedures so certificate verification is not disabled as an operational workaround.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/encrypt_env.sh (reported line 4)May include surrounding context.

sh
#!/usr/bin/env bash
set -euo pipefail

# Encrypt a plaintext .env file into ~/clawd/secrets/proton.env.age using age.
# Usage:
#   encrypt_env.sh /path/to/proton.env age1PUBLICKEY...

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill instructs use of local files containing encrypted secrets and execution of shell/Python scripts, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this can enable broader-than-expected file access and shell execution, increasing the chance that secrets under paths like ~/clawd/secrets or ~/.config/age are read or misused without clear operator consent.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/proton-bridge-setup.md (reported line 14)May include surrounding context.

bash
cd ~/Downloads
sudo apt install ./protonmail-bridge_*.deb
sudo apt -f install

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/proton-bridge-setup.md (reported line 15)May include surrounding context.

bash
cd ~/Downloads
sudo apt install ./protonmail-bridge_*.deb
sudo apt -f install

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instructions explicitly tell the user to place live Proton Bridge credentials, including the bridge password, into a plaintext file before encryption. Even if temporary, plaintext secrets can be exposed through backups, shell/editor swap files, filesystem recovery, permissive file permissions, or accidental commits, making credential compromise plausible.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/encrypt_env.sh (reported line 20)May include surrounding context.

sh
OUT_FILE="$OUT_DIR/proton.env.age"

mkdir -p "$OUT_DIR"
chmod 700 "$OUT_DIR" || true

age -r "$PUBKEY" -o "$OUT_FILE" "$ENV_FILE"

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/send_email.py (reported line 30)May include surrounding context.

python
def decrypt_age(path: str, key_path: str) -> str:
    return subprocess.check_output(["age", "-d", "-i", key_path, path], text=True)


def parse_env(text: str) -> dict:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script decrypts a secrets file and loads SMTP credentials, then uses them to send email, which is a network operation involving user/system data. While the module docstring documents configuration, it does not clearly warn the user that stored credentials will be decrypted and used to transmit email, and there is no confirmation prompt or runtime notice.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.