T09 · Insecure Skill Coding Practices
Error
- Location
- tool/wechat_cli/core/db_cache.py:29
- Finding
- Predictable Shared Plaintext Cache Permits Symlink-Based Disclosure, File Overwrite, and Unsafe Cleanup<![CDATA[ ## Vulnerability Details **File Location**: `tool/wechat_cli/core/db_cache.py:29-37, 42, 68-75, 113-133`; `tool/wechat_cli/core/crypto.py:31-44` **Vulnerability Type**: Unsafe temporary-file handling and symlink traversal **Risk Level**: High ### Vulnerable Code ```python class DBCache: CACHE_DIR = os.path.join(tempfile.gettempdir(), "wechat_cli_cache") MTIME_FILE = os.path.join(tempfile.gettempdir(), "wechat_cli_cache", "_mtimes.json") def __init__(self, all_keys, db_dir): self._all_keys = all_keys self._db_dir = db_dir self._cache = {} os.makedirs(self.CACHE_DIR, exist_ok=True) _restrict_dir(self.CACHE_DIR) self._load_persistent_cache() def _cache_path(self, rel_key): h = hashlib.md5(rel_key.encode()).hexdigest()[:12] return os.path.join(self.CACHE_DIR, f"{h}.db") ``` ```python def _save_persistent_cache(self): data = {} for rel_key, (db_mt, wal_mt, path) in self._cache.items(): data[rel_key] = {"db_mt": db_mt, "wal_mt": wal_mt, "path": path} try: with open(self.MTIME_FILE, 'w', encoding="utf-8") as f: json.dump(data, f) except OSError: pass ``` ```python def full_decrypt(db_path, out_path, enc_key): file_size = os.path.getsize(db_path) total_pages = file_size // PAGE_SZ os.makedirs(os.path.dirname(out_path), exist_ok=True) with open(db_path, 'rb') as fin, open(out_path, 'wb') as fout: for pgno in range(1, total_pages + 1): page = fin.read(PAGE_SZ) if len(page) < PAGE_SZ: if len(page) > 0: page = page + b'\x00' * (PAGE_SZ - len(page)) else: break fout.write(decrypt_page(enc_key, page, pgno)) return total_pages ``` ```python @classmethod def clear_cache(cls): removed = 0 if os.path.isdir(cls.CACHE_DIR): for entry in os.listdir(cls.CACHE_DIR): p = os.path ...[truncated 2856 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace the global fixed path with a private per-user cache directory, preferably created using `tempfile.mkdtemp()` or a platform-specific secure user cache location. 2. Create the directory with mode `0700` from the outset rather than applying permissions afterward. 3. Use `os.lstat()` to reject symbolic links and verify that the cache directory is owned by the effective user. 4. Create plaintext files with `os.open()` using `O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW` and mode `0600`, where supported. 5. Write decrypted content to a securely created temporary file and atomically rename it after successful completion. 6. Treat ownership and permission-hardening failures as fatal errors instead of silently ignoring them. 7. Before cleanup, verify the cache root's device, inode, ownership, permissions, and non-symlink status. 8. Do not recursively delete unexpected directories. Maintain an explicit manifest of files created by the application and delete only verified regular files. 9. Use an HMAC or random mapping for cache filenames rather than a short, predictable MD5 digest. ]]>
