Back to skill

Security audit

Fastmail Jmap.Disabled

Security checks for vulnerabilities and agentic risk

Overview

This Fastmail skill mostly does what it claims, but it also ships an undocumented contacts tool and allows sensitive email/contact actions with limited built-in safeguards.

Review carefully before installing. Use a least-privilege Fastmail token, avoid granting Contacts scope unless you intentionally want contact access, and require explicit human approval before send, move, trash, or bulk contact commands. Be aware that command output may place email or contact data into agent logs or transcripts.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/contacts.py:92
Finding
Undocumented Bulk Access to Sensitive Fastmail Contact Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/contacts.py`, lines 92–106 **Vulnerability Type**: Undeclared sensitive-data access that exceeds the Skill's documented email functionality **Risk Level**: Medium ### Vulnerable Code ```python def cmd_search(query): # Fetch all and filter client-side (JMAP Contacts search support varies) resp = _call([ ["ContactCard/query", {"accountId": None, "limit": 500}, "0"], ["ContactCard/get", {"accountId": None, "#ids": {"resultOf": "0", "name": "ContactCard/query", "path": "/ids"}, "properties": ["name", "emails", "phones", "organizations"]}, "1"] ]) contacts = resp["methodResponses"][1][1]["list"] q = query.lower() results = [] for c in contacts: full, emails, phones, org = _format_contact(c) haystack = " ".join([full, org] + emails + phones).lower() ``` The same utility can retrieve additional sensitive fields, including addresses and notes, when a specific contact is requested: ```python for v in c.get("addresses", {}).values(): parts = [v.get("street",""), v.get("locality",""), v.get("region",""), v.get("country","")] addr = ", ".join(p for p in parts if p) if addr: print(f"Addr: {addr}") if c.get("notes"): for v in c["notes"].values(): print(f"Notes: {v.get('note','')}") ``` ### Technical Analysis The declared Skill functionality in `SKILL.md` is Fastmail email management: reading, searching, sending, moving, marking, and trashing email. Its documented token scopes are Email and Email Submission. However, the package also contains `scripts/contacts.py`, which requires Contacts scope and can access names, email addresses, telephone numbers, organizations, postal addresses, and free-form contact notes. The contacts capability is not documented in the Skill's command list or its file inventory. In particular, `cmd_search` does not perform a narrow ...[truncated 2323 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `scripts/contacts.py` from this email-only Skill if contact access is not required for its declared functionality. 2. If contact support is intentional, document it prominently in `SKILL.md`, including: - All contacts commands - The categories of personal data retrieved - The separate Contacts scope requirement - The fact that search may currently retrieve hundreds of records 3. Require a separately issued Contacts-only token rather than encouraging one token with Email, Email Submission, and Contacts permissions. 4. Use Fastmail-supported server-side filtering where available instead of downloading up to 500 complete contact records. 5. Request only fields needed for the active operation. For example, an email-address lookup should not retrieve telephone numbers, organizations, addresses, or notes. 6. Add explicit user confirmation before bulk listing or searching contacts when commands are invoked by an autonomous agent. 7. Provide structured output modes that redact sensitive values by default and expose complete contact data only through an explicit flag. 8. Warn users that command output may enter agent context or logs and recommend appropriate retention and access controls. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Tainted flow: 'req' from os.environ.get (line 40, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
return
    headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
    req = urllib.request.Request("https://api.fastmail.com/jmap/session", headers=headers)
    session = json.loads(urllib.request.urlopen(req).read())
    ACCOUNT = list(session["accounts"].keys())[0]
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 40, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
    body = json.dumps({"using": USING, "methodCalls": method_calls}).encode()
    req = urllib.request.Request("https://api.fastmail.com/jmap/api/", body, headers, method="POST")
    resp = json.loads(urllib.request.urlopen(req).read())
    for mr in resp.get("methodResponses", []):
        if mr[0] == "error":
            print(f"Error: {mr[1].get('type')} — {mr[1].get('description', '')}", file=sys.stderr)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 52, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
