T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/token_usage_tracker.py:13
- Finding
- Unsafe Local Data-File Path Handling Enables Symlink-Directed File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/token_usage_tracker.py`, lines 13-37 **Vulnerability Type**: Unsafe path handling, symlink following, and insecure file permissions **Risk Level**: Medium ### Vulnerable Code ```python class TokenUsageTracker: def __init__(self, data_file: str = "~/.openclaw/token_usage.json"): self.data_file = data_file self.usage_data = self._load_data() def _load_data(self) -> Dict: """Load token usage data from file""" try: with open(self.data_file, 'r') as f: return json.load(f) except FileNotFoundError: return { "sessions": {}, "daily_totals": {}, "thresholds": {}, "model_pricing": { "gpt-4": 0.03 / 1000, "gpt-3.5-turbo": 0.0015 / 1000, "claude-2": 0.0110 / 1000, "doubao-seed": 0.002 / 1000 } } def _save_data(self): """Save token usage data to file""" import os os.makedirs(os.path.dirname(self.data_file), exist_ok=True) with open(self.data_file, 'w') as f: json.dump(self.usage_data, f, indent=2) ``` ### Technical Analysis The default data path contains `~`, but Python's `open()` and `os.makedirs()` functions do not automatically expand this notation. Consequently, the default path is interpreted relative to the current working directory as `./~/.openclaw/token_usage.json`, rather than as a file beneath the executing user's home directory. The save operation also opens the destination with mode `w` without checking whether the destination is a symbolic link. If an attacker can prepare the relative path in the process's working directory, the attacker can place a symbolic link at the expected destination. The subsequent save will follow that link and truncate or replace any linked file that i ...[truncated 1737 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Expand and normalize the configured path before any file operation: ```python self.data_file = os.path.abspath(os.path.expanduser(data_file)) ``` 2. Ensure the resolved path remains beneath an explicitly approved storage directory. 3. Create the storage directory with mode `0o700`. 4. Reject symbolic links for both the destination and relevant parent directories. 5. Create files with mode `0o600` using `os.open()` and appropriate flags, including `O_NOFOLLOW` where supported. 6. Save through a securely created temporary file in the same directory, call `flush()` and `os.fsync()`, and atomically replace the destination with `os.replace()`. 7. Document the resolved storage location and avoid running the tracker from attacker-writable working directories. ]]>
