Back to skill

Security audit

Office 365 Connector

Security checks for vulnerabilities and agentic risk

Overview

This is a real Office 365 connector, but it grants broad Microsoft account access and has credential-handling and account-selection flaws that should be reviewed before use.

Review before installing. Use only with a dedicated, least-privilege Azure app if possible, avoid the raw token command, do not provide secrets on shared or logged shells, and prefer waiting for fixes that validate account names, remove unused scopes and stored client secrets, and add confirmations for send/cancel/remove 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 (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
auth.js:12
Finding

Excessive and Unused Microsoft Graph OAuth Permissions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
accounts.js:60
Finding

Account Name Path Traversal Allows Token Operations Outside the Intended Directory

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
auth.js:27
Finding

Explicitly Requested Missing Account Silently Falls Back to Legacy Credentials

Content
View full analysis
...'); } // Return legacy format return { name: 'legacy', tenantId, clientId, clientSecret, tokenPath: path.join(process.env.HOME, '.openclaw', 'auth', 'microsoft-graph.json') }; } } ``` ### Technical Analysis The function catches every error raised by `getAccount()`, without distinguishing between: - No multi-account configuration being available - No default account being configured - An explicitly requested account not existing - Malformed or unreadable account configuration - Other unexpected account-loading failures If legacy environment credentials exist, all these failures silently select the legacy identity. Therefore, a typo or attacker-controlled account name does not fail safely. Instead, sensitive operations proceed under a different Microsoft account. This is especially dangerous for operations with external or destructive effects, such as sending email, replying to messages, or cancelling calendar events. The behavior also conflicts with documentation that promises an “Account not found” error. ### Attack Path 1. A user has legacy Azure credentials and a legacy token configured in environment variables and `~/.openclaw/auth/microsoft-graph.json`. 2. The user or an automation workflow explicitly invokes a comm ...[truncated 1021 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
accounts.js:239
Finding

Client Secrets Are Exposed in Process Arguments and Stored Despite Being Unused

Content
View full analysis
[email] [description]'); process.exit(1); } const [name, tenantId, clientId, clientSecret, email, description] = args; addAccount(name, tenantId, clientId, clientSecret, { email, description }); console.log(`✅ Added account "${name}"`); ``` The secret is stored in plaintext by `accounts.js:67-74`: ```js function addAccount(name, tenantId, clientId, clientSecret, options = {}) { const config = loadAccounts(); config.accounts[name] = { tenantId, clientId, clientSecret, email: options.email || null, description: options.description || null, addedAt: config.accounts[name]?.addedAt || new Date().toISOString() }; ``` However, the authentication implementation deliberately does not use the secret at `auth.js:150-155`: ```js // Only include client_secret if it exists (for confidential client apps) // Public clients (device code flow) should NOT include it if (accountConfig.clientSecret && accountConfig.clientSecret !== '') { // Don't include it - public clients fail with client_secret } ``` ### Technical Analysis The documented account-add command requires users to provide an Azure client secret as a command-line argument. Command-line arguments may be exposed through: - Process enumeration tools while the command is running - Shell history - Terminal session recording - Automation logs - CI/CD job output or metadata - Diagnostic and monitoring systems The secret is then persisted in plaintext inside `office365-accounts.json`. File mode `0600` reduces exposure to other local users but does not protect the secret from malware, backups, processes runni ...[truncated 1680 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
auth.js:320
Finding

Authentication CLI Prints Microsoft Graph Bearer Tokens to Standard Output

Content
View full analysis
{ console.log(token); process.exit(0); }) ``` This command is also advertised in `SKILL.md:383` and `MULTI-ACCOUNT.md:286`. ### Technical Analysis The `token` command prints a live Microsoft Graph bearer token directly to standard output. Bearer tokens grant access based solely on possession and therefore must be treated as credentials. Standard output is frequently captured by systems beyond the immediate terminal, including: - Agent conversation transcripts - Shell and terminal logging - CI/CD logs - Process supervisors - Redirected files and pipelines - Remote support or screen-recording tools The behavior conflicts with the project's own guidance in `references/permissions.md:351-355`, which states that tokens should never be exposed in logs or error messages. Although access tokens are time-limited, they are sufficiently long-lived to support immediate mailbox, calendar, contact, and email-sending abuse. The associated refresh token is not printed by this command. ### Attack Path 1. A user, automation process, or AI agent invokes `node auth.js token`. 2. `getAccessToken()` loads or refreshes the account's bearer token. 3. The complete bearer token is printed to standard output. 4. The output is retained in a terminal transcript, redirected file, build log, agent log, or monitoring system. 5. An attacker obtains the captured token. 6. The attacker sends authenticated requests directly to Microsoft Graph until the token expires or is revoked. ### Impact Assessment A disclosed access token carries the delegated scopes granted during authentication. In the current implementation, those scopes include reading and modifying mail, sending email, reading and modifying calendars and ...[truncated 433 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (30)

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

Documenting a command that returns an access token materially increases credential exposure risk because bearer tokens can be copied, logged, pasted into shells, or harvested from terminal history and then used to access Microsoft Graph resources. In a multi-account context, this is more dangerous because one skill can expose tokens for several identities, broadening the blast radius if an operator or downstream tool mishandles them.

Content

Scanner excerpt · MULTI-ACCOUNT.md (reported line 286)May include surrounding context.

bash
node auth.js login [--account=name]            # Authenticate
node auth.js status [--account=name]           # Check status
node auth.js token [--account=name]            # Get access token

