$npx -y skills add vibeeval/vibecosystem --skill compliance-patternsGDPR data handling, audit logging, data classification, retention policies, and consent management for regulatory compliance.
| 1 | # Compliance Patterns |
| 2 | |
| 3 | Data governance and regulatory compliance patterns for software systems. |
| 4 | |
| 5 | ## Data Classification |
| 6 | |
| 7 | ```typescript |
| 8 | // Tag every data field with its classification level |
| 9 | enum DataClass { |
| 10 | PUBLIC = 'public', // Marketing content, product info |
| 11 | INTERNAL = 'internal', // Business metrics, employee count |
| 12 | CONFIDENTIAL = 'confidential', // Customer emails, order history |
| 13 | RESTRICTED = 'restricted', // Passwords, SSN, payment cards, health data |
| 14 | } |
| 15 | |
| 16 | // Schema-level classification |
| 17 | interface UserRecord { |
| 18 | id: string // INTERNAL |
| 19 | email: string // CONFIDENTIAL (PII) |
| 20 | displayName: string // CONFIDENTIAL (PII) |
| 21 | passwordHash: string // RESTRICTED |
| 22 | dateOfBirth: string // RESTRICTED (sensitive PII) |
| 23 | preferences: object // INTERNAL |
| 24 | createdAt: Date // INTERNAL |
| 25 | } |
| 26 | |
| 27 | // Field-level encryption for RESTRICTED data |
| 28 | const ENCRYPTED_FIELDS: Record<string, DataClass> = { |
| 29 | 'user.email': DataClass.CONFIDENTIAL, |
| 30 | 'user.dateOfBirth': DataClass.RESTRICTED, |
| 31 | 'user.ssn': DataClass.RESTRICTED, |
| 32 | 'payment.cardNumber': DataClass.RESTRICTED, |
| 33 | } |
| 34 | |
| 35 | function shouldEncryptAtRest(fieldPath: string): boolean { |
| 36 | const classification = ENCRYPTED_FIELDS[fieldPath] |
| 37 | return classification === DataClass.RESTRICTED |
| 38 | } |
| 39 | |
| 40 | function shouldMaskInLogs(fieldPath: string): boolean { |
| 41 | const classification = ENCRYPTED_FIELDS[fieldPath] |
| 42 | return classification === DataClass.CONFIDENTIAL || classification === DataClass.RESTRICTED |
| 43 | } |
| 44 | ``` |
| 45 | |
| 46 | ## Audit Logging |
| 47 | |
| 48 | ```typescript |
| 49 | interface AuditEvent { |
| 50 | id: string |
| 51 | timestamp: string // ISO 8601 |
| 52 | actor: { |
| 53 | id: string |
| 54 | type: 'user' | 'system' | 'admin' |
| 55 | ip?: string |
| 56 | } |
| 57 | action: string // e.g., 'user.profile.updated', 'order.deleted' |
| 58 | resource: { |
| 59 | type: string |
| 60 | id: string |
| 61 | } |
| 62 | changes?: { |
| 63 | field: string |
| 64 | oldValue: unknown // Masked if RESTRICTED |
| 65 | newValue: unknown // Masked if RESTRICTED |
| 66 | }[] |
| 67 | metadata?: Record<string, unknown> |
| 68 | result: 'success' | 'failure' | 'denied' |
| 69 | reason?: string // For denied/failure |
| 70 | } |
| 71 | |
| 72 | class AuditLogger { |
| 73 | constructor(private store: AuditStore) {} |
| 74 | |
| 75 | async log(event: Omit<AuditEvent, 'id' | 'timestamp'>): Promise<void> { |
| 76 | const auditEvent: AuditEvent = { |
| 77 | ...event, |
| 78 | id: crypto.randomUUID(), |
| 79 | timestamp: new Date().toISOString(), |
| 80 | changes: event.changes?.map(c => ({ |
| 81 | ...c, |
| 82 | oldValue: shouldMaskInLogs(c.field) ? '[REDACTED]' : c.oldValue, |
| 83 | newValue: shouldMaskInLogs(c.field) ? '[REDACTED]' : c.newValue, |
| 84 | })), |
| 85 | } |
| 86 | |
| 87 | // Audit logs are append-only, immutable, tamper-evident |
| 88 | await this.store.append(auditEvent) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | // Middleware: auto-audit all mutations |
| 93 | function auditMiddleware(auditLogger: AuditLogger) { |
| 94 | return async (req: Request, res: Response, next: NextFunction) => { |
| 95 | const originalJson = res.json.bind(res) |
| 96 | |
| 97 | res.json = function(body: any) { |
| 98 | if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) { |
| 99 | auditLogger.log({ |
| 100 | actor: { id: req.user?.id ?? 'anonymous', type: 'user', ip: req.ip }, |
| 101 | action: `${req.method.toLowerCase()}.${req.path}`, |
| 102 | resource: { type: req.path.split('/')[2], id: req.params.id ?? 'N/A' }, |
| 103 | result: res.statusCode < 400 ? 'success' : 'failure', |
| 104 | }).catch(err => console.error('Audit log failed:', err)) |
| 105 | } |
| 106 | return originalJson(body) |
| 107 | } |
| 108 | |
| 109 | next() |
| 110 | } |
| 111 | } |
| 112 | ``` |
| 113 | |
| 114 | ## GDPR Data Subject Rights |
| 115 | |
| 116 | ```typescript |
| 117 | // Right to Access (Article 15): export all user data |
| 118 | async function exportUserData(userId: string): Promise<DataExport> { |
| 119 | const user = await db.user.findUnique({ where: { id: userId } }) |
| 120 | const orders = await db.order.findMany({ where: { userId } }) |
| 121 | const activityLog = await db.activityLog.findMany({ where: { userId } }) |
| 122 | const consents = await db.consent.findMany({ where: { userId } }) |
| 123 | |
| 124 | return { |
| 125 | exportDate: new Date().toISOString(), |
| 126 | subject: { id: userId, email: user?.email }, |
| 127 | personalData: { |
| 128 | profile: sanitizeForExport(user), |
| 129 | orders: orders.map(sanitizeForExport), |
| 130 | activity: activityLog, |
| 131 | consents, |
| 132 | }, |
| 133 | format: 'JSON', |
| 134 | version: '1.0', |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // Right to Erasure (Article 17): delete user data |
| 139 | async function deleteUserData(userId: string): Promise<DeletionReport> { |
| 140 | const report: DeletionReport = { userId, deletedAt: new Date(), items: [] } |
| 141 | |
| 142 | // Soft-delete user profile |
| 143 | await db.user.update({ |
| 144 | where: { id: userId }, |
| 145 | data: { email: `deleted_${userId}@deleted.local`, deletedAt: new Date() } |
| 146 | }) |
| 147 | report.items.push({ type: 'user_profile', action: 'anonymized' }) |
| 148 | |
| 149 | // Hard-delete activity logs |
| 150 | const { count } = await db.activityLog.deleteMany({ where: { userId } }) |
| 151 | report.items.push({ type: 'activity_logs', action: ' |