Back to skill

Security audit

Outlook Delegate

Security checks for vulnerabilities and agentic risk

Overview

The skill largely does what it says for Outlook delegation, but it requests and persists broad Microsoft Graph authority and exposes tokens in ways users should review before installing.

Install only if you intentionally want an AI assistant to have delegated access to read, modify, delete, and send Outlook mail and edit calendars. Before use, reduce Microsoft Graph scopes where possible, avoid granting both SendAs and SendOnBehalf, protect ~/.outlook-mcp from backups or sync, do not run the token-printing command in logged environments, and verify item IDs carefully before delete, move, update, or send-draft actions.

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

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/outlook-token.sh:44
Finding

OAuth Scope Requests Permissions Beyond Implemented Functionality

Content
View full analysis

Vulnerability Details

File Location: scripts/outlook-token.sh:44-46
Additional Locations: SKILL.md:84-93, references/setup.md:42-51, references/setup.md:188-194
Vulnerability Type: Excessive Microsoft Graph delegated permissions
Risk Level: Medium

Vulnerable Code

bash
# Token endpoint (tenant-specific, not /common)
TOKEN_URL="https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token"

SCOPE="offline_access User.Read Mail.ReadWrite Mail.Send Mail.ReadWrite.Shared Mail.Send.Shared Calendars.ReadWrite Calendars.ReadWrite.Shared"

The same excessive permission set is documented during setup:

markdown
**Basic permissions:**
- `Mail.ReadWrite` — Read/write assistant's mail
- `Mail.Send` — Send as assistant
- `Calendars.ReadWrite` — Calendar access
- `User.Read` — Read profile
- `offline_access` — Refresh tokens

**Delegate permissions:**
- `Mail.ReadWrite.Shared` — Read/write owner's mail
- `Mail.Send.Shared` — Send as/on behalf of owner
- `Calendars.ReadWrite.Shared` — Owner's calendar

Technical Analysis

The Skill's implemented mail-reading and mail-management operations target the owner's shared mailbox through /users/{owner}. Its only implemented operation involving the delegate's mailbox is sending mail through /users/{delegate}/sendMail.

Despite this, the OAuth scope always requests:

  • Mail.ReadWrite, which permits reading and modifying the delegate's own mailbox.
  • Calendars.ReadWrite, which permits reading and modifying the delegate's own calendars.

These permissions are not required by the audited implementation. Shared-resource functionality is already represented by Mail.ReadWrite.Shared, Mail.Send.Shared, and Calendars.ReadWrite.Shared. Sending as the delegate requires Mail.Send, but it does not require full read/write access to the delegate's mailbox.

