Back to skill

Security audit

OpenClaw Temperature

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent GIF-reaction purpose, but it handles its hosted-service API key too loosely, including a path that can print the full key into chat and URLs.

Install only if you are comfortable with a hosted reaction service receiving short, non-sensitive reaction metadata and storing a local API key. Avoid passing secrets or private conversation summaries in metadata, and treat any recharge output carefully because this version can reveal the full API key in chat and URLs.

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

T09 · Insecure Skill Coding Practices

Error
Location
index.js:320
Finding
API Key Exposed Through Recharge URL and Chat Output<![CDATA[ ## Vulnerability Details **File Location**: `index.js:320-330` and `index.js:448-458` **Vulnerability Type**: Credential exposure through URL query parameters and rendered chat content **Risk Level**: High ### Vulnerable Code ```js const response = await fetchImpl(`${hostedApiBaseUrl}/v1/public/commerce-settings`); const body = response.ok ? await response.json() : {}; const settings = body.commerce_settings ?? {}; return { mode: "recharge_instructions", apiKey: resolved.apiKey, apiKeyHint: maskApiKey(resolved.apiKey), price: settings.betaPriceDisplay ?? "当前免费", paymentMethod: settings.paymentMethodLabel ?? "无需付款", buyPageUrl: `${hostedApiBaseUrl}/recharge?api_key=${encodeURIComponent(resolved.apiKey)}` }; ``` ```js export function formatRechargeMarkdown(recharge) { const buyPageUrl = recharge.buyPageUrl ?? recharge.rechargeUrl ?? "https://claw-temp.nydhfc.cn/recharge"; return [ "OpenClaw 温度层当前已改为免费 Beta,通常不需要充值。", "", `价格:${recharge.price ?? "当前免费"}`, `付款方式:${recharge.paymentMethod ?? "无需付款"}`, `API Key:${recharge.apiKey ?? recharge.apiKeyHint ?? "请让 OpenClaw 读取本地保存的 ocl_ key"}`, "", `查看免费说明:${buyPageUrl}` ].join("\n"); } ``` The behavior is explicitly validated by `tests/index.test.js:91-98`, which requires both the full API key and the URL containing it to appear in generated Markdown. ### Technical Analysis The API key is a bearer credential used to authorize reaction API requests. The implementation places that credential in a URL query parameter and also returns the unmasked credential in Markdown intended for display in a conversation. Credentials in query strings can be recorded by browser history, HTTP access logs, reverse proxies, monitoring systems, analytics services, screenshots, copied chat messages, and potentially referrer headers. Rendering the raw key in chat also unnecessarily expands its exposure to conversation storage, chat bridge operators, users with transcript access, and ...[truncated 1261 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `apiKey` from the object returned by `getRechargeInstructions()`. 2. Never include reusable credentials in query parameters or rendered Markdown. 3. Display only a masked identifier such as `apiKeyHint`. 4. If account navigation is required, submit an authenticated POST request to create a short-lived, single-use session. 5. Return an opaque session URL that expires quickly and cannot be converted back into the API key. 6. Ensure server and proxy logs redact authorization headers, tokens, and session identifiers. 7. Replace the tests that require full-key disclosure with assertions that the raw key never appears in Markdown or URLs. 8. Rotate previously exposed keys where feasible. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:334
Finding
Unrestricted Metadata Transmission and Hosted API Endpoint Override<![CDATA[ ## Vulnerability Details **File Location**: `index.js:193-216`, `index.js:334-355`, and `index.js:494-555` **Vulnerability Type**: Unvalidated data transmission and credential redirection **Risk Level**: Medium ### Vulnerable Code ```js export function buildReactionEvent({ eventType, emotionalFamily, intensity = "low", confidence = 0.8, metadata = {} }) { return { schema_version: "reaction-event.v1", event: { event_id: makeEventId(), event_type: eventType, emotional_family: emotionalFamily ?? DEFAULT_EVENT_MAP[eventType], intensity, timestamp: new Date().toISOString(), source_context: { surface: "chat_reply", trigger_moment: "after_main_reply", confidence }, metadata } }; } ``` ```js export async function requestReaction({ hostedApiBaseUrl = HOSTED_API_BASE_URL, apiKey = null, payload, storage = createDefaultApiKeyStorage(), fetchImpl = fetch }) { try { const resolved = await ensureApiKey({ hostedApiBaseUrl, apiKey, storage, fetchImpl }); const response = await fetchImpl(`${hostedApiBaseUrl}/v1/reactions/decide`, { method: "POST", headers: { authorization: `Bearer ${resolved.apiKey}`, "content-type": "application/json", "x-openclaw-skill-version": SKILL_VERSION }, body: JSON.stringify(payload) }); ``` ```js const payload = buildReactionEvent({ eventType: classification.eventType, emotionalFamily: emotionalFamily ?? classification.emotionalFamily, intensity: intensity ?? classification.intensity, confidence: confidence ?? classification.confidence, metadata: { ...metadata, classification_reason: classification.reason } }); const result = await requestReaction({ hostedApiBaseUrl, apiKey, payload, storage, fetchImpl }); ``` ### Technical Analysis Arbitrary caller-provided `metadata` is inserted into the reaction payload w ...[truncated 2184 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove public production support for overriding `hostedApiBaseUrl`, or enforce an exact origin comparison against `https://claw-temp.nydhfc.cn`. 2. Parse the destination with `URL` and reject non-HTTPS schemes, alternate ports, credentials, subdomain tricks, and redirects to unapproved origins. 3. Apply the same destination validation before API-key registration, commerce requests, and reaction requests. 4. Define a strict metadata schema with a small allowlist of non-sensitive fields. 5. Reject unknown keys and non-primitive or nested values unless explicitly required. 6. Impose per-field and total-payload size limits. 7. Redact common credential, token, private-key, and personal-data patterns before transmission. 8. Separate dependency injection used by tests from the public production API. 9. Document every field transmitted to the hosted service and require callers to avoid secrets. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
index.js:181
Finding
Locally Stored API Key Lacks Explicit Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `index.js:181-190` **Vulnerability Type**: Insecure local credential storage permissions **Risk Level**: Low ### Vulnerable Code ```js const fs = await import("node:fs/promises"); const path = await import("node:path"); await fs.mkdir(path.dirname(filePath), { recursive: true }); await fs.writeFile( filePath, JSON.stringify({ apiKey, createdAt: new Date().toISOString() }, null, 2), "utf8" ); ``` ### Technical Analysis The Skill stores a reusable API key in `.openclaw-temperature/api-key.json`, but it does not explicitly set permissions on the directory or file. The effective permissions therefore depend on the process umask and the permissions of the surrounding working directory. In an environment with a permissive umask or shared working directory, another local account or process may be able to read the credential. The implementation also does not explicitly protect the destination against pre-existing symbolic links. Local storage is necessary for the declared functionality of reusing a registered key, but storing the credential with permissions inherited entirely from the runtime environment is not the minimum safe implementation. ### Attack Path 1. The Skill initializes and automatically registers an API key. 2. `setItem()` creates the storage directory and credential file without explicit restrictive modes. 3. A permissive runtime umask results in a file or directory readable by other local principals. 4. Another local account or process reads `api-key.json`. 5. The extracted key is reused to make authorized hosted-service requests. Where an attacker can modify the storage directory, a pre-created symbolic link could also redirect the write to another accessible location; exploitability depends on local filesystem permissions and runtime ownership. ### Impact Assessment A successful local attack exposes the stored hosted-service bearer credential. The attacker can perform operations ...[truncated 247 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the credential directory with mode `0700`. 2. Create or write the credential file with mode `0600`. 3. Verify and correct permissions on an existing storage directory and file before use. 4. Use exclusive creation and atomic replacement where practical. 5. Reject symbolic links and verify that the final path is a regular file owned by the current user. 6. Prefer an operating-system credential manager or secret store when available. 7. Avoid storing additional account data alongside the key. 8. Document the storage path and expected ownership and permission requirements. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Ae1

High
Category
analysis-evasion
Content
Import from `index.js` and call one of these functions:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Import from `index.js` and call one of these functions:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The publish instructions use `npx clawhub` without pinning an exact package version, so anyone following the documented release process may execute whatever version is currently served by the registry at publish time. If the package is compromised, typosquatted, or a newer malicious version is introduced, this can lead to arbitrary code execution on the publisher's machine or manipulation of the published skill metadata.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill persists an API key to localStorage in browser contexts or to a plaintext JSON file in Node contexts without any consent prompt, warning, or protection mechanism. This creates credential exposure risk through XSS, other local users/processes, backups, logs, or accidental file disclosure, and the key appears to grant access to a third-party hosted service.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
Several user-visible messages are hardcoded in Chinese, such as the initialization status text, recharge text, and default captions. The file does not offer locale selection or document that the skill is intentionally region-specific, which conflicts with the policy against forcing a language without user opt-in.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill packages metadata derived from user messages, replies, tool summaries, and conversation summaries into a payload and sends it to a remote host for reaction generation, but there is no user-facing disclosure, consent, or minimization control. Because these fields can contain sensitive conversational or system data, this can result in unintended exfiltration to an external domain.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest presents the skill as a simple GIF-reaction add-on, but it also declares hosted API usage and automatic API key registration. This mismatch is security-relevant because it can mislead users and reviewers about hidden networked behavior, account provisioning, and data exchange with a remote service.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
Automatic API key registration is not obviously necessary for a skill whose stated function is adding occasional GIF reactions. Unnecessary credential provisioning expands attack surface, can create opaque remote accounts, and may enable unexpected tracking or later abuse if the remote service is compromised or behaves differently than users expect.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The manifest indicates automatic API key registration and local storage of the resulting key, but it does not warn users about credential transmission, storage, or handling. This lack of transparency increases the risk of users unknowingly sending identifiers to a third party and storing secrets locally without understanding retention, protection, or revocation implications.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The package description says this is a skill for hosted GIF reactions with API key registration and beta access, but the package name and homepage indicate a temperature-themed skill. The keywords also reinforce the GIF/reaction purpose rather than temperature functionality, creating a clear semantic mismatch in the declared intent of the skill.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This file includes user-facing strings such as payment labels and instructions entirely in Chinese, and the tests assert those exact values. Because there is no indication here of user opt-in, language selection, or a documented region-specific constraint, this reflects a natural-language locale policy issue under the requirement not to force a specific language without user choice.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
Several assertions require Chinese captions and messaging for generated markdown output, which indicates the skill behavior is expected to be Chinese-only. In the absence of any visible opt-in, locale negotiation, or documentation that the skill is region-specific, this is a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
These tests validate reaction classification solely against Chinese user messages and assistant replies, suggesting the skill may be designed around a single forced language. Without evidence of optional language handling or documented locale limits, this creates a policy concern about forcing a locale without user choice.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
tests/index.test.js:96