Back to skill

Security audit

outlook-microsoft

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to perform the advertised Outlook mail and calendar tasks, but it keeps powerful Microsoft access credentials in local plaintext files without enforcing private permissions.

Install only if you trust this skill with your mailbox and calendar, including the ability to read, send, modify, and delete content. Use a dedicated least-privilege app registration where possible, avoid providing a client secret for the device-code flow unless truly required, protect or replace ~/.outlook-microsoft with a secure credential store, and revoke the Microsoft app consent if you stop using it.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/outlook_auth.py:46
Finding
OAuth Tokens and Client Secret Are Stored Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/outlook_auth.py:46-68`, `scripts/outlook_auth.py:188-203` **Vulnerability Type**: Plaintext credential storage with process-default filesystem permissions **Risk Level**: High ### Vulnerable Code ```python def save_config(config): CONFIG_DIR.mkdir(parents=True, exist_ok=True) with open(CONFIG_FILE, "w") as f: json.dump(config, f, indent=2) def load_credentials(): if not CREDS_FILE.exists(): return None with open(CREDS_FILE, "r") as f: return json.load(f) def save_credentials(creds): CONFIG_DIR.mkdir(parents=True, exist_ok=True) with open(CREDS_FILE, "w") as f: json.dump(creds, f, indent=2) ``` The persisted data includes bearer tokens, refresh tokens, and an optional client secret: ```python creds = { "access_token": access_token, "refresh_token": refresh_token, "expires_at": time.time() + expires_in_token - 60, "token_type": token_resp.get("token_type", "Bearer") } save_credentials(creds) config = { "client_id": client_id, "tenant_id": tenant_id, "client_secret": client_secret } save_config(config) ``` ### Technical Analysis The Skill writes `~/.outlook-microsoft/credentials.json` and `~/.outlook-microsoft/config.json` using ordinary `open(..., "w")` calls. It does not explicitly create the configuration directory with mode `0700` or the credential files with mode `0600`. Consequently, the resulting permissions depend on the user's current umask and any pre-existing directory or file permissions. In an environment with a permissive umask, shared home directory, container volume, backup process, or multi-user host, another local principal may be able to read the stored credentials. The data is stored as unencrypted JSON and includes: - A Microsoft Graph access token. - An `offline_access` refresh token. - An optional application client secret. - Tenant and client identifiers. Persisting the client se ...[truncated 1830 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the credential directory with owner-only permissions: ```python CONFIG_DIR.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(CONFIG_DIR, 0o700) ``` 2. Create or replace credential files atomically with mode `0600`. For example, use `os.open` with explicit permissions and write through a temporary owner-only file before an atomic rename: ```python fd = os.open( CREDS_FILE, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(creds, f, indent=2) os.chmod(CREDS_FILE, 0o600) ``` 3. Apply the same protection to `config.json`, and validate existing permissions before reading either file. Refuse insecure files or repair their permissions with an explicit warning. 4. Do not request, accept, or persist `OUTLOOK_CLIENT_SECRET` when operating as a public client with the device-code flow. Remove it from `.env`, setup instructions, configuration persistence, and refresh requests unless a separate confidential-client flow is intentionally implemented. 5. Prefer an operating-system credential store or secret-management service for refresh tokens rather than plaintext JSON. 6. Add clear token-revocation and logout functionality. Deleting the local credential file alone does not revoke a refresh token already copied by an attacker. 7. Avoid including token responses in diagnostic output, logs, exceptions, or backups. ]]>

T08 · Insecure Dependencies

Warning
Location
references/setup.md:134
Finding
Dependency Installation Uses an Unpinned Package Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `references/setup.md:134-138` **Vulnerability Type**: Mutable third-party dependency installation without version or hash verification **Risk Level**: Medium ### Vulnerable Instructions ```bash pip install requests # or pip3 install requests ``` ### Technical Analysis The setup documentation instructs users to install `requests` without specifying a reviewed version, dependency lock file, package hashes, trusted package index, or isolated virtual environment. The installed package and its transitive dependencies are therefore resolved at installation time according to the user's current pip configuration. The effective runtime components can change after this Skill has been audited. Risks include: - A compromised future package or transitive dependency release. - A malicious or misconfigured package index or mirror. - User-level pip configuration redirecting resolution to an untrusted source. - Dependency changes that introduce exploitable behavior or compatibility failures. - Installation into the global Python environment, potentially affecting other applications. No malicious package name or currently compromised dependency was identified. The issue is the lack of deterministic and integrity-verified dependency resolution. ### Attack Path 1. A user follows the documented setup command. 2. Pip contacts the package index or mirror configured in the user's environment. 3. Pip resolves the latest compatible `requests` release and its transitive dependencies rather than a version set reviewed with the Skill. 4. If the configured index, a future release, or a transitive dependency is compromised, malicious package content is downloaded. 5. The package executes installation behavior or malicious runtime code when imported by the Outlook scripts. 6. The code runs with the privileges of the user invoking pip or the Skill and may access locally stored Outlook credentials. ### Impact Assessment A compro ...[truncated 709 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Provide a reviewed dependency lock file containing exact versions and cryptographic hashes. 2. Require hash verification during installation: ```bash python3 -m venv .venv . .venv/bin/activate python3 -m pip install --require-hashes -r requirements.txt ``` 3. A locked requirements entry should pin both direct and transitive dependencies and include approved hashes, for example: ```text requests==<reviewed-version> \ --hash=sha256:<approved-hash> ``` 4. Regenerate and review the lock file through a controlled dependency-update process. Do not silently track the latest package release. 5. Document the expected trusted package index and discourage installation through unknown mirrors. 6. Install dependencies in a dedicated virtual environment rather than the global interpreter or a shared user package directory. 7. Add automated dependency vulnerability and provenance scanning to the release process. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (48)

Tainted flow: 'device_code_url' from os.getenv (line 99, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}, ensure_ascii=False))

    try:
        resp = requests.post(device_code_url, data=device_payload, timeout=30)
        resp.raise_for_status()
        device_data = resp.json()
    except requests.exceptions.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'token_url' from os.getenv (line 268, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}

        try:
            resp = requests.post(token_url, data=token_payload, timeout=30)
            resp.raise_for_status()
            token_resp = resp.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'token_url' from os.getenv (line 268, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}

        try:
            resp = requests.post(token_url, data=token_payload, timeout=30)
            resp.raise_for_status()
            token_resp = resp.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The documented behavior claims broad Outlook mail and calendar management, but the analyzed skill description primarily evidences OAuth authorization, token refresh, local credential storage, and a connection test. This mismatch is security-relevant because users and orchestrators may grant trust or permissions based on an inaccurate description, enabling credential handling or persistence that was not clearly scoped.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior claims broad Outlook mail and calendar management, but the analyzed skill description primarily evidences OAuth authorization, token refresh, local credential storage, and a connection test. This mismatch is security-relevant because users and orchestrators may grant trust or permissions based on an inaccurate description, enabling credential handling or persistence that was not clearly scoped.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented behavior claims broad Outlook mail and calendar management, but the analyzed skill description primarily evidences OAuth authorization, token refresh, local credential storage, and a connection test. This mismatch is security-relevant because users and orchestrators may grant trust or permissions based on an inaccurate description, enabling credential handling or persistence that was not clearly scoped.

