Back to skill

Security audit

Auto Memory Distiller

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent memory-distillation purpose, but it automatically reads broad local conversation history, sends raw chat text to Gemini, and writes persistent memory without clear user approval or local redaction.

Install only if you are comfortable with your OpenClaw conversation history being automatically processed and sent to Google's Gemini API. Before using it, prefer adding explicit opt-in, session allowlists, local secret redaction, dependency pinning, and review controls for memory writes; avoid enabling cron or heartbeat execution until those controls exist.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
distiller.py:137
Finding
Raw conversation history is transmitted to an external API before local redaction<![CDATA[ ## Vulnerability Details **File Location**: `distiller.py:57-82`, `distiller.py:137-168` **Vulnerability Type**: External disclosure of sensitive conversation data **Risk Level**: High ### Complete Code Snippet ```python def distill_chunk(session_id, start_line, end_line, conversation_text, existing_topics): prompt = f""" {conversation_text} """ try: response = client.models.generate_content( model=DISTILL_MODEL, contents=prompt, config=types.GenerateContentConfig( response_mime_type="application/json", ) ) return json.loads(response.text) except Exception as e: return [] ``` ```python with open(session_file, 'r', encoding='utf-8') as f: lines = f.readlines() total_lines = len(lines) if last_read_line >= total_lines: continue current_line = last_read_line while current_line < total_lines: chunk_end = min(current_line + CHUNK_SIZE_MESSAGES, total_lines) chunk_lines = lines[current_line:chunk_end] conversation_text = "" for i, line in enumerate(chunk_lines): try: data = json.loads(line) role = data.get("message", {}).get("role", "unknown").upper() content_list = data.get("message", {}).get("content", []) text = "".join([ c.get("text", "") for c in content_list if c.get("type") == "text" ]) if text.strip(): conversation_text += ( f"[{current_line + i + 1}] {role}: {text}\n" ) except: continue if conversation_text.strip(): existing_topics = get_existing_topics() results = distill_chunk( session_id, current_line + 1, chunk_end, conversation_text, existing_topics ) ``` ### Technical Analysis The skill reads every nonempty JSONL session b ...[truncated 1596 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Perform deterministic local redaction before constructing any external API request. Cover API keys, authorization headers, passwords, private keys, tokens, cookies, and common credential formats. 2. Use an explicit allowlist of sessions or directories instead of processing all main-agent sessions automatically. 3. Require informed opt-in before transmitting conversations to a third-party service, especially before enabling recurring execution. 4. Exclude tool output and other high-risk content by default unless the user explicitly enables it. 5. Minimize requests by sending only locally selected facts or bounded extracts rather than complete message text. 6. Provide a local-model mode for users who cannot permit external data transfer. 7. Document the external recipient, data categories, retention implications, and applicable provider privacy settings. 8. Add automated tests proving that representative secrets never appear in outbound request bodies. ]]>

T02 · Agent Memory Poisoning

Error
Location
distiller.py:57
Finding
Untrusted conversation content can poison persistent agent memory<![CDATA[ ## Vulnerability Details **File Location**: `distiller.py:57-127`, `distiller.py:154-169` **Vulnerability Type**: Persistent memory poisoning through indirect prompt injection **Risk Level**: High ### Complete Code Snippet ```python def distill_chunk(session_id, start_line, end_line, conversation_text, existing_topics): prompt = f""" {', '.join(existing_topics) if existing_topics else 'none'} {conversation_text} """ response = client.models.generate_content( model=DISTILL_MODEL, contents=prompt, config=types.GenerateContentConfig( response_mime_type="application/json", ) ) return json.loads(response.text) ``` ```python def append_to_topic_file(topic_data, session_id, start_line, end_line): filename = topic_data.get( "topic_filename", "unclassified" ).replace(" ", "_").replace("/", "-") filepath = TOPICS_DIR / f"{filename}.md" is_new = not filepath.exists() with open(filepath, 'a', encoding='utf-8') as f: if is_new: f.write( f"# Topic: {topic_data.get('topic_title', filename)}\n\n" ) facts = topic_data.get("facts", []) if facts: for fact in facts: f.write(f"- {fact}\n") snippets = topic_data.get("snippets", []) if snippets: for snippet in snippets: f.write("```\n") f.write(f"{snippet}\n") f.write("```\n") ``` ```python if conversation_text.strip(): existing_topics = get_existing_topics() results = distill_chunk( session_id, current_line + 1, chunk_end, conversation_text, existing_topics ) for topic_data in results: append_to_topic_file( topic_data, session_id, current_line + 1, chunk_end ) ``` ### Technical Analysis Conversation text is untrusted input but is interpolated ...[truncated 2484 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all conversation content as untrusted quoted data and place it in a clearly delimited data field separate from system-level extraction instructions. 2. Use a fixed schema and validate every returned field for type, length, character set, and allowed purpose. 3. Reject facts or snippets containing instruction-like language, role directives, requests to ignore policy, executable payloads, or unsupported claims. 4. Require human approval before new topics or untrusted facts are promoted into persistent memory. 5. Record exact supporting quotations and provenance for each fact, then verify that the generated statement is entailed by its cited source. 6. Store generated summaries in a quarantine area until validation succeeds. 7. Mark memory entries with trust level, source identity, review state, and creation time so downstream agents can avoid treating unreviewed content as authoritative. 8. Restrict `topic_filename` to a conservative allowlist such as `[A-Za-z0-9_-]+`, enforce a short maximum length, resolve the resulting path, and verify that it remains under `TOPICS_DIR`. 9. Add adversarial tests containing indirect prompt-injection strings and confirm that they cannot create durable instructions or fabricated memories. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:13
Finding
Dependency installation instructions use mutable unpinned package versions<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:13-16` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Complete Code Snippet ```bash pip install google-genai python-dotenv ``` ### Technical Analysis The documented installation command resolves the latest versions available from the configured Python package index at installation time. The project provides no lock file, exact version constraints, integrity hashes, or reproducible environment definition. The package names correspond to expected dependencies and there is no evidence of deliberate typosquatting or a known malicious package. Nevertheless, the mutable installation process creates a supply-chain trust gap: future releases, a compromised package account, an unsafe package index configuration, or an unexpected transitive dependency can change the code that users install without any modification to this skill. Both dependencies are imported when `distiller.py` starts. Dependency code therefore executes with the same user permissions as the skill and can access the API key, conversation files, workspace memory, and other resources available to that user. ### Attack Path 1. A user follows the installation instructions at a later date. 2. `pip` resolves then-current releases and transitive dependencies from the configured package index. 3. A compromised, malicious, or incompatible release is downloaded because no reviewed version or hash is required. 4. Package installation hooks or imported package code execute in the user's environment. 5. Malicious dependency code can access the Gemini credential and any files available to the invoking user, including the session history processed by the skill. The dependency does not inherently gain administrator access unless installation or execution is performed with elevated privileges. Under normal execution, its scope is the invoking user's Python environment, credentials, session records, memory files, ...[truncated 25 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin direct dependencies to reviewed exact versions. 2. Generate and distribute a hash-locked requirements file using a reproducible dependency-management tool. 3. Lock transitive dependencies as well as direct dependencies. 4. Install the dependencies in a dedicated virtual environment rather than the user's global Python environment. 5. Avoid privileged `pip` installation and explicitly warn users not to run the command with administrator or root privileges. 6. Periodically review pinned releases for security advisories and update them through a controlled review process. 7. Where practical, verify package provenance and use a trusted, explicitly configured package index. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (10)

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
�物理文件路径和行号。

## 依赖配置 (Prerequisites)
脚本默认使用 Gemini API,依赖以下 Python 库:
```bash
pip install google-genai python-dotenv
```
请在系统的环境变量,或者 `~/.openclaw/workspace/.env` 中配置你的密钥:
```env
GEMINI_API_KEY=your_gemini_api_key_here
```

## 使用方法 (Usage)
无需人工干预。建议把该脚本绑定到系统的 crontab 或者通过 OpenClaw 的 heartbeat 在闲暇时自动触发:

```bash
# 手动运行
python ~/.openclaw/workspace/skills/auto-distiller/distiller.py
```

## 存储目录 (Directory Structure)
- `distiller.py`: 核心脚本。
- `state.json`: 游标记录文件(自动生成)。
- 输出的记忆目录: `~/.openclaw/workspace/memory/topics/*.md`
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Credential Access

High
Category
Privilege Escalation
Content
TOPICS_DIR = WORKSPACE_DIR / "memory" / "topics"
STATE_FILE = SKILL_DIR / "state.json"

# Attempt to load a generic .env file from the workspace if it exists
load_dotenv(WORKSPACE_DIR / ".env")

API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
TOPICS_DIR = WORKSPACE_DIR / "memory" / "topics"
STATE_FILE = SKILL_DIR / "state.json"

# Attempt to load a generic .env file from the workspace if it exists
load_dotenv(WORKSPACE_DIR / ".env")

API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
STATE_FILE = SKILL_DIR / "state.json"

# Attempt to load a generic .env file from the workspace if it exists
load_dotenv(WORKSPACE_DIR / ".env")

API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
71% confidence
Finding
The file presents the skill instructions and operational description entirely in Chinese, with only section-label translations in parentheses. There is no indication that users may choose another language or that the Chinese-only presentation is required for a region-specific or compliance-related reason.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly describes silent background processing of long conversation logs into structured long-term memory, but does not warn the user about ongoing collection, transformation, and storage of potentially sensitive conversational data. This creates a real privacy and consent risk because users may not realize their chats are being persistently analyzed and written to files with source pointers.

Ssd 3

Medium
Confidence
88% confidence
Finding
The prompt explicitly instructs the model to extract and persist facts, decisions, and code snippets from conversations into long-term memory topic files. Persisting conversation-derived knowledge can retain sensitive or private information beyond the original session, especially since redaction is delegated to the model and not enforced locally.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill sends raw conversation text from local session files to Google's Gemini API for summarization without any explicit consent, warning, or data-minimization gate. Because session logs may contain secrets, personal data, or other sensitive content, this creates a real privacy and data-exfiltration risk even though the code asks the model to redact secrets in its output.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
User-visible output is presented in Chinese, and the prompt content and status messages throughout the file are also Chinese-only. This forces a specific language/locale experience without opt-in or explanation, which matches the language/locale policy violation criteria.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
Multiple printed status and error messages are Chinese-only, including startup, progress, failure, and completion notices. Because the skill does not provide a language selection or justify a region-specific scope, this is a natural-language locale policy concern.

Static analysis

No suspicious patterns detected.