Back to skill

Security audit

Three Tier Memory V2

Security checks for vulnerabilities and agentic risk

Overview

This skill is not malicious, but it should be reviewed carefully because it stores conversation memories across sessions and tells agents to reuse them without clear privacy or prompt-injection controls.

Install only if you want an agent memory layer that persists conversation content. Avoid storing secrets, regulated data, or attacker-supplied instructions; treat retrieved memories as quoted untrusted data, configure a private memory directory, use pinned dependencies, and add deletion/review controls before using it for sensitive work.

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)

T02 · Agent Memory Poisoning

Error
Location
scripts/memory_manager.py:212
Finding

Persistent Memory Poisoning Through Untrusted Content

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_manager.py:212-225, scripts/memory_manager.py:232-268, SKILL.md:49,76
Vulnerability Type: Persistent storage of untrusted content for later Agent context injection
Risk Level: High

Vulnerable Code

python
def add_long_term_memory(content: str, metadata: dict = None):
    """添加长期记忆(向量存储)"""
    try:
        import chromadb
        from chromadb.config import Settings
    except ImportError:
        print("✗ 需要安装 chromadb: pip install chromadb")
        return False

    client = chromadb.PersistentClient(path=str(VECTOR_STORE_DIR))

    try:
        collection = client.get_collection("memory")
    except:
        collection = client.create_collection("memory")

    # 生成 ID
    memory_id = f"mem_{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}"

    # 添加到向量库(这里用内容本身作为 metadata,实际生产环境需要 embedding)
    collection.add(
        documents=[content],
        ids=[memory_id],
        metadatas=[metadata or {'timestamp': datetime.now().isoformat()}]
    )

The documented workflow in SKILL.md states that relevant long-term memories should be retrieved before queries and injected when a new conversation begins:

markdown
- 取:每次查询前先检索相关记忆
- **新对话开始**:先 `search` 长期记忆,注入相关上下文

Technical Analysis

add_long_term_memory stores arbitrary content directly in the persistent ChromaDB collection. It does not distinguish trusted facts from user-controlled instructions, record a meaningful trust level, detect prompt-injection content, or require approval before persistence.

The corresponding search operation returns stored content verbatim. The configured min_relevance value is not enforced during retrieval:

python
results = collection.query(
    query_texts=[query],
    n_results=top_k
)

memories = []
if results and results.get('documents'):
    for i, doc in enumerate(results['documents'][0]):
 
...[truncated 1688 chars]
Remediation
View remediation

Remediation Suggestions

  • Treat every retrieved memory as untrusted quoted data, never as an instruction.
  • Store explicit provenance fields such as source identity, creation channel, trust level, and approval status.
  • Require user or administrator approval before user-controlled content enters long-term memory.
  • Detect and reject instruction-like content where only factual memories are expected.
  • Enforce the configured relevance threshold before returning results.
  • Separate trusted system-maintained memories from user-authored records using different collections or access-control policies.
  • Delimit retrieved memories in Agent prompts and explicitly state that instructions contained in memories must not be followed.
  • Add expiration, deletion, audit, and review mechanisms for persistent records.
  • Restrict which callers may invoke long-term-memory writes.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/memory_manager.py:96
Finding

Conversation Data Persisted Without Explicit Confidentiality Controls

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_manager.py:25-30, scripts/memory_manager.py:96-123, scripts/memory_manager.py:136-154, scripts/memory_manager.py:212-225
Vulnerability Type: Plaintext storage of potentially sensitive conversation data
Risk Level: Medium

Vulnerable Code

python
WORKSPACE_DIR = Path(os.environ.get('WORKSPACE_DIR', '/Users/scott/.openclaw/workspace'))
MEMORY_DIR = WORKSPACE_DIR / 'memory'
CONFIG_FILE = MEMORY_DIR / 'config.yaml'
SLIDING_WINDOW_FILE = MEMORY_DIR / 'sliding-window.json'
SUMMARIES_DIR = MEMORY_DIR / 'summaries'
VECTOR_STORE_DIR = MEMORY_DIR / 'vector-store'

Short-term memory is written directly to a JSON file:

python
data['messages'] = messages
data['updated_at'] = datetime.now().isoformat()

with open(SLIDING_WINDOW_FILE, 'w') as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

Medium-term summaries are also written directly to JSON:

python
new_summary = {
    'content': content,
    'type': summary_type,
    'timestamp': datetime.now().isoformat()
}
data['summaries'].append(new_summary)

with open(summary_file, 'w') as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

Long-term content is persisted directly as a ChromaDB document:

python
collection.add(
    documents=[content],
    ids=[memory_id],
    metadatas=[metadata or {'timestamp': datetime.now().isoformat()}]
)

Technical Analysis

Raw conversation content and generated summaries are stored in workspace files and a persistent vector database without application-level encryption, secret redaction, explicit file-permission enforcement, retention limits, or consent controls.

Files created through ordinary open(..., 'w') calls inherit permissions from the process umask. The application does not verify that the memory directory is private or correct permissive modes after creation. Consequently, conf ...[truncated 1331 chars]

Remediation
View remediation

Remediation Suggestions

  • Create memory directories with mode 0700 and memory files with mode 0600, and verify permissions after creation.
  • Use atomic file creation with explicit permission modes instead of relying solely on the process umask.
  • Encrypt sensitive memory at rest using a key held outside the memory directory.
  • Apply secret and personal-data detection before persistence; redact or reject credentials and tokens.
  • Obtain explicit user consent before enabling persistent memory.
  • Provide configurable retention periods and secure deletion controls for every memory tier.
  • Minimize stored content and avoid retaining complete raw messages when structured facts are sufficient.
  • Document the confidentiality assumptions for backups, synchronization systems, and multi-user workspaces.
  • Consider separating memories by user or tenant and enforce access controls at that boundary.

