Add minimal sensitive-info hygiene plan and projection cause sample diagnostics
This commit is contained in:
9
artifacts/plans/14-sensitive-info-hygiene.md
Normal file
9
artifacts/plans/14-sensitive-info-hygiene.md
Normal 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.
|
||||||
@@ -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
|
||||||
|
row = {"message_id": message_id, "issue": issue, "cause": cause}
|
||||||
|
if extra:
|
||||||
|
row.update(dict(extra))
|
||||||
if len(details) < max(0, int(detail_limit)):
|
if len(details) < max(0, int(detail_limit)):
|
||||||
row = {"message_id": message_id, "issue": issue, "cause": cause}
|
|
||||||
if extra:
|
|
||||||
row.update(dict(extra))
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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 {}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user