mirror of
https://github.com/barkeser2002/offline-db.git
synced 2026-09-25 02:19:59 +03:00
🚨 Severity: HIGH 💡 Vulnerability: The `WatchPartyConsumer` accepted WebSocket connections from unauthenticated users, potentially allowing them to act as invisible 'ghost listeners' in private watch parties. In addition, when attempting to reject non-existent rooms, the consumer called `await self.close()` before `await self.accept()`, leading to ASGI protocol violations and unhandled connection terminations. 🎯 Impact: Unauthenticated users could eavesdrop on chat and video sync messages for watch parties they were not part of. Bad connections also crashed `channels` tests by prematurely dropping the handshake. 🔧 Fix: Updated the connection logic to raise `DenyConnection()` to securely and properly reject unauthorized or invalid connections before they are accepted. Also fixed the tests to pass a correctly formatted UUID to the WebSocket so the `URLRouter` doesn't drop the connection via an exception before it reaches the consumer. ✅ Verification: Ran `pytest apps/watchparty/` and `pytest content/tests/test_watch_party_websocket_security.py` locally and verified both passes. Created an explicit test for the unauthenticated case. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
51 lines
1.9 KiB
Python
51 lines
1.9 KiB
Python
import pytest
|
|
from channels.testing import WebsocketCommunicator
|
|
from aniscrap_core.asgi import application
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db(transaction=True)
|
|
async def test_connect_to_non_existent_party():
|
|
# Attempt to connect to a non-existent party
|
|
room_name = "00000000-0000-0000-0000-000000000000"
|
|
communicator = WebsocketCommunicator(application, f"ws/watch-party/{room_name}/")
|
|
|
|
from channels.exceptions import DenyConnection
|
|
try:
|
|
connected, subprotocol = await communicator.connect()
|
|
assert connected is False, "Connection should be rejected for non-existent party"
|
|
except DenyConnection:
|
|
pass
|
|
|
|
await communicator.disconnect()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.django_db(transaction=True)
|
|
async def test_connect_unauthenticated():
|
|
from channels.db import database_sync_to_async
|
|
from django.contrib.auth import get_user_model
|
|
from apps.watchparty.models import Room
|
|
from content.models import Anime, Season, Episode
|
|
User = get_user_model()
|
|
|
|
@database_sync_to_async
|
|
def create_room():
|
|
user = User.objects.create_user(username='hostuser', password='password')
|
|
anime = Anime.objects.create(title='Test Anime')
|
|
season = Season.objects.create(anime=anime, number=1)
|
|
episode = Episode.objects.create(season=season, number=1, title='Test Ep')
|
|
room = Room.objects.create(episode=episode, host=user)
|
|
return room
|
|
|
|
room = await create_room()
|
|
communicator = WebsocketCommunicator(application, f"ws/watch-party/{room.uuid}/")
|
|
|
|
# We expect the connection to be rejected because user is unauthenticated
|
|
from channels.exceptions import DenyConnection
|
|
try:
|
|
connected, subprotocol = await communicator.connect()
|
|
assert connected is False, "Connection should be rejected for unauthenticated users"
|
|
except DenyConnection:
|
|
pass
|
|
|
|
await communicator.disconnect()
|