Back to skill

Security audit

task-progress-stream

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent progress streamer, but it automatically injects raw command or log output into an OpenClaw chat session, which can expose sensitive log text or influence the agent context.

Install only if you are comfortable with selected command output or log-file contents being sent into the chosen OpenClaw chat session. Do not point it at logs that may contain secrets, personal data, proprietary output, or attacker-controlled text, and prefer low-privilege sessions for automated updates.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/task_progress_stream.js:252
Finding
Untrusted Log Content Is Injected into an Agent Chat Session<![CDATA[ ## Vulnerability Details **File Location**: `scripts/task_progress_stream.js`, lines 175, 198, 252–273, 319–321, and 395–397 **Vulnerability Type**: Prompt injection through untrusted process or log output **Risk Level**: High ### Vulnerable Code ```js // Lines 175 and 198: untrusted log content is embedded in chat messages. if (this.lastLine) rows.push(`- 最新日志: \`${truncate(this.lastLine, 160)}\``); ... if (this.lastLine) rows.push(`- 最后一行日志: \`${truncate(this.lastLine, 200)}\``); // Lines 252–273: the resulting message is injected into a chat session. function spawnOpenClawInject(session, message) { return new Promise((resolve) => { const payload = JSON.stringify({ sessionKey: session, message }); const child = spawn( "openclaw", ["gateway", "call", "chat.inject", payload], { stdio: ["ignore", "pipe", "pipe"], } ); let out = ""; let err = ""; child.stdout.on("data", (d) => (out += d.toString())); child.stderr.on("data", (d) => (err += d.toString())); child.on("close", (code) => { resolve({ code, out, err }); }); child.on("error", (e) => { resolve({ code: -1, out: "", err: String(e) }); }); }); } // Lines 319–321: command output reaches the progress state. function onLine(line) { state.pushLine(line); logStream.write(line + "\n"); flushState(); } // Lines 395–397: tailed file content also reaches the progress state. for (const line of text.split(/\r?\n/)) { if (line.trim()) state.pushLine(line); } ``` ### Technical Analysis The script treats output from a spawned process and content from a tailed file as trusted chat content. `pushLine()` stores each input line in `state.lastLine`. The `summaryText()` and `finalText()` methods then interpolate that value into a Markdown message, which is submitted to the selected OpenClaw session through `chat.inject`. The only content control applied is length truncation. Truncation does not escape Markdow ...[truncated 1949 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not inject raw process output or file content into an agent-interpreted chat message. 2. Prefer a structured, UI-only progress API whose content is not added to the language model's instruction context. 3. If no such API exists, omit `lastLine` from injected messages and transmit only strictly parsed numeric fields such as step, epoch, loss, and percentage. 4. Apply strict schemas and bounds to every transmitted field. Reject unexpected values rather than forwarding arbitrary text. 5. If log excerpts must be displayed, escape Markdown delimiters and control characters and render the excerpt in a non-agent-visible interface. Escaping alone must not be treated as a complete prompt-injection defense. 6. Mark all externally derived content as untrusted data and ensure the receiving system enforces that distinction independently of natural-language labels. 7. Restrict allowed destination sessions and require explicit user confirmation before sending externally controlled content to a privileged session. 8. Apply least privilege and confirmation gates to any tools available in sessions receiving automated updates. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/task_progress_stream.js:386
Finding
Unbounded Log-File Allocation Can Cause Memory Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/task_progress_stream.js`, lines 386–390 **Vulnerability Type**: Unbounded memory allocation from a monitored file **Risk Level**: Medium ### Vulnerable Code ```js if (stat.size > position) { const fd = fs.openSync(file, "r"); const len = stat.size - position; const buf = Buffer.alloc(len); fs.readSync(fd, buf, 0, len, position); fs.closeSync(fd); position = stat.size; const text = buf.toString("utf-8"); for (const line of text.split(/\r?\n/)) { if (line.trim()) state.pushLine(line); } flushState(); } ``` ### Technical Analysis The tailing loop calculates the entire unread file size and allocates one buffer of exactly that size. There is no maximum delta, chunk-size limit, file-size policy, or streaming backpressure. If the monitored file grows substantially between polling iterations, `Buffer.alloc(len)` may request enough memory to trigger allocation failure, excessive garbage collection, process termination, or host-level memory pressure. Conversion of the buffer to a string and splitting it into an array of lines can require additional memory beyond the original buffer. The synchronous file operations also block the Node.js event loop for the duration of allocation, reading, decoding, and line splitting. ### Attack Path 1. A user starts `tail` mode on a file that an attacker or untrusted process can modify. 2. The attacker appends a very large amount of data between polling cycles, or supplies a large file after the script has reset its position because of truncation or rotation. 3. The next iteration computes `len` as the complete unread size. 4. The script calls `Buffer.alloc(len)` and then creates further in-memory representations through `toString()` and `split()`. 5. The streamer becomes unresponsive or terminates because of memory exhaustion; significant host resource pressure may also occur. ### Impact Assessment Exploitation can cause denial of service a ...[truncated 412 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read appended data incrementally using a fixed-size buffer or `fs.createReadStream()` with explicit byte ranges. 2. Define a maximum number of bytes processed during each polling cycle, such as 1–8 MiB, and defer remaining data to later cycles. 3. Enforce maximum line lengths and discard or truncate oversized lines before storing them. 4. Avoid splitting an entire large string at once. Use an incremental line parser that retains only a bounded partial-line buffer. 5. Set a policy for excessive backlog, such as skipping old bytes while recording a warning that output was dropped. 6. Replace synchronous file operations with asynchronous streaming operations to prevent event-loop blocking. 7. Ensure file descriptors are closed with `try/finally` or equivalent resource-management logic when read operations fail. 8. Consider file ownership and permission checks before tailing files located in directories writable by untrusted users. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill is presented as a progress-streaming utility, but the documented behavior includes executing arbitrary shell commands, reading local files, writing artifacts, and controlling child processes. That gap matters because users may invoke it expecting passive monitoring, while it actually introduces powerful execution and filesystem capabilities that increase the risk of command injection, data exposure, and unintended system modification.

Ae1

High
Category
analysis-evasion
Content
node ./scripts/task_progress_stream.js run \
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill description does not clearly warn that command output and tailed log contents will be injected into the chat UI. This can expose secrets, credentials, personal data, or proprietary information present in stdout/stderr or logs to downstream chat consumers, and can also enable prompt-injection-style content to enter the conversation context.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The skill emits injected progress summaries and final status messages using fixed Chinese text such as '进度更新', '状态', and '已结束'. This imposes a specific language on users without offering a locale choice or documenting that the tool is intended only for a Chinese-language environment.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/task_progress_stream.js:255