Email

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
See [Permissions Reference](references/permissions.md) for detailed information about what each permission allows.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
See [Permissions Reference](references/permissions.md) for detailed information about what each permission allows.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Use the `accounts.js` CLI to manage:

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The documented auth.js token command exposes a raw access token to the caller, which is a credential that can be reused to access Microsoft Graph on behalf of the user until expiry. In a multi-account skill with mail, calendar, and contacts scopes, token disclosure materially increases risk of account takeover of delegated API access and downstream data exfiltration or manipulation.

Content

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

bash
node auth.js login [--account=name]            # Authenticate
node auth.js status [--account=name]           # Check status
node auth.js token [--account=name]            # Get access token

Email

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 · auth.js (reported line 136)May include surrounding context.

js
}

/**
 * Refresh access token
 */
async function refreshAccessToken(refreshToken, accountConfig) {
  const authority = `https://login.microsoftonline.com/${accountConfig.tenantId}`;

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 · auth.js (reported line 206)May include surrounding context.

js
}

/**
 * Refresh access token
 */
async function refreshAccessToken(refreshToken, accountConfig) {
  const authority = `https://login.microsoftonline.com/${accountConfig.tenantId}`;

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 · auth.js (reported line 334)May include surrounding context.

js
}

/**
 * Refresh access token
 */
async function refreshAccessToken(refreshToken, accountConfig) {
  const authority = `https://login.microsoftonline.com/${accountConfig.tenantId}`;

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/permissions.md (reported line 426)May include surrounding context.

md
A: No. This skill only requests email, calendar, and contact permissions. File access requires separate permissions (Files.Read, Files.ReadWrite, etc.).

**Q: How long do tokens last?**
A: Access tokens expire after 1 hour. Refresh tokens typically last 90 days but are automatically refreshed as long as you use the app regularly.

**Q: Is this secure?**
A: When configured properly, yes. The OAuth 2.0 protocol is industry-standard and secure. Follow the security best practices in this document and the setup guide.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The markdown documents node accounts.js remove <name> and notes that it deletes account configuration and authentication tokens, but it does not warn users about the irreversible impact or recommend confirmation/backup steps. Because this operation affects stored credentials and account state, the skill description should clearly disclose the risk before users run it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The command reference includes node cancel-event.js <event-id> but provides no warning that invoking it may cancel a calendar event and potentially notify participants. This is a destructive or hard-to-reverse action affecting external users, so the markdown should disclose that consequence.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill documents use of environment variables for Azure tenant/client credentials but does not declare an explicit tool scope such as permissions or allowed-tools. That omission weakens reviewability and policy enforcement because consumers cannot easily tell that the skill expects access to sensitive configuration and authentication material.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The capability list includes destructive actions such as deleting messages, updating or deleting calendar events, and creating/updating/deleting contacts without prominent safety warnings or confirmation guidance. In an agent context, this increases the chance of accidental data loss or unauthorized modification if the skill is invoked incorrectly or by a compromised workflow.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
### Email Permissions
- `Mail.Read` - Read user email
- `Mail.ReadWrite` - Read and write access to user email
- `Mail.Send` - Send email as the user

### Calendar Permissions

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code stores the Azure client secret directly in a local JSON config file under the user's home directory. Although the file is written with mode 0600, persisting long-lived credentials to disk increases the risk of secret exposure through local compromise, backups, or accidental disclosure, and the CLI does not clearly warn the user that the secret will be stored.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The removeAccount function unlinks the token file and removes the account configuration, which is a destructive operation affecting local authentication state. While the CLI prints a success message afterward, there is no confirmation prompt or pre-action warning before deletion occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The legacy import flow reads sensitive Azure credentials from environment variables and then saves them into the local accounts configuration, converting ephemeral secrets into persisted plaintext secrets. This broadens exposure without a clear warning to the user and can surprise operators who expected the credentials to remain only in process environment scope.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The CLI token command prints a live Microsoft Graph access token directly to stdout. In practice, stdout is often captured by shell history, logs, CI systems, terminal recorders, or parent processes, so this can unintentionally disclose credentials that grant mailbox, calendar, and contacts access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code fetches personal calendar information from Microsoft Graph and prints event details including subject, location, organizer, and attendees to stdout. While the network access and output behavior are visible in code, there is no user-facing warning or disclosure that potentially sensitive calendar data will be retrieved from a remote service and exposed in terminal output.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script prints email subjects, senders, timestamps, and body previews directly to stdout without any confirmation, redaction, or warning. In a connector handling Microsoft 365 mailbox data, this can expose sensitive message contents and metadata to terminal logs, shell history workflows, CI logs, or other observers with access to the execution environment.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
3. Confirm you're in the correct directory (top-right corner)
   - If managing multiple tenants, switch to the correct one

## Step 2: Create App Registration

1. In Azure Portal, search for "App registrations" in the top search bar
2. Click **+ New registration**

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The guide instructs users to grant a broad set of Microsoft Graph delegated permissions covering full read/write access to mail, calendar, and contacts plus offline refresh capability. In a setup guide for an integration this may be functionally motivated, but it still meaningfully expands the blast radius if the skill, stored tokens, or host are compromised, and the guide does not prominently explain that these scopes allow ongoing access to highly sensitive user data.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
**⚠️ Security note:** 
- Client secrets are sensitive - treat them like passwords
- Don't commit secrets to version control
- Restrict file permissions: `chmod 600 ~/.openclaw/openclaw.json`

## Step 9: First Authentication

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The guide shows how to send email from a specific Office 365 account, but it does not include any warning that the command will transmit a real message from the chosen identity. For a multi-account skill, a brief disclosure is important to help users avoid sending mail from the wrong account.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The event time formatting is forced to use the 'en-US' locale, and other date displays later in the file also use 'en-US'. This imposes a specific language/locale behavior without giving the user a choice or documenting that the skill is intentionally U.S.-locale-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.