Back to skill

Security audit

Exchange2010

Security checks for vulnerabilities and agentic risk

Overview

This Exchange skill mostly matches its stated purpose, but it has serious review-worthy risks around hidden credential defaults, broad mailbox mutation, and unsafe attachment file writes.

Install only if you are comfortable giving this skill delegated Exchange mailbox access and can constrain how agents call it. Require explicit review before sending email, deleting or changing calendar/tasks, downloading attachments, or setting out-of-office replies. Do not use it until the credential variable mismatch and hardcoded default Exchange endpoint are fixed, and restrict attachment downloads to a safe directory with sanitized filenames and cleanup.

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

Error
Location
__init__.py:31
Finding
Undocumented Hardcoded Exchange Endpoint May Receive Credentials## Vulnerability Details **File Location**: `__init__.py`, lines 31–55; duplicated configuration logic at lines 166–181 **Vulnerability Type**: Insecure credential and endpoint configuration **Risk Level**: High ### Vulnerable Code ```python domain = os.getenv('EXCHANGE_DOMAIN', 'friendly-it') username = os.getenv('PICARD_USERNAME', 'picard') email = os.getenv('EXCHANGE_EMAIL', 'picard@friendly-it.com') password = os.getenv('PICARD_PASSWORD') server = os.getenv('EXCHANGE_SERVER', 'oberau.friendly-it.at') if not password: raise ValueError("EXCHANGE_PASSWORD not found in .env.credentials") creds = Credentials(username=f"{domain}\\{username}", password=password) config = Configuration( server=server, credentials=creds, auth_type=NTLM, version=Version(EXCHANGE_2010_SP2) ) return Account( primary_smtp_address=email, config=config, autodiscover=False, access_type=DELEGATE ) ``` ### Technical Analysis The implementation silently defaults to the organization-specific Exchange endpoint `oberau.friendly-it.at` and identity values such as `friendly-it`, `picard`, and `picard@friendly-it.com`. These defaults are not disclosed in `SKILL.md`. The documented credential variable is `EXCHANGE_PASSWORD`, but the implementation reads `PICARD_PASSWORD`. This inconsistency can cause credentials inherited from the environment or supplied under the implementation-specific variable to be used with the hardcoded endpoint when `EXCHANGE_SERVER` is absent. Because NTLM authentication is configured with the resulting credentials, invoking any public operation that calls `get_account()` can initiate authentication against that endpoint. The same unsafe configuration pattern is repeated in `get_shared_calendar()`. ### Attack Path 1. A user or execution environment supplies `PICARD_PASSWORD`, intentionally or through an inherited environment variable. 2. `EXCHANGE_SERVER` is absent because the user follows incomplete configuration, re ...[truncated 1035 chars]
Remediation
## Remediation Suggestions - Remove all organization-specific default values for the server, domain, username, and email address. - Require explicit `EXCHANGE_SERVER`, `EXCHANGE_DOMAIN`, `EXCHANGE_USERNAME`, `EXCHANGE_EMAIL`, and `EXCHANGE_PASSWORD` settings. - Align implementation variable names with those documented in `SKILL.md`. - Fail closed with a clear configuration error if any mandatory setting is absent. - Validate the configured Exchange server against an administrator-controlled allowlist where feasible. - Consolidate Exchange configuration in one function so `get_account()` and `get_shared_calendar()` cannot diverge. - Avoid placing credentials into global process environment state when a scoped configuration object can be used. - Add tests confirming that missing endpoint configuration never triggers a network connection.

T09 · Insecure Skill Coding Practices

Error
Location
__init__.py:719
Finding
Path Traversal Through Untrusted Email Attachment Names## Vulnerability Details **File Location**: `__init__.py`, lines 719–724 **Vulnerability Type**: Arbitrary file write through path traversal **Risk Level**: High ### Vulnerable Code ```python if download_path and hasattr(attachment, 'content'): import os os.makedirs(download_path, exist_ok=True) file_path = os.path.join(download_path, attachment.name) with open(file_path, 'wb') as f: f.write(attachment.content) att_info['downloaded_path'] = file_path ``` ### Technical Analysis The attachment name originates from an email and must therefore be treated as attacker-controlled input. It is joined directly to the caller-provided download directory without normalization or containment validation. A filename containing traversal components such as `../../target` can escape the requested directory. On platforms where absolute attachment names are accepted, `os.path.join()` can also discard `download_path` entirely. The subsequent `open(..., 'wb')` call creates or truncates the resolved file. There is no check that the canonical destination remains under `download_path`, no rejection of path separators, and no protection against overwriting existing files. ### Attack Path 1. An attacker sends an email containing an attachment whose name includes directory traversal components or an absolute path. 2. The email reaches the Exchange inbox accessible to the Skill. 3. A user or automated workflow calls `get_email_attachments(email_id, download_path)`. 4. The Skill combines `download_path` with the attacker-controlled attachment name. 5. Path resolution escapes the intended attachment directory. 6. The attachment bytes are written to the resulting location, truncating an existing file if one is present. 7. If the overwritten location is later interpreted as configuration, source code, a startup file, or another executable resource, additional compromise may occur. ### Impact Assessment The attacker can potentially create or o ...[truncated 540 chars]
Remediation
## Remediation Suggestions - Reduce attachment names to safe leaf names with `os.path.basename()` and reject names that change during sanitization. - Reject absolute paths, `.` and `..` components, directory separators, NUL characters, and platform-specific alternate separators. - Resolve both the destination directory and candidate file with `pathlib.Path.resolve()`, then verify the candidate is strictly contained within the destination. - Generate a server-side filename rather than trusting the attachment name; retain the original name only as metadata. - Use exclusive creation mode such as `xb` or an atomic no-clobber strategy to prevent overwriting existing files. - Apply restrictive file permissions and download into a dedicated non-executable directory. - Add tests for absolute paths, nested traversal, mixed separators, encoded separators, Unicode edge cases, and symlink-based escapes.

