T09 · Insecure Skill Coding Practices
- Location
scripts/geekbi_auth.py:374- Finding
Bearer Access Token Is Replicated Across Multiple Plaintext Storage Locations
- Content
View full analysis
Vulnerability Details
File Location:
scripts/geekbi_auth.py:54-73,scripts/geekbi_auth.py:374-384, andscripts/geekbi_auth.py:637-652
Vulnerability Type: T09: Insecure Skill Coding Practices
Risk Level: MediumVulnerable Code
The authentication module selects three separate locations for authentication state:
python def _resolve_stores(): candidates = ( ResolvedStore(_user_config_state_path(), "user-config-directory"), ResolvedStore(_skill_state_path(), "skill-directory"), ResolvedStore(_workspace_state_path(), "working-directory"), ) stores = [] seen_paths = set() for store in candidates: path_key = os.path.normcase(os.fspath(store.path)) if path_key in seen_paths: continue seen_paths.add(path_key) stores.append(store) return tuple(stores)It then writes the same authentication state to every usable location:
python def _write_state_files(stores, payload): normalized = _normalize_state(payload) errors = [] written = 0 for store in stores: try: _write_state_file(store, normalized) written += 1 except OSError as error: errors.append(f"{store.kind}: {_storage_probe_reason(error)}") if written == 0: reason = ";".join(errors) or "登录状态目录不可用" raise OSError(reason)The replicated state includes the bearer access token:
python def save_token(latest): latest_server = latest["servers"].get(server_key) if not isinstance(latest_server, dict): return False, False latest_pending = latest_server.get("pending") if not isinstance(latest_pending, dict): return False, False if latest_pending.get("deviceCode") != pending.get("deviceCode"): return False, False _remove_access_token(latest_server) latest_server["accessToken"] = access_token latest_server["accessTokenExpiresAt"] = now + max(0, expires_in ...[truncated 3207 chars]- Remediation
View remediation
Remediation Suggestions
-
Use one authentication store only. Store authentication state exclusively in the operating-system user configuration directory rather than mirroring it into the Skill and working directories.
-
Prefer an operating-system credential manager. Store the bearer token in facilities such as Windows Credential Manager, macOS Keychain, or a Linux secret service. Keep only non-sensitive metadata in the JSON state file.
-
Use fallback selection instead of replication. If the preferred credential store is unavailable, choose exactly one secure fallback location. Do not write the same token to every writable candidate.
-
Migrate existing installations safely. On the first run after remediation:
- Read the token from the highest-priority valid store.
- Move it to the selected secure store.
- Remove legacy copies from the Skill and workspace directories.
- Report cleanup failures without printing token contents.
-
Strengthen cleanup behavior. Make
clear_auth_state()verify and report whether each known copy was deleted instead of silently ignoring deletion errors. -
Minimize token lifetime and privilege. Continue applying short expiration periods and ensure server-issued tokens are restricted to the minimum API scopes required for SHEIN review and site queries.
-
Retain existing safeguards. Preserve atomic file replacement, restrictive POSIX permissions, token redaction, and
.gitignorecoverage as defense-in-depth measures, but do not rely on them as substitutes for eliminating redundant credential copies.
-
