File attachments were embedded into AI messages without any PII scanning, allowing credentials, tokens, and other sensitive data to be forwarded to AI providers in plaintext. Typed chat messages had the same gap: a user could type a password or API key directly and it would be sent unscanned. Changes: - chat_message (Rust): defence-in-depth scan of all attachment body content (between --- Attached: markers); hard rejects if PII found - detect_pii (Rust): fix return type from pii::PiiDetectionResult (spans/original_text) to db::models::PiiDetectionResult (detections/total_pii_found) to match the TypeScript contract; the LogUpload PII review workflow was receiving undefined for detections - scan_text_for_pii (Rust): new command — scans arbitrary text for PII without creating DB records; used for typed message warnings - Triage/index.tsx: PendingFile now carries logFileId; handleSend gates each text attachment through detectPiiCmd (hard block on PII found); typed message text scanned via scanTextForPiiCmd with a one-time warning — second send of same message proceeds as acknowledgment
4.4 KiB
TICKET: PII Detection Bypass in AI Chat
Branch: feature/attachment-db-storage-recall
Description
Two PII detection bypasses were identified and fixed in the AI triage chat interface.
Bypass 1 — File Attachments (Critical)
When a user attached a file to a chat message, its content was read via readTextFile(), sliced to 8 KB, and embedded directly into the AI message string — bypassing the existing PII pipeline entirely. The message was forwarded to the configured AI provider in plaintext. The audit log recorded the full file content without a SHA-256 hash or redaction marker.
Root cause: handleAttach stored raw file content in React state. handleSend concatenated it into aiMessage with no call to detectPiiCmd or applyRedactionsCmd. The backend chat_message command applied no validation.
Bypass 2 — Typed Chat Messages (High)
Plain typed chat messages were sent to the AI provider without any PII scan. A user typing How secure is my password: abc123!! would have the password forwarded to the AI and persisted in the audit log in plaintext.
Related Fix — Wrong Return Type on detect_pii
detect_pii was serialising pii::PiiDetectionResult (spans, original_text) while the TypeScript interface expected db::models::PiiDetectionResult (detections, total_pii_found). All frontend code reading result.detections received undefined, meaning the LogUpload PII review workflow was silently broken. Fixed as part of this work.
Acceptance Criteria
- Attaching a text file containing PII blocks send with a descriptive error naming the file and PII types
- The user is directed to use Log Analysis to redact before attaching
- Attaching a clean text file proceeds normally
- Attaching an image (binary,
content === null) skips PII scan and proceeds - Typing a message containing PII triggers a one-time warning; sending the same message a second time proceeds (explicit acknowledgment)
- After a successful send the warning state is cleared
- Backend
chat_messageindependently rejects attachment content containing PII, regardless of frontend state - Backend attachment parser catches headers both with and without trailing
--- detectPiiCmdreturnsdetections: PiiSpan[]andtotal_pii_found: numbermatching the TypeScript contract- All Rust and frontend tests pass; zero clippy warnings; tsc clean
Work Implemented
src-tauri/src/commands/analysis.rs
- Fixed
detect_piito returndb::models::PiiDetectionResult(detections,total_pii_found) instead ofpii::PiiDetectionResult(spans,original_text) - Added
scan_text_for_pii(text: String)command: scans arbitrary text for PII without creating DB records
src-tauri/src/commands/ai.rs
- Added defence-in-depth PII scan inside
chat_messagebefore user message is appended to the AI request - Extracts body content from all
--- Attached:blocks; catches headers with and without trailing--- - Returns a hard error if PII spans are found in attachment content
src-tauri/src/lib.rs
- Registered
scan_text_for_piiingenerate_handler![]
src/lib/tauriCommands.ts
- Added
scanTextForPiiCmd(text: string)wrapper
src/pages/Triage/index.tsx
- Updated
PendingFiletype: addedlogFileId: string - Stored
logFile.idwhen attaching files - Added attachment PII gate in
handleSend: callsdetectPiiCmdon each text attachment; hard-blocks if PII found - Added message PII warning in
handleSend: callsscanTextForPiiCmdon typed message; warns once; second send with same message proceeds - Added
piiWarnedMessageRefto track prior warning state; cleared infinallyafter every send attempt
Testing Needed
- Attach a file containing
password: secret123→ send is blocked; error names the file and PII type - Attach a clean text file → send proceeds
- Attach an image (.png) → no PII scan, send proceeds
- Type
My password is abc123!!in chat → first send shows PII warning - Send the same message again → proceeds (acknowledgment)
- Send a different message → prior warning cleared, sends normally
- On LogUpload page, upload a file with a known IP/email → confirm PII spans appear in the review UI (was previously broken due to wrong struct)
- Directly call
chat_messageIPC with a message containing--- Attached: test ---\npassword: secret→ backend returns error cargo test→ 228/228 pass;npm run test:run→ 103/103 pass