Problem
Several security-relevant defaults found during the review. None are fixed yet — they need product decisions.
1. Default admin password is "admin" (config.py)
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "admin") — anyone who can reach the Flask port can authenticate as admin (start/stop listening, spawn the Meet bot toward any URL, read the viewer password) unless the env var is set.
Suggested fix: refuse to start (or generate a random password and print it, like the viewer passphrase) when ADMIN_PASSWORD is unset.
2. cors_allowed_origins="*" on SocketIO (app.py)
Combined with the server binding 0.0.0.0, any website a LAN user visits can open a WebSocket to the app and attempt admin/viewer auth.
Suggested fix: restrict to the expected origin(s), or make it configurable with a safe default.
3. Viewer passphrase stored in plaintext (viewer_password.txt)
Low severity (it's a meeting-viewing passphrase, by design shared with viewers), but worth noting it persists across runs in the working directory.
4. Password comparisons are not constant-time
password == Config.ADMIN_PASSWORD etc. — use hmac.compare_digest(). Minor in practice, trivial to fix.
5. /api/viewer/password authenticates via raw Authorization header equality
The admin password is sent as a bare header value (no scheme), compared with ==. Works, but combine with 4 and consider a session check instead.
Status
Open — not addressed in PR #16/#17 (out of scope for the bug-fix pass).
Problem
Several security-relevant defaults found during the review. None are fixed yet — they need product decisions.
1. Default admin password is
"admin"(config.py)ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "admin")— anyone who can reach the Flask port can authenticate as admin (start/stop listening, spawn the Meet bot toward any URL, read the viewer password) unless the env var is set.Suggested fix: refuse to start (or generate a random password and print it, like the viewer passphrase) when
ADMIN_PASSWORDis unset.2.
cors_allowed_origins="*"on SocketIO (app.py)Combined with the server binding
0.0.0.0, any website a LAN user visits can open a WebSocket to the app and attempt admin/viewer auth.Suggested fix: restrict to the expected origin(s), or make it configurable with a safe default.
3. Viewer passphrase stored in plaintext (
viewer_password.txt)Low severity (it's a meeting-viewing passphrase, by design shared with viewers), but worth noting it persists across runs in the working directory.
4. Password comparisons are not constant-time
password == Config.ADMIN_PASSWORDetc. — usehmac.compare_digest(). Minor in practice, trivial to fix.5.
/api/viewer/passwordauthenticates via rawAuthorizationheader equalityThe admin password is sent as a bare header value (no scheme), compared with
==. Works, but combine with 4 and consider a session check instead.Status
Open — not addressed in PR #16/#17 (out of scope for the bug-fix pass).