Skip to content

Commit 4234904

Browse files
committed
policy: implement scopes field
PR-URL: nodejs#34552 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 87c43de commit 4234904

19 files changed

+1154
-339
lines changed

doc/api/policy.md

+89
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,93 @@ module.exports = function fn(...args) {
196196
};
197197
```
198198

199+
### Scopes
200+
201+
Use the `"scopes"` field of a manifest to set configuration for many resources
202+
at once. The `"scopes"` field works by matching resources by their segments.
203+
If a scope or resource includes `"cascade": true` unknown specifiers will
204+
be searched for in their containing scope. The containing scope for cascading
205+
is found by recursively reducing the resource URL by removing segments for
206+
[special schemes][], keeping trailing `"/"` suffixes and removing the query and
207+
hash fragment. This leads to the eventual reduction of the URL to its origin.
208+
If the URL is non-special the scope will be located by the URL's origin. If no
209+
scope is found for the origin or in the case of opaque origins, a protocol
210+
string can be used as a scope.
211+
212+
Note, `blob:` URLs adopt their origin from the path they contain, and so a scope
213+
of `"blob:https://nodejs.org"` will have no effect since no URL can have an
214+
origin of `blob:https://nodejs.org`; URLs starting with
215+
`blob:https://nodejs.org/` will use `https://nodejs.org` for its origin and
216+
thus `https:` for its protocol scope. For opaque origin `blob:` URLs they will
217+
have `blob:` for their protocol scope since they do not adopt origins.
218+
219+
#### Integrity Using Scopes
220+
221+
Setting an integrity to `true` on a scope will set the integrity for any
222+
resource not found in the manifest to `true`.
223+
224+
Setting an integrity to `null` on a scope will set the integrity for any
225+
resource not found in the manifest to fail matching.
226+
227+
Not including an integrity is the same as setting the integrity to `null`.
228+
229+
`"cascade"` for integrity checks will be ignored if `"integrity"` is explicitly
230+
set.
231+
232+
The following example allows loading any file:
233+
234+
```json
235+
{
236+
"scopes": {
237+
"file:": {
238+
"integrity": true
239+
}
240+
}
241+
}
242+
```
243+
244+
#### Dependency Redirection Using Scopes
245+
246+
The following example, would allow access to `fs` for all resources within
247+
`./app/`:
248+
249+
```json
250+
{
251+
"resources": {
252+
"./app/checked.js": {
253+
"cascade": true,
254+
"integrity": true
255+
}
256+
},
257+
"scopes": {
258+
"./app/": {
259+
"dependencies": {
260+
"fs": true
261+
}
262+
}
263+
}
264+
}
265+
```
266+
267+
The following example, would allow access to `fs` for all `data:` resources:
268+
269+
```json
270+
{
271+
"resources": {
272+
"data:text/javascript,import('fs');": {
273+
"cascade": true,
274+
"integrity": true
275+
}
276+
},
277+
"scopes": {
278+
"data:": {
279+
"dependencies": {
280+
"fs": true
281+
}
282+
}
283+
}
284+
}
285+
```
286+
199287
[relative url string]: https://url.spec.whatwg.org/#relative-url-with-fragment-string
288+
[special schemes]: https://url.spec.whatwg.org/#special-scheme

lib/internal/modules/cjs/loader.js

