node-sec-patterns
Advanced tools
Comparing version 2.0.1 to 2.0.2
@@ -20,3 +20,9 @@ 'use strict' | ||
const { apply } = Reflect | ||
const arrayMap = Array.prototype.map | ||
const { | ||
indexOf: arrayIndexOf, | ||
join: arrayJoin, | ||
map: arrayMap, | ||
slice: arraySlice, | ||
push: arrayPush | ||
} = Array.prototype | ||
const mapGet = Map.prototype.get | ||
@@ -26,3 +32,3 @@ const mapSet = Map.prototype.set | ||
const weakSetHas = WeakSet.prototype.has | ||
const { lastIndexOf, substring } = String.prototype | ||
const { lastIndexOf, split, substring } = String.prototype | ||
@@ -57,2 +63,79 @@ const { dedot, dirname } = require('module-keys/lib/relpath.js') | ||
/** | ||
* Updates whitelist to add the target package to package whitelists | ||
* for the given contract keys. | ||
*/ | ||
function incorporateSelfNominationsFor (targetPackage, contractKeys) { | ||
if (isArray(contractKeys)) { | ||
for (let i = 0, nNoms = contractKeys.length; i < nNoms; ++i) { | ||
if (apply(hasOwnProperty, contractKeys, [ i ])) { | ||
const selfNomination = contractKeys[i] | ||
whitelist[selfNomination] = whitelist[selfNomination] || [] | ||
apply(arrayPush, whitelist[selfNomination], [ targetPackage ]) | ||
} | ||
} | ||
} | ||
} | ||
function computeResolvePaths () { | ||
const resolvePaths = [] | ||
const configRootParts = apply(split, configRoot, [ sep ]) | ||
for (let i = configRootParts.length; --i >= 0;) { | ||
resolvePaths[resolvePaths.length] = `${ | ||
apply( | ||
arrayJoin, | ||
apply(arraySlice, configRootParts, [ 0, i + 1 ]), | ||
[ sep ]) | ||
}${sep}node_modules` | ||
} | ||
return resolvePaths | ||
} | ||
/** | ||
* Loads configuration files for packages whose self nominations have | ||
* been seconded and modifies whitelist in place to incorporate them. | ||
*/ | ||
function incorporateSelfNominations (second) { | ||
const resolvePaths = computeResolvePaths() | ||
for (let i = 0, nSeconds = second.length; i < nSeconds; ++i) { | ||
if (apply(hasOwnProperty, second, [ i ])) { | ||
let targetConfigPath = `${second[i]}` | ||
if (!targetConfigPath) { | ||
continue | ||
} | ||
// eslint-disable-next-line no-magic-numbers | ||
if (apply(substring, targetConfigPath, [ targetConfigPath.length - 5 ]) !== '.json') { | ||
targetConfigPath += '/package.json' | ||
} | ||
// Infer the target package name from the configuration path file | ||
// "path/to/config.json" => "/abs/node_modules/path/to/config.json" | ||
const resolvedTargetConfigPath = require.resolve(targetConfigPath, { paths: resolvePaths }) | ||
let targetPackage = resolvedTargetConfigPath | ||
// "/abs/node_modules/path/to/config.json" | ||
// => [ "", "abs", "node_modules", "path", "to", "config.json" ] | ||
targetPackage = apply(split, targetPackage, [ '/' ]) | ||
// [ "", "abs", "node_modules", "path", "to", "config.json" ] | ||
// => [ "path", "to", "config.json" ] | ||
targetPackage = apply( | ||
arraySlice, targetPackage, | ||
[ apply(arrayIndexOf, targetPackage, [ 'node_modules' ]) + 1 ]) | ||
// [ "path", "to", "config.json" ] => [ "path" ] | ||
// OR [ "@namespace", "path", "to", "..." ] => [ "@namespace", "path" ] | ||
targetPackage = apply( | ||
arraySlice, targetPackage, | ||
[ 0, targetPackage[0][0] === '@' ? 2 : 1 ]) | ||
// [ "@namespace", "path" ] => "@namespace/path" | ||
targetPackage = apply(arrayJoin, targetPackage, [ '/' ]) | ||
// Fetch the target configuration | ||
// eslint-disable-next-line global-require | ||
const targetConfig = require(resolvedTargetConfigPath) | ||
incorporateSelfNominationsFor( | ||
targetPackage, | ||
((targetConfig && targetConfig.mintable) || {}).selfNominate) | ||
} | ||
} | ||
} | ||
/** | ||
* Takes a configuration object with "mintableGrants" &| "mintableMode" | ||
@@ -68,2 +151,3 @@ */ | ||
grants = {}, | ||
second, | ||
mode | ||
@@ -74,5 +158,7 @@ } = {} | ||
whitelist = create(null) | ||
try { | ||
failureMode = modeFromConfig(options, mode) | ||
// Defensively copy grants over | ||
for (const key in grants) { | ||
@@ -82,7 +168,15 @@ if (typeof key === 'string' && apply(hasOwnProperty, grants, [ key ])) { | ||
if (isArray(val)) { | ||
whitelist[key] = freeze(apply(arrayMap, val, [ (ele) => `${ele}` ])) | ||
whitelist[key] = apply(arrayMap, val, [ (ele) => `${ele}` ]) | ||
} | ||
} | ||
} | ||
if (isArray(second)) { | ||
incorporateSelfNominations(second, whitelist) | ||
} | ||
} finally { | ||
for (const key in whitelist) { | ||
freeze(whitelist[key]) | ||
} | ||
freeze(whitelist) | ||
@@ -214,3 +308,3 @@ } | ||
const { grants, pubKeys } = grantRecord | ||
if (apply(weakSetHas, pubKeys, pubKey)) { | ||
if (apply(weakSetHas, pubKeys, [ pubKey ])) { | ||
// We've seen it before, great! | ||
@@ -217,0 +311,0 @@ return true |
{ | ||
"name": "node-sec-patterns", | ||
"description": "Allow projects control over which dependencies can create objects that encapsulate security guarantees.", | ||
"version": "2.0.1", | ||
"version": "2.0.2", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "design-patterns", |
@@ -19,2 +19,3 @@ # Node security design patterns | ||
* [Configuration](#configuration) | ||
* [Suggesting grants](#suggesting-grants) | ||
* [Defining a Mintable Type](#defining-a-mintable-type) | ||
@@ -120,3 +121,90 @@ * [Example](#example) | ||
### Suggesting grants | ||
Library code may also suggest grants. It may **self nominate** for | ||
certain privileges, and then an application may **second** those | ||
privileges. | ||
For example, if a library's package.json includes | ||
```json | ||
{ | ||
... | ||
"mintable": { | ||
"selfNominate": [ | ||
"contractKey0", | ||
"contractKey1" | ||
] | ||
} | ||
} | ||
``` | ||
and an application's package.json includes | ||
```json | ||
{ | ||
... | ||
"mintable": { | ||
"second": [ | ||
"path/to/library" | ||
] | ||
} | ||
} | ||
``` | ||
then `Mintable.minterFor` will behave as if the application's | ||
package.json had done | ||
```json | ||
{ | ||
... | ||
"mintable": { | ||
"grants": { | ||
"contractKey0": [ "path/to/library" ], | ||
"contractKey1": [ "path/to/library" ] | ||
} | ||
} | ||
} | ||
``` | ||
Application maintainers can run the below to see what effect self nominations have, | ||
but keep in mind that a package might change its self nominations in future versions so | ||
seconding self-nominated grants for a module is placing trust in that module's future | ||
development practices. | ||
```sh | ||
$ node -e 'for (const second of require(`./package.json`).mintable.second) { | ||
const config = /[.]json$/.test(second) ? second : `${ second }/package.json`; | ||
console.group(second); | ||
console.log(JSON.stringify(require(config).mintable.selfNominate, null, 2)); | ||
console.groupEnd(); | ||
}' | ||
``` | ||
Seconded nominations are resolved using the following algorithm: | ||
1. for (targetConfigPath of configuration.mintable.second) | ||
1. Make sure we're loading a configuration file: | ||
1. if targetConfigPath does not end with `.json` then targetConfigPath += '/package.json' | ||
1. Infer the target package name from the configuration path file: | ||
1. let targetPackage = require.resolve(targetConfigPath) | ||
1. targetPackage = targetPackage.split('/') | ||
1. targetPackage = targetPackage.slice(targetPackage.indexOf('node_modules') + 1) | ||
1. targetPackage = targetPackage.slice(0, targetPackage[0][0] === '@' ? 2 : 1) | ||
1. targetPackage = targetPackage.join('/') | ||
1. Fetch the target configuration | ||
1. let targetConfig = require(targetConfigPath) | ||
1. Incorporate any self nominations into the application's grants | ||
1. let selfNominations = (targetConfig.mintable || {}).selfNominate || [] | ||
1. for (selfNomination of selfNominations) | ||
1. grants[selfNomination] = grants[selfNomination] || [] | ||
1. grants[selfNomination].push( | ||
If a self nomination path ends in `.json` then `/package.json` is not appended to the | ||
config file. | ||
Internal package directories are stripped when figuring out to whom access is granted. | ||
## Defining a Mintable Type | ||
@@ -123,0 +211,0 @@ Mintable types are subclasses of `class Mintable` exported by this module. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38926
426
333
6