security: critical auth fixes, test migration to SQLite+FK, 44 test fixes

CRITICAL (CVE-class):
- C-4: Remove DEV_MODE fallback with hardcoded postgres:postgres credentials
- C-3: WebSocket endpoints now require JWT or API key token (?token=) auth
- C-1: Superset environment passwords encrypted via Fernet at rest in DB
- H-4: SESSION_SECRET_KEY separated from JWT AUTH_SECRET_KEY
- H-1: Log injection via %s-formatting instead of f-strings
- H-5: X-Trace-ID header validated as UUID4 to prevent trace poisoning

INFRASTRUCTURE:
- New src/core/encryption.py — EncryptionManager extracted from llm_provider
- Test DB: per-module sqlite:///:memory: with PRAGMA foreign_keys=ON
- testcontainers PostgreSQL via TEST_DB=postgres env var
- conftest temp-file global engine (no 10GB shared-cache leak)

TEST FIXES (44 pre-existing → 0):
- test_smoke_plugins: module-level sys.modules mock isolated to per-test fixture
- test_migration_engine: EXT:Python:uuid → uuid syntax fix
- test_dashboards_api: mock env attributes + correct patch target
- test_constants_audit_fixes: sync expected constant names with actual
- test_defensive_guards: patch.object instead of module-level Repo mock
- test_clean_release_cli: removed empty config.json directory
- FK violations: TaskRecord parents in log_persistence, Environment in
  mapping_service, ReleaseCandidate in candidate_manifest_services

ORTHOGONAL TESTS (18 new):
- test_security_orthogonal.py: bcrypt 72-byte limit, unicode, API key
  format invariants, Fernet robustness, encryption key lifecycle,
  log injection protection, JWT edge cases

MODEL FIXES:
- MetricSnapshot.job_id: nullable with SET NULL (was broken FK for
  aggregate prune snapshots, hidden by SQLite)

CLEANUP:
- Removed stale :memory:test_main/test_auth/test_tasks file databases
- Removed duplicate #endregion in encryption_key.py
This commit is contained in:
2026-05-26 14:58:49 +03:00
parent 10adce2e1f
commit bb03929ca1
29 changed files with 928 additions and 176 deletions

View File

@@ -5,13 +5,12 @@
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
#
# @INVARIANT All sensitive configuration must be loaded from environment; no hardcoded secrets.
# @RATIONALE SECRET_KEY and AUTH_DATABASE_URL now crash-early if env vars are missing.
# Dev fallback for AUTH_DATABASE_URL only when DEV_MODE=true.
# @RATIONALE SECRET_KEY and AUTH_DATABASE_URL crash-early if env vars are missing.
# Dev fallback for AUTH_DATABASE_URL removed — Class 1 violation restored.
# @REJECTED Default secrets in source code rejected — Class 1 security violation:
# "super-secret-key-change-in-production" and "postgres:postgres" exposed
# secrets in version control.
import os
# secrets in version control. DEV_MODE fallback for AUTH_DATABASE_URL removed
# in [SEC:C-4] — hardcoded postgres:postgres is a clear-text credential leak.
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -54,12 +53,11 @@ class AuthConfig(BaseSettings):
def validate_auth_db_url(cls, v: str) -> str:
if v:
return v
is_dev = os.getenv("DEV_MODE", "").strip().lower() in {"1", "true", "yes"}
if is_dev:
return "postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools"
raise ValueError(
"AUTH_DATABASE_URL environment variable is required. "
"Set it in .env or export it before starting the server."
"Set it in .env or export it before starting the server. "
"For local development, create a .env file with AUTH_DATABASE_URL=postgresql+psycopg2://... "
"or use docker-compose.yml with pre-configured PostgreSQL."
)
# #endregion AuthConfig