Back to skill

Security audit

Openclaw Deeprecall

Security checks for vulnerabilities and agentic risk

Overview

DeepRecall has a coherent memory purpose, but it stores raw session memory permanently and has under-scoped paths that can read, transmit, or delete more local data than users would reasonably expect.

Review this skill carefully before installing. Use it only for memory content you are comfortable storing long term, set no_store_raw or store_raw_content false where possible, avoid scheduling automatic jobs until retention and provider settings are understood, and do not process arbitrary file paths. Prefer an explicit trusted provider or local-only extraction, and keep sensitive secrets, credentials, private keys, and regulated data out of memory files.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/memory_summarizer.py:863
Finding
Arbitrary Local File Read, Persistent Archival, and External Transmission<![CDATA[ ## Vulnerability Details **File Location**: `scripts/memory_summarizer.py:292, 321-325, 863`; `scripts/memory_db_tool.py:137-143` **Vulnerability Type**: Path traversal and missing workspace-boundary enforcement **Risk Level**: High ### Vulnerable Code ```python # scripts/memory_summarizer.py file_path = Path(memory_dir) / process_file success = await summarizer.process_single_file( file_path, store_raw=store_raw ) ``` ```python # scripts/memory_db_tool.py file_path = Path(args.process_file) if not file_path.is_absolute(): file_path = Path(summarizer.memory_dir) / file_path success = asyncio.run( summarizer.process_single_file(file_path, store_raw) ) ``` After reading the selected file, its contents are included in the LLM prompt and transmitted: ```python full_prompt = self.extraction_prompt_template + "\n" + content[:max_content_length] ``` ```python async with session.post( f"{base_url}/chat/completions", json=payload, headers=headers, timeout=timeout ) as response: ``` ### Technical Analysis The `process_file` parameter is intended to identify a memory file relative to the workspace memory directory. However, neither entry point canonicalizes the resulting path nor verifies that it remains beneath the authorized memory root. An absolute path bypasses the intended base directory because joining a `Path` with an absolute operand selects the absolute path. A relative value containing `../` can similarly traverse outside the memory directory. Symlinks within the memory directory can also point to files outside the permitted tree. `process_single_file()` reads the resulting path. By default, the complete file is stored in the L2 SQLite archive, while up to `max_content_length` characters are inserted into the LLM prompt and sent over the network. ### Attack Path 1. An attacker or manipulated agent invokes `summarize_memory_files` or the CLI with a path such as: - `/home/user/.ssh/id_rsa` - `../../.env` ...[truncated 895 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve and validate both the memory root and requested path: ```python memory_root = Path(memory_dir).resolve(strict=True) if Path(process_file).is_absolute(): raise ValueError("Absolute paths are not permitted") candidate = (memory_root / process_file).resolve(strict=True) if not candidate.is_relative_to(memory_root): raise ValueError("Requested file is outside the memory directory") ``` 2. Reject symlink escapes and require the target to be a regular file. 3. Restrict accepted files to the necessary extension, such as `.md`. 4. Apply the same validation in the module tool wrapper, CLI, and `process_single_file()` so callers cannot bypass checks. 5. Require explicit approval before transmitting file contents externally. 6. Add secret scanning and redaction before archival or network transmission. 7. Disable raw-content archival by default. 8. Run the Skill under an operating-system account that cannot read unrelated credentials or workspaces. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/memory_summarizer.py:233
Finding
Sensitive Agent Memory Sent to an Unrestricted Automatically Selected Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/memory_summarizer.py:131-145, 233-258, 292-325` **Vulnerability Type**: Unrestricted sensitive-data transmission and unsafe provider selection **Risk Level**: High ### Vulnerable Code ```python possible_paths = [ Path.home() / ".openclaw" / "openclaw.json", Path("/etc/openclaw/openclaw.json"), Path.cwd().parent / "openclaw.json", Path.cwd() / "openclaw.json" ] ``` ```python if preferred_provider and preferred_provider in providers: api_config = providers[preferred_provider] provider_key = preferred_provider else: # Auto-select first available provider with baseUrl and apiKey for name, provider_cfg in providers.items(): if "baseUrl" in provider_cfg and provider_cfg.get("apiKey"): api_config = provider_cfg provider_key = name print(f"Auto-selected provider: {name}") break ``` ```python base_url = api_config.get("baseUrl", "https://api.deepseek.com/v1") if base_url.endswith('/'): base_url = base_url.rstrip('/') api_key = api_config.get("apiKey", "") ``` ```python full_prompt = self.extraction_prompt_template + "\n" + content[:max_content_length] headers = { "Content-Type": "application/json", "Authorization": f"Bearer {api_key}" } payload = { "model": model_id, "messages": [ {"role": "user", "content": full_prompt} ], "temperature": temperature, "max_tokens": max_tokens } ``` ```python async with session.post( f"{base_url}/chat/completions", json=payload, headers=headers, timeout=timeout ) as response: ``` ### Technical Analysis The summarizer sends raw session-memory text to an OpenAI-compatible provider. If no preferred provider is selected, it automatically uses the first configuration entry containing a `baseUrl` and `apiKey`. The destination is not subject to an HTTPS requirement, hostname allowlist, explicit user approval, or checks against loop ...[truncated 1626 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit provider selection; do not automatically select the first configured provider. 2. Display the exact destination hostname and require informed consent before the first transmission and after destination changes. 3. Enforce HTTPS with valid certificate verification. 4. Maintain an approved provider-host allowlist. 5. Resolve destination addresses and reject loopback, link-local, private, and metadata-service ranges unless a local provider was explicitly authorized. 6. Redact API keys, passwords, private keys, tokens, and other secrets before constructing the request. 7. Add configurable data-classification rules so highly sensitive memories are never sent externally. 8. Prefer local rule-based extraction unless external processing is explicitly enabled. 9. Document external data handling, provider retention implications, and transmitted fields in the manifest. 10. Use separate, narrowly scoped API credentials for this Skill. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/memory_retriever.py:489
Finding
Cleanup Command Can Delete Markdown Files Outside the Agent Workspace<![CDATA[ ## Vulnerability Details **File Location**: `scripts/memory_db_tool.py:32-39, 76-81`; `scripts/memory_retriever.py:489-522, 583` **Vulnerability Type**: Unrestricted destructive filesystem operation **Risk Level**: Medium ### Vulnerable Code ```python cleanup_parser.add_argument( "--retention-days", type=int, default=1, help="Keep files newer than N days (default: 1)" ) cleanup_parser.add_argument( "--max-size-kb", type=int, default=250, help="Maximum total size in KB (default: 250)" ) cleanup_parser.add_argument( "--dry-run", action="store_true", help="Show what would be deleted without actually deleting" ) cleanup_parser.add_argument( "--memory-dir", type=str, help="Custom memory directory path (default: auto-detect)" ) ``` ```python result = cleanup_raw_files( retention_days=args.retention_days, max_size_kb=args.max_size_kb, memory_dir=args.memory_dir, dry_run=args.dry_run ) ``` ```python def cleanup_raw_files( retention_days: int = 1, max_size_kb: int = 250, memory_dir: str = None, dry_run: bool = False ) -> dict: ``` ```python md_files = glob.glob(os.path.join(memory_dir, "*.md")) ``` ```python if not dry_run: for file_info in files_to_delete: try: os.remove(file_info["path"]) deleted_files.append(file_info["path"]) deleted_size_kb += file_info["size_kb"] except Exception as e: continue ``` ### Technical Analysis The cleanup operation accepts an arbitrary `memory_dir` and immediately deletes selected `.md` files unless dry-run mode is explicitly requested. It does not verify that the directory is the current agent's memory directory or even beneath the workspace. The numeric arguments are also not constrained to non-negative values. A negative retention period produces a cutoff time in the future, causing existing files to qualify as old. A negative size limit can cause the size-based ...[truncated 1186 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the arbitrary `memory_dir` parameter from exposed tool calls, or restrict it to the resolved workspace memory root. 2. Resolve and compare the requested directory against `OPENCLAW_WORKSPACE/memory`. 3. Reject symlinks and paths outside the authorized directory. 4. Validate `retention_days >= 0` and `max_size_kb >= 0`, and impose reasonable upper bounds. 5. Default cleanup to dry-run mode. 6. Require explicit confirmation containing the directory and number of files before deletion. 7. Refuse cleanup if the detected directory appears to be a filesystem root, home directory, or project root. 8. Move files into a recoverable quarantine directory before permanent deletion. 9. Log each deletion failure rather than silently suppressing exceptions. 10. Consider deleting only files already recorded as successfully processed. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/memory_retriever.py:90
Finding
Unencrypted Permanent Storage of Complete Session Memory Without Retention Controls<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:82-98`; `scripts/memory_retriever.py:90-164`; `scripts/memory_summarizer.py:512-563` **Vulnerability Type**: Excessive sensitive-data retention and plaintext local storage **Risk Level**: Medium ### Vulnerable Code and Declared Behavior ```markdown ### l2_archive (Permanent Storage) - `date`, `source_file`, `raw_content` **Important**: Database records are permanent and never deleted. ``` ```python cursor.execute(''' CREATE TABLE IF NOT EXISTS l2_archive ( id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, source_file TEXT UNIQUE NOT NULL, raw_content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') ``` ```python cursor.execute( """ INSERT INTO l2_archive (date, source_file, raw_content) VALUES (?, ?, ?) """, (date, source_file, content) ) ``` Existing archive records are overwritten with the new plaintext content but are not subject to expiration: ```python cursor.execute( """ UPDATE l2_archive SET raw_content = ?, date = ? WHERE source_file = ? """, (content, date, source_file) ) ``` ### Technical Analysis Complete raw memory files are stored by default in a regular SQLite database. The implementation does not provide encryption, database-file permission hardening, record expiration, a deletion interface, sensitivity classification, or selective field redaction. The separate raw-file cleanup operation does not remove the archived database copy. This can create a false expectation that old session information has been deleted when it remains available through the L2 retrieval tool. Permanent raw storage is not necessary for the core L1 structured-fact search function. Exact archival may be an optional feature, but default indefinite retention exceeds the minimum data-storage privilege needed for summarization and fact retrieval. ### Attack Path 1. A memory file con ...[truncated 901 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Default `store_raw_content` to `false`; store structured facts unless the user explicitly enables raw archival. 2. Add configurable retention periods and automatic expiration for L1 and L2 records. 3. Provide commands to delete individual records, source-file archives, and the complete database. 4. Set restrictive database permissions at creation, such as owner read/write only. 5. Encrypt sensitive database content using a properly managed key where the threat model requires protection at rest. 6. Detect and redact credentials and private keys before storage. 7. Separate highly sensitive records from ordinary facts and require additional authorization for raw retrieval. 8. Make cleanup semantics explicit: deleting source files does not delete database archives. 9. Support verifiable purge operations, including backup-retention guidance. 10. Minimize archive content to only the portions required for exact retrieval. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:3
Finding
Unpinned Network-Facing Runtime Dependency<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:3` **Vulnerability Type**: Non-reproducible third-party dependency resolution **Risk Level**: Low ### Vulnerable Code ```text # DeepRecall Dependencies # Required for LLM summarization functionality aiohttp>=3.8.0 ``` ### Technical Analysis The lower-bound-only requirement allows installation of any future `aiohttp` release. As a result, the exact code installed in the Skill environment is not fixed to the version reviewed during the audit. No malicious package or dependency-confusion name was identified. The risk is that future compromised, vulnerable, or incompatible releases could be installed automatically without a corresponding Skill review. This dependency is security-sensitive because it handles bearer credentials and transmits memory content. ### Attack Path 1. The Skill is installed or rebuilt at a later date. 2. The package resolver selects a newer `aiohttp` release than the one originally tested. 3. The unreviewed release executes in the Skill process. 4. A compromised or vulnerable release could access API credentials, request payloads, local process data, or network traffic handled by the library. ### Impact Assessment A malicious dependency release would execute with the same filesystem and network privileges as the Skill. It could potentially read memory files, access provider credentials already loaded into the process, alter requests, or exfiltrate stored data. The finding is lower severity because the audited project does not identify a currently malicious dependency, and exploitation depends on a future unsafe release or compromised package distribution. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `aiohttp` to a reviewed exact version. 2. Generate and commit a lockfile suitable for the deployment environment. 3. Require package hashes during installation, for example through a hash-locked requirements file. 4. Use a trusted package index and disable unexpected supplemental indexes. 5. Run dependency vulnerability scanning in CI. 6. Upgrade only through a controlled review and testing process. 7. Record transitive dependency versions so installations are reproducible. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Rogue AgentSelf-Modification, Session Persistence
Findings (24)

Ssd 3

High
Confidence
99% confidence
Finding
The skill explicitly states that exact raw session content is stored permanently and that records are never deleted. Indefinite retention of verbatim user content materially increases the chance of long-term exposure of secrets, personal data, proprietary information, and prompt history through later retrieval, compromise, or misuse.

Ssd 3

High
Confidence
98% confidence
Finding
This code transmits complete memory content to an external LLM provider for processing, and the prompt explicitly solicits user profile, identity, project, and technical information. In the context of a memory system, that content is especially likely to contain sensitive disclosures, credentials, internal architecture notes, or personal data, making third-party exfiltration a serious confidentiality risk.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The guide documents configuration that sends content to third-party LLM providers but does not clearly warn users that their data may leave the local environment. In a memory/summarization feature, users may reasonably include sensitive notes or workspace content, so omission of a transmission warning creates a real privacy and compliance risk even if the document is otherwise instructional.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The documented default `store_raw_content: true` means original user content is retained, but the guide does not clearly warn about persistence, retention scope, or privacy implications. Retaining raw content can increase exposure of secrets, personal data, and internal documents if storage is later accessed, backed up, or exfiltrated.

Ssd 3

Medium
Confidence
96% confidence
Finding
The skill instructs automatic tool registration and broad future-use retrieval for prior work, decisions, people, preferences, and todos, effectively enabling persistent cross-session memory by default. This widens the blast radius of any sensitive data captured once, because future interactions may surface it without fresh user intent or contextual authorization.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill documents permanent storage of raw session content and the use of an external LLM for summarization, but it does not present a clear, prominent privacy warning or require informed user consent before either action. This creates a real risk that sensitive prompts, notes, credentials, or personal data are retained indefinitely and potentially disclosed to third-party model providers.

Ssd 3

Medium
Confidence
92% confidence
Finding
Describing the database as a permanent vault for all extracted knowledge promotes indefinite accumulation of user-derived information and normalizes over-retention. Even without an exploit chain in the markdown itself, this design encourages broad preservation and later resurfacing of sensitive context beyond the original purpose of collection.

Session Persistence

Medium
Category
Rogue Agent
Content
## Self-Bootstrapping Design

DeepRecall features zero-config deployment:
- Tables are created automatically on first use (`CREATE TABLE IF NOT EXISTS`)
- No "no such table" errors - database self-initializes
- Indexes are created for optimal query performance
- Works out-of-the-box with no manual setup
Confidence
81% confidence
Finding
Self-initializing persistent storage is not inherently unsafe, but in this skill it materially supports silent, durable session persistence without explicit setup or user review. In the context of a memory system that stores raw content and facts across sessions, automatic database creation lowers friction for broad retention and increases the chance of unnoticed data accumulation.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The manifest auto-registers several tools, including data retrieval and cleanup operations, without any explicit trigger boundaries, consent requirements, or examples of when they must not be invoked. In an agent setting, ambiguous tool scope can lead to unintended access to stored memory or accidental destructive actions such as cleanup being invoked without a clearly scoped user request.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The module docstring presents this file as a memory retrieval engine focused on searching and retrieving content. In practice, constructing MemoryRetriever bootstraps the database schema, creates directories, creates triggers/indexes, and other functions mark files as processed and delete raw files, which are active write/delete side effects rather than retrieval-only behavior.

Unbounded Output

Medium
Category
Output Handling
Content
clean_type = fact_type
            
            # Compact format: [date | type | source:file] content
            # Note: no truncation is applied; content is returned in full
            line = f"[{date} | {clean_type} | source:{source_file}] {content}"
            formatted.append(line)
Confidence
88% confidence
Finding
L1 retrieval returns content in full with no truncation or output-size controls, and L2 retrieval also returns complete raw content. In an agent environment, this can leak excessive sensitive memory contents into model context, logs, or downstream tools and can also cause denial-of-service-like token bloat when large records are retrieved.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The cleanup function claims to operate only on workspace memory files, but it accepts an arbitrary memory_dir and then deletes matching .md files in that directory. In an agent/tool context, any caller able to influence memory_dir can turn a maintenance helper into a generic file-deletion primitive against accessible paths, causing unintended data loss.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The function deletes files automatically once retention or size thresholds are met, with no interactive confirmation or user-visible warning at execution time. In an autonomous agent setting, silent destructive behavior increases the chance of accidental or policy-bypassing data loss, especially if invoked with unexpected parameters or against the wrong directory.

Ssd 3

Medium
Confidence
93% confidence
Finding
The tool stores full raw memory files in the archive database by default, creating long-term retention of potentially sensitive information beyond what may be necessary for fact extraction. Persistent storage increases blast radius if the SQLite database is accessed by other local users, included in backups, or later exfiltrated.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Warning: No LLM API configuration found, using rule-based extraction")
                return await self._extract_facts_with_rules(content)
            
            base_url = api_config.get("baseUrl", "https://api.deepseek.com/v1")
            # Remove trailing slash to avoid double slashes in URL concatenation
            if base_url.endswith('/'):
                base_url = base_url.rstrip('/')
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Warning: No LLM API configuration found, using rule-based extraction")
                return await self._extract_facts_with_rules(content)
            
            base_url = api_config.get("baseUrl", "https://api.deepseek.com/v1")
            # Remove trailing slash to avoid double slashes in URL concatenation
            if base_url.endswith('/'):
                base_url = base_url.rstrip('/')
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code sends raw memory content to a configured external LLM endpoint during normal processing, but there is no explicit user consent or warning at the point of transmission. Because the prompt explicitly targets personal, project, and technical facts, users may unknowingly export sensitive material to third-party providers.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The statement "All content in English for international compatibility" imposes a language policy rather than offering a user choice. This is a natural-language policy violation because it forces a specific language without opt-in or documented necessity.

Vague Triggers

Low
Confidence
88% confidence
Finding
Allowing an empty query to return the latest facts creates a permissive default that may disclose memory contents even when the user did not ask for retrieval with sufficient specificity. Because this skill is a persistent memory system, broad default recall increases the chance of unintended exposure of prior session data or sensitive stored facts.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# DeepRecall Dependencies
# Required for LLM summarization functionality
aiohttp>=3.8.0

# SQLite3 is included in Python standard library
# No additional dependencies needed for core memory retrieval functions
Confidence
97% confidence
Finding
The dependency is specified with a minimum version only, which allows installation of any later release and makes builds non-reproducible. This increases supply-chain risk because different environments may resolve to different versions, including ones with breaking changes or known vulnerabilities.

Unverifiable Dependency: aiohttp has 16 known advisory(ies) (CVE-2024-52303 (aiohttp has a memory leak when middleware is enabled when requesting a resource ); CVE-2026-54279 (aiohttp: Host-Only Cookies Become Domain Cookies After CookieJar Persistence); CVE-2026-34514 (AIOHTTP has CRLF injection through multipart part content type header constructi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding
Because aiohttp is not pinned, it is impossible to verify from this manifest whether the installed version is one affected by known advisories. In a skill that performs LLM summarization over networked components, using a vulnerable HTTP client library could expose the system to issues such as request handling flaws, injection, cookie handling weaknesses, or denial of service depending on the resolved version.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The function unlinks .md.processed marker files automatically when their corresponding source files are absent. This is another file-deletion path with no confirmation or explicit user-facing notification at execution time.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The code labels the prompt template as an English version and uses fixed English instructions for analysis. This imposes a specific language choice in natural-language behavior without any user opt-in or documented justification for the locale constraint.

Intent-Code Divergence

Low
Confidence
93% confidence
Finding
The --test-config help text says it will "Test OpenClaw configuration and API connectivity," which implies an active network connectivity check. However, when invoked, the code only calls test_configuration(), and that function reads config files and reports provider/model selection details without making any API request.

Static analysis

No suspicious patterns detected.