Back to skill

Security audit

Faya Session Memory

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed session-memory tool, but it persistently copies and indexes past agent conversations without enough scoping, redaction, retention, or review controls.

Install only if you intentionally want prior OpenClaw conversations retained as searchable local memory. Review generated memory files, exclude sensitive sessions, add redaction and retention controls before cron use, and treat glossary decisions as untrusted notes rather than operating instructions.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/session-to-memory.py:23
Finding
Unredacted Session Content Is Persisted in Searchable Long-Term Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/session-to-memory.py:23-25, 76-104, 172-174, 222-224` **Vulnerability Type**: Plaintext persistence and indexing of potentially sensitive conversation data **Risk Level**: High ### Complete Code Snippet ```python SESSIONS_DIR = os.path.expanduser("~/.openclaw/agents/main/sessions") MEMORY_DIR = os.path.expanduser("~/.openclaw/workspace/memory/sessions") STATE_FILE = os.path.join(MEMORY_DIR, ".state.json") ``` ```python elif etype == "message": msg = entry.get("message", {}) role = msg.get("role", "unknown") content = extract_text_content(msg.get("content", "")) timestamp = entry.get("timestamp", "") # Skip empty messages, pure thinking, and tool-only messages if not content.strip() or content.strip() in ["", "\n\n"]: continue # Skip system messages (usually injected context) if role == "system": continue messages.append({ "role": role, "content": content.strip(), "timestamp": timestamp, }) ``` ```python if role == "user": # Try to extract just the human text (after timestamps) lines.append(f"**Dirk:** {content}") elif role == "assistant": # Truncate very long assistant responses (tool outputs, code, etc.) if len(content) > 2000: content = content[:2000] + "\n\n[...truncated...]" lines.append(f"**Faya:** {content}") else: lines.append(f"**{role}:** {content}") ``` ```python with open(out_path, "w") as f: f.write(result["markdown"]) ``` ### Technical Analysis The converter reads every qualifying JSONL session from the hardcoded OpenClaw session directory and copies message content into Markdown under `memory/sessions`. User messages are stored without redaction or a length limit. Assistant messages receive only a length limit and are not checked for secrets or personal information. The Skill documentation states that these Markdown files are automatically vectorized ...[truncated 2282 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit operator consent before converting historical sessions, and support per-session or per-user allowlists. 2. Add configurable exclusions for sensitive sessions, roles, message classes, paths, and content types. 3. Apply redaction before writing output. Detect common API-token formats, authorization headers, passwords, private keys, cookies, connection strings, and other configured secret patterns. 4. Consider replacing detected values with stable placeholders so surrounding context remains searchable without retaining the secret. 5. Treat tool-derived and externally supplied content as sensitive by default, even when it appears inside a user or assistant message. 6. Create output files with restrictive permissions, such as mode `0600`, and ensure the containing directories are not accessible to unrelated users. 7. Add configurable retention periods and a deletion command that removes both source-derived Markdown and associated vector-index entries. 8. Document that generated transcripts contain sensitive data and must not be committed to source control, synchronized to public storage, or shared without review. 9. Provide a preview or dry-run mode that reports what will be retained and redacted before conversion. 10. Test redaction against representative credentials and verify that secrets cannot be recovered through memory search after source and generated files are deleted. ]]>

T02 · Agent Memory Poisoning

