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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.