openvibers/openvibe.live Commits
Docs: voice chat security fixes in the audit; friendly global chat and chat rendering in chat-system.md
Changes
@@@ -59,6 +59,7 @@ test/authorization were also run against the previous code and failed there, cas| Low-Medium | Anyone could set the saved playback position of any media request (unauthenticated). | Channel managers only. | authorization || Low-Medium | AI viewer "clone" copied any user's chat history from every channel (and their AI insight) into a bot. | Streamers clone only from what that person said in their own channel; staff unchanged. | authorization || Low-Medium | No limit on chat WebSocket connections per address. | 48 per address (admins exempt); clients back off to 30 s on a 4029 close. | test/chat-connection-cap.test.js |++| Medium | Voice: 1:1 call channels were listed publicly and anyone (anonymous included) could walk into them; `call-user` had no rate limit or block check; anyone could answer any invite; a kicked user could rejoin at once; one host could fill the 8-seat Public channel; TURN was emitted without credentials on a box with no secret; static TURN credentials went to every visitor. | Private calls hidden from strangers and invite-only; call-user 6/min + block check; respond requires the invite; 60 s kick cooldown; 3 sockets per address, duplicate accounts replaced; TURN only with credentials, optional short-lived HMAC credentials (`TURN_AUTH_SECRET`, coturn `use-auth-secret`). | manual + two-client voice test || Low | Slot `control_config_id` accepted other users' profiles; restream viewer counts writable for any destination. | Ownership checks. | manual || Low | ONVIF discovery (LAN scan with default-password probing) available to every user. | Admin only, bounded timeout. | manual || Low | RobotStreamer API calls carrying the user's RS token disabled TLS verification. | Verification on for api.robotstreamer.com (valid certificate). | manual |@@@ -92,6 +93,7 @@ validation, private VOD cache leak, HTTP-FLV bound to loopback, admin bans never| Medium | Inline event handlers keep `'unsafe-inline'` in the enforced CSP. | Progressive migration (see §3). || Medium | Per-address limits (broadcast viewers, rate limits) trust `CF-Connecting-IP`; if the origin is reachable without Cloudflare that header is spoofable. | Firewall the origin to Cloudflare ranges (deploy/cloudflare/checklist.md). || Low | Chat connect/close/join still scan every client to update counts. | Profile under load before indexing clients by room. |++| Low | Voice TURN credentials are still the static pair until `TURN_AUTH_SECRET` is set and coturn switched to `use-auth-secret` (production change). | Configure the secret on the host. || Low | RobotStreamer SFU WebSocket connections still skip certificate verification (hosts are assigned by RS at runtime). | Confirm RS SFU certificates and enable verification. || Low | Analytics roll-ups and the 90-day DELETE run synchronously on the main thread (vendor/openvibe-shared/analytics.js). | Companion change in OpenVibe.Network's openvibe-shared, then re-sync. |@@@ -123,3 +123,20 @@ Related surfaces: `GET /api/streams/channel/:username/bio-en` (bio in English),`GET /api/chat-ai/live-captions/:username` (English rendering of live speech — needs the AItimeline on and a multilingual whisper model, see `WHISPER_MODEL_MULTI`), and the home-page"Star of OpenVibe" spotlight (`GET /api/home/star`, `star_streamer` setting / `STAR_STREAMER` env).++++++## Friendly global chat (2026-09-17)++++A viewer-side setting (chat settings → Behavior). When on, messages in global chat that match++`FRIENDLY_FILTER_CATEGORIES` in `server/chat/moderation-utils.js` (slurs, hate slogans, threats,++sexual content, anything sexualising minors) fold into a "N messages hidden by Friendly chat ·++Show" line. Nothing is deleted and nobody is moderated for it. It is on for a viewer's first day++(the chat `auth` message carries `newcomer`, from the account age or the anon's first sighting)++unless the viewer chose otherwise; the rules come from `GET /api/chat/filters/friendly`.++++## History and rendering++++`buildChatMessageEl()` in `public/js/chat.js` renders every surface (page, popout, broadcast desk,++floating widget). History renders off-screen and swaps in once; the last 200 rows of a room are++cached in localStorage per user for an instant paint on reload. The view follows new messages++unless the reader scrolled back (`_watchChatPin`).
@@ -59,6 +59,7 @@ test/authorization were also run against the previous code and failed there, cas
@@ -59,6 +59,7 @@ test/authorization were also run against the previous code and failed there, cas
| Low-Medium | Anyone could set the saved playback position of any media request (unauthenticated). | Channel managers only. | authorization |
| Low-Medium | Anyone could set the saved playback position of any media request (unauthenticated). | Channel managers only. | authorization |
| Low-Medium | AI viewer "clone" copied any user's chat history from every channel (and their AI insight) into a bot. | Streamers clone only from what that person said in their own channel; staff unchanged. | authorization |
| Low-Medium | AI viewer "clone" copied any user's chat history from every channel (and their AI insight) into a bot. | Streamers clone only from what that person said in their own channel; staff unchanged. | authorization |
| Low-Medium | No limit on chat WebSocket connections per address. | 48 per address (admins exempt); clients back off to 30 s on a 4029 close. | test/chat-connection-cap.test.js |
| Low-Medium | No limit on chat WebSocket connections per address. | 48 per address (admins exempt); clients back off to 30 s on a 4029 close. | test/chat-connection-cap.test.js |
+| Medium | Voice: 1:1 call channels were listed publicly and anyone (anonymous included) could walk into them; `call-user` had no rate limit or block check; anyone could answer any invite; a kicked user could rejoin at once; one host could fill the 8-seat Public channel; TURN was emitted without credentials on a box with no secret; static TURN credentials went to every visitor. | Private calls hidden from strangers and invite-only; call-user 6/min + block check; respond requires the invite; 60 s kick cooldown; 3 sockets per address, duplicate accounts replaced; TURN only with credentials, optional short-lived HMAC credentials (`TURN_AUTH_SECRET`, coturn `use-auth-secret`). | manual + two-client voice test |
| Low | Slot `control_config_id` accepted other users' profiles; restream viewer counts writable for any destination. | Ownership checks. | manual |
| Low | Slot `control_config_id` accepted other users' profiles; restream viewer counts writable for any destination. | Ownership checks. | manual |
| Low | ONVIF discovery (LAN scan with default-password probing) available to every user. | Admin only, bounded timeout. | manual |
| Low | ONVIF discovery (LAN scan with default-password probing) available to every user. | Admin only, bounded timeout. | manual |
| Low | RobotStreamer API calls carrying the user's RS token disabled TLS verification. | Verification on for api.robotstreamer.com (valid certificate). | manual |
| Low | RobotStreamer API calls carrying the user's RS token disabled TLS verification. | Verification on for api.robotstreamer.com (valid certificate). | manual |
@@ -92,6 +93,7 @@ validation, private VOD cache leak, HTTP-FLV bound to loopback, admin bans never
@@ -92,6 +93,7 @@ validation, private VOD cache leak, HTTP-FLV bound to loopback, admin bans never
| Medium | Inline event handlers keep `'unsafe-inline'` in the enforced CSP. | Progressive migration (see §3). |
| Medium | Inline event handlers keep `'unsafe-inline'` in the enforced CSP. | Progressive migration (see §3). |
| Medium | Per-address limits (broadcast viewers, rate limits) trust `CF-Connecting-IP`; if the origin is reachable without Cloudflare that header is spoofable. | Firewall the origin to Cloudflare ranges (deploy/cloudflare/checklist.md). |
| Medium | Per-address limits (broadcast viewers, rate limits) trust `CF-Connecting-IP`; if the origin is reachable without Cloudflare that header is spoofable. | Firewall the origin to Cloudflare ranges (deploy/cloudflare/checklist.md). |
| Low | Chat connect/close/join still scan every client to update counts. | Profile under load before indexing clients by room. |
| Low | Chat connect/close/join still scan every client to update counts. | Profile under load before indexing clients by room. |
+| Low | Voice TURN credentials are still the static pair until `TURN_AUTH_SECRET` is set and coturn switched to `use-auth-secret` (production change). | Configure the secret on the host. |
| Low | RobotStreamer SFU WebSocket connections still skip certificate verification (hosts are assigned by RS at runtime). | Confirm RS SFU certificates and enable verification. |
| Low | RobotStreamer SFU WebSocket connections still skip certificate verification (hosts are assigned by RS at runtime). | Confirm RS SFU certificates and enable verification. |
| Low | Analytics roll-ups and the 90-day DELETE run synchronously on the main thread (vendor/openvibe-shared/analytics.js). | Companion change in OpenVibe.Network's openvibe-shared, then re-sync. |
| Low | Analytics roll-ups and the 90-day DELETE run synchronously on the main thread (vendor/openvibe-shared/analytics.js). | Companion change in OpenVibe.Network's openvibe-shared, then re-sync. |
@@ -123,3 +123,20 @@ Related surfaces: `GET /api/streams/channel/:username/bio-en` (bio in English),
@@ -123,3 +123,20 @@ Related surfaces: `GET /api/streams/channel/:username/bio-en` (bio in English),
`GET /api/chat-ai/live-captions/:username` (English rendering of live speech — needs the AI
`GET /api/chat-ai/live-captions/:username` (English rendering of live speech — needs the AI
timeline on and a multilingual whisper model, see `WHISPER_MODEL_MULTI`), and the home-page
timeline on and a multilingual whisper model, see `WHISPER_MODEL_MULTI`), and the home-page
"Star of OpenVibe" spotlight (`GET /api/home/star`, `star_streamer` setting / `STAR_STREAMER` env).
"Star of OpenVibe" spotlight (`GET /api/home/star`, `star_streamer` setting / `STAR_STREAMER` env).
+
+
+## Friendly global chat (2026-09-17)
+
+A viewer-side setting (chat settings → Behavior). When on, messages in global chat that match
+`FRIENDLY_FILTER_CATEGORIES` in `server/chat/moderation-utils.js` (slurs, hate slogans, threats,
+sexual content, anything sexualising minors) fold into a "N messages hidden by Friendly chat ·
+Show" line. Nothing is deleted and nobody is moderated for it. It is on for a viewer's first day
+(the chat `auth` message carries `newcomer`, from the account age or the anon's first sighting)
+unless the viewer chose otherwise; the rules come from `GET /api/chat/filters/friendly`.
+
+## History and rendering
+
+`buildChatMessageEl()` in `public/js/chat.js` renders every surface (page, popout, broadcast desk,
+floating widget). History renders off-screen and swaps in once; the last 200 rows of a room are
+cached in localStorage per user for an instant paint on reload. The view follows new messages
+unless the reader scrolled back (`_watchChatPin`).