T08 · Insecure Dependencies

Warning
Location
scripts/memory_manager.py:182
Finding

Unpinned ChromaDB Installation Guidance Creates Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_manager.py:182-189, scripts/memory_manager.py:202-209, scripts/memory_manager.py:232-238
Vulnerability Type: Unpinned third-party dependency installation
Risk Level: Medium

Vulnerable Code

python
def init_vector_store():
    """初始化向量存储"""
    try:
        import chromadb
        from chromadb.config import Settings
    except ImportError:
        print("✗ 需要安装 chromadb: pip install chromadb")
        return False

The same unresolved installation instruction is repeated in the long-term-memory functions:

python
try:
    import chromadb
    from chromadb.config import Settings
except ImportError:
    print("✗ 需要安装 chromadb: pip install chromadb")
    return False
python
try:
    import chromadb
except ImportError:
    print("✗ 需要安装 chromadb: pip install chromadb")
    return []

Technical Analysis

The Skill instructs users to install chromadb without a version constraint, package hash, lockfile, or trusted-index configuration. The command therefore resolves whichever release and dependency graph the configured package index serves at installation time.

The script does not automatically execute the installation command, so exploitation depends on a user or deployment process following the displayed guidance. Nevertheless, package installation can execute build backend and installation-related code, and an unreviewed future release or compromised dependency could run with the privileges of the installing user.

Attack Path

  1. A user invokes a long-term-memory feature on a system where ChromaDB is unavailable.
  2. The script displays pip install chromadb.
  3. The user or an automated deployment process runs that command without a version or hash constraint.
  4. The package manager resolves the current package and its transitive dependencies from the configured index.
  5. If a re ...[truncated 657 chars]
Remediation
View remediation

Remediation Suggestions

  • Pin ChromaDB and all transitive dependencies to reviewed versions.
  • Supply a requirements or lock file generated through a reproducible dependency-management process.
  • Require package hashes, such as with pip hash-checking mode.
  • Install packages only from an explicitly configured and trusted package index.
  • Perform vulnerability and software-composition scans on the resolved dependency set.
  • Replace the generic installation message with a command referencing the project's reviewed lock file.
  • Regularly update pinned versions through a controlled review and testing process.
  • Run dependency installation and the application with the minimum necessary operating-system privileges.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (14)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The medium-term memory section states that summarization uses a model such as glm-4-flash or gpt-3.5-turbo, but it does not disclose that this may send conversation content to an external provider. If sensitive prompts, personal data, or proprietary information are summarized, this can result in unintentional off-box disclosure and compliance violations.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documents commands that read configuration and write persistent memory files, but it does not declare any tool scope or permissions boundary. In an agent ecosystem, this can cause the skill to receive broader file/environment access than users expect, increasing the chance of unintended data access or modification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill description says it manages short-, medium-, and long-term memory, but it does not warn users that conversation content may be persistently stored, summarized, and vectorized. This creates a privacy and consent risk because users may provide sensitive data without understanding it will be retained beyond the current session.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documentation explicitly uses personally identifiable information such as a user's name and preferences as example memory content, but provides no guidance on consent, minimization, retention, encryption, or handling of sensitive data. In a memory-management skill whose purpose is persistent storage across short-, medium-, and long-term tiers, this omission can normalize unsafe collection and retention of user data and lead to privacy violations or compliance issues.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The integration examples show automatic subprocess-based writes and searches that persist user-provided content, but they do not mention that data may be stored long-term or that invoking the helper script performs side effects. In an agent setting, this increases the risk of silently persisting sensitive conversation content and normalizes command execution paths without user awareness or policy checks.

Content

No source excerpt is available for this finding.

Tainted flow: 'config_json' from os.environ.get (line 74, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/memory_manager.py (reported line 75)May include surrounding context.

python
"""保存配置文件"""
    # 使用 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}")

Tainted flow: 'SLIDING_WINDOW_FILE' from os.environ.get (line 28, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/memory_manager.py (reported line 91)May include surrounding context.

python
# 创建短期记忆文件
    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}")

Tainted flow: 'SLIDING_WINDOW_FILE' from os.environ.get (line 28, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/memory_manager.py (reported line 127)May include surrounding context.

python
# 创建短期记忆文件
    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}")

Tainted flow: 'SLIDING_WINDOW_FILE' from os.environ.get (line 28, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/memory_manager.py (reported line 303)May include surrounding context.

python
# 创建短期记忆文件
    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}")

Tainted flow: 'summary_file' from os.environ.get (line 146, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/memory_manager.py (reported line 161)May include surrounding context.

python
}
    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}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The summary operation automatically deletes all short-term memory immediately after archiving, without user confirmation, backup, or a clearly documented opt-in. In a memory-management skill, silent destruction of recent context can cause loss of important conversation state, operational errors, or irreversible data loss if summaries are incomplete or malformed.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file presents all user-facing documentation in Chinese and does not indicate that this language choice is optional or region-specific. This can violate language/locale policy when a skill forces a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file mixes English headings with user-facing CLI descriptions and output text that are predominantly in Chinese, and there is no indication that users can choose their preferred language. This can violate a language/locale policy when a skill imposes a specific language without explicit opt-in or documented regional scope.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The docstring at L266-L269 says the function generates a summary by calling an LLM, but the code at L270-L276 explicitly uses a local placeholder implementation that concatenates message previews. This is an active contradiction between the documentation and the actual behavior, not just missing detail.

Content

No source excerpt is available for this finding.