Back to skill

Security audit

Full access to all Exchange 2010 EWS functions, should work with other EWS Open Source

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its Exchange mailbox-management purpose, but it needs Review because it combines broad mailbox write access with under-disclosed credential defaults and unsafe attachment file handling.

Install only for a tightly scoped, least-privileged Exchange account. Review and fix configuration before use: require explicit Exchange server, username, email, domain, and password variables; remove the hardcoded defaults; protect the credentials file; add confirmations for send/delete/update/OOF actions; and sanitize or restrict all attachment download paths. Avoid processing sensitive attachments until temp-file cleanup and download containment are fixed.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
__init__.py:31
Finding
Undocumented Hardcoded Exchange Server and Account Defaults May Expose Authentication Material## Vulnerability Details **File Location**: `__init__.py`, lines 31-45 and 166-178 **Vulnerability Type**: Unsafe credential configuration and hardcoded external destination **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) ) ``` The shared-calendar connection repeats the unsafe defaults: ```python domain = os.getenv('EXCHANGE_DOMAIN', 'friendly-it') username = os.getenv('PICARD_USERNAME', 'picard') password = os.getenv('PICARD_PASSWORD') server = os.getenv('EXCHANGE_SERVER', 'oberau.friendly-it.at') creds = Credentials(username=f"{domain}\\{username}", password=password) config = Configuration( server=server, credentials=creds, auth_type=NTLM, version=Version(EXCHANGE_2010_SP2) ) ``` ### Technical Analysis The documented setup instructs users to define `EXCHANGE_PASSWORD`, but the implementation reads `PICARD_PASSWORD`. The exception message also incorrectly claims that `EXCHANGE_PASSWORD` is missing. In addition, the implementation silently supplies organization-specific defaults for the Exchange domain, username, email address, and server. Because `autodiscover` is disabled, the configured or default server is used directly. If a `PICARD_PASSWORD` value is present while the documented Exchange variables are missing, the application can attempt NTLM authentication against the hardcoded server `oberau.friendly-it.at` ...[truncated 1525 chars]
Remediation
## Remediation Suggestions - Remove all organization-specific defaults for the server, domain, username, and email address. - Require explicit values for `EXCHANGE_SERVER`, `EXCHANGE_DOMAIN`, `EXCHANGE_USERNAME`, `EXCHANGE_EMAIL`, and `EXCHANGE_PASSWORD`. - Use the same variable names in the implementation, documentation, and error messages. - Fail closed if any mandatory setting is absent. - Validate the Exchange server against an administrator-controlled allowlist. - Require certificate validation and prohibit plaintext or downgraded transport. - Avoid NTLM where modern authentication is available. - Add tests confirming that missing configuration never initiates a connection to a fallback destination.

T09 · Insecure Skill Coding Practices

Error
Location
__init__.py:719
Finding
Email Attachment Filename Allows Directory Traversal and Arbitrary File Overwrite## Vulnerability Details **File Location**: `__init__.py`, lines 719-725 **Vulnerability Type**: Path traversal and arbitrary file write **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 `attachment.name` originates from an email and is therefore sender-controlled. The value is passed directly to `os.path.join()` without stripping directory components, rejecting absolute paths, normalizing the result, or verifying that the final path remains inside `download_path`. A filename containing traversal elements such as `../../target` can escape the intended download directory. On platforms where an absolute second path causes `os.path.join()` to discard the first path, an absolute attachment name may directly select the destination. The use of `wb` truncates an existing file before writing attacker-controlled attachment content. Exploitability depends on whether the Exchange library preserves malicious path components in attachment names and on the filesystem permissions of the process. The code itself establishes no containment boundary. ### Attack Path 1. An attacker sends an email containing an attachment with a crafted name such as `../../application/config.py`. 2. The email arrives in the mailbox inbox. 3. The user or Agent invokes `get_email_attachments(email_id, download_path)` for that message. 4. The function joins the trusted download directory with the untrusted filename. 5. Filesystem path resolution processes the traversal components. 6. The attachment content is written outside the requested directory, truncating the destination if it already exists. 7. If the overwritten file is late ...[truncated 732 chars]
Remediation
## Remediation Suggestions - Treat every attachment filename as untrusted. - Reduce the supplied filename to a safe basename and reject empty, absolute, reserved, or traversal-containing names. - Resolve both the destination directory and candidate path, then verify that the candidate remains below the destination directory. - Generate a server-side filename and retain the original name only as metadata. - Use exclusive creation mode to avoid silently overwriting existing files. - Apply restrictive file permissions and enforce attachment size limits. - Add tests for `../`, absolute paths, mixed path separators, encoded traversal, Unicode edge cases, and name collisions. A safe design should resemble: ```python base = os.path.realpath(download_path) safe_name = os.path.basename(attachment.name) if not safe_name or safe_name != attachment.name: raise ValueError("Unsafe attachment filename") candidate = os.path.realpath(os.path.join(base, safe_name)) if os.path.commonpath([base, candidate]) != base: raise ValueError("Attachment path escapes download directory") with open(candidate, 'xb') as f: f.write(attachment.content) ```

T09 · Insecure Skill Coding Practices

