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("