Back to skill

Security audit

Clever Compact

Security checks for vulnerabilities and agentic risk

Overview

The plugin is a coherent memory/restore tool, but it injects unvalidated local files into system context and its documentation overstates automatic save behavior, so users should review it carefully before installing.

Install only if you are comfortable with a plugin that stores plaintext session summaries and automatically restores them into system context. Keep the memory directory trusted, review or delete compact-state files before sensitive work, do not store secrets there, and avoid following the changelog's root chown or models.json overwrite commands without backups and deployment-specific review.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T02 · Agent Memory Poisoning

Error
Location
index.js:36
Finding

Persistent System-Context Injection Through Unvalidated State Files

Content
View full analysis
f.startsWith(STATE_FILE_PREFIX) && f.endsWith(".md")) .sort() .reverse(); } catch { return null; } if (!files.length) return null; const match = files[0].match(/compact-state-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})\.md/); if (match) { const [, y, mo, d, h, mi] = match; const fileDate = new Date(`${y}-${mo}-${d}T${h}:${mi}:00`); const ageHours = (Date.now() - fileDate.getTime()) / (1000 * 60 * 60); if (ageHours > MAX_STATE_AGE_HOURS) return null; } return join(memDir, files[0]); } ``` ```javascript api.on("before_prompt_build", (_event, _ctx) => { if (hasInjectedThisSession) return; const stateFile = findMostRecentStateFile(); if (!stateFile) { hasInjectedThisSession = true; return; } try { const content = readFileSync(stateFile, "utf-8"); hasInjectedThisSession = true; return { prependSystemContext: `${RESTORE_HEADER}${content}\n\n---\n\n`, }; } catch { hasInjectedThisSession = true; return; } }); ``` ### Technical Analysis The plugin considers any directory entry beginning with `compact-state-` and ending with `.md` eligible for restoration. It then injects the file contents verbatim through `prependSystemContext`, which gives the content system-context placement. There is no content schema validation, provenance verification, integrity protection, instruction neutralization, or explicit boundary telling the model that the restored text is untrusted historical data. Consequently, instructions stored in a state file can influence future sessions with substantially greater authority ...[truncated 1884 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:36
Finding

Symbolic-Link Following Allows Unintended Local File Ingestion

Content
View full analysis
f.startsWith(STATE_FILE_PREFIX) && f.endsWith(".md")) .sort() .reverse(); } catch { return null; } if (!files.length) return null; const match = files[0].match(/compact-state-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})\.md/); if (match) { const [, y, mo, d, h, mi] = match; const fileDate = new Date(`${y}-${mo}-${d}T${h}:${mi}:00`); const ageHours = (Date.now() - fileDate.getTime()) / (1000 * 60 * 60); if (ageHours > MAX_STATE_AGE_HOURS) return null; } return join(memDir, files[0]); } ``` ```javascript try { const content = readFileSync(stateFile, "utf-8"); hasInjectedThisSession = true; return { prependSystemContext: `${RESTORE_HEADER}${content}\n\n---\n\n`, }; } catch { hasInjectedThisSession = true; return; } ``` ### Technical Analysis The plugin validates directory-entry names but does not establish that the selected entry is a regular file. Node.js `readFileSync` follows symbolic links by default. Therefore, a matching entry inside the memory directory can point to a readable file outside that directory. This contradicts the documented security claim that reads are confined to `OPENCLAW_WORKSPACE/memory/`. The path string is located under that directory, but its resolved target may not be. No canonical-path containment check, `lstat` check, no-follow file-open operation, ownership check, or regular-file validation is performed. The same weakness exists in both JavaScript and TypeScript implementations. ### Attack Path 1. An attacker or compromised local component obtains permission to create entries in the work ...[truncated 1140 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
CHANGELOG.md:27
Finding

Troubleshooting Documentation Recommends Privileged and Destructive Configuration Commands

