Back to skill

Security audit

Intent Engineering - Make Agents Optimize for What You Actually Want

Security checks for vulnerabilities and agentic risk

Overview

The skill is not malicious, but it should be reviewed because it changes future subagent prompts and can automatically load workspace JavaScript without strong safeguards.

Before installing, review the JavaScript loader and only use it in workspaces where memory files and lib/correction-tracker.js are trusted. Back up any existing INTENT.md and lib/agent-context-loader.js, and consider removing or gating the correction-tracker require() and treating memory snippets as untrusted reference text rather than instructions.

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

Warning
Location
references/agent-context-loader-template.js:147
Finding
Unsanitized Episodic Memory Is Injected into Subagent Instructions<![CDATA[ ## Vulnerability Details **File Location**: `references/agent-context-loader-template.js`, lines 147-174 and 240-251 **Vulnerability Type**: Prompt injection through untrusted stored content **Risk Level**: Medium ### Vulnerable Code ```javascript function getRecentEpisodicEntries(taskType, workspaceRoot, limit = 3) { const episodicDir = path.join(workspaceRoot, 'memory', 'episodic'); const keywords = keywordsFrom(taskType); const results = []; let files = []; try { files = fs.readdirSync(episodicDir) .filter(f => f.endsWith('.md')) .map(f => ({ name: f, fullPath: path.join(episodicDir, f), mtime: (() => { try { return fs.statSync(path.join(episodicDir, f)).mtimeMs; } catch (_) { return 0; } })() })) .sort((a, b) => b.mtime - a.mtime); } catch (_) { return []; } for (const file of files) { if (results.length >= limit) break; const lines = safeReadLines(file.fullPath); const content = lines.join('\n'); if (matchesKeywords(content, keywords)) { const snippet = lines.find(l => l.trim().length > 0) || ''; results.push({ file: file.name, snippet: snippet.trim().slice(0, 100) }); } } return results; } ``` The selected memory content is later inserted directly into the subagent context: ```javascript if (episodic.length > 0 || routing.length > 0) { parts.push('\n## Relevant Context'); parts.push('> Refer to INTENT.md for optimization priorities.\n'); if (episodic.length > 0) { parts.push('**Recent episodic memory:**'); for (const e of episodic) parts.push(`- ${e.file}: ${e.snippet}`); } if (routing.length > 0) { parts.push('**Recent routing decisions:**'); for (const r of routing) parts.push(`- ${r.task_type} → ${r.target} (${r.timestamp})`); } } const block = parts.join('\n'); const context = block.length <= 700 ? block : block.slice(0, 697) + '...'; ``` ### Technical Analysis The loader searches Markdown fil ...[truncated 2240 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all episodic-memory and routing-log content as untrusted data. 2. Do not concatenate retrieved memory into the same instruction channel as the task. Pass it through a structured, explicitly non-authoritative data field when the agent framework supports message-role separation. 3. Define a strict schema for memory entries and select only validated declarative fields instead of arbitrary Markdown lines. 4. Reject or neutralize instruction-like content, role markers, tool directives, prompt delimiters, and other control syntax before inclusion. 5. Place the original task and immutable safety requirements in a higher-priority channel than retrieved context. 6. Add an explicit statement that retrieved memory is untrusted reference material and must not override the task or system constraints. 7. Apply workspace permission controls so only trusted components can modify `memory/episodic`. 8. Add security tests using malicious memory entries to verify that stored text cannot redirect tasks or override safety requirements. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/agent-context-loader-template.js:40
Finding
Automatic Loading of a Workspace-Writable JavaScript Module Enables Code Execution<![CDATA[ ## Vulnerability Details **File Location**: `references/agent-context-loader-template.js`, lines 40-49 and 235-237 **Vulnerability Type**: Unsafe dynamic module loading **Risk Level**: Medium ### Vulnerable Code ```javascript function loadCorrectionPreamble(taskType, workspaceRoot) { try { const trackerPath = path.join(workspaceRoot, 'lib', 'correction-tracker.js'); let tracker; try { tracker = require(trackerPath); } catch (_) { return ''; } if (typeof tracker.buildCorrectionPreamble !== 'function') return ''; const agentType = detectAgentType(taskType); return tracker.buildCorrectionPreamble(agentType, workspaceRoot) || ''; } catch (_) { return ''; } } ``` The function is automatically invoked during context preparation: ```javascript // Inject correction preamble if correction-tracker is installed const correctionBlock = loadCorrectionPreamble(taskType, workspaceRoot); if (correctionBlock) parts.push(correctionBlock); ``` ### Technical Analysis `loadCorrectionPreamble()` constructs a module path inside the supplied workspace and passes it to Node.js `require()`. Loading a CommonJS module executes all of its top-level JavaScript immediately, before the code verifies that the exported `buildCorrectionPreamble` property is a function. No integrity verification, ownership check, trusted-module allowlist, explicit user approval, or sandbox boundary is applied. Consequently, a malicious `lib/correction-tracker.js` can execute arbitrary JavaScript in the OpenClaw process whenever `prepareAgentContext()` is called. The broad exception handlers suppress all loading and execution errors. This behavior can hide indicators of a malicious or defective module and makes detection and incident investigation more difficult. ### Attack Path 1. An attacker obtains the ability to create or replace `$OPENCLAW_WORKSPACE/lib/correction-tracker.js`. 2. The attacker places malicious top-level JavaScript in that file. The file does ...[truncated 1138 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic discovery and loading of JavaScript modules from mutable workspace directories. 2. Require explicit administrator configuration before enabling the correction-tracker integration. 3. Load plugins only from a trusted, read-only installation directory rather than from the workspace. 4. Verify plugin integrity using a pinned cryptographic digest or signed manifest before execution. 5. Validate file ownership and permissions and reject modules writable by untrusted users or processes. 6. Where extension support is necessary, run plugins in a separate least-privileged process with restricted filesystem, environment, child-process, and network access. 7. Replace executable plugin integration with a declarative data format when only correction rules or preamble text are required. 8. Log module validation and loading failures securely instead of suppressing every exception. 9. Add tests confirming that an unexpected workspace file cannot be executed merely by preparing subagent context. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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 (4)

Ae1

High
Category
analysis-evasion
Content
- `references/agent-context-loader-template.js` — Complete agent-context-loader.js implementation
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The loader automatically reads from workspace memory and routing logs and prepends that data to subagent task prompts. Because the injected context is selected by loose keyword matching and there is no consent, classification, or sanitization boundary, sensitive or adversarial content from prior workspace files can be propagated into new agent contexts, causing prompt injection, unnecessary data exposure, or cross-task information leakage.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The skill instructs users to copy files into the workspace root and lib directory, which modifies persistent workspace state, but it does not explicitly warn the user that installation will create or overwrite files. In a security-sensitive agent environment, unannounced file writes can lead to accidental clobbering of existing files or unexpected changes being applied without adequate review.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The docstring says the function optionally includes an active phase summary, alongside episodic memory and routing decisions. In the implementation, the function only loads intent summary, correction preamble, episodic entries, and routing decisions; there is no code that reads or appends any active phase summary.