When a request fails inside a service, the handler decides what the caller โ and the logs โ get to see. Two contracts are simulated here, drawn as a flat schematic you can drag to pan and scroll to zoom:
Verbose (insecure):
HTTP 500 { stack: "...", db_url: "postgres://...",
query: "SELECT * WHERE id=..." }
โ returned straight to the client
Sanitized (secure):
HTTP 500 { message: "Internal error",
correlationId: "c7f1-9a2e" }
โ full stack/context written server-side only,
keyed by correlationId, for support to look up later
The exposure score approximates a real metric: of all errors raised, what fraction returned internal detail to an untrusted caller?
exposure = secretsLeaked / errorsTriggered ร 100%
- Mode toggle โ switches every future error between the verbose and sanitized contract; requests already in flight finish under the mode they started with.
- Error rate โ probability that a request hitting the Service node fails, independent of the handling mode.
- Force error now โ guarantees the very next request fails, so you can watch one packet through the whole pipeline.
- Verbose payload fields โ which internal fields a leaked (verbose-mode) error actually carries; unticking one still counts as a leak but shrinks the field-exposure tally and the event-log detail, matching how a partially-hardened handler still leaks whatever fields it forgot to strip.
- Correlation ID โ a random token stamped on every sanitized error and written to the centralized Log Store, so support can trace a user's "Internal error" report back to the real stack trace without ever exposing it to them.
This is the OWASP-recommended pattern (A09:2021 โ Security Logging and Monitoring Failures / improper error handling): never let raw exceptions reach an untrusted client, but never lose the diagnostic detail either โ route it to a log store the client can't read.