feat(detmir): ship ops, dlp, 1c and mcp updates
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user