Back to skill

Security audit

Session Reset

Security checks for vulnerabilities and agentic risk

Overview

This skill appears intended for OpenClaw session maintenance, but its reset and restore code can delete or overwrite files outside the intended session directories if given crafted inputs.

Review this skill carefully before installing. Its purpose is understandable, but do not use it on important OpenClaw environments until scope and restore inputs are strictly validated, path containment is enforced, and destructive commands are guarded by clear confirmations. Avoid --force, avoid untrusted backup manifests, and only pass known agent names or valid backup timestamps.

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
scripts/reset-session.py:170
Finding
Path Traversal in Agent Scope Enables Deletion of JSONL Files Outside the Agent Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/reset-session.py:170-175`, `scripts/reset-session.py:640-647`, `scripts/reset-session.py:323-334`, and `scripts/reset-session.py:399-403` **Vulnerability Type**: Path traversal and insufficient input validation **Risk Level**: High ### Vulnerable Code ```python def get_session_files(agent_id: str) -> List[Path]: """获取指定 agent 的所有 session 文件""" sessions_dir = AGENTS_DIR / agent_id / "sessions" if not sessions_dir.exists(): return [] return list(sessions_dir.glob("*.jsonl")) ``` ```python if "," in scope: # 多 agent 模式 sessions = {} for agent_id in scope.split(","): agent_id = agent_id.strip() agent_sessions = get_session_files(agent_id) if agent_sessions: sessions[agent_id] = [parse_session_file(f) for f in agent_sessions] ``` ```python for agent_id, agent_sessions in sessions.items(): agent_backup_dir = backup_path / agent_id agent_backup_dir.mkdir(exist_ok=True) manifest["agents"][agent_id] = [] for session in agent_sessions: src_file = session["file"] dst_file = agent_backup_dir / src_file.name shutil.copy2(src_file, dst_file) ``` ```python for agent_id, agent_sessions in sessions.items(): for session in agent_sessions: try: session_file = session["file"] session_file.unlink() print(f" {Colors.GREEN}✓{Colors.END} {agent_id}/{session['session_id']}") success += 1 except Exception as e: print(f" {Colors.RED}✗{Colors.END} {agent_id}/{session['session_id']}: {e}") failed += 1 ``` ### Technical Analysis Comma-separated `--scope` values are treated as agent identifiers and passed directly into filesystem path construction. The code does not reject absolute paths, path separators, or `..` traversal components. In `pathlib`, joining a base path with an absolute path discards the original base. ...[truncated 2072 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Accept only existing agent names returned by `discover_agents()`. 2. Reject empty names, absolute paths, path separators, `.` components, and `..` components. 3. Resolve every constructed path and verify containment before reading, copying, or deleting: ```python def require_descendant(candidate: Path, root: Path) -> Path: resolved_root = root.resolve(strict=True) resolved_candidate = candidate.resolve(strict=True) if not resolved_candidate.is_relative_to(resolved_root): raise ValueError("Path escapes the permitted root") return resolved_candidate ``` 4. Validate identifiers with a restrictive format such as: ```python AGENT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$") ``` 5. Reject symbolic links for agent directories, session directories, and session files, or safely resolve and validate their final targets. 6. Repeat the containment check immediately before `copy2()` and `unlink()` to reduce time-of-check/time-of-use risk. 7. Construct backup directory names from validated identifiers rather than raw CLI input. 8. Consider disabling `--force` for scopes that are not explicit built-in values, or require an additional non-interactive safety token. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/reset-session.py:448
Finding
Untrusted Backup Paths and Manifest Fields Enable Filesystem Traversal During Restoration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/reset-session.py:448-485` **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```python def restore_backup(timestamp: str) -> bool: """从备份恢复""" backup_path = BACKUP_DIR / timestamp manifest_file = backup_path / "backup.manifest" if not manifest_file.exists(): print(f"{Colors.RED}✗ 备份不存在: {timestamp}{Colors.END}") return False with open(manifest_file, 'r') as f: manifest = json.load(f) print(f"\n{Colors.CYAN}📦 备份信息:{Colors.END}") print(f" 时间戳: {timestamp}") print(f" 创建时间: {manifest.get('created_at', 'N/A')}") print(f" Agents: {len(manifest.get('agents', {}))}") print(f" 文件数: {manifest.get('total_files', 0)}") if not confirm_action("确认恢复此备份? (将覆盖现有 session 文件)"): print(f"{Colors.YELLOW}已取消{Colors.END}") return False print(f"\n{Colors.CYAN}🔄 执行恢复...{Colors.END}\n") success = 0 failed = 0 for agent_id, sessions in manifest.get("agents", {}).items(): agent_sessions_dir = AGENTS_DIR / agent_id / "sessions" agent_sessions_dir.mkdir(parents=True, exist_ok=True) for session_info in sessions: src_file = backup_path / agent_id / session_info["file"] dst_file = agent_sessions_dir / session_info["file"] try: shutil.copy2(src_file, dst_file) print(f" {Colors.GREEN}✓{Colors.END} {agent_id}/{session_info['session_id']}") success += 1 except Exception as e: print(f" {Colors.RED}✗{Colors.END} {agent_id}/{session_info['session_id']}: {e}") failed += 1 ``` ### Technical Analysis The restoration function trusts three separate path components: - The CLI-provided `timestamp`. - Each `agent_id` key in `backup.manifest`. - Each `session_info["file"]` value in the manifest. The documented timestamp ...[truncated 2440 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Strictly validate restore identifiers before constructing paths: ```python TIMESTAMP_PATTERN = re.compile(r"^\d{8}_\d{6}$") if not TIMESTAMP_PATTERN.fullmatch(timestamp): raise ValueError("Invalid backup timestamp") ``` 2. Resolve `backup_path` and require it to be an immediate directory beneath `BACKUP_DIR`. 3. Require manifest agent identifiers to match a restrictive identifier pattern and correspond to known Agents. 4. Require each manifest filename to be a basename: ```python filename = Path(session_info["file"]) if filename.name != session_info["file"] or filename.suffix != ".jsonl": raise ValueError("Unsafe backup filename") ``` 5. Resolve and verify every source path is beneath the selected backup directory. 6. Resolve and verify every destination path is beneath the intended Agent's `sessions` directory. 7. Reject symlinks in the backup directory, manifest, source path, destination directory, and destination file. 8. Display all resolved restore destinations before requesting confirmation. 9. Validate manifest structure and field types against a strict schema. 10. Add cryptographic integrity protection to manifests, or store and compare hashes for every backup file. 11. Use safe creation and overwrite policies. Where practical, restore to temporary files and atomically rename them only after verification. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/reset-session.py:204
Finding
Unknown Scope Values Fail Open and Select All Discovered Sessions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/reset-session.py:204-236` and `scripts/reset-session.py:638-648` **Vulnerability Type**: Fail-open authorization and destructive scope expansion **Risk Level**: Medium ### Vulnerable Code ```python agent_sessions = [] for session_file in sessions_dir.glob("*.jsonl"): # 解析 session 信息 session_info = parse_session_file(session_file) # 根据 scope 过滤 if scope == "default": # 默认:Discord 频道,排除 cron/subagent if session_info.get("kind") in ["cron", "subagent"]: continue elif scope == "agents": # 仅配置的默认 agents default_agents = get_default_agents() if not default_agents: print(f"{Colors.YELLOW}⚠️ 未配置默认 agents,请先运行: reset-session --init{Colors.END}") return {} if agent_id not in default_agents: continue if session_info.get("kind") in ["cron", "subagent"]: continue elif scope == "cron": if session_info.get("kind") != "cron": continue elif scope == "subagent": if session_info.get("kind") != "subagent": continue elif scope.startswith("agent:"): # 指定 agent target_agent = scope.replace("agent:", "") if agent_id != target_agent: continue agent_sessions.append(session_info) ``` ```python # 获取 sessions if "," in scope: # 多 agent 模式 sessions = {} for agent_id in scope.split(","): agent_id = agent_id.strip() agent_sessions = get_session_files(agent_id) if agent_sessions: sessions[agent_id] = [parse_session_file(f) for f in agent_sessions] else: sessions = get_all_sessions(scope) ``` ### Technical Analysis The scope filter has no final `else` branch that rejects an unrecognized scope. If a single scope value is not one of the documented options and does not begin with `agent:`, none of the filtering branches execute. The function then append ...[truncated 1827 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse scope values before filesystem enumeration using an explicit allowlist: ```python BUILTIN_SCOPES = {"default", "all", "agents", "cron", "subagent"} if scope not in BUILTIN_SCOPES and not scope.startswith("agent:"): raise ValueError(f"Unknown scope: {scope}") ``` 2. Add an explicit `scope == "all"` branch rather than relying on fall-through behavior. 3. Add a final rejection branch in `get_all_sessions()`: ```python else: raise ValueError(f"Unsupported scope: {scope}") ``` 4. For `default`, include only sessions positively classified as Discord: ```python if scope == "default" and session_info.get("kind") != "discord": continue ``` 5. Treat malformed or unrecognized session metadata as ineligible for destructive operations unless the operator explicitly selects `all`. 6. Print the normalized scope prominently in previews and confirmations. 7. Add automated tests for misspellings, empty scopes, whitespace, case variations, malformed session files, and every supported scope. 8. Consider prohibiting `--force` when validation warnings or unknown session types are present. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (14)

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The skill documentation is written entirely in Chinese, including headings, warnings, examples, and operational guidance, with no indication that another language is supported or that the language restriction is intentional for a region-specific use case. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

