Harden creation of child processes (#55697) (#59801)

Add general protection against RCE vulnerabilities similar to the one
described in CVE-2019-7609.

Closes #49605
This commit is contained in:
Thomas Watson 2020-03-10 22:05:30 +01:00 committed by GitHub
parent 63a2d4433b
commit fc57b6fba2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 809 additions and 10 deletions

View file

@ -518,6 +518,16 @@ module.exports = {
},
},
/**
* Harden specific rules
*/
{
files: ['test/harden/*.js'],
rules: {
'mocha/handle-done-callback': 'off', // TODO: Find a way to disable all mocha rules
},
},
/**
* APM overrides
*/

View file

@ -243,6 +243,7 @@
"regenerator-runtime": "^0.13.3",
"regression": "2.0.1",
"request": "^2.88.0",
"require-in-the-middle": "^5.0.2",
"reselect": "^4.0.0",
"resize-observer-polyfill": "^1.5.0",
"rison-node": "1.0.2",
@ -474,6 +475,7 @@
"strip-ansi": "^3.0.1",
"supertest": "^3.1.0",
"supertest-as-promised": "^4.0.2",
"tape": "^4.13.0",
"tree-kill": "^1.2.2",
"typescript": "3.7.2",
"typings-tester": "^0.3.2",

41
scripts/test_hardening.js Normal file
View file

@ -0,0 +1,41 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var execFileSync = require('child_process').execFileSync;
var path = require('path');
var syncGlob = require('glob').sync;
var program = require('commander');
program
.name('node scripts/test_hardening.js')
.arguments('[file...]')
.description(
'Run the tests in test/harden directory. If no files are provided, all files within the directory will be run.'
)
.action(function(globs) {
if (globs.length === 0) globs.push(path.join('test', 'harden', '*'));
globs.forEach(function(glob) {
syncGlob(glob).forEach(function(filename) {
if (path.basename(filename)[0] === '_') return;
console.log(process.argv[0], filename);
execFileSync(process.argv[0], [filename], { stdio: 'inherit' });
});
});
})
.parse(process.argv);

View file

@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var hook = require('require-in-the-middle');
hook(['child_process'], function(exports, name) {
return require(`./patches/${name}`)(exports); // eslint-disable-line import/no-dynamic-require
});

View file

@ -17,6 +17,7 @@
* under the License.
*/
require('./harden'); // this require MUST be executed before any others
require('symbol-observable');
require('./root');
require('./node_version_validator');

View file

@ -0,0 +1,76 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Ensure, when spawning a new child process, that the `options` and the
// `options.env` object passed to the child process function doesn't inherit
// from `Object.prototype`. This protects against similar RCE vulnerabilities
// as described in CVE-2019-7609
module.exports = function(cp) {
// The `exec` function is currently just a wrapper around `execFile`. So for
// now there's no need to patch it. If this changes in the future, our tests
// will fail and we can uncomment the line below.
//
// cp.exec = new Proxy(cp.exec, { apply: patchOptions() });
cp.execFile = new Proxy(cp.execFile, { apply: patchOptions(true) });
cp.fork = new Proxy(cp.fork, { apply: patchOptions(true) });
cp.spawn = new Proxy(cp.spawn, { apply: patchOptions(true) });
cp.execFileSync = new Proxy(cp.execFileSync, { apply: patchOptions(true) });
cp.execSync = new Proxy(cp.execSync, { apply: patchOptions() });
cp.spawnSync = new Proxy(cp.spawnSync, { apply: patchOptions(true) });
return cp;
};
function patchOptions(hasArgs) {
return function apply(target, thisArg, args) {
var pos = 1;
if (pos === args.length) {
// fn(arg1)
args[pos] = prototypelessSpawnOpts();
} else if (pos < args.length) {
if (hasArgs && (Array.isArray(args[pos]) || args[pos] == null)) {
// fn(arg1, args, ...)
pos++;
}
if (typeof args[pos] === 'object' && args[pos] !== null) {
// fn(arg1, {}, ...)
// fn(arg1, args, {}, ...)
args[pos] = prototypelessSpawnOpts(args[pos]);
} else if (args[pos] == null) {
// fn(arg1, null/undefined, ...)
// fn(arg1, args, null/undefined, ...)
args[pos] = prototypelessSpawnOpts();
} else if (typeof args[pos] === 'function') {
// fn(arg1, callback)
// fn(arg1, args, callback)
args.splice(pos, 0, prototypelessSpawnOpts());
}
}
return target.apply(thisArg, args);
};
}
function prototypelessSpawnOpts(obj) {
var prototypelessObj = Object.assign(Object.create(null), obj);
prototypelessObj.env = Object.assign(Object.create(null), prototypelessObj.env || process.env);
return prototypelessObj;
}

