T09 · Insecure Skill Coding Practices
Error
- Location
- src/asp/api/state.py:75
- Finding
- Plaintext persistence of account passwords and session secrets<![CDATA[ ## Vulnerability Details **File Location**: `src/asp/api/state.py:75-115`; credential persistence is invoked from `src/asp/api/auth.py:26-33` and `src/asp/api/auth.py:54-70` **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```python def _save_cookies(self, cookies: httpx.Cookies) -> None: jar_list = [] for cookie in cookies.jar: jar_list.append({ "name": cookie.name, "value": cookie.value, "domain": cookie.domain, "path": cookie.path, }) self.cookie_file.write_text(json.dumps(jar_list, ensure_ascii=False, indent=2)) # ── metadata ─────────────────────────────────────────────────────── def _load_meta(self) -> dict[str, Any]: if not self.state_file.exists(): return {} try: return json.loads(self.state_file.read_text()) except Exception: return {} def _save_meta(self, meta: dict[str, Any]) -> None: self.state_file.write_text(json.dumps(meta, ensure_ascii=False, indent=2)) # ── credentials ─────────────────────────────────────────────────── def load_credentials(self) -> dict[str, str] | None: if not self.credentials_file.exists(): return None try: data = json.loads(self.credentials_file.read_text()) if data.get("email") and data.get("password"): return data except Exception: pass return None def save_credentials(self, email: str, password: str) -> None: self.credentials_file.write_text( json.dumps({"email": email, "password": password}, indent=2) ) ``` Successful authentication automatically invokes this storage behavior: ```python if result.get("authenticated"): self.state.save_credentials(email, password) ``` ### Technical Analysis The application stores account passwords as unencrypted JSON in `credentials.json`. It also writes reusable HTTP cookies and CSRF-related metadata to JSON files. No OS-bac ...[truncated 1979 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Stop saving raw passwords by default. Retain only the service-issued session token when persistence is necessary. 2. If automatic reauthentication is required, store credentials in an OS keyring or another encrypted secret manager. 3. Require explicit, informed user consent before enabling credential persistence, with a `--save-credentials` option disabled by default. 4. Create `~/.asp/` with mode `0700` and secret-bearing files with mode `0600`, independent of the process umask. 5. Use atomic writes through a securely created temporary file followed by `os.replace()`. 6. Separate low-sensitivity application state from passwords and session secrets. 7. Provide commands to remove saved credentials and invalidate active sessions without deleting unrelated configuration. 8. After remediation, advise existing users to delete plaintext credentials, rotate their passwords, and revoke stored sessions. ]]>