The fixed scope is used during initial authorization and token refresh, so the unnecessary privileges remain associated with r ...[truncated 1360 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove Mail.ReadWrite unless the Skill explicitly implements and documents reading or managing the delegate's own mailbox.
  2. Remove Calendars.ReadWrite unless the Skill explicitly implements and documents management of the delegate's own calendar.
  3. Retain only the scopes required by enabled features:
    • User.Read
    • Mail.Send for sending as the delegate
    • Mail.ReadWrite.Shared for managing the owner's shared mailbox
    • Mail.Send.Shared for delegated sending
    • Calendars.ReadWrite.Shared for managing the owner's shared calendar
    • offline_access only if persistent refresh is required
  4. Build the authorization scope dynamically from explicitly enabled features rather than using one unconditional scope.
  5. Update both SKILL.md and references/setup.md to explain why each permission is required.
  6. Revoke the existing application consent and reauthorize after reducing the scope, because editing the script alone does not remove permissions from previously issued grants.
  7. Consider separate application registrations or authorization profiles for read-only, mail-management, calendar-management, and delegated-send use cases.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/outlook-token.sh:20
Finding

Credential Handling Exposes OAuth Secrets Through Non-Atomic Files and Command Interfaces

Content
View full analysis

Vulnerability Details

File Location: scripts/outlook-token.sh:20-25
Additional Locations: scripts/outlook-token.sh:153-158, scripts/outlook-token.sh:214-240, references/setup.md:198-215
Vulnerability Type: Insecure OAuth credential handling
Risk Level: Medium

Vulnerable Code

The credential file is created through shell redirection before its permissions are restricted:

bash
# Secure write to credentials file
write_credentials() {
    local CONTENT="$1"
    ensure_config_dir
    echo "$CONTENT" > "$CREDS_FILE"
    chmod 600 "$CREDS_FILE"
}

The setup documentation uses the same creation pattern:

bash
# Use stdin to avoid exposing secrets in process list
printf 'client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&grant_type=authorization_code&scope=%s' \
  "$CLIENT_ID" "$CLIENT_SECRET" "$CODE" "$REDIRECT" "$SCOPE" | \
  curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
  --data @- > ~/.outlook-mcp/credentials.json

chmod 600 ~/.outlook-mcp/credentials.json

# Verify
cat ~/.outlook-mcp/credentials.json | jq '{status: "authorized", expires_in, scope}'

The authorization code is accepted as a command-line argument:

bash
exchange)
    # Exchange authorization code for tokens
    CODE="$2"

    if [ -z "$CODE" ]; then
        echo "Usage: outlook-token.sh exchange <authorization-code>"
        echo ""
        echo "Get the code by running: outlook-token.sh auth-url"
        exit 1
    fi

    if [ -z "$CLIENT_ID" ] || [ "$CLIENT_ID" = "null" ]; then
        echo '{"error": "No client_id in config.json"}'
        exit 1
    fi

    if [ -z "$CLIENT_SECRET" ] || [ "$CLIENT_SECRET" = "null" ]; then
        echo '{"error": "No client_secret in config.json"}'
        exit 1
    fi

    REDIRECT="http://localhost:8400/callback"

    # Exchange code using tenant-specific endpoint
    # POST data sent via stdin to avoid exposing secrets in process list
    RESPONSE=$(printf 'client_id=%s
...[truncated 4122 chars]
Remediation
View remediation

Remediation Suggestions

  1. Set a restrictive umask near the beginning of every script that handles secrets:
bash
umask 077
  1. Write credentials atomically through a protected temporary file:
bash
write_credentials() {
    local content="$1"
    local temp_file

    ensure_config_dir
    temp_file=$(mktemp "$CONFIG_DIR/credentials.json.XXXXXX") || return 1
    chmod 600 "$temp_file" || {
        rm -f "$temp_file"
        return 1
    }

    printf '%s\n' "$content" > "$temp_file" || {
        rm -f "$temp_file"
        return 1
    }

    mv -f "$temp_file" "$CREDS_FILE"
}
  1. Update the setup guide to use umask 077 and a protected temporary file rather than redirecting the token response directly to its final path.
  2. Read authorization codes from protected standard input instead of command-line arguments. For interactive use, disable terminal echo while reading:
bash
IFS= read -r -s -p "Authorization code: " CODE
printf '\n'
  1. Remove the get operation if external token retrieval is not essential.
  2. If token retrieval is required, require an explicit opt-in, warn that the value is sensitive, and avoid returning it through ordinary agent or CI output channels.
  3. Prefer having mail and calendar scripts consume the token internally rather than exposing it to callers.
  4. Avoid placing token-returning commands in examples likely to be run through logged automation.
  5. Rotate or revoke credentials if token output or files may already have been captured.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (124)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The description claims a broader Outlook skill covering both email and calendar management, plus delegate-aware mail sending behaviors. This code chunk is narrowly focused on calendar access for a delegate using Graph API endpoints under /users/{owner}/calendar*, /calendars, and calendarView. It supports viewing, creating, updating, and deleting calendar events and checking availability, which is consistent with part of the declared purpose. However, there is no email functionality at all in this chunk, and none of the claimed send modes are implemented here. Because key declared capabilities are absent and the actual scope is materially narrower than the description, this is a description/behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The email-related portion of the description is largely accurate: the script uses Microsoft Graph, loads delegate/owner configuration, reads/searches/manages mailbox contents, supports drafts, and implements self/send-as/send-on-behalf sending patterns. However, the description explicitly claims calendar support and management, while this code chunk contains no calendar commands or event-management functionality. There is a helper function to resolve event IDs, but it is unused and insufficient to substantiate calendar support. Therefore the declared description overstates the implemented capabilities in a material way.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
./scripts/outlook-token.sh refresh # Refresh expired token

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
./scripts/outlook-token.sh refresh # Refresh expired token

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
./scripts/outlook-token.sh refresh # Refresh expired token

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
./scripts/outlook-token.sh refresh # Refresh expired token

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

A command that prints the access token materially increases the risk of credential leakage through terminal history, logs, screenshots, copied output, or downstream agent handling. Because the token authorizes Microsoft Graph access to delegated mail and calendar resources, accidental disclosure can enable account misuse and data exfiltration.

Content

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

bash
./scripts/outlook-token.sh refresh   # Refresh expired token
./scripts/outlook-token.sh test      # Test connection to both accounts
./scripts/outlook-token.sh get       # Print access token
./scripts/outlook-token.sh info      # Show configuration info

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The skill stores OAuth access and refresh tokens in a local credentials file, which is inherently sensitive because compromise of that file can grant ongoing mailbox and calendar access, potentially including impersonation workflows. In a delegate mailbox context, token theft can expose another user's communications and enable unauthorized mail operations.

Content

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

md
## Files

- `~/.outlook-mcp/config.json` — Configuration (client ID, tenant ID, emails, timezone)
- `~/.outlook-mcp/credentials.json` — OAuth tokens (access + refresh)

## Changelog

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The workflow stores OAuth tokens obtained for mailbox access in ~/.outlook-mcp/credentials.json on disk. Even with restrictive file permissions, plaintext local token storage materially increases risk: compromise of the local account, backups, logs, or filesystem access could yield reusable tokens for reading mail and acting as the delegate across the owner's mailbox.

Content

Scanner excerpt · references/setup.md (reported line 210)May include surrounding context.

md
printf 'client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&grant_type=authorization_code&scope=%s' \
  "$CLIENT_ID" "$CLIENT_SECRET" "$CODE" "$REDIRECT" "$SCOPE" | \
  curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
  --data @- > ~/.outlook-mcp/credentials.json

chmod 600 ~/.outlook-mcp/credentials.json

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The credentials file is explicitly persisted after token exchange, meaning long-lived mailbox access material is kept locally. In a delegate-access design, theft of this file can enable unauthorized access to both the assistant's account context and shared owner mailbox actions.

Content

Scanner excerpt · references/setup.md (reported line 212)May include surrounding context.

md
curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
  --data @- > ~/.outlook-mcp/credentials.json

chmod 600 ~/.outlook-mcp/credentials.json

# Verify
cat ~/.outlook-mcp/credentials.json | jq '{status: "authorized", expires_in, scope}'

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% 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).

