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

Missing User Warnings

Medium
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.

Whitespace Padding

Medium
Category
Prompt Injection
Content
### 日历相关

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

Whitespace Padding

Medium
Category
Prompt Injection
Content
| 方法            | 说明                    | 参数                                                                                                                                                          |
| ------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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 |
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.

Missing User Warnings

Medium
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.

Natural-Language Policy Violations

Medium
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.

Natural-Language Policy Violations

Medium
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.

Natural-Language Policy Violations

Medium
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.

Intent-Code Divergence

Medium
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.

Missing User Warnings

Medium
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.

Natural-Language Policy Violations

Medium
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.

Natural-Language Policy Violations

Low
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.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
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:
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Natural-Language Policy Violations

Low
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.

Natural-Language Policy Violations

Low
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.

Static analysis

No suspicious patterns detected.