T09 · Insecure Skill Coding Practices

Warning
Location
__init__.py:560
Finding
Sensitive Attachments Persist in Undeleted Temporary Files## Vulnerability Details **File Location**: `__init__.py`, lines 560–579 **Vulnerability Type**: Insecure temporary-file lifecycle and sensitive data retention **Risk Level**: Medium ### Vulnerable Code ```python with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(attachment.name)[1]) as tmp: tmp.write(attachment.content) tmp_path = tmp.name result['temp_path'] = tmp_path # Try to extract text content based on file type content_type = (attachment.content_type or '').lower() if 'pdf' in content_type or attachment.name.endswith('.pdf'): result['extracted_text'] = _extract_pdf_text(tmp_path) elif 'text' in content_type or any(attachment.name.endswith(ext) for ext in ['.txt', '.csv', '.json']): try: with open(tmp_path, 'r', encoding='utf-8', errors='ignore') as f: result['extracted_text'] = f.read() except Exception as e: result['extraction_error'] = str(e) elif 'image' in content_type: result['note'] = 'Image attachment - use vision model for OCR' ``` ### Technical Analysis `process_attachment_content()` stores complete email attachments in files created with `delete=False`. The function never removes these files after extraction, including on successful processing and error paths. It also returns each temporary path to the caller. Although `NamedTemporaryFile` normally creates files with restrictive permissions, the attachment remains available to later processes operating under the same account, privileged local users, backup systems, diagnostic collection, or any component that receives the returned path. Repeated processing can also cause unbounded accumulation of sensitive data and disk consumption. ### Attack Path 1. A sensitive attachment exists in an Exchange inbox. 2. A user or workflow invokes `process_attachment_content()` for that email. 3. The function writes the complete attachment to an operating-system temporary directory with `delete=False`. 4. Text extract ...[truncated 977 chars]
Remediation
## Remediation Suggestions - Delete every temporary file in a `finally` block immediately after extraction. - Prefer in-memory processing with `io.BytesIO` when supported by the PDF or text parser. - Do not return temporary filesystem paths unless persistent output is explicitly requested. - If a persistent copy is necessary, require an explicit destination and retention policy. - Preserve restrictive permissions and ensure the temporary directory is not shared more broadly than necessary. - Enforce attachment-size and aggregate-processing limits to prevent disk and memory exhaustion. - Add tests verifying cleanup after successful extraction, parser exceptions, unsupported formats, and interrupted processing.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The module exposes a very broad set of Exchange capabilities including reading mail, sending mail, accessing shared calendars, modifying events, deleting items, resolving names, processing attachments, and changing out-of-office settings. With no documented scope restriction or purpose limitation, this creates an over-privileged integration surface that could be abused by an agent or prompt-influenced workflow to access or modify sensitive mailbox data far beyond a minimal business need.

Missing User Warnings

High
Confidence
98% confidence
Finding
The email-sending function can transmit arbitrary content to arbitrary recipients immediately, with no confirmation, recipient restrictions, or content review. In an agent-integrated setting this is highly dangerous because prompt injection, data exfiltration, or mistaken automation could send sensitive information externally from a trusted corporate mailbox.

Missing User Warnings

High
Confidence
98% confidence
Finding
The function deletes calendar events based solely on an ID and returns after the first match, without user confirmation or additional validation. This enables destructive modification of mailbox data and could be triggered by an agent mistake or malicious prompt, leading to loss of schedule integrity and business disruption.

Missing User Warnings

High
Confidence
97% confidence
Finding
The task deletion function performs an irreversible mailbox write operation with no confirmation or safety checks. In an automated agent workflow, this can cause silent loss of to-do items and undermine user trust and operational tracking.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation advertises destructive capabilities such as sending mail, marking messages as read, updating and deleting calendar events, deleting tasks, and changing out-of-office settings without any safety warnings, confirmation guidance, or scope limitations. In an agent skill context, this increases the chance of unintended modification of enterprise communications and scheduling data because users or downstream agents may treat these operations as routine and low-risk.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill documentation highlights access to shared calendars, shared mailboxes, global address list data, and attachment text extraction, but does not warn about privacy, confidentiality, or authorization requirements. In practice, this can normalize broad access to other users' communications and files, increasing the risk of accidental over-collection, mishandling of sensitive data, or unauthorized use in enterprise environments.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill loads secrets from a local .env.credentials file and environment variables, then injects them into process environment state for later authentication. This pattern broadens secret exposure within the runtime and bypasses more controlled secret-management mechanisms, increasing the chance of accidental leakage, misuse by other components, or unauthorized mailbox access if the host is compromised.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The function creates calendar events directly on the Exchange server without any confirmation, preview, or policy check. In an agent context, prompt injection or user misunderstanding could cause unauthorized event creation, calendar spam, or silent manipulation of a user's schedule.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The docstring for search_contacts says it searches 'the global address list (GAL) or local contacts', but the function only queries account.contacts.filter(...) and never performs a GAL lookup. That is an active contradiction between the documented intent and the implemented behavior.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The attachment-processing routine writes untrusted email attachment content to local temporary files and exposes the temp path in results, without user disclosure or cleanup. This increases local data exposure, may leave sensitive artifacts on disk, and creates a bridge from untrusted remote content into the local filesystem where downstream tooling may further process it unsafely.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The attachment download function writes attacker-controlled email attachment names and contents to an arbitrary caller-supplied path without warning or strong path validation. This can overwrite files within writable locations, leak sensitive attachments to disk, or facilitate unsafe handling of untrusted content by other local processes.

Static analysis

No suspicious patterns detected.