return
    headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
    req = urllib.request.Request("https://api.fastmail.com/jmap/session", headers=headers)
    session = json.loads(urllib.request.urlopen(req).read())
    ACCOUNT = list(session["accounts"].keys())[0]
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 52, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
    body = json.dumps({"using": using or USING, "methodCalls": method_calls}).encode()
    req = urllib.request.Request(API, body, headers, method="POST")
    resp = json.loads(urllib.request.urlopen(req).read())
    # Check for errors
    for mr in resp.get("methodResponses", []):
        if mr[0] == "error":
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description says the skill provides email superpowers via Fastmail JMAP, specifically read/search/send/move/trash mail. However, the supplied code only interacts with Fastmail contacts using JMAP Contacts methods (ContactCard/query and ContactCard/get). It requires a token with Contacts scope, fetches the JMAP session, and supports list/search/get for contacts. There is no code for reading email messages, searching mail, sending mail, moving mail between mailboxes, or trashing messages. This is a material description-behavior mismatch because the primary purpose and accessed resource are contacts, not email.

Credential Access

High
Category
Privilege Escalation
Content
}
```

Or use 1Password injection: `op run --env-file=.env -- python3 scripts/fastmail.py unread`

## How It Works
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Ae1

High
Category
analysis-evasion
Content
- `SKILL.md` — this file
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares access to environment variables and networked email operations but does not define an explicit tool/permission scope. That makes the skill harder to sandbox and review, and increases the chance an agent can invoke sensitive capabilities beyond what a user expects. In an email skill, unclear scoping is especially risky because it combines secret handling (API tokens) with read/write access to private communications.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill advertises full email read/write capability, including destructive actions like move and trash, without a prominent privacy and safety warning. Users may not appreciate that granting the token enables access to all mailbox contents and allows irreversible or hard-to-audit actions on sensitive communications. For an agent skill, this can lead to overbroad delegation and accidental data exposure or mailbox tampering.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Sending email
- `python3 scripts/fastmail.py send "user@example.com" "Subject" "Body text"`
- Always ask before sending. Never send without approval.
```

### In heartbeat/cron:
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
This code makes authenticated network requests to Fastmail and sends user-supplied queries plus retrieves contact records, but the skill provides no explicit user-facing warning that contact data will be transmitted to an external service. The module docstring notes the required token, but it does not disclose the privacy impact of network access to personal contact information.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Get one at: https://app.fastmail.com/settings/security/tokens", file=sys.stderr)
    sys.exit(1)

API = "https://api.fastmail.com/jmap/api/"
USING = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"]
USING_SEND = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"]
ACCOUNT = None
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Get one at: https://app.fastmail.com/settings/security/tokens", file=sys.stderr)
    sys.exit(1)

API = "https://api.fastmail.com/jmap/api/"
USING = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"]
USING_SEND = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"]
ACCOUNT = None
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Get one at: https://app.fastmail.com/settings/security/tokens", file=sys.stderr)
    sys.exit(1)

API = "https://api.fastmail.com/jmap/api/"
USING = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"]
USING_SEND = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"]
ACCOUNT = None
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Get one at: https://app.fastmail.com/settings/security/tokens", file=sys.stderr)
    sys.exit(1)

API = "https://api.fastmail.com/jmap/api/"
USING = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"]
USING_SEND = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"]
ACCOUNT = None
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
Move and trash operations change mailbox state immediately without confirmation, undo guidance, or safety interlocks. In an autonomous-agent setting this can lead to accidental message loss, hiding important emails, or destructive actions triggered by prompt injection or misclassification of user intent.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The send command can transmit arbitrary email contents immediately with no confirmation prompt, dry-run mode, or policy check. In an agent context, this increases the risk of unintended outbound communication, data leakage, or social-engineering messages being sent if the tool is invoked from ambiguous or prompt-injected instructions.

Static analysis

No suspicious patterns detected.