T09 · Insecure Skill Coding Practices
- Location
scripts/cost-report.py:57- Finding
Unrestricted Agent Identifier Allows Filesystem Path Traversal
- Content
View full analysis
Vulnerability Details
File Location:
scripts/cost-report.py, lines 57–65; user-controlled argument declared at line 224
Vulnerability Type: Path traversal and insufficient path validation
Risk Level: MediumVulnerable Code
python def load_sessions(agent_id="main"): """Load session data from OpenClaw's session store.""" home = Path.home() sessions_file = home / ".openclaw" / "agents" / agent_id / "sessions" / "sessions.json" if not sessions_file.exists(): print(f"Error: Session file not found at {sessions_file}", file=sys.stderr) sys.exit(1) with open(sessions_file) as f: return json.load(f)The value passed to this function is obtained directly from the command-line argument:
python parser.add_argument("--agent", default="main", help="Agent ID (default: main)")Technical Analysis
The user-controlled
--agentvalue is inserted directly into a filesystem path without validating its syntax or confirming that the resolved path remains beneath the intended~/.openclaw/agentsdirectory.Python's
pathlibpermits absolute path components and parent-directory components such as... An absoluteagent_idcan replace the preceding base path, while traversal components can escape the expected agents directory. The program then appendssessions/sessions.jsonand reads the resulting file using the privileges of the running process.Exploitation is constrained to targets whose resolved path ends in
sessions/sessions.json. The target must also contain JSON compatible with the session-reporting logic to produce meaningful output. Nevertheless, no authorization or containment boundary is enforced.Attack Path
-
An attacker able to influence invocation arguments supplies an absolute or traversal-based value through
--agent. -
For example, an invocation can reference an attacker-selected directory:
` ...[truncated 1306 chars]
-
- Remediation
View remediation
Remediation Suggestions
-
Restrict agent identifiers to a conservative allowlist, such as ASCII letters, digits, underscores, and hyphens:
python import re AGENT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$") def load_sessions(agent_id="main"): if not AGENT_ID_PATTERN.fullmatch(agent_id): raise ValueError("Invalid agent ID") agents_root = (Path.home() / ".openclaw" / "agents").resolve() sessions_file = ( agents_root / agent_id / "sessions" / "sessions.json" ).resolve() try: sessions_file.relative_to(agents_root) except ValueError: raise ValueError("Agent path escapes the configured agents directory") with sessions_file.open(encoding="utf-8") as f: return json.load(f) -
Explicitly reject absolute paths, path separators,
.components, and..components even if an allowlist is also used. -
Resolve both the trusted base directory and final path, then enforce containment with
Path.relative_to()before opening the file. -
Consider rejecting symlinked agent directories or opening files through a directory file descriptor with platform-supported no-follow protections if local untrusted users can modify the agent store.
-
Handle
OSError,JSONDecodeError, and schema-validation failures without exposing unnecessary absolute path details. -
Validate the loaded JSON against the expected session schema before processing it.
-
Add tests covering absolute paths, nested separators,
../traversal, symlink escapes, malformed JSON, and valid agent identifiers.
-
