Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2eab40a

Browse files
RafaelGSSjuanarbol
authored andcommittedOct 7, 2022
doc: move policy docs to the permissions scope
PR-URL: #44222 Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 299fcf3 commit 2eab40a

File tree

5 files changed

+456
-421
lines changed

5 files changed

+456
-421
lines changed
 

‎doc/api/errors.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3529,7 +3529,7 @@ The native call from `process.cpuUsage` could not be processed.
35293529
[domains]: domain.md
35303530
[event emitter-based]: events.md#class-eventemitter
35313531
[file descriptors]: https://en.wikipedia.org/wiki/File_descriptor
3532-
[policy]: policy.md
3532+
[policy]: permissions.md#policies
35333533
[self-reference a package using its name]: packages.md#self-referencing-a-package-using-its-name
35343534
[stream-based]: stream.md
35353535
[syscall]: https://man7.org/linux/man-pages/man2/syscalls.2.html

‎doc/api/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* [OS](os.md)
4646
* [Path](path.md)
4747
* [Performance hooks](perf_hooks.md)
48-
* [Policies](policy.md)
48+
* [Permissions](permissions.md)
4949
* [Process](process.md)
5050
* [Punycode](punycode.md)
5151
* [Query strings](querystring.md)

‎doc/api/permissions.md

