Remove high-risk defaults and tighten data handling across auth, storage, IPC, provider calls, and capabilities so sensitive data is better protected by default. Also update README/wiki security guidance and add targeted tests for the new hardening behaviors. Made-with: Cursor
4.5 KiB
Security Model
Threat Model Summary
TFTSR handles sensitive IT incident data including log files that may contain credentials, PII, and internal infrastructure details. The security model addresses:
- Data at rest — Database encryption
- Data in transit — PII redaction before AI send, TLS for all outbound requests
- Secret storage — API keys in Stronghold vault
- Audit trail — Complete log of every external data transmission
- Least privilege — Minimal Tauri capabilities
Database Encryption (SQLCipher AES-256)
Production builds use SQLCipher:
- Cipher: AES-256-CBC
- KDF: PBKDF2-HMAC-SHA512, 256,000 iterations
- HMAC: HMAC-SHA512
- Page size: 16384 bytes
- Key source:
TFTSR_DB_KEYenvironment variable
Debug builds use plain SQLite (no encryption) for developer convenience.
Release builds now fail startup if TFTSR_DB_KEY is missing or empty.
Credential Encryption
Integration tokens are encrypted with AES-256-GCM before persistence:
- Key source:
TFTSR_ENCRYPTION_KEY(required in release builds) - Key derivation: SHA-256 hash of key material to a fixed 32-byte AES key
- Nonce: Cryptographically secure random nonce per encryption
Release builds fail secure operations if TFTSR_ENCRYPTION_KEY is unset or empty.
The Stronghold plugin remains enabled and now uses a per-installation salt derived from the app data directory path hash instead of a fixed static salt.
PII Redaction
Mandatory path: No text can be sent to an AI provider without going through the PII detection and user-approval flow.
log file → detect_pii() → user approves spans → apply_redactions() → AI provider
- Original text never leaves the machine
- Only the redacted version is transmitted
- The SHA-256 hash of the redacted text is recorded in the audit log for integrity verification
pii_spans.original_valueis cleared after redaction to avoid retaining raw detected secrets in storage- See PII Detection for the full list of detected patterns
Audit Log
Every external data transmission is recorded:
write_audit_event(
&conn,
action, // "ai_send", "publish_to_confluence", etc.
entity_type, // "issue", "document"
entity_id, // UUID of the related record
details, // JSON: provider, model, hashes, log_file_ids
)?;
The audit log is stored in the encrypted SQLite database. It cannot be deleted through the UI.
Tamper Evidence
audit_log entries now include:
prev_hash— hash of the previous audit entryentry_hash— SHA-256 hash of current entry payload +prev_hash
This creates a hash chain and makes post-hoc modification detectable.
Audit entry fields:
action— what was doneentity_type— type of record involvedentity_id— UUID of that recorduser_id— always"local"(single-user app)details— JSON blob with hashes and metadatatimestamp— UTC datetime
Tauri Capabilities (Least Privilege)
Defined in src-tauri/capabilities/default.json:
| Plugin | Permissions granted |
|---|---|
dialog |
allow-open, allow-save |
fs |
read-text, write-text, read, write, mkdir — scoped to app dir and temp |
shell |
allow-open only |
http |
default — connect only to approved origins |
Content Security Policy
default-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: asset: https:;
connect-src 'self'
http://localhost:11434
https://api.openai.com
https://api.anthropic.com
https://api.mistral.ai
https://generativelanguage.googleapis.com;
HTTP is blocked by default. Only whitelisted HTTPS endpoints (and localhost for Ollama) are reachable.
TLS
All outbound HTTP requests use reqwest with certificate verification enabled and a request timeout configured for provider calls.
CI/CD currently uses internal http:// endpoints for self-hosted Gitea release automation on a trusted LAN. Recommended hardening: migrate runners and API calls to HTTPS with internal certificates.
Security Checklist for New Features
- Does it send data externally? → Add audit log entry
- Does it handle user-provided text? → Run PII detection first
- Does it store secrets? → Use Stronghold, not the SQLite DB
- Does it need filesystem access? → Scope the fs capability
- Does it need a new HTTP endpoint? → Add to CSP
connect-src - Does it add a new provider endpoint? → Avoid query-param secrets, use auth headers