Back to skill

Security audit

YandexGPT Proxy

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it starts an unauthenticated local proxy using your Yandex credentials and includes unsafe scripts that modify configuration and use shared temporary files.

Install only after reviewing or patching the scripts. Use a dedicated least-privilege Yandex API key, set ~/.openclaw/yandexgpt.env to owner-only permissions, back up openclaw.json before running patch-config.sh, avoid untrusted OPENCLAW_CONFIG values, and do not run the proxy on shared machines unless local authentication, request limits, and private runtime files are added.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/patch-config.sh:2
Finding

Arbitrary JavaScript Execution Through Configuration Path Injection

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/start.sh:4
Finding

Predictable Shared Temporary Files Permit Symlink-Based File Overwrite

Content
View full analysis
/dev/null; then echo "YandexGPT proxy already running (PID $(cat "$PID_FILE"))" exit 0 fi nohup node "$SCRIPT_DIR/../src/proxy.js" > /tmp/yandexgpt-proxy.log 2>&1 & echo $! > "$PID_FILE" ``` ### Technical Analysis The script writes to fixed paths in the globally shared `/tmp` directory: ```text /tmp/yandexgpt-proxy.pid /tmp/yandexgpt-proxy.log ``` It does not verify file ownership, reject symbolic links, create files atomically, or use a private runtime directory. Shell redirection follows symbolic links and opens the target with truncation. An attacker who can create entries in `/tmp` before the script runs can redirect these writes to another file writable by the victim. The PID file is also created using the caller's ambient `umask`, potentially allowing other local users to replace or modify it. ### Attack Path 1. A local attacker predicts the fixed PID or log filename. 2. Before the victim starts the proxy, the attacker creates one of those paths as a symbolic link to a victim-owned file. 3. The victim runs `scripts/start.sh`. 4. Shell redirection follows the log symlink and truncates or writes proxy output to the linked target. Similarly, writing the PID can overwrite the PID symlink target with the new process ID. 5. The targeted file is corrupted using the victim's filesystem privileges. ### Impact Assessment An attacker may truncate or overwrite files writable by the invoking user. Depending on the chosen target, this can cause data loss, corrupt application configuration, disrupt shell or application behavior, or manipulate runtime state. Exploitation does not inherently bypass filesystem permissions: the victim must already have write access to the final sy ...[truncated 17 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/stop.sh:2
Finding

Untrusted PID File Can Be Used to Signal an Unrelated Process

Content
View full analysis
/dev/null && echo "Stopped" || echo "Not running" rm -f "$PID_FILE" else echo "Not running" fi ``` Related trust in the same PID file also occurs in `scripts/start.sh` and `scripts/status.sh`: ```bash if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then ``` ### Technical Analysis The scripts trust a fixed PID file in `/tmp` without checking: - Whether the file is owned by the current user. - Whether it is a regular file rather than a symbolic link. - Whether its contents are a single valid positive integer. - Whether the referenced process is the YandexGPT proxy. - Whether PID reuse has occurred since the file was created. A local attacker who can modify or replace the PID file can cause `stop.sh` to send a termination signal to another process running under the victim's account. The same manipulation can make `start.sh` incorrectly report that the proxy is already running. ### Attack Path 1. The attacker identifies the PID of another process owned by the victim. 2. The attacker replaces or modifies `/tmp/yandexgpt-proxy.pid` so it contains that PID. 3. The victim invokes `scripts/stop.sh`. 4. The script reads the attacker-selected PID and invokes `kill` without checking process identity. 5. The unrelated process receives the termination signal. Alternatively, an attacker can place the PID of any currently running process in the file and cause `start.sh` to skip proxy startup. ### Impact Assessment The direct impact is termination or disruption of another process owned by the invoking user. This can cause loss of unsaved data or availability failures. The attacker cannot normally signal processes belonging to more privileged user ...[truncated 61 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
src/proxy.js:112
Finding

Unauthenticated Local Clients Can Consume the Configured Yandex API Account

Content
View full analysis
{ const url = req.url; if (url === "/v1/models" && req.method === "GET") { res.writeHead(200, { "Content-Type": "application/json" }); res.end( JSON.stringify({ object: "list", data: Object.keys(MODELS).map((id) => ({ id, object: "model", owned_by: "yandex" })), }), ); return; } if (url === "/v1/chat/completions" && req.method === "POST") { try { const body = JSON.parse(await readBody(req)); const ycBody = translateRequest(body); const ycResp = await ycRequest(ycBody); if (ycResp.error) { res.writeHead(502, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: ycResp.error })); return; } const openaiResp = translateResponse(ycResp, body.model || "yandexgpt-lite"); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(openaiResp)); } catch (e) { res.writeHead(500, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: { message: e.message } })); } return; } res.writeHead(404); res.end("Not found"); }); ``` ### Technical Analysis The completion endpoint does not inspect or validate an `Authorization` header or any other client credential. Although the server binds to `127.0.0.1`, loopback binding is not an authentication boundary. Any process operating in the same network namespace and able to connect to the port can submit requests that the proxy forwards using the configured `YANDEX_API_KEY`. The OpenClaw configuration explicitly uses the placeholder API key `not-needed`, confirming that no meaningful client credential is expected by the proxy. ### Attack Path 1. An untrusted loc ...[truncated 920 chars]
Remediation
View remediation
` for completion requests. - Compare credentials using a constant-time comparison after validating equal lengths. - Return HTTP 401 for missing or invalid credentials. - Apply request rate limits and concurrency limits. - If only one trusted application needs access, consider a Unix domain socket with restrictive filesystem permissions instead of a TCP listener. - Keep the loopback bind as defense in depth; do not expose the service on all interfaces. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/proxy.js:104
Finding

