feat(dlp): add server-side analyzer, evidence chain, compliance scheduler, and syslog forwarder

This commit is contained in:
igor04091968
2026-05-13 04:54:08 +03:00
parent 0f1b82233a
commit 6ebf0ac67e
17 changed files with 505 additions and 19 deletions
+16 -3
View File
@@ -8,6 +8,8 @@ from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Iterator
from evidence_chain import evidence_sha256, normalize_evidence_chain
class CaseStorage:
def __init__(self, db_path: Path) -> None:
@@ -98,6 +100,15 @@ class CaseStorage:
def create_case(self, payload: dict[str, Any], actor: str | None = None) -> dict[str, Any]:
now = self._now()
normalized_evidence = None
evidence_digest = None
if payload.get("evidence") is not None:
normalized_evidence = normalize_evidence_chain(
payload=payload.get("evidence"),
source_bucket=payload.get("source_bucket"),
source_event_ts=payload.get("source_event_ts"),
)
evidence_digest = normalized_evidence.get("latest_sha256") or evidence_sha256(payload.get("evidence"))
with self.conn() as c:
cur = c.execute(
"""
@@ -114,7 +125,7 @@ class CaseStorage:
payload.get("assignee"),
payload.get("source_bucket"),
payload.get("source_event_ts"),
json.dumps(payload.get("evidence"), ensure_ascii=False) if payload.get("evidence") is not None else None,
json.dumps(normalized_evidence, ensure_ascii=False) if normalized_evidence is not None else None,
now,
now,
),
@@ -125,7 +136,10 @@ class CaseStorage:
case_id=case_id,
action="create",
actor=actor,
details={"fields": {k: v for k, v in payload.items() if k != "evidence"}},
details={
"fields": {k: v for k, v in payload.items() if k != "evidence"},
"evidence_sha256": evidence_digest,
},
)
c.commit()
return self.get_case(case_id, c)
@@ -252,4 +266,3 @@ class CaseStorage:
self._now(),
),
)
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
from __future__ import annotations
import hashlib
import json
from datetime import datetime, timezone
from typing import Any
def _utc_now() -> str:
return datetime.now(timezone.utc).isoformat()
def _canonical_json(payload: Any) -> str:
return json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def evidence_sha256(payload: Any) -> str:
return hashlib.sha256(_canonical_json(payload).encode("utf-8")).hexdigest()
def build_evidence_record(
payload: Any,
source_bucket: str | None = None,
source_event_ts: str | None = None,
) -> dict[str, Any]:
return {
"recorded_at": _utc_now(),
"source_bucket": source_bucket,
"source_event_ts": source_event_ts,
"sha256": evidence_sha256(payload),
"payload": payload,
}
def normalize_evidence_chain(
payload: Any,
source_bucket: str | None = None,
source_event_ts: str | None = None,
) -> dict[str, Any]:
if isinstance(payload, dict) and isinstance(payload.get("items"), list):
return payload
record = build_evidence_record(
payload=payload,
source_bucket=source_bucket,
source_event_ts=source_event_ts,
)
return {
"items": [record],
"latest_sha256": record["sha256"],
"chain_length": 1,
}