T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/token_stats.py:32
- Finding
- Path Traversal Through Unvalidated Agent Identifier## Vulnerability Details **File Location**: `scripts/token_stats.py`, lines 32 and 43-56 **Vulnerability Type**: Path traversal caused by unsafe path construction **Risk Level**: Medium ### Vulnerable Code ```python p.add_argument("--agent", default="main", help="Agent ID (default: main)") ``` ```python def find_session_files(agent_id, include_deleted): base = os.path.expanduser("~/.openclaw/agents/%s/sessions/" % agent_id) patterns = [base + "*.jsonl"] if include_deleted: patterns.append(base + "*.jsonl.deleted*") patterns.append(base + "*.jsonl.bak*") files = [] for pat in patterns: files.extend(glob.glob(pat)) return sorted(set(files), key=os.path.getmtime, reverse=True) def load_labels(agent_id): """Load session labels from sessions.json.""" sj_path = os.path.expanduser( "~/.openclaw/agents/%s/sessions/sessions.json" % agent_id ) ``` ### Technical Analysis The `--agent` command-line argument is accepted without validation and interpolated directly into filesystem paths. The application does not reject path separators or `..` components, nor does it canonicalize the resulting path and verify that it remains under the intended `~/.openclaw/agents/` directory. Consequently, an attacker who can influence the command-line arguments can supply a traversal value that causes both `find_session_files()` and `load_labels()` to access an unintended, locally accessible directory whose final component is `sessions`. Exploitation is constrained by the script's fixed filename patterns and parsers: the target directory must contain matching `*.jsonl` files or a `sessions.json` file, and meaningful output requires compatible JSON structures. The script does not print complete message contents, but it can expose derived token statistics, timestamps, session identifiers, and labels from files outside the intended agent directory. ### Attack Pa ...[truncated 1418 chars]
- Remediation
- ## Remediation Suggestions 1. Restrict agent identifiers to a conservative allowlist, such as letters, digits, underscores, and hyphens: ```python import re AGENT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$") def validate_agent_id(agent_id): if not AGENT_ID_PATTERN.fullmatch(agent_id): raise ValueError("Invalid agent ID") return agent_id ``` 2. Build paths with `pathlib.Path` rather than string interpolation. 3. Resolve both the trusted agents root and candidate directory, then enforce containment before performing any read: ```python from pathlib import Path def get_sessions_dir(agent_id): validate_agent_id(agent_id) agents_root = (Path.home() / ".openclaw" / "agents").resolve() sessions_dir = (agents_root / agent_id / "sessions").resolve() if agents_root not in sessions_dir.parents: raise ValueError("Agent path escapes the agents directory") return sessions_dir ``` 4. Use the validated directory returned by a single shared helper in both `find_session_files()` and `load_labels()` so that security checks cannot diverge. 5. Reject symbolic-link-based escapes by performing containment validation after path resolution. If the environment permits untrusted users to modify directories concurrently, open files using descriptor-relative APIs and appropriate no-follow protections to reduce time-of-check/time-of-use risks. 6. Add automated tests covering absolute paths, `../` traversal, nested traversal, path separators, symbolic links, valid agent identifiers, and directories that merely share the expected filename structure.
