What Is a Channel?
A channel is a distinct path through which secrets can leave your Node.js process. envtrap monitors five channels, each independently configurable. Each channel is set to one of three modes inenvtrap.json:
network — Outbound HTTP/HTTPS
Default mode: "block"
envtrap starts a local MITM TLS proxy on 127.0.0.1 at a random port and routes all child process HTTP/HTTPS traffic through it. This allows envtrap to decrypt, inspect, and optionally block outbound requests.
What is scanned:
- Request URL/path
- All request headers
- Request body (up to 1 MB)
- Response data (scanned inline, upstream connection dropped on block)
- Plain HTTP:
403 Forbiddenresponse - HTTPS CONNECT: socket is destroyed, client receives connection error
stdout — Standard Output
Default mode: "warn"
envtrap reads every chunk written to process.stdout by the child process (the child is spawned with stdio: ['inherit', 'pipe', 'pipe']) and scans it for active secrets.
warn mode behaviour:
- Secrets are redacted in the output:
Bearer [REDACTED: SHA256:f23831a9] - A leak alert is printed to envtrap’s own stderr
- The child process continues running normally
block mode behaviour:
- Child process receives
SIGTERMimmediately - No further stdout output is forwarded
exclusions.paths, the hooks.mjs preRedact function replaces the secret with [REDACTED: PATH_EXCLUDED] inside the child, so the parent scanner sees only the sanitised value and emits no alert.
stderr — Standard Error
Default mode: "warn"
Identical behaviour to the stdout channel, but for process.stderr. envtrap reads stderr line by line, filtering internal envtrap protocol messages (prefixed with [envtrap]) from normal application error output.
Internal messages like [envtrap] Child process leak: secret "KEY" passed to: COMMAND are parsed from the child’s stderr stream and used to trigger child_process channel alerts in the parent.
child_process — Subprocess Spawning
Default mode: "warn"
envtrap intercepts all Node.js subprocess APIs by wrapping the node:child_process module — for both ESM imports and CommonJS require() calls.
Intercepted APIs:
spawn(command, args, options)— checksoptions.envspawnSync(command, args, options)— checksoptions.envexec(command, options, callback)— checksoptions.envexecSync(command, options)— checksoptions.envexecFile(file, args, options, callback)— checksoptions.envexecFileSync(file, args, options)— checksoptions.envfork(modulePath, args, options)— checksoptions.env
options.env is provided, envtrap checks whether any key name in options.env matches a registered secret key AND the corresponding value matches exactly. If both match, the leak is reported.
warn mode: Reports the leak via stderr, subprocess is allowed to proceed.block mode: Throws a synchronous Error before any OS fork — the subprocess never executes.
Path exclusions: Checked via call stack — if the calling file matches exclusions.paths, the check is skipped.
dns — Hostname Resolution
Default mode: "block"
envtrap intercepts all DNS resolution calls by wrapping node:dns, for both ESM and CommonJS.
Intercepted APIs (callback style):
lookup, resolve, resolve4, resolve6, resolveAny, resolveCname, resolveMx, resolveNaptr, resolveNs, resolvePtr, resolveSoa, resolveSrv, resolveTxt
Intercepted APIs (promise style — dns.promises.*):
All of the above, plus lookup
Two detection mechanisms:
- Secret match: If the target hostname contains any registered secret value as a substring, the leak is reported
-
High-entropy subdomain warning: The hostname is split on
.and each label is evaluated — if any label has:- Length ≥
entropy.minLength(default12) - Shannon entropy ≥
entropy.threshold(default3.5)
- Length ≥
warn mode: Logs the detection, allows the DNS lookup to proceed.block mode: Throws a synchronous Error, preventing any DNS packet from being sent.
Path exclusions: Checked via call stack — if the calling file matches exclusions.paths, the entire check is skipped.