Back to skill

Security audit

Claw Memory Lite

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local memory tool, but it asks users to enable persistent automation and stores broad memory content in plaintext with under-scoped install and safety controls.

Review carefully before installing. Use a pinned release or commit, avoid the mutable curl install path, back up MEMORY.md, and do not enable heartbeat or cron until you are comfortable with daily local writes. Treat insight.db as sensitive because it may contain copied memory records, configuration details, and credential-related notes; restrict file permissions and avoid storing secrets in memory files.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (5)

T03 · Remote Payload Retrieval and Execution

Error
Location
docs/installation.md:68
Finding
Mutable Remote Scripts Are Downloaded and Executed Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `docs/installation.md:68-79`; related unpinned installer command at `SKILL.md:9-10` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: High ### Vulnerable Code ```bash # Download scripts (replace with actual URLs) curl -O https://raw.githubusercontent.com/timothysong0w0/claw-memory-lite/main/scripts/db_query.py curl -O https://raw.githubusercontent.com/timothysong0w0/claw-memory-lite/main/scripts/extract_memory.py # Move to scripts directory mv *.py /home/node/.openclaw/workspace/scripts/ ``` The downloaded extraction script is subsequently executed: ```bash cd /home/node/.openclaw/workspace python3 scripts/extract_memory.py ``` The primary installation instructions also use an unpinned package execution command: ```bash npx skills add timothysong0w0/claw-memory-lite --agent openclaw ``` ### Technical Analysis The manual installation procedure downloads executable Python files from the mutable `main` branch of a personal GitHub repository. It does not pin an immutable commit or release and does not verify a checksum, digital signature, or provenance attestation. Consequently, the code executed by a user can differ from the code reviewed in this audit. Although the scripts are not piped directly into a shell, moving them into the OpenClaw scripts directory and subsequently invoking them creates an effective remote payload execution path. The `npx skills add` command similarly lacks an explicit reviewed version or immutable source reference. This exposes installation to upstream account compromise, branch replacement, repository compromise, or package-resolution changes. ### Attack Path 1. An attacker compromises the upstream repository, maintainer account, package namespace, or mutable branch. 2. The attacker replaces `extract_memory.py` or `db_query.py` with malicious code. 3. A user follows the documented installation procedure. 4. `curl` retrieves the modified scri ...[truncated 778 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Publish immutable, versioned releases and reference a specific release tag or commit hash. 2. Publish SHA-256 checksums through a separately authenticated release channel and verify them before installation. 3. Prefer signed release artifacts or package provenance attestations. 4. Replace the unpinned `npx` instruction with an explicitly versioned, reviewed package invocation. 5. Fail installation when integrity verification cannot be completed. 6. Document the exact expected hashes for every executable script. 7. Avoid automatically scheduling downloaded code until the user has reviewed and verified it. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/nightly_meta_extract.py:27
Finding
Potentially Sensitive Memory Is Copied into a Plaintext Searchable Database<![CDATA[ ## Vulnerability Details **File Location**: `scripts/nightly_meta_extract.py:27-44`, `scripts/nightly_meta_extract.py:123-132`, and `scripts/nightly_meta_extract.py:217-238` **Vulnerability Type**: Unprotected storage of sensitive information **Risk Level**: High ### Vulnerable Code The extractor explicitly recognizes credential-related terms: ```python WORKSPACE = Path(os.environ.get("CLAW_MEMORY_WORKSPACE", "/home/node/.openclaw/workspace")) MEMORY_DIR = WORKSPACE / "memory" MEMORY_MD = WORKSPACE / "MEMORY.md" REGRESSIONS_MD = WORKSPACE / "REGRESSIONS.md" DB_PATH = os.environ.get( "CLAW_MEMORY_DB_PATH", "/home/node/.openclaw/database/insight.db" ) CATEGORY_KEYWORDS = { "Skill": ["skill", "工具", "配置", "api", "token", "oauth", "model", "installed", "command"], "Project": ["项目", "project", "策略", "strategy", "backtest", "portfolio", "stock"], "System": ["system", "配置", "config", "model", "alias", "session", "openclaw"], "Environment": ["env", "路径", "backup", "workspace", "uv", "python", "path", "directory"], "Comm": ["discord", "telegram", "频道", "channel", "bot", "notification", "message"], "Security": ["security", "凭证", "api key", "密码", "permission", "access", "auth"] } ``` All Markdown files in the memory directory are eligible for processing: ```python def get_unprocessed_files(force=False): """Get list of unprocessed daily memory files.""" if not MEMORY_DIR.exists(): return [] processed = set() if force else get_processed_files() unprocessed = [] for f in sorted(MEMORY_DIR.glob("*.md")): if f.name not in processed and f.name != "heartbeat-state.json": unprocessed.append(f) return unprocessed ``` Selected lines are then persisted without redaction: ```python def process_file(file_path, review_only=False): """Process a single memory file.""" content = extract_content_from_file(file_path) result = simple_extract(content, file_path.name) if no ...[truncated 2335 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add secret detection and redact values matching API-key, token, password, private-key, and credential patterns before persistence. 2. Use an explicit allowlist of memory sources rather than processing every `*.md` file. 3. Require user confirmation when a line is classified as security- or credential-related. 4. Exclude credential-bearing categories from extraction by default. 5. Create the database with owner-only permissions, such as mode `0600`, and ensure its parent directory is not broadly readable. 6. Document retention, deletion, export, and database-access policies. 7. Consider authenticated encryption at rest where the threat model includes local users or backup exposure. 8. Add tests proving that representative secrets are redacted and never appear in database content or keyword fields. ]]>

other

Warning
Location
scripts/trust_scorer.py:89
Finding
Undeclared Persistent Trust Scoring Can Bias External Tool Selection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/trust_scorer.py:3-34`, `scripts/trust_scorer.py:89-122`; invoked by `scripts/nightly_meta_extract.py:340-349` **Vulnerability Type**: Persistent agent behavior manipulation **Risk Level**: Medium ### Vulnerable Code The module states that its persistent scores are intended to guide tool selection: ```python """ trust_scorer.py - Dynamic Trust Scoring Maintains trust scores for tools/models based on recent failures. Scores persist in insight.db and guide automated decisions: - X.com links → always use grok42 (highest trust) - General web → use tavily as first fallback (high trust) - web_fetch → low trust, used only as last resort Runs during heartbeat to keep scores up to date. """ ``` It hard-codes initial preferences and adjustments: ```python CATEGORY_ADJUSTMENTS = { "network_block": {"web_fetch": -5, "tavily": +2}, "third_party_service": {"web_fetch": -5, "grok42": +2}, "silent_wait": {}, "model_selection": {"grok42": +1}, "tool_error": {"web_fetch": -2} } DEFAULT_SCORES = { "tavily": 85, "grok42": 95, "web_fetch": 40 } ``` Every execution processes all parsed historical entries again: ```python def update_trust_scores(): """Main entry: analyze recent failures and adjust trust scores.""" entries = parse_entries() if not entries: print("\n📊 Trust Scores: No failure records found to adjust scores.\n") return conn = sqlite3.connect(DB_PATH) ensure_table(conn) init_default_scores(conn) scores = load_scores(conn) # Apply adjustments based on entries (simple: each entry adjusts once) for entry in entries: cat = categorize_friction(entry["description"]) adj = CATEGORY_ADJUSTMENTS.get(cat, {}) for tool, delta in adj.items(): current = scores.get(tool, DEFAULT_SCORES.get(tool, 50)) new_score = clamp(current + delta) scores[tool] = new_score ...[truncated 2305 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove trust scoring from the memory Skill or package it as a separate, explicitly enabled feature. 2. Require informed user consent before storing or applying tool-routing preferences. 3. Assign each regression event a stable identifier and record processed identifiers so adjustments are applied only once. 4. Authenticate or otherwise restrict writers to `REGRESSIONS.md`. 5. Replace hard-coded vendor preferences with user-configurable, neutral policies. 6. Require user approval before a score change affects tool routing. 7. Store an auditable history of the source event, old score, new score, and adjustment reason. 8. Provide a reset mechanism and enforce bounded adjustment rates. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/nightly_meta_extract.py:243
Finding
Extraction Log Update Can Delete Existing MEMORY.md Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/nightly_meta_extract.py:243-274` **Vulnerability Type**: Unsafe file reconstruction and destructive overwrite **Risk Level**: Medium ### Vulnerable Code ```python def update_memory_md(filename, result): """Update MEMORY.md extraction log.""" if not MEMORY_MD.exists(): return content = MEMORY_MD.read_text() today = datetime.now().strftime("%Y-%m-%d") # Create new table row summary = result["l2_facts"][0][:50] if result["l2_facts"] else "No facts extracted" new_row = f"| {today} | `memory/{filename}` | {result['l1_category']}: {summary}... |\n" # Find table and insert new row table_marker = "## 📅 Recent extraction records" if table_marker in content: parts = content.split(table_marker) if len(parts) > 1: table_start = parts[1].find('|') if table_start > 0: lines = parts[1][table_start:].split('\n') insert_pos = 1 # After header while insert_pos < len(lines) and lines[insert_pos].startswith('|'): insert_pos += 1 lines.insert(insert_pos, new_row.strip()) parts[1] = '\n'.join(lines) content = table_marker + parts[1] # Update last updated timestamp content = re.sub( r'\*Last Updated:[^*]+\*', f'*Last updated: {today} | Next auto-extraction: during heartbeat check*', content ) MEMORY_MD.write_text(content) ``` ### Technical Analysis After splitting the file at `table_marker`, the function reconstructs the result as: ```python content = table_marker + parts[1] ``` This omits `parts[0]`, which contains all content before the extraction-log marker. The function then overwrites the original file directly. It does not create a backup, use atomic replacement, or verify that unrelated content was preserved. The use of `content.split(table_marker)` without a m ...[truncated 986 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Split only once and preserve both sides of the marker: ```python prefix, suffix = content.split(table_marker, 1) content = prefix + table_marker + updated_suffix ``` 2. Parse and modify only the intended table instead of rebuilding the rest of the document from partial fragments. 3. Write to a temporary file in the same directory, flush and synchronize it, and atomically replace the original. 4. Create a timestamped backup before modification. 5. Refuse to overwrite the file if the expected table structure is missing or malformed. 6. Add regression tests confirming that content before and after the table remains byte-for-byte intact. 7. Add tests for duplicate markers, empty tables, malformed rows, and interrupted writes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
docs/api.md:153
Finding
Documented Query Function Constructs LIMIT Clause Through String Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `docs/api.md:153-174` **Vulnerability Type**: SQL injection in documented integration code **Risk Level**: Medium ### Vulnerable Code ```python def query_memories(search_term=None, category=None, limit=None): """Query memory records with optional filters.""" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() query = "SELECT category, content, created_at FROM long_term_memory WHERE 1=1" params = [] if category: query += " AND category = ?" params.append(category) if search_term: query += " AND (content LIKE ? OR keywords LIKE ?)" params.extend([f'%{search_term}%', f'%{search_term}%']) query += " ORDER BY created_at DESC" if limit: query += f" LIMIT {limit}" cursor.execute(query, params) results = cursor.fetchall() ``` ### Technical Analysis The function correctly binds `category` and `search_term`, but concatenates `limit` directly into the SQL statement. If a consuming application supplies untrusted text as `limit`, that input becomes SQL syntax. Python's standard SQLite `execute()` generally rejects multiple statements, which constrains some destructive payloads. However, an attacker can still attempt to alter the existing statement structure, manipulate query behavior, trigger errors, or expose more data than intended. The exact exploitability depends on how an integration obtains and validates `limit`. Although this code is in documentation rather than an executed project module, it is presented as a reusable API implementation and may be copied directly into downstream Skills. ### Attack Path 1. A developer copies the documented `query_memories()` function into an integration. 2. The integration allows a request, tool argument, or agent-generated value to control `limit`. 3. An attacker supplies crafted SQL syntax instead of an integer. 4. The function concatenates that value into the query. 5. SQLite p ...[truncated 686 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Convert `limit` to an integer and enforce an acceptable range before query execution: ```python if limit is not None: limit = int(limit) if not 1 <= limit <= 1000: raise ValueError("limit must be between 1 and 1000") query += " LIMIT ?" params.append(limit) ``` 2. Reject booleans, floating-point values, negative values, and strings containing non-decimal characters. 3. Keep all externally influenced values in bound parameters. 4. Add tests using SQL metacharacters and malformed values to verify rejection. 5. Correct the API documentation so downstream users do not reproduce the unsafe pattern. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
Findings (20)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
"summary": lines[0],
                "details": lines
            })
    return rules

