Audit logs that are not scoped by tenant become a cross-tenant intelligence source: a tenant admin can query another organization's activity history, exposing who is active, what features they use, and what actions their users take. CWE-285 and NIST AU-9 both address this; SOC 2 CC7.2 requires that audit records be protected from unauthorized access. Beyond privacy, unscoped audit logs mean a legitimate tenant admin cannot be confident that the logs they see reflect only their organization's actions.
Low because exploiting unscoped audit logs requires tenant-admin privileges, limiting the attacker surface, but exposure of another tenant's activity trail is a direct SOC 2 CC7.2 control failure.
Add organizationId as a required field on every audit log write and as a mandatory filter on every read:
// Write — include tenant context on every event
await db.auditLog.create({
data: {
organizationId: session.user.organizationId,
userId: session.user.id,
action: 'document.deleted',
resourceId: docId,
createdAt: new Date()
}
})
// Read — always scope to the requesting tenant
const logs = await db.auditLog.findMany({
where: { organizationId: session.user.organizationId },
orderBy: { createdAt: 'desc' },
take: 100
})
If the audit log table was created without an organizationId column, add it via migration and backfill from the related resource's organizationId before removing the global-read path.
ID: saas-multi-tenancy.tenant-management.audit-logs-tenant-scoped
Severity: low
What to look for: Examine audit log storage and retrieval — activity logs, event logs, admin action logs. Check whether audit log records include a tenant identifier and whether the audit log retrieval API scopes results to the requesting tenant. Look for patterns where audit logs are stored in a central table without tenant isolation, or where a tenant admin can query audit events from other tenants.
Pass criteria: Count all audit log write locations. Audit log records include at least 1 tenant identifier. The audit log retrieval API filters by the requesting user's tenant. A tenant admin can see their own tenant's logs but not other tenants' logs.
Fail criteria: Audit logs are stored without a tenant identifier. The audit log API returns results across all tenants without scoping to the requester's tenant.
Skip (N/A) when: No audit logging system is detected. Signal: no audit_log or activity_log table in the schema, no event logging code for user/admin actions.
Detail on fail: Example: "GET /api/audit-logs in src/app/api/audit-logs/route.ts returns all audit log records with no organizationId filter. Any tenant admin can see activity from all tenants."
Remediation: Add tenant scoping to audit log queries:
const logs = await db.auditLog.findMany({
where: { organizationId: session.user.organizationId },
orderBy: { createdAt: 'desc' },
take: 100
})