# Reports generated monthly/on-demand with summary and detail

- **Pattern:** `ab-001390` (`finserv-audit-trail.retention-compliance.audit-reports`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001390
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001390)

## 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:

```ts
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`):

  ```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 AU-6
- sox Section 302
- external FINRA-Rule-4511 — https://www.finra.org/rules-guidance/rulebook/finra-rules/4511

Taxons: regulatory-conformance, observability

HTML version: https://auditbuffet.com/patterns/ab-001390
