Skip to content

Comments

feat: add security agent audit log backend for SOC2 compliance#449

Merged
jeanduplessis merged 2 commits intomainfrom
jdp/security-agent-audit-log
Feb 24, 2026
Merged

feat: add security agent audit log backend for SOC2 compliance#449
jeanduplessis merged 2 commits intomainfrom
jdp/security-agent-audit-log

Conversation

@jeanduplessis
Copy link
Contributor

@jeanduplessis jeanduplessis commented Feb 22, 2026

Summary

  • Add an append-only security_audit_log table with XOR ownership (org or user), SecurityAuditLogAction enum enforced at the DB level via enumCheck, and a fire-and-forget logSecurityAudit service
  • Integrate audit log calls into all security agent mutations (saveConfig, setEnabled, triggerSync, dismissFinding, startAnalysis, deleteFindingsByRepository, autoDismissEligible) in both personal and org routers, plus backend services (auto-dismiss, cron sync, analysis callback)
  • Add tRPC query routers for both personal and org contexts with list (cursor-based pagination, filtering by action/actor/resource/date range/fuzzy search on metadata), getSummary, getActionTypes, and export (CSV/JSON, capped at 10k rows)
  • Update softDeleteUser to anonymize actor PII on org-owned audit rows (user-owned rows cascade-delete via FK), with corresponding test
  • UI component is not included in this PR (backend only)

Actions Logged

Action Resource Type Trigger
security.finding.dismissed security_finding Manual finding dismissal
security.finding.auto_dismissed security_finding Auto-dismiss by triage/sandbox
security.finding.analysis_started security_finding AI analysis triggered
security.finding.analysis_completed security_finding AI analysis finished
security.finding.deleted security_finding Findings deleted by repository
security.config.enabled agent_config Security agent enabled
security.config.disabled agent_config Security agent disabled
security.config.updated agent_config Settings changed (with before/after state)
security.sync.triggered agent_config Manual sync triggered
security.sync.completed agent_config Sync completed (cron)
security.audit_log.exported audit_log Audit log exported

Test Plan

  • Schema enum snapshot test updated for SecurityAuditLogAction
  • GDPR test: verifies softDeleteUser anonymizes actor_email/actor_name on org-owned security audit log rows while preserving actor_id and action
  • pnpm typecheck passes clean

@jeanduplessis jeanduplessis changed the base branch from main to jdp/dependabot-write-back February 22, 2026 20:08
@kiloconnect
Copy link
Contributor

kiloconnect bot commented Feb 22, 2026

Code Review Summary

Status: No New Issues Found | Recommendation: Address existing comments before merge

All significant issues have already been identified by prior reviewers. The existing inline comments cover:

Severity Count
WARNING 4
SUGGESTION 4
Existing Issues (already commented)

WARNING

File Line Issue
src/routers/organizations/organization-security-audit-log-router.ts 204 Kilo admin actor masking missing from export audit log entry
src/routers/security-audit-log-router.ts N/A Personal audit log router missing Kilo admin masking
src/routers/organizations/organization-security-audit-log-router.ts 120 Pagination bug when using after cursor
src/routers/security-audit-log-router.ts 113 Same pagination bug as the org router

SUGGESTION

File Line Issue
src/routers/organizations/organization-security-agent-router.ts 231 before_state/after_state omit SLA fields
src/routers/security-agent-router.ts 211 Same — before_state/after_state omit SLA fields
src/routers/organizations/organization-security-audit-log-router.ts N/A export uses .select() (all columns)
src/routers/security-audit-log-router.ts N/A Same — export uses .select() (all columns)
Review Notes