Warning
Location
__init__.py:559
Finding
Sensitive Email Attachments Persist in Unmanaged Temporary Files## Vulnerability Details **File Location**: `__init__.py`, lines 559-578 **Vulnerability Type**: Insecure temporary-file lifecycle and sensitive-data persistence **Risk Level**: Medium ### Vulnerable Code ```python if hasattr(attachment, 'content'): 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 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) ``` ### Technical Analysis The function writes complete email attachments to temporary files with `delete=False`. No `os.unlink()` call, `finally` cleanup, temporary-directory lifecycle, or retention policy removes these files after extraction. The filesystem path is also returned to the caller through `result['temp_path']`, encouraging persistence beyond the extraction operation. Temporary-file creation itself avoids a predictable-name race because `NamedTemporaryFile` securely creates the file. The vulnerability is the unmanaged lifetime of sensitive mailbox data, not temporary-name predictability. ### Attack Path 1. A mailbox contains an attachment with confidential information. 2. The user or Agent invokes `process_attachment_content()`. 3. The function writes the complete attachment to the system temporary directory. 4. Text extraction completes or fails. 5. The function returns without deleting the temporary file. 6. The file remains ...[truncated 790 chars]
Remediation
## Remediation Suggestions - Delete each temporary file in a `finally` block immediately after extraction. - Prefer in-memory parsing through `io.BytesIO` when supported by the PDF or text parser. - If disk-backed processing is required, use a context-managed `TemporaryDirectory`. - Do not return temporary filesystem paths unless persistence is an explicit, documented feature. - Apply restrictive permissions and configure a strict maximum attachment size. - Document and enforce a retention policy for any intentionally persisted attachment. - Add automated tests that confirm temporary files are removed after successful processing and after every exception path.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:162
Finding
Unpinned Runtime Dependency Installation Creates Supply-Chain Risk## Vulnerability Details **File Location**: `SKILL.md`, line 162 **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```text Prerequisite: `pip install PyPDF2` for PDF text extraction ``` ### Technical Analysis The setup instruction installs `PyPDF2` without a reviewed version constraint, lockfile, integrity hash, or approved package index. Consequently, separate installations may retrieve different package releases and transitive dependencies. No evidence was found that `PyPDF2` is currently malicious. The issue is that the installation process trusts whatever release the configured package index resolves at installation time, making the deployed code differ from the audited code and reducing reproducibility. Python package installation can execute build backend or installation-related code. A compromised upstream account, package-index incident, dependency compromise, or unsafe future release could therefore affect the environment before attachment processing occurs. ### Attack Path 1. An administrator follows the documented prerequisite. 2. `pip` resolves the current `PyPDF2` release from the configured package index. 3. The selected artifact is not verified against a project-controlled version and hash. 4. A compromised or unexpectedly changed package release is downloaded. 5. Package-controlled installation or runtime code executes in the installer or application context. 6. The compromised dependency gains access to data and privileges available to that context, including processed mailbox attachments. ### Impact Assessment Impact depends on the privileges used for package installation and Skill execution. A compromised dependency could execute arbitrary Python code, read environment variables and credentials, inspect attachment contents, modify application files, or communicate with external systems. If installation is performed as an administrator, the sco ...[truncated 162 chars]
Remediation
## Remediation Suggestions - Pin `PyPDF2` to a reviewed exact version. - Maintain dependencies in a lockfile rather than relying on an ad hoc installation command. - Require hashes for downloaded artifacts, for example through a hash-locked requirements file. - Use an approved package index or internal artifact mirror. - Install dependencies in a dedicated, least-privileged virtual environment. - Audit dependency updates before changing the lockfile. - Add automated vulnerability and provenance scanning to the release process.
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 (9)

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The setup instructions require storing Exchange server, domain, email address, and password in `.env.credentials` without any privacy or secret-handling warning. This can normalize unsafe credential practices, leading users or agents to expose sensitive enterprise credentials in repos, logs, prompts, or insecure local files.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill advertises capabilities to send email, mark messages as read, create/update/delete calendar events, complete/delete tasks, and set out-of-office replies, but it does not prominently warn that these actions can modify or destroy user data and communicate externally. In an agent setting, missing safety guidance increases the chance of unintended mailbox changes, data loss, or unauthorized communications through overbroad or mistaken tool use.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
This skill exposes broad read/write Exchange capabilities, including sending email, creating/updating/deleting calendar items and tasks, changing Out of Office settings, and writing attachment contents to disk. In an agent skill context, this is dangerous because a compromised or mis-prompted agent could perform impactful actions across a mailbox without meaningful scoping, approval gates, or least-privilege restrictions.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
Calendar events can be deleted immediately by ID with no confirmation, dry-run, or approval step. In an agent environment, this creates a direct destructive action surface where prompt injection, operator error, or misuse can silently remove important meetings from personal or shared calendars.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
Tasks are deleted immediately by ID without any confirmation or safety interlock. This enables accidental or malicious loss of user productivity data, especially when an agent can act autonomously or on ambiguous natural-language instructions.

Intent-Code Divergence

Low
Confidence
86% confidence
Finding
The function advertises event lookup within a selected calendar, but actually performs an account-wide fetch by ID and ignores the chosen calendar scope. This can bypass expected resource scoping and may return details for items outside the intended shared calendar, undermining access boundaries assumed by callers or higher-level policy controls.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The docstring at L490 says the function searches 'the global address list (GAL) or local contacts', but the code only queries account.contacts.filter(...) and never performs a GAL lookup. That is an active contradiction about what directory sources are searched.

Missing User Warnings

Low
Confidence
90% confidence
Finding
Attachment content is written to temporary files on disk without clear disclosure, path lifecycle controls, or cleanup. This can leave sensitive email attachments resident on disk longer than intended, increasing exposure to other local processes, forensic recovery, or accidental reuse, especially since the temp path is returned to callers.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The function can download email attachments directly to an arbitrary provided path with no confirmation or output restrictions. In an agent setting, this creates a straightforward data-exfiltration and local persistence mechanism for potentially sensitive mailbox contents.

Static analysis

No suspicious patterns detected.