142 lines
5.5 KiB
Python
142 lines
5.5 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Test script to detect and reproduce session contamination bugs
|
|
Usage: docker exec igny8_backend python test_session_contamination.py
|
|
"""
|
|
import os
|
|
import django
|
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
|
|
django.setup()
|
|
|
|
from django.contrib.sessions.models import Session
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import RequestFactory
|
|
from django.contrib.sessions.middleware import SessionMiddleware
|
|
from igny8_core.auth.middleware import AccountContextMiddleware
|
|
from datetime import datetime, timedelta
|
|
|
|
User = get_user_model()
|
|
|
|
def test_session_isolation():
|
|
"""Test that sessions are properly isolated between users"""
|
|
print("\n=== SESSION CONTAMINATION TEST ===\n")
|
|
|
|
# Get test users
|
|
try:
|
|
developer = User.objects.get(username='developer')
|
|
scale_user = User.objects.filter(account__slug='scale-account').first()
|
|
|
|
if not scale_user:
|
|
print("⚠️ No scale account user found, creating one...")
|
|
from igny8_core.auth.models import Account
|
|
scale_account = Account.objects.filter(slug='scale-account').first()
|
|
if scale_account:
|
|
scale_user = User.objects.create_user(
|
|
username='scale_test',
|
|
email='scale@test.com',
|
|
password='testpass123',
|
|
account=scale_account,
|
|
role='owner'
|
|
)
|
|
else:
|
|
print("❌ No scale account found")
|
|
return False
|
|
|
|
print(f"✓ Developer user: {developer.username} (account: {developer.account.slug})")
|
|
print(f"✓ Scale user: {scale_user.username} (account: {scale_user.account.slug if scale_user.account else 'None'})")
|
|
|
|
except Exception as e:
|
|
print(f"❌ Failed to get test users: {e}")
|
|
return False
|
|
|
|
# Check active sessions
|
|
active_sessions = Session.objects.filter(expire_date__gte=datetime.now())
|
|
print(f"\n📊 Total active sessions: {active_sessions.count()}")
|
|
|
|
# Count sessions by user
|
|
user_sessions = {}
|
|
for session in active_sessions:
|
|
try:
|
|
data = session.get_decoded()
|
|
user_id = data.get('_auth_user_id')
|
|
if user_id:
|
|
user = User.objects.get(id=user_id)
|
|
key = f"{user.username} ({user.account.slug if user.account else 'no-account'})"
|
|
user_sessions[key] = user_sessions.get(key, 0) + 1
|
|
except:
|
|
pass
|
|
|
|
print("\n📈 Sessions by user:")
|
|
for user_key, count in sorted(user_sessions.items(), key=lambda x: x[1], reverse=True):
|
|
print(f" {user_key}: {count} sessions")
|
|
|
|
# Check for session contamination patterns
|
|
contamination_found = False
|
|
|
|
# Pattern 1: Too many sessions for one user
|
|
for user_key, count in user_sessions.items():
|
|
if count > 20:
|
|
print(f"\n⚠️ WARNING: {user_key} has {count} sessions (possible proliferation)")
|
|
contamination_found = True
|
|
|
|
# Pattern 2: Check session cookie settings
|
|
from django.conf import settings
|
|
print(f"\n🔧 Session Configuration:")
|
|
print(f" SESSION_COOKIE_NAME: {settings.SESSION_COOKIE_NAME}")
|
|
print(f" SESSION_COOKIE_DOMAIN: {getattr(settings, 'SESSION_COOKIE_DOMAIN', 'Not set (good)')}")
|
|
print(f" SESSION_COOKIE_SAMESITE: {getattr(settings, 'SESSION_COOKIE_SAMESITE', 'Not set')}")
|
|
print(f" SESSION_COOKIE_HTTPONLY: {settings.SESSION_COOKIE_HTTPONLY}")
|
|
print(f" SESSION_ENGINE: {settings.SESSION_ENGINE}")
|
|
|
|
if getattr(settings, 'SESSION_COOKIE_SAMESITE', None) != 'Strict':
|
|
print(f"\n⚠️ WARNING: SESSION_COOKIE_SAMESITE should be 'Strict' (currently: {getattr(settings, 'SESSION_COOKIE_SAMESITE', 'Not set')})")
|
|
contamination_found = True
|
|
|
|
# Test middleware isolation
|
|
print(f"\n🧪 Testing Middleware Isolation...")
|
|
factory = RequestFactory()
|
|
|
|
# Simulate two requests from different users
|
|
request1 = factory.get('/api/v1/test/')
|
|
request1.user = developer
|
|
request1.session = {}
|
|
|
|
request2 = factory.get('/api/v1/test/')
|
|
request2.user = scale_user
|
|
request2.session = {}
|
|
|
|
middleware = AccountContextMiddleware(lambda x: None)
|
|
|
|
# Process requests
|
|
middleware.process_request(request1)
|
|
middleware.process_request(request2)
|
|
|
|
# Check isolation
|
|
account1 = getattr(request1, 'account', None)
|
|
account2 = getattr(request2, 'account', None)
|
|
|
|
print(f" Request 1 account: {account1.slug if account1 else 'None'}")
|
|
print(f" Request 2 account: {account2.slug if account2 else 'None'}")
|
|
|
|
if account1 and account2 and account1.id == account2.id:
|
|
print(f"\n❌ CONTAMINATION DETECTED: Both requests have same account!")
|
|
contamination_found = True
|
|
else:
|
|
print(f"\n✓ Middleware isolation working correctly")
|
|
|
|
# Final result
|
|
if contamination_found:
|
|
print(f"\n❌ SESSION CONTAMINATION DETECTED")
|
|
print(f"\nRecommended fixes:")
|
|
print(f"1. Set SESSION_COOKIE_SAMESITE='Strict' in settings.py")
|
|
print(f"2. Clear all existing sessions: Session.objects.all().delete()")
|
|
print(f"3. Ensure users logout and re-login with fresh cookies")
|
|
return False
|
|
else:
|
|
print(f"\n✅ No contamination detected - sessions appear isolated")
|
|
return True
|
|
|
|
if __name__ == '__main__':
|
|
result = test_session_isolation()
|
|
exit(0 if result else 1)
|