"""Managed installation regression tests; use only disposable local files.""" import base64 from concurrent.futures import ThreadPoolExecutor from contextlib import closing, redirect_stderr, redirect_stdout import io import json import os from pathlib import Path import sqlite3 import stat import tempfile from threading import Barrier import unittest from unittest.mock import patch from backend.app import container_bootstrap as bootstrap class ContainerBootstrapTests(unittest.TestCase): def setUp(self): temporary = tempfile.TemporaryDirectory() self.addCleanup(temporary.cleanup) self.root = Path(temporary.name) self.data = self.root / "data" self.data.mkdir(mode=0o700) self.state_path = self.data / bootstrap.STATE_FILENAME self.database = self.data / "magent.db" self.environment = { "MAGENT_MANAGED_SECRETS": "true", "MAGENT_APPLICATION_URL": "https://magent.example.test", } def prepare(self, **changes): return bootstrap.prepare_environment({**self.environment, **changes}, self.data) def state(self): return json.loads(self.state_path.read_text(encoding="utf-8")) def create_database(self, *, completed=0, admin=False): with closing(sqlite3.connect(self.database)) as connection: with connection: connection.execute("CREATE TABLE installation_setup (id INTEGER PRIMARY KEY, completed INTEGER)") connection.execute("INSERT INTO installation_setup VALUES (1, ?)", (completed,)) connection.execute("CREATE TABLE users (role TEXT)") if admin: connection.execute("INSERT INTO users VALUES ('ADMIN')") def create_symlink(self, path, target, *, directory=False): try: path.symlink_to(target, target_is_directory=directory) except (OSError, NotImplementedError) as exc: self.skipTest(f"This platform cannot create test symlinks: {type(exc).__name__}") def test_fresh_install_generates_independent_valid_random_secrets(self): before = dict(self.environment) prepared = self.prepare() state = self.state() self.assertEqual(self.environment, before) self.assertEqual(set(state), {"version", *bootstrap.SECRET_NAMES}) self.assertEqual(state["version"], 1) for name in ("JWT_SECRET", "SETUP_TOKEN"): self.assertRegex(state[name], r"^[A-Za-z0-9_-]{64}$") self.assertNotEqual(state["JWT_SECRET"], state["SETUP_TOKEN"]) self.assertEqual(len(base64.urlsafe_b64decode(state["SETTINGS_ENCRYPTION_KEY"])), 32) for name in bootstrap.SECRET_NAMES: self.assertEqual(prepared[name], state[name]) self.assertEqual(prepared["SQLITE_PATH"], str(self.database.absolute())) self.assertFalse(self.database.exists()) self.assertEqual(list(self.data.glob(".magent-secrets-*")), []) @unittest.skipUnless(os.name == "posix", "POSIX filesystem ownership/permissions") def test_state_has_private_permissions_and_runtime_ownership(self): self.prepare() metadata = self.state_path.stat() self.assertEqual(stat.S_IMODE(metadata.st_mode), 0o600) self.assertEqual(metadata.st_uid, os.geteuid()) def test_separate_installations_get_different_secrets(self): first = self.prepare() other = self.root / "other" other.mkdir(mode=0o700) second = bootstrap.prepare_environment(self.environment, other) for name in bootstrap.SECRET_NAMES: self.assertNotEqual(first[name], second[name]) def test_restart_and_existing_database_reuse_exact_file_and_values(self): first = self.prepare() original = self.state_path.read_bytes() original_modified = self.state_path.stat().st_mtime_ns self.create_database(admin=True) with patch.object(bootstrap.secrets, "token_bytes", side_effect=AssertionError("Must not regenerate")), \ patch.object(bootstrap.secrets, "token_urlsafe", side_effect=AssertionError("Must not regenerate")): second = self.prepare() self.assertEqual(first, second) self.assertEqual(self.state_path.read_bytes(), original) self.assertEqual(self.state_path.stat().st_mtime_ns, original_modified) def test_disabled_mode_is_an_unchanged_copy_without_filesystem_access(self): for value in (None, "false", "0", "no", "", " FALSE "): with self.subTest(mode=value): environment = {"JWT_SECRET": "legacy-key", "MAGENT_APPLICATION_URL": "invalid"} if value is not None: environment["MAGENT_MANAGED_SECRETS"] = value result = bootstrap.prepare_environment(environment, self.root / "does-not-exist") self.assertEqual(result, environment) self.assertIsNot(result, environment) self.assertFalse(self.state_path.exists()) def test_invalid_managed_mode_fails_before_writing(self): with self.assertRaises(bootstrap.BootstrapError): self.prepare(MAGENT_MANAGED_SECRETS="perhaps") self.assertFalse(self.state_path.exists()) def test_auto_mode_generates_fresh_install_keys_without_explicit_jwt(self): prepared = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data) self.assertTrue(self.state_path.exists()) self.assertEqual(prepared["MAGENT_MANAGED_SECRETS"], "true") self.assertEqual(prepared["MAGENT_RUNTIME_MANAGED"], "1") for name in bootstrap.SECRET_NAMES: self.assertEqual(prepared[name], self.state()[name]) def test_auto_mode_preserves_explicit_jwt_manual_install_without_filesystem_access(self): environment = { "MAGENT_MANAGED_SECRETS": "auto", "JWT_SECRET": "legacy-explicit-signing-key", "SQLITE_PATH": "/existing/custom-database.db", "API_DOCS_ENABLED": "true", "MAGENT_APPLICATION_URL": "https://legacy.example.test", "CORS_ALLOW_ORIGIN": "https://legacy.example.test", } prepared = bootstrap.prepare_environment(environment, self.root / "does-not-exist") self.assertEqual(prepared, environment) self.assertIsNot(prepared, environment) self.assertNotIn("SETTINGS_ENCRYPTION_KEY", prepared) self.assertNotIn("MAGENT_RUNTIME_MANAGED", prepared) self.assertFalse(self.state_path.exists()) def test_auto_mode_whitespace_jwt_is_treated_as_unset(self): prepared = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto", "JWT_SECRET": " "}, self.data) self.assertEqual(prepared["JWT_SECRET"], self.state()["JWT_SECRET"]) def test_absent_application_url_uses_fixed_defaults_without_claiming_an_origin(self): prepared = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data) self.assertFalse(prepared.get("MAGENT_APPLICATION_URL")) self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], "http://localhost:3000") self.assertEqual(prepared["AUTH_COOKIE_SECURE"], "false") self.assertEqual(prepared["API_DOCS_ENABLED"], "false") self.assertEqual(prepared["SQLITE_PATH"], str(self.database.absolute())) def test_empty_application_url_is_deferred_to_setup(self): prepared = self.prepare(MAGENT_APPLICATION_URL="") self.assertEqual(prepared["MAGENT_APPLICATION_URL"], "") self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], "http://localhost:3000") self.assertTrue(self.state_path.exists()) def test_managed_api_docs_cannot_be_enabled(self): for value in ("true", "1", "yes", "on", "invalid"): with self.subTest(value=value), self.assertRaisesRegex(bootstrap.BootstrapError, "API_DOCS_ENABLED"): self.prepare(API_DOCS_ENABLED=value) self.assertFalse(self.state_path.exists()) def test_saved_public_url_controls_restart_without_key_regeneration(self): original = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data) state_bytes = self.state_path.read_bytes() self.create_database(admin=True) with closing(sqlite3.connect(self.database)) as connection: with connection: connection.execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)") connection.execute("INSERT INTO settings VALUES ('magent_application_url', 'https://saved.example.test')") restarted = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data) self.assertEqual(restarted["MAGENT_APPLICATION_URL"], "https://saved.example.test") self.assertEqual(restarted["CORS_ALLOW_ORIGIN"], "https://saved.example.test") self.assertEqual(restarted["AUTH_COOKIE_SECURE"], "true") self.assertEqual(self.state_path.read_bytes(), state_bytes) for name in bootstrap.SECRET_NAMES: self.assertEqual(restarted[name], original[name]) def test_saved_public_url_wins_over_stale_deployment_url_on_restart(self): self.prepare() self.create_database(admin=True) with closing(sqlite3.connect(self.database)) as connection: with connection: connection.execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)") connection.execute("INSERT INTO settings VALUES ('magent_application_url', 'http://magent.lan:3000')") restarted = self.prepare(CORS_ALLOW_ORIGIN="https://magent.example.test") self.assertEqual(restarted["MAGENT_APPLICATION_URL"], "http://magent.lan:3000") self.assertEqual(restarted["CORS_ALLOW_ORIGIN"], "http://magent.lan:3000") self.assertEqual(restarted["AUTH_COOKIE_SECURE"], "false") def test_invalid_saved_url_fails_closed_without_changing_keys(self): self.prepare() original = self.state_path.read_bytes() self.create_database(admin=True) with closing(sqlite3.connect(self.database)) as connection: with connection: connection.execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)") connection.execute("INSERT INTO settings VALUES ('magent_application_url', 'https://user:secret@evil.test')") with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertEqual(self.state_path.read_bytes(), original) def test_existing_database_or_recovery_sidecar_never_generates_replacement_keys(self): for suffix in ("", "-wal", "-shm", "-journal"): with self.subTest(suffix=suffix): path = Path(str(self.database) + suffix) path.write_bytes(b"existing installation data") try: with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertEqual(path.read_bytes(), b"existing installation data") self.assertFalse(self.state_path.exists()) finally: path.unlink() def test_lost_keys_after_initialization_are_not_recreated(self): self.prepare() self.create_database() self.state_path.unlink() with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertFalse(self.state_path.exists()) def test_fresh_manual_secrets_conflict_without_writing_state(self): for name in bootstrap.SECRET_NAMES: with self.subTest(name=name), self.assertRaises(bootstrap.BootstrapError): self.prepare(**{name: "synthetic-manual-secret"}) self.assertFalse(self.state_path.exists()) def test_matching_environment_values_are_accepted_but_conflicts_never_replace_file(self): first = self.prepare() original = self.state_path.read_bytes() keys = {name: first[name] for name in bootstrap.SECRET_NAMES} self.assertEqual(self.prepare(**keys), first) for name in bootstrap.SECRET_NAMES: with self.subTest(name=name), self.assertRaises(bootstrap.BootstrapError) as raised: self.prepare(**{name: "conflicting-private-value"}) self.assertNotIn("conflicting-private-value", str(raised.exception)) self.assertEqual(self.state_path.read_bytes(), original) def test_custom_database_location_is_rejected_without_touching_it(self): custom = self.root / "other.db" with self.assertRaises(bootstrap.BootstrapError): self.prepare(SQLITE_PATH=str(custom)) self.assertFalse(custom.exists()) self.assertFalse(self.state_path.exists()) def test_missing_or_symlink_data_directory_is_rejected(self): with self.assertRaises(bootstrap.BootstrapError): bootstrap.prepare_environment(self.environment, self.root / "missing") linked = self.root / "linked-data" self.create_symlink(linked, self.data, directory=True) with self.assertRaises(bootstrap.BootstrapError): bootstrap.prepare_environment(self.environment, linked) self.assertFalse(self.state_path.exists()) @unittest.skipUnless(os.name == "posix", "POSIX filesystem permissions") def test_shared_writable_data_directory_is_rejected(self): self.data.chmod(0o777) with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertFalse(self.state_path.exists()) def test_malformed_json_oversized_and_invalid_schema_never_get_replaced(self): self.prepare() valid = self.state() invalid_states = [ b"not-json", b"\xff", b"x" * (bootstrap.MAX_STATE_BYTES + 1), b"[]", b"{}", json.dumps({**valid, "version": True}).encode(), json.dumps({**valid, "version": 2}).encode(), json.dumps({**valid, "unexpected": "value"}).encode(), json.dumps({**valid, "JWT_SECRET": None}).encode(), json.dumps({**valid, "JWT_SECRET": "a" * 64}).encode(), json.dumps({**valid, "JWT_SECRET": "short"}).encode(), json.dumps({**valid, "SETUP_TOKEN": valid["JWT_SECRET"]}).encode(), json.dumps({**valid, "SETTINGS_ENCRYPTION_KEY": "invalid-key"}).encode(), json.dumps({**valid, "SETTINGS_ENCRYPTION_KEY": base64.urlsafe_b64encode(b"short").decode()}).encode(), ] for index, payload in enumerate(invalid_states): with self.subTest(case=index): self.state_path.write_bytes(payload) with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertEqual(self.state_path.read_bytes(), payload) def test_state_directory_is_not_replaced(self): self.state_path.mkdir() with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertTrue(self.state_path.is_dir()) def test_state_symlink_is_not_followed_or_replaced(self): self.prepare() target = self.root / "original-secrets.json" self.state_path.rename(target) original = target.read_bytes() self.create_symlink(self.state_path, target) with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertEqual(target.read_bytes(), original) self.assertTrue(self.state_path.is_symlink()) @unittest.skipUnless(os.name == "posix", "POSIX filesystem permissions") def test_publicly_readable_secrets_are_rejected_without_fixing_or_overwriting_them(self): self.prepare() original = self.state_path.read_bytes() self.state_path.chmod(0o644) with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertEqual(stat.S_IMODE(self.state_path.stat().st_mode), 0o644) self.assertEqual(self.state_path.read_bytes(), original) @unittest.skipUnless(hasattr(os, "mkfifo"), "POSIX named pipes") def test_named_pipe_state_is_rejected_without_blocking(self): os.mkfifo(self.state_path, 0o600) with self.assertRaises(bootstrap.BootstrapError): self.prepare() self.assertTrue(stat.S_ISFIFO(self.state_path.stat().st_mode)) def test_https_sets_matching_cors_and_secure_cookies(self): prepared = self.prepare() self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], self.environment["MAGENT_APPLICATION_URL"]) self.assertEqual(prepared["AUTH_COOKIE_SECURE"], "true") def test_explicit_http_lan_origin_disables_secure_cookie_flag_only(self): prepared = self.prepare(MAGENT_APPLICATION_URL="http://192.0.2.10:3000") self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], "http://192.0.2.10:3000") self.assertEqual(prepared["AUTH_COOKIE_SECURE"], "false") def test_invalid_origin_fails_without_creating_keys(self): origins = ( "not-a-url", "https://magent.example.test/", "https://magent.example.test/path", "//magent.example.test", "ftp://magent.example.test", "http:/magent.example.test", "https://user:password@magent.example.test", "https://@magent.example.test", "https://magent.example.test?", "https://magent.example.test#", "https://magent.example.test:0", "https://magent.example.test:65536", "https://*.example.test", "https://magent.\ttest", "https://magent.example.test\\path", " https://magent.example.test", "https://magent.example.test\x00", ) for origin in origins: with self.subTest(origin=repr(origin)), self.assertRaises(bootstrap.BootstrapError): self.prepare(MAGENT_APPLICATION_URL=origin) self.assertFalse(self.state_path.exists()) def test_cors_mismatch_or_cookie_scheme_conflict_fails_without_keys(self): cases = ( {"CORS_ALLOW_ORIGIN": "https://elsewhere.example.test"}, {"AUTH_COOKIE_SECURE": "false"}, {"AUTH_COOKIE_SECURE": "0"}, {"AUTH_COOKIE_SECURE": "maybe"}, {"MAGENT_APPLICATION_URL": "http://magent.lan:3000", "AUTH_COOKIE_SECURE": "true"}, {"MAGENT_APPLICATION_URL": "http://magent.lan:3000", "AUTH_COOKIE_SECURE": "1"}, ) for changes in cases: with self.subTest(changes=changes), self.assertRaises(bootstrap.BootstrapError): self.prepare(**changes) self.assertFalse(self.state_path.exists()) def test_racing_initializers_publish_and_return_one_complete_state(self): barrier = Barrier(8) def initialize(_): barrier.wait(timeout=10) return self.prepare() with ThreadPoolExecutor(max_workers=8) as executor: results = list(executor.map(initialize, range(8))) for result in results: self.assertEqual(result, results[0]) state = self.state() for name in bootstrap.SECRET_NAMES: self.assertEqual(state[name], results[0][name]) self.assertEqual(list(self.data.glob(".magent-secrets-*")), []) def test_token_command_requires_managed_mode_and_does_not_create_state(self): with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token({}, self.data) self.assertFalse(self.state_path.exists()) with self.assertRaises((bootstrap.BootstrapError, FileNotFoundError)): bootstrap.setup_token(self.environment, self.data) self.assertFalse(self.state_path.exists()) self.assertFalse(self.database.exists()) def test_token_command_does_not_create_an_uninitialized_database(self): self.prepare() with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) self.assertFalse(self.database.exists()) def test_token_command_returns_only_initial_token_using_readonly_closed_connection(self): prepared = self.prepare() self.create_database() before = {path.name: path.read_bytes() for path in self.data.iterdir()} connections = [] real_connect = sqlite3.connect def connect(*args, **kwargs): self.assertTrue(kwargs.get("uri")) self.assertTrue(args[0].endswith("?mode=ro")) connection = real_connect(*args, **kwargs) with self.assertRaises(sqlite3.OperationalError): connection.execute("INSERT INTO users VALUES ('admin')") connections.append(connection) return connection with patch.object(bootstrap.sqlite3, "connect", side_effect=connect): token = bootstrap.setup_token(self.environment, self.data) self.assertEqual(token, prepared["SETUP_TOKEN"]) self.assertEqual({path.name: path.read_bytes() for path in self.data.iterdir()}, before) for connection in connections: with self.assertRaises(sqlite3.ProgrammingError): connection.execute("SELECT 1") def test_token_command_refuses_once_any_admin_exists_even_before_setup_completion(self): self.prepare() self.create_database(admin=True) with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) def test_token_command_refuses_completed_setup_even_without_admin(self): self.prepare() self.create_database(completed=1) with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) def test_token_command_refuses_unknown_or_invalid_database_state(self): self.prepare() for payload in (b"", b"not a SQLite database"): with self.subTest(payload=payload): self.database.write_bytes(payload) with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) self.assertEqual(self.database.read_bytes(), payload) self.database.unlink() self.create_database() with closing(sqlite3.connect(self.database)) as connection: with connection: connection.execute("DELETE FROM installation_setup") with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) def test_existing_database_symlink_is_rejected_even_with_valid_state(self): self.prepare() self.create_database() target = self.root / "other.db" self.database.rename(target) original = target.read_bytes() self.create_symlink(self.database, target) with self.assertRaises(bootstrap.BootstrapError): self.prepare() with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) self.assertEqual(target.read_bytes(), original) def test_existing_database_directory_is_rejected_even_with_valid_state(self): self.prepare() self.database.mkdir() with self.assertRaises(bootstrap.BootstrapError): self.prepare() with self.assertRaises(bootstrap.BootstrapError): bootstrap.setup_token(self.environment, self.data) self.assertTrue(self.database.is_dir()) def test_startup_passes_keys_to_runtime_without_printing_them(self): prepared = self.prepare() stdout, stderr = io.StringIO(), io.StringIO() with patch.dict(os.environ, self.environment, clear=True), \ patch.object(bootstrap.sys, "argv", ["bootstrap", "supervisord", "-c", "config"]), \ patch.object(bootstrap, "prepare_environment", return_value=prepared), \ patch.object(bootstrap.os, "execvpe") as execute, \ redirect_stdout(stdout), redirect_stderr(stderr): self.assertEqual(bootstrap.main(), 0) execute.assert_called_once_with("supervisord", ["supervisord", "-c", "config"], prepared) self.assertIn("setup-token", stdout.getvalue()) self.assertEqual(stderr.getvalue(), "") for name in bootstrap.SECRET_NAMES: self.assertNotIn(prepared[name], stdout.getvalue() + stderr.getvalue()) def test_disabled_startup_does_not_print_managed_install_instructions(self): stdout, stderr = io.StringIO(), io.StringIO() environment = {"JWT_SECRET": "manual-test-value"} with patch.dict(os.environ, environment, clear=True), \ patch.object(bootstrap.sys, "argv", ["bootstrap", "supervisord"]), \ patch.object(bootstrap.os, "execvpe") as execute, \ redirect_stdout(stdout), redirect_stderr(stderr): self.assertEqual(bootstrap.main(), 0) execute.assert_called_once_with("supervisord", ["supervisord"], environment) self.assertEqual(stdout.getvalue() + stderr.getvalue(), "") def test_cli_explicit_token_command_prints_only_token_not_other_keys(self): prepared = self.prepare() self.create_database() retrieve = bootstrap.setup_token stdout, stderr = io.StringIO(), io.StringIO() with patch.dict(os.environ, self.environment, clear=True), \ patch.object(bootstrap.sys, "argv", ["bootstrap", "setup-token"]), \ patch.object(bootstrap, "setup_token", side_effect=lambda env: retrieve(env, self.data)), \ patch.object(bootstrap.os, "execvpe") as execute, \ redirect_stdout(stdout), redirect_stderr(stderr): self.assertEqual(bootstrap.main(), 0) execute.assert_not_called() self.assertEqual(stdout.getvalue(), prepared["SETUP_TOKEN"] + "\n") self.assertEqual(stderr.getvalue(), "") self.assertNotIn(prepared["JWT_SECRET"], stdout.getvalue()) self.assertNotIn(prepared["SETTINGS_ENCRYPTION_KEY"], stdout.getvalue()) def test_cli_unexpected_io_failure_never_logs_sensitive_exception_details(self): stdout, stderr = io.StringIO(), io.StringIO() with patch.object(bootstrap.sys, "argv", ["bootstrap", "supervisord"]), \ patch.object(bootstrap, "prepare_environment", side_effect=OSError("private-secret-material")), \ redirect_stdout(stdout), redirect_stderr(stderr): self.assertEqual(bootstrap.main(), 1) self.assertEqual(stdout.getvalue(), "") self.assertNotIn("private-secret-material", stderr.getvalue()) self.assertIn("Check volume permissions", stderr.getvalue()) if __name__ == "__main__": unittest.main()