Back to skill

Security audit

OAuth Providers

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches an OAuth provider setup tool, but it handles high-value credentials in ways that need review before installation.

Review before installing. Prefer the manual token or API-key paths unless the auto-detect flow is changed to require explicit informed consent, avoid returning any raw token to the browser, and use stronger OAuth session isolation. Confirm you are comfortable copying a Claude CLI credential into OpenClaw storage and with the provider policy risk described by the skill.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
references/auth-login.ts:167
Finding

Anthropic access token is exposed in the browser-facing RPC response

Content
View full analysis

Vulnerability Details

File Location: references/auth-login.ts, lines 167-171
Vulnerability Type: Sensitive credential exposure through an RPC response
Risk Level: High

Vulnerable Code:

ts
const result = await storeAnthropicSetupToken(accessToken, profileName ?? "claude-cli");
if (!result.ok) {
  return result;
}
return { ok: true, profileId: result.profileId, token: accessToken };

Technical Analysis

The autoDetectAnthropicToken function reads an Anthropic OAuth bearer token from the local ~/.claude/.credentials.json file. After storing the credential, it unnecessarily includes the complete token in its return value.

The auth.login.anthropic-auto gateway handler sends this object back through the RPC response. Although the UI controller declares a narrower TypeScript response type that omits token, TypeScript types do not remove properties from runtime objects. The raw credential therefore still reaches the browser.

Returning a stored bearer credential violates secret-minimization principles. It exposes the token to browser memory, gateway or RPC instrumentation, debugging tools, browser extensions, client-side compromises, and any logging layer that records RPC responses.

