Back to skill

Security audit

Outlook Pywin32

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Outlook automation tool, but it needs review because it can access sensitive mailbox data and has under-disclosed state-changing behaviors.

Review this carefully before installing on a work or multi-account Outlook profile. Use explicit account settings, avoid calendar-edit unless you have verified the target event and account, and be aware that reading mail with this tool can mark messages as read. Prefer a version that fails closed on unknown accounts and makes write operations opt-in or confirmable.

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

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/outlook_pywin32/calendar.py:19
Finding

Invalid Account Selection Silently Falls Back to the Default Calendar

Content
View full analysis

Vulnerability Details

File Location: scripts/outlook_pywin32/calendar.py:19-30 and scripts/outlook_pywin32/calendar.py:233-244
Vulnerability Type: Fail-open account authorization boundary
Risk Level: Medium

Vulnerable Code

python
if account:
    for acc in namespace.Accounts:
        if acc.SmtpAddress.lower() == account.lower():
            store = acc.DeliveryStore
            calendar_folder = store.GetDefaultFolder(9)
            break
    else:
        # If the specified account is not found, use the default folder
        calendar_folder = namespace.GetDefaultFolder(9)
else:
    calendar_folder = namespace.GetDefaultFolder(9)

The same fallback pattern is used by calendar_edit:

python
if account:
    for acc in namespace.Accounts:
        if acc.SmtpAddress.lower() == account.lower():
            store = acc.DeliveryStore
            calendar_folder = store.GetDefaultFolder(9)
            break
    else:
        # If the specified account is not found, use the default folder
        calendar_folder = namespace.GetDefaultFolder(9)
else:
    calendar_folder = namespace.GetDefaultFolder(9)

Technical Analysis

When a caller explicitly supplies an Outlook account, the application searches the locally configured accounts for a matching SMTP address. If no match is found, both calendar_list and calendar_edit silently select the default calendar instead of rejecting the request.

This is a fail-open account-selection behavior. An explicit account identifier establishes the intended mailbox boundary, so failure to resolve it should terminate the operation. Falling back to another mailbox violates that boundary and is inconsistent with get_mail_folder, which raises an exception when a requested mail account cannot be found.

The edit operation is particularly sensitive because it searches the selected calendar for the first matching subject or start time and then modifies that item. The unintended fallba ...[truncated 1302 chars]

Remediation
View remediation

Remediation Suggestions

  • Fail closed whenever an explicitly requested account cannot be found.
  • Create a shared strict account resolver and use it consistently for mail, calendar, and folder operations.
  • Never substitute the default mailbox when the caller supplied an account.
  • Return a clear error without exposing other configured account data.
  • Require both an immutable event identifier and the intended account for calendar edits where possible.
  • Add tests covering nonexistent accounts, case-insensitive matching, multiple accounts, and delegated stores.

Example hardening:

python
def get_calendar_folder(namespace, account_email=None):
    if not account_email:
        return namespace.GetDefaultFolder(9)

    for account in namespace.Accounts:
        smtp_address = getattr(account, "SmtpAddress", "")
        if smtp_address.lower() == account_email.lower():
            return account.DeliveryStore.GetDefaultFolder(9)

    raise ValueError(f"Outlook account not found: {account_email}")

Both listing and editing functions should call this resolver and terminate if it raises an error.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/outlook_pywin32/mail.py:176
Finding

Mail Read Operation Silently Marks Messages as Read

Content
View full analysis

Vulnerability Details

File Location: scripts/outlook_pywin32/mail.py:176-180
Vulnerability Type: Undocumented mailbox state mutation
Risk Level: Low

Vulnerable Code

python
print(result["body"])

# Mark as read
if hasattr(msg, "UnRead"):
    msg.UnRead = False

return result

Technical Analysis

The mail_read operation is presented as a function for retrieving message content, but it also changes the message's unread state. The mutation occurs automatically and has no dedicated option, confirmation, or documented opt-out.

