[]previous and next page

The secret scan of the build output

The secret scan of the build output in the Configuration with dusk.config.ts guide.

For agentsUpdated

Everything under dist/client is public: the bundles, the prerendered pages, the files copied from public/ and, if you ask for them, the source maps. After the client build and the prerender, and before the manifest and the adapter copy the folder anywhere, the build reads every text file in it (the binary formats such as images and fonts, and any file with a NUL byte, are skipped; symbolic links are not followed) and fails on two things:

  • The value of a server variable, written as a quoted string ("value", 'value', a backtick string, or the escaped form inside a source map). A variable whose name looks like a secret (SECRET, TOKEN, KEY, PASSWORD, DATABASE, COOKIE ...) is checked from 8 characters. Any other server variable is checked from 16 characters, so production or a port never matches. The error is [ENV_SECRET_EXPOSED]; it names the variable and the file, never the value. Variables with a public prefix are meant for the browser and are never a leak. Four kinds of value are left alone because they are public by nature: the deployment id (it ships in the client on purpose, even when it comes from a commit id in the environment), a value that a public variable carries too, an http(s) URL without credentials or query string, and the variables the machine or the CI provider sets (npm_*, GITHUB_*, VERCEL_* ...) unless their name looks like a secret.
  • A credential by its shape: an AWS access key id, a Stripe or Clerk secret key (sk_, rk_), a GitHub token, a Slack token, a Google API key, a webhook signing secret (whsec_), or a private key with its body. The error is [BUILD_SECRET_PATTERN]; it names the file and the rule, never the matched text. A publishable key (pk_), a placeholder such as sk_test_xxxxxxxx and the ...EXAMPLE keys of vendor documentation are not credentials.

The scan has bounds, and a bound fails the build instead of skipping a file. A text file over 64 MiB, or a client output folder with more than 20,000 entries, stops the build with [BUILD_SCAN_LIMIT], which names the file or the count and never a value. A file the scan cannot read is a file it cannot clear: move it out of the client output, or turn the scan off as described below. The bounds are not configurable.

Source maps are off in a production client build: a map carries the original sources. The Vite setting is the opt-in, so build: { sourcemap: true } in vite.config.ts (or the client environment's own build.sourcemap) writes them, and the scan then reads them too.

ts
export default defineConfig({
  // Default is true. false turns the scan off for this app.
  build: { secretScan: false },
});

With secretScan: false a secret in the browser output is your responsibility. Turn it off only for a value the scan cannot tell from a secret, and prefer fixing the cause: read the variable on the server only, give a browser variable a public prefix, or move the file out of public/. The scan is a last check on the output, not a license to put secrets in client code. It reads the variables of the env files and the environment of the build, so run the build with the same variables as production.