T02 · Agent Memory Poisoning
- Location
scripts/memory_manager.py:273- Finding
Persistent Memory Poisoning Through Untrusted Summary Input
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This memory skill does what it claims broadly, but it can persist personal conversation data and send short-term memory to an external LLM with weak disclosure and controls.
Install only if you are comfortable with conversation memory being saved locally in plaintext and, when summarizing, being sent to the GLM/BigModel service using your OpenClaw Zhipu API key. Avoid storing secrets, tokens, regulated data, or private business content until the skill adds explicit opt-in, redaction, deletion/retention controls, and pinned dependencies.
scripts/memory_manager.py:273Persistent Memory Poisoning Through Untrusted Summary Input
scripts/memory_manager.py:273Undisclosed Transmission of Conversation Content to an External LLM Service
scripts/memory_manager.py:120Sensitive Memory Content Stored in Plaintext with Default File Permissions
SKILL.md:52Unpinned Third-Party Dependency Installation
The skill advertises operational commands and integrations that imply file I/O, environment access, and optional networked/vector-store capabilities, but it does not declare any tool scope or permissions boundaries. In an agent ecosystem, this can cause overbroad execution authority and makes it harder for users or policy layers to understand what the skill may access, increasing the risk of unintended data exposure or misuse.
The documentation states that conversation memory is automatically saved at session end and even provides an example of storing personal data like an email address, but it does not warn about retention, consent, access controls, or sensitive-data handling. This is dangerous because users may unknowingly persist PII or confidential chat content into local summaries/vector stores, creating privacy, compliance, and secondary exposure risks.
The documentation includes an example that stores personally identifiable information ("用户邮箱: lei@example.com") in long-term memory without any warning, minimization guidance, consent requirements, or retention limits. In a memory-management skill, this is more dangerous than a generic example because the feature is explicitly designed to persist and retrieve user data over time, increasing privacy, compliance, and accidental disclosure risk.
The manifest describes medium-term memory as '自动摘要(LLM)', and the config even defines a summary_threshold. However, the code never checks the threshold or triggers summarization automatically; summaries are only created when the summary command calls trigger_summary manually.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
"""保存配置文件"""
# 使用 JSON 而非 YAML,减少依赖
config_json = CONFIG_FILE.with_suffix('.json')
with open(config_json, 'w') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
print(f"✓ 已保存配置: {config_json}")
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# 创建短期记忆文件
if not SLIDING_WINDOW_FILE.exists():
with open(SLIDING_WINDOW_FILE, 'w') as f:
json.dump({'messages': [], 'updated_at': datetime.now().isoformat()}, f, indent=2, ensure_ascii=False)
print(f"✓ 已创建短期记忆: {SLIDING_WINDOW_FILE}")
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# 创建短期记忆文件
if not SLIDING_WINDOW_FILE.exists():
with open(SLIDING_WINDOW_FILE, 'w') as f:
json.dump({'messages': [], 'updated_at': datetime.now().isoformat()}, f, indent=2, ensure_ascii=False)
print(f"✓ 已创建短期记忆: {SLIDING_WINDOW_FILE}")
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# 创建短期记忆文件
if not SLIDING_WINDOW_FILE.exists():
with open(SLIDING_WINDOW_FILE, 'w') as f:
json.dump({'messages': [], 'updated_at': datetime.now().isoformat()}, f, indent=2, ensure_ascii=False)
print(f"✓ 已创建短期记忆: {SLIDING_WINDOW_FILE}")
The docstring for add_medium_term_memory says it adds medium-term memory as a summary, which implies summarization behavior. In reality, the function simply stores the caller-provided content verbatim into a daily JSON file and performs no summarization at all, making the documentation materially misleading about the function's behavior.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
}
data['summaries'].append(new_summary)
with open(summary_file, 'w') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
print(f"✓ 已添加中期记忆: {summary_file}")
The skill claims long-term memory uses vector retrieval/RAG, but the implementation simply adds documents to a Chroma collection and even notes that a real embedding pipeline is not implemented. This creates a semantic mismatch between the advertised long-term semantic retrieval design and the simplified storage/query behavior actually present in code.
The manifest describes long-term memory as vector retrieval/RAG, but this code comment states that it adds content to the vector DB using the raw content itself and notes that real production use would require embeddings. This directly indicates the implementation is not actually performing the intended vectorized long-term memory mechanism described by the skill.
The summarization prompt instructs the model in Chinese and requires the summary to be produced in that language, while the CLI descriptions and usage are also Chinese-centric. There is no indication that users can opt into another language or locale, which creates a language policy concern.
This code sends accumulated conversation content to an external LLM API for summarization with no explicit notice, consent, or redaction step. Because the skill is a memory manager, it is especially likely to contain sensitive personal, conversational, or proprietary data, making silent exfiltration to a third party materially risky.
The manifest description and core explanatory text are primarily Chinese, which may effectively impose a specific language on users. The file does not indicate that Chinese is optional, nor does it provide a documented reason for limiting the skill's instructions to that locale.
The file title, headings, and descriptions are entirely in Chinese, which imposes a language choice on users without any opt-in or explanation. Under the policy, language constraints should either offer user choice or be clearly documented and justified.
The skill reads API credentials from a separate config file in the user's home directory without making that capability explicit in the skill's stated behavior. Hidden access to local secrets expands the skill's trust boundary and can surprise users, especially because the same code later uses the credential to send conversation content to an external service.
Reading API credentials from a local config file without explicit disclosure is risky because it uses broader ambient authority than users may expect from a memory-management skill. Combined with outbound API calls, this can silently activate external data transfer using credentials the user did not knowingly authorize this script to consume.