Unbounded HTTP Request Buffering Enables Memory Exhaustion and Slow-Request Denial of Service

Content
View full analysis
{ let chunks = []; req.on("data", (c) => chunks.push(c)); req.on("end", () => resolve(Buffer.concat(chunks).toString())); }); } ``` The unbounded function is used by the completion endpoint: ```js const body = JSON.parse(await readBody(req)); ``` ### Technical Analysis The server stores every incoming request-body chunk in an array and concatenates the complete body in memory. It does not enforce a maximum body size, reject oversized `Content-Length` values, stop chunked requests after a threshold, or handle aborted and stream-error conditions. No request timeout is configured in the audited code. A caller can therefore send a very large body to consume memory or keep connections open while transmitting bytes slowly. Because the service is a single Node.js process, memory exhaustion can terminate the entire proxy. ### Attack Path 1. A local attacker connects to the completion endpoint. 2. The attacker sends a very large body, optionally using chunked transfer encoding to avoid declaring its final size. 3. The proxy appends every chunk to memory without enforcing a limit. 4. `Buffer.concat()` creates an additional contiguous allocation when the body ends. 5. Process memory grows until the proxy slows down, encounters an out-of-memory condition, or is terminated. 6. Alternatively, the attacker opens multiple connections and sends body data slowly, retaining server resources for an extended period. ### Impact Assessment Successful exploitation can make the proxy unavailable to legitimate users and can increase memory and connection consumption on the host. The direct attack surface is limited to clients able to reach the loopback listener, but no authentication is required. ]]>
Remediation
View remediation
{ const chunks = []; let received = 0; req.on("data", (chunk) => { received += chunk.length; if (received > maxBytes) { const error = new Error("Request body too large"); error.statusCode = 413; reject(error); req.destroy(); return; } chunks.push(chunk); }); req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); req.on("error", reject); req.on("aborted", () => reject(new Error("Request aborted"))); }); } ``` Also: - Reject an oversized declared `Content-Length` before reading the body. - Configure request, header, keep-alive, and socket timeouts. - Limit concurrent completion requests. - Return HTTP 400 for malformed JSON and HTTP 413 for oversized requests instead of a generic HTTP 500. - Apply rate limiting per authenticated client. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:3
Finding

Credential Environment File Is Created Without Restrictive Permissions

Content
View full analysis
"$ENV_FILE" << 'EOF' YANDEX_API_KEY= YANDEX_FOLDER_ID= YANDEX_PROXY_PORT=8444 EOF ``` ### Technical Analysis The setup script creates the environment file through ordinary shell redirection without setting a restrictive `umask` or explicitly changing its mode. The resulting permissions depend on the caller's ambient `umask`. With a common `022` umask, a newly created file normally has mode `0644`, making it readable by other local users. The user is instructed to place `YANDEX_API_KEY` in this file, so a template that remains permissively readable becomes a plaintext credential exposure after being populated. The script also does not verify that the parent directory or existing path is securely owned. ### Attack Path 1. The victim runs `scripts/setup.sh` with a permissive `umask`. 2. The script creates `~/.openclaw/yandexgpt.env` with permissions such as `0644`. 3. The victim adds a valid Yandex API key and folder ID as instructed. 4. Another local user reads the environment file. 5. The attacker uses the exposed credential directly against allowed Yandex Cloud APIs. ### Impact Assessment Credential disclosure can allow use of the Yandex API key outside the local proxy. The exact cloud impact depends on the permissions associated with that key. At minimum, an attacker may consume model quota; broader cloud access is possible if the key has additional permissions. The issue requires another local account or process with filesystem read access. ]]>
Remediation
View remediation
"$ENV_FILE" chmod 600 "$ENV_FILE" cat > "$ENV_FILE" <<'EOF' YANDEX_API_KEY= YANDEX_FOLDER_ID= YANDEX_PROXY_PORT=8444 EOF fi ``` Additionally: - Verify that the directory and file are owned by the current user. - Reject symbolic links and unexpected non-regular files. - Check and repair the permissions of an existing credential file. - Document least-privilege requirements for the Yandex API key and recommend a key dedicated to this proxy. - Prefer a platform credential store where practical. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose is a translation proxy, but the skill also instructs modification of OpenClaw configuration and its described behavior is not substantiated in the provided file. This mismatch is dangerous because deceptive or incomplete descriptions can cause users to approve execution or config changes they would not otherwise allow, enabling persistence or unsafe provider registration.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares shell and environment-variable dependent behavior but does not declare any explicit tool scope or permissions. That creates an authorization and transparency gap: a user or orchestrator may treat the skill as low-risk documentation while it actually invokes scripts, reads secrets, and starts local services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script unconditionally rewrites the user's OpenClaw configuration file and injects a new provider entry without any confirmation, backup, or dry-run behavior. This is dangerous because it silently changes persistent user settings, can overwrite existing configuration state, and may redirect future model traffic to a local HTTP endpoint the user did not explicitly approve.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% 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 · scripts/start.sh (reported line 11)May include surrounding context.

sh
exit 0
fi

nohup node "$SCRIPT_DIR/../src/proxy.js" > /tmp/yandexgpt-proxy.log 2>&1 &
echo $! > "$PID_FILE"
echo "YandexGPT proxy started (PID $!)"
sleep 1

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This shell script kills the process identified in the PID file and then removes that file, which are safety-relevant actions affecting system state. There is no confirmation prompt, explanatory comment, or user-facing warning beyond a generic status message, so the destructive behavior is not disclosed in the file itself.

Content

No source excerpt is available for this finding.