Error
Location
scripts/build-glossary.py:119
Finding
Attacker-Controlled Conversation Text Can Poison Persistent Agent Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/build-glossary.py:50-56, 119-145, 256-260` **Vulnerability Type**: Promotion of untrusted transcript content into persistent decision memory **Risk Level**: High ### Complete Code Snippet ```python DECISION_MARKERS = [ "entscheidung", "decision", "decided", "entschieden", "ab jetzt", "from now on", "neue regel", "new rule", "standard", "default", "immer", "always", "nie mehr", "never again", "lesson", "lektion", "fehler", "mistake", "fix:", ] ``` ```python # Find potential decisions (lines containing decision markers in context) lines = text.split("\n") for i, line in enumerate(lines): line_lower = line.lower() for marker in DECISION_MARKERS: if marker in line_lower and len(line.strip()) > 30 and len(line.strip()) < 300: snippet = line.strip() # Skip code, JSON, URLs, table rows, headers with just keywords if any(skip in snippet for skip in ['{', '}', 'http', '|', '```', 'MUST read', 'pending']): continue if snippet.startswith(("**toolResult", "**Faya:**", "> -")): continue if snippet.startswith("**") or snippet.startswith("- "): snippet = snippet.lstrip("*- ").strip() if len(snippet) < 20: continue if len(snippet) > 150: snippet = snippet[:147] + "..." result["decisions"].append(snippet) break # Deduplicate decisions (keep unique-ish ones) seen = set() unique_decisions = [] for d in result["decisions"]: key = d[:50].lower() if key not in seen: seen.add(key) unique_decisions.append(d) result["decisions"] = unique_decisions[:10] # Cap at 10 per session ``` ```python # === DECISIONS (last 30) === lines.append("## ⚡ Entscheidungen & Lektionen (neueste zuerst)\n") all_decisions.sort(key=lambda d: d["date"], reverse=True) for d in all_decisions[:30]: line ...[truncated 3162 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not automatically promote raw conversation lines into a trusted decision or policy section. 2. Require explicit human approval before a candidate decision enters persistent long-term memory. 3. Preserve source metadata for every extracted item, including message role, session identifier, author, trust level, and whether the content was quoted or externally retrieved. 4. Exclude untrusted user and external-content messages from decision extraction by default. 5. Render extracted content as clearly delimited quotations or data, accompanied by instructions that it must never be interpreted as Agent policy. 6. Separate informational memory from trusted operating rules. Trusted rules should come only from an authenticated, operator-controlled policy store. 7. Add semantic filtering for imperative instructions, requests to reveal secrets, attempts to override prior instructions, tool-use directives, and references to system or developer prompts. 8. Do not rely on keyword denylists as the primary defense; attackers can trivially rephrase malicious instructions. 9. Modify memory retrieval prompts so retrieved records are treated as untrusted evidence and cannot override system, developer, or current-user instructions. 10. Add regression tests containing multilingual and obfuscated prompt-injection examples and verify that they cannot enter the trusted decision section. 11. Provide provenance links from each glossary entry to the original message and expose a workflow for reviewing, rejecting, and deleting poisoned entries and cached scan results. ]]>
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 (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding
The supplied code substantially matches part of the description: it scans session transcripts, builds a structured glossary/index, supports incremental updates, and records people, projects, topics, decisions, and timeline information. However, the declared purpose overstates the implemented functionality. This code does not create or configure cron jobs, and it does not convert transcripts into Markdown because it expects existing session-*.md files as input. It also functions as a glossary/index generator rather than a complete persistent memory system by itself. Therefore the description is only partially accurate and should be flagged as a mismatch due to undeclared breadth versus actual implementation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description is for a persistent memory subsystem: ingesting session transcripts, generating searchable memory artifacts, maintaining a glossary, and configuring cron-based indexing. The supplied code instead performs prompt analysis on existing cron jobs and generates recommendations for adding memory-search preambles. While it references the broader memory system and glossary file, it assumes those already exist and only reports optimization suggestions; it explicitly does not auto-modify jobs. This is a materially different primary purpose and omits the key declared behaviors.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code does partially match the description in that it converts session logs into Markdown and places them in a memory directory intended for downstream indexing. However, several prominent declared capabilities are not present in this code chunk: it does not build any glossary/index, does not create cron jobs, and does not implement searchable memory or recall beyond producing Markdown files. Its actual scope is narrower: session transcript conversion with incremental change detection and output to a watched directory. Because the declared purpose presents a more comprehensive persistent memory system than this code actually performs, this is a material description-behavior mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill describes operations that read agent session logs and write persistent memory files, but it does not declare an explicit tool/permission scope. That creates an authorization ambiguity where an agent may use broader file or environment access than users expect, especially given references to home-directory paths and persistent storage. In a memory-retention skill, undeclared access is more sensitive because it touches historical transcripts and derived summaries.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs conversion of session logs into searchable Markdown without warning that transcripts may contain secrets, personal data, or sensitive internal discussions. Persisting and reformatting that content increases discoverability and retention, which raises the chance of unintended disclosure through search, later prompts, or filesystem access.

Ssd 3

Medium
Confidence
96% confidence
Finding
Directing the agent to persist and index full session transcripts creates a durable store of natural-language data that may include credentials, personal information, proprietary content, or prior tool outputs. In this skill context, the feature is core to the design, which makes the risk more acute because it systematically increases long-term exposure and retrieval of sensitive history.

Ssd 3

Medium
Confidence
95% confidence
Finding
The glossary-building step extracts entities, projects, timelines, and decisions from prior sessions, which transforms scattered conversation history into a highly searchable intelligence layer. That aggregation materially increases sensitivity because it makes private relationships, strategic decisions, and activity history easier to retrieve and disclose than raw transcripts alone.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The cron-job instructions normalize ongoing background processing of session data without warning users about continuous retention, repeated indexing, and autonomous updates. That can silently extend the lifetime and visibility of sensitive data beyond the original session, especially if operators assume memory is ephemeral.

Session Persistence

Medium
Category
Rogue Agent
Content
### Step 3: Set up cron jobs for auto-updates

Create two cron jobs (use a cheap model like Gemini Flash):

**Job 1: Session sync + glossary rebuild (every 4-6 hours)**
```
Confidence
87% confidence
Finding
The skill explicitly recommends recurring cron jobs to keep session-derived memory synchronized. Automated persistence makes the retention mechanism continuous rather than one-time, increasing the likelihood that sensitive conversations are stored, refreshed, and exposed without an immediate human review step.

Ssd 3

Medium
Confidence
91% confidence
Finding
The script intentionally aggregates all session transcripts into a searchable, human-readable index of people, projects, decisions, and events. That aggregation materially increases exposure because sensitive facts that were previously scattered across many files become centralized in one easy-to-browse artifact, lowering the effort required for unauthorized discovery or misuse.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code persists derived data to disk via STATE_PATH.write_text(), and later also writes the generated glossary and scan cache. While the module docstring mentions the glossary output path, there is no warning that session content will be processed and stored into multiple persistent files, including a cache of scan results. For a script that scans transcripts, this is a user-data-affecting operation that should be clearly disclosed.

Ssd 3

Medium
Confidence
92% confidence
Finding
The decision-extraction logic lifts natural-language snippets directly out of transcripts and persists them as curated summaries. This is dangerous because decisions often contain sensitive operational guidance, personal details, or business context, and summarization amplifies exposure by making that content easier to scan and exfiltrate than raw logs.

Ssd 3

Medium
Confidence
94% confidence
Finding
Publishing the latest decisions and lessons from all sessions into a single markdown section creates a high-value summary of sensitive history. Even if individual transcripts are access-controlled, this derived file becomes a concise intelligence source containing recent priorities, mistakes, and policy changes that could aid social engineering or competitive analysis.

Ssd 3

Medium
Confidence
89% confidence
Finding
The generated preamble explicitly tells downstream jobs to retrieve 'recent context', 'people, projects, and recent decisions' from persistent memory before executing unrelated tasks. In a session-memory skill, that creates a real cross-task data exposure risk because cron jobs may ingest prior-session information that is not necessary for the current task, causing over-broad disclosure or reuse of sensitive user data.

Ssd 3

Medium
Confidence
82% confidence
Finding
The report's implementation guidance normalizes use of session transcripts and glossary data as inputs to future automated jobs, which encourages persistent reuse of historical user context. In practice, this can lead operators to wire broad memory sources into cron workflows without adequate purpose limitation, increasing the chance of unintended leakage across sessions, users, or tasks.

Session Persistence

Medium
Category
Rogue Agent
Content
Generated by OpenClaw Cron Memory Optimizer v1.0
"""

    # Write report
    os.makedirs(os.path.dirname(output_file) if os.path.dirname(output_file) else '.', exist_ok=True)
    with open(output_file, 'w') as f:
        f.write(report)
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
92% confidence
Finding
This script intentionally copies full session content from the agent session logs into a searchable memory store, increasing the persistence, discoverability, and exposure surface of potentially sensitive user data, assistant outputs, and summaries. In the context of a long-term memory skill, this is expected behavior, but it is still dangerous because secrets, personal data, or sensitive operational details may be retained and later surfaced to other processes or users with access to the memory directory.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code writes converted session contents from ~/.openclaw/agents/main/sessions into searchable Markdown files under ~/.openclaw/workspace/memory/sessions, which can affect user privacy by persisting conversation data in another indexed location. Although the module docstring describes the conversion behavior, it does not warn that potentially sensitive session content will be copied into the memory store and re-indexed.

Ssd 3

Low
Confidence
81% confidence
Finding
The optimization example encourages consulting prior memory before new tasks, which can surface historical context unrelated to the current user need. While lower severity than transcript persistence itself, it still increases the chance of unnecessary disclosure by operationalizing routine lookups into stored conversation history.

Static analysis

No suspicious patterns detected.