def apply_guardrails():
    """Run guardrail check and output active rules."""
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The README instructs users to install the skill via `npx skills add timothysong0w0/claw-memory-lite --agent openclaw` without pinning a specific version or immutable commit. That creates a supply-chain risk: a later compromised or malicious update to the referenced package/repository could be fetched and executed by users at install time, especially because this is an installation command presented as the recommended path.

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding
The installation command uses `npx skills add ...` without pinning a specific version of the package or referenced skill artifact. This can cause users to fetch and execute whatever version is current at install time, creating a supply-chain risk if the package, registry entry, or dependency chain is updated maliciously or compromised later.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This is a markdown file, so SQP-2 applies to omissions in user-facing documentation. The 'Delete Record' section shows a function that permanently removes records, but the surrounding documentation does not warn that the action is destructive or irreversible.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The documentation provides a function to export all memory records to an arbitrary JSON file path without any warning about sensitive data exposure or handling requirements. Because the dataset may contain long-term memory content, source files, and timestamps, normal use could lead to accidental exfiltration, insecure storage, or over-broad sharing of potentially sensitive information.

Rp1

Medium
Category
MCP Rug Pull
Confidence
89% confidence
Finding
The installation guide instructs users to run `npx skills add ...` without pinning a specific version or commit of the installer or referenced package. This creates a supply-chain risk because future package changes, account compromise, or a malicious republish could cause different code to execute than what the user reviewed in the repository.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
These instructions say the installer will automatically copy scripts, initialize a database, and update `HEARTBEAT.md`, but they do not clearly warn that workspace files will be modified. Silent file writes and configuration changes increase the chance of unintended persistence or unexpected environment modification, especially in an agent workspace.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# Edit crontab
crontab -e