File System Enumeration

Medium
Category
Data Exfiltration
Content
```bash
# 确保对 ~/.openclaw 目录有写权限
ls -la ~/.openclaw/

# 如果需要,修改权限
chmod -R u+rw ~/.openclaw/
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

File System Enumeration

Medium
Category
Data Exfiltration
Content
```bash
# 确保对 ~/.openclaw 目录有写权限
ls -la ~/.openclaw/

# 如果需要,修改权限
chmod -R u+rw ~/.openclaw/
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill describes functionality that reads, backs up, deletes, and restores session files, but it does not declare any tool scope or permissions boundary. That creates a trust gap: an agent may be able to perform file operations beyond what reviewers or runtime policy expect, increasing the risk of unintended file access or destructive actions during session reset and restore flows.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The skill content, invocation guidance, and operational instructions are entirely presented in Chinese, with no indication that the user may choose another language or that the skill is region-specific. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The script's user-facing comments and printed instructions are entirely in Chinese, which imposes a specific language on users. The file does not indicate that the skill is region-specific or provide any opt-in or alternative locale, matching the language/locale policy violation criteria.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script performs a state-changing restore whenever any first argument is present, with no interactive confirmation, dry-run gate, or explicit safety flag. In the context of a session-reset/backup-management skill, this increases the chance of accidental rollback to the wrong backup, causing loss of current session context or unexpected overwrite of newer state.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script's comments and echoed user-facing messages are written in Chinese throughout, which imposes a specific language on users. Under the policy, locale or language constraints should either offer user opt-in/choice or be clearly documented as justified for a region-specific skill, neither of which is present here.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The README content, headings, usage steps, and warnings are entirely in Chinese, which effectively forces a specific language for users of the skill. Under the policy, language constraints should either offer user choice or be clearly documented and justified as locale-specific.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code deletes session files via `session_file.unlink()` and supports a `--force` path that skips the confirmation prompt entirely. Although the tool prints previews and backup messages, there is no interactive warning at execution time when `--force` is used for this destructive operation.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The manifest uses Chinese-only natural-language descriptions in the user-facing metadata. For a general-purpose skill manifest, this can create a language/locale policy issue because no opt-in, alternative locale, or region-specific justification is provided.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
This shell script's natural-language comments and echo output are entirely in Chinese, with no indication that the skill is region-specific or that users can choose another language. That creates a language/locale policy issue under the rule for forced language without opt-in.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
Natural-language strings in comments and echoed output are presented only in Chinese, which can impose a fixed language on users without opt-in. The policy allows locale constraints when they are explicitly justified, but this script does not indicate that it is intentionally region- or language-specific.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The file’s user-facing title, help text, prompts, and status messages are predominantly in Chinese, which effectively imposes a locale on users of the skill. There is no visible mechanism to let users choose another language or documentation justifying a region-specific constraint.

Static analysis

No suspicious patterns detected.