Back to skill

Security audit

Agent Progressive Memory

Security checks for vulnerabilities and agentic risk

Overview

The skill fits a group-memory workflow, but it adds persistent hooks that automatically write tracking data and use unsafe file handling that can overwrite writable files through symlinks.

Review this before installing. It is not showing exfiltration or malicious intent, but it will add persistent OpenClaw hooks, store local session/activity metadata, and automatically run before compaction if enabled. Install only in trusted workspaces, avoid shared or attacker-writable memory directories, and prefer a version that rejects symlinks, validates canonical paths, limits stored metadata, and provides clear disable/uninstall and retention controls.

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

T09 · Insecure Skill Coding Practices

Error
Location
hooks/remem-flush/handler.js:57
Finding
Filesystem Boundary Bypass Through Symbolic Links in remem-flush Hook## Vulnerability Details **File Location**: `hooks/remem-flush/handler.js`, lines 57-59, 89-90, 103-104, and 147 **Vulnerability Type**: Symbolic-link following and arbitrary writable-file clobbering **Risk Level**: High ### Vulnerable Code ```javascript function writeJSON(filePath, data) { FS.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8'); } ``` ```javascript const memoryDir = PATH.join(workspaceDir, 'memory'); const flushStatePath = PATH.join(memoryDir, 'flush-state.json'); ``` ```javascript const groupDir = PATH.join(memoryDir, 'groups', group); const attentionFile = PATH.join(groupDir, 'attention.md'); ``` ```javascript writeJSON(flushStatePath, newState); ``` Group enumeration also follows symbolic links: ```javascript return FS.readdirSync(groupsDir) .filter(f => { const stat = FS.statSync(PATH.join(groupsDir, f)); return stat.isDirectory(); }); ``` ### Technical Analysis The hook derives paths from the configured workspace but does not verify that their canonical targets remain inside the workspace memory directory. `FS.statSync()` follows symbolic links during group discovery, while `FS.writeFileSync()` follows an existing symbolic link at `memory/flush-state.json`. There is no use of `lstatSync()`, `realpathSync()`, canonical path containment checks, or no-follow file-opening semantics. Consequently, possession of write access to the workspace is sufficient to redirect hook filesystem operations outside the intended `memory` hierarchy. A symbolic-link group directory can cause the hook to inspect matching Markdown files outside `memory/groups`. Their canonical paths and modification times can then be recorded in `flush-state.json`. More critically, replacing `memory/flush-state.json` with a symbolic link causes the hook to overwrite the link target with generated JSON. ### Attack Path 1. An attacker obtains the ability to create or replace file ...[truncated 1473 chars]
Remediation
## Remediation Suggestions - Use `FS.lstatSync()` for group entries and reject every symbolic link before treating an entry as a directory. - Resolve both the intended memory root and each accessed path with `FS.realpathSync()`, then verify that the resolved target is equal to or nested beneath the canonical memory root. - Refuse to write when `flush-state.json` already exists as a symbolic link, directory, device, or other non-regular file. - Open the destination with no-follow and exclusive semantics where supported, such as `O_NOFOLLOW`, rather than passing the path directly to `writeFileSync()`. - Write to a newly created temporary regular file in the same validated directory, set restrictive permissions such as `0600`, call `fsync` when durability matters, and atomically rename it over the validated destination. - Revalidate the destination immediately before replacement to reduce time-of-check/time-of-use race conditions. - Run the hook under a dedicated low-privilege account with write access limited to the intended workspace memory directory.

T09 · Insecure Skill Coding Practices

