secrets-engine/src/secrets_engine/evidence.py
tegwick f579f3761c
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Record privileged action failure evidence
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 12:58:12 +02:00

231 lines
7.8 KiB
Python

"""Non-secret evidence writer.
Every privileged or noteworthy action emits an evidence record to a local
append-only JSONL log and, best-effort, to the State Hub progress API. Records
are scrubbed of anything that looks like a secret value before they are written.
"""
from __future__ import annotations
import json
import os
import urllib.error
import urllib.request
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from secrets_engine.errors import DecisionError, SecretsEngineError
from secrets_engine.redact import looks_secret, redact_text
# Keys that must never carry a value into evidence regardless of nesting.
_FORBIDDEN_VALUE_KEYS = {"value", "secret", "token", "password", "raw"}
def _scrub(obj: Any) -> Any:
"""Recursively drop secret-looking keys and redact token shapes in strings."""
if isinstance(obj, dict):
out = {}
for k, v in obj.items():
if k.lower() in _FORBIDDEN_VALUE_KEYS or looks_secret(k):
out[k] = "<omitted: non-secret evidence only>"
else:
out[k] = _scrub(v)
return out
if isinstance(obj, list):
return [_scrub(v) for v in obj]
if isinstance(obj, str):
return redact_text(obj)
return obj
@dataclass
class EvidenceWriter:
evidence_dir: Path
hub_url: str = ""
topic_id: str = ""
workstream_id: str = ""
author: str = "secrets-engine"
actor: str = field(default_factory=lambda: os.environ.get("USER", "unknown"))
def __post_init__(self) -> None:
self.evidence_dir = Path(self.evidence_dir)
def _log_path(self) -> Path:
self.evidence_dir.mkdir(parents=True, exist_ok=True)
day = datetime.now(timezone.utc).strftime("%Y-%m-%d")
return self.evidence_dir / f"evidence-{day}.jsonl"
def _append_local(self, record: dict[str, Any]) -> None:
"""Append one record before any best-effort external delivery."""
path = self._log_path()
with path.open("a", encoding="utf-8") as fh:
fh.write(json.dumps(record, sort_keys=True) + "\n")
def record(
self,
action: str,
*,
result: str,
catalog_id: str = "",
stage: str = "",
decision_id: str = "",
detail: dict[str, Any] | None = None,
hub: bool = True,
) -> dict[str, Any]:
"""Append one non-secret evidence record. Returns the stored record."""
record_id = str(uuid.uuid4())
hub_requested = bool(hub and self.hub_url)
record = {
"record_id": record_id,
"ts": datetime.now(timezone.utc).isoformat(),
"action": action,
"result": result,
"actor": self.actor,
"catalog_id": catalog_id,
"stage": stage,
"decision_id": decision_id,
"detail": _scrub(detail or {}),
"hub_delivery_requested": hub_requested,
}
self._append_local(record)
if hub_requested:
delivery_result = self._post_hub(
action, result, catalog_id, stage, decision_id
)
# Append-only companion evidence makes an unavailable State Hub
# visible without rewriting or delaying the primary local record.
self._append_local(
{
"record_id": str(uuid.uuid4()),
"related_record_id": record_id,
"ts": datetime.now(timezone.utc).isoformat(),
"action": "evidence-delivery",
"result": delivery_result,
"actor": self.actor,
"catalog_id": catalog_id,
"stage": stage,
"decision_id": decision_id,
"detail": {},
"hub_delivery_requested": False,
}
)
return record
def _post_hub(
self, action: str, result: str, catalog_id: str, stage: str, decision_id: str
) -> str:
"""Best-effort progress note; return a non-secret delivery outcome."""
if not self.topic_id:
return "skipped-no-topic"
summary = f"secrets-engine {action}: {result}"
if catalog_id:
summary += f" [{catalog_id}{'/' + stage if stage else ''}]"
payload: dict[str, Any] = {
"topic_id": self.topic_id,
"event_type": "note",
"summary": summary,
"author": self.author,
}
if self.workstream_id:
payload["workplan_id"] = self.workstream_id
if decision_id:
payload["detail"] = {"decision_id": decision_id, "catalog_id": catalog_id}
try:
req = urllib.request.Request(
self.hub_url.rstrip("/") + "/progress/",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req, timeout=3).read()
return "delivered"
except (urllib.error.URLError, OSError, ValueError):
# Hub being offline must never block secret work or leak anything.
return "failed"
@dataclass
class PrivilegedActionEvidence:
"""Record one privileged command's attempt, approval, and terminal result."""
writer: EvidenceWriter
action: str
catalog_id: str
stage: str
decision_ref: str = ""
approval_required: bool = False
detail: dict[str, Any] = field(default_factory=dict)
decision_id: str = ""
approval_status: str = "pending"
completed: bool = False
def __post_init__(self) -> None:
if not self.approval_required:
self.approval_status = "not-required"
def _detail(self, extra: dict[str, Any] | None = None) -> dict[str, Any]:
merged = dict(self.detail)
merged.update(
{
"approval_status": self.approval_status,
"decision_ref": self.decision_ref,
}
)
if extra:
merged.update(extra)
return merged
def __enter__(self) -> "PrivilegedActionEvidence":
self.writer.record(
self.action,
result="attempt",
catalog_id=self.catalog_id,
stage=self.stage,
detail=self._detail(),
)
return self
def mark_approved(self, decision: object | None) -> None:
if decision is None:
self.approval_status = "not-required"
return
self.decision_id = str(getattr(decision, "id", ""))
self.approval_status = "approved"
def finish(
self, result: str, *, detail: dict[str, Any] | None = None
) -> dict[str, Any]:
self.completed = True
return self.writer.record(
self.action,
result=result,
catalog_id=self.catalog_id,
stage=self.stage,
decision_id=self.decision_id,
detail=self._detail(detail),
)
def __exit__(self, exc_type, exc, _traceback) -> bool:
if exc_type is None:
if not self.completed:
self.finish("failed-incomplete")
return False
if isinstance(exc, DecisionError):
self.approval_status = "rejected"
if isinstance(exc, (KeyboardInterrupt, SystemExit)):
result = "interrupted"
elif isinstance(exc, SecretsEngineError):
result = f"failed-{type(exc).__name__}"
else:
result = "failed-unexpected"
self.writer.record(
self.action,
result=result,
catalog_id=self.catalog_id,
stage=self.stage,
decision_id=self.decision_id,
detail=self._detail({"error_type": type(exc).__name__}),
)
return False