Files
A-Team-Security-Infra-Agent…/backend/routes/cis_engine.py
nogueiraguh 1135e9d6a9 refactor: decompose monolith — app.py 10,600→143 lines, 30+ modules
Fase 0 complete: extract all business logic, auth, database, and 176
endpoints from monolithic app.py into dedicated modules.

Structure:
- app.py: FastAPI hub (CORS, routers, startup/shutdown)
- models.py: 13 Pydantic request models
- utils.py: shared utilities (embed status, upload validation, process registries)
- config.py: constants, env vars, model catalogs
- auth/: crypto, jwt_auth, oidc, rate_limit
- database/: db(), init_db(), schema DDL
- services/: genai, compliance, chat, terraform, embeddings, cis_reports, mcp
- routes/: 13 APIRouter modules (auth, users, oci_config, oci_explorer,
  genai, mcp, adb, embeddings, reports, chat, terraform, settings, cis_engine)

Also: README updated with OCIR auth instructions for manual docker run.

86 tests passing.
2026-04-06 15:20:10 -03:00

106 lines
4.7 KiB
Python

"""CIS Engine routes — version management, check-update, update, health check."""
import re
import time
from pathlib import Path
from datetime import datetime
from fastapi import APIRouter, HTTPException, Depends
from config import VERSION, _START_TIME, log
from database import db
from auth.jwt_auth import current_user, require, _audit
router = APIRouter()
# ── Constants ────────────────────────────────────────────────────────────────
CIS_REPORTS_PATH = Path("/app/cis_reports.py")
CIS_GITHUB_RAW = "https://raw.githubusercontent.com/oci-landing-zones/oci-cis-landingzone-quickstart/main/scripts/cis_reports.py"
CIS_PATCHES = [
{"name": "region_none_check_1",
"original": "elif region.region_name in self.__regions_to_run_in or self.__run_in_all_regions:",
"patched": "elif self.__run_in_all_regions or (self.__regions_to_run_in and region.region_name in self.__regions_to_run_in):"},
{"name": "region_none_check_2",
"original": "if self.__home_region not in self.__regions_to_run_in:",
"patched": "if not self.__run_in_all_regions and self.__regions_to_run_in and self.__home_region not in self.__regions_to_run_in:"},
]
# ── Helpers ──────────────────────────────────────────────────────────────────
def _read_cis_version(content: str = None) -> dict:
if content is None:
if not CIS_REPORTS_PATH.exists(): return {"version": "unknown", "date": "unknown"}
content = CIS_REPORTS_PATH.read_text(encoding="utf-8", errors="replace")
ver = re.search(r'RELEASE_VERSION\s*=\s*["\']([^"\']+)["\']', content)
dt = re.search(r'UPDATED_DATE\s*=\s*["\']([^"\']+)["\']', content)
return {"version": ver.group(1) if ver else "unknown", "date": dt.group(1) if dt else "unknown"}
# ── Endpoints ────────────────────────────────────────────────────────────────
@router.get("/api/cis-engine/version")
async def cis_engine_version(u=Depends(current_user)):
info = _read_cis_version()
return {"version": info["version"], "updated_date": info["date"], "file_path": str(CIS_REPORTS_PATH)}
@router.get("/api/cis-engine/check-update")
async def cis_engine_check_update(u=Depends(require("admin"))):
import requests as req
local = _read_cis_version()
try:
resp = req.get(CIS_GITHUB_RAW, timeout=30)
resp.raise_for_status()
remote = _read_cis_version(resp.text)
except Exception as e:
raise HTTPException(502, f"Failed to check GitHub: {str(e)[:300]}")
return {
"local_version": local["version"], "local_date": local["date"],
"remote_version": remote["version"], "remote_date": remote["date"],
"update_available": remote["version"] != local["version"]
}
@router.post("/api/cis-engine/update")
async def cis_engine_update(u=Depends(require("admin"))):
import requests as req
old = _read_cis_version()
try:
resp = req.get(CIS_GITHUB_RAW, timeout=60)
resp.raise_for_status()
content = resp.text
except Exception as e:
raise HTTPException(502, f"Failed to download from GitHub: {str(e)[:300]}")
new = _read_cis_version(content)
# Apply patches
patches_applied = []
for p in CIS_PATCHES:
if p["original"] in content:
content = content.replace(p["original"], p["patched"])
patches_applied.append(p["name"])
CIS_REPORTS_PATH.write_text(content, encoding="utf-8")
# Save version info
with db() as c:
c.execute("INSERT INTO app_settings (key,value,updated_at) VALUES ('cis_engine_version',?,datetime('now')) ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at",
(new["version"],))
log.info(f"CIS engine updated: {old['version']}{new['version']}, patches: {patches_applied}")
_audit(u["id"], u["username"], "cis_engine_update", None, f"{old['version']}{new['version']}")
return {"ok": True, "old_version": old["version"], "new_version": new["version"], "patches_applied": patches_applied}
@router.get("/api/health")
async def health():
db_ok = False
try:
with db() as c:
c.execute("SELECT 1")
db_ok = True
except Exception:
pass
return {
"status": "ok",
"version": VERSION,
"uptime_seconds": int(time.time() - _START_TIME),
"db_ok": db_ok,
"ts": datetime.utcnow().isoformat(),
}