Add minimal sensitive-info hygiene plan and projection cause sample diagnostics

This commit is contained in:
2026-03-05 02:21:21 +00:00
parent 2140c5facf
commit f21abd6299
4 changed files with 24 additions and 3 deletions

View File

@@ -0,0 +1,9 @@
# 14) Sensitive Information Hygiene
## Goal
Detect and remove sensitive data exposure from code, config, logs, and payload surfaces.
## Minimal Plan
1. Add a repeatable scan for sensitive patterns across repo and runtime-generated artifacts.
2. Expunge discovered sensitive values and replace with safe placeholders or references.
3. Add guardrails to prevent reintroduction and document the remediation workflow.

View File

@@ -179,15 +179,19 @@ def shadow_compare_session(session: ChatSession, detail_limit: int = 50) -> dict
"ambiguous_reaction_target": 0, "ambiguous_reaction_target": 0,
"payload_normalization_gap": 0, "payload_normalization_gap": 0,
} }
cause_samples = {key: [] for key in cause_counts.keys()}
cause_sample_limit = min(5, max(0, int(detail_limit)))
def _record_detail(message_id: str, issue: str, cause: str, extra: dict | None = None): def _record_detail(message_id: str, issue: str, cause: str, extra: dict | None = None):
if cause in cause_counts: if cause in cause_counts:
cause_counts[cause] += 1 cause_counts[cause] += 1
if len(details) < max(0, int(detail_limit)):
row = {"message_id": message_id, "issue": issue, "cause": cause} row = {"message_id": message_id, "issue": issue, "cause": cause}
if extra: if extra:
row.update(dict(extra)) row.update(dict(extra))
if len(details) < max(0, int(detail_limit)):
details.append(row) details.append(row)
if cause in cause_samples and len(cause_samples[cause]) < cause_sample_limit:
cause_samples[cause].append(row)
for message_id, db_row in db_by_id.items(): for message_id, db_row in db_by_id.items():
projected = projected_by_id.get(message_id) projected = projected_by_id.get(message_id)
@@ -289,5 +293,6 @@ def shadow_compare_session(session: ChatSession, detail_limit: int = 50) -> dict
"mismatch_total": mismatch_total, "mismatch_total": mismatch_total,
"counters": counters, "counters": counters,
"cause_counts": cause_counts, "cause_counts": cause_counts,
"cause_samples": cause_samples,
"details": details, "details": details,
} }

View File

@@ -96,6 +96,12 @@ class EventProjectionShadowTests(TestCase):
) )
compared = shadow_compare_session(self.session, detail_limit=10) compared = shadow_compare_session(self.session, detail_limit=10)
self.assertGreater(compared["counters"]["missing_in_projection"], 0) self.assertGreater(compared["counters"]["missing_in_projection"], 0)
self.assertIn("cause_samples", compared)
self.assertIn("missing_event_write", compared["cause_samples"])
self.assertGreaterEqual(
len(compared["cause_samples"]["missing_event_write"]),
1,
)
def test_management_command_emits_summary(self): def test_management_command_emits_summary(self):
out = StringIO() out = StringIO()

View File

@@ -257,5 +257,6 @@ class EventProjectionShadowAPI(SuperUserRequiredMixin, View):
"ok": True, "ok": True,
"result": compared, "result": compared,
"cause_summary": dict(compared.get("cause_counts") or {}), "cause_summary": dict(compared.get("cause_counts") or {}),
"cause_samples": dict(compared.get("cause_samples") or {}),
} }
) )