Consent records are immutable (append-only)
Why it matters
GDPR Article 5(2) requires you to demonstrate accountability for all processing decisions — an audit trail you can retroactively rewrite is not an audit trail. Article 7(1) requires you to demonstrate that consent was given; if the row showing consent was later updated, you have no proof of the original grant. CWE-472 (External Control of Assumed-Immutable Web Parameter) captures the class of vulnerability where data assumed to be authoritative can be silently altered. A consent record that can be UPDATEd can be altered after the fact — by a rogue employee, a compromised service account, or a migration that inadvertently clears historical grants — leaving you unable to prove lawful basis for past sends.
Severity rationale
Critical because mutable consent records allow retroactive alteration of the legal basis for historical sends, making it impossible to demonstrate GDPR Art. 7(1) compliance and exposing the business to fraudulent erasure of withdrawal records.
Remediation
Audit all write paths to the consent table and replace every UPDATE or upsert with an INSERT. Then enforce the constraint at the database level so no future code can accidentally violate it:
-- Option 1: PostgreSQL RULE blocks UPDATE and DELETE at the SQL layer
CREATE RULE consent_no_update AS ON UPDATE TO consent_records DO INSTEAD NOTHING;
CREATE RULE consent_no_delete AS ON DELETE TO consent_records DO INSTEAD NOTHING;
In application code, a consent revocation always inserts a new row with granted = false — it never touches the existing grant row:
// Correct — append a revocation record
await db.consentRecord.create({
data: {
contactId: contact.id,
scope: 'marketing',
granted: false,
source: 'unsubscribe-link',
policyVersion: CURRENT_POLICY_VERSION,
createdAt: new Date()
}
})
Search src/ for any db.consent*.update or UPDATE consent_records and replace them before deploying the database constraint.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Compliance & Consent Engine Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- gdpr · Art. 5(2) — Accountability — controller must demonstrate compliance
- gdpr · Art. 7(1) — Controller must demonstrate consent was given
- cwe · CWE-472 — External control of assumed-immutable data
- iso-27001:2022 · A.8.15 — Logging — log integrity
- nist:rev5 · AU-9 — Protection of audit information
Taxons
History
- 2026-04-18·v1.0.0·Initial import from compliance-consent-engine·automated