Attack Path

  1. The user has previously authenticated with the Claude CLI, causing an Anthropic token to be stored in ~/.claude/.credentials.json.
  2. The user or an authorized Control UI client invokes auth.login.anthropic-auto.
  3. The gateway reads claudeAiOauth.accessToken from the local credential file.
  4. The gateway stores the token in the OpenClaw authentication profile.
  5. The function returns the same raw token as the token property of its result.
  6. The RPC handler serializes that result and transmits it to the browser.
  7. A compromised browser context, extension, RPC interceptor, developer tool, or response-logging component captures the bearer token.
  8. The captured token can be ...[truncated 636 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove the token from the success response and return only non-sensitive metadata:

    ts
    return { ok: true, profileId: result.profileId };
    
  • Change the function's declared return type so it does not contain an optional token field.

  • Ensure gateway handlers never serialize access tokens, API keys, authorization codes, refresh tokens, or other bearer credentials.

  • Review RPC logging and telemetry to confirm that previous responses containing this field were not retained.

  • Add automated tests asserting that successful and failed authentication RPC responses contain no credential values or credential-shaped properties.

  • Consider explicitly constructing response objects at the RPC boundary rather than forwarding internal service-layer objects.

  • If exposure may already have occurred, revoke or rotate the affected Anthropic credential.

T09 · Insecure Skill Coding Practices

Warning
Location
references/auth-login.ts:286
Finding

OAuth sessions use weak identifiers and are not bound to the initiating client

Content
View full analysis

Vulnerability Details

File Location: references/auth-login.ts, lines 286-287, 301-310, and 423-432
Vulnerability Type: Weak session identifier generation and missing session ownership enforcement
Risk Level: Medium

Vulnerable Code:

ts
const sessionId = `oauth-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
pendingOAuthSessions.set(sessionId, { url, resultPromise, submitCode: resolveManualCode });
ts
const session = pendingOAuthSessions.get(sessionId);
if (!session) {
  return { ok: false, error: "No active OAuth session. Please start the flow again." };
}
try {
  const result = await session.resultPromise;
  pendingOAuthSessions.delete(sessionId);
  return result;
} catch (err) {
  pendingOAuthSessions.delete(sessionId);
  return { ok: false, error: String(err) };
}
ts
const session = pendingOAuthSessions.get(sessionId);
if (!session) {
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "No active OAuth session for this sessionId."));
  return;
}
if (!session.submitCode) {
  respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, "Manual code submission not supported for this session."));
  return;
}
session.submitCode(input);
respond(true, { ok: true }, undefined);

Technical Analysis

The OAuth session identifier combines the current timestamp with six base-36 characters generated by Math.random(). Math.random() is not a cryptographically secure random-number generator and must not be used to create security-sensitive session identifiers.

The timestamp is predictable, and the remaining random component has limited entropy. This makes the identifier materially weaker than a session token generated with a cryptographically secure random-number generator.

In addition, entries in pendingOAuthSessions contain no authenticated user, gateway connection, or browser-client ownership information. The polli ...[truncated 2501 chars]

Remediation
View remediation

Remediation Suggestions

  • Generate session identifiers using a cryptographically secure primitive such as crypto.randomUUID() or at least 32 random bytes from node:crypto.
  • Bind each pending OAuth session to the authenticated user and initiating gateway connection or client identity.
  • Verify ownership in both the polling and manual-code submission handlers before accessing the session.
  • Add a short, explicit expiration time and automatically delete expired sessions from pendingOAuthSessions.
  • Add a cancellation RPC that removes the gateway-side session and terminates or invalidates the pending flow when possible.
  • Mark manual submission as consumed before resolving its promise, reject subsequent submissions, and limit failed attempts.
  • Delete sessions in all completion, rejection, timeout, and cancellation paths.
  • Avoid exposing session identifiers in logs, URLs, or telemetry.
  • Add concurrency tests demonstrating that one authenticated client cannot poll, submit to, cancel, or otherwise affect another client's OAuth session.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared description is broader UI-focused functionality, while the supplied code chunk is specifically a backend RPC module for authentication flows. Parts of the description do match: Anthropic setup-token handling, OpenAI Codex OAuth, and manual-paste fallback are implemented. However, this code also performs an undeclared capability by reading Claude CLI credentials from the user's home directory for automatic token import, and it supports profile removal, neither of which are described. Conversely, several declared features are absent from this chunk, including API key handling for multiple providers, UI tab/badge behavior, and explicit encrypted storage in secrets.json. Because the actual code's behavior only partially matches the declared purpose and includes materially undeclared credential-file access/removal functionality, this should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

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 · SKILL.md (reported line 4)May include surrounding context.

md
---
name: oauth-providers
version: 1.1.0
description: Adds an "OAuth" settings tab to the OpenClaw Control UI for connecting AI model providers. Supports Anthropic Claude Pro/Max subscription tokens (setup-token flow), OpenAI Codex PKCE OAuth with manual-paste fallback for WSL2, and API keys for Anthropic, OpenAI, Google (Gemini), and OpenRouter. Includes auth profile order troubleshooting, badge rendering logic, and architecture reference for the auth-profiles system. Credentials are stored encrypted in auth-profiles.json and secrets.json.
---

# OAuth Providers Tab

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 · SKILL.md (reported line 46)May include surrounding context.

md
---
name: oauth-providers
version: 1.1.0
description: Adds an "OAuth" settings tab to the OpenClaw Control UI for connecting AI model providers. Supports Anthropic Claude Pro/Max subscription tokens (setup-token flow), OpenAI Codex PKCE OAuth with manual-paste fallback for WSL2, and API keys for Anthropic, OpenAI, Google (Gemini), and OpenRouter. Includes auth profile order troubleshooting, badge rendering logic, and architecture reference for the auth-profiles system. Credentials are stored encrypted in auth-profiles.json and secrets.json.
---

# OAuth Providers Tab

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The skill explicitly describes reading an access token from ~/.claude/.credentials.json and importing it into another system via RPC. Accessing credentials from another application's private store is highly sensitive because it can exfiltrate or repurpose tokens beyond the user's original intent, especially when the token is noted as restricted to Claude Code.

Content

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

md
- User runs `claude setup-token` in terminal to generate a `sk-ant-oat01-...` token
- Pastes token into UI
- Validated by `validateAnthropicSetupToken()`, stored via `buildTokenProfileId()` + `upsertAuthProfile()`
- **Auto-detect button**: Reads existing `accessToken` from `~/.claude/.credentials.json` (under `claudeAiOauth`) and stores it via `auth.login.anthropic-auto` RPC
- **⚠️ Important**: Anthropic has blocked some subscription usage outside Claude Code. The docs warn: *"This credential is only authorized for use with Claude Code."* Setup-token support is "technical compatibility only" with policy risk. If you get a "not authorized" error, an API key is required.

### API Keys (all providers)

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The documented auth.login.anthropic-auto RPC auto-detects and imports a token from ~/.claude/.credentials.json, which is a cross-application credential access path. Even if intended for convenience, this creates a high-risk mechanism for unauthorized credential reuse if invoked without strong user consent and access restrictions.

Content

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

md
|---|---|
| `auth.login.status` | List all configured auth profiles |
| `auth.login.anthropic-token` | Validate + store Anthropic setup-token |
| `auth.login.anthropic-auto` | Auto-detect token from `~/.claude/.credentials.json` |
| `auth.login.openai-codex` | Run PKCE OAuth (opens browser) |
| `auth.login.openai-codex.submit-code` | Manual paste of redirect URL (WSL2 fallback) |
| `auth.login.remove` | Remove a profile by `profileId` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Copy `references/auth-login.ts` → `src/gateway/server-methods/auth-login.ts`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Copy `references/auth-login.ts` → `src/gateway/server-methods/auth-login.ts`

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The known-gotchas section reiterates that the feature reads ~/.claude/.credentials.json as a workaround, confirming intentional credential access outside the primary application boundary. This increases confidence that the skill includes a sensitive credential-harvesting capability rather than a purely descriptive mention.

Content

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

md
1. **Auth order precedence**: `auth-profiles.json` order beats `openclaw.json`. If badge shows wrong auth mode, check for stale order in auth-profiles.json first.
2. **Gateway restart overwrites**: Manual edits to `auth-profiles.json` can be overwritten by gateway processes. Prefer editing `openclaw.json` for persistent config.
3. **WSL2 loopback isolation**: The OpenAI Codex OAuth callback server binds to `127.0.0.1:1455` (hardcoded in `@mariozechner/pi-ai`). Windows browsers can't reach WSL2 localhost. Use the manual-paste field.
4. **`claude setup-token` requires interactive TTY**: Uses Ink/raw mode — cannot be run non-interactively. The auto-detect button reads `~/.claude/.credentials.json` as a workaround.
5. **Anthropic policy risk**: `sk-ant-oat01-*` tokens may be blocked outside Claude Code. If API calls return authorization errors, switch to an API key.
6. **`lastGood` persistence**: The `lastGood` field in `auth-profiles.json` can cause the gateway to skip the configured order and jump straight to a previously-working profile. Remove it along with `order` when troubleshooting.

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The changelog confirms the addition of a button that reads from ~/.claude/.credentials.json, reinforcing that this is implemented functionality and not incidental documentation. Such functionality is dangerous because it normalizes accessing another tool's stored secrets and may bypass the original auth boundaries intended by the provider.

Content

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

md
### v1.1.0
- Added WSL2 manual-paste fallback for OpenAI Codex OAuth (`onManualCodeInput` + `auth.login.openai-codex.submit-code` RPC)
- Added Anthropic auto-detect button (`auth.login.anthropic-auto` RPC) — reads from `~/.claude/.credentials.json`
- Added auth badge rendering reference (`renderAuthBadge()` from `grouped-render.ts`)
- Added auth profile order architecture documentation with troubleshooting guide
- Added stale order/ghost profile diagnosis and fix procedures

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The code is designed to access another application's credential store and repurpose an OAuth access token for this application. Cross-application credential harvesting is highly sensitive because it bypasses a fresh authentication flow and can grant this app the same account access as the original CLI, especially if the token is long-lived or over-scoped.

Content

Scanner excerpt · references/auth-login.ts (reported line 109)May include surrounding context.

ts
// ─── Anthropic auto-detect (from claude CLI) ─────────────────────────────────

/**
 * Read the token from the claude CLI credentials file (~/.claude/.credentials.json)
 * and store it directly as an Anthropic subscription token profile.
 * 
 * Claude CLI stores an sk-ant-oat01-... OAuth access token that OpenClaw can use directly.

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The implementation explicitly targets an OAuth access token from Claude CLI and treats it as a reusable credential for OpenClaw. Reusing bearer tokens across applications is dangerous because anyone obtaining or storing that token can act as the user until expiry, and this code also broadens the token's storage and usage surface.

Content

Scanner excerpt · references/auth-login.ts (reported line 112)May include surrounding context.

ts
* Read the token from the claude CLI credentials file (~/.claude/.credentials.json)
 * and store it directly as an Anthropic subscription token profile.
 * 
 * Claude CLI stores an sk-ant-oat01-... OAuth access token that OpenClaw can use directly.
 */
async function autoDetectAnthropicToken(profileName?: string): Promise<{
  ok: boolean;

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

This line reads ~/.claude/.credentials.json directly from the user's home directory, which is a sensitive secret source. Accessing and importing secrets from unrelated local stores materially raises the risk of credential theft, misuse, and unintended propagation of tokens into additional storage locations.

Content

Scanner excerpt · references/auth-login.ts (reported line 120)May include surrounding context.

ts
error?: string;
  token?: string;
}> {
  const credPath = join(homedir(), ".claude", ".credentials.json");
  let raw: string;
  try {
    raw = await readFile(credPath, "utf-8");

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The YAML front-matter description broadly states that the skill 'Adds an "OAuth" settings tab' and summarizes many capabilities, but it does not specify when this skill should be invoked or what user requests should or should not trigger it. For manifest-scoped trigger quality, the lack of explicit invocation boundaries can cause the skill to match generic OAuth, credentials, or provider-setup requests beyond its intended OpenClaw UI context.

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.

(sleep 3 && systemctl --user restart openclaw-gateway) &

text

**Why Option 1 is preferred**: The CLI writes to `auth-profiles.json`, but gateway restarts or other processes can overwrite it. Removing the stored order lets `openclaw.json` (which is the canonical config) be the authority.

### Repair Logic

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code automatically reads a third-party CLI credential file from ~/.claude/.credentials.json and imports the access token into OpenClaw storage without any user-facing consent step in this handler. Even if invoked through a UI action, silently copying reusable credentials across trust boundaries increases the risk of unintended credential reuse, surprise persistence, and accidental exposure through later application behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The removeProfile function deletes profile entries from both auth-profiles storage and openclaw.json, which is a destructive persistent operation. In this file, the operation lacks a confirmation prompt, warning, or visible user-facing disclosure beyond internal comments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The function immediately issues an auth.login.remove request and reports success, but there is no visible confirmation prompt, cautionary comment, or user-facing warning in this file before removing a saved profile. Because removing an authentication profile is a destructive action that can disconnect an account and may require reconfiguration, this operation should be disclosed or confirmed.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.