+446
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,446 @@
1+
# Permissions
2+
3+
Permissions can be used to control what system resources the
4+
Node.js process has access to or what actions the process can take
5+
with those resources. Permissions can also control what modules can
6+
be 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+
13+
If 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+
28+
Node.js contains experimental support for creating policies on loading code.
29+
30+
Policies are a security feature intended to allow guarantees
31+
about what code Node.js is able to load. The use of policies assumes
32+
safe practices for the policy files such as ensuring that policy
33+
files cannot be overwritten by the Node.js application by using
34+
file permissions.
35+
36+
A best practice would be to ensure that the policy manifest is read-only for
37+
the running Node.js application and that the file cannot be changed
38+
by the running Node.js application in any way. A typical setup would be to
39+
create the policy file as a different user id than the one running Node.js
40+
and granting read permissions to the user id running Node.js.
41+
42+
#### Enabling
43+
44+
<!-- type=misc -->
45+
46+
The `--experimental-policy` flag can be used to enable features for policies
47+
when loading modules.
48+
49+
Once this has been set, all modules must conform to a policy manifest file
50+
passed to the flag:
51+
52+
```bash
53+
node --experimental-policy=policy.json app.js
54+
```
55+
56+
The policy manifest will be used to enforce constraints on code loaded by
57+
Node.js.
58+
59+
To mitigate tampering with policy files on disk, an integrity for
60+
the policy file itself may be provided via `--policy-integrity`.
61+
This allows running `node` and asserting the policy file contents
62+
even if the file is changed on disk.
63+
64+
```bash
65+
node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" app.js
66+
```
67+
68+
#### Features
69+
70+
##### Error behavior
71+
72+
When a policy check fails, Node.js by default will throw an error.
73+
It is possible to change the error behavior to one of a few possibilities
74+
by defining an "onerror" field in a policy manifest. The following values are
75+
available 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+
96+
Policy files must use integrity checks with Subresource Integrity strings
97+
compatible with the browser
98+
[integrity attribute](https://www.w3.org/TR/SRI/#the-integrity-attribute)
99+
associated with absolute URLs.
100+
101+
When using `require()` or `import` all resources involved in loading are checked
102+
for integrity if a policy manifest has been specified. If a resource does not
103+
match the integrity listed in the manifest, an error will be thrown.
104+
105+
An 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+
117+
Each resource listed in the policy manifest can be of one the following
118+
formats to determine its location:
119+
120+
1. A [relative-URL string][] to a resource from the manifest such as `./resource.js`, `../resource.js`, or `/resource.js`.
121+
2. A complete URL string to a resource such as `file:///resource.js`.
122+
123+
When loading resources the entire URL must match including search parameters
124+
and hash fragment. `./a.js?b` will not be used when attempting to load
125+
`./a.js` and vice versa.
126+
127+
To 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`
129+
can be used.
130+
131+
Integrity can be specified as the boolean value `true` to accept any
132+
body for the resource which can be useful for local development. It is not
133+
recommended in production since it would allow unexpected alteration of
134+
resources to be considered valid.
135+
136+
##### Dependency redirection
137+
138+
An application may need to ship patched versions of modules or to prevent
139+
modules from allowing all modules access to all other modules. Redirection
140+
can be used by intercepting attempts to load the modules wishing to be
141+
replaced.
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+
157+
The dependencies are keyed by the requested specifier string and have values
158+
of either `true`, `null`, a string pointing to a module to be resolved,
159+
or a conditions object.
160+
161+
The specifier string does not perform any searching and must match exactly what
162+
is provided to the `require()` or `import` except for a canonicalization step.
163+
Therefore, multiple specifiers may be needed in the policy if it uses multiple
164+
different strings to point to the same module (such as excluding the extension).
165+
166+
Specifier strings are canonicalized but not resolved prior to be used for
167+
matching in order to have some compatibility with import maps, for example if a
168+
resource `file:///C:/app/server.js` was given the following redirection from a
169+
policy 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+
183+
Any specifier used to load `file:///C:/app/utils.js` would then be intercepted
184+
and redirected to `file:///C:/app/utils-v2.js` instead regardless of using an
185+
absolute or relative specifier. However, if a specifier that is not an absolute
186+
or relative URL string is used, it would not be intercepted. So, if an import
187+
such as `import('#utils')` was used, it would not be intercepted.
188+
189+
If the value of the redirection is `true`, a "dependencies" field at the top of
190+
the 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+
193+
If the value of the redirection is a string, it is resolved relative to
194+
the manifest and then immediately used without searching.
195+
196+
Any specifier string for which resolution is attempted and that is not listed in
197+
the dependencies results in an error according to the policy.
198+
199+
Redirection does not prevent access to APIs through means such as direct access
200+
to `require.cache` or through `module.constructor` which allow access to
201+
loading modules. Policy redirection only affects specifiers to `require()` and
202+
`import`. Other means, such as to prevent undesired access to APIs through
203+
variables, are necessary to lock down that path of loading modules.
204+
205+
A boolean value of `true` for the dependencies map can be specified to allow a
206+
module to load any specifier without redirection. This can be useful for local
207+
development and may have some valid usage in production, but should be used
208+
only with care after auditing a module to ensure its behavior is valid.
209+
210+
Similar to `"exports"` in `package.json`, dependencies can also be specified to
211+
be objects containing conditions which branch how dependencies are loaded. In
212+
the preceding example, `"http"` is allowed when the `"import"` condition is
213+
part of loading it.
214+
215+
A value of `null` for the resolved value causes the resolution to fail. This
216+
can be used to ensure some kinds of dynamic access are explicitly prevented.
217+
218+
Unknown values for the resolved module location cause failures but are
219+
not guaranteed to be forward compatible.
220+
221+
##### Example: Patched dependency
222+
223+
Redirected dependencies can provide attenuated or modified functionality as fits
224+
the application. For example, log data about timing of function durations by
225+
wrapping the original:
226+
227+
```js
228+
const original = require('fn');
229+
module.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+
243+
Use the `"scopes"` field of a manifest to set configuration for many resources
244+
at once. The `"scopes"` field works by matching resources by their segments.
245+
If a scope or resource includes `"cascade": true`, unknown specifiers will
246+
be searched for in their containing scope. The containing scope for cascading
247+
is found by recursively reducing the resource URL by removing segments for
248+
[special schemes][], keeping trailing `"/"` suffixes, and removing the query and
249+
hash fragment. This leads to the eventual reduction of the URL to its origin.
250+
If the URL is non-special the scope will be located by the URL's origin. If no
251+
scope is found for the origin or in the case of opaque origins, a protocol
252+
string can be used as a scope. If no scope is found for the URL's protocol, a
253+
final empty string `""` scope will be used.
254+
255+
Note, `blob:` URLs adopt their origin from the path they contain, and so a scope
256+
of `"blob:https://nodejs.org"` will have no effect since no URL can have an
257+
origin of `blob:https://nodejs.org`; URLs starting with
258+
`blob:https://nodejs.org/` will use `https://nodejs.org` for its origin and
259+
thus `https:` for its protocol scope. For opaque origin `blob:` URLs they will
260+
have `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+
274+
Given a file located at `file:///C:/app/bin/main.js`, the following scopes would
275+
be checked in order:
276+
277+
1. `"file:///C:/app/bin/"`
278+
279+
This 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
281+
would be skipped. Adding this scope to the policy would cause it to be used
282+
prior to the `"file:///C:/app/"` scope.
283+
284+
2. `"file:///C:/app/"`
285+
286+
This 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
288+
determine the policy for the resource at `file:///C:/app/bin/main.js`. If the
289+
scope has `"cascade": true`, any unsatisfied queries about the resource would
290+
delegate to the next relevant scope for `file:///C:/app/bin/main.js`, `"file:"`.
291+
292+
3. `"file:///C:/"`
293+
294+
This determines the policy for all file based resources within `"file:///C:/"`.
295+
This is not in the `"scopes"` field of the policy and would be skipped. It would
296+
not be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to
297+
cascade or is not in the `"scopes"` of the policy.
298+
299+
4. `"file:///"`
300+
301+
This determines the policy for all file based resources on the `localhost`. This
302+
is not in the `"scopes"` field of the policy and would be skipped. It would not
303+
be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade
304+
or is not in the `"scopes"` of the policy.
305+
306+
5. `"file:"`
307+
308+
This determines the policy for all file based resources. It would not be used
309+
for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade or is not
310+
in the `"scopes"` of the policy.
311+
312+
6. `""`
313+
314+
This 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+
319+
Setting an integrity to `true` on a scope will set the integrity for any
320+
resource not found in the manifest to `true`.
321+
322+
Setting an integrity to `null` on a scope will set the integrity for any
323+
resource not found in the manifest to fail matching.
324+
325+
Not 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
328+
set.
329+
330+
The 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+
344+
The 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+
365+
The 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+
387+
Given 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`.
424+
Policies require this to be opt-in since it enables all resources of the
425+
application cross linkage which doesn't make sense for many scenarios. They also
426+
assume any given scope has access to any scope above its allowed dependencies;
427+
all scopes emulating import maps must set `"cascade": true`.
428+
429+
Import maps only have a single top level scope for their "imports". So for
430+
emulating `"imports"` use the `""` scope. For emulating `"scopes"` use the
431+
`"scopes"` in a similar manner to how `"scopes"` works in import maps.
432+
433+
Caveats: Policies do not use string matching for various finding of scope. They
434+
do URL traversals. This means things like `blob:` and `data:` URLs might not be
435+
entirely interoperable between the two systems. For example import maps can
436+
partially match a `data:` or `blob:` URL by partitioning the URL on a `/`
437+
character, policies intentionally cannot. For `blob:` URLs import map scopes do
438+
not adopt the origin of the `blob:` URL.
439+
440+
Additionally, 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

‎doc/api/policy.md

+2-419
Original file line numberDiff line numberDiff line change
@@ -6,423 +6,6 @@
66

77
> Stability: 1 - Experimental
88
9-
<!-- name=policy -->
9+
The former Policies documentation is now at [Permissions documentation][]
1010

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

‎test/doctool/test-make-doc.mjs

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const linkedHtmls = [...new Set(links)].map((link) => link.match(re)[1])
4545
const expectedJsons = linkedHtmls
4646
.map((name) => name.replace('.html', '.json'));
4747
const expectedDocs = linkedHtmls.concat(expectedJsons);
48+
const renamedDocs = ['policy.json', 'policy.html'];
4849

4950
// Test that all the relative links in the TOC match to the actual documents.
5051
for (const expectedDoc of expectedDocs) {
@@ -54,6 +55,11 @@ for (const expectedDoc of expectedDocs) {
5455
// Test that all the actual documents match to the relative links in the TOC
5556
// and that they are not empty files.
5657
for (const actualDoc of actualDocs) {
58+
// When renaming the documentation, the old url is lost
59+
// Unless the old file is still available pointing to the correct location
60+
// 301 redirects are not yet automated. So keeping the old URL is a
61+
// reasonable workaround.
62+
if (renamedDocs.includes(actualDoc)) continue;
5763
assert.ok(
5864
expectedDocs.includes(actualDoc), `${actualDoc} does not match TOC`);
5965

0 commit comments

Comments
 (0)
Please sign in to comment.