Skip to content

Commit 18b04ab

Browse files
bmeckrichardlau
authored andcommitted
policy: implement scopes field
PR-URL: #34552 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent f6a5999 commit 18b04ab

19 files changed

Lines changed: 1154 additions & 339 deletions

‎doc/api/policy.md‎

Lines changed: 89 additions & 0 deletions
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‎

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ const {
4646
SafeMap,
4747
String,
4848
StringPrototypeEndsWith,
49-
StringPrototypeIndexOf,
5049
StringPrototypeLastIndexOf,
50+
StringPrototypeIndexOf,
5151
StringPrototypeMatch,
5252
StringPrototypeSlice,
5353
StringPrototypeStartsWith,
@@ -81,8 +81,9 @@ const { getOptionValue } = require('internal/options');
8181
constenableSourceMaps=getOptionValue('--enable-source-maps');
8282
constpreserveSymlinks=getOptionValue('--preserve-symlinks');
8383
constpreserveSymlinksMain=getOptionValue('--preserve-symlinks-main');
84-
constmanifest=getOptionValue('--experimental-policy') ?
85-
require('internal/process/policy').manifest :
84+
// Do not eagerly grab .manifest, it may be in TDZ
85+
constpolicy=getOptionValue('--experimental-policy') ?
86+
require('internal/process/policy') :
8687
null;
8788
const{ compileFunction }=internalBinding('contextify');
8889

@@ -1029,10 +1030,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
10291030
Module.prototype._compile=function(content,filename){
10301031
letmoduleURL;
10311032
letredirects;
1032-
if(manifest){
1033+
if(policy?.manifest){
10331034
moduleURL=pathToFileURL(filename);
1034-
redirects=manifest.getRedirector(moduleURL);
1035-
manifest.assertIntegrity(moduleURL,content);
1035+
redirects=policy.manifest.getDependencyMapper(moduleURL);
1036+
policy.manifest.assertIntegrity(moduleURL,content);
10361037
}
10371038

10381039
maybeCacheSourceMap(filename,content,this);
@@ -1101,9 +1102,9 @@ Module._extensions['.js'] = function(module, filename) {
11011102
Module._extensions['.json']=function(module,filename){
11021103
constcontent=fs.readFileSync(filename,'utf8');
11031104

1104-
if(manifest){
1105+
if(policy?.manifest){
11051106
constmoduleURL=pathToFileURL(filename);
1106-
manifest.assertIntegrity(moduleURL,content);
1107+
policy.manifest.assertIntegrity(moduleURL,content);
11071108
}
11081109

11091110
try{
@@ -1117,10 +1118,10 @@ Module._extensions['.json'] = function(module, filename) {
11171118

11181119
// Native extension for .node
11191120
Module._extensions['.node']=function(module,filename){
1120-
if(manifest){
1121+
if(policy?.manifest){
11211122
constcontent=fs.readFileSync(filename);
11221123
constmoduleURL=pathToFileURL(filename);
1123-
manifest.assertIntegrity(moduleURL,content);
1124+
policy.manifest.assertIntegrity(moduleURL,content);
11241125
}
11251126
// Be aware this doesn't use `content`
11261127
returnprocess.dlopen(module,path.toNamespacedPath(filename));

‎lib/internal/modules/esm/get_source.js‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict';
22

33
const{ getOptionValue }=require('internal/options');
4-
constmanifest=getOptionValue('--experimental-policy') ?
5-
require('internal/process/policy').manifest :
4+
// Do not eagerly grab .manifest, it may be in TDZ
5+
constpolicy=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
thrownewERR_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‎

Lines changed: 5 additions & 4 deletions
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-
constmanifest=getOptionValue('--experimental-policy') ?
34-
require('internal/process/policy').manifest :
33+
// Do not eagerly grab .manifest, it may be in TDZ
34+
constpolicy=getOptionValue('--experimental-policy') ?
35+
require('internal/process/policy') :
3536
null;
3637
const{ sep, relative }=require('path');
3738
constpreserveSymlinks=getOptionValue('--preserve-symlinks');
@@ -714,8 +715,8 @@ function resolveAsCommonJS(specifier, parentURL) {
714715

715716
functiondefaultResolve(specifier,context={},defaultResolveUnused){
716717
let{ parentURL, conditions }=context;
717-
if(manifest){
718-
constredirects=manifest.getRedirector(parentURL);
718+
if(parentURL&&policy?.manifest){
719+
constredirects=policy.manifest.getDependencyMapper(parentURL);
719720
if(redirects){
720721
const{ resolve, reaction }=redirects;
721722
constdestination=resolve(specifier,newSafeSet(conditions));

‎lib/internal/modules/package_json_reader.js‎

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

88
constcache=newSafeMap();
99

10+
letmanifest;
11+
1012
/**
1113
*
1214
* @param {string} jsonPath
@@ -22,10 +24,12 @@ function read(jsonPath) {
2224
constresult={ string, containsKeys };
2325
const{ getOptionValue }=require('internal/options');
2426
if(string!==undefined){
25-
constmanifest=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
constjsonURL=pathToFileURL(jsonPath);
3034
manifest.assertIntegrity(jsonURL,string);
3135
}

0 commit comments

Comments
 (0)