Content

Scanner excerpt · references/setup.md (reported line 345)May include surrounding context.

Also delete the local credentials:

bash
rm -rf ~/.outlook-mcp/

And optionally delete the Microsoft Entra ID app registration in the portal.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% 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).

Content

Scanner excerpt · references/setup.md (reported line 345)May include surrounding context.

Also delete the local credentials:

bash
rm -rf ~/.outlook-mcp/

And optionally delete the Microsoft Entra ID app registration in the portal.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/setup.md (reported line 215)May include surrounding context.

md
# All times are handled in the configured timezone.

CONFIG_DIR="$HOME/.outlook-mcp"
CREDS_FILE="$CONFIG_DIR/credentials.json"
CONFIG_FILE="$CONFIG_DIR/config.json"

# Load token

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-calendar.sh (reported line 9)May include surrounding context.

sh
# All times are handled in the configured timezone.

CONFIG_DIR="$HOME/.outlook-mcp"
CREDS_FILE="$CONFIG_DIR/credentials.json"
CONFIG_FILE="$CONFIG_DIR/config.json"

# Load token

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-mail.sh (reported line 16)May include surrounding context.

sh
# All times are handled in the configured timezone.

CONFIG_DIR="$HOME/.outlook-mcp"
CREDS_FILE="$CONFIG_DIR/credentials.json"
CONFIG_FILE="$CONFIG_DIR/config.json"

# Load token

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-token.sh (reported line 10)May include surrounding context.

sh
# All times are handled in the configured timezone.

CONFIG_DIR="$HOME/.outlook-mcp"
CREDS_FILE="$CONFIG_DIR/credentials.json"
CONFIG_FILE="$CONFIG_DIR/config.json"

# Load token

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-calendar.sh (reported line 16)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-mail.sh (reported line 23)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-mail.sh (reported line 30)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-token.sh (reported line 84)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-token.sh (reported line 260)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-token.sh (reported line 298)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/outlook-token.sh (reported line 299)May include surrounding context.

sh
ACCESS_TOKEN=$(jq -r '.access_token' "$CREDS_FILE" 2>/dev/null)

if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
    echo '{"error": "No access token. Run outlook-token.sh refresh or complete setup."}'
    exit 1
fi

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
89% confidence
Finding

The helper resolves an event by matching any event ID ending with a user-supplied suffix across the latest 200 events, then silently takes the first match. This can let ambiguous or very short IDs operate on the wrong event, which is especially risky because the same resolution logic feeds read, update, and delete actions on another user's calendar.

Content

Scanner excerpt · scripts/outlook-calendar.sh (reported line 42)May include surrounding context.

sh
# Helper: find full event ID from partial suffix
find_full_event_id() {
    local PARTIAL_ID="$1"
    curl -s "$API/calendar/events?\$top=200&\$select=id&\$orderby=start/dateTime%20desc" \
        -K "$AUTH_HEADER_FILE" | \
        jq -r --arg suffix "$PARTIAL_ID" '.value[] | select(.id | endswith($suffix)) | .id' | head -1
}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
70% 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).

Content

Scanner excerpt · scripts/outlook-calendar.sh (reported line 90)May include surrounding context.

sh
COUNT=$(echo "$COUNT" | grep -o '^[0-9]*' | head -1)
        COUNT=${COUNT:-10}
        NOW=$(get_now)
        curl -s "$API/calendarView?startDateTime=$NOW&endDateTime=2099-12-31T23:59:59Z&\$top=$COUNT&\$orderby=start/dateTime&\$select=id,subject,start,end,location,isAllDay" \
            -K "$AUTH_HEADER_FILE" \
            -H "Prefer: outlook.timezone=\"$TIMEZONE\"" | jq 'if .error then {error: .error.message} else (.value | to_entries | .[] | {n: (.key + 1), subject: .value.subject, start: .value.start.dateTime[0:16], end: .value.end.dateTime[0:16], location: (.value.location.displayName // ""), id: .value.id[-20:]}) end'
        ;;

Static analysis

No suspicious patterns detected.