Reports generated monthly/on-demand with summary and detail
Why it matters
SOX §302 requires that the principal executive and financial officers certify in each quarterly and annual report that the disclosure controls and procedures are effective — and effective controls include the ability to produce a summary of financial activity for the period. NIST 800-53 AU-6 requires that audit records be reviewed and that findings be reported to designated officials; automated monthly reports operationalize this requirement. FINRA Rule 4511 requires that books and records be available on demand. A system that cannot generate a monthly transaction summary report on demand fails both the SOX certification requirement and the FINRA examination readiness requirement. Without summary-plus-detail reports, management has no structured way to spot anomalies across the portfolio, and compliance officers cannot produce required periodic disclosures.
Severity rationale
Low because report generation is a detective control rather than a preventive one — its absence delays anomaly detection but does not itself create an exploitable vulnerability.
Remediation
Add a monthly report endpoint at src/app/api/admin/reports/monthly/route.ts that returns both a summary block and full transaction detail in a single response:
export async function GET(req: Request) {
const { month, year } = Object.fromEntries(new URL(req.url).searchParams);
const start = new Date(`${year}-${month.padStart(2,'0')}-01`);
const end = new Date(start.getFullYear(), start.getMonth() + 1, 0, 23, 59, 59);
const rows = await db('transaction_logs')
.whereBetween('timestamp', [start, end]).orderBy('timestamp');
const summary = rows.reduce((acc, r) => {
acc.total_transactions++;
acc.by_type[r.operation_type] = (acc.by_type[r.operation_type] ?? 0) + 1;
acc.total_amount += Number(r.amount ?? 0);
return acc;
}, { total_transactions: 0, by_type: {} as Record<string,number>, total_amount: 0 });
return Response.json({ summary, transactions: rows });
}
Schedule automated delivery via src/jobs/monthly-report.ts on the first of each month so compliance officers receive the report without manual intervention.
Detection
-
ID:
audit-reports -
Severity:
low -
What to look for: Count all report generation endpoints, scheduled jobs, or UI pages. Enumerate the data fields each report includes and classify as "summary" or "detail" type. Verify reports include at least 3 summary fields (total transactions, by-type counts, total amount) and at least 1 detail section (individual transaction records). Quote the actual report endpoint paths found.
-
Pass criteria: At least 1 audit report can be generated monthly (automated or on-demand) with at least 3 summary fields and transaction-level detail records. Report the count even on pass (e.g., "1 report endpoint at /api/admin/reports/monthly with 4 summary fields + full transaction detail").
-
Fail criteria: No audit report generation capability (0 endpoints), or reports only provide summaries without transaction details (or vice versa), or fewer than 3 summary fields are included.
-
Skip (N/A) when: Audit report generation is verifiably outsourced to a third-party processor (e.g., Stripe reporting, external audit firm — cite the actual integration found).
-
Detail on fail:
"No audit report generation — 0 report endpoints found. Compliance cannot produce monthly reports."or"Report at /api/reports provides 2 summary fields but 0 transaction-level detail records.". -
Remediation: Add report generation (in
src/app/api/admin/reports/monthly/route.ts):app.get('/api/admin/reports/monthly', authenticate, requireRole('compliance'), async (req, res) => { const { month, year } = req.query; const startDate = new Date(`${year}-${String(month).padStart(2, '0')}-01`); const endDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 0); const logs = await db.transactionLogs .find({ timestamp: { $gte: startDate, $lte: endDate } }) .lean(); const summary = { month, year, totalTransactions: logs.length, byType: {}, byStatus: { pass: 0, fail: 0, skip: 0 }, totalAmount: 0 }; logs.forEach(log => { summary.byType[log.operationType] = (summary.byType[log.operationType] || 0) + 1; summary.byStatus[log.result] = (summary.byStatus[log.result] || 0) + 1; if (log.amount) summary.totalAmount += log.amount; }); res.json({ summary, transactions: logs }); });
External references
- nist:rev5 · AU-6 — Audit Record Review, Analysis, and Reporting
- sox · Section 302 — Corporate Responsibility for Financial Reports — periodic disclosure accuracy
- external · FINRA-Rule-4511 — FINRA Rule 4511 — periodic reporting obligations
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-audit-trail·automated