T09 · Insecure Skill Coding Practices
- Location
scripts/token_stats.py:32- Finding
Path Traversal Through Unvalidated Agent Identifier
- Content
View full analysis
Vulnerability Details
File Location:
scripts/token_stats.py, lines 32 and 43-56
Vulnerability Type: Path traversal caused by unsafe path construction
Risk Level: MediumVulnerable 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
--agentcommand-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()andload_labels()to access an unintended, locally accessible directory whose final component issessions.Exploitation is constrained by the script's fixed filename patterns and parsers: the target directory must contain matching
*.jsonlfiles or asessions.jsonfile, 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
View remediation
Remediation Suggestions
-
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 -
Build paths with
pathlib.Pathrather than string interpolation. -
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 -
Use the validated directory returned by a single shared helper in both
find_session_files()andload_labels()so that security checks cannot diverge. -
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.
-
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.
-
