Skip to content

hotfix: close production bleed-through and dashboard auth gaps - #2

Merged
Namnarak merged 21 commits into
mainfrom
hotfix/security-2026-09-07
Sep 7, 2026
Merged

Namnarak merged 21 commits into
mainfrom
hotfix/security-2026-09-07

Conversation

@Namnarak

@Namnarak Namnarak commented Sep 7, 2026

Copy link
Copy Markdown
Member

Security/correctness hotfix for Community Edition after auditing live Cyori first.

Critical changes:

  • fail-closed dashboard middleware with Discord OAuth identity verification
  • browser user_id is no longer trusted; sensitive guild reads require membership
  • guild-setting writes require Manage Guild; player controls require same voice channel
  • legacy unauthenticated WebSocket control is disabled and falls back to authenticated HTTP
  • direct sensitive legacy endpoints are retired from browser access
  • Stripe webhook requires configured secret + Stripe-Signature before reaching handler
  • Cloudflare checkout uses Discord-verified user ID and forwards webhook authenticity headers
  • Community edge backend is deployer-configured; no default Cyori/Cytech production VPS
  • Community bot/client identity derives from its own token/runtime config, never Cyori's production ID
  • invite flow no longer requests guilds.join or auto-joins Cyori's support server
  • stale Cyori invite/vote links are rewritten to the deployment client ID or blocked
  • Mongo empty find_one/update_one selectors are forced to the resolved root document
  • DJ/vote server-setting commands get Manage Guild defense-in-depth checks

Realtime WebSocket is intentionally disabled in this hotfix until its protocol binds every control frame to an authenticated Discord identity.

This PR is intended to be squash-merged after CI passes.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-07T16:36:24.324371Z 32b356b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 32b356bb5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +227 to +229
action = data.get("action")
guild_raw = data.get("guild_id")
guild_id = int(guild_raw) if str(guild_raw or "").isdigit() else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Merge query parameters before authorization

POST authorization inspects action, cmd_action, and guild_id only in the JSON body, while APIProxyManager.handle_proxy_request subsequently merges query parameters into the dispatched request. An authenticated user can therefore send /api/proxy?action=proxy_control&cmd_action=guild_settings_save&guild_id=<target> with only settings in the body; these variables are absent during the Manage Guild check but present downstream, so the guild settings update executes without the required permission. The same technique bypasses same-voice checks for player controls.

Useful? React with 👍 / 👎.

Comment on lines +107 to +110
const clientId = /^\d{15,22}$/.test(env.DISCORD_CLIENT_ID || "")
? String(env.DISCORD_CLIENT_ID)
: "";
return jsonResponse(request, env, { client_id: clientId });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fall back to the configured backend for runtime identity

On Cloudflare Pages, runtime_config is always intercepted here and returns an empty client ID unless the newly introduced DISCORD_CLIENT_ID binding exists, even when VPS_API_URL points to a correctly configured bot whose runtime middleware can provide its identity. Existing deployments have no such documented binding, so security-bootstrap.js leaves CYTECHMUSIC_CLIENT_ID empty and script.js refuses login while the invite page reports that configuration is unavailable. Forward this action to the backend when the edge binding is absent, or support the existing bot-ID configuration.

Useful? React with 👍 / 👎.

Comment on lines +151 to +152
if not config.STRIPE_WEBHOOK_SECRET:
return web.json_response({"error": "stripe_webhook_not_configured"}, status=503)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Accept the dynamically provisioned webhook secret

When STRIPE_API_KEY is configured but STRIPE_WEBHOOK_SECRET is initially absent, Premium.cog_load deliberately runs auto_setup_webhook, which creates a Stripe endpoint and stores its new signing secret in Premium.webhook_secret. This middleware checks only the immutable config value, so it returns 503 before the handler can verify requests with that dynamically provisioned secret; checkout completions are consequently never credited in the repository's automatic webhook-setup mode.

Useful? React with 👍 / 👎.

Comment thread utils/config.py
Comment on lines +78 to +80
elif TOKEN_CLIENT_ID:
DB_NAME = f"CytechMusic_{TOKEN_CLIENT_ID}"
else:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the existing automatic database name

When DB_NAME is omitted, upgrading an existing deployment now switches MongoDB from the previous automatically selected database—normally the sanitized bot username, with Komo_<client-id> as its token-derived fallback—to CytechMusic_<client-id>. bot.py opens this value directly and there is no migration or lookup of the former name, so all existing guild settings, premium records, playlists, and favorites appear to disappear after the upgrade for deployments that relied on automatic naming.

Useful? React with 👍 / 👎.

@Namnarak
Namnarak merged commit 2ecd8ed into main Sep 7, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant