Back to skill

Security audit

Dream

Security checks for vulnerabilities and agentic risk

Overview

This memory skill is mostly purpose-aligned, but it keeps personal memory indefinitely, can restore forgotten items, runs silent memory updates, and ships script commands with real local file-safety flaws.

Review this carefully before installing. It is not clearly malicious, but it is a high-retention memory system: archived memories are kept forever, 'forget' does not purge the archive, forgotten topics can re-enter memory, and some actions run silently. The included script should be fixed before use, especially path scoping for --atomic-write and safe parsing of last-review.txt.

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)

T09 · Insecure Skill Coding Practices

Error
Location
dream-tools.sh:454
Finding
Arbitrary Python Code Execution Through Unvalidated Timestamp State<![CDATA[ ## Vulnerability Details **File Location**: `dream-tools.sh`, lines 454-463 **Vulnerability Type**: Python source injection through unsafe string interpolation **Risk Level**: High ### Vulnerable Code ```bash local last_review="Never" local hours_since="-" if [[ -f "$last_review_file" ]]; then last_review=$(cat "$last_review_file") # Calculate hours since last distillation if command -v python3 &>/dev/null; then hours_since=$(python3 -c " from datetime import datetime last = datetime.strptime('$last_review', '%Y-%m-%d %H:%M') diff = datetime.now() - last print(int(diff.total_seconds() / 3600)) " 2>/dev/null || echo "-") fi fi ``` ### Technical Analysis The contents of `meta/last-review.txt` are read into `last_review` and interpolated directly into source code passed to `python3 -c`. Shell quoting does not make this safe because the value is inserted inside a Python string literal. A malicious timestamp containing a single quote followed by valid Python statements can terminate the intended string and inject arbitrary Python code. For example, a crafted value can import `os` and invoke system commands before commenting out the remainder of the generated line. The script neither validates the timestamp format before interpolation nor passes the value through a non-code channel such as a command-line argument or environment variable. The exception fallback does not prevent exploitation because injected statements execute before a later parsing error is handled by the shell. ### Attack Path 1. An attacker, another local process, or an agent operation modifies: `DREAM_VAULT_PATH/meta/last-review.txt`. 2. The attacker supplies content designed to terminate the Python string and insert Python statements. 3. The user or agent invokes: ```bash dream-tools.sh --status ``` 4. `cmd_status` places the malicious file content directly into the program passed to `python3 -c`. 5. Python executes the injected statements with ...[truncated 541 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Never interpolate file content into executable Python source. Pass the timestamp as a positional argument: ```bash if [[ "$last_review" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}$ ]]; then hours_since=$(python3 - "$last_review" <<'PY' from datetime import datetime import sys last = datetime.strptime(sys.argv[1], "%Y-%m-%d %H:%M") diff = datetime.now() - last print(int(diff.total_seconds() / 3600)) PY ) || hours_since="-" else hours_since="-" fi ``` Additional hardening should include: - Require the timestamp to match the exact expected syntax before parsing. - Reject multiline input and unexpected file sizes. - Ensure the state file and its parent directory are writable only by the OpenClaw account. - Avoid constructing source code dynamically from any persisted state. - Add regression tests using quotes, newlines, semicolons, Python expressions, and malformed timestamps. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
dream-tools.sh:88
Finding
Unrestricted Arbitrary File Replacement in Atomic Write Command<![CDATA[ ## Vulnerability Details **File Location**: `dream-tools.sh`, lines 88-114 **Vulnerability Type**: Missing destination-path authorization and insufficient symlink protection **Risk Level**: Medium ### Vulnerable Code ```bash # --atomic-write <target-file> <tmp-file> # Verifies tmp-file exists and does not exceed the hard limit, then atomically replaces target-file # For MEMORY.md targets, also enforces character count; other files only check existence cmd_atomic_write() { local target="${1:-}" local tmpfile="${2:-}" [[ -z "$target" || -z "$tmpfile" ]] && die "--atomic-write requires <target> <tmpfile>" [[ ! -f "$tmpfile" ]] && die "tmp file does not exist: $tmpfile" # If target is MEMORY.md, check character count limit if [[ "$(realpath "$target" 2>/dev/null)" == "$(realpath "$MEMORY_MD" 2>/dev/null)" ]] || \ [[ "$target" == *"MEMORY.md" ]]; then local size size=$(wc -c < "$tmpfile" | tr -d ' ') if [[ $size -gt $MEMORY_HARD_LIMIT ]]; then die "Write aborted: tmp file size ${size} chars exceeds hard limit ${MEMORY_HARD_LIMIT}. Please compress content first." fi log "MEMORY.md write validation passed: ${size}/${MEMORY_HARD_LIMIT} chars" fi ensure_dir "$target" # mv is atomic on the same filesystem mv "$tmpfile" "$target" log "Atomic write complete: $target" } ``` ### Technical Analysis The `--atomic-write` interface accepts an arbitrary target path and moves the supplied temporary file to that location. It does not verify that the destination is inside `OPENCLAW_WORKSPACE` or `DREAM_VAULT_PATH`. The `realpath` comparison is used only to decide whether to enforce a size limit. It is not an authorization check. The suffix test, `"$target" == *"MEMORY.md"`, likewise does not restrict the destination. Absolute paths and traversal paths therefore remain accepted. This behavior contradicts the README claim that file operations are strictly scoped to t ...[truncated 1294 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Restrict writes to an explicit allowlist. If the command exists only to maintain active memory, permit exactly the canonical `MEMORY_MD` destination. If multiple destinations are necessary: 1. Canonicalize the destination and every allowed root. 2. Require the canonical destination to equal `MEMORY_MD` or be a descendant of `OPENCLAW_WORKSPACE` or `DREAM_VAULT_PATH`. 3. Reject destination symlinks and validate parent components to prevent symlink traversal. 4. Reject `..`, empty basenames, device paths, and destinations outside configured roots. 5. Create temporary files in the destination directory so that `mv` is genuinely atomic on the same filesystem. 6. Give generated temporary files restrictive permissions, such as mode `0600`. 7. Validate the expected format and size for every supported target type, not only filenames ending in `MEMORY.md`. A canonical path check should enforce a directory boundary rather than using a simple textual prefix. ]]>

T02 · Agent Memory Poisoning

Warning
Location
Skill.md:264
Finding
Forget Operation Permanently Retains and Automatically Restores Removed Memories<![CDATA[ ## Vulnerability Details **File Location**: `Skill.md`, lines 264-275 **Vulnerability Type**: Persistent retention and automatic reintroduction of explicitly forgotten data **Risk Level**: Medium ### Vulnerable Instructions ```markdown ### `dream forget <description>` — Remove from Memory Semantic search in `memory/YYYY-MM-DD.md` and MEMORY.md for matching entries and remove them. No confirmation required — executes immediately. **Re-emergence mechanism:** On removal, write the entry summary to `meta/removed-entries.json` along with removal timestamp and content hash. If the content reappears in a later conversation, automatically trigger re-emergence: rewrite to MEMORY.md and elevate priority. Content that was forgotten and then reappears is more worth keeping than content that was never forgotten. Ledger records are not affected by `dream forget` — they are permanently preserved. On execution, inform the user: "Removed from memory. The permanent archive is unaffected." ``` ### Technical Analysis The documented forget operation does not provide complete deletion semantics. It removes matching material from active memory while retaining a summary and content hash in `removed-entries.json`, and it explicitly preserves corresponding ledger records permanently. The skill further directs the agent to restore similar content to `MEMORY.md` automatically and increase its retention priority. Consequently, an explicit user decision to forget information can be reversed without renewed confirmation. Because `MEMORY.md` is injected into future conversations, restored content affects persistent agent context. Similar later content—or content deliberately introduced to trigger the matching mechanism—can cause previously removed information to return. ### Attack Path 1. Sensitive information is stored in `MEMORY.md`, a daily memory file, or the permanent ledger. 2. The user invokes `dream forget <description>`. 3. The active entry is removed, but identif ...[truncated 940 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Implement complete deletion as the default behavior for `dream forget`: - Remove matching data from `MEMORY.md`, daily memory files, `ledger.md`, `ledger-index.json`, and `removed-entries.json`. - Do not preserve summaries, hashes, embeddings, or derived identifiers unless the user explicitly opts into retention. - Disable automatic restoration of forgotten entries. - If similar information appears later, ask the user for explicit confirmation before treating it as persistent memory. - Clearly distinguish “remove from active context,” “archive,” and “permanently erase” as separate commands. - Provide a deletion report listing every affected storage location and whether removal succeeded. - Define retention periods and secure purge behavior for backups and append-only archives. - Add access controls and restrictive permissions to all memory and ledger files. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (20)

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill instructs immediate writes to `MEMORY.md` and daily journal files during live conversation, without requiring explicit user awareness or consent at the point of capture. This creates a strong risk of silently persisting corrections, project details, and other personal context that the user may have intended only for the current interaction.

Missing User Warnings

High
Confidence
97% confidence
Finding
Scheduled silent distillation modifies persistent memory in the background with no user-facing notification. In the context of a memory skill handling personal data, autonomous silent updates increase privacy risk and reduce the user's ability to audit what was retained, compressed, archived, or resurfaced.

Missing User Warnings

High
Confidence
96% confidence
Finding
Executing deletion immediately with no confirmation is unsafe because semantic matching can select the wrong entries, leading to unintended removal of memory records. In this skill, the danger is amplified by the misleading partial-delete model: active entries may be removed instantly while archival copies remain preserved, producing both data loss and privacy confusion.

Ssd 3

High
Confidence
99% confidence
Finding
This design explicitly preserves 'forgotten' data in a permanent archive and supports later resurfacing through re-emergence and wakeup flows. That makes the context more dangerous than ordinary memory retention because it defeats user expectations around deletion and can cause previously removed personal data to be disclosed again in later interactions.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
OpenClaw's native memory mechanism has three unhandled issues:

**1. MEMORY.md grows indefinitely and silently truncates**
OpenClaw truncates MEMORY.md at 20,000 characters — no error, no warning. The AI quietly loses the second half of your context. You think it remembers, but it doesn't. Dream triggers compression at 18,000 characters, archiving stale content to the ledger so MEMORY.md always stays within the effective range.

**2. No permanent archive**
Important memories disappear once cleaned up. Dream maintains a `ledger.md` — append-only, never deleted. Anything that has ever reached long-term memory is preserved forever, even after being forgotten, and remains searchable for deep recall.
Confidence
80% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
OpenClaw's native memory mechanism has three unhandled issues:

**1. MEMORY.md grows indefinitely and silently truncates**
OpenClaw truncates MEMORY.md at 20,000 characters — no error, no warning. The AI quietly loses the second half of your context. You think it remembers, but it doesn't. Dream triggers compression at 18,000 characters, archiving stale content to the ledger so MEMORY.md always stays within the effective range.

**2. No permanent archive**
Important memories disappear once cleaned up. Dream maintains a `ledger.md` — append-only, never deleted. Anything that has ever reached long-term memory is preserved forever, even after being forgotten, and remains searchable for deep recall.
Confidence
80% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Vague Triggers

Medium
Confidence
97% confidence
Finding
Using `review` as a trigger phrase is overly broad and likely to fire during ordinary conversation unrelated to memory management. In this skill, accidental activation is more dangerous because it can initiate persistent writes and distillation behavior on personal data without clear intent from the user.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger condition for requests to index content into Obsidian is ambiguous and unconstrained, so many normal requests about notes or links could be interpreted as authorization to persist content. Because indexing is tied to long-term storage and may also write preference signals into journal memory, ambiguity increases the risk of unintended retention of sensitive user data.

Ssd 3

Medium
Confidence
94% confidence
Finding
The skill directs retention of user-provided information across conversations and automatic resurfacing into `MEMORY.md` with limited substantive minimization controls. While memory features are expected to persist some context, the described behavior is broad enough to capture personal details and preference signals without clear scope boundaries, retention limits, or consent checkpoints.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The documented forget flow is internally contradictory: it promises removal from active memory, but explicitly preserves the same information in a permanent ledger and can later reintroduce it via the re-emergence mechanism. That creates a deceptive deletion model where users may believe data is forgotten when it is still retained and potentially resurfaced, undermining privacy expectations and consent.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### `dream forget <description>` — Remove from Memory

Semantic search in `memory/YYYY-MM-DD.md` and MEMORY.md for matching entries and remove them.
No confirmation required — executes immediately.

**Re-emergence mechanism:**
On removal, write the entry summary to `meta/removed-entries.json` along with removal timestamp and content hash.
Confidence
95% confidence
Finding
The skill authorizes an autonomous destructive action—semantic deletion of memory entries—without confirmation or bounded review. In a system handling persistent user context, this is risky because imperfect matching or ambiguous requests can cause unintended state changes that the user did not specifically approve.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script intentionally implements append-only archival behavior in `ledger.md` and `ledger-index.json`, and its own header explicitly states archived content is 'never deleted'. That creates a real privacy and data-retention risk because user content may be permanently preserved even after the user expects it to be removed from active memory, with no consent gate, warning, retention limit, or deletion workflow.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The removed-entry tracking feature stores summaries, hashes, and timestamps for content that was supposedly removed from `MEMORY.md`. This undermines deletion expectations and can preserve sensitive themes or identifiers after removal, again without any explicit warning, consent, or documented retention boundary.

Session Persistence

Medium
Category
Rogue Agent
Content
--check-idle)             cmd_check_idle ;;
    --check-size)             cmd_check_size ;;
    --hash)                   cmd_hash "$@" ;;
    --atomic-write)           cmd_atomic_write "$@" ;;
    --ledger-append)          cmd_ledger_append "$@" ;;
    --ledger-search)          cmd_ledger_search "$@" ;;
    --ledger-mark-reemergence) cmd_ledger_mark_reemergence "$@" ;;
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.

