A quarterly compliance gap assessment is the mechanism by which a team discovers that a retention policy was accidentally shortened, a new developer removed a role restriction, or the daily reconciliation job has been silently failing for three weeks. SOX §404 requires not only that controls exist but that management evaluates their effectiveness on an ongoing basis — a quarterly review operationalizes that requirement at the team level. NIST 800-53 AU-6 requires organizations to review and analyze audit records to find indications of inappropriate activity and report findings to designated officials at a defined frequency. FINRA Rule 3110 (Supervision) requires that firms establish and maintain supervisory procedures reasonably designed to achieve compliance — a quarterly assessment is a direct implementation of that supervision requirement.
Info because the compliance gap assessment is a meta-control — its absence does not create a direct vulnerability, but means that degradation of primary controls (retention, access, reconciliation) will go undetected.
Implement a quarterly assessment script in src/jobs/compliance-assessment.ts that checks the three most critical controls and persists results to compliance_assessments:
async function runQuarterlyAssessment() {
const checks = [];
// 1. Retention: oldest log must be ≥7 years old (or policy must state ≥2,555 days)
const oldest = await db('transaction_logs').orderBy('timestamp').first();
const retentionDays = oldest
? (Date.now() - new Date(oldest.timestamp).getTime()) / 86_400_000
: 0;
checks.push({ name: 'retention_7yr', passed: retentionDays >= 2555,
detail: `${Math.floor(retentionDays)} days retained` });
// 2. Access: compliance_role must be the only role with SELECT
const openRoles = await db.raw(
`SELECT grantee FROM information_schema.role_table_grants
WHERE table_name='transaction_logs' AND privilege_type='SELECT'
AND grantee NOT IN ('compliance_role','dba_role')`);
checks.push({ name: 'access_restricted', passed: openRoles.rows.length === 0,
detail: `${openRoles.rows.length} non-compliance roles with SELECT` });
// 3. Reconciliation: zero failed runs in last 90 days
const failures = await db('reconciliation_runs')
.where('status', 'failed')
.where('run_at', '>=', new Date(Date.now() - 90 * 86_400_000))
.count();
checks.push({ name: 'daily_reconciliation', passed: Number(failures) === 0,
detail: `${failures} failed reconciliation runs in last 90 days` });
await db('compliance_assessments').insert({ quarter: getCurrentQuarter(), checks });
if (checks.some(c => !c.passed)) await alertCompliance('Compliance gaps found', checks);
}
cron.schedule('0 9 1 1,4,7,10 *', runQuarterlyAssessment);
ID: finserv-audit-trail.tamper-evidence.compliance-gap-assessment
Severity: info
What to look for: Count all compliance assessment documents, automated check scripts, or scheduled compliance review jobs. Enumerate the control areas reviewed (retention, access, tamper-evidence, reconciliation). Quote the actual schedule or last assessment date found. Verify assessments run at least once every 90 days.
Pass criteria: At least 1 documented quarterly compliance gap assessment process exists that reviews at least 3 audit trail control areas (e.g., retention, access controls, tamper evidence). Assessments run at least once every 90 days. Report the count even on pass (e.g., "1 quarterly assessment script found, reviews 4 control areas, last run 2026-01-15").
Fail criteria: No quarterly assessment process documented (0 assessment records), or assessments run less frequently than every 90 days, or fewer than 3 control areas reviewed.
Skip (N/A) when: Application has been in production for fewer than 3 months (first assessment not yet due) — cite the actual deployment date found.
Detail on fail: "No documented compliance gap assessment — 0 assessment records, 0 scheduled review jobs found.".
Remediation: Implement quarterly compliance checks (in src/jobs/compliance-assessment.ts or docs/compliance/):
// Quarterly compliance check
async function quarterlyComplianceAssessment() {
const assessment = {
quarter: getCurrentQuarter(),
checks: []
};
// 1. Verify retention policy compliance
const oldestLog = await db.transactionLogs.findOne().sort({ timestamp: 1 });
const retentionYears = (new Date() - oldestLog.timestamp) / (1000 * 60 * 60 * 24 * 365);
assessment.checks.push({
name: 'Retention Policy (7 years)',
passed: retentionYears >= 7,
detail: `Currently retaining ${Math.floor(retentionYears)} years`
});
// 2. Verify access controls
const unauthorizedAccesses = await db.auditLogAccess.countDocuments({
role: { $nin: ['compliance', 'audit', 'admin'] },
createdAt: { $gte: threemonthsAgo() }
});
assessment.checks.push({
name: 'Access Controls',
passed: unauthorizedAccesses === 0,
detail: `${unauthorizedAccesses} unauthorized access attempts in quarter`
});
// 3. Verify daily reconciliation runs
const failedReconciliations = await db.reconciliationRuns
.countDocuments({
status: 'failed',
runAt: { $gte: threeMonthsAgo() }
});
assessment.checks.push({
name: 'Daily Reconciliation',
passed: failedReconciliations === 0,
detail: `${failedReconciliations} failed reconciliation runs`
});
// Store and report
await db.complianceAssessments.create(assessment);
if (assessment.checks.some(c => !c.passed)) {
await notificationService.alertCompliance('Compliance gaps found', assessment);
}
}