Require Bearer token on all API endpoints (PINVAULT_API_KEY env). Rate limit PIN access to 5 attempts per 15min lockout per PIN. Make NAS_BACKUP_DIR configurable via PINVAULT_NAS_BACKUP_DIR. Replace bootstrap() sys.exit(1) with graceful False return. Add tests for rate limiting and auth.
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
import os
|
|
import sys
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
|
|
|
|
class TestPinVaultSecurity(unittest.TestCase):
|
|
|
|
def test_api_key_required(self):
|
|
import app
|
|
self.assertTrue(hasattr(app, 'require_api_key'))
|
|
self.assertTrue(len(app.API_KEY) >= 32)
|
|
|
|
def test_rate_limit_exists(self):
|
|
import app
|
|
self.assertTrue(hasattr(app, '_check_pin_rate_limit'))
|
|
self.assertEqual(app._PIN_MAX_ATTEMPTS, 5)
|
|
self.assertEqual(app._PIN_LOCKOUT_SECONDS, 900)
|
|
|
|
def test_rate_limit_blocks_after_max(self):
|
|
import app
|
|
app._pin_attempt_locks.clear()
|
|
for i in range(app._PIN_MAX_ATTEMPTS):
|
|
allowed, _ = app._check_pin_rate_limit(999)
|
|
self.assertTrue(allowed, f"Attempt {i+1} should be allowed")
|
|
allowed, remaining = app._check_pin_rate_limit(999)
|
|
self.assertFalse(allowed, "Should be locked out after max attempts")
|
|
self.assertGreater(remaining, 0)
|
|
app._pin_attempt_locks.clear()
|
|
|
|
def test_rate_limit_resets_on_success(self):
|
|
import app
|
|
app._pin_attempt_locks.clear()
|
|
for i in range(3):
|
|
app._check_pin_rate_limit(999)
|
|
app._reset_pin_rate_limit(999)
|
|
allowed, _ = app._check_pin_rate_limit(999)
|
|
self.assertTrue(allowed, "Should allow after reset")
|
|
app._pin_attempt_locks.clear()
|
|
|
|
def test_nas_backup_configurable(self):
|
|
import app
|
|
self.assertEqual(app.NAS_BACKUP_DIR, os.environ.get("PINVAULT_NAS_BACKUP_DIR", ""))
|
|
|
|
def test_bootstrap_returns_bool(self):
|
|
import app
|
|
self.assertIsInstance(app._bootstrap_ok, bool)
|
|
|
|
def test_no_sys_exit_in_bootstrap(self):
|
|
import inspect, app
|
|
src = inspect.getsource(app.bootstrap)
|
|
self.assertNotIn("sys.exit", src, "bootstrap must not call sys.exit")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|