• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Permissions
2
3Permissions can be used to control what system resources the
4Node.js process has access to or what actions the process can take
5with those resources. Permissions can also control what modules can
6be accessed by other modules.
7
8* [Module-based permissions](#module-based-permissions) control which files
9  or URLs are available to other modules during application execution.
10  This can be used to control what modules can be accessed by third-party
11  dependencies, for example.
12
13If you find a potential security vulnerability, please refer to our
14[Security Policy][].
15
16## Module-based permissions
17
18### Policies
19
20<!--introduced_in=v11.8.0-->
21
22<!-- type=misc -->
23
24> Stability: 1 - Experimental
25
26<!-- name=policy -->
27
28Node.js contains experimental support for creating policies on loading code.
29
30Policies are a security feature intended to allow guarantees
31about what code Node.js is able to load. The use of policies assumes
32safe practices for the policy files such as ensuring that policy
33files cannot be overwritten by the Node.js application by using
34file permissions.
35
36A best practice would be to ensure that the policy manifest is read-only for
37the running Node.js application and that the file cannot be changed
38by the running Node.js application in any way. A typical setup would be to
39create the policy file as a different user id than the one running Node.js
40and granting read permissions to the user id running Node.js.
41
42#### Enabling
43
44<!-- type=misc -->
45
46The `--experimental-policy` flag can be used to enable features for policies
47when loading modules.
48
49Once this has been set, all modules must conform to a policy manifest file
50passed to the flag:
51
52```bash
53node --experimental-policy=policy.json app.js
54```
55
56The policy manifest will be used to enforce constraints on code loaded by
57Node.js.
58
59To mitigate tampering with policy files on disk, an integrity for
60the policy file itself may be provided via `--policy-integrity`.
61This allows running `node` and asserting the policy file contents
62even if the file is changed on disk.
63
64```bash
65node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" app.js
66```
67
68#### Features
69
70##### Error behavior
71
72When a policy check fails, Node.js by default will throw an error.
73It is possible to change the error behavior to one of a few possibilities
74by defining an "onerror" field in a policy manifest. The following values are
75available to change the behavior:
76
77* `"exit"`: will exit the process immediately.
78  No cleanup code will be allowed to run.
79* `"log"`: will log the error at the site of the failure.
80* `"throw"`: will throw a JS error at the site of the failure. This is the
81  default.
82
83```json
84{
85  "onerror": "log",
86  "resources": {
87    "./app/checked.js": {
88      "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0"
89    }
90  }
91}
92```
93
94##### Integrity checks
95
96Policy files must use integrity checks with Subresource Integrity strings
97compatible with the browser
98[integrity attribute](https://www.w3.org/TR/SRI/#the-integrity-attribute)
99associated with absolute URLs.
100
101When using `require()` or `import` all resources involved in loading are checked
102for integrity if a policy manifest has been specified. If a resource does not
103match the integrity listed in the manifest, an error will be thrown.
104
105An example policy file that would allow loading a file `checked.js`:
106
107```json
108{
109  "resources": {
110    "./app/checked.js": {
111      "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0"
112    }
113  }
114}
115```
116
117Each resource listed in the policy manifest can be of one the following
118formats to determine its location:
119
1201. A [relative-URL string][] to a resource from the manifest such as `./resource.js`, `../resource.js`, or `/resource.js`.
1212. A complete URL string to a resource such as `file:///resource.js`.
122
123When loading resources the entire URL must match including search parameters
124and hash fragment. `./a.js?b` will not be used when attempting to load
125`./a.js` and vice versa.
126
127To generate integrity strings, a script such as
128`node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < FILE`
129can be used.
130
131Integrity can be specified as the boolean value `true` to accept any
132body for the resource which can be useful for local development. It is not
133recommended in production since it would allow unexpected alteration of
134resources to be considered valid.
135
136##### Dependency redirection
137
138An application may need to ship patched versions of modules or to prevent
139modules from allowing all modules access to all other modules. Redirection
140can be used by intercepting attempts to load the modules wishing to be
141replaced.
142
143```json
144{
145  "resources": {
146    "./app/checked.js": {
147      "dependencies": {
148        "fs": true,
149        "os": "./app/node_modules/alt-os",
150        "http": { "import": true }
151      }
152    }
153  }
154}
155```
156
157The dependencies are keyed by the requested specifier string and have values
158of either `true`, `null`, a string pointing to a module to be resolved,
159or a conditions object.
160
161The specifier string does not perform any searching and must match exactly what
162is provided to the `require()` or `import` except for a canonicalization step.
163Therefore, multiple specifiers may be needed in the policy if it uses multiple
164different strings to point to the same module (such as excluding the extension).
165
166Specifier strings are canonicalized but not resolved prior to be used for
167matching in order to have some compatibility with import maps, for example if a
168resource `file:///C:/app/server.js` was given the following redirection from a
169policy located at `file:///C:/app/policy.json`:
170
171```json
172{
173  "resources": {
174    "file:///C:/app/utils.js": {
175      "dependencies": {
176        "./utils.js": "./utils-v2.js"
177      }
178    }
179  }
180}
181```
182
183Any specifier used to load `file:///C:/app/utils.js` would then be intercepted
184and redirected to `file:///C:/app/utils-v2.js` instead regardless of using an
185absolute or relative specifier. However, if a specifier that is not an absolute
186or relative URL string is used, it would not be intercepted. So, if an import
187such as `import('#utils')` was used, it would not be intercepted.
188
189If the value of the redirection is `true`, a "dependencies" field at the top of
190the policy file will be used. If that field at the top of the policy file is
191`true` the default node searching algorithms are used to find the module.
192
193If the value of the redirection is a string, it is resolved relative to
194the manifest and then immediately used without searching.
195
196Any specifier string for which resolution is attempted and that is not listed in
197the dependencies results in an error according to the policy.
198
199Redirection does not prevent access to APIs through means such as direct access
200to `require.cache` or through `module.constructor` which allow access to
201loading modules. Policy redirection only affects specifiers to `require()` and
202`import`. Other means, such as to prevent undesired access to APIs through
203variables, are necessary to lock down that path of loading modules.
204
205A boolean value of `true` for the dependencies map can be specified to allow a
206module to load any specifier without redirection. This can be useful for local
207development and may have some valid usage in production, but should be used
208only with care after auditing a module to ensure its behavior is valid.
209
210Similar to `"exports"` in `package.json`, dependencies can also be specified to
211be objects containing conditions which branch how dependencies are loaded. In
212the preceding example, `"http"` is allowed when the `"import"` condition is
213part of loading it.
214
215A value of `null` for the resolved value causes the resolution to fail. This
216can be used to ensure some kinds of dynamic access are explicitly prevented.
217
218Unknown values for the resolved module location cause failures but are
219not guaranteed to be forward compatible.
220
221##### Example: Patched dependency
222
223Redirected dependencies can provide attenuated or modified functionality as fits
224the application. For example, log data about timing of function durations by
225wrapping the original:
226
227```js
228const original = require('fn');
229module.exports = function fn(...args) {
230  console.time();
231  try {
232    return new.target ?
233      Reflect.construct(original, args) :
234      Reflect.apply(original, this, args);
235  } finally {
236    console.timeEnd();
237  }
238};
239```
240
241#### Scopes
242
243Use the `"scopes"` field of a manifest to set configuration for many resources
244at once. The `"scopes"` field works by matching resources by their segments.
245If a scope or resource includes `"cascade": true`, unknown specifiers will
246be searched for in their containing scope. The containing scope for cascading
247is found by recursively reducing the resource URL by removing segments for
248[special schemes][], keeping trailing `"/"` suffixes, and removing the query and
249hash fragment. This leads to the eventual reduction of the URL to its origin.
250If the URL is non-special the scope will be located by the URL's origin. If no
251scope is found for the origin or in the case of opaque origins, a protocol
252string can be used as a scope. If no scope is found for the URL's protocol, a
253final empty string `""` scope will be used.
254
255Note, `blob:` URLs adopt their origin from the path they contain, and so a scope
256of `"blob:https://nodejs.org"` will have no effect since no URL can have an
257origin of `blob:https://nodejs.org`; URLs starting with
258`blob:https://nodejs.org/` will use `https://nodejs.org` for its origin and
259thus `https:` for its protocol scope. For opaque origin `blob:` URLs they will
260have `blob:` for their protocol scope since they do not adopt origins.
261
262##### Example
263
264```json
265{
266  "scopes": {
267    "file:///C:/app/": {},
268    "file:": {},
269    "": {}
270  }
271}
272```
273
274Given a file located at `file:///C:/app/bin/main.js`, the following scopes would
275be checked in order:
276
2771. `"file:///C:/app/bin/"`
278
279This determines the policy for all file based resources within
280`"file:///C:/app/bin/"`. This is not in the `"scopes"` field of the policy and
281would be skipped. Adding this scope to the policy would cause it to be used
282prior to the `"file:///C:/app/"` scope.
283
2842. `"file:///C:/app/"`
285
286This determines the policy for all file based resources within
287`"file:///C:/app/"`. This is in the `"scopes"` field of the policy and it would
288determine the policy for the resource at `file:///C:/app/bin/main.js`. If the
289scope has `"cascade": true`, any unsatisfied queries about the resource would
290delegate to the next relevant scope for `file:///C:/app/bin/main.js`, `"file:"`.
291
2923. `"file:///C:/"`
293
294This determines the policy for all file based resources within `"file:///C:/"`.
295This is not in the `"scopes"` field of the policy and would be skipped. It would
296not be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to
297cascade or is not in the `"scopes"` of the policy.
298
2994. `"file:///"`
300
301This determines the policy for all file based resources on the `localhost`. This
302is not in the `"scopes"` field of the policy and would be skipped. It would not
303be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade
304or is not in the `"scopes"` of the policy.
305
3065. `"file:"`
307
308This determines the policy for all file based resources. It would not be used
309for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade or is not
310in the `"scopes"` of the policy.
311
3126. `""`
313
314This determines the policy for all resources. It would not be used for
315`file:///C:/app/bin/main.js` unless `"file:"` is set to cascade.
316
317##### Integrity using scopes
318
319Setting an integrity to `true` on a scope will set the integrity for any
320resource not found in the manifest to `true`.
321
322Setting an integrity to `null` on a scope will set the integrity for any
323resource not found in the manifest to fail matching.
324
325Not including an integrity is the same as setting the integrity to `null`.
326
327`"cascade"` for integrity checks will be ignored if `"integrity"` is explicitly
328set.
329
330The following example allows loading any file:
331
332```json
333{
334  "scopes": {
335    "file:": {
336      "integrity": true
337    }
338  }
339}
340```
341
342##### Dependency redirection using scopes
343
344The following example, would allow access to `fs` for all resources within
345`./app/`:
346
347```json
348{
349  "resources": {
350    "./app/checked.js": {
351      "cascade": true,
352      "integrity": true
353    }
354  },
355  "scopes": {
356    "./app/": {
357      "dependencies": {
358        "fs": true
359      }
360    }
361  }
362}
363```
364
365The following example, would allow access to `fs` for all `data:` resources:
366
367```json
368{
369  "resources": {
370    "data:text/javascript,import('node:fs');": {
371      "cascade": true,
372      "integrity": true
373    }
374  },
375  "scopes": {
376    "data:": {
377      "dependencies": {
378        "fs": true
379      }
380    }
381  }
382}
383```
384
385##### Example: import maps emulation
386
387Given an import map:
388
389```json
390{
391  "imports": {
392    "react": "./app/node_modules/react/index.js"
393  },
394  "scopes": {
395    "./ssr/": {
396      "react": "./app/node_modules/server-side-react/index.js"
397    }
398  }
399}
400```
401
402```json
403{
404  "dependencies": true,
405  "scopes": {
406    "": {
407      "cascade": true,
408      "dependencies": {
409        "react": "./app/node_modules/react/index.js"
410      }
411    },
412    "./ssr/": {
413      "cascade": true,
414      "dependencies": {
415        "react": "./app/node_modules/server-side-react/index.js"
416      }
417    }
418  }
419}
420```
421
422[Import maps][] assume you can get any resource by default. This means
423`"dependencies"` at the top level of the policy should be set to `true`.
424Policies require this to be opt-in since it enables all resources of the
425application cross linkage which doesn't make sense for many scenarios. They also
426assume any given scope has access to any scope above its allowed dependencies;
427all scopes emulating import maps must set `"cascade": true`.
428
429Import maps only have a single top level scope for their "imports". So for
430emulating `"imports"` use the `""` scope. For emulating `"scopes"` use the
431`"scopes"` in a similar manner to how `"scopes"` works in import maps.
432
433Caveats: Policies do not use string matching for various finding of scope. They
434do URL traversals. This means things like `blob:` and `data:` URLs might not be
435entirely interoperable between the two systems. For example import maps can
436partially match a `data:` or `blob:` URL by partitioning the URL on a `/`
437character, policies intentionally cannot. For `blob:` URLs import map scopes do
438not adopt the origin of the `blob:` URL.
439
440Additionally, import maps only work on `import` so it may be desirable to add a
441`"import"` condition to all dependency mappings.
442
443[Security Policy]: https://github.com/nodejs/node/blob/main/SECURITY.md
444[import maps]: https://url.spec.whatwg.org/#relative-url-with-fragment-string
445[relative-url string]: https://url.spec.whatwg.org/#relative-url-with-fragment-string
446[special schemes]: https://url.spec.whatwg.org/#special-scheme
447