Files
Aegis/backend/app/config.py
Kitos 222979574a feat(phase-38): automatic intelligence — OSINT enrichment + stale coverage detection
Tarea 4.1 — OSINT Enrichment:
- Add OsintItem model with source_type, severity, CVSS metadata, review flag
- Add Alembic migration b022 with osint_items table and optimized indexes
- Add osint_enrichment_service with NVD API integration, deduplication, rate limiting
- Add OSINT router: GET /osint/items, /osint/summary, /osint/technique/{id}
- Add POST /osint/items/{id}/review to mark items as reviewed
- Add POST /osint/enrich/{technique_id} for manual single-technique enrichment
- Techniques with new CVEs are automatically flagged review_required=True
- Register weekly enrichment job in APScheduler
- Add NVD_API_KEY config setting for optional increased rate limits

Tarea 4.2 — Stale Coverage Detection:
- Add stale_detection_service that flags techniques with no validated test
  in the last N days, or never-validated but with a coverage status
- Configurable threshold via STALE_THRESHOLD_DAYS setting (default 365)
- Register daily stale detection job in APScheduler
- Only flags techniques not already marked review_required
2026-02-17 17:47:47 +01:00

109 lines
5.2 KiB
Python

import os
import secrets
import warnings
from pydantic_settings import BaseSettings
# ---------------------------------------------------------------------------
# Detect environment: "production" when AEGIS_ENV or common indicators are set
# ---------------------------------------------------------------------------
_is_production = os.environ.get("AEGIS_ENV", "").lower() == "production" or bool(
os.environ.get("SECRET_KEY") # having an explicit SECRET_KEY hints prod
)
class Settings(BaseSettings):
DATABASE_URL: str = "postgresql://postgres:postgres@postgres:5432/attackdb"
# ── Security ──────────────────────────────────────────────────────
# SECRET_KEY has NO safe default. In development a random key is
# generated at startup (tokens invalidate on restart — acceptable
# for local dev). In production it MUST be supplied via env/.env
# so tokens survive restarts.
SECRET_KEY: str = ""
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 15 # short-lived for security; configurable via env
# ── Redis ─────────────────────────────────────────────────────────
REDIS_URL: str = "redis://redis:6379/0"
# ── CORS ─────────────────────────────────────────────────────────
# Comma-separated list of allowed origins, or a JSON array.
# In dev this defaults to common local ports; in production set it
# to the actual frontend domain(s).
CORS_ORIGINS: str = "http://localhost:3000,http://localhost:5173"
# ── MinIO / S3 ───────────────────────────────────────────────────
MINIO_ENDPOINT: str = "minio:9000"
MINIO_ACCESS_KEY: str = "minioadmin"
MINIO_SECRET_KEY: str = "minioadmin"
MINIO_BUCKET: str = "evidence"
MINIO_SECURE: bool = False # True → use HTTPS to connect to MinIO
# ── Re-testing ───────────────────────────────────────────────────
MAX_RETEST_COUNT: int = 3 # maximum automatic retests per original test
# ── Jira Integration ────────────────────────────────────────────
JIRA_ENABLED: bool = False
JIRA_URL: str = ""
JIRA_USERNAME: str = ""
JIRA_API_TOKEN: str = ""
JIRA_IS_CLOUD: bool = True
JIRA_DEFAULT_PROJECT: str = ""
JIRA_ISSUE_TYPE_TEST: str = "Task"
JIRA_ISSUE_TYPE_CAMPAIGN: str = "Epic"
# ── Tempo Integration ─────────────────────────────────────────────
TEMPO_ENABLED: bool = False
TEMPO_API_TOKEN: str = ""
TEMPO_DEFAULT_WORK_TYPE: str = "Red Team"
# ── OSINT / Intelligence ────────────────────────────────────────
NVD_API_KEY: str = "" # optional; increases NVD rate limit from 5/30s to 50/30s
STALE_THRESHOLD_DAYS: int = 365 # days before coverage is considered stale
# ── Reporting ─────────────────────────────────────────────────────
REPORT_TEMPLATES_DIR: str = "app/templates/reports"
REPORT_OUTPUT_DIR: str = "/tmp/aegis_reports"
COMPANY_NAME: str = "Organization"
COMPANY_LOGO_PATH: str = "app/templates/reports/assets/logo.png"
# ── Scoring weights (must sum to 100) ────────────────────────────
SCORING_WEIGHT_TESTS: int = 40
SCORING_WEIGHT_DETECTION_RULES: int = 20
SCORING_WEIGHT_D3FEND: int = 15
SCORING_WEIGHT_FRESHNESS: int = 15
SCORING_WEIGHT_PLATFORM_DIVERSITY: int = 10
class Config:
env_file = ".env"
settings = Settings()
# ---------------------------------------------------------------------------
# Post-init validation for SECRET_KEY
# ---------------------------------------------------------------------------
_UNSAFE_SECRETS = {
"",
"change-me-in-production",
"change-me-in-production-use-a-long-random-string",
}
if settings.SECRET_KEY in _UNSAFE_SECRETS:
if _is_production:
raise RuntimeError(
"CRITICAL: SECRET_KEY is not configured. "
"Set a strong random value (>= 32 chars) via the SECRET_KEY "
"environment variable or in your .env file before running in "
"production. Example: openssl rand -hex 32"
)
# Development: auto-generate an ephemeral key and warn
settings.SECRET_KEY = secrets.token_hex(32)
warnings.warn(
"SECRET_KEY was not set — using an auto-generated ephemeral key. "
"JWT tokens will be invalidated on every restart. "
"Set SECRET_KEY in your environment for persistent sessions.",
stacklevel=2,
)