# Thunkle frontend secrets lab — 1.0.0

Eleven synthetic-value cases across three local production builds. This is a teaching experiment and exact-literal matcher, NOT a general secret scanner or security certification.

## Run

Unzip into a disposable directory outside an application. Node >=22.12 required; recorded environment: Node 26.7.0, macOS arm64. Dependencies are pinned and the lockfile is included.

```sh
npm ci --ignore-scripts --no-audit --no-fund
npm test
npm run experiment
```

Installation contacts the npm registry. The experiment has no upload/analytics endpoint. It copies fixtures to a new temporary directory, builds Vite 8.3.0 and Next.js 16.2.9 (explicit webpack, React 19.2.3), and leaves synthetic outputs/logs there for inspection. It uses a small environment allowlist rather than forwarding application credentials. Do not add real credentials to these fixtures, their config, or the canary map.

The printed results path contains file-level observations, versions and fixture SHA-256 hashes. Build file names can differ between runs. Temporary outputs can be removed manually after inspection; the runner never deletes them automatically.

## Surfaces

- Vite `dist` for the default and custom-prefix fixtures.
- Next `.next/static` for browser assets.
- Next `.next/server/app`, only `.html` and `.rsc` files, for prerendered response content. Server JS is excluded.
- Server positive control: `SERVER_CANARY_AVAILABLE` must appear in generated HTML.

### Cases

| Marker | Input/use | Recorded output surface |
| --- | --- | --- |
| vite_public | Referenced VITE_ value | Vite JavaScript |
| vite_private | Unprefixed import.meta.env read | No exact match |
| vite_unused | Unreferenced VITE_ value | No exact match |
| vite_define | Unprefixed value explicitly injected by define | Vite JavaScript |
| vite_custom | Custom envPrefix value | Vite JavaScript |
| next_public | Referenced NEXT_PUBLIC_ value | Client JavaScript and HTML |
| next_private | Unprefixed value rendered by Client Component | HTML only |
| next_config | next.config env injection | Client JavaScript and HTML |
| next_server | Server-only value used for boolean control | No exact match |
| next_serialized | Server value passed as client prop | HTML and RSC |
| next_unused | Unreferenced NEXT_PUBLIC_ value | No exact match |

## Corrected hypothesis, retained evidence

`results-initial-2026-09-18.json` retains the first run, which incorrectly expected `next_private` to be absent from all inspected surfaces. It was absent from client JS but present in HTML. Client Components participate in server prerendering. The confirmation run keeps the same fixtures and uses surface-specific expectations; compare `sourceHashes` between both reports. No newly discovered framework vulnerability is claimed.

## Exit codes

`run-lab.mjs`: 0 = all expected surfaces match, 1 = a measurement differs, 2 = experiment failed. Intentional exposure examples should be found; exit 0 is not an application security pass.

Standalone matcher, no npm dependencies:

```sh
node scan-canaries.mjs ./dist ./canaries.json
```

`scan-canaries.mjs`: 1 = exact synthetic literal found, 0 = no exact match in the selected nonempty file set, 2 = inconclusive/error. Empty/missing directories, symlinks and files over 20 MiB are rejected. The input map only accepts clearly synthetic marker values. Output includes file paths and marker IDs, not file contents. Run against stable local artifacts, not concurrently changing or untrusted filesystems.

## Limitations

Literal matching misses encoded, split, transformed and runtime-only values. No exact match does not prove absence of secrets. Only these versions, fixtures, build modes and artifact surfaces were measured; no hosted builders, source-map variants, customer applications, deployed endpoints or browser behavior were tested. Not a prevalence study or framework comparison score.

## Attribution

Article: https://thunkle.ai/blog/where-frontend-environment-variables-end-up

Free, ungated, MIT licensed. Attribution links are welcome but not required. Cite the versions, date and scope if discussing the results. Do not describe synthetic canaries as stolen credentials or this demonstration as a new vulnerability.
