T09 · Insecure Skill Coding Practices
Warning
- Location
- tmx_cli.py:291
- Finding
- Authentication Cookies and Search Tokens Are Stored Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `tmx_cli.py:291-307` and `tmx_cli.py:654-678` **Vulnerability Type**: Insecure local storage of authentication material **Risk Level**: Medium ### Vulnerable Code ```python def save_cookies_from_jar(jar: CookieJar): """Save cookies from CookieJar to JSON file (Puppeteer-compatible format).""" cookies_list = [] for cookie in jar: cookies_list.append({ "name": cookie.name, "value": cookie.value, "domain": cookie.domain, "path": cookie.path, "expires": cookie.expires or -1, "httpOnly": cookie.has_nonstandard_attr("HttpOnly"), "secure": cookie.secure, "session": cookie.expires is None, }) with open(COOKIES_FILE, "w", encoding="utf-8") as f: json.dump(cookies_list, f, ensure_ascii=False, indent=2) return cookies_list ``` ```python def get_search_token(cookies: dict[str, str]) -> Optional[str]: """Get Algolia search token from Cookidoo API.""" # Check cached token if SEARCH_TOKEN_FILE.exists(): try: with open(SEARCH_TOKEN_FILE, "r") as f: cached = json.load(f) # Check if still valid (with 5 min buffer) if cached.get("validUntil", 0) > dt.datetime.now().timestamp() + 300: return cached.get("apiKey") except: pass # Fetch new token url = f"{COOKIDOO_BASE}/search/api/subscription/token" status, body = fetch(url, cookies) if status != 200: return None try: data = json.loads(body) # Cache token with open(SEARCH_TOKEN_FILE, "w") as f: json.dump(data, f) return data.get("apiKey") ``` ### Technical Analysis The CLI writes reusable Cookidoo session cookies and an Algolia search API token using ordinary `open(..., "w")` operations. It does not explicitly create these files with owne ...[truncated 2474 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store credentials in a dedicated per-user data directory rather than beside the executable, such as an appropriate platform-specific application data directory. 2. Create credential files with owner-only permissions from the outset. On POSIX systems, use `os.open()` with mode `0o600`, then write through the resulting file descriptor. 3. Explicitly set existing credential files to mode `0600` and reject files owned by another user or having unsafe permissions. 4. Use atomic writes through a temporary owner-only file followed by `os.replace()` to prevent partial files and reduce race conditions. 5. Store only cookies required for Cookidoo authentication instead of serializing the entire OAuth cookie jar. 6. Prefer an operating-system credential manager or keyring for reusable session material. 7. Provide a logout or session-revocation operation that securely removes cached cookies and tokens. 8. Document the actual storage location and security sensitivity of each file. 9. Avoid broad exception handlers around secret loading because they can conceal permission and integrity problems that should be reported to the user. A POSIX-oriented implementation can follow this pattern: ```python import os def secure_json_write(path: Path, data: object) -> None: flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC fd = os.open(path, flags, 0o600) try: with os.fdopen(fd, "w", encoding="utf-8") as stream: json.dump(data, stream, ensure_ascii=False, indent=2) except Exception: try: os.close(fd) except OSError: pass raise ``` ]]>