Read-oriented operations should normally be free of persistent side effects. Automatically changing mailbox state violates least-surprise principles and can interfere with workflows that use unread status as a queue, notification mechanism, or processing marker.

The result already records was_unread, which demonstrates that the original state is available. However, that state is not preserved.

Attack Path

  1. A caller invokes mail-read for an unread message.
  2. The function retrieves and prints the message metadata and body.
  3. The function assigns False to msg.UnRead.
  4. Outlook persists or synchronizes the updated read state.
  5. The message no longer appears unread to the user or to other processes that rely on that status.

Impact Assessment

This issue does not provide additional system or mailbox privileges. Its impact is limited to unauthorized state changes within mailboxes the current Outlook profile can already access.

Potential consequences include:

  • Loss of unread-state tracking.
  • Missed follow-up actions or notifications.
  • Interference with mailbox-processing workflows.
  • Synchronization of the unintended state change to other Outlook clients.
  • Concealment that a message had not previously been reviewed by the user.
Remediation
View remediation

Remediation Suggestions

  • Make mail-read non-mutating by default.
  • Remove the automatic assignment to msg.UnRead.
  • If marking messages as read is required, add an explicit Boolean option such as --mark-read.
  • Clearly document the state-changing behavior.
  • Only save or synchronize the change when the caller explicitly requests it.
  • Add tests confirming that ordinary reads preserve the original unread state.

Example hardening:

python
def mail_read(
    folder: str = "inbox",
    index: int = 1,
    account: str = None,
    mark_read: bool = False,
):
    # Retrieve and display the message.

    if mark_read and hasattr(msg, "UnRead"):
        msg.UnRead = False
        msg.Save()

    return result

T08 · Insecure Dependencies

Note
Location
README.md:50
Finding

PyWin32 Dependency Is Installed Without a Version or Integrity Pin

Content
View full analysis

Vulnerability Details

File Location: README.md:50 and SKILL.md:59
Vulnerability Type: Unpinned third-party dependency
Risk Level: Low

Vulnerable Code

bash
pip install pywin32

Technical Analysis

The documented installation command asks pip to resolve the latest available pywin32 release at installation time. No reviewed version, lock file, package hash, or integrity policy is provided.

The package name is consistent with the legitimate dependency, and the audited repository does not specify a suspicious index or alternate download source. Therefore, there is no evidence that the current dependency is malicious. Nevertheless, the installation is not reproducible and implicitly trusts all future releases selected by the package resolver.

Because pywin32 provides native Windows components and COM access, compromise of the resolved package or its distribution channel could result in code execution with the installing user's privileges.

Attack Path

  1. A user follows the documented installation instructions.
  2. pip queries its configured package index and selects the latest compatible pywin32 release.
  3. The selected package has not been pinned to the version reviewed with this Skill.
  4. If that future release, configured index, or delivery path is compromised, malicious installation or runtime code can execute.
  5. The code executes with the privileges of the user running pip or the Outlook automation tool.

This is a supply-chain hardening weakness rather than evidence of an active malicious dependency.

Impact Assessment

If dependency resolution were compromised, the resulting code could execute with the current user's permissions. Depending on those permissions, it could access local files, interact with Outlook, read mailbox data, or alter the user environment.