View file

@ -198,6 +198,12 @@ module.exports = function(grunt) {
args: ['scripts/notice', '--validate'],
}),
test_hardening: scriptWithGithubChecks({
title: 'Node.js hardening tests',
cmd: NODE,
args: ['scripts/test_hardening.js'],
}),
apiIntegrationTests: scriptWithGithubChecks({
title: 'API integration tests',
cmd: NODE,

View file

@ -36,6 +36,7 @@ module.exports = function(grunt) {
'run:test_jest_integration',
'run:test_projects',
'run:test_karma_ci',
'run:test_hardening',
'run:apiIntegrationTests',
]);
};

3
test/harden/_echo.sh Executable file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env sh
echo $POLLUTED$custom

20
test/harden/_fork.js Normal file
View file

@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
console.log(`${process.env.POLLUTED || ''}${process.env.custom || ''}`);

View file

@ -0,0 +1,587 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
require('../../src/setup_node_env');
const cp = require('child_process');
const path = require('path');
const test = require('tape');
Object.prototype.POLLUTED = 'polluted!'; // eslint-disable-line no-extend-native
const notSet = [null, undefined];
test.onFinish(() => {
delete Object.prototype.POLLUTED;
});
test('test setup ok', t => {
t.equal({}.POLLUTED, 'polluted!');
t.end();
});
// TODO: fork() has been omitted as it doesn't validate its arguments in
// Node.js 10 and will throw an internal error asynchronously. This is fixed in
// newer versions. See https://github.com/elastic/kibana/issues/59628
const functions = ['exec', 'execFile', 'spawn', 'execFileSync', 'execSync', 'spawnSync'];
for (const name of functions) {
test(`${name}()`, t => {
t.throws(() => cp[name](), /argument must be of type string/);
t.end();
});
}
{
const command = 'echo $POLLUTED$custom';
test('exec(command)', t => {
assertProcess(t, cp.exec(command));
});
test('exec(command, callback)', t => {
cp.exec(command, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test('exec(command, options)', t => {
assertProcess(t, cp.exec(command, {}));
});
test('exec(command, options) - with custom env', t => {
assertProcess(t, cp.exec(command, { env: { custom: 'custom' } }), { stdout: 'custom' });
});
test('exec(command, options, callback)', t => {
cp.exec(command, {}, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test('exec(command, options, callback) - with custom env', t => {
cp.exec(command, { env: { custom: 'custom' } }, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), 'custom');
t.equal(stderr.trim(), '');
t.end();
});
});
for (const unset of notSet) {
test(`exec(command, ${unset})`, t => {
assertProcess(t, cp.exec(command, unset));
});
test(`exec(command, ${unset}, callback)`, t => {
cp.exec(command, unset, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
}
}
{
const file = path.join('test', 'harden', '_echo.sh');
test('execFile(file)', t => {
assertProcess(t, cp.execFile(file));
});
test('execFile(file, args)', t => {
assertProcess(t, cp.execFile(file, []));
});
test('execFile(file, callback)', t => {
cp.execFile(file, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test('execFile(file, options)', t => {
assertProcess(t, cp.execFile(file, {}));
});
test('execFile(file, options) - with custom env', t => {
assertProcess(t, cp.execFile(file, { env: { custom: 'custom' } }), { stdout: 'custom' });
});
test('execFile(file, options, callback)', t => {
cp.execFile(file, {}, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test('execFile(file, options, callback) - with custom env', t => {
cp.execFile(file, { env: { custom: 'custom' } }, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), 'custom');
t.equal(stderr.trim(), '');
t.end();
});
});
test('execFile(file, args, callback)', t => {
cp.execFile(file, [], (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test('execFile(file, args, options)', t => {
assertProcess(t, cp.execFile(file, [], {}));
});
test('execFile(file, args, options) - with custom env', t => {
assertProcess(t, cp.execFile(file, [], { env: { custom: 'custom' } }), { stdout: 'custom' });
});
test('execFile(file, args, options, callback)', t => {
cp.execFile(file, [], {}, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test('execFile(file, args, options, callback) - with custom env', t => {
cp.execFile(file, [], { env: { custom: 'custom' } }, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), 'custom');
t.equal(stderr.trim(), '');
t.end();
});
});
for (const unset of notSet) {
test(`execFile(file, ${unset})`, t => {
assertProcess(t, cp.execFile(file, unset));
});
test(`execFile(file, ${unset}, ${unset})`, t => {
assertProcess(t, cp.execFile(file, unset, unset));
});
test(`execFile(file, ${unset}, callback)`, t => {
cp.execFile(file, unset, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test(`execFile(file, ${unset}, ${unset}, callback)`, t => {
cp.execFile(file, unset, unset, (err, stdout, stderr) => {
t.error(err);
t.equal(stdout.trim(), '');
t.equal(stderr.trim(), '');
t.end();
});
});
test(`execFile(file, ${unset}, options)`, t => {
assertProcess(t, cp.execFile(file, unset, {}));
});
}
}
{
const modulePath = path.join('test', 'harden', '_fork.js');
// NOTE: Forked processes don't have any stdout we can monitor without providing options
test.skip('fork(modulePath)', t => {
assertProcess(t, cp.fork(modulePath));
});
// NOTE: Forked processes don't have any stdout we can monitor without providing options
test.skip('execFile(file, args)', t => {
assertProcess(t, cp.fork(modulePath, []));
});
test('fork(modulePath, options)', t => {
assertProcess(
t,
cp.fork(modulePath, {
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
})
);
});
test('fork(modulePath, options) - with custom env', t => {
assertProcess(
t,
cp.fork(modulePath, {
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
env: { custom: 'custom' },
}),
{ stdout: 'custom' }
);
});
test('fork(modulePath, args, options)', t => {
assertProcess(
t,
cp.fork(modulePath, [], {
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
})
);
});
test('fork(modulePath, args, options) - with custom env', t => {
assertProcess(
t,
cp.fork(modulePath, [], {
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
env: { custom: 'custom' },
}),
{ stdout: 'custom' }
);
});
for (const unset of notSet) {
// NOTE: Forked processes don't have any stdout we can monitor without providing options
test.skip(`fork(modulePath, ${unset})`, t => {
assertProcess(t, cp.fork(modulePath, unset));
});
// NOTE: Forked processes don't have any stdout we can monitor without providing options
test.skip(`fork(modulePath, ${unset}, ${unset})`, t => {
assertProcess(t, cp.fork(modulePath, unset, unset));
});
test(`fork(modulePath, ${unset}, options)`, t => {
assertProcess(
t,
cp.fork(modulePath, unset, {
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
})
);
});
}
}
{
const command = path.join('test', 'harden', '_echo.sh');
test('spawn(command)', t => {
assertProcess(t, cp.spawn(command));
});
test('spawn(command, args)', t => {
assertProcess(t, cp.spawn(command, []));
});
test('spawn(command, options)', t => {
assertProcess(t, cp.spawn(command, {}));
});
test('spawn(command, options) - with custom env', t => {
assertProcess(t, cp.spawn(command, { env: { custom: 'custom' } }), { stdout: 'custom' });
});
test('spawn(command, args, options)', t => {
assertProcess(t, cp.spawn(command, [], {}));
});
test('spawn(command, args, options) - with custom env', t => {
assertProcess(t, cp.spawn(command, [], { env: { custom: 'custom' } }), { stdout: 'custom' });
});
for (const unset of notSet) {
test(`spawn(command, ${unset})`, t => {
assertProcess(t, cp.spawn(command, unset));
});
test(`spawn(command, ${unset}, ${unset})`, t => {
assertProcess(t, cp.spawn(command, unset, unset));
});
test(`spawn(command, ${unset}, options)`, t => {
assertProcess(t, cp.spawn(command, unset, {}));
});
}
}
{
const file = path.join('test', 'harden', '_echo.sh');
test('execFileSync(file)', t => {
t.equal(
cp
.execFileSync(file)
.toString()
.trim(),
''
);
t.end();
});
test('execFileSync(file, args)', t => {
t.equal(
cp
.execFileSync(file, [])
.toString()
.trim(),
''
);
t.end();
});
test('execFileSync(file, options)', t => {
t.equal(
cp
.execFileSync(file, {})
.toString()
.trim(),
''
);
t.end();
});
test('execFileSync(file, options) - with custom env', t => {
t.equal(
cp
.execFileSync(file, { env: { custom: 'custom' } })
.toString()
.trim(),
'custom'
);
t.end();
});
test('execFileSync(file, args, options)', t => {
t.equal(
cp
.execFileSync(file, [], {})
.toString()
.trim(),
''
);
t.end();
});
test('execFileSync(file, args, options) - with custom env', t => {
t.equal(
cp
.execFileSync(file, [], { env: { custom: 'custom' } })
.toString()
.trim(),
'custom'
);
t.end();
});
for (const unset of notSet) {
test(`execFileSync(file, ${unset})`, t => {
t.equal(
cp
.execFileSync(file, unset)
.toString()
.trim(),
''
);
t.end();
});
test(`execFileSync(file, ${unset}, ${unset})`, t => {
t.equal(
cp
.execFileSync(file, unset, unset)
.toString()
.trim(),
''
);
t.end();
});
test(`execFileSync(file, ${unset}, options)`, t => {
t.equal(
cp
.execFileSync(file, unset, {})
.toString()
.trim(),
''
);
t.end();
});
}
}
{
const command = 'echo $POLLUTED$custom';
test('execSync(command)', t => {
t.equal(
cp
.execSync(command)
.toString()
.trim(),
''
);
t.end();
});
test('execSync(command, options)', t => {
t.equal(
cp
.execSync(command, {})
.toString()
.trim(),
''
);
t.end();
});
test('execSync(command, options) - with custom env', t => {
t.equal(
cp
.execSync(command, { env: { custom: 'custom' } })
.toString()
.trim(),
'custom'
);
t.end();
});
for (const unset of notSet) {
test(`execSync(command, ${unset})`, t => {
t.equal(
cp
.execSync(command, unset)
.toString()
.trim(),
''
);
t.end();
});
}
}
{
const command = path.join('test', 'harden', '_echo.sh');
test('spawnSync(command)', t => {
const result = cp.spawnSync(command);
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test('spawnSync(command, args)', t => {
const result = cp.spawnSync(command, []);
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test('spawnSync(command, options)', t => {
const result = cp.spawnSync(command, {});
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test('spawnSync(command, options) - with custom env', t => {
const result = cp.spawnSync(command, { env: { custom: 'custom' } });
t.error(result.error);
t.equal(result.stdout.toString().trim(), 'custom');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test('spawnSync(command, args, options)', t => {
const result = cp.spawnSync(command, [], {});
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test('spawnSync(command, args, options) - with custom env', t => {
const result = cp.spawnSync(command, [], { env: { custom: 'custom' } });
t.error(result.error);
t.equal(result.stdout.toString().trim(), 'custom');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
for (const unset of notSet) {
test(`spawnSync(command, ${unset})`, t => {
const result = cp.spawnSync(command, unset);
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test(`spawnSync(command, ${unset}, ${unset})`, t => {
const result = cp.spawnSync(command, unset, unset);
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
test(`spawnSync(command, ${unset}, options)`, t => {
const result = cp.spawnSync(command, unset, {});
t.error(result.error);
t.equal(result.stdout.toString().trim(), '');
t.equal(result.stderr.toString().trim(), '');
t.end();
});
}
}
function assertProcess(t, cmd, { stdout = '' } = {}) {
t.plan(2);
cmd.stdout.on('data', data => {
t.equal(data.toString().trim(), stdout);
});
cmd.stderr.on('data', data => {
t.fail(`Unexpected data on STDERR: "${data}"`);
});
cmd.on('close', code => {
t.equal(code, 0);
t.end();
});
}

View file

@ -11857,7 +11857,7 @@ deep-equal@^1.0.0, deep-equal@^1.0.1, deep-equal@~1.0.1:
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
deep-equal@^1.1.1:
deep-equal@^1.1.1, deep-equal@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
@ -12520,6 +12520,13 @@ dotenv@^8.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2"
integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==
dotignore@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905"
integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==
dependencies:
minimatch "^3.0.4"
downgrade-root@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/downgrade-root/-/downgrade-root-1.2.2.tgz#531319715b0e81ffcc22eb28478ba27643e12c6c"
@ -13034,7 +13041,7 @@ error@^7.0.0, error@^7.0.2:
string-template "~0.2.1"
xtend "~4.0.0"
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14.2, es-abstract@^1.17.0-next.1, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0:
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14.2, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1"
integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==
@ -13051,7 +13058,7 @@ es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14
string.prototype.trimleft "^2.1.1"
string.prototype.trimright "^2.1.1"
es-abstract@^1.15.0:
es-abstract@^1.15.0, es-abstract@^1.17.0-next.1:
version "1.17.4"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
@ -15564,7 +15571,7 @@ glob@^6.0.1, glob@^6.0.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@ -17325,7 +17332,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@~2.0.3:
inherits@2, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -18153,7 +18160,7 @@ is-redirect@^1.0.0:
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=
is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5:
is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5, is-regex@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
@ -22454,7 +22461,7 @@ object-identity-map@^1.0.2:
dependencies:
object.entries "^1.1.0"
object-inspect@^1.7.0:
object-inspect@^1.7.0, object-inspect@~1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
@ -26428,7 +26435,7 @@ require-from-string@^2.0.1:
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-in-the-middle@^5.0.0:
require-in-the-middle@^5.0.0, require-in-the-middle@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.0.2.tgz#ce3593007a61583b39ccdcd2c167a2a326c670b2"
integrity sha512-l2r6F9i6t5xp4OE9cw/daB/ooQKHZOOW1AYPADhEvk/Tj/THJDS8gePp76Zyuht6Cj57a0KL+eHK5Dyv7wZnKA==
@ -26640,7 +26647,7 @@ resolve@^1.12.0, resolve@^1.4.0:
dependencies:
path-parse "^1.0.6"
resolve@^1.13.1:
resolve@^1.13.1, resolve@~1.14.2:
version "1.14.2"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2"
integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==
@ -28476,7 +28483,7 @@ string.prototype.padstart@^3.0.0:
es-abstract "^1.4.3"
function-bind "^1.0.2"
string.prototype.trim@^1.2.1:
string.prototype.trim@^1.2.1, string.prototype.trim@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782"
integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==
@ -29046,6 +29053,27 @@ tapable@^1.1.3:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
tape@^4.13.0:
version "4.13.0"
resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.0.tgz#e2f581ff5f12a7cbd787e9f83c76c2851782fce2"
integrity sha512-J/hvA+GJnuWJ0Sj8Z0dmu3JgMNU+MmusvkCT7+SN4/2TklW18FNCp/UuHIEhPZwHfy4sXfKYgC7kypKg4umbOw==
dependencies:
deep-equal "~1.1.1"
defined "~1.0.0"
dotignore "~0.1.2"
for-each "~0.3.3"
function-bind "~1.1.1"
glob "~7.1.6"
has "~1.0.3"
inherits "~2.0.4"
is-regex "~1.0.5"
minimist "~1.2.0"
object-inspect "~1.7.0"
resolve "~1.14.2"
resumer "~0.0.0"
string.prototype.trim "~1.2.1"
through "~2.3.8"
tape@^4.5.1:
version "4.10.2"
resolved "https://registry.yarnpkg.com/tape/-/tape-4.10.2.tgz#129fcf62f86df92687036a52cce7b8ddcaffd7a6"