feat(detmir): ship ops, dlp, 1c and mcp updates

This commit is contained in:
igor04091968
2026-05-27 05:56:43 +03:00
parent 033ae810f3
commit 3042bd5042
85 changed files with 12028 additions and 460 deletions
@@ -10,7 +10,7 @@ from fastapi.middleware.cors import CORSMiddleware
from case_rules import is_self_test_case
from case_schema import CaseCommentCreate, CaseCreate, CaseHayabusaLink, CaseUpdate
from case_storage import CaseStorage
from case_storage import CaseStorage, ForensicsHostMismatchError
DB = Path(os.environ.get("AW_DLP_CASE_DB_PATH", "/opt/activitywatch/dlp-case-management/cases.db"))
APP = FastAPI(title="AWatch DLP Case Management")
@@ -85,5 +85,7 @@ def list_comments(case_id: int, limit: int = Query(default=200, ge=1, le=2000))
def link_hayabusa(case_id: int, payload: CaseHayabusaLink) -> dict[str, Any]:
try:
return STORE.link_hayabusa(case_id=case_id, payload=payload.model_dump(exclude_none=True), actor="api")
except ForensicsHostMismatchError as exc:
raise HTTPException(status_code=409, detail=str(exc))
except KeyError:
raise HTTPException(status_code=404, detail="case not found")
@@ -11,6 +11,10 @@ from typing import Any, Iterator
from evidence_chain import evidence_sha256, normalize_evidence_chain
class ForensicsHostMismatchError(ValueError):
pass
class CaseStorage:
def __init__(self, db_path: Path) -> None:
self.db_path = db_path
@@ -86,6 +90,10 @@ class CaseStorage:
def _now() -> str:
return datetime.now(timezone.utc).isoformat()
@staticmethod
def _normalize_host(value: Any) -> str:
return str(value or "").strip().lower()
@staticmethod
def _load_json_field(raw: Any) -> dict[str, Any] | None:
if not raw:
@@ -228,6 +236,12 @@ class CaseStorage:
now = self._now()
with self.conn() as c:
existing = self.get_case(case_id, c)
case_host = self._normalize_host(existing.get("host"))
forensic_host = self._normalize_host(payload.get("host"))
if case_host and forensic_host and case_host != forensic_host:
raise ForensicsHostMismatchError(
f"hayabusa host mismatch: case host={existing.get('host')} payload host={payload.get('host')}"
)
forensics = existing.get("forensics") or {}
forensics["hayabusa"] = {
"tool": "hayabusa",
@@ -5,7 +5,7 @@ import tempfile
import unittest
from pathlib import Path
from case_storage import CaseStorage
from case_storage import CaseStorage, ForensicsHostMismatchError
class CaseStorageHayabusaLinkTest(unittest.TestCase):
@@ -62,6 +62,32 @@ class CaseStorageHayabusaLinkTest(unittest.TestCase):
cases = storage.list_cases(host="SHARKON2025", limit=10)
self.assertEqual(len(cases), 1)
def test_link_hayabusa_rejects_host_mismatch(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
db_path = Path(tmpdir) / "cases.db"
storage = CaseStorage(db_path)
created = storage.create_case(
{
"incident_id": "inc-2",
"host": "SHARKON2025",
"title": "DLP print incident",
"severity": "high",
},
actor="test",
)
with self.assertRaises(ForensicsHostMismatchError):
storage.link_hayabusa(
case_id=int(created["id"]),
payload={
"host": "stability",
"mode": "incident",
"status": "ok",
"intake_id": "pkg-2",
"report_dir": "/opt/hayabusa/reports/stability/run-1",
},
actor="test",
)
if __name__ == "__main__":
unittest.main()