The present audit found no evidence of dependency confusion, typosquatting, an unsafe custom repository, or a known malicious pac ...[truncated 108 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin pywin32 to a specifically reviewed version.
  • Record dependencies in a requirements or lock file.
  • Use package hashes and install with --require-hashes where practical.
  • Review and update the pin through a controlled dependency-update process.
  • Document the expected Python and Windows architecture compatibility.
  • Use only a trusted package index and avoid unreviewed extra indexes.

Example:

text
pywin32==<reviewed-version> \
    --hash=sha256:<verified-distribution-hash>

Install it with:

bash
python -m pip install --require-hashes -r requirements.txt
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (15)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

该技能说明全文均以中文提供,未见任何语言选择、英文替代说明或用户可自行选择语言的表述。根据规则,若技能在自然语言层面强制单一语言且未提供用户选择,属于潜在的语言/locale 政策违规。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill explicitly enables local access to Outlook mail, accounts, folders, and calendars, which commonly contain sensitive personal and business data. Without a clear warning in the description that the tool can read potentially sensitive mailbox contents, users may invoke it without informed consent or appropriate safeguards.

Content

No source excerpt is available for this finding.

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

Scanner excerpt · SKILL.md (reported line 89)May include surrounding context.

md
### 日历相关

| 方法            | 说明                    | 参数                                                                                                                                                          |
| ------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| calendar-list | 列出即将举行的日程安排事件         | --limit, --days, --include-today, --account                                                                                                                 |
| calendar-new  | 创建一个日程安排事件            | --subject, --start, --end, --location, --body, --required-attendees, --optional-attendees, --all-day, --reminder, --account                                 |

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

Scanner excerpt · SKILL.md (reported line 91)May include surrounding context.

md
| 方法            | 说明                    | 参数                                                                                                                                                          |
| ------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| calendar-list | 列出即将举行的日程安排事件         | --limit, --days, --include-today, --account                                                                                                                 |
| calendar-new  | 创建一个日程安排事件            | --subject, --start, --end, --location, --body, --required-attendees, --optional-attendees, --all-day, --reminder, --account                                 |
| calendar-edit | 修改一个日程安排事件(仅保存,不发送通知) | --subject, --start, --new-subject, --new-start, --new-end, --location, --body, --required-attendees, --optional-attendees, --all-day, --reminder, --account |

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documented calendar creation and editing commands can modify Outlook data, including creating events and changing existing schedule details. Omitting a warning about write-side effects increases the risk of accidental data modification, scheduling disruption, or silent alteration of business records.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The docstring and user-facing print strings are entirely in Chinese, which imposes a specific language on users without any opt-in or configuration mechanism. Under the language/locale policy, this is a natural-language policy violation unless the locale restriction is explicitly justified or optional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The docstrings and user-facing messages in this file are written only in Chinese, and the skill does not indicate that Chinese is optional or that the tool is intended for a Chinese-only audience. This can violate language/locale policy requirements when users are not given an explicit choice or opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file's docstrings and printed output are consistently written in Chinese, with no mechanism for language selection or indication that the skill is intentionally limited to Chinese-speaking users. This creates a natural-language policy issue because it imposes a locale/language choice on all users without opt-in.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The function is documented and named as a read operation, but it performs a state-changing side effect by setting the message to read. In an email workflow, changing read status can alter auditability, user expectations, and downstream automation, making this a real integrity issue even though it is not remote code execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Automatically marking email as read without warning introduces an unintended state change that may hide unread messages from the user, interfere with triage rules, or signal message access to other clients and workflows. In a mail-management skill, this is especially risky because users may reasonably expect a read/list action to be passive unless explicitly told otherwise.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code includes natural-language docstrings and printed error text exclusively in Chinese, such as the function description and the Outlook connection failure message. The file does not indicate that the skill is intended only for Chinese-speaking users or provide any language/locale opt-in, which can violate language-choice policy.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The module docstring and printed usage/error strings are written exclusively in Chinese, which imposes a specific language on all users of the skill. The file does not offer any language choice, fallback, or documented locale justification, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/outlook-pywin32.py (reported line 81)May include surrounding context.

python
for param_name, param in sig.parameters.items():
        if param_name == "self":
            continue
        value = getattr(args, param_name, None)
        if value is not None:
            # 类型转换
            if param.annotation == int:

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The docstring is written entirely in Chinese, and the function's user-facing output strings are also Chinese, implying a fixed language choice. The file does not offer any user language selection or explain a justified locale restriction, which can violate language/locale policy requirements.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The function prints visible messages in Chinese only, which enforces a specific output language for users regardless of preference. There is no opt-in, fallback, or configuration shown in this file.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.