import os
import sys
import unittest
from unittest import mock
from httpx import AsyncClient, ASGITransport
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "api"))
class TestAuthSecurity(unittest.TestCase):
def test_rate_limiting_exists(self):
import main
self.assertTrue(hasattr(main, '_check_rate_limit'))
self.assertEqual(main._LOGIN_MAX_ATTEMPTS, 5)
self.assertEqual(main._LOGIN_WINDOW_SECONDS, 900)
def test_csrf_token_generation(self):
import main
t1 = main._generate_csrf_token()
t2 = main._generate_csrf_token()
self.assertNotEqual(t1, t2)
self.assertEqual(len(t1), 64)
def test_csrf_verify_rejects_empty(self):
import main
req = mock.MagicMock()
req.cookies.get.return_value = None
self.assertFalse(main._verify_csrf_token(req, "any-token"))
def test_csrf_verify_rejects_mismatch(self):
import main
req = mock.MagicMock()
req.cookies.get.return_value = "stored-token"
self.assertFalse(main._verify_csrf_token(req, "different-token"))
def test_csrf_verify_accepts_match(self):
import main
req = mock.MagicMock()
req.cookies.get.return_value = "matching-token"
self.assertTrue(main._verify_csrf_token(req, "matching-token"))
def test_no_duplicate_get_user_from_cookie(self):
import main, inspect
sources = inspect.getsourcelines(main)[0]
count = sum(1 for line in sources if 'def get_user_from_cookie' in line)
self.assertEqual(count, 1, "get_user_from_cookie must be defined exactly once")
def test_xss_escaped_in_sign_error(self):
"""Check that HTML in error messages gets escaped."""
import html as h
err = ''
escaped = h.escape(err)
self.assertNotIn("