CMMC 2.0 AC.L1-3.1.22 (NIST 800-171r2 3.1.22) requires that organizations control information posted or processed on publicly accessible systems. Exposing stack traces, database error messages, internal file paths, or table names in HTTP responses or error pages hands an attacker a reconnaissance map — they learn your ORM, schema structure, file layout, and often your database product from a single failed request. OWASP A05 (Security Misconfiguration) and CWE-209 (Generation of Error Message Containing Sensitive Information) both call this out. In a CMMC assessment, any FCI surfacing in a public error response is a direct finding.
Medium because information disclosure accelerates subsequent attacks but does not itself expose FCI data — it reduces the attacker's effort to reach a critical exploit.
Catch all errors at the API route boundary and return a sanitized message. Log the full error server-side only — the client gets a generic response regardless of what failed:
// app/api/contracts/route.ts
export async function GET(req: Request) {
try {
const session = await getServerSession()
if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const data = await db.contract.findMany({ where: { userId: session.user.id } })
return Response.json(data)
} catch (error) {
console.error('[contracts/GET]', error) // Full error to server logs only
return Response.json({ error: 'An error occurred. Please try again.' }, { status: 500 })
}
}
In app/error.tsx, never render error.message or error.stack — those fields contain internal paths in production Next.js builds. Render a fixed string and a retry button only.
ID: gov-cmmc-level-1.access-control.public-info-control
Severity: medium
CMMC Practice: AC.L1-3.1.22
What to look for: Examine public routes, static pages, error pages, and API responses for data leakage. Check error handling to confirm stack traces, internal file paths, database errors, and schema details are never sent to the client. Look at public API endpoints — do they ever return more data than intended? Check for debug modes that may be active in production. Look for console.log statements in server code that include sensitive data. Review error boundary implementations and 500/404 page designs to ensure they don't expose internal information.
Pass criteria: Count all error response handlers and public pages. Public pages contain no FCI or internal system data. No more than 0 error responses may expose stack traces, database errors, or internal paths. Extract and quote the first error response pattern you find to verify it does not leak internals. Report: "X error handlers examined, 0 expose internal details."
Fail criteria: Error pages or API error responses expose stack traces, internal file paths, database error messages, table names, or schema details. Do NOT pass when error responses show generic messages in the UI but include internal details in the JSON response body.
Cross-reference: For data encryption in transit that protects these responses, see the data-in-transit check in this audit.
Skip (N/A) when: Never — controlling public-facing information is fundamental to CMMC.
Detail on fail: Identify the specific disclosure. Example: "500 errors in app/api/[...]/route.ts return raw error.message including database connection strings. Error boundary in error.tsx renders error.stack in development and production." Keep under 500 characters.
Remediation: Catch all errors in API routes and return only generic messages to the client:
// app/api/contracts/route.ts
export async function GET(req: Request) {
try {
const session = await getServerSession()
if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const data = await db.contract.findMany({ where: { userId: session.user.id } })
return Response.json(data)
} catch (error) {
// Log full error server-side only — never send raw error to client
console.error('[contracts/GET]', error)
return Response.json(
{ error: 'An error occurred. Please try again.' },
{ status: 500 }
)
}
}
For Next.js error pages, avoid rendering error.message or error.stack in production:
// app/error.tsx
'use client'
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
// Log error for monitoring without exposing to end users
// Never render error.message or error.stack in user-facing UI
return (
<div>
<h2>Something went wrong.</h2>
<button onClick={reset}>Try again</button>
</div>
)
}