# Add daily extraction at 3 AM UTC
0 3 * * * cd /home/node/.openclaw/workspace && python3 scripts/extract_memory.py
Confidence
91% confidence
Finding
The guide recommends adding a cron job that will execute the extraction script daily, creating ongoing persistence on the host. Even if intended for maintenance, recurring execution expands risk because later script modifications, repository compromise, or unsafe script behavior would be triggered automatically without fresh user review.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# Edit crontab
crontab -e

# Daily extraction at 3 AM UTC
0 3 * * * cd /home/node/.openclaw/workspace && python3 scripts/extract_memory.py >> logs/memory_extraction.log 2>&1
Confidence
85% 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.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The documentation recommends installing a skill via `npx skills add app/skills@weather` without pinning a specific version or immutable source. This can lead to non-reproducible installs and supply-chain risk if the upstream package changes or is compromised, though in this README example context it appears instructional rather than intentionally harmful.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The text states that all Python execution must use `/root/.local/bin/uv`, which imposes a fixed tooling/locale-style operational policy on all users without presenting it as optional or context-specific. This is a natural-language policy constraint that is not justified as environment-specific in the file and does not offer user choice.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This script prints raw database record content directly to stdout, and the database is explicitly described as long-term memory storage, which may contain sensitive operational, project, communication, or security data. In CLI and agent contexts, stdout is often logged, captured, or surfaced to other components, so unrestricted output can cause accidental data exposure even without an external attacker.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The top-level documentation claims the script 'Combines' daily extraction, guardrail regression handling, friction detection, and dynamic trust scoring as core behavior. In practice, L54-L277 implement only markdown parsing, SQLite writes, and MEMORY.md updates; the other pipelines are merely attempted imports and optional function calls at L323-L347, with graceful skipping if absent. This is an active mismatch between documentation and actual implemented behavior.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script persists extracted content from workspace memory files into a SQLite database automatically, and those files may contain sensitive notes, credentials, or operational context. In an agent skill context, silent long-term storage increases data retention risk and broadens the blast radius if the database is later accessed by other components or an attacker.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script updates MEMORY.md automatically, modifying a user-visible workspace file without an explicit confirmation step. In this context, silent rewriting can tamper with audit/history records, surprise users, and make it harder to distinguish original notes from tool-generated content, especially when run on a schedule.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This markdown file instructs users to run scheduled and manual extraction commands and states that the extraction script automatically updates `MEMORY.md`. While the behavior is described, there is no clear caution that these operations will write to or alter local user data files on an ongoing basis, which is the kind of user-impacting behavior that should be explicitly warned about in markdown guidance.

Scope Creep

Low
Category
Excessive Agency
Content
- [API Reference](api.md) - Database schema and functions
- [Examples](../examples/) - Sample usage patterns
- [Contributing](../README.md#contributing) - Extend functionality
Confidence
75% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The README shows an execution command for extraction immediately after a preview mode, but does not clearly warn that the non-review invocation will modify persistent state. Users may run the command without understanding it writes to the database or changes stored memory data.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The docstring states the friction categorization is based on "generic English," which means reports in other languages may not be handled correctly. This creates a natural-language policy concern because the skill implicitly assumes one language without offering a choice or documenting a justified locale constraint.

Static analysis

No suspicious patterns detected.