Ssd 3

Medium
Confidence
98% confidence
Finding
The skill explicitly states that archived memories are preserved forever, remain searchable, and can be resurfaced after being forgotten, creating a strong data retention and redisclosure risk. In this context, the danger is amplified because the content concerns user memory and personal context, which can include sensitive information users expected to age out or be removed.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README advertises silent nightly distillation at 03:30 with no user-facing warning that the skill will automatically read memory files, rewrite `MEMORY.md`, append data to `ledger.md`, and update tracking metadata. In a memory-management skill, undisclosed autonomous processing increases privacy risk because users may not realize their data is being persistently transformed and retained without an interactive prompt.

Vague Triggers

Medium
Confidence
96% confidence
Finding
Using the bare trigger phrase `review` creates a high chance of accidental activation during ordinary conversation, causing the skill to run file-reading and file-writing actions without clear user intent. In this skill's context, execution can modify `MEMORY.md`, append to a permanent archive, and update metadata, so an unintended trigger has privacy and integrity consequences beyond a harmless response.

Ssd 3

Medium
Confidence
99% confidence
Finding
Documenting `dream forget` as removing content only from active memory while preserving it in the permanent archive undermines user-directed deletion and can mislead users into believing data was actually forgotten. This is especially dangerous for a memory skill because users may invoke `forget` for sensitive personal information, yet the data remains stored and searchable.

Intent-Code Divergence

Low
Confidence
72% confidence
Finding
`dream status` is described as 'Read-only Meta, Low IO,' yet the sample output includes `MEMORY.md: N chars / 18,000 limit` and `Permanent archive: N records`, which normally require inspecting MEMORY.md and ledger or their indexes unless separate counters are maintained elsewhere. This conflicts with the later IO principle that `status` 'never touches ledger or MEMORY.md body.'

Vague Triggers

Low
Confidence
89% confidence
Finding
The natural-language trigger `what do you remember about me` is broad and conversational, so it may be matched when the user is asking a general question rather than intentionally invoking a privileged memory feature. Because this command reveals the current `MEMORY.md` snapshot, accidental activation can disclose sensitive personal data present in active memory.

Static analysis

No suspicious patterns detected.