+11-10
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ const {
4747
SafeWeakMap,
4848
String,
4949
StringPrototypeEndsWith,
50-
StringPrototypeIndexOf,
5150
StringPrototypeLastIndexOf,
51+
StringPrototypeIndexOf,
5252
StringPrototypeMatch,
5353
StringPrototypeSlice,
5454
StringPrototypeStartsWith,
@@ -82,8 +82,9 @@ const {
8282
const { getOptionValue } = require('internal/options');
8383
const preserveSymlinks = getOptionValue('--preserve-symlinks');
8484
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
85-
const manifest = getOptionValue('--experimental-policy') ?
86-
require('internal/process/policy').manifest :
85+
// Do not eagerly grab .manifest, it may be in TDZ
86+
const policy = getOptionValue('--experimental-policy') ?
87+
require('internal/process/policy') :
8788
null;
8889
const { compileFunction } = internalBinding('contextify');
8990

@@ -1043,10 +1044,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
10431044
Module.prototype._compile = function(content, filename) {
10441045
let moduleURL;
10451046
let redirects;
1046-
if (manifest) {
1047+
if (policy?.manifest) {
10471048
moduleURL = pathToFileURL(filename);
1048-
redirects = manifest.getRedirector(moduleURL);
1049-
manifest.assertIntegrity(moduleURL, content);
1049+
redirects = policy.manifest.getDependencyMapper(moduleURL);
1050+
policy.manifest.assertIntegrity(moduleURL, content);
10501051
}
10511052

10521053
maybeCacheSourceMap(filename, content, this);
@@ -1115,9 +1116,9 @@ Module._extensions['.js'] = function(module, filename) {
11151116
Module._extensions['.json'] = function(module, filename) {
11161117
const content = fs.readFileSync(filename, 'utf8');
11171118

1118-
if (manifest) {
1119+
if (policy?.manifest) {
11191120
const moduleURL = pathToFileURL(filename);
1120-
manifest.assertIntegrity(moduleURL, content);
1121+
policy.manifest.assertIntegrity(moduleURL, content);
11211122
}
11221123

11231124
try {
@@ -1131,10 +1132,10 @@ Module._extensions['.json'] = function(module, filename) {
11311132

11321133
// Native extension for .node
11331134
Module._extensions['.node'] = function(module, filename) {
1134-
if (manifest) {
1135+
if (policy?.manifest) {
11351136
const content = fs.readFileSync(filename);
11361137
const moduleURL = pathToFileURL(filename);
1137-
manifest.assertIntegrity(moduleURL, content);
1138+
policy.manifest.assertIntegrity(moduleURL, content);
11381139
}
11391140
// Be aware this doesn't use `content`
11401141
return process.dlopen(module, path.toNamespacedPath(filename));

lib/internal/modules/esm/get_source.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict';
22

33
const { getOptionValue } = require('internal/options');
4-
const manifest = getOptionValue('--experimental-policy') ?
5-
require('internal/process/policy').manifest :
4+
// Do not eagerly grab .manifest, it may be in TDZ
5+
const policy = getOptionValue('--experimental-policy') ?
6+
require('internal/process/policy') :
67
null;
78

89
const { Buffer } = require('buffer');
@@ -33,8 +34,8 @@ async function defaultGetSource(url, { format } = {}, defaultGetSource) {
3334
} else {
3435
throw new ERR_INVALID_URL_SCHEME(['file', 'data']);
3536
}
36-
if (manifest) {
37-
manifest.assertIntegrity(parsed, source);
37+
if (policy?.manifest) {
38+
policy.manifest.assertIntegrity(parsed, source);
3839
}
3940
return { source };
4041
}

lib/internal/modules/esm/resolve.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ const {
3030
Stats,
3131
} = require('fs');
3232
const { getOptionValue } = require('internal/options');
33-
const manifest = getOptionValue('--experimental-policy') ?
34-
require('internal/process/policy').manifest :
33+
// Do not eagerly grab .manifest, it may be in TDZ
34+
const policy = getOptionValue('--experimental-policy') ?
35+
require('internal/process/policy') :
3536
null;
3637
const { sep, relative } = require('path');
3738
const preserveSymlinks = getOptionValue('--preserve-symlinks');
@@ -714,8 +715,8 @@ function resolveAsCommonJS(specifier, parentURL) {
714715

715716
function defaultResolve(specifier, context = {}, defaultResolveUnused) {
716717
let { parentURL, conditions } = context;
717-
if (manifest) {
718-
const redirects = manifest.getRedirector(parentURL);
718+
if (parentURL && policy?.manifest) {
719+
const redirects = policy.manifest.getDependencyMapper(parentURL);
719720
if (redirects) {
720721
const { resolve, reaction } = redirects;
721722
const destination = resolve(specifier, new SafeSet(conditions));

lib/internal/modules/package_json_reader.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const { toNamespacedPath } = require('path');
77

88
const cache = new SafeMap();
99

10+
let manifest;
11+
1012
/**
1113
*
1214
* @param {string} jsonPath
@@ -22,10 +24,12 @@ function read(jsonPath) {
2224
const result = { string, containsKeys };
2325
const { getOptionValue } = require('internal/options');
2426
if (string !== undefined) {
25-
const manifest = getOptionValue('--experimental-policy') ?
26-
require('internal/process/policy').manifest :
27-
null;
28-
if (manifest) {
27+
if (manifest === undefined) {
28+
manifest = getOptionValue('--experimental-policy') ?
29+
require('internal/process/policy').manifest :
30+
null;
31+
}
32+
if (manifest !== null) {
2933
const jsonURL = pathToFileURL(jsonPath);
3034
manifest.assertIntegrity(jsonURL, string);
3135
}

0 commit comments

Comments
 (0)