Back to skill

Security audit

Dynamic Model Router

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent model-routing purpose, but it includes under-disclosed high-impact code paths that can execute shell commands, write prompts to shared temp files, probe arbitrary provider URLs, and persist local configuration/log data.

Review this skill carefully before installing. It may be appropriate only if you trust the publisher and are comfortable with local config/log persistence plus OpenClaw provider integration code. Do not use it with sensitive prompts or API keys until shell execution is changed to argument-based spawning, temp prompt files are protected or removed, provider URLs are strictly allowlisted, and the privacy documentation matches the actual data flows.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/openclaw/openclaw-invoker.ts:297
Finding
Shell Command Injection Through Model Messages<![CDATA[ ## Vulnerability Details **File Location**: `src/openclaw/openclaw-invoker.ts:297-320` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```ts const messagesJson = JSON.stringify(request.messages); const modelArg = request.model; // Build the base command let command = `${this.config.openclawPath} `; // Attempt to use the models subcommand command += `models invoke --model "${modelArg}" --messages '${messagesJson}'`; // Add optional parameters if (request.maxTokens) { command += ` --max-tokens ${request.maxTokens}`; } if (request.temperature !== undefined) { command += ` --temperature ${request.temperature}`; } logger.debug('Executing CLI command', { requestId, command: command.substring(0, 100) + '...', }); // Execute the command const { stdout, stderr } = await execAsync(command, { timeout: this.config.timeoutMs, cwd: this.config.workspaceDir, }); ``` ### Technical Analysis The invoker serializes caller-controlled messages and interpolates them directly into a shell command surrounded by single quotes. A single quote inside message content can terminate the `--messages` argument and introduce additional shell operators and commands. The model argument and mutable `openclawPath` configuration are also interpolated into the command string. Request validation verifies message types and whether the model appears in the provider model list, but it does not perform shell escaping. Because Node.js `child_process.exec()` invokes a shell, shell metacharacters are interpreted rather than passed literally to the OpenClaw executable. ### Attack Path 1. An attacker supplies a model request containing crafted message content, such as content that closes the single-quoted `--messages` argument. 2. `invokeModel()` validates that the content is a string but does not reject shell syntax. 3. For a DeepSeek provider, `selectInvocationStrategy()` selects `cli-direct`. 4. `invokeViaCliDirect()` inserts the s ...[truncated 704 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `child_process.exec()` with `execFile()` or `spawn()` using an argument array. - Pass the model, message JSON, token count, and temperature as separate arguments without invoking a shell. - Prefer transmitting request JSON through the child process's standard input rather than a command-line argument. - Resolve and allowlist the OpenClaw executable path; do not permit arbitrary runtime modification of `openclawPath`. - Retain strict model allowlisting and add numeric range validation for token and temperature values. - Add regression tests containing single quotes, command substitutions, semicolons, newlines, backticks, and shell redirection syntax. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/openclaw/openclaw-invoker.ts:484
Finding
Sensitive Prompts Written to Predictable Shared Temporary Storage<![CDATA[ ## Vulnerability Details **File Location**: `src/openclaw/openclaw-invoker.ts:484-504` **Vulnerability Type**: Insecure temporary-file handling and plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```ts logger.debug('Using temporary file invocation', { requestId, providerId: provider.id }); // Create temporary request file const requestFile = path.join(this.config.tempDir, `${requestId}_request.json`); const requestData: TempRequestFile = { request: { model: request.model, messages: request.messages, max_tokens: request.maxTokens, temperature: request.temperature, stream: false, }, metadata: { requestId, timestamp: new Date().toISOString(), providerId: provider.id, }, }; await fs.promises.writeFile(requestFile, JSON.stringify(requestData, null, 2)); ``` The default directory is created earlier without an explicit restrictive mode: ```ts tempDir: '/tmp/openclaw-invoker', if (!fs.existsSync(this.config.tempDir)) { fs.mkdirSync(this.config.tempDir, { recursive: true }); } ``` ### Technical Analysis Full system, user, and assistant messages are stored as plaintext in `/tmp/openclaw-invoker`. Neither directory creation nor file creation specifies restrictive permissions. Effective permissions therefore depend on the process umask and may allow other local users to inspect request files. The implementation uses a shared, predictable directory and `writeFile()` without exclusive creation or explicit symlink defenses. Cleanup only occurs after command completion and is best-effort, so termination, crashes, or failed cleanup can leave prompts on disk. The same design is also used for session files at `src/openclaw/openclaw-invoker.ts:410-427`. ### Attack Path 1. A user invokes a model with confidential prompt content. 2. The invoker selects the temporary-file strategy for non-DeepSeek providers. 3. The full request is written in plaintext beneath `/tmp/openclaw-invoker`. 4. Another ...[truncated 763 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Avoid writing prompts to disk; send request data through standard input or an authenticated local IPC mechanism. - If files are unavoidable, create a private per-process directory with `fs.mkdtemp()` under a protected runtime directory. - Set the directory mode to `0700` and request-file mode to `0600`. - Use exclusive creation flags such as `wx` and reject symbolic links. - Remove files in a `finally` block and register termination handlers for best-effort cleanup. - Track only files created by the current instance. - Never log prompt contents or command strings containing prompt fragments. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/openclaw/model-adapter.ts:269
Finding
Prompts and Bearer Credentials Sent to Unvalidated Provider URLs<![CDATA[ ## Vulnerability Details **File Location**: `src/openclaw/model-adapter.ts:269-301` **Vulnerability Type**: Credential and sensitive-data exfiltration through an unvalidated destination **Risk Level**: Critical ### Vulnerable Code ```ts if (!this.apiKey) { throw new RouterError('DeepSeek API key is not configured', 'AUTH_ERROR'); } const url = `${this.provider.baseUrl}/chat/completions`; const requestBody = { model: request.model, messages: request.messages, max_tokens: request.maxTokens || this.config.maxTokens, temperature: request.temperature || this.config.temperature, stream: false, }; logger.debug('Sending DeepSeek API request', { url, model: request.model, messageCount: request.messages.length, }); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs); try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, 'User-Agent': 'OpenClaw-DynamicRouter/0.1.0', }, body: JSON.stringify(requestBody), signal: controller.signal, }); ``` ### Technical Analysis The destination is constructed from `provider.baseUrl`, which can originate from OpenClaw configuration. The adapter does not require HTTPS, verify that the hostname belongs to the declared provider, prevent private or link-local destinations, or restrict redirects. The request sends both the complete conversation and the provider API key. Consequently, an attacker who can alter or influence the provider configuration can direct sensitive prompts and bearer credentials to an attacker-controlled server. This behavior contradicts the Skill documentation, which states that routing is processed locally and that user data is not sent to external servers. The adapter is not re-exported by `src/openclaw/index.ts`, which reduces default exposure, but the implementation remains directly i ...[truncated 996 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `model-adapter.ts` from the package if it is obsolete and unused. - Otherwise, require HTTPS and enforce an explicit mapping between provider identifiers and approved origins. - Bind each API key to one exact approved origin; never send credentials to a caller-supplied URL. - Reject URLs containing embedded credentials, unsupported ports, fragments, or non-HTTP schemes. - Resolve hostnames and block loopback, private, link-local, multicast, reserved, and cloud-metadata addresses. - Disable redirects or revalidate every redirect destination before forwarding credentials or request bodies. - Clearly disclose remote prompt processing and require explicit user consent. - Add tests proving that keys are never sent when destination validation fails. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/openclaw/provider-discovery.ts:460
Finding
Server-Side Request Forgery in Provider Discovery<![CDATA[ ## Vulnerability Details **File Location**: `src/openclaw/provider-discovery.ts:460-476` **Vulnerability Type**: Server-side request forgery through configuration-controlled provider URLs **Risk Level**: High ### Vulnerable Code ```ts logger.debug('Validating provider connectivity', { providerId: provider.id, baseUrl: provider.baseUrl, }); // Attempt to connect to the provider health-check endpoint const healthCheckUrl = this.getHealthCheckUrl(provider); try { // Use fetch for a simple connection test const response = await fetch(healthCheckUrl, { method: 'HEAD', headers: { 'User-Agent': 'OpenClaw-DynamicRouter/0.1.0', }, // timeout: 10000, }); ``` The target is generated without destination validation: ```ts const endpoint = healthCheckEndpoints[provider.id] || '/'; return `${provider.baseUrl}${endpoint}`; ``` ### Technical Analysis `discoverAll()` reads provider data from an OpenClaw JSON configuration file and validates enabled providers by issuing HTTP HEAD requests. The implementation does not constrain the scheme, hostname, resolved IP address, port, or redirect destination. An attacker who controls the provider configuration can therefore cause requests to originate from the Agent's network context. Fetch follows redirects by default, and this discovery request has no active timeout despite the commented timeout note. The request does not directly include prompts or API keys, so the principal confirmed risk in this file is SSRF and network-context leakage rather than direct credential exfiltration. ### Attack Path 1. An attacker changes `~/.openclaw/openclaw.json` or supplies an alternate configuration path. 2. The attacker sets an enabled provider's `baseUrl` to a loopback, private-network, link-local, metadata, or attacker-controlled endpoint. 3. The Skill calls `discoverAll()`. 4. `validateProvider()` constructs a health-check URL from the malicious base URL. 5. The Agent sends a HEAD request from i ...[truncated 565 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Parse destinations with the standard `URL` class rather than concatenating strings. - Require HTTPS and approved provider hostnames. - Resolve DNS and reject loopback, private, link-local, reserved, multicast, and metadata address ranges for both IPv4 and IPv6. - Disable redirects or validate each redirect destination. - Add an `AbortController` timeout and strict response-size limits. - Make active provider validation opt-in; local routing can use configured metadata without network probing. - Do not infer arbitrary domains using patterns such as `https://api.${providerId}.com`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/openclaw/status-monitor.ts:322
Finding
Recurring SSRF Through the Provider Status Monitor<![CDATA[ ## Vulnerability Details **File Location**: `src/openclaw/status-monitor.ts:322-335` **Vulnerability Type**: Recurring server-side request forgery **Risk Level**: High ### Vulnerable Code ```ts try { // Use a simple HTTP HEAD request to test connectivity const testUrl = this.getConnectionTestUrl(provider); const response = await fetch(testUrl, { method: 'HEAD', headers: { 'User-Agent': 'OpenClaw-DynamicRouter-Monitor/0.1.0', }, signal: controller.signal, }); clearTimeout(timeoutId); // Check response status const isOk = response.ok || response.status < 500; ``` Monitoring runs immediately and then periodically: ```ts this.performHealthCheck().catch(error => { logger.error('Initial health check failed', error as Error); }); this.monitoringInterval = setInterval(() => { this.performHealthCheck().catch(error => { logger.error('Periodic health check failed', error as Error); }); }, this.config.checkInterval); ``` ### Technical Analysis The status monitor constructs request URLs from `provider.baseUrl` without validating destination safety. Once monitoring starts, it performs an immediate request and repeats the operation every minute by default. The monitor therefore turns a configuration-controlled SSRF primitive into a recurring network action. It leaks the presence and timing of the Agent through its User-Agent and can repeatedly interact with internal or attacker-controlled services. The alert implementation only writes to the logger; it contains a TODO for external notifications. No metric exfiltration through alert channels is currently implemented. ### Attack Path 1. An attacker introduces a provider with a malicious `baseUrl`. 2. The provider and adapter are registered with `StatusMonitor`. 3. A caller invokes `startMonitoring()`. 4. The monitor immediately sends a HEAD request to the chosen destination. 5. It repeats the request according to `checkInterval`, which defaults to 60 seconds. ...[truncated 493 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Apply strict URL, DNS, IP-range, port, and redirect validation before every request. - Restrict monitoring to a predefined set of provider endpoints. - Require explicit user opt-in before enabling network health checks. - Enforce safe minimum and maximum monitoring intervals and introduce randomized backoff. - Stop monitoring automatically when no longer required. - Prefer passive health assessment based on actual invocation results rather than generating additional network requests. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/openclaw/openclaw-invoker.ts:628
Finding
Arbitrary File Deletion Through Mutable Temporary Directory Configuration<![CDATA[ ## Vulnerability Details **File Location**: `src/openclaw/openclaw-invoker.ts:628-655` **Vulnerability Type**: Unsafe cleanup of a caller-controlled directory **Risk Level**: High ### Vulnerable Code ```ts updateConfig(updates: Partial<OpenClawInvokerConfig>): void { this.config = { ...this.config, ...updates }; logger.debug('Invoker configuration updated', { updates }); } /** * Clean temporary files */ async cleanupTempFiles(): Promise<void> { try { const files = await fs.promises.readdir(this.config.tempDir); const cleanupPromises = files.map(async (file) => { const filePath = path.join(this.config.tempDir, file); const stats = await fs.promises.stat(filePath); // Delete temporary files older than one hour const age = Date.now() - stats.mtimeMs; if (age > 3600000) { await fs.promises.unlink(filePath); logger.debug('Deleted old temporary file', { file, age }); } }); await Promise.all(cleanupPromises); logger.info('Temporary-file cleanup completed', { tempDir: this.config.tempDir }); ``` ### Technical Analysis `updateConfig()` permits unrestricted modification of `tempDir`. `cleanupTempFiles()` then enumerates that directory and deletes every entry older than one hour. It does not verify that the path remains inside a private temporary root, that files were created by this invoker, or that entries match the invoker's naming convention. The cleanup uses `stat()` rather than a symlink-aware ownership policy and does not maintain a registry of generated files. Destroying the singleton also invokes this cleanup method, making deletion reachable through normal lifecycle handling after malicious configuration. ### Attack Path 1. An attacker or untrusted caller obtains access to the exported invoker instance. 2. The caller invokes `updateConfig({ tempDir: targetDirectory })`. 3. The target directory contains files older than one hour that are writable by the Agent accou ...[truncated 651 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Make the private temporary root immutable after construction. - Create and retain a canonical per-instance temporary directory. - Maintain an internal set of files created by the instance and delete only those files. - Verify canonical-path containment before every cleanup operation. - Use `lstat()` and reject symbolic links, directories, device nodes, and other unexpected entry types. - Require filenames to match a strict generated pattern, while treating pattern matching only as defense in depth. - Do not expose general directory cleanup through a public configuration update API. ]]>
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
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (96)

Known Vulnerable Dependency: handlebars==4.7.8 — 8 advisory(ies): CVE-2026-33916 (Handlebars.js has Prototype Pollution Leading to XSS through Partial Template In); CVE-2026-33937 (Handlebars.js has JavaScript Injection via AST Type Confusion); CVE-2026-33938 (Handlebars.js has JavaScript Injection via AST Type Confusion by tampering @part) +5 more

Critical
Category
Supply Chain
Confidence
97% confidence
Finding
handlebars 4.7.8 is present and has multiple severe advisories including prototype pollution and possible code or script injection through crafted templates/AST manipulation. Even as a dev dependency, this is highly dangerous if any untrusted template content is compiled or rendered during code generation, testing, or build automation.

Known Vulnerable Dependency: brace-expansion==1.1.12 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
95% confidence
Finding
brace-expansion 1.1.12 is present and is associated with multiple denial-of-service issues involving pathological brace patterns that can trigger excessive CPU or memory consumption. Even though it is transitive and primarily in development tooling paths, it remains a real vulnerability if any code path accepts untrusted glob or pattern input.

Known Vulnerable Dependency: js-yaml==3.14.2 — 4 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); CVE-2026-59869 (js-yaml: YAML merge-key chains can force quadratic CPU consumption); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026) +1 more

High
Category
Supply Chain
Confidence
92% confidence
Finding
js-yaml 3.14.2 is included and has multiple reported CPU-exhaustion issues when parsing crafted YAML with merge keys or special structures. This is a real vulnerability in the dependency, though in this lockfile it appears under dev tooling, so exploitability depends on whether untrusted YAML is ever parsed during tests or build steps.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
95% confidence
Finding
brace-expansion 2.0.2 is also present and carries the same class of denial-of-service issues from crafted brace patterns that can consume large CPU or memory resources. Because there are multiple copies in the dependency graph, the exposure is broader across tooling that performs glob expansion.

Known Vulnerable Dependency: browserslist==4.28.1 — 2 advisory(ies): CVE-2026-73088 (Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.); CVE-2026-73089 (Browserslist: Unbounded memory growth (no cache eviction) via distinct query res)

High
Category
Supply Chain
Confidence
88% confidence
Finding
browserslist 4.28.1 is present and is reported with crash/prototype-write and unbounded-memory-growth issues when handling untrusted stats or adversarial queries. This is a real issue, but in this repository it is part of the development toolchain rather than the production skill path, which lowers the immediate exploitability.

Known Vulnerable Dependency: flatted==3.4.1 — 1 advisory(ies): CVE-2026-33228 (Prototype Pollution via parse() in NodeJS flatted)

High
Category
Supply Chain
Confidence
93% confidence
Finding
flatted 3.4.1 is reported as vulnerable to prototype pollution via parse(), which can let attacker-controlled input modify object prototypes and alter program behavior in dangerous ways. Although this instance is in dev tooling, prototype pollution is broadly dangerous because it can lead to logic bypass, unexpected property resolution, or downstream code execution conditions in some contexts.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
} as PredictedPerformance;
      } catch (error) {
        logger.warn('历史预测失败,使用规则预测', { error });
        return rulePrediction;
      }
    }
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
} as PredictedPerformance;
      } catch (error) {
        logger.warn('历史预测失败,使用规则预测', { error });
        return rulePrediction;
      }
    }
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
The document begins entirely in Chinese and continues in Chinese throughout, with no indication that users may choose another language or that the skill is intended only for a Chinese-speaking or region-specific audience. This can violate a language/locale policy when a skill imposes a specific language without user opt-in.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This markdown file presents all user-facing instructions, setup steps, and API guidance exclusively in Chinese. Under the policy for natural-language violations, forcing a specific language without user opt-in or a documented justification is a reportable issue.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This manifest’s user-facing natural-language fields, including name, description, capability descriptions, configuration descriptions, and examples, are presented exclusively in Chinese. Under the policy, forcing a specific language without offering a user choice or documenting a justified locale constraint is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code logs a preview of raw task content before routing, which can capture sensitive prompts, credentials, personal data, or proprietary text in application logs. Because this occurs automatically and there is no evidence here of consent, redaction, or log-level gating, normal use can create an unintended data exposure path.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The exported skill interface presents itself as a routing skill, but it also exposes privileged state-changing operations such as configuration mutation and shutdown. In an agent-skill context, this expands the attack surface beyond the advertised purpose and can let prompts or other skills reconfigure routing behavior persistently, disable safeguards, or induce denial of service.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
This TypeScript file uses Chinese throughout its descriptive comments and operational log messages, including initialization, error, and status text, with no indication that the skill is China-specific or that users can opt into this locale. Under the language/locale policy, forcing a specific language without user choice is a natural-language policy concern.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The function defensively derives a safe `modelId` from multiple possible `selectedModel` shapes, but the created `LearningSample` later uses `decision.selectedModel.modelId` directly. If untrusted or malformed routing data reaches this path, that direct dereference can throw at runtime or record inconsistent sample data, allowing an attacker or bad input to disrupt learning, suppress telemetry, or poison model-specific statistics. In this learning/router context, availability and integrity of routing feedback are important, so this mismatch is more dangerous than a mere style issue.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code sends `request.messages` in an HTTP POST body to a third-party model provider, which may include user or system data. Although there is developer-oriented debug logging, there is no confirmation prompt or clear user-facing disclosure in this file that request contents will be transmitted externally.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
With no manifest available, this skill's purpose is unknown, so spawning subprocesses via child_process and operating on local files/temp directories are capabilities that are not justified by any stated intent. The code does more than simple in-process request handling by invoking an external binary, reading environment-derived paths, and creating local workspace/temp state.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The code constructs a shell command by interpolating request.model, serialized messages, numeric options, and openclawPath into a single string passed to exec. Because shell metacharacters and quoting edge cases in user-controlled content can break out of the intended arguments, this creates a command injection risk that could lead to arbitrary command execution under the application's privileges.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The invoker writes full request payloads, including conversation messages, to predictable files under a shared temp directory. This can expose sensitive prompts, system messages, API-related metadata, or proprietary data to other local users/processes and leaves recoverable artifacts on disk if cleanup fails or the process crashes.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
Temporary request/session files store raw user and system messages on disk without confidentiality protections or explicit lifecycle guarantees. In this context, prompts may contain secrets, personal data, or internal instructions, so storing them in /tmp-like locations materially increases local disclosure risk and post-mortem data leakage.

External Transmission

Medium
Category
Data Exfiltration
Content
private inferBaseUrl(providerId: string): string {
    const urlMap: Record<string, string> = {
      'deepseek': 'https://api.deepseek.com',
      'openai': 'https://api.openai.com/v1',
      'anthropic': 'https://api.anthropic.com',
      'google': 'https://generativelanguage.googleapis.com',
      'mistral': 'https://api.mistral.ai/v1',
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
'openai': 'https://api.openai.com/v1',
      'anthropic': 'https://api.anthropic.com',
      'google': 'https://generativelanguage.googleapis.com',
      'mistral': 'https://api.mistral.ai/v1',
      'cohere': 'https://api.cohere.ai',
      'minimax': 'https://api.minimax.chat/v1',
      'qwen': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
'google': 'https://generativelanguage.googleapis.com',
      'mistral': 'https://api.mistral.ai/v1',
      'cohere': 'https://api.cohere.ai',
      'minimax': 'https://api.minimax.chat/v1',
      'qwen': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
      'baichuan': 'https://api.baichuan-ai.com/v1',
    };
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/openclaw/provider-discovery.ts:96

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/openclaw/model-adapter.ts:414