from __future__ import annotations from pathlib import Path import re REPOSITORY_ROOT = Path(__file__).resolve().parents[4] DOCKERIGNORE = REPOSITORY_ROOT / ".dockerignore" DOCKERFILE = REPOSITORY_ROOT / "infra" / "docker" / "Dockerfile" REQUIREMENTS_LOCK = REPOSITORY_ROOT / "app" / "requirements.lock" POSTGRES_DOCKERFILE = ( REPOSITORY_ROOT / "infra" / "docker" / "postgres" / "Dockerfile" ) COMPOSE_FILE = ( REPOSITORY_ROOT / "infra" / "compose" / "docker-compose.yml" ) EXCHANGE_COMPOSE_FILE = ( REPOSITORY_ROOT / "infra" / "compose" / "docker-compose.exchange-auth.yml" ) POSTGRES_INIT_SCRIPT = ( REPOSITORY_ROOT / "infra" / "docker" / "postgres" / "init-application-role.sh" ) POSTGRES_HEALTHCHECK_SCRIPT = ( REPOSITORY_ROOT / "infra" / "docker" / "postgres" / "healthcheck-application-role.sh" ) PYTHON_IMAGE = ( "python:3.12.13-slim-bookworm@sha256:" "d50fb7611f86d04a3b0471b46d7557818d88983fc3136726336b2a4c657aa30b" ) POSTGRES_IMAGE = ( "postgres:16.14-alpine@sha256:" "57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777" ) def read_text(path: Path) -> str: return path.read_text(encoding="utf-8") def yaml_mapping_block( content: str, *, key: str, indentation: int, ) -> str: lines = content.splitlines() marker = f"{' ' * indentation}{key}:" try: start = lines.index(marker) except ValueError as error: raise AssertionError(f"YAML block {key!r} not found") from error end = len(lines) for index in range(start + 1, len(lines)): line = lines[index] if not line.strip(): continue current_indentation = len(line) - len(line.lstrip()) if current_indentation <= indentation: end = index break return "\n".join(lines[start:end]) def test_docker_context_is_an_explicit_production_allowlist() -> None: content = read_text(DOCKERIGNORE) active_lines = tuple( line.strip() for line in content.splitlines() if line.strip() and not line.lstrip().startswith("#") ) expected_inclusions = { "!.dockerignore", "!infra/", "!infra/docker/", "!infra/docker/Dockerfile", "!infra/docker/postgres/", "!infra/docker/postgres/Dockerfile", "!infra/docker/postgres/healthcheck-application-role.sh", "!infra/docker/postgres/init-application-role.sh", "!app/", "!app/requirements.txt", "!app/requirements.lock", "!app/src/", "!app/src/**", } assert active_lines[0] == "**" assert { line for line in active_lines if line.startswith("!") } == expected_inclusions assert not any( line.startswith("!app/.env") or line.startswith("!app/.venv") or line.startswith("!app/tests") for line in active_lines ) assert "app/src/**/__pycache__/" in active_lines assert "app/src/**/*.py[cod]" in active_lines assert { "app/src/**/.env", "app/src/**/.env.*", "app/src/**/*.pem", "app/src/**/*.key", "app/src/**/*.p12", "app/src/**/*.pfx", "app/src/**/*.log", "app/src/**/*.dump", "app/src/**/*.sqlite", "app/src/**/*.sqlite3", }.issubset(active_lines) def test_dockerfile_uses_pinned_minimal_non_root_image() -> None: content = read_text(DOCKERFILE) copy_or_add_instructions = tuple( line.strip() for line in content.splitlines() if line.lstrip().startswith(("COPY ", "ADD ")) ) assert content.splitlines()[0] == f"FROM {PYTHON_IMAGE}" assert copy_or_add_instructions == ( "COPY app/requirements.lock ./requirements.lock", "COPY --chown=10001:10001 app/src ./src", ) assert "--require-hashes" in content assert "--requirement requirements.lock" in content assert "USER 10001:10001" in content assert "STOPSIGNAL SIGINT" in content assert "/var/lib/dzentra/runtime.env" in content def test_compose_requires_explicit_project_namespace() -> None: content = read_text(COMPOSE_FILE) assert content.splitlines()[0] == ( "name: ${DZENTRA_COMPOSE_PROJECT_NAME:" "?DZENTRA_COMPOSE_PROJECT_NAME must be set}" ) def test_production_dependencies_have_complete_hash_lock() -> None: content = read_text(REQUIREMENTS_LOCK) package_matches = tuple(re.finditer( r"^([a-z0-9][a-z0-9._-]*)==([^ \\;]+)", content, re.MULTILINE, )) assert len(package_matches) == 25 for index, package_match in enumerate(package_matches): block_end = ( package_matches[index + 1].start() if index + 1 < len(package_matches) else len(content) ) assert "--hash=sha256:" in content[package_match.start():block_end] assert "pytest==" not in content assert "pyright==" not in content def test_compose_uses_pinned_internal_postgres() -> None: content = read_text(COMPOSE_FILE) postgres = yaml_mapping_block( content, key="postgres", indentation=2, ) postgres_dockerfile = read_text(POSTGRES_DOCKERFILE) assert postgres_dockerfile.splitlines()[0] == f"FROM {POSTGRES_IMAGE}" assert postgres_dockerfile.count("COPY --chmod=0555") == 2 assert "ADD " not in postgres_dockerfile assert "init-application-role.sh" in postgres_dockerfile assert "healthcheck-application-role.sh" in postgres_dockerfile assert "dockerfile: infra/docker/postgres/Dockerfile" in postgres assert "dzentra-postgres:16.14-hardened" in postgres assert "pull_policy: build" in postgres assert ( "POSTGRES_PASSWORD_FILE: /run/secrets/postgres_admin_password" in postgres ) assert "APP_DB_USER: ${DB_USER:-dzentra_bot}" in postgres assert "postgres_admin_password" in postgres assert "db_password" in postgres assert not re.search(r"^\s+POSTGRES_PASSWORD:\s", postgres, re.MULTILINE) assert "ports:" not in postgres assert "container_name:" not in postgres assert "privileged:" not in postgres assert "network_mode:" not in postgres assert yaml_mapping_block( content, key="networks", indentation=0, ).rstrip() == ( "networks:\n" " database:\n" " internal: true\n" " egress:" ) assert 'restart: "no"' in postgres assert yaml_mapping_block( postgres, key="security_opt", indentation=4, ) == " security_opt:\n - no-new-privileges:true" assert yaml_mapping_block( postgres, key="cap_drop", indentation=4, ) == " cap_drop:\n - ALL" assert yaml_mapping_block( postgres, key="cap_add", indentation=4, ) == ( " cap_add:\n" " - CHOWN\n" " - DAC_OVERRIDE\n" " - FOWNER\n" " - SETGID\n" " - SETUID" ) assert yaml_mapping_block( postgres, key="networks", indentation=4, ) == " networks:\n - database" assert yaml_mapping_block( postgres, key="volumes", indentation=4, ) == ( " volumes:\n" " - dzentra_postgres_data:/var/lib/postgresql/data" ) assert yaml_mapping_block( postgres, key="tmpfs", indentation=4, ) == ( " tmpfs:\n" " - /tmp:rw,noexec,nosuid,nodev,size=64m\n" " - /var/run/postgresql:rw,nosuid,nodev,size=16m" ) assert "read_only: true" in postgres assert "POSTGRES_STOP_GRACE_PERIOD must be set" in postgres assert "/usr/local/bin/dzentra-postgres-healthcheck" in postgres def test_compose_does_not_inject_direct_application_secrets() -> None: content = read_text(COMPOSE_FILE) bot = yaml_mapping_block( content, key="bot", indentation=2, ) assert "env_file:" not in bot assert "privileged:" not in bot assert "network_mode:" not in bot assert "BOT_TOKEN_FILE: /run/secrets/bot_token" in bot assert "DB_PASSWORD_FILE: /run/secrets/db_password" in bot assert not re.search(r"^\s+BOT_TOKEN:\s", bot, re.MULTILINE) assert not re.search(r"^\s+DB_PASSWORD:\s", bot, re.MULTILINE) assert not re.search(r"^\s+EXCHANGE_API_KEY:\s", bot, re.MULTILINE) assert not re.search( r"^\s+EXCHANGE_API_SECRET:\s", bot, re.MULTILINE, ) def test_compose_hardens_bot_and_preserves_runtime_settings() -> None: content = read_text(COMPOSE_FILE) bot = yaml_mapping_block( content, key="bot", indentation=2, ) assert 'user: "10001:10001"' in bot assert "pull_policy: build" in bot assert 'restart: "no"' in bot assert yaml_mapping_block( bot, key="security_opt", indentation=4, ) == " security_opt:\n - no-new-privileges:true" assert yaml_mapping_block( bot, key="cap_drop", indentation=4, ) == " cap_drop:\n - ALL" assert " cap_add:" not in bot assert yaml_mapping_block( bot, key="networks", indentation=4, ) == " networks:\n - database\n - egress" assert yaml_mapping_block( bot, key="volumes", indentation=4, ) == ( " volumes:\n" " - dzentra_runtime_config:/var/lib/dzentra" ) assert yaml_mapping_block( bot, key="tmpfs", indentation=4, ) == " tmpfs:\n - /tmp:rw,noexec,nosuid,nodev,size=64m" assert "read_only: true" in bot assert "init: true" in bot assert "BOT_STOP_GRACE_PERIOD must be set" in bot assert "DZENTRA_RUNTIME_ENV_FILE: /var/lib/dzentra/runtime.env" in bot assert "dzentra_runtime_config:/var/lib/dzentra" in bot def test_compose_declares_file_backed_secret_sources() -> None: content = read_text(COMPOSE_FILE) assert "POSTGRES_ADMIN_PASSWORD_SECRET_FILE must be set" in content assert "BOT_TOKEN_SECRET_FILE must be set" in content assert "DB_PASSWORD_SECRET_FILE must be set" in content assert "file: ${BOT_TOKEN_SECRET_FILE:" in content assert "file: ${DB_PASSWORD_SECRET_FILE:" in content def test_exchange_credentials_have_explicit_opt_in_override() -> None: content = read_text(EXCHANGE_COMPOSE_FILE) assert "EXCHANGE_API_KEY_FILE: /run/secrets/exchange_api_key" in content assert ( "EXCHANGE_API_SECRET_FILE: /run/secrets/exchange_api_secret" in content ) assert "EXCHANGE_API_KEY_SOURCE_FILE must be set" in content assert "EXCHANGE_API_SECRET_SOURCE_FILE must be set" in content assert not re.search(r"^\s+EXCHANGE_API_KEY:\s", content, re.MULTILINE) assert not re.search( r"^\s+EXCHANGE_API_SECRET:\s", content, re.MULTILINE, ) def test_postgres_bootstrap_separates_admin_and_application_roles() -> None: content = read_text(POSTGRES_INIT_SCRIPT) assert 'if [ "$APP_DB_USER" = "$POSTGRES_USER" ]' in content assert "Пароли PostgreSQL admin и application roles должны различаться" in content assert "postgres_admin_password=$POSTGRES_PASSWORD" in content assert "read_secret_file /run/secrets/postgres_admin_password" not in content assert "--single-transaction" in content assert "--no-psqlrc" in content assert "\\getenv app_password DZENTRA_APP_DB_PASSWORD" in content assert "NOSUPERUSER NOCREATEDB" in content assert "NOCREATEROLE INHERIT NOREPLICATION NOBYPASSRLS" in content assert "ALTER DATABASE %I OWNER TO %I" in content assert "GRANT USAGE, CREATE ON SCHEMA public TO %I" in content assert "--set app_password" not in content def test_postgres_healthcheck_rejects_privileged_application_role() -> None: content = read_text(POSTGRES_HEALTHCHECK_SCRIPT) assert "NOT rolsuper" in content assert "NOT rolcreatedb" in content assert "NOT rolcreaterole" in content assert "NOT rolreplication" in content assert "NOT rolbypassrls" in content assert "pg_catalog.pg_auth_members" in content assert "member_role.rolname = :'app_user'" in content assert "pg_catalog.pg_get_userbyid" in content assert "test \"$role_status\" = \"1\"" in content