After a secret was renamed with the prefix that marks an environment variable public and three releases shipped, where does that value now exist?
answer
- published, not merely misconfigured
- count the copies, not the mistake
- caches you will never reach
- rebuild fixes future artifacts only
- replace the credential at its issuer
basics
~20 sIn every client artifact built since the rename, in the HTML those builds served, in published sourcemaps, and in CDN and browser caches. Removing the prefix fixes only future builds; treat the value as disclosed.
solid answer
~40 sMarking a name public tells the build to copy the value into downloadable files, so the question is not whether it leaked but how many copies exist. Each affected build inlined it as a literal into one or more client chunks; those chunks sat behind long-lived cache headers on a CDN and in visitors' browsers, and any published sourcemap carries it too. If server-rendered HTML embedded the value, every cached document holds it as well. Renaming the variable back and rebuilding stops *new* artifacts from containing it — it recalls nothing already served, and you cannot know who fetched it. The remediation that matters is at the issuer: the credential is replaced so the disclosed string stops being an authenticator. Then add the guard that would have caught the rename.
go deeper
Learn the one-way property: once a value is copied into a downloadable file, you cannot take it back. Marking a name public is a publishing decision, so treat it as one.
Be able to list where copies end up — client chunks, sourcemaps, rendered HTML, CDN and browser caches — and explain why a rebuild only changes what ships next.
Run the response: replace the credential at its issuer, check its usage logs, then add the post-build scan and the review rule. Judge severity by what possession of the string grants, not by how obscure the read was.
Attack the incentive. Developers publish values to unblock a broken client read, so make the private read fail loudly with guidance, and make moving the call to the server the easy path.
## Why this is a disclosure and not a bug The naming rule is an instruction, not a judgement. When a name carries the public marker, the build does exactly what it was told: it copies the value into files intended for anonymous download. There is no second check that asks whether the value looks like a credential. So the moment the rename shipped, the value moved from "held by two processes you control" to "held by every client that loaded the app, plus every cache in between". The usual way this happens is not malice or ignorance about secrecy. It is a debugging move: a feature broke because client code read an unmarked name and got nothing, and the fastest way to make it work again was to add the prefix. ## Where the copies are - **The emitted client chunks** of every build since the rename, each holding the value as a literal at every read site. - **Any published sourcemap**, which preserves the pre-minification text. - **The served HTML**, if the value was rendered into the document or into a payload the page carries. - **CDN caches**, holding content-hashed assets under deliberately long lifetimes. - **Browser caches** on machines you have no relationship with — the copy you can never reach. - **Anything that archives pages**: crawlers, error-reporting tools that capture source, corporate proxies, offline mirrors. The last three are why "we took the deployment down" is not a remediation. ## What each candidate action actually achieves | Action | What it achieves | |---|---| | Remove the prefix and rebuild | Future artifacts no longer contain the value | | Purge the CDN and redeploy | Stops new fetches of the old asset from the edge; copies already fetched remain | | Delete the old build output | Housekeeping; changes nothing already downloaded | | Rewrite history in the repository | Irrelevant — the value never needed to be in the repository to be exposed | | Replace the credential at its issuer | Ends the disclosure, because the leaked string stops authenticating | | Review the issuer's logs for use of that credential | Tells you whether the disclosure was exercised, and by whom | Only the last two change the security position. The rest change your artifacts. ## Judging severity honestly Not every publicly marked value is an incident, and treating them all as one burns credibility: - **Designed-public identifiers** — a public API base URL, a client-side analytics or map key that the vendor expects to be visible — belong on the public side. Their protection is a restriction at the issuer (allowed origins, referrer rules, scope, quota), not concealment. - **Bearer credentials** — anything that grants access by mere possession — are an incident the instant they are inlined, regardless of how obscure the read site was. - **Values that are not credentials but reveal structure** — internal hostnames, queue names, a staging admin path — are an information-disclosure finding: worth fixing, rarely an emergency. The test is possession: if holding the string is sufficient to act, it must never have the public marker. ## The guards that would have caught it 1. **A build-time check on the names themselves.** Fail the build when a name matching credential-shaped patterns also carries the public marker, and when a public name's value matches known credential formats. 2. **A scan of the emitted output.** After the build, search the client chunks for the values of names classified private. This catches both the rename and the subtler case where server code copied a private value into something the client receives. 3. **A review rule with teeth.** Adding or renaming a public-marked variable is a diff that a human must approve — it is a one-line change that publishes data, so it deserves the scrutiny a schema migration gets. 4. **Make the failing path loud instead of tempting.** If client code reading an unmarked name fails the build with a message explaining the split, the developer under time pressure is pushed toward moving the *call* to the server rather than moving the *value* to the client. ## The follow-through After the credential is replaced, ask why client code needed it at all. Almost always the answer is that a request was being made from the browser that should have been made by the server on the browser's behalf, with the credential attached there and never handed out. Fixing the placement removes the pressure that produced the rename; fixing only the name leaves it.
- The team purged the CDN cache and redeployed within an hour. Why is that not sufficient?Purging stops the edge from serving the old asset again, but every copy already fetched stays where it landed — browser caches, proxies, crawlers, error-reporting capture. You cannot enumerate who fetched it during that hour, and a bearer credential only needs one reader. The disclosure ends when the credential is replaced at its issuer, not when the file stops being served.
- How do you decide whether an inlined value is an incident or an acceptable public identifier?Apply the possession test: if holding the string is enough to act on a system, it is a credential and its exposure is an incident. If the value is an identifier whose issuer constrains use by origin, referrer, scope or quota, it was designed to be visible and belongs on the public side. Obscurity is never the deciding factor.
- What check would have caught this before the first release, and where does it belong?A post-build scan of the emitted client files for the values of names classified private, run in the pipeline and failing the build on a hit. It is stronger than reviewing names alone because it also catches a private value copied into client-bound data by server code. Pair it with human review on any diff that adds or renames a public-marked variable.
saying these in an interview costs you the question
- Says removing the prefix and redeploying resolves the exposure
- Treats purging the CDN cache as containment
- Focuses on repository history for a value that shipped in a bundle
- Assumes an obscure read site limits who can find the value
- Calls every publicly marked value an incident regardless of what it grants
- Skips asking why client code needed the credential at all