@@ -61,9 +61,9 @@ The Permission Model has two operational modes:
6161
6262When starting Node.js with ` --permission ` ,
6363the ability to access the file system through the ` fs ` module, access the network,
64- spawn processes, use ` node:worker_threads ` , use native addons, use WASI , use
65- FFI, and enable the runtime inspector will be restricted (the listener for
66- SIGUSR1 won't be created).
64+ access environment variables, spawn processes, use ` node:worker_threads ` , use
65+ native addons, use WASI, use FFI, and enable the runtime inspector will be
66+ restricted (the listener for SIGUSR1 won't be created).
6767
6868``` console
6969$ node --permission index.js
@@ -79,6 +79,8 @@ Error: Access to this API has been restricted
7979Allowing access to spawning a process and creating worker threads can be done
8080using the [ ` --allow-child-process ` ] [ ] and [ ` --allow-worker ` ] [ ] respectively.
8181
82+ To grant access to environment variables, use [ ` --allow-env ` ] [ ] .
83+
8284To allow network access, use [ ` --allow-net ` ] [ ] and for allowing native addons
8385when using permission model, use the [ ` --allow-addons ` ] [ ]
8486flag. For WASI, use the [ ` --allow-wasi ` ] [ ] flag. For FFI, use the
@@ -157,9 +159,9 @@ mode. Execution continues normally.
157159Audit mode is useful for discovering what permissions your application
158160requires before deploying with [ ` --permission ` ] [ ] . It can also be combined
159161with the [ ` --allow-fs-read ` ] [ ] , [ ` --allow-fs-write ` ] [ ] , [ ` --allow-net ` ] [ ] ,
160- [ ` --allow-child-process ` ] [ ] , [ ` --allow-worker ` ] [ ] , [ ` --allow-addons ` ] [ ] ,
161- [ ` --allow-wasi ` ] [ ] , and [ ` --allow-ffi ` ] [ ] flags to audit a subset of
162- permissions while granting others.
162+ [ ` --allow-env ` ] [ ] , [ ` --allow-child-process ` ] [ ] , [ ` --allow-worker ` ] [ ] ,
163+ [ ` --allow-addons ` ] [ ] , [ ` --allow- wasi` ] [ ] , and [ ` --allow-ffi ` ] [ ] flags to audit
164+ a subset of permissions while granting others.
163165
164166When a permission check fails in audit mode, a message is published to the
165167diagnostics channel corresponding to the denied scope. The channel names are:
@@ -172,6 +174,7 @@ diagnostics channel corresponding to the denied scope. The channel names are:
172174* ` node:permission-model:wasi ` — WASI
173175* ` node:permission-model:addon ` — Native Addons
174176* ` node:permission-model:ffi ` — FFI
177+ * ` node:permission-model:env ` — Environment variables
175178
176179Each message is an object with the following properties:
177180
@@ -266,6 +269,98 @@ both to the top-level `node:fs` functions and to the equivalent
266269` FileHandle ` methods, and currently includes ` fsync ` /` fdatasync ` ,
267270` fchmod ` , and ` fchown ` (and their synchronous variants).
268271
272+ #### Environment variable permissions
273+
274+ When the Permission Model is enforced, the process only has access to the
275+ environment variables that [ ` --allow-env ` ] [ ] grants access to.
276+
277+ Instead of checking each access, Node.js removes every other variable from the
278+ process environment at startup, before any JavaScript code runs and before
279+ Node.js starts any other thread. Removed variables are absent from everything
280+ that exposes the environment of the process: ` process.env ` , diagnostic reports,
281+ native code calling ` getenv() ` , worker threads, and the environment inherited by
282+ child processes.
283+
284+ ``` console
285+ $ node --permission --allow-env=PORT --allow-env=APP_* index.js
286+ ```
287+
288+ The valid arguments for the flag are:
289+
290+ * ` * ` - Grants access to every environment variable. Nothing is removed.
291+ * A variable name, such as ` PORT ` .
292+ * A variable name prefix followed by ` * ` , such as ` APP_* ` .
293+
294+ Some variables are always kept:
295+
296+ * The variables that Node.js and its bundled dependencies read after startup,
297+ such as ` NODE_OPTIONS ` , ` NODE_EXTRA_CA_CERTS ` , ` PATH ` , ` HOME ` , ` TMPDIR ` , ` TZ ` ,
298+ ` LANG ` , ` SSL_CERT_FILE ` , and the variables that terminal color detection
299+ reads. Other variables whose names start with ` NODE_ ` , such as
300+ ` NODE_AUTH_TOKEN ` , are not kept.
301+ * The variables defined in the files passed to [ ` --env-file ` ] [ ] and
302+ [ ` --env-file-if-exists ` ] [ ] . If a variable is defined in such a file and also
303+ inherited from the parent process, and ` --allow-env ` does not grant access to
304+ it, the inherited value is removed and the value from the file is used.
305+
306+ ` NODE_ENV ` is not kept either. Node.js does not read it, but many applications
307+ and libraries do, and treat it being unset as a development environment. Grant
308+ access to it explicitly:
309+
310+ ``` console
311+ $ node --permission --allow-env=NODE_ENV index.js
312+ ```
313+
314+ Proxy URLs often contain credentials, so the ` HTTP_PROXY ` , ` HTTPS_PROXY ` , and
315+ ` NO_PROXY ` variables, and their lowercase forms, are not kept. Grant access to
316+ them explicitly when using [ ` --use-env-proxy ` ] [ ] . When ` --use-env-proxy ` is
317+ enabled and any of them were removed at startup, a warning naming them is
318+ emitted.
319+
320+ Reading a variable that was removed at startup returns ` undefined ` , emits a
321+ warning the first time, and publishes a message to the
322+ ` node:permission-model:env ` diagnostics channel.
323+
324+ Variables set at runtime, for example with ` process.env.KEY = 'value' ` or
325+ [ ` process.loadEnvFile() ` ] [ ] , are not restricted, as they cannot reveal what was
326+ removed.
327+
328+ Dropping a variable with [ ` permission.drop() ` ] [ ] removes it from the
329+ environment. Dropping the whole ` env ` scope removes every variable except the
330+ ones Node.js reads itself. This makes it possible to read a secret during
331+ initialization, and then remove it:
332+
333+ ``` js
334+ const databaseUrl = process .env .DATABASE_URL ;
335+ process .permission .drop (' env' , ' DATABASE_URL' );
336+ ```
337+
338+ When a process that enforces the Permission Model spawns a child process, the
339+ child is started with ` --allow-env=* ` : the environment it inherits only contains
340+ variables that the parent had access to. The child can still read its own
341+ ` /proc/<pid>/environ ` on Linux, but not that of any other process, see below.
342+
343+ In audit mode, nothing is removed. Accesses to variables that ` --allow-env `
344+ does not grant access to are published to the ` node:permission-model:env `
345+ diagnostics channel instead.
346+
347+ On Linux, ` /proc/<pid>/environ ` exposes the environment a process was started
348+ with. When the Permission Model is enforced, reading the ` /proc/<pid>/environ `
349+ file of any other process, including the parent process and its ancestors, is
350+ denied regardless of [ ` --allow-fs-read ` ] [ ] . Reading the process's own file is
351+ only allowed with ` --allow-env=* ` . Symbolic links are resolved before the
352+ check, so paths that reach these files indirectly, such as
353+ ` /dev/fd/../environ ` , are denied as well.
354+
355+ In addition, the removed variables are overwritten in the initial environment
356+ block of the process, so that other processes do not find them in its
357+ ` /proc/<pid>/environ ` either. Variables removed later with
358+ [ ` permission.drop() ` ] [ ] are overwritten there as well.
359+
360+ These measures do not change the environment of other processes. A process
361+ granted [ ` --allow-child-process ` ] [ ] can read their environment through other
362+ programs.
363+
269364#### Configuration file support
270365
271366In addition to passing permission flags on the command line, they can also be
@@ -297,6 +392,20 @@ automatically enables the `--permission` flag. Run with:
297392$ node --experimental-default-config-file app.js
298393```
299394
395+ A configuration file, like the ` NODE_OPTIONS ` defined in an [ ` --env-file ` ] [ ]
396+ file, may be controlled by the project being run rather than by whoever starts
397+ Node.js. When the command line or the ` NODE_OPTIONS ` environment variable
398+ enable the Permission Model, the ` allow-env ` values these files define can only
399+ narrow the access that [ ` --allow-env ` ] [ ] grants, and never widen it:
400+
401+ ``` console
402+ $ node --permission --allow-env=APP_* --experimental-config-file=node.config.json app.js
403+ ```
404+
405+ With ` "allow-env": ["*"] ` in ` node.config.json ` , only the variables starting with
406+ ` APP_ ` are kept. With ` "allow-env": ["APP_DATABASE_URL", "OTHER"] ` , only
407+ ` APP_DATABASE_URL ` is.
408+
300409#### Using the Permission Model with ` npx `
301410
302411If you're using [ ` npx ` ] [ ] to execute a Node.js script, you can enable the
@@ -342,6 +451,7 @@ There are constraints you need to know before using this system:
342451* When using the Permission Model the following features will be restricted:
343452 * Native modules
344453 * Network
454+ * Environment variables
345455 * Child process
346456 * Worker Threads
347457 * Inspector protocol
@@ -404,15 +514,21 @@ Developers relying on --permission to sandbox untrusted code should be aware tha
404514[ Security Policy ] : https://github.com/nodejs/node/blob/main/SECURITY.md
405515[ `--allow-addons` ] : cli.md#--allow-addons
406516[ `--allow-child-process` ] : cli.md#--allow-child-process
517+ [ `--allow-env` ] : cli.md#--allow-env
407518[ `--allow-ffi` ] : cli.md#--allow-ffi
408519[ `--allow-fs-read` ] : cli.md#--allow-fs-read
409520[ `--allow-fs-write` ] : cli.md#--allow-fs-write
410521[ `--allow-net` ] : cli.md#--allow-net
411522[ `--allow-openssl-store` ] : cli.md#--allow-openssl-store
412523[ `--allow-wasi` ] : cli.md#--allow-wasi
413524[ `--allow-worker` ] : cli.md#--allow-worker
525+ [ `--env-file-if-exists` ] : cli.md#--env-file-if-existsfile
526+ [ `--env-file` ] : cli.md#--env-filefile
414527[ `--permission-audit` ] : cli.md#--permission-audit
415528[ `--permission` ] : cli.md#--permission
529+ [ `--use-env-proxy` ] : cli.md#--use-env-proxy
416530[ `crypto.createPrivateKey()` ] : crypto.md#cryptocreateprivatekeykey
417531[ `npx` ] : https://docs.npmjs.com/cli/commands/npx
532+ [ `permission.drop()` ] : process.md#processpermissiondropscope-reference
418533[ `permission.has()` ] : process.md#processpermissionhasscope-reference
534+ [ `process.loadEnvFile()` ] : process.md#processloadenvfilepath
0 commit comments