Back to skill

Security audit

上下文缓存管理器

Security checks for vulnerabilities and agentic risk

Overview

This context-caching skill has a coherent purpose, but it stores highly sensitive agent context and restores it with unsafe pickle deserialization and weak session-id/path controls.

Review carefully before installing. This skill is not clearly malicious, but it should only be used in a controlled local environment where cache files cannot be modified by other users or processes. Avoid using it with secrets, credentials, private customer data, or sensitive system prompts until it replaces pickle with safe serialization, validates session IDs, limits what it stores, and applies restrictive cache permissions or encryption.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
context_cache_manager.py:129
Finding
Arbitrary Code Execution Through Unsafe Pickle Deserialization<![CDATA[ ## Vulnerability Details **File Location**: `context_cache_manager.py:129-136` **Vulnerability Type**: Unsafe deserialization **Risk Level**: Critical ### Vulnerable Code ```python def load_snapshot(session_id: str) -> Optional[ContextSnapshot]: """从磁盘加载快照""" cache_path = get_cache_path(session_id) # 查找可能的文件 for file_path in CACHE_DIR.glob(f"{session_id}-*.pkl.gz"): try: with gzip.open(file_path, 'rb') as f: data = pickle.load(f) return ContextSnapshot(**data) except Exception: continue return None ``` ### Technical Analysis The application deserializes cache files with `pickle.load()`. Python pickle is an executable serialization format: a crafted object can define reduction operations that invoke arbitrary functions during deserialization. The cache file is not authenticated or otherwise verified before it is loaded. Catching exceptions does not mitigate this issue because malicious reduction operations execute during `pickle.load()` before the function returns or raises a subsequent validation error. An attacker who can create or replace a matching `.pkl.gz` file in the cache directory can therefore execute arbitrary Python code when the affected session is restored or forked. ### Attack Path 1. The attacker obtains write access to `~/.openclaw/workspace/tmp/context-cache`, or exploits another path-handling or local file-write weakness. 2. The attacker generates a malicious pickle payload whose reduction method executes a command or Python callable. 3. The payload is gzip-compressed and saved using a name matching `<session_id>-*.pkl.gz`. 4. The victim invokes `restore()` or `fork_context()` for the matching session. 5. `load_snapshot()` opens the attacker's file and passes its contents to `pickle.load()`. 6. The embedded reduction operation executes with the privileges of the Agent process. ### Impact Assessment Successful exploitation ...[truncated 466 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace pickle with a non-executable serialization format such as JSON. 2. Validate the complete decoded schema before constructing `ContextSnapshot`, including: - Required and permitted field names - Exact field types - Maximum string and collection sizes - Permitted `state` values - Message object structure 3. Reject unknown fields and malformed snapshots rather than silently continuing. 4. Authenticate snapshots with a keyed MAC if local cache tampering is within the threat model. 5. If legacy pickle migration is required, perform it once in a tightly isolated, low-privilege process. Do not load unauthenticated legacy pickle files in the main Agent process. 6. Add tests proving that malformed or attacker-controlled serialized data cannot invoke code or construct unexpected object types. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
context_cache_manager.py:52
Finding
Filesystem Path Traversal and Cross-Session Cache Matching Through Unvalidated Session IDs<![CDATA[ ## Vulnerability Details **File Location**: `context_cache_manager.py:52-56` and `context_cache_manager.py:129-136` **Vulnerability Type**: Path traversal and glob-pattern injection **Risk Level**: High ### Vulnerable Code ```python def get_cache_path(session_id: str) -> Path: """生成缓存文件路径""" ensure_cache_dir() date_str = datetime.now().strftime("%Y%m%d") return CACHE_DIR / f"{session_id}-{date_str}.pkl.gz" ``` ```python def load_snapshot(session_id: str) -> Optional[ContextSnapshot]: """从磁盘加载快照""" cache_path = get_cache_path(session_id) # 查找可能的文件 for file_path in CACHE_DIR.glob(f"{session_id}-*.pkl.gz"): try: with gzip.open(file_path, 'rb') as f: data = pickle.load(f) return ContextSnapshot(**data) except Exception: continue return None ``` ### Technical Analysis `session_id` is accepted from callers and inserted directly into both a filesystem path and a glob expression. Path separators and traversal components such as `../` can cause the path produced by `get_cache_path()` to resolve outside the intended cache directory. An absolute-path-like value may also override the base path depending on the constructed value and platform behavior. Glob metacharacters such as `*`, `?`, and character classes can broaden the files selected by `load_snapshot()`. This can cause one session to load another session's snapshot. Because matched files are also passed to `pickle.load()`, the issue expands the reachability of the unsafe-deserialization vulnerability. Although `cache_path` is computed in `load_snapshot()`, it is not used to enforce exact file selection. ### Attack Path #### Path traversal during capture 1. An attacker controls or influences the session ID supplied to `ContextCacheManager`. 2. The attacker supplies a session ID containing path separators or traversal components. 3. `get_cache_path()` appends the unvalidated value ...[truncated 1113 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate every session ID before filesystem use with a conservative allowlist, for example: ```python SESSION_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]{1,128}$") ``` 2. Reject path separators, traversal components, glob metacharacters, control characters, empty identifiers, and overlong values. 3. Resolve the generated path and verify that it remains under the resolved cache directory: ```python candidate = (CACHE_DIR / filename).resolve() candidate.relative_to(CACHE_DIR.resolve()) ``` 4. Do not use caller-controlled values as glob patterns. Generate one canonical filename or maintain a trusted index that maps validated session IDs to exact paths. 5. If multiple dated snapshots are required, enumerate files independently and compare parsed, validated identifier fields rather than interpolating user input into a glob. 6. Use exclusive or atomic writes to reduce unintended overwrites and race conditions. 7. Apply the same identifier validation to parent and child IDs passed to `fork_context()`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
context_cache_manager.py:113
Finding
Sensitive Agent Context Stored Without Confidentiality or Explicit Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `context_cache_manager.py:113-122` and `context_cache_manager.py:145-153` **Vulnerability Type**: Insecure storage of sensitive data **Risk Level**: Medium ### Vulnerable Code ```python def save_snapshot(snapshot: ContextSnapshot) -> Path: """保存快照到磁盘""" cache_path = get_cache_path(snapshot.session_id) # pickle + gzip压缩 with gzip.open(cache_path, 'wb') as f: pickle.dump(asdict(snapshot), f) # 更新索引 update_cache_index(snapshot) return cache_path ``` ```python index[snapshot.session_id] = { "created_at": snapshot.created_at, "size_chars": snapshot.size_chars, "compressed": snapshot.compressed, "state": snapshot.state } CACHE_INDEX_FILE.parent.mkdir(parents=True, exist_ok=True) with open(CACHE_INDEX_FILE, 'w', encoding='utf-8') as f: json.dump(index, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis Snapshots contain system prompts, rendered system prompts, message histories, and content-replacement state. These fields may include confidential conversation data, operational instructions, tokens, credentials copied into messages, or other sensitive context. The snapshots are written in gzip-compressed pickle files. Gzip only compresses data and does not provide encryption, integrity, or access control. File and directory creation rely on the process's inherited umask rather than explicitly enforcing owner-only permissions. The cache index is handled similarly. Consequently, cache confidentiality depends on external workspace permissions and runtime configuration that the module neither verifies nor enforces. ### Attack Path 1. A session processes sensitive prompts, messages, or replacement-state data. 2. `capture()` creates a snapshot containing that information. 3. `save_snapshot()` writes the snapshot to the workspace cache without encryption and without explicitly setting owner-onl ...[truncated 728 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create cache and index directories with owner-only permissions, such as `0700`. 2. Create snapshot and index files with mode `0600`, independent of the inherited umask. 3. Write to a securely created temporary file, flush and synchronize it, then atomically replace the destination. 4. Encrypt sensitive snapshots at rest using a properly managed key when local storage disclosure is in scope. 5. Add integrity protection so unauthorized modifications are detected before loading. 6. Minimize retained data: - Exclude secrets and credentials. - Redact sensitive message fields. - Avoid storing rendered prompts unless restoration strictly requires them. 7. Document what data is retained, where it is stored, and how long it remains. 8. Provide explicit deletion and secure cleanup controls, and keep the cache lifetime as short as operationally possible. 9. Verify permissions at startup and fail closed or warn prominently if the cache location is accessible to unintended users. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Vague Triggers

Medium
Confidence
91% confidence
Finding
The skill's invocation guidance is broad enough that an agent could trigger context capture, compaction, or restore in situations where it is not strictly necessary. Because this skill handles full system prompts, message history, and content-replacement state, overbroad activation increases the chance of unnecessary persistence and propagation of sensitive conversational context across sessions or child agents.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The skill explicitly permits writing cached context to disk and uses a persistent cache directory with serialized session data, but it does not warn users that sensitive prompts, chat history, and internal state may be stored locally for up to 24 hours. In this context, the cached material can include secrets, personal data, and system instructions, so silent persistence materially increases confidentiality risk if the host is shared, compromised, or logs/backups are accessible.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The documentation for `compress_messages` says older messages will keep key fields, implying per-message retention in compressed form. The actual code instead discards all non-system older messages and inserts a single synthetic summary entry, which materially contradicts the stated behavior and changes what context is preserved.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The snapshot contains potentially sensitive prompts, message history, and replacement state, and it is written to disk automatically in a persistent cache location. In an agent-skill context, these artifacts may include secrets, proprietary prompts, or user data; persisting them without explicit consent, minimization, encryption, or permission hardening increases the risk of local disclosure and unintended retention.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Content
for file_path in CACHE_DIR.glob(f"{session_id}-*.pkl.gz"):
        try:
            with gzip.open(file_path, 'rb') as f:
                data = pickle.load(f)
                return ContextSnapshot(**data)
        except Exception:
            continue
Confidence
98% confidence
Finding
The code deserializes cache files with pickle.load() from a path derived from session IDs and by globbing matching files in a user-writable cache directory. Pickle is not safe for untrusted input: if an attacker can place or replace a matching .pkl.gz file, loading it can trigger arbitrary code execution during restore or fork operations.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
Much of the natural-language content is presented in Chinese, including core usage guidance and activation descriptions, with no indication that this locale is optional or required for a region-specific audience. This can violate language-choice policy when a skill effectively assumes a specific language without user opt-in.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The module’s natural-language documentation and comments are written in Chinese throughout, which can impose a language constraint on users or maintainers without any stated opt-in or locale justification. Under the stated policy, language-specific behavior or presentation should either offer choice or clearly document the constraint.

Missing User Warnings

Low
Confidence
85% confidence
Finding
The cleanup routine irreversibly removes cache files with unlink() once they are older than the configured age. The function has only an internal docstring and provides no user-visible notice that stored context data may be deleted automatically.

Static analysis

Detected: suspicious.prompt_injection_instructions

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
SKILL.md:27