Error
Location
hooks/precompact-remem/handler.js:60
Finding
Filesystem Boundary Bypass Through Symbolic Links in precompact-remem Hook## Vulnerability Details **File Location**: `hooks/precompact-remem/handler.js`, lines 60-62, 78-79, 94-95, and 141 **Vulnerability Type**: Symbolic-link following and arbitrary writable-file clobbering **Risk Level**: High ### Vulnerable Code ```javascript function writeJSON(filePath, data) { FS.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8'); } ``` ```javascript const memoryDir = PATH.join(workspaceDir, 'memory'); const flushStatePath = PATH.join(memoryDir, 'flush-state.json'); ``` ```javascript const groupDir = PATH.join(memoryDir, 'groups', group); const files = ['attention.md', 'experience.md', 'project.md', 'people.md']; ``` ```javascript writeJSON(flushStatePath, newState); ``` Group enumeration also follows symbolic links: ```javascript return FS.readdirSync(groupsDir).filter(f => { const stat = FS.statSync(PATH.join(groupsDir, f)); return stat.isDirectory(); }); ``` ### Technical Analysis The pre-compaction hook trusts paths beneath the event-provided `workspaceDir` without verifying their canonical destinations. `FS.statSync()` follows symbolic links when discovering group directories, and `FS.writeFileSync()` follows a symbolic link at the state-file destination. The hook performs no symbolic-link rejection, canonical path containment enforcement, or regular-file validation. A workspace-controlled link can therefore redirect reads and writes beyond the intended memory directory. The vulnerability is automatically reachable when an eligible session enters compaction, increasing exposure compared with a flow that requires an explicit maintenance operation. ### Attack Path 1. An attacker with workspace write access replaces `memory/flush-state.json` with a symbolic link to another file writable by the OpenClaw process. 2. The attacker waits for or induces session compaction after the session reaches at least 10 messages or 1,000 tokens. 3. The `session:co ...[truncated 1146 chars]
Remediation
## Remediation Suggestions - Enumerate entries with `FS.lstatSync()` and reject symbolic links and non-directory objects. - Canonicalize the configured workspace and memory roots, then verify every resolved group, input file, and output file remains beneath the canonical memory root. - Ensure `memory/flush-state.json` is a regular file owned by the expected account and is not a symbolic link. - Use no-follow file-opening flags where the platform supports them. - Implement secure atomic writes through a uniquely created temporary regular file in the validated directory, restrictive permissions, and an atomic rename. - Protect against race conditions by performing validation on opened file descriptors or rechecking immediately before the final operation. - Apply least-privilege filesystem permissions so the hook account cannot modify unrelated application or user files. - Consider serializing writes from both hooks to prevent state races because both modify the same `flush-state.json` file.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented behavior includes intercepting a /remem trigger and performing background flush/delta tracking, which is not disclosed by the declared purpose of progressive memory retrieval. Hidden command-triggered persistence features increase the chance that users will invoke behavior with privacy and integrity consequences they did not consent to.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented behavior includes intercepting a /remem trigger and performing background flush/delta tracking, which is not disclosed by the declared purpose of progressive memory retrieval. Hidden command-triggered persistence features increase the chance that users will invoke behavior with privacy and integrity consequences they did not consent to.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The manifest description and the main protocol text are presented entirely in Chinese and use mandatory language about how the agent must behave. Under the policy, forcing a specific language or locale without user opt-in is a natural-language policy violation unless the locale constraint is clearly justified, which is not stated here.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The instructions discuss writing decisions and task-state changes back to multiple memory files, but they do not prominently warn that conversation content may be persisted to disk. Insufficient disclosure can lead to accidental retention of sensitive project, personnel, or operational data that users believed would remain ephemeral.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The skill expands from scoped memory retrieval into automatic writeback, periodic flushing, pending-item tracking, and session-state management. That broadening increases attack surface and privacy risk because more conversation content can be transformed into durable storage outside the narrow context the user likely expected.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
Cron-style automatic flushing is not necessary for on-demand memory access and can cause unattended persistence of session-derived information at preset times. Background scheduling is especially risky when users may not remember that sensitive intermediate discussion, names, or project details can be written without a fresh review at write time.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The recommended hook and cron-based auto-flush enable automatic persistence of session context without fresh confirmation at the moment of writing. In a group-memory context, this can silently capture sensitive discussions, identities, project decisions, or temporary hypotheses and store them durably, making privacy leaks and inaccurate memory poisoning more likely.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The instructions direct installation of persistent hooks under the user's home directory, which changes the runtime environment beyond the immediate skill execution. Persistent hooks can continue to observe or act on future sessions, so installing them from a memory protocol meaningfully increases long-term exposure and the blast radius of any faulty or abusive behavior.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The handler persists session metadata to disk in flush-state.json, including session_key, message_count, token_count, timing data, and a list of changed memory files, without any user-facing consent, minimization, or retention controls. In a collaborative memory skill, this creates a privacy risk because local disk artifacts can reveal conversation activity patterns and identifiers even if the actual message contents are not stored.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The hook explicitly modifies persisted memory files when a user sends `/remem`, but the documentation does not clearly warn that this command causes stateful writes. In a group-memory context, silent mutation of shared memory can surprise users, lead to unintended persistence of sensitive or incorrect content, and reduce auditability of who caused memory changes.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest describes a protocol to prevent agents from loading all memory at once or bypassing an index in group-chat memory access. In code, the handler reacts to /remem by scanning memory group directories, comparing mtimes, and writing a flush-state file; it does not read indexed memory content for disclosure, enforce staged access, or mediate what memory the agent can load.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The hook persistently writes workspace metadata in response to a chat command without any explicit notice, consent, or confirmation. In a collaborative memory skill, silent state mutation can create auditability and privacy issues, especially if users do not expect that invoking /remem changes on-disk records tied to sessions and memory groups.

Intent-Code Divergence

Low
Confidence
88% confidence
Finding
The documentation suggests a memory flush operation, which ordinarily implies clearing, rotating, or emitting memory content. The code instead inspects directories and file mtimes, stores summary state in flush-state.json, and logs completion without flushing memory contents or enforcing disclosure behavior.

Static analysis

No suspicious patterns detected.