PCI-DSS Req 10.3.2 explicitly requires that audit log files be protected to prevent unauthorized modifications. NIST AU-9 requires protection of audit information from unauthorized access, modification, and deletion. An audit log a privileged user or a compromised service account can modify is not a trustworthy audit trail — an attacker who can delete audit events covering their tracks has neutralized your forensic capability entirely. SOC 2 CC7.2 requires integrity controls on security-relevant records. The most common failure mode is an audit_events table created with the same full-CRUD RLS policy as application tables, making its records as mutable as any other data in the system.
Medium because a mutable audit log weakens forensic integrity and fails PCI-DSS Req 10.3.2 and NIST AU-9, but exploitation requires privileged access already compromised.
Restrict the audit_events table to INSERT-only at the database level so no application code path — including a compromised service account — can modify or delete records.
For Supabase, create an INSERT-only RLS policy and explicitly omit UPDATE/DELETE:
-- Allow authenticated users to insert their own audit events
CREATE POLICY "audit_insert_only" ON audit_events
FOR INSERT TO authenticated
WITH CHECK (actor_id = auth.uid());
-- No UPDATE or DELETE policy — Postgres denies by default under RLS
-- Service role inserts via server-side API (bypasses RLS as expected)
Alternatively, route audit events to an external append-only log service (Better Stack Logs, Axiom, AWS CloudWatch) — these are immutable by design and require no additional database policy work.
ID: saas-logging.audit-trail.audit-logs-immutable
Severity: medium
What to look for: Enumerate all relevant files and Examine the audit log implementation (if it exists). Check: does the database schema include UPDATE or DELETE permissions on the audit log table? Does the application code include any update or delete operations on audit log records? Are Row-Level Security policies (for Supabase) or database permissions configured to prevent modification? Look for REVOKE UPDATE ON audit_events FROM app_user patterns in migrations, RLS policies that only allow INSERT, or use of an append-only log service (Axiom, Papertrail, CloudWatch, Better Stack Logs).
Pass criteria: At least 1 conforming pattern must exist. Audit log records cannot be modified or deleted by the application. This is satisfied by: RLS policy allowing only INSERT (not UPDATE/DELETE) on the audit log table, database permission GRANT INSERT ONLY, no UPDATE/DELETE code paths for audit records in the application, OR use of an external append-only log service.
Fail criteria: The audit log table has no write protections — standard RLS is disabled or allows full CRUD, and application code has no explicit prevention of audit record modification.
Skip (N/A) when: No audit logging mechanism exists (if audit-log-sensitive-ops is skipped or failed with "no mechanism found"). Skip result should include a note referencing the failed prerequisite.
Detail on fail: "audit_events table has no INSERT-only RLS policy — records can be modified or deleted by the application service role" or "No immutability controls found on audit log implementation"
Remediation: An audit log someone can modify is not a reliable audit log. Immutability is what makes audit trails trustworthy.
For Supabase, add an RLS policy that prevents update and delete:
-- Allow authenticated users to insert their own audit events
CREATE POLICY "audit_insert_only" ON audit_events
FOR INSERT TO authenticated
WITH CHECK (actor_id = auth.uid());
-- No UPDATE or DELETE policy — deny by default
-- Service role can still write via server-side API
Alternatively, use an external log service (Better Stack Logs, Axiom, AWS CloudWatch) that is append-only by nature — no application code can delete log entries.