Adds a new "procedural" tree type for linear step-by-step project workflows (domain controller setup, M365 onboarding, VPN config, etc). Includes intake form builder, two-panel step navigation, variable resolution, procedural exports, 3 seed templates, and UI rename from "Trees" to "Flows". Also archives 19 implemented plan docs and creates deferred features backlog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.7 KiB
Phase C: Sensitive Data Redaction — Design Document
Status: Approved — ready for implementation planning Spec:
docs/plans/2026-02-13-EXPORT-IMPROVEMENTS-SPEC.mdsection C1 UI Decision: Simple toggle (Option 1) Branch:feat/export-phase-c
Overview
Server-side regex redaction with a simple checkbox toggle in the export preview modal. No rich editor — keeps the existing textarea. User sees a summary of what was masked and can manually edit the result.
Backend
New File: backend/app/services/redaction_service.py
apply_redaction(session) -> tuple[Session, RedactionSummary]
- Deep-copies the session (original ORM object never mutated)
- Walks
decisionslist andcustom_steps, applies regex replacements to all string fields:answer,notes,command_output,content,action_performed - Also redacts top-level session fields:
scratchpad,outcome_notes,next_steps - Returns the sanitized copy and a summary of what was found
RedactionSummary dataclass:
@dataclass
class RedactionSummary:
ips: int = 0
emails: int = 0
tokens: int = 0
unc_paths: int = 0
Regex Patterns (conservative — false positives > false negatives)
| Pattern | Regex | Replacement |
|---|---|---|
| IPv4 | \b(?:\d{1,3}\.){3}\d{1,3}\b |
[IP REDACTED] |
| IPv6 | \b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\b |
[IP REDACTED] |
\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b |
[EMAIL REDACTED] |
|
| Bearer tokens | Bearer\s+[A-Za-z0-9._-]+ |
[TOKEN REDACTED] |
| API key patterns | Long hex/base64 strings (32+ chars) | [TOKEN REDACTED] |
| UNC paths | \\\\[\w.-]+\\[\w$.-]+ |
[UNC PATH REDACTED] |
Hostname redaction is not included — MSP tickets legitimately reference hostnames.
Schema Change: backend/app/schemas/session.py
Add to SessionExport:
redaction_mode: Literal["none", "mask"] = "none"
Integration Point: backend/app/api/endpoints/sessions.py
Insert at ~line 297 (after session fetch, before format branching):
redaction_summary = None
if export_options.redaction_mode == "mask":
session, redaction_summary = apply_redaction(session)
Export generators receive redaction_summary and append a footer when present:
--- Redacted: 3 IPs, 2 emails, 1 token ---
Response
The redaction summary is returned via an X-Redaction-Summary response header (JSON-encoded) to avoid changing the existing content-based response body.
No Migration Needed
All changes are runtime — no database schema changes.
Frontend
ExportPreviewModal.tsx
New props:
redactionEnabled?: booleanonToggleRedaction?: (enabled: boolean) => voidredactionSummary?: { ips: number; emails: number; tokens: number; unc_paths: number } | null
Add a "Mask Sensitive Data" checkbox next to the existing "Include Summary" checkbox, using the same visual pattern:
<label className="flex items-center gap-2 text-sm text-white/60 cursor-pointer">
<input type="checkbox" checked={redactionEnabled} onChange={...} />
Mask Sensitive Data
</label>
When redactionSummary has matches, show an info line below the toggles in text-blue-400:
Masked: 3 IPs, 2 emails, 1 token
If redaction is on but nothing was found: "No sensitive data detected" in text-white/40.
SessionDetailPage.tsx
- Add
redactionModestate ('none' | 'mask') - Wire into export options object
- Pass toggle callback to
ExportPreviewModal - Same pattern as existing
includeSummarystate
types/session.ts
Add to SessionExport type:
redaction_mode?: 'none' | 'mask'
Testing
Backend: backend/tests/test_psa_export.py — TestPhaseC class
- Test redaction of each pattern type individually (IP, email, bearer token, API key, UNC path)
- Test
redaction_mode="none"leaves content untouched - Test original session object is not mutated (deep copy verification)
- Test redaction summary counts are accurate
- Test redaction across all text fields (
notes,command_output,answer,scratchpad,outcome_notes,next_steps) - Test edge cases: empty strings, no matches, overlapping patterns
Frontend
npm run build validates types. No new component tests needed for a checkbox toggle.
Files to Create/Modify
| Action | File |
|---|---|
| Create | backend/app/services/redaction_service.py |
| Modify | backend/app/schemas/session.py |
| Modify | backend/app/api/endpoints/sessions.py |
| Modify | frontend/src/types/session.ts |
| Modify | frontend/src/components/session/ExportPreviewModal.tsx |
| Modify | frontend/src/pages/SessionDetailPage.tsx |
| Extend | backend/tests/test_psa_export.py |