Skip to content

Commit

Permalink
policy: implement scopes field
Browse files Browse the repository at this point in the history
PR-URL: #34552
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
bmeck authored and richardlau committed Sep 1, 2020
1 parent f6a5999 commit 18b04ab
Show file tree
Hide file tree
Showing 19 changed files with 1,154 additions and 339 deletions.
89 changes: 89 additions & 0 deletions doc/api/policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,93 @@ module.exports = function fn(...args) {
};
```

### Scopes

Use the `"scopes"` field of a manifest to set configuration for many resources
at once. The `"scopes"` field works by matching resources by their segments.
If a scope or resource includes `"cascade": true` unknown specifiers will
be searched for in their containing scope. The containing scope for cascading
is found by recursively reducing the resource URL by removing segments for
[special schemes][], keeping trailing `"/"` suffixes and removing the query and
hash fragment. This leads to the eventual reduction of the URL to its origin.
If the URL is non-special the scope will be located by the URL's origin. If no
scope is found for the origin or in the case of opaque origins, a protocol
string can be used as a scope.

Note, `blob:` URLs adopt their origin from the path they contain, and so a scope
of `"blob:https://nodejs.org"` will have no effect since no URL can have an
origin of `blob:https://nodejs.org`; URLs starting with
`blob:https://nodejs.org/` will use `https://nodejs.org` for its origin and
thus `https:` for its protocol scope. For opaque origin `blob:` URLs they will
have `blob:` for their protocol scope since they do not adopt origins.

#### Integrity Using Scopes

Setting an integrity to `true` on a scope will set the integrity for any
resource not found in the manifest to `true`.

Setting an integrity to `null` on a scope will set the integrity for any
resource not found in the manifest to fail matching.

Not including an integrity is the same as setting the integrity to `null`.

`"cascade"` for integrity checks will be ignored if `"integrity"` is explicitly
set.

The following example allows loading any file:

```json
{
"scopes": {
"file:": {
"integrity": true
}
}
}
```

#### Dependency Redirection Using Scopes

The following example, would allow access to `fs` for all resources within
`./app/`:

```json
{
"resources": {
"./app/checked.js": {
"cascade": true,
"integrity": true
}
},
"scopes": {
"./app/": {
"dependencies": {
"fs": true
}
}
}
}
```

The following example, would allow access to `fs` for all `data:` resources:

```json
{
"resources": {
"data:text/javascript,import('fs');": {
"cascade": true,
"integrity": true
}
},
"scopes": {
"data:": {
"dependencies": {
"fs": true
}
}
}
}
```

[relative url string]: https://url.spec.whatwg.org/#relative-url-with-fragment-string
[special schemes]: https://url.spec.whatwg.org/#special-scheme
21 changes: 11 additions & 10 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ const {
SafeMap,
String,
StringPrototypeEndsWith,
StringPrototypeIndexOf,
StringPrototypeLastIndexOf,
StringPrototypeIndexOf,
StringPrototypeMatch,
StringPrototypeSlice,
StringPrototypeStartsWith,
Expand Down Expand Up @@ -81,8 +81,9 @@ const { getOptionValue } = require('internal/options');
const enableSourceMaps = getOptionValue('--enable-source-maps');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
// Do not eagerly grab .manifest, it may be in TDZ
const policy = getOptionValue('--experimental-policy') ?
require('internal/process/policy') :
null;
const { compileFunction } = internalBinding('contextify');

Expand Down Expand Up @@ -1029,10 +1030,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
Module.prototype._compile = function(content, filename) {
let moduleURL;
let redirects;
if (manifest) {
if (policy?.manifest) {
moduleURL = pathToFileURL(filename);
redirects = manifest.getRedirector(moduleURL);
manifest.assertIntegrity(moduleURL, content);
redirects = policy.manifest.getDependencyMapper(moduleURL);
policy.manifest.assertIntegrity(moduleURL, content);
}

maybeCacheSourceMap(filename, content, this);
Expand Down Expand Up @@ -1101,9 +1102,9 @@ Module._extensions['.js'] = function(module, filename) {
Module._extensions['.json'] = function(module, filename) {
const content = fs.readFileSync(filename, 'utf8');

if (manifest) {
if (policy?.manifest) {
const moduleURL = pathToFileURL(filename);
manifest.assertIntegrity(moduleURL, content);
policy.manifest.assertIntegrity(moduleURL, content);
}

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

// Native extension for .node
Module._extensions['.node'] = function(module, filename) {
if (manifest) {
if (policy?.manifest) {
const content = fs.readFileSync(filename);
const moduleURL = pathToFileURL(filename);
manifest.assertIntegrity(moduleURL, content);
policy.manifest.assertIntegrity(moduleURL, content);
}
// Be aware this doesn't use `content`
return process.dlopen(module, path.toNamespacedPath(filename));
Expand Down
9 changes: 5 additions & 4 deletions lib/internal/modules/esm/get_source.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

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

const { Buffer } = require('buffer');
Expand Down Expand Up @@ -33,8 +34,8 @@ async function defaultGetSource(url, { format } = {}, defaultGetSource) {
} else {
throw new ERR_INVALID_URL_SCHEME(['file', 'data']);
}
if (manifest) {
manifest.assertIntegrity(parsed, source);
if (policy?.manifest) {
policy.manifest.assertIntegrity(parsed, source);
}
return { source };
}
Expand Down
9 changes: 5 additions & 4 deletions lib/internal/modules/esm/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ const {
Stats,
} = require('fs');
const { getOptionValue } = require('internal/options');
const manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
// Do not eagerly grab .manifest, it may be in TDZ
const policy = getOptionValue('--experimental-policy') ?
require('internal/process/policy') :
null;
const { sep, relative } = require('path');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
Expand Down Expand Up @@ -714,8 +715,8 @@ function resolveAsCommonJS(specifier, parentURL) {

function defaultResolve(specifier, context = {}, defaultResolveUnused) {
let { parentURL, conditions } = context;
if (manifest) {
const redirects = manifest.getRedirector(parentURL);
if (parentURL && policy?.manifest) {
const redirects = policy.manifest.getDependencyMapper(parentURL);
if (redirects) {
const { resolve, reaction } = redirects;
const destination = resolve(specifier, new SafeSet(conditions));
Expand Down
12 changes: 8 additions & 4 deletions lib/internal/modules/package_json_reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const { toNamespacedPath } = require('path');

const cache = new SafeMap();

let manifest;

/**
*
* @param {string} jsonPath
Expand All @@ -22,10 +24,12 @@ function read(jsonPath) {
const result = { string, containsKeys };
const { getOptionValue } = require('internal/options');
if (string !== undefined) {
const manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
null;
if (manifest) {
if (manifest === undefined) {
manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
null;
}
if (manifest !== null) {
const jsonURL = pathToFileURL(jsonPath);
manifest.assertIntegrity(jsonURL, string);
}
Expand Down
Loading

0 comments on commit 18b04ab

Please sign in to comment.