Content
View full analysis
~/.openclaw/agents/main/agent/models.json openclaw gateway restart ``` ### Technical Analysis The ownership command recursively assigns the extension tree to `root:root` without determining which account operates the OpenClaw gateway. This can violate least-privilege deployment practices and make files inaccessible or unmaintainable by the actual service account. The configuration command replaces the complete `models.json` file with an empty provider object. It does not remove only the allegedly broken `codex` provider and does not create a backup. Any unrelated provider definitions, settings, or local configuration in that file are destroyed. These commands are documentation-driven rather than automatically executed by the plugin. Exploitation or damage therefore requires an administrator or user to follow the troubleshooting instructions. ### Attack Path 1. A user encounters an ownership warning or provider-related failure. 2. The user follows the changelog instructions, potentially with sufficient privileges for `chown`. 3. Recursive root ownership is applied to the entire extension directory, even if OpenClaw runs under another account. 4. The gateway may lose required write or read access, and normal updates may fail. 5. If the user follows ...[truncated 819 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The core declared purpose is only partially implemented. The code does support one important advertised behavior: injecting the most recent saved state at session start, with age limiting and one-time injection. However, the description prominently claims it 'flushes before every compact' and 'fixes all three' reset scenarios automatically. In the supplied code, there is no listener for compact/compaction events and no automatic persistence trigger; instead, writeState is merely exposed as api.fn('clever-compact:write', writeState), meaning another component or manual action must invoke it. That is a material description-to-behavior mismatch because the advertised automatic pre-compaction save capability is absent from this code chunk. File system access to the workspace memory directory is consistent with the plugin purpose, so the mismatch is about missing/overstated behavior rather than unrelated malicious capability.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code does partially match the description in that it injects prior state once at session start and avoids repeated per-turn injection. However, the main advertised behavior—automatic flushing before every compact and across /new/context resets—is not present. The file header and comments explicitly state there is no pre-compaction hook and that writing must be triggered explicitly via api.fn("clever-compact:write"), manual phrase, heartbeat, or cron pattern. Therefore the plugin is not fully automatic and does not itself ensure memory is preserved before compaction or /new. Additionally, the implementation reads and writes plaintext state files in a memory directory, which is a resource access capability not reflected in the declared permissions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill advertises automatic flushing before every compaction and resilience across resets, but the implementation explicitly requires a manual or external trigger to persist state. This mismatch can cause users or downstream agents to rely on protections that do not actually exist, leading to silent loss of context and potentially unsafe operation if important constraints, approvals, or prior security decisions are assumed to be preserved.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documented command echo '{"providers":{}}' > ~/.openclaw/agents/main/agent/models.json destructively overwrites configuration and can erase existing provider settings. In a security-sensitive agent environment, encouraging blind execution of overwrite commands without backup or warning increases the chance of accidental denial of service, configuration loss, or unsafe recovery practices.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The changelog describes a core architectural limitation in v2.1.0: there is no pre-compaction lifecycle hook, so writes are explicit only. If later documentation or marketing claims 'automatic flush before every compact,' that discrepancy can mislead users into relying on protections that may not actually occur, causing sensitive session state to be lost or stale at compaction boundaries.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · SECURITY.md (reported line 20)May include surrounding context.

md
## Capability Justifications

### `fs` (file read/write)
**Why required:** Memory plugins must persist state to disk. Clever Compact reads the most recent `compact-state-*.md` file on session start and writes a new one before compaction. No memory plugin can function without `fs`.  
**Scope:** Reads/writes only within `OPENCLAW_WORKSPACE/memory/`. No other paths are accessed.  
**Network:** None. All I/O is local disk only.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

The skill is explicitly designed to persist and reinject prior session state as system context across resets. That creates a real security risk because sensitive data, prior instructions, or poisoned prompt content can survive context boundaries and be automatically reintroduced with elevated authority on the next session, increasing the blast radius of prompt injection or accidental secret retention.

Content

Scanner excerpt · SKILL.md (reported line 28)May include surrounding context.

md
**Session restore (automatic):** At the start of every session — including after `/new` and after compaction — Clever Compact checks for a recent compact-state file. If one exists (written within 72 hours), it injects the content as system context on the **first turn only**. Your agent wakes up oriented with zero per-turn token overhead.

**State write (triggered by you):** OpenClaw doesn't expose a pre-compaction lifecycle hook yet, so the write side is explicit — not automatic. Three ways to trigger it; pick one (see below).

---

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation presents the plugin as automatically fixing memory loss after compaction and /new, while the code comments admit writes must be triggered explicitly. In a security-sensitive agent environment, misleading automation claims are dangerous because they encourage operators to trust continuity of state that may never have been written, which can bypass expected safeguards or recovery steps.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The plugin automatically reads a plaintext memory file and injects its contents into system context at session start without any runtime disclosure, consent prompt, or provenance check. This creates a prompt-injection and sensitive-data exposure risk: anything written into the state file, including hostile instructions or private information, is elevated into trusted context on the next session.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The entry states 'No functional changes; plugin behavior identical to v2.2.4' while also documenting that SECURITY.md was added to justify capabilities for review. This is a documentation-level contradiction about what changed in the release contents, though it does not indicate hidden runtime behavior or a security-relevant code/intent mismatch in the skill itself.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.