From 991ccdbbb1aac860a4ef7cf3e724d29c4e16a5dd Mon Sep 17 00:00:00 2001 From: MiTHRAL Date: Wed, 13 May 2026 22:13:26 -0400 Subject: [PATCH] Harden deploy token and config handling --- README.md | 14 ++++++++++++++ status_bot.py | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/README.md b/README.md index f36f4c6..4891241 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Edit `.env` and set: - `DASHBOARD_USERNAME` - `DASHBOARD_PASSWORD_HASH` +Use the raw Discord bot token value. Do not include a `Bot ` prefix. + Generate the dashboard password hash: ```sh @@ -95,9 +97,21 @@ Sign in with `DASHBOARD_USERNAME` and the password you used when generating `DAS ```sh cp .env.deploy.example .env cp services.example.json services.json +python3 status_bot.py --hash-password docker compose up -d --build ``` +Paste the generated password hash into `.env` before starting the container. + +Make sure Docker can read the service config and write runtime state: + +```sh +chmod 600 .env +chmod 644 services.json +mkdir -p state +chmod 755 state +``` + The bot stores the Discord message ID in `state/status-message.json`. Keep that file mounted so the bot edits the same message after restarts. The deploy compose joins your existing reverse-proxy network: diff --git a/status_bot.py b/status_bot.py index e287639..9acadc4 100644 --- a/status_bot.py +++ b/status_bot.py @@ -167,6 +167,13 @@ def env(name: str, default: str | None = None) -> str: return value.strip() +def normalize_discord_token(token: str) -> str: + cleaned = token.strip().strip("\"'") + if cleaned.lower().startswith("bot "): + cleaned = cleaned[4:].strip() + return cleaned + + def load_dotenv(path: Path = Path(".env")) -> None: if not path.exists(): return @@ -189,6 +196,8 @@ def load_json(path: Path) -> dict[str, Any]: data = json.load(handle) except FileNotFoundError as exc: raise ValueError(f"Config file not found: {path}") from exc + except PermissionError as exc: + raise ValueError(f"Config file is not readable: {path}") from exc except json.JSONDecodeError as exc: raise ValueError(f"Invalid JSON in {path}: {exc}") from exc @@ -876,6 +885,7 @@ def main() -> int: return 0 token = env("DISCORD_BOT_TOKEN") + token = normalize_discord_token(token) channel_id = env("DISCORD_CHANNEL_ID") config_path = Path(env("ARCHIVE_STATUS_CONFIG", "services.json")) state_path = Path(env("ARCHIVE_STATUS_STATE", "state/status-message.json"))