Credential Access

High
Category
Privilege Escalation
Content
- 配置文件:`~/.outlook-microsoft/`
  - `config.json` - Client ID、Secret、Tenant ID
  - `credentials.json` - OAuth Token(自动管理)
- 安装指南:[references/setup.md](references/setup.md)
Confidence
97% confidence
Finding
Documenting persistent storage of OAuth tokens in credentials.json indicates credential material is retained locally, creating a direct credential-access target. In the context of an Outlook/Graph skill with Mail.ReadWrite, Mail.Send, and Calendars.ReadWrite, token theft could allow mailbox access, message sending, and calendar manipulation.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
如果需要重新授权,删除 credentials 文件后重新授权:

```bash
rm ~/.outlook-microsoft/credentials.json
PYTHONIOENCODING=utf-8 python3 /path/to/outlook_auth.py authorize
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR = Path(__file__).parent
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR = Path(__file__).parent
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR = Path(__file__).parent
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR = Path(__file__).parent
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR = Path(__file__).parent
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"


def load_env():
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"


def load_env():
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
CONFIG_DIR = Path.home() / ".outlook-microsoft"
CONFIG_FILE = CONFIG_DIR / "config.json"
CREDS_FILE = CONFIG_DIR / "credentials.json"
ENV_FILE = SCRIPT_DIR / ".env"


def load_env():
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """从 .env 文件加载环境变量"""
    if not ENV_FILE.exists():
        return
    with open(ENV_FILE, "r") as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """从 .env 文件加载环境变量"""
    if not ENV_FILE.exists():
        return
    with open(ENV_FILE, "r") as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """从 .env 文件加载环境变量"""
    if not ENV_FILE.exists():
        return
    with open(ENV_FILE, "r") as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """从 .env 文件加载环境变量"""
    if not ENV_FILE.exists():
        return
    with open(ENV_FILE, "r") as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """从 .env 文件加载环境变量"""
    if not ENV_FILE.exists():
        return
    with open(ENV_FILE, "r") as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """从 .env 文件加载环境变量"""
    if not ENV_FILE.exists():
        return
    with open(ENV_FILE, "r") as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def get_access_token():
    """获取有效的 Access Token"""
    creds = load_credentials()
    if not creds:
        print(json.dumps({
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def get_access_token():
    """获取有效的 Access Token"""
    creds = load_credentials()
    if not creds:
        print(json.dumps({
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def get_access_token():
    """获取有效的 Access Token"""
    creds = load_credentials()
    if not creds:
        print(json.dumps({
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

No suspicious patterns detected.