What looks good

  • GDPR compliance: security_audit_log is already handled in softDeleteUser (src/lib/user.ts) with actor PII anonymization, and tested in src/lib/user.test.ts
  • Fire-and-forget pattern: logSecurityAudit correctly catches errors and reports to Sentry without blocking the main operation
  • Schema design: XOR ownership constraint, enum check constraint, and proper indexing on the security_audit_log table
  • Input validation: Zod schemas properly validate all inputs including datetime strings, enum values, and string length limits
  • LIKE injection prevention: fuzzySearch correctly escapes %, _, and \ for LIKE pattern semantics
  • Admin masking: maskKiloAdminActors properly hides internal admin details from non-admin users in both list and export endpoints
  • rethrowAsPaymentRequired refactor: The try/catch restructuring in startAnalysis is correct — rethrowAsPaymentRequired returns never so TypeScript flow analysis works properly

Minor observations (not blocking)

  • The fuzzySearch ILIKE on metadata::text will perform a sequential scan since JSONB-to-text casts can't use btree indexes. Consider a GIN index with gin_trgm_ops if this becomes a performance concern at scale.
  • CSV export double-quotes all fields which provides basic protection against formula injection in spreadsheets, though a defense-in-depth approach could prefix formula-triggering characters (=, +, -, @) with a tab character.
Files Reviewed (10 files)
  • src/app/api/internal/security-analysis-callback/[findingId]/route.ts - 0 new issues
  • src/db/migrations/0027_unknown_shiva.sql - 0 new issues (migration)
  • src/db/migrations/meta/0027_snapshot.json - 0 new issues (generated)
  • src/db/schema.ts - 0 new issues
  • src/lib/security-agent/core/enums.ts - 0 new issues
  • src/lib/security-agent/services/audit-log-service.ts - 0 new issues
  • src/routers/organizations/organization-security-agent-router.ts - 0 new issues
  • src/routers/organizations/organization-security-audit-log-router.ts - 0 new issues
  • src/routers/security-agent-router.ts - 0 new issues
  • src/routers/security-audit-log-router.ts - 0 new issues

Fix these issues in Kilo Cloud

@jeanduplessis jeanduplessis force-pushed the jdp/security-agent-audit-log branch from 2e6bc85 to 1ee0a7e Compare February 22, 2026 20:19
@jeanduplessis jeanduplessis force-pushed the jdp/security-agent-audit-log branch 2 times, most recently from 081c94e to c02f609 Compare February 22, 2026 20:32
@jeanduplessis jeanduplessis force-pushed the jdp/dependabot-write-back branch from 5b08274 to 8daba2f Compare February 24, 2026 08:58
Base automatically changed from jdp/dependabot-write-back to main February 24, 2026 10:11
@jeanduplessis jeanduplessis force-pushed the jdp/security-agent-audit-log branch 2 times, most recently from d1fee64 to 25e9f63 Compare February 24, 2026 12:22
Add an append-only security_audit_log table with XOR ownership, a
SecurityAuditLogAction enum enforced at the DB level via enumCheck,
and a fire-and-forget logging service. Integrate audit log calls into
all security agent mutations (both personal and org routers) and
backend services (auto-dismiss, sync, analysis callback).

Add tRPC query routers (list, getSummary, getActionTypes, export) for
both personal and org contexts with cursor-based pagination, filtering,
and CSV/JSON export capped at 10k rows.

Update softDeleteUser to anonymize actor PII on org-owned audit rows
(user-owned rows cascade-delete via FK), with corresponding test.
@jeanduplessis jeanduplessis force-pushed the jdp/security-agent-audit-log branch from 25e9f63 to 5ae0484 Compare February 24, 2026 13:09
Fix cursor-based pagination dropping valid items instead of the overflow
sentinel when using the 'after' cursor, by slicing before reversing.

Use explicit column selection in export endpoints to exclude internal
ownership columns (owned_by_organization_id/owned_by_user_id) from
exported CSV/JSON data.
@jeanduplessis jeanduplessis merged commit db7f4d7 into main Feb 24, 2026
12 checks passed
@jeanduplessis jeanduplessis deleted the jdp/security-agent-audit-log branch February 24, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants