security: await audit writes, add per-turn AssistantPrompt audit (#55)
- Auth.js authorize/signOut: await createAuditEntry on every branch so auth events land in the audit store before the JWT is minted / session closes. Previously these were fire-and-forget and would be dropped under DB load. - Assistant chat: make appendPromptInjectionGuard async and await its own SecurityAlert audit; add auditUserPromptTurn() that records every user message turn as an AssistantPrompt entry containing conversationId, length, SHA-256 fingerprint, pageContext and whether the injection guard fired. Raw prompt text is intentionally not stored — the hash lets a responder correlate a chat transcript with a forensic request without the audit store accumulating a plain-text corpus of everything users typed. - Replace bare crypto.* with explicit node:crypto imports. - Document the retention posture in docs/security-architecture.md §6. Fixes gitea #55.
This commit is contained in:
@@ -131,11 +131,24 @@ injection attempts and to surface them as audit-log entries.
|
||||
|
||||
### Activity History System
|
||||
|
||||
- Centralized `createAuditEntry()` function (fire-and-forget, never blocks)
|
||||
- Centralized `createAuditEntry()` function. Security-critical callers (auth, assistant
|
||||
prompts, admin mutations) `await` the write so the entry is durable before the
|
||||
user-visible effect completes; non-critical callers may fire-and-forget
|
||||
- Covers 29+ of 36 tRPC routers
|
||||
- Logged fields: `entityType`, `entityId`, `action`, `userId`, `changes` (JSONB with before/after/diff), `source`, `summary`
|
||||
- Authentication events: login success/failure, logout, rate limiting, MFA failures
|
||||
|
||||
### Assistant prompt audit
|
||||
|
||||
Each user turn through the AI assistant writes an `AssistantPrompt` audit row
|
||||
with conversation ID, prompt length, SHA-256 fingerprint, current page context,
|
||||
and whether the prompt-injection guard flagged the input. Raw prompt text is
|
||||
**not** retained by default — the hash + length fingerprint is enough for a
|
||||
responder to correlate an audit row with a later forensic export if the user
|
||||
retains their chat transcript, but the audit store itself does not accumulate a
|
||||
plain-text corpus of everything users typed into the assistant. This balances
|
||||
GDPR Art. 30 (records of processing) against data-minimisation.
|
||||
|
||||
### External API Call Logging
|
||||
|
||||
- All OpenAI/Azure/Gemini API calls logged via `loggedAiCall()` wrapper
|
||||
|
||||
Reference in New Issue
Block a user