A clean repository tells you something real: the secret isn't in the code you scanned. It can't tell you everywhere that secret travels once the application starts running. Most secret-scanning focuses on artifacts — source, commits, PRs, config, container images — and those controls are essential. But a credential can be absent from every one of them and still become exposed at runtime: not because someone made an obvious mistake, but because several reasonable pieces of the system interacted exactly as designed.
What we found
A live third-party API key was appearing in application logs in cleartext. It was not committed to the repository, hardcoded in the application, present in Git history, or exposed through a config artifact. It was supplied to a third-party provider at runtime, in the request format that provider's API expects. On successful calls, nothing unusual happened. But when a request failed, the error handler did something entirely reasonable: it logged the failed request so an engineer could see what went wrong. The request included the full URL — and the full URL included the key.
The code was clean. The runtime wasn't. Every failed call to that provider had the potential to write a valid credential into the logging layer, in cleartext.
Why this is a different class of problem
The path matters, because the risk doesn't live in any single step — each one is a reasonable engineering decision:
No single step looked dangerous. The risk emerged from the composition of them — and that's the part that matters.
The gap between code security and runtime security
A repository scanner can correctly report no secrets found and still miss this — not because it's broken, but because the secret was never in the repository. A static tool analyzes the code it sees. This risk only becomes visible when you follow the runtime path:
The exposure was created by behavior. The system became insecure in motion, not at rest — which is why "our code is clean" and "our secrets are safe" are not the same statement.
This wasn't the first time sensitive values reached logs through normal application behavior. In an earlier finding — The Credential You Didn't Know Was Exposed — cloud access credentials were landing in application logs too. Different credentials, different mechanism, same underlying pattern: sensitive values moving through a legitimate runtime path and entering the logging layer as a side effect of normal behavior. Once you've seen it twice, it stops looking like an isolated mistake and starts looking like a category of risk — one that repository scanning alone doesn't cover.
What the systems reported vs. what was happening
Every system was telling the truth about its own slice. The exposure existed in the relationship between them — how the credential traveled, what happened when the request failed, and what the application recorded afterward.
What this means for you
A clean codebase is necessary. It isn't sufficient. "Did we expose a secret in our code?" and "where can this secret end up while the system is running?" are different questions — and the second requires following the behavior of the system. Worth asking, separately from any repo scan:
- Where do credentials travel at runtime?
- Can URLs, headers, query params, or request objects be captured by error handling?
- What exactly gets logged when a request fails — and are sensitive fields redacted before logs leave the application?
- Where do those logs go afterward, and who and what can access them?
The shift is simple: don't only inspect where secrets are stored — inspect where they travel. A secret can be perfectly protected in the repository and still be exposed downstream.
Your code can be clean and your runtime can still be leaking. Security controls tell you whether the things they inspect are safe; engineering reality depends on what happens between those controls, and after the system starts running. That's where this credential was exposed — not in the code, in the behavior.