e7a881b577
CodeQL py/clear-text-logging-sensitive-data flagged 3 call sites that logged the auto-generated admin password to stdout via logger.info(). Production log aggregators (ELK/Splunk/etc) would have captured those cleartext secrets. Replace with a shared helper that writes to .deer-flow/admin_initial_credentials.txt with mode 0600, and log only the path. New file -------- - app/gateway/auth/credential_file.py: write_initial_credentials() helper. Takes email, password, and a "initial"/"reset" label. Creates .deer-flow/ if missing, writes a header comment plus the email+password, chmods 0o600, returns the absolute Path. Modified -------- - app/gateway/app.py: both _ensure_admin_user paths (fresh creation + needs_setup password reset) now write to file and log the path - app/gateway/auth/reset_admin.py: rewritten to use the shared ORM repo (SQLiteUserRepository with session_factory) and the credential_file helper. The previous implementation was broken after the earlier ORM refactor — it still imported _get_users_conn and constructed SQLiteUserRepository() without a session factory. No tests changed — the three password-log sites are all exercised via existing test_ensure_admin.py which checks that startup succeeds, not that a specific string appears in logs. CodeQL alerts 272, 283, 284: all resolved.
39 lines
1.6 KiB
Python
39 lines
1.6 KiB
Python
"""Write initial admin credentials to a restricted file instead of logs.
|
|
|
|
Logging secrets to stdout/stderr is a well-known CodeQL finding
|
|
(py/clear-text-logging-sensitive-data) — in production those logs
|
|
get collected into ELK/Splunk/etc and become a secret sprawl
|
|
source. This helper writes the credential to a 0600 file that only
|
|
the process user can read, and returns the path so the caller can
|
|
log **the path** (not the password) for the operator to pick up.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
_CREDENTIAL_FILE = Path(".deer-flow") / "admin_initial_credentials.txt"
|
|
|
|
|
|
def write_initial_credentials(email: str, password: str, *, label: str = "initial") -> Path:
|
|
"""Write the admin email + password to ``.deer-flow/admin_initial_credentials.txt``.
|
|
|
|
Creates the parent directory if it does not exist. Sets the file
|
|
mode to 0600 so only the owning process user can read it.
|
|
|
|
``label`` distinguishes "initial" (fresh creation) from "reset"
|
|
(password reset) in the file header, so an operator picking up
|
|
the file after a restart can tell which event produced it.
|
|
|
|
Returns the absolute :class:`Path` to the file.
|
|
"""
|
|
_CREDENTIAL_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
content = (
|
|
f"# DeerFlow admin {label} credentials\n# This file is generated on first boot or password reset.\n# Change the password after login via Settings -> Account,\n# then delete this file.\n#\nemail: {email}\npassword: {password}\n"
|
|
)
|
|
_CREDENTIAL_FILE.write_text(content)
|
|
os.chmod(_CREDENTIAL_FILE, 0o600)
|
|
return _CREDENTIAL_FILE.resolve()
|