T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/chain_analyzer.py:75
- Finding
- Unsafe Deserialization of Local Option-Chain Cache Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/chain_analyzer.py:75-105` **Vulnerability Type**: Unsafe Python pickle deserialization **Risk Level**: Medium ### Vulnerable Code ```python def _get_cache_path(self, cache_key: str) -> str: """Get cache file path""" return os.path.join(self.cache_dir, f"{cache_key}.pkl") def _load_from_cache(self, cache_key: str) -> Optional[Dict]: """Load data from cache if valid""" cache_path = self._get_cache_path(cache_key) if not os.path.exists(cache_path): return None try: with open(cache_path, 'rb') as f: cached = pickle.load(f) # Check TTL if time.time() - cached.get('timestamp', 0) > self.cache_ttl: return None return cached.get('data') except (FileNotFoundError, PermissionError, pickle.PickleError, IOError): return None def _save_to_cache(self, cache_key: str, data: Dict): """Save data to cache""" cache_path = self._get_cache_path(cache_key) try: with open(cache_path, 'wb') as f: pickle.dump({'timestamp': time.time(), 'data': data}, f) except (FileNotFoundError, PermissionError, pickle.PickleError, IOError) as e: logger.warning(f"Failed to save cache: {e}") ``` ### Technical Analysis Python pickle is an executable serialization format. During `pickle.load()`, serialized reduction instructions can import modules and invoke arbitrary callables. Consequently, validity checks performed after deserialization, including the cache timestamp check, cannot protect against a malicious payload because code execution occurs while the file is being loaded. The cache is persistent under `~/.openclaw/options_cache` by default, and cache filenames are derived from ticker-based cache keys. `ChainFetcher` does not independently validate those keys before constructing paths. Although `quant_scanner.py` validates tickers in one CLI path, `ChainFetch ...[truncated 1563 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace pickle with a non-executable format such as JSON. 2. Define an explicit cache schema and validate every field after parsing, including timestamps, ticker symbols, expiration dates, and numeric option data. 3. Restrict cache keys to a strict allowlist, such as uppercase letters, digits, periods, and hyphens. 4. Resolve the resulting path and verify that it remains beneath the intended cache directory. 5. Create the cache directory with mode `0700` and cache files with mode `0600`. 6. Write cache entries atomically by creating a restrictive temporary file in the same directory and then using `os.replace()`. 7. If integrity against local modification is required, authenticate entries with a key stored separately from the cache. Authentication must occur before any complex deserialization. 8. Delete existing `.pkl` cache entries during migration so legacy malicious files cannot remain reachable. ]]>
