Add @elastic/safer-lodash-set as an alternative to lodash.set (#67452)

This commit is contained in:
Thomas Watson 2020-07-15 10:29:57 +02:00 committed by GitHub
parent 42c3efdcab
commit fc5bc6b6a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
137 changed files with 2475 additions and 196 deletions

View file

@ -49,6 +49,31 @@ const ELASTIC_LICENSE_HEADER = `
*/ */
`; `;
const SAFER_LODASH_SET_HEADER = `
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See \`packages/elastic-safer-lodash-set/LICENSE\` for more information.
*/
`;
const SAFER_LODASH_SET_LODASH_HEADER = `
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See \`packages/elastic-safer-lodash-set/LICENSE\` for more information.
*/
`;
const SAFER_LODASH_SET_DEFINITELYTYPED_HEADER = `
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See \`packages/elastic-safer-lodash-set/LICENSE\` for more information.
*/
`;
const allMochaRulesOff = {}; const allMochaRulesOff = {};
Object.keys(require('eslint-plugin-mocha').rules).forEach((k) => { Object.keys(require('eslint-plugin-mocha').rules).forEach((k) => {
allMochaRulesOff['mocha/' + k] = 'off'; allMochaRulesOff['mocha/' + k] = 'off';
@ -143,7 +168,12 @@ module.exports = {
'@kbn/eslint/disallow-license-headers': [ '@kbn/eslint/disallow-license-headers': [
'error', 'error',
{ {
licenses: [ELASTIC_LICENSE_HEADER], licenses: [
ELASTIC_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
}, },
], ],
}, },
@ -174,7 +204,82 @@ module.exports = {
'@kbn/eslint/disallow-license-headers': [ '@kbn/eslint/disallow-license-headers': [
'error', 'error',
{ {
licenses: [APACHE_2_0_LICENSE_HEADER], licenses: [
APACHE_2_0_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
/**
* safer-lodash-set package requires special license headers
*/
{
files: ['packages/elastic-safer-lodash-set/**/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: SAFER_LODASH_SET_LODASH_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
ELASTIC_LICENSE_HEADER,
APACHE_2_0_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
{
files: ['packages/elastic-safer-lodash-set/test/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: SAFER_LODASH_SET_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
ELASTIC_LICENSE_HEADER,
APACHE_2_0_LICENSE_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
{
files: ['packages/elastic-safer-lodash-set/**/*.d.ts'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
ELASTIC_LICENSE_HEADER,
APACHE_2_0_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
],
}, },
], ],
}, },
@ -541,9 +646,129 @@ module.exports = {
* Harden specific rules * Harden specific rules
*/ */
{ {
files: ['test/harden/*.js'], files: ['test/harden/*.js', 'packages/elastic-safer-lodash-set/test/*.js'],
rules: allMochaRulesOff, rules: allMochaRulesOff,
}, },
{
files: ['**/*.{js,mjs,ts,tsx}'],
rules: {
'no-restricted-imports': [
2,
{
paths: [
{
name: 'lodash',
importNames: ['set', 'setWith'],
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.setwith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp',
importNames: ['set', 'setWith', 'assoc', 'assocPath'],
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/assoc',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
],
},
],
'no-restricted-modules': [
2,
{
paths: [
{
name: 'lodash.set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.setwith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
],
},
],
'no-restricted-properties': [
2,
{
object: 'lodash',
property: 'set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: 'lodash',
property: 'setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: 'lodash',
property: 'assoc',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'assoc',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: 'lodash',
property: 'assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
],
},
},
/** /**
* APM overrides * APM overrides

View file

@ -132,6 +132,7 @@
"@elastic/good": "8.1.1-kibana2", "@elastic/good": "8.1.1-kibana2",
"@elastic/numeral": "^2.5.0", "@elastic/numeral": "^2.5.0",
"@elastic/request-crypto": "1.1.4", "@elastic/request-crypto": "1.1.4",
"@elastic/safer-lodash-set": "0.0.0",
"@elastic/ui-ace": "0.2.3", "@elastic/ui-ace": "0.2.3",
"@hapi/good-squeeze": "5.2.1", "@hapi/good-squeeze": "5.2.1",
"@hapi/wreck": "^15.0.2", "@hapi/wreck": "^15.0.2",

View file

@ -0,0 +1,2 @@
.tmp
node_modules

View file

@ -0,0 +1,3 @@
tsconfig.json
scripts
test

View file

@ -0,0 +1,34 @@
The MIT License (MIT)
Copyright (c) Elasticsearch BV
Copyright (c) Brian Zengel <https://github.com/bczengel>, Ilya Mochalov <https://github.com/chrootsu>
Copyright (c) JS Foundation and other contributors <https://js.foundation/>
Lodash is based on Underscore.js, copyright Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at the following locations:
- https://github.com/lodash/lodash
- https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/lodash
- https://github.com/elastic/kibana/tree/master/packages/elastic-safer-lodash-set
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,113 @@
# @elastic/safer-lodash-set
This module adds protection against prototype pollution to the [`set`]
and [`setWith`] functions from [Lodash] and are API compatible with
Lodash v4.x.
## Example Usage
```js
const { set } = require('@elastic/safer-loadsh-set');
const object = { a: [{ b: { c: 3 } }] };
set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c); // => 4
set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z); // => 5
```
## API
The main module exposes two functions, `set` and `setWith`:
```js
const { set, setWith } = require('@elastic/safer-lodash-set');
```
Besides the main module, it's also possible to require each function
individually:
```js
const set = require('@elastic/safer-lodash-set/set');
const setWith = require('@elastic/safer-lodash-set/setWith');
```
The APIs of these functions are identical to the equivalent Lodash
[`set`] and [`setWith`] functions. Please refer to the Lodash
documentation for the respective functions for details.
### Functional Programming support (fp)
This module also supports the `lodash/fp` api and hence exposes the
following fp compatible functions:
```js
const { set, setWith } = require('@elastic/safer-lodash-set/fp');
```
Besides the main fp module, it's also possible to require each function
individually:
```js
const set = require('@elastic/safer-lodash-set/fp/set');
const setWith = require('@elastic/safer-lodash-set/fp/setWith');
```
## Limitations
The safety improvements in this module is achieved by adding the
following limitations to the algorithm used to walk the `path` given as
the 2nd argument to the `set` and `setWith` functions:
### Only own properties are followed when walking the `path`
```js
const parent = { foo: 1 };
const child = { bar: 2 };
Object.setPrototypeOf(child, parent);
// Now `child` can access `foo` through prototype inheritance
console.log(child.foo); // 1
set(child, 'foo', 3);
// A different `foo` property has now been added directly to the `child`
// object and the `parent` object has not been modified:
console.log(child.foo); // 3
console.log(parent.foo); // 1
console.log(Object.prototype.hasOwnProperty.call(child, 'foo')); // true
```
### The `path` must not access function prototypes
```js
const object = {
fn1: function () {},
fn2: () => {},
};
// Attempting to access any function prototype will result in an
// exception being thrown:
assert.throws(() => {
// Throws: Illegal access of function prototype
set(object, 'fn1.prototype.toString', 'bang!');
});
// This also goes for arrow functions even though they don't have a
// prototype property. This is just to keep things consistent:
assert.throws(() => {
// Throws: Illegal access of function prototype
set(object, 'fn2.prototype.toString', 'bang!');
});
```
## License
[MIT](LICENSE)
[`set`]: https://lodash.com/docs/4.17.15#set
[`setwith`]: https://lodash.com/docs/4.17.15#setWith
[lodash]: https://lodash.com/

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { assoc } from './index';
export = assoc;

View file

@ -0,0 +1,8 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
module.exports = require('./set');

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { assocPath } from './index';
export = assocPath;

View file

@ -0,0 +1,8 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
module.exports = require('./set');

View file

@ -0,0 +1,225 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import lodash = require('lodash');
export = SaferLodashSet;
export as namespace SaferLodashSet;
declare const SaferLodashSet: SaferLodashSet.SaferLoDashStaticFp;
declare namespace SaferLodashSet {
interface LodashSet {
(path: lodash.PropertyPath): LodashSet1x1;
(path: lodash.__, value: any): LodashSet1x2;
(path: lodash.PropertyPath, value: any): LodashSet1x3;
<T extends object>(path: lodash.__, value: lodash.__, object: T): LodashSet1x4<T>;
<T extends object>(path: lodash.PropertyPath, value: lodash.__, object: T): LodashSet1x5<T>;
<T extends object>(path: lodash.__, value: any, object: T): LodashSet1x6<T>;
<T extends object>(path: lodash.PropertyPath, value: any, object: T): T;
(path: lodash.__, value: lodash.__, object: object): LodashSet2x4;
(path: lodash.PropertyPath, value: lodash.__, object: object): LodashSet2x5;
(path: lodash.__, value: any, object: object): LodashSet2x6;
<TResult>(path: lodash.PropertyPath, value: any, object: object): TResult;
}
interface LodashSet1x1 {
(value: any): LodashSet1x3;
<T extends object>(value: lodash.__, object: T): LodashSet1x5<T>;
<T extends object>(value: any, object: T): T;
(value: lodash.__, object: object): LodashSet2x5;
<TResult>(value: any, object: object): TResult;
}
interface LodashSet1x2 {
(path: lodash.PropertyPath): LodashSet1x3;
<T extends object>(path: lodash.__, object: T): LodashSet1x6<T>;
<T extends object>(path: lodash.PropertyPath, object: T): T;
(path: lodash.__, object: object): LodashSet2x6;
<TResult>(path: lodash.PropertyPath, object: object): TResult;
}
interface LodashSet1x3 {
<T extends object>(object: T): T;
<TResult>(object: object): TResult;
}
interface LodashSet1x4<T> {
(path: lodash.PropertyPath): LodashSet1x5<T>;
(path: lodash.__, value: any): LodashSet1x6<T>;
(path: lodash.PropertyPath, value: any): T;
}
type LodashSet1x5<T> = (value: any) => T;
type LodashSet1x6<T> = (path: lodash.PropertyPath) => T;
interface LodashSet2x4 {
(path: lodash.PropertyPath): LodashSet2x5;
(path: lodash.__, value: any): LodashSet2x6;
<TResult>(path: lodash.PropertyPath, value: any): TResult;
}
type LodashSet2x5 = <TResult>(value: any) => TResult;
type LodashSet2x6 = <TResult>(path: lodash.PropertyPath) => TResult;
interface LodashSetWith {
<T extends object>(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x1<T>;
(customizer: lodash.__, path: lodash.PropertyPath): LodashSetWith1x2;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.PropertyPath
): LodashSetWith1x3<T>;
(customizer: lodash.__, path: lodash.__, value: any): LodashSetWith1x4;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.__,
value: any
): LodashSetWith1x5<T>;
(customizer: lodash.__, path: lodash.PropertyPath, value: any): LodashSetWith1x6;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.PropertyPath,
value: any
): LodashSetWith1x7<T>;
<T extends object>(
customizer: lodash.__,
path: lodash.__,
value: lodash.__,
object: T
): LodashSetWith1x8<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.__,
value: lodash.__,
object: T
): LodashSetWith1x9<T>;
<T extends object>(
customizer: lodash.__,
path: lodash.PropertyPath,
value: lodash.__,
object: T
): LodashSetWith1x10<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.PropertyPath,
value: lodash.__,
object: T
): LodashSetWith1x11<T>;
<T extends object>(
customizer: lodash.__,
path: lodash.__,
value: any,
object: T
): LodashSetWith1x12<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.__,
value: any,
object: T
): LodashSetWith1x13<T>;
<T extends object>(
customizer: lodash.__,
path: lodash.PropertyPath,
value: any,
object: T
): LodashSetWith1x14<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.PropertyPath,
value: any,
object: T
): T;
}
interface LodashSetWith1x1<T> {
(path: lodash.PropertyPath): LodashSetWith1x3<T>;
(path: lodash.__, value: any): LodashSetWith1x5<T>;
(path: lodash.PropertyPath, value: any): LodashSetWith1x7<T>;
(path: lodash.__, value: lodash.__, object: T): LodashSetWith1x9<T>;
(path: lodash.PropertyPath, value: lodash.__, object: T): LodashSetWith1x11<T>;
(path: lodash.__, value: any, object: T): LodashSetWith1x13<T>;
(path: lodash.PropertyPath, value: any, object: T): T;
}
interface LodashSetWith1x2 {
<T extends object>(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x3<T>;
(customizer: lodash.__, value: any): LodashSetWith1x6;
<T extends object>(customizer: lodash.SetWithCustomizer<T>, value: any): LodashSetWith1x7<T>;
<T extends object>(customizer: lodash.__, value: lodash.__, object: T): LodashSetWith1x10<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
value: lodash.__,
object: T
): LodashSetWith1x11<T>;
<T extends object>(customizer: lodash.__, value: any, object: T): LodashSetWith1x14<T>;
<T extends object>(customizer: lodash.SetWithCustomizer<T>, value: any, object: T): T;
}
interface LodashSetWith1x3<T> {
(value: any): LodashSetWith1x7<T>;
(value: lodash.__, object: T): LodashSetWith1x11<T>;
(value: any, object: T): T;
}
interface LodashSetWith1x4 {
<T extends object>(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x5<T>;
(customizer: lodash.__, path: lodash.PropertyPath): LodashSetWith1x6;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.PropertyPath
): LodashSetWith1x7<T>;
<T extends object>(customizer: lodash.__, path: lodash.__, object: T): LodashSetWith1x12<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.__,
object: T
): LodashSetWith1x13<T>;
<T extends object>(
customizer: lodash.__,
path: lodash.PropertyPath,
object: T
): LodashSetWith1x14<T>;
<T extends object>(
customizer: lodash.SetWithCustomizer<T>,
path: lodash.PropertyPath,
object: T
): T;
}
interface LodashSetWith1x5<T> {
(path: lodash.PropertyPath): LodashSetWith1x7<T>;
(path: lodash.__, object: T): LodashSetWith1x13<T>;
(path: lodash.PropertyPath, object: T): T;
}
interface LodashSetWith1x6 {
<T extends object>(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x7<T>;
<T extends object>(customizer: lodash.__, object: T): LodashSetWith1x14<T>;
<T extends object>(customizer: lodash.SetWithCustomizer<T>, object: T): T;
}
type LodashSetWith1x7<T> = (object: T) => T;
interface LodashSetWith1x8<T> {
(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x9<T>;
(customizer: lodash.__, path: lodash.PropertyPath): LodashSetWith1x10<T>;
(customizer: lodash.SetWithCustomizer<T>, path: lodash.PropertyPath): LodashSetWith1x11<T>;
(customizer: lodash.__, path: lodash.__, value: any): LodashSetWith1x12<T>;
(customizer: lodash.SetWithCustomizer<T>, path: lodash.__, value: any): LodashSetWith1x13<T>;
(customizer: lodash.__, path: lodash.PropertyPath, value: any): LodashSetWith1x14<T>;
(customizer: lodash.SetWithCustomizer<T>, path: lodash.PropertyPath, value: any): T;
}
interface LodashSetWith1x9<T> {
(path: lodash.PropertyPath): LodashSetWith1x11<T>;
(path: lodash.__, value: any): LodashSetWith1x13<T>;
(path: lodash.PropertyPath, value: any): T;
}
interface LodashSetWith1x10<T> {
(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x11<T>;
(customizer: lodash.__, value: any): LodashSetWith1x14<T>;
(customizer: lodash.SetWithCustomizer<T>, value: any): T;
}
type LodashSetWith1x11<T> = (value: any) => T;
interface LodashSetWith1x12<T> {
(customizer: lodash.SetWithCustomizer<T>): LodashSetWith1x13<T>;
(customizer: lodash.__, path: lodash.PropertyPath): LodashSetWith1x14<T>;
(customizer: lodash.SetWithCustomizer<T>, path: lodash.PropertyPath): T;
}
type LodashSetWith1x13<T> = (path: lodash.PropertyPath) => T;
type LodashSetWith1x14<T> = (customizer: lodash.SetWithCustomizer<T>) => T;
interface SaferLoDashStaticFp {
assoc: LodashSet;
assocPath: LodashSet;
set: LodashSet;
setWith: LodashSetWith;
}
}

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
exports.set = exports.assoc = exports.assocPath = require('./set');
exports.setWith = require('./setWith');

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { set } from './index';
export = set;

View file

@ -0,0 +1,13 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
/*eslint no-var:0 */
var convert = require('lodash/fp/convert');
var func = convert('set', require('../set'));
func.placeholder = require('lodash/fp/placeholder');
module.exports = func;

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { setWith } from './index';
export = setWith;

View file

@ -0,0 +1,13 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
/*eslint no-var:0 */
var convert = require('lodash/fp/convert');
var func = convert('setWith', require('../setWith'));
func.placeholder = require('lodash/fp/placeholder');
module.exports = func;

View file

@ -0,0 +1,64 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
export = SaferLodashSet;
export as namespace SaferLodashSet;
type Many<T> = T | readonly T[];
type PropertyName = string | number | symbol;
type PropertyPath = Many<PropertyName>;
type SetWithCustomizer<T> = (nsValue: any, key: string, nsObject: T) => any;
declare const SaferLodashSet: SaferLodashSet.SaferLoDashStatic;
declare namespace SaferLodashSet {
interface SaferLoDashStatic {
/**
* Sets the value at path of object. If a portion of path doesnt exist its
* created. Arrays are created for missing index properties while objects
* are created for all other missing properties. Use SaferLodashSet.setWith
* to customize path creation.
*
* @param object The object to modify.
* @param path The path of the property to set.
* @param value The value to set.
* @return Returns object.
*/
set<T extends object>(object: T, path: PropertyPath, value: any): T;
/**
* @see SaferLodashSet.set
*/
set<TResult>(object: object, path: PropertyPath, value: any): TResult;
/**
* This method is like SaferLodashSet.set except that it accepts customizer
* which is invoked to produce the objects of path. If customizer returns
* undefined path creation is handled by the method instead. The customizer
* is invoked with three arguments: (nsValue, key, nsObject).
*
* @param object The object to modify.
* @param path The path of the property to set.
* @param value The value to set.
* @param customizer The function to customize assigned values.
* @return Returns object.
*/
setWith<T extends object>(
object: T,
path: PropertyPath,
value: any,
customizer?: SetWithCustomizer<T>
): T;
/**
* @see SaferLodashSet.setWith
*/
setWith<T extends object, TResult>(
object: T,
path: PropertyPath,
value: any,
customizer?: SetWithCustomizer<T>
): TResult;
}
}

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
exports.set = require('./lodash/set');
exports.setWith = require('./lodash/setWith');

View file

@ -0,0 +1,61 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
/* eslint-disable */
var assignValue = require('lodash/_assignValue'),
castPath = require('lodash/_castPath'),
isFunction = require('lodash/isFunction'),
isIndex = require('lodash/_isIndex'),
isObject = require('lodash/isObject'),
toKey = require('lodash/_toKey');
/**
* The base implementation of `_.set`.
*
* @private
* @param {Object} object The object to modify.
* @param {Array|string} path The path of the property to set.
* @param {*} value The value to set.
* @param {Function} [customizer] The function to customize path creation.
* @returns {Object} Returns `object`.
*/
function baseSet(object, path, value, customizer) {
if (!isObject(object)) {
return object;
}
path = castPath(path, object);
var index = -1,
length = path.length,
lastIndex = length - 1,
nested = object;
while (nested != null && ++index < length) {
var key = toKey(path[index]),
newValue = value;
if (key == 'prototype' && isFunction(nested)) {
throw new Error('Illegal access of function prototype')
}
if (index != lastIndex) {
var objValue = hasOwnProperty.call(nested, key) ? nested[key] : undefined
newValue = customizer ? customizer(objValue, key, nested) : undefined;
if (newValue === undefined) {
newValue = isObject(objValue)
? objValue
: (isIndex(path[index + 1]) ? [] : {});
}
}
assignValue(nested, key, newValue);
nested = nested[key];
}
return object;
}
module.exports = baseSet;

View file

@ -0,0 +1,44 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
/* eslint-disable */
var baseSet = require('./_baseSet');
/**
* Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
* it's created. Arrays are created for missing index properties while objects
* are created for all other missing properties. Use `_.setWith` to customize
* `path` creation.
*
* **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 3.7.0
* @category Object
* @param {Object} object The object to modify.
* @param {Array|string} path The path of the property to set.
* @param {*} value The value to set.
* @returns {Object} Returns `object`.
* @example
*
* var object = { 'a': [{ 'b': { 'c': 3 } }] };
*
* _.set(object, 'a[0].b.c', 4);
* console.log(object.a[0].b.c);
* // => 4
*
* _.set(object, ['x', '0', 'y', 'z'], 5);
* console.log(object.x[0].y.z);
* // => 5
*/
function set(object, path, value) {
return object == null ? object : baseSet(object, path, value);
}
module.exports = set;

View file

@ -0,0 +1,41 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
/* eslint-disable */
var baseSet = require('./_baseSet');
/**
* This method is like `_.set` except that it accepts `customizer` which is
* invoked to produce the objects of `path`. If `customizer` returns `undefined`
* path creation is handled by the method instead. The `customizer` is invoked
* with three arguments: (nsValue, key, nsObject).
*
* **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Object
* @param {Object} object The object to modify.
* @param {Array|string} path The path of the property to set.
* @param {*} value The value to set.
* @param {Function} [customizer] The function to customize assigned values.
* @returns {Object} Returns `object`.
* @example
*
* var object = {};
*
* _.setWith(object, '[0][1]', 'a', Object);
* // => { '0': { '1': 'a' } }
*/
function setWith(object, path, value, customizer) {
customizer = typeof customizer == 'function' ? customizer : undefined;
return object == null ? object : baseSet(object, path, value, customizer);
}
module.exports = setWith;

View file

@ -0,0 +1,49 @@
{
"name": "@elastic/safer-lodash-set",
"version": "0.0.0",
"description": "A safer version of the lodash set and setWith functions",
"main": "index.js",
"types": "index.d.ts",
"dependencies": {},
"devDependencies": {
"dependency-check": "^4.1.0",
"tape": "^5.0.1",
"tsd": "^0.13.1"
},
"peerDependencies": {
"lodash": "4.x"
},
"scripts": {
"lint": "dependency-check --no-dev package.json set.js setWith.js fp/*.js",
"test": "npm run lint && tape test/*.js && npm run test:types",
"test:types": "./scripts/tsd.sh",
"update": "./scripts/update.sh",
"save_state": "./scripts/save_state.sh"
},
"repository": {
"type": "git",
"url": "git+https://github.com/elastic/kibana.git"
},
"keywords": [
"lodash",
"security",
"set",
"setWith",
"prototype",
"pollution"
],
"author": "Thomas Watson <w@tson.dk> (https://twitter.com/wa7son)",
"license": "MIT",
"bugs": {
"url": "https://github.com/elastic/kibana/issues"
},
"homepage": "https://github.com/elastic/kibana/tree/master/packages/safer-lodash-set#readme",
"standard": {
"ignore": [
"/lodash/"
]
},
"tsd": {
"directory": "test"
}
}

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Elasticsearch B.V licenses this file to you under the MIT License.
# See `packages/elastic-safer-lodash-set/LICENSE` for more information.
clean_up () {
exit_code=$?
rm -fr .tmp
exit $exit_code
}
trap clean_up EXIT
# Get a temporary copy of the latest v4 lodash
rm -fr .tmp
npm install --no-fund --ignore-scripts --no-audit --loglevel error --prefix ./.tmp lodash@4 > /dev/null

View file

@ -0,0 +1,7 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/

View file

@ -0,0 +1,31 @@
1,5c1,15
< var assignValue = require('./_assignValue'),
< castPath = require('./_castPath'),
< isIndex = require('./_isIndex'),
< isObject = require('./isObject'),
< toKey = require('./_toKey');
---
> /*
> * This file is forked from the lodash project (https://lodash.com/),
> * and may include modifications made by Elasticsearch B.V.
> * Elasticsearch B.V. licenses this file to you under the MIT License.
> * See `packages/elastic-safer-lodash-set/LICENSE` for more information.
> */
>
> /* eslint-disable */
>
> var assignValue = require('lodash/_assignValue'),
> castPath = require('lodash/_castPath'),
> isFunction = require('lodash/isFunction'),
> isIndex = require('lodash/_isIndex'),
> isObject = require('lodash/isObject'),
> toKey = require('lodash/_toKey');
31a42,45
> if (key == 'prototype' && isFunction(nested)) {
> throw new Error('Illegal access of function prototype')
> }
>
33c47
< var objValue = nested[key];
---
> var objValue = hasOwnProperty.call(nested, key) ? nested[key] : undefined

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Elasticsearch B.V licenses this file to you under the MIT License.
# See `packages/elastic-safer-lodash-set/LICENSE` for more information.
set -e
source ./scripts/_get_lodash.sh
modified_lodash_files=(_baseSet.js)
# Create fresh patch files for each of the modified files
for file in "${modified_lodash_files[@]}"
do
diff ".tmp/node_modules/lodash/$file" "lodash/$file" > "scripts/patches/$file.patch" || true
done
echo "State updated!"

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Elasticsearch B.V licenses this file to you under the MIT License.
# See `packages/elastic-safer-lodash-set/LICENSE` for more information.
# tsd will get confused if it finds a tsconfig.json file in the project
# directory and start to scan the entirety of Kibana. We don't want that.
mv tsconfig.json tsconfig.tmp
clean_up () {
exit_code=$?
mv tsconfig.tmp tsconfig.json
exit $exit_code
}
trap clean_up EXIT
./node_modules/.bin/tsd

View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Elasticsearch B.V licenses this file to you under the MIT License.
# See `packages/elastic-safer-lodash-set/LICENSE` for more information.
set -e
source ./scripts/_get_lodash.sh
all_files=$(cd lodash && ls)
modified_lodash_files=(_baseSet.js)
# Get fresh copies of all the files that was originally copied from lodash,
# expect the ones in the whitelist
for file in $all_files
do
if [[ ! "${modified_lodash_files[@]}" =~ "${file}" ]]
then
cat scripts/license-header.txt > "lodash/$file"
printf "/* eslint-disable */\n\n" >> "lodash/$file"
cat ".tmp/node_modules/lodash/$file" >> "lodash/$file"
fi
done
# Check if there's changes to the patched files
for file in "${modified_lodash_files[@]}"
do
diff ".tmp/node_modules/lodash/$file" "lodash/$file" > ".tmp/$file.patch" || true
if [[ $(diff ".tmp/$file.patch" "scripts/patches/$file.patch") ]]; then
echo "WARNING: The modified file $file have changed in a newer version of lodash, but was not updated:"
echo "------------------------------------------------------------------------"
diff ".tmp/$file.patch" "scripts/patches/$file.patch" || true
echo "------------------------------------------------------------------------"
fi
done
echo "Update complete!"

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { set } from './index';
export = set;

View file

@ -0,0 +1,8 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
module.exports = require('./lodash/set');

View file

@ -0,0 +1,9 @@
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { setWith } from './index';
export = setWith;

View file

@ -0,0 +1,8 @@
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
module.exports = require('./lodash/setWith');

View file

@ -0,0 +1,85 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import { set, setWith, assoc, assocPath } from '../fp';
const someObj: object = {};
const anyValue: any = 'any value';
function customizer(value: any, key: string, obj: object) {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
}
expectType<object>(set('a.b.c', anyValue, someObj));
expectType<object>(set('a.b.c')(anyValue, someObj));
expectType<object>(set('a.b.c')(anyValue)(someObj));
expectType<object>(set('a.b.c', anyValue)(someObj));
expectType<object>(set(['a.b.c'], anyValue, someObj));
expectType<object>(set(['a.b.c'])(anyValue, someObj));
expectType<object>(set(['a.b.c'])(anyValue)(someObj));
expectType<object>(set(['a.b.c'], anyValue)(someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')], anyValue)(someObj));
expectType<object>(assoc('a.b.c', anyValue, someObj));
expectType<object>(assoc('a.b.c')(anyValue, someObj));
expectType<object>(assoc('a.b.c')(anyValue)(someObj));
expectType<object>(assoc('a.b.c', anyValue)(someObj));
expectType<object>(assoc(['a.b.c'], anyValue, someObj));
expectType<object>(assoc(['a.b.c'])(anyValue, someObj));
expectType<object>(assoc(['a.b.c'])(anyValue)(someObj));
expectType<object>(assoc(['a.b.c'], anyValue)(someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')], anyValue)(someObj));
expectType<object>(assocPath('a.b.c', anyValue, someObj));
expectType<object>(assocPath('a.b.c')(anyValue, someObj));
expectType<object>(assocPath('a.b.c')(anyValue)(someObj));
expectType<object>(assocPath('a.b.c', anyValue)(someObj));
expectType<object>(assocPath(['a.b.c'], anyValue, someObj));
expectType<object>(assocPath(['a.b.c'])(anyValue, someObj));
expectType<object>(assocPath(['a.b.c'])(anyValue)(someObj));
expectType<object>(assocPath(['a.b.c'], anyValue)(someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')], anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c', anyValue, someObj));
expectType<object>(setWith(customizer)('a.b.c', anyValue, someObj));
expectType<object>(setWith(customizer)('a.b.c')(anyValue, someObj));
expectType<object>(setWith(customizer)('a.b.c')(anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c')(anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c', anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c')(anyValue, someObj));
expectType<object>(setWith(customizer, ['a.b.c'], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c'])(anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c'], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c'])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c'])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c'], anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c'])(anyValue, someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')], anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')])(anyValue, someObj));

View file

@ -0,0 +1,25 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import assoc from '../fp/assoc';
const someObj: object = {};
const anyValue: any = 'any value';
expectType<object>(assoc('a.b.c', anyValue, someObj));
expectType<object>(assoc('a.b.c')(anyValue, someObj));
expectType<object>(assoc('a.b.c')(anyValue)(someObj));
expectType<object>(assoc('a.b.c', anyValue)(someObj));
expectType<object>(assoc(['a.b.c'], anyValue, someObj));
expectType<object>(assoc(['a.b.c'])(anyValue, someObj));
expectType<object>(assoc(['a.b.c'])(anyValue)(someObj));
expectType<object>(assoc(['a.b.c'], anyValue)(someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(assoc(['a.b.c', 2, Symbol('hep')], anyValue)(someObj));

View file

@ -0,0 +1,25 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import assocPath from '../fp/assocPath';
const someObj: object = {};
const anyValue: any = 'any value';
expectType<object>(assocPath('a.b.c', anyValue, someObj));
expectType<object>(assocPath('a.b.c')(anyValue, someObj));
expectType<object>(assocPath('a.b.c')(anyValue)(someObj));
expectType<object>(assocPath('a.b.c', anyValue)(someObj));
expectType<object>(assocPath(['a.b.c'], anyValue, someObj));
expectType<object>(assocPath(['a.b.c'])(anyValue, someObj));
expectType<object>(assocPath(['a.b.c'])(anyValue)(someObj));
expectType<object>(assocPath(['a.b.c'], anyValue)(someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(assocPath(['a.b.c', 2, Symbol('hep')], anyValue)(someObj));

View file

@ -0,0 +1,290 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
const test = require('tape');
const setFunctions = [
[testSet, require('../fp').set, 'fp.set'],
[testSet, require('../fp/set'), 'fp/set'],
[testSet, require('../fp').assoc, 'fp.assoc'],
[testSet, require('../fp/assoc'), 'fp/assoc'],
[testSet, require('../fp').assocPath, 'fp.assocPath'],
[testSet, require('../fp/assocPath'), 'fp/assocPath'],
[testSetWithAsSet, require('../fp').setWith, 'fp.setWith'],
[testSetWithAsSet, require('../fp/setWith'), 'fp/setWith'],
];
const setWithFunctions = [
[testSetWith, require('../fp').setWith, 'fp.setWith'],
[testSetWith, require('../fp/setWith'), 'fp/setWith'],
];
function testSet(fn, args, onCall) {
const [a, b, c] = args;
onCall(fn(b, c, a));
onCall(fn(b, c)(a));
onCall(fn(b)(c, a));
onCall(fn(b)(c)(a));
}
testSet.assertionCalls = 4;
function testSetWith(fn, args, onCall) {
const [a, b, c, d] = args;
onCall(fn(d, b, c, a));
onCall(fn(d)(b, c, a));
onCall(fn(d)(b)(c, a));
onCall(fn(d)(b)(c)(a));
onCall(fn(d, b)(c)(a));
onCall(fn(d, b, c)(a));
onCall(fn(d)(b, c)(a));
}
testSetWith.assertionCalls = 7;
// use `fp.setWith` with the same API as `fp.set` by injecting a noop function as the first argument
function testSetWithAsSet(fn, args, onCall) {
args.push(() => {});
testSetWith(fn, args, onCall);
}
testSetWithAsSet.assertionCalls = testSetWith.assertionCalls;
setFunctions.forEach(([testPermutations, set, testName]) => {
/**
* GENERAL USAGE TESTS
*/
const isSetWith = testPermutations.name === 'testSetWithAsSet';
test(`${testName}: No side-effects`, (t) => {
t.plan(testPermutations.assertionCalls * 5);
const o1 = {
a: { b: 1 },
c: { d: 2 },
};
testPermutations(set, [o1, 'a.b', 3], (o2) => {
t.notStrictEqual(o1, o2); // clone touched paths
t.notStrictEqual(o1.a, o2.a); // clone touched paths
t.deepEqual(o1.c, o2.c); // do not clone untouched paths
t.deepEqual(o1, { a: { b: 1 }, c: { d: 2 } });
t.deepEqual(o2, { a: { b: 3 }, c: { d: 2 } });
});
});
test(`${testName}: Non-objects`, (t) => {
const nonObjects = [null, undefined, NaN, 42];
t.plan(testPermutations.assertionCalls * nonObjects.length * 3);
nonObjects.forEach((nonObject) => {
t.comment(String(nonObject));
testPermutations(set, [nonObject, 'a.b', 'foo'], (result) => {
if (Number.isNaN(nonObject)) {
t.ok(result instanceof Number);
t.strictEqual(result.toString(), 'NaN');
t.deepEqual(result, Object.assign(NaN, { a: { b: 'foo' } })); // will produce new object due to cloning
} else if (nonObject === 42) {
t.ok(result instanceof Number);
t.strictEqual(result.toString(), '42');
t.deepEqual(result, Object.assign(42, { a: { b: 'foo' } })); // will produce new object due to cloning
} else {
t.ok(result instanceof Object);
t.strictEqual(result.toString(), '[object Object]');
t.deepEqual(result, { a: { b: 'foo' } }); // will produce new object due to cloning
}
});
});
});
test(`${testName}: Overwrites existing object properties`, (t) => {
t.plan(testPermutations.assertionCalls);
testPermutations(set, [{ a: { b: { c: 3 } } }, 'a.b', 'foo'], (result) => {
t.deepEqual(result, { a: { b: 'foo' } });
});
});
test(`${testName}: Adds missing properties without touching other areas`, (t) => {
t.plan(testPermutations.assertionCalls);
testPermutations(
set,
[{ a: [{ aa: { aaa: 3, aab: 4 } }, { ab: 2 }], b: 1 }, 'a[0].aa.aaa.aaaa', 'foo'],
(result) => {
t.deepEqual(result, {
a: [{ aa: { aaa: Object.assign(3, { aaaa: 'foo' }), aab: 4 } }, { ab: 2 }],
b: 1,
});
}
);
});
test(`${testName}: Overwrites existing elements in array`, (t) => {
t.plan(testPermutations.assertionCalls);
testPermutations(set, [{ a: [1, 2, 3] }, 'a[1]', 'foo'], (result) => {
t.deepEqual(result, { a: [1, 'foo', 3] });
});
});
test(`${testName}: Create new array`, (t) => {
t.plan(testPermutations.assertionCalls);
testPermutations(set, [{}, ['x', '0', 'y', 'z'], 'foo'], (result) => {
t.deepEqual(result, { x: [{ y: { z: 'foo' } }] });
});
});
/**
* PROTOTYPE POLLUTION PROTECTION TESTS
*/
const testCases = [
['__proto__', { ['__proto__']: 'foo' }],
['.__proto__', { '': { ['__proto__']: 'foo' } }],
['o.__proto__', { o: { ['__proto__']: 'foo' } }],
['a[0].__proto__', { a: [{ ['__proto__']: 'foo' }] }],
['constructor', { constructor: 'foo' }],
['.constructor', { '': { constructor: 'foo' } }],
['o.constructor', { o: { constructor: 'foo' } }],
['a[0].constructor', { a: [{ constructor: 'foo' }] }],
['constructor.something', { constructor: { something: 'foo' } }],
['.constructor.something', { '': { constructor: { something: 'foo' } } }],
['o.constructor.something', { o: { constructor: { something: 'foo' } } }],
['a[0].constructor.something', { a: [{ constructor: { something: 'foo' } }] }],
['prototype', { prototype: 'foo' }],
['.prototype', { '': { prototype: 'foo' } }],
['o.prototype', { o: { prototype: 'foo' } }],
['a[0].prototype', { a: [{ prototype: 'foo' }] }],
['constructor.prototype', { constructor: { prototype: 'foo' } }],
['.constructor.prototype', { '': { constructor: { prototype: 'foo' } } }],
['o.constructor.prototype', { o: { constructor: { prototype: 'foo' } } }],
['a[0].constructor.prototype', { a: [{ constructor: { prototype: 'foo' } }] }],
['constructor.something.prototype', { constructor: { something: { prototype: 'foo' } } }],
[
'.constructor.something.prototype',
{ '': { constructor: { something: { prototype: 'foo' } } } },
],
[
'o.constructor.something.prototype',
{ o: { constructor: { something: { prototype: 'foo' } } } },
],
[
'a[0].constructor.something.prototype',
{ a: [{ constructor: { something: { prototype: 'foo' } } }] },
],
];
testCases.forEach(([path, expected]) => {
test(`${testName}: Object manipulation, ${path}`, (t) => {
t.plan(testPermutations.assertionCalls);
testPermutations(set, [{}, path, 'foo'], (result) => {
t.deepLooseEqual(result, expected); // Use loose check because the prototype of result isn't Object.prototype
});
});
});
testCases.forEach(([path, expected]) => {
test(`${testName}: Array manipulation, ${path}`, (t) => {
t.plan(testPermutations.assertionCalls * 4);
const arr = [];
testPermutations(set, [arr, path, 'foo'], (result) => {
t.notStrictEqual(arr, result);
t.ok(Array.isArray(result));
Object.keys(expected).forEach((key) => {
t.ok(Object.prototype.hasOwnProperty.call(result, key));
t.deepEqual(result[key], expected[key]);
});
});
});
});
test(`${testName}: Function manipulation, object containing function`, (t) => {
const funcTestCases = [
[{ fn: function () {} }, 'fn.prototype'],
[{ fn: () => {} }, 'fn.prototype'],
];
const expected = /Illegal access of function prototype/;
t.plan((isSetWith ? 7 : 4) * funcTestCases.length);
funcTestCases.forEach(([obj, path]) => {
if (isSetWith) {
t.throws(() => set(() => {}, path, 'foo', obj), expected);
t.throws(() => set(() => {})(path, 'foo', obj), expected);
t.throws(() => set(() => {})(path)('foo', obj), expected);
t.throws(() => set(() => {})(path)('foo')(obj), expected);
t.throws(() => set(() => {}, path)('foo')(obj), expected);
t.throws(() => set(() => {}, path, 'foo')(obj), expected);
t.throws(() => set(() => {})(path, 'foo')(obj), expected);
} else {
t.throws(() => set(path, 'foo', obj), expected);
t.throws(() => set(path, 'foo')(obj), expected);
t.throws(() => set(path)('foo', obj), expected);
t.throws(() => set(path)('foo')(obj), expected);
}
});
});
test(`${testName}: Function manipulation, arrow function`, (t) => {
// This doesn't really make sense to do with the `fp` variant of lodash, as it will return a regular non-function object
t.plan(testPermutations.assertionCalls * 2);
const obj = () => {};
testPermutations(set, [obj, 'prototype', 'foo'], (result) => {
t.notStrictEqual(result, obj);
t.strictEqual(result.prototype, 'foo');
});
});
test(`${testName}: Function manipulation, regular function`, (t) => {
// This doesn't really make sense to do with the `fp` variant of lodash, as it will return a regular non-function object
t.plan(testPermutations.assertionCalls * 2);
const obj = function () {};
testPermutations(set, [obj, 'prototype', 'foo'], (result) => {
t.notStrictEqual(result, obj);
t.strictEqual(result.prototype, 'foo');
});
});
});
/**
* setWith specific tests
*/
setWithFunctions.forEach(([testPermutations, setWith, testName]) => {
test(`${testName}: Return undefined`, (t) => {
t.plan(testPermutations.assertionCalls);
testPermutations(setWith, [{}, 'a.b', 'foo', () => {}], (result) => {
t.deepEqual(result, { a: { b: 'foo' } });
});
});
test(`${testName}: Customizer arguments`, (t) => {
let i = 0;
const expectedCustomizerArgs = [
[{ b: Object(42) }, 'a', { a: { b: Object(42) } }],
[Object(42), 'b', { b: Object(42) }],
];
t.plan(testPermutations.assertionCalls * (expectedCustomizerArgs.length + 1));
testPermutations(
setWith,
[
{ a: { b: 42 } },
'a.b.c',
'foo',
(...args) => {
t.deepEqual(
args,
expectedCustomizerArgs[i++ % 2],
'customizer args should be as expected'
);
},
],
(result) => {
t.deepEqual(result, { a: { b: Object.assign(42, { c: 'foo' }) } });
}
);
});
test(`${testName}: Return value`, (t) => {
t.plan(testPermutations.assertionCalls);
testSetWith(setWith, [{}, '[0][1]', 'a', Object], (result) => {
t.deepEqual(result, { 0: { 1: 'a' } });
});
});
});

View file

@ -0,0 +1,25 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import set from '../fp/set';
const someObj: object = {};
const anyValue: any = 'any value';
expectType<object>(set('a.b.c', anyValue, someObj));
expectType<object>(set('a.b.c')(anyValue, someObj));
expectType<object>(set('a.b.c')(anyValue)(someObj));
expectType<object>(set('a.b.c', anyValue)(someObj));
expectType<object>(set(['a.b.c'], anyValue, someObj));
expectType<object>(set(['a.b.c'])(anyValue, someObj));
expectType<object>(set(['a.b.c'])(anyValue)(someObj));
expectType<object>(set(['a.b.c'], anyValue)(someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(set(['a.b.c', 2, Symbol('hep')], anyValue)(someObj));

View file

@ -0,0 +1,40 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import setWith from '../fp/setWith';
const someObj: object = {};
const anyValue: any = 'any value';
function customizer(value: any, key: string, obj: object) {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
}
expectType<object>(setWith(customizer, 'a.b.c', anyValue, someObj));
expectType<object>(setWith(customizer)('a.b.c', anyValue, someObj));
expectType<object>(setWith(customizer)('a.b.c')(anyValue, someObj));
expectType<object>(setWith(customizer)('a.b.c')(anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c')(anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c', anyValue)(someObj));
expectType<object>(setWith(customizer, 'a.b.c')(anyValue, someObj));
expectType<object>(setWith(customizer, ['a.b.c'], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c'])(anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c'], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c'])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c'])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c'], anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c'])(anyValue, someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c', 2, Symbol('hep')])(anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c', 2, Symbol('hep')], anyValue, someObj));
expectType<object>(setWith(customizer)(['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')])(anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')], anyValue)(someObj));
expectType<object>(setWith(customizer, ['a.b.c', 2, Symbol('hep')])(anyValue, someObj));

View file

@ -0,0 +1,37 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import { set, setWith } from '../';
const someObj: object = {};
const anyValue: any = 'any value';
expectType<object>(set(someObj, 'a.b.c', anyValue));
expectType<object>(
setWith(someObj, 'a.b.c', anyValue, (value, key, obj) => {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
})
);
expectType<object>(set(someObj, ['a.b.c'], anyValue));
expectType<object>(
setWith(someObj, ['a.b.c'], anyValue, (value, key, obj) => {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
})
);
expectType<object>(set(someObj, ['a.b.c', 2, Symbol('hep')], anyValue));
expectType<object>(
setWith(someObj, ['a.b.c', 2, Symbol('hep')], anyValue, (value, key, obj) => {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
})
);

View file

@ -0,0 +1,174 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
const test = require('tape');
const setFunctions = [
[require('../').set, 'module.set'],
[require('../set'), 'module/set'],
];
const setWithFunctions = [
[require('../').setWith, 'module.setWith'],
[require('../setWith'), 'module/setWith'],
];
const setAndSetWithFunctions = [].concat(setFunctions, setWithFunctions);
setAndSetWithFunctions.forEach(([set, testName]) => {
/**
* GENERAL USAGE TESTS
*/
test(`${testName}: Returns same object`, (t) => {
const o1 = {};
const o2 = set(o1, 'foo', 'bar');
t.strictEqual(o1, o2);
t.end();
});
test(`${testName}: Non-objects`, (t) => {
t.strictEqual(set(null, 'a.b', 'foo'), null);
t.strictEqual(set(undefined, 'a.b', 'foo'), undefined);
t.strictEqual(set(NaN, 'a.b', 'foo'), NaN);
t.strictEqual(set(42, 'a.b', 'foo'), 42);
t.end();
});
test(`${testName}: Overwrites existing object properties`, (t) => {
t.deepEqual(set({ a: { b: { c: 3 } } }, 'a.b', 'foo'), { a: { b: 'foo' } });
t.end();
});
test(`${testName}: Adds missing properties without touching other areas`, (t) => {
t.deepEqual(
set({ a: [{ aa: { aaa: 3, aab: 4 } }, { ab: 2 }], b: 1 }, 'a[0].aa.aaa.aaaa', 'foo'),
{ a: [{ aa: { aaa: { aaaa: 'foo' }, aab: 4 } }, { ab: 2 }], b: 1 }
);
t.end();
});
test(`${testName}: Overwrites existing elements in array`, (t) => {
t.deepEqual(set({ a: [1, 2, 3] }, 'a[1]', 'foo'), { a: [1, 'foo', 3] });
t.end();
});
test(`${testName}: Create new array`, (t) => {
t.deepEqual(set({}, ['x', '0', 'y', 'z'], 'foo'), { x: [{ y: { z: 'foo' } }] });
t.end();
});
/**
* PROTOTYPE POLLUTION PROTECTION TESTS
*/
const testCases = [
['__proto__', { ['__proto__']: 'foo' }],
['.__proto__', { '': { ['__proto__']: 'foo' } }],
['o.__proto__', { o: { ['__proto__']: 'foo' } }],
['a[0].__proto__', { a: [{ ['__proto__']: 'foo' }] }],
['constructor', { constructor: 'foo' }],
['.constructor', { '': { constructor: 'foo' } }],
['o.constructor', { o: { constructor: 'foo' } }],
['a[0].constructor', { a: [{ constructor: 'foo' }] }],
['constructor.something', { constructor: { something: 'foo' } }],
['.constructor.something', { '': { constructor: { something: 'foo' } } }],
['o.constructor.something', { o: { constructor: { something: 'foo' } } }],
['a[0].constructor.something', { a: [{ constructor: { something: 'foo' } }] }],
['prototype', { prototype: 'foo' }],
['.prototype', { '': { prototype: 'foo' } }],
['o.prototype', { o: { prototype: 'foo' } }],
['a[0].prototype', { a: [{ prototype: 'foo' }] }],
['constructor.prototype', { constructor: { prototype: 'foo' } }],
['.constructor.prototype', { '': { constructor: { prototype: 'foo' } } }],
['o.constructor.prototype', { o: { constructor: { prototype: 'foo' } } }],
['a[0].constructor.prototype', { a: [{ constructor: { prototype: 'foo' } }] }],
['constructor.something.prototype', { constructor: { something: { prototype: 'foo' } } }],
[
'.constructor.something.prototype',
{ '': { constructor: { something: { prototype: 'foo' } } } },
],
[
'o.constructor.something.prototype',
{ o: { constructor: { something: { prototype: 'foo' } } } },
],
[
'a[0].constructor.something.prototype',
{ a: [{ constructor: { something: { prototype: 'foo' } } }] },
],
];
testCases.forEach(([path, expected]) => {
test(`${testName}: Object manipulation, ${path}`, (t) => {
t.deepEqual(set({}, path, 'foo'), expected);
t.end();
});
});
testCases.forEach(([path, expected]) => {
test(`${testName}: Array manipulation, ${path}`, (t) => {
const arr = [];
set(arr, path, 'foo');
Object.keys(expected).forEach((key) => {
t.ok(Object.prototype.hasOwnProperty.call(arr, key));
t.deepEqual(arr[key], expected[key]);
});
t.end();
});
});
test(`${testName}: Function manipulation`, (t) => {
const funcTestCases = [
[function () {}, 'prototype'],
[() => {}, 'prototype'],
[{ fn: function () {} }, 'fn.prototype'],
[{ fn: () => {} }, 'fn.prototype'],
];
funcTestCases.forEach(([obj, path]) => {
t.throws(() => set(obj, path, 'foo'), /Illegal access of function prototype/);
});
t.end();
});
});
/**
* setWith specific tests
*/
setWithFunctions.forEach(([setWith, testName]) => {
test(`${testName}: Return undefined`, (t) => {
t.deepEqual(
setWith({}, 'a.b', 'foo', () => {}),
{ a: { b: 'foo' } }
);
t.end();
});
test(`${testName}: Customizer arguments`, (t) => {
t.plan(3);
const expectedCustomizerArgs = [
[{ b: 42 }, 'a', { a: { b: 42 } }],
[42, 'b', { b: 42 }],
];
t.deepEqual(
setWith({ a: { b: 42 } }, 'a.b.c', 'foo', (...args) => {
t.deepEqual(args, expectedCustomizerArgs.shift());
}),
{ a: { b: { c: 'foo' } } }
);
t.end();
});
test(`${testName}: Return value`, (t) => {
t.deepEqual(setWith({}, '[0][1]', 'a', Object), { 0: { 1: 'a' } });
t.end();
});
});

View file

@ -0,0 +1,14 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import set from '../set';
const someObj: object = {};
const anyValue: any = 'any value';
expectType<object>(set(someObj, 'a.b.c', anyValue));
expectType<object>(set(someObj, ['a.b.c'], anyValue));
expectType<object>(set(someObj, ['a.b.c', 2, Symbol('hep')], anyValue));

View file

@ -0,0 +1,32 @@
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See `packages/elastic-safer-lodash-set/LICENSE` for more information.
*/
import { expectType } from 'tsd';
import setWith from '../setWith';
const someObj: object = {};
const anyValue: any = 'any value';
expectType<object>(
setWith(someObj, 'a.b.c', anyValue, (value, key, obj) => {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
})
);
expectType<object>(
setWith(someObj, ['a.b.c'], anyValue, (value, key, obj) => {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
})
);
expectType<object>(
setWith(someObj, ['a.b.c', 2, Symbol('hep')], anyValue, (value, key, obj) => {
expectType<any>(value);
expectType<string>(key);
expectType<object>(obj);
})
);

View file

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"include": [
"**/*"
],
"exclude": [
"**/*.test-d.ts"
]
}

View file

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import * as _ from 'lodash'; import { cloneDeep } from 'lodash';
import * as ts from 'typescript'; import * as ts from 'typescript';
import { parsedWorkingCollector } from './__fixture__/parsed_working_collector'; import { parsedWorkingCollector } from './__fixture__/parsed_working_collector';
import { checkCompatibleTypeDescriptor, checkMatchingMapping } from './check_collector_integrity'; import { checkCompatibleTypeDescriptor, checkMatchingMapping } from './check_collector_integrity';
@ -42,7 +42,7 @@ describe('checkMatchingMapping', () => {
describe('Collector change', () => { describe('Collector change', () => {
it('returns diff on mismatching parsedCollections and stored mapping', async () => { it('returns diff on mismatching parsedCollections and stored mapping', async () => {
const mockSchema = await parseJsonFile('mock_schema.json'); const mockSchema = await parseJsonFile('mock_schema.json');
const malformedParsedCollector = _.cloneDeep(parsedWorkingCollector); const malformedParsedCollector = cloneDeep(parsedWorkingCollector);
const fieldMapping = { type: 'number' }; const fieldMapping = { type: 'number' };
malformedParsedCollector[1].schema.value.flat = fieldMapping; malformedParsedCollector[1].schema.value.flat = fieldMapping;
@ -58,7 +58,7 @@ describe('checkMatchingMapping', () => {
it('returns diff on unknown parsedCollections', async () => { it('returns diff on unknown parsedCollections', async () => {
const mockSchema = await parseJsonFile('mock_schema.json'); const mockSchema = await parseJsonFile('mock_schema.json');
const malformedParsedCollector = _.cloneDeep(parsedWorkingCollector); const malformedParsedCollector = cloneDeep(parsedWorkingCollector);
const collectorName = 'New Collector in town!'; const collectorName = 'New Collector in town!';
const collectorMapping = { some_usage: { type: 'number' } }; const collectorMapping = { some_usage: { type: 'number' } };
malformedParsedCollector[1].collectorName = collectorName; malformedParsedCollector[1].collectorName = collectorName;
@ -84,7 +84,7 @@ describe('checkCompatibleTypeDescriptor', () => {
describe('Interface Change', () => { describe('Interface Change', () => {
it('returns diff on incompatible type descriptor with mapping', () => { it('returns diff on incompatible type descriptor with mapping', () => {
const malformedParsedCollector = _.cloneDeep(parsedWorkingCollector); const malformedParsedCollector = cloneDeep(parsedWorkingCollector);
malformedParsedCollector[1].fetch.typeDescriptor.flat.kind = ts.SyntaxKind.BooleanKeyword; malformedParsedCollector[1].fetch.typeDescriptor.flat.kind = ts.SyntaxKind.BooleanKeyword;
const incompatibles = checkCompatibleTypeDescriptor([malformedParsedCollector]); const incompatibles = checkCompatibleTypeDescriptor([malformedParsedCollector]);
expect(incompatibles).toHaveLength(1); expect(incompatibles).toHaveLength(1);
@ -101,14 +101,14 @@ describe('checkCompatibleTypeDescriptor', () => {
describe('Mapping change', () => { describe('Mapping change', () => {
it('returns no diff when mapping change between text and keyword', () => { it('returns no diff when mapping change between text and keyword', () => {
const malformedParsedCollector = _.cloneDeep(parsedWorkingCollector); const malformedParsedCollector = cloneDeep(parsedWorkingCollector);
malformedParsedCollector[1].schema.value.flat.type = 'text'; malformedParsedCollector[1].schema.value.flat.type = 'text';
const incompatibles = checkCompatibleTypeDescriptor([malformedParsedCollector]); const incompatibles = checkCompatibleTypeDescriptor([malformedParsedCollector]);
expect(incompatibles).toHaveLength(0); expect(incompatibles).toHaveLength(0);
}); });
it('returns diff on incompatible type descriptor with mapping', () => { it('returns diff on incompatible type descriptor with mapping', () => {
const malformedParsedCollector = _.cloneDeep(parsedWorkingCollector); const malformedParsedCollector = cloneDeep(parsedWorkingCollector);
malformedParsedCollector[1].schema.value.flat.type = 'boolean'; malformedParsedCollector[1].schema.value.flat.type = 'boolean';
const incompatibles = checkCompatibleTypeDescriptor([malformedParsedCollector]); const incompatibles = checkCompatibleTypeDescriptor([malformedParsedCollector]);
expect(incompatibles).toHaveLength(1); expect(incompatibles).toHaveLength(1);

View file

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import * as _ from 'lodash'; import { reduce } from 'lodash';
import { difference, flattenKeys, pickDeep } from './utils'; import { difference, flattenKeys, pickDeep } from './utils';
import { ParsedUsageCollection } from './ts_parser'; import { ParsedUsageCollection } from './ts_parser';
import { generateMapping, compatibleSchemaTypes } from './manage_schema'; import { generateMapping, compatibleSchemaTypes } from './manage_schema';
@ -44,7 +44,7 @@ export function checkCompatibleTypeDescriptor(
const typeDescriptorTypes = flattenKeys( const typeDescriptorTypes = flattenKeys(
pickDeep(collectorDetails.fetch.typeDescriptor, 'kind') pickDeep(collectorDetails.fetch.typeDescriptor, 'kind')
); );
const typeDescriptorKinds = _.reduce( const typeDescriptorKinds = reduce(
typeDescriptorTypes, typeDescriptorTypes,
(acc: any, type: number, key: string) => { (acc: any, type: number, key: string) => {
try { try {
@ -58,7 +58,7 @@ export function checkCompatibleTypeDescriptor(
); );
const schemaTypes = flattenKeys(pickDeep(collectorDetails.schema.value, 'type')); const schemaTypes = flattenKeys(pickDeep(collectorDetails.schema.value, 'type'));
const transformedMappingKinds = _.reduce( const transformedMappingKinds = reduce(
schemaTypes, schemaTypes,
(acc: any, type: string, key: string) => { (acc: any, type: string, key: string) => {
try { try {

View file

@ -17,7 +17,6 @@
* under the License. * under the License.
*/ */
import * as _ from 'lodash';
import { TaskContext } from './task_context'; import { TaskContext } from './task_context';
import { generateMapping } from '../manage_schema'; import { generateMapping } from '../manage_schema';

View file

@ -18,7 +18,7 @@
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import * as _ from 'lodash'; import { pick, isObject, each, isArray, reduce, isEmpty, merge, transform, isEqual } from 'lodash';
import * as path from 'path'; import * as path from 'path';
import glob from 'glob'; import glob from 'glob';
import { readFile, writeFile } from 'fs'; import { readFile, writeFile } from 'fs';
@ -178,17 +178,17 @@ export function getPropertyValue(
} }
export function pickDeep(collection: any, identity: any, thisArg?: any) { export function pickDeep(collection: any, identity: any, thisArg?: any) {
const picked: any = _.pick(collection, identity, thisArg); const picked: any = pick(collection, identity, thisArg);
const collections = _.pick(collection, _.isObject, thisArg); const collections = pick(collection, isObject, thisArg);
_.each(collections, function (item, key) { each(collections, function (item, key) {
let object; let object;
if (_.isArray(item)) { if (isArray(item)) {
object = _.reduce( object = reduce(
item, item,
function (result, value) { function (result, value) {
const pickedDeep = pickDeep(value, identity, thisArg); const pickedDeep = pickDeep(value, identity, thisArg);
if (!_.isEmpty(pickedDeep)) { if (!isEmpty(pickedDeep)) {
result.push(pickedDeep); result.push(pickedDeep);
} }
return result; return result;
@ -199,7 +199,7 @@ export function pickDeep(collection: any, identity: any, thisArg?: any) {
object = pickDeep(item, identity, thisArg); object = pickDeep(item, identity, thisArg);
} }
if (!_.isEmpty(object)) { if (!isEmpty(object)) {
picked[key || ''] = object; picked[key || ''] = object;
} }
}); });
@ -208,12 +208,12 @@ export function pickDeep(collection: any, identity: any, thisArg?: any) {
} }
export const flattenKeys = (obj: any, keyPath: any[] = []): any => { export const flattenKeys = (obj: any, keyPath: any[] = []): any => {
if (_.isObject(obj)) { if (isObject(obj)) {
return _.reduce( return reduce(
obj, obj,
(cum, next, key) => { (cum, next, key) => {
const keys = [...keyPath, key]; const keys = [...keyPath, key];
return _.merge(cum, flattenKeys(next, keys)); return merge(cum, flattenKeys(next, keys));
}, },
{} {}
); );
@ -223,10 +223,9 @@ export const flattenKeys = (obj: any, keyPath: any[] = []): any => {
export function difference(actual: any, expected: any) { export function difference(actual: any, expected: any) {
function changes(obj: any, base: any) { function changes(obj: any, base: any) {
return _.transform(obj, function (result, value, key) { return transform(obj, function (result, value, key) {
if (key && !_.isEqual(value, base[key])) { if (key && !isEqual(value, base[key])) {
result[key] = result[key] = isObject(value) && isObject(base[key]) ? changes(value, base[key]) : value;
_.isObject(value) && _.isObject(base[key]) ? changes(value, base[key]) : value;
} }
}); });
} }

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import Chalk from 'chalk'; import Chalk from 'chalk';
@ -86,7 +87,7 @@ Command.prototype.collectUnknownOptions = function () {
val = opt[1]; val = opt[1];
} }
_.set(opts, opt[0].slice(2), val); set(opts, opt[0].slice(2), val);
} }
return opts; return opts;

View file

@ -18,7 +18,7 @@
*/ */
import path from 'path'; import path from 'path';
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { Keystore } from '../../legacy/server/keystore'; import { Keystore } from '../../legacy/server/keystore';
import { getDataPath } from '../../core/server/path'; import { getDataPath } from '../../core/server/path';

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set as lodashSet } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { statSync } from 'fs'; import { statSync } from 'fs';
import { resolve } from 'path'; import { resolve } from 'path';
@ -65,7 +66,7 @@ const pluginDirCollector = pathCollector();
const pluginPathCollector = pathCollector(); const pluginPathCollector = pathCollector();
function applyConfigOverrides(rawConfig, opts, extraCliOptions) { function applyConfigOverrides(rawConfig, opts, extraCliOptions) {
const set = _.partial(_.set, rawConfig); const set = _.partial(lodashSet, rawConfig);
const get = _.partial(_.get, rawConfig); const get = _.partial(_.get, rawConfig);
const has = _.partial(_.has, rawConfig); const has = _.partial(_.has, rawConfig);
const merge = _.partial(_.merge, rawConfig); const merge = _.partial(_.merge, rawConfig);

View file

@ -17,7 +17,8 @@
* under the License. * under the License.
*/ */
import { get, has, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get, has } from 'lodash';
import { SavedObject as SavedObjectType } from '../../server'; import { SavedObject as SavedObjectType } from '../../server';
import { SavedObjectsClientContract } from './saved_objects_client'; import { SavedObjectsClientContract } from './saved_objects_client';

View file

@ -17,7 +17,8 @@
* under the License. * under the License.
*/ */
import { get, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get } from 'lodash';
import { ConfigDeprecation, ConfigDeprecationLogger, ConfigDeprecationFactory } from './types'; import { ConfigDeprecation, ConfigDeprecationLogger, ConfigDeprecationFactory } from './types';
import { unset } from '../../../utils'; import { unset } from '../../../utils';

View file

@ -17,7 +17,8 @@
* under the License. * under the License.
*/ */
import { cloneDeep, get, has, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { cloneDeep, get, has } from 'lodash';
import { getFlattenedObject } from '../../utils'; import { getFlattenedObject } from '../../utils';
import { Config, ConfigPath } from './'; import { Config, ConfigPath } from './';

View file

@ -20,7 +20,8 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { safeLoad } from 'js-yaml'; import { safeLoad } from 'js-yaml';
import { isPlainObject, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { isPlainObject } from 'lodash';
import { ensureDeepObject } from './ensure_deep_object'; import { ensureDeepObject } from './ensure_deep_object';
const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8')); const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8'));

View file

@ -17,7 +17,8 @@
* under the License. * under the License.
*/ */
import { difference, get, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { difference, get } from 'lodash';
// @ts-expect-error // @ts-expect-error
import { getTransform } from '../../../../legacy/deprecation/index'; import { getTransform } from '../../../../legacy/deprecation/index';
import { unset } from '../../../../legacy/utils'; import { unset } from '../../../../legacy/utils';

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { SavedObjectUnsanitizedDoc } from '../../serialization'; import { SavedObjectUnsanitizedDoc } from '../../serialization';
import { DocumentMigrator } from './document_migrator'; import { DocumentMigrator } from './document_migrator';
@ -132,7 +133,7 @@ describe('DocumentMigrator', () => {
name: 'user', name: 'user',
migrations: { migrations: {
'1.2.3': (doc) => { '1.2.3': (doc) => {
_.set(doc, 'attributes.name', 'Mike'); set(doc, 'attributes.name', 'Mike');
return doc; return doc;
}, },
}, },
@ -639,7 +640,7 @@ describe('DocumentMigrator', () => {
typeRegistry: createRegistry({ typeRegistry: createRegistry({
name: 'aaa', name: 'aaa',
migrations: { migrations: {
'2.3.4': (d) => _.set(d, 'attributes.counter', 42), '2.3.4': (d) => set(d, 'attributes.counter', 42),
}, },
}), }),
validateDoc: (d) => { validateDoc: (d) => {
@ -657,12 +658,12 @@ describe('DocumentMigrator', () => {
function renameAttr(path: string, newPath: string) { function renameAttr(path: string, newPath: string) {
return (doc: SavedObjectUnsanitizedDoc) => return (doc: SavedObjectUnsanitizedDoc) =>
_.omit(_.set(doc, newPath, _.get(doc, path)) as {}, path) as SavedObjectUnsanitizedDoc; _.omit(set(doc, newPath, _.get(doc, path)) as {}, path) as SavedObjectUnsanitizedDoc;
} }
function setAttr(path: string, value: any) { function setAttr(path: string, value: any) {
return (doc: SavedObjectUnsanitizedDoc) => return (doc: SavedObjectUnsanitizedDoc) =>
_.set( set(
doc, doc,
path, path,
_.isFunction(value) ? value(_.get(doc, path)) : value _.isFunction(value) ? value(_.get(doc, path)) : value

View file

@ -61,6 +61,7 @@
*/ */
import Boom from 'boom'; import Boom from 'boom';
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import Semver from 'semver'; import Semver from 'semver';
import { Logger } from '../../../logging'; import { Logger } from '../../../logging';
@ -291,7 +292,7 @@ function markAsUpToDate(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMigrat
...doc, ...doc,
migrationVersion: props(doc).reduce((acc, prop) => { migrationVersion: props(doc).reduce((acc, prop) => {
const version = propVersion(migrations, prop); const version = propVersion(migrations, prop);
return version ? _.set(acc, prop, version) : acc; return version ? set(acc, prop, version) : acc;
}, {}), }, {}),
}; };
} }

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
import { SavedObjectsSerializer } from '../../serialization'; import { SavedObjectsSerializer } from '../../serialization';
@ -25,7 +26,7 @@ import { createSavedObjectsMigrationLoggerMock } from '../../migrations/mocks';
describe('migrateRawDocs', () => { describe('migrateRawDocs', () => {
test('converts raw docs to saved objects', async () => { test('converts raw docs to saved objects', async () => {
const transform = jest.fn<any, any>((doc: any) => _.set(doc, 'attributes.name', 'HOI!')); const transform = jest.fn<any, any>((doc: any) => set(doc, 'attributes.name', 'HOI!'));
const result = await migrateRawDocs( const result = await migrateRawDocs(
new SavedObjectsSerializer(new SavedObjectTypeRegistry()), new SavedObjectsSerializer(new SavedObjectTypeRegistry()),
transform, transform,
@ -53,7 +54,7 @@ describe('migrateRawDocs', () => {
test('passes invalid docs through untouched and logs error', async () => { test('passes invalid docs through untouched and logs error', async () => {
const logger = createSavedObjectsMigrationLoggerMock(); const logger = createSavedObjectsMigrationLoggerMock();
const transform = jest.fn<any, any>((doc: any) => const transform = jest.fn<any, any>((doc: any) =>
_.set(_.cloneDeep(doc), 'attributes.name', 'TADA') set(_.cloneDeep(doc), 'attributes.name', 'TADA')
); );
const result = await migrateRawDocs( const result = await migrateRawDocs(
new SavedObjectsSerializer(new SavedObjectTypeRegistry()), new SavedObjectsSerializer(new SavedObjectTypeRegistry()),

View file

@ -17,7 +17,8 @@
* under the License. * under the License.
*/ */
import { get, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get } from 'lodash';
import { SavedObjectsErrorHelpers } from './errors'; import { SavedObjectsErrorHelpers } from './errors';
import { IndexMapping } from '../../mappings'; import { IndexMapping } from '../../mappings';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths // eslint-disable-next-line @kbn/eslint/no-restricted-paths

View file

@ -55,7 +55,9 @@ export class File {
} }
public isFixture() { public isFixture() {
return this.relativePath.split(sep).includes('__fixtures__'); return (
this.relativePath.split(sep).includes('__fixtures__') || this.path.endsWith('.test-d.ts')
);
} }
public getRelativeParentDirs() { public getRelativeParentDirs() {

View file

@ -61,6 +61,9 @@ export const IGNORE_FILE_GLOBS = [
// filename required by api-extractor // filename required by api-extractor
'api-documenter.json', 'api-documenter.json',
// filename must match upstream filenames from lodash
'packages/elastic-safer-lodash-set/**/*',
// TODO fix file names in APM to remove these // TODO fix file names in APM to remove these
'x-pack/plugins/apm/public/**/*', 'x-pack/plugins/apm/public/**/*',
'x-pack/plugins/apm/scripts/**/*', 'x-pack/plugins/apm/scripts/**/*',

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
let values = {}; let values = {};
export default { export default {
@ -24,11 +25,11 @@ export default {
return _.get(values, path, def); return _.get(values, path, def);
}, },
set: function (path, val) { set: function (path, val) {
_.set(values, path, val); set(values, path, val);
return val; return val;
}, },
setSilent: function (path, val) { setSilent: function (path, val) {
_.set(values, path, val); set(values, path, val);
return val; return val;
}, },
emit: _.noop, emit: _.noop,

View file

@ -17,7 +17,8 @@
* under the License. * under the License.
*/ */
import { get, isUndefined, noop, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get, isUndefined, noop } from 'lodash';
import { unset } from '../../utils'; import { unset } from '../../utils';
export function rename(oldKey, newKey) { export function rename(oldKey, newKey) {

View file

@ -18,6 +18,7 @@
*/ */
import Joi from 'joi'; import Joi from 'joi';
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { override } from './override'; import { override } from './override';
import createDefaultSchema from './schema'; import createDefaultSchema from './schema';
@ -56,7 +57,7 @@ export class Config {
throw new Error(`Config schema already has key: ${key}`); throw new Error(`Config schema already has key: ${key}`);
} }
_.set(this[schemaExts], key, extension); set(this[schemaExts], key, extension);
this[schema] = null; this[schema] = null;
this.set(key, settings); this.set(key, settings);
@ -82,7 +83,7 @@ export class Config {
if (_.isPlainObject(key)) { if (_.isPlainObject(key)) {
config = override(config, key); config = override(config, key);
} else { } else {
_.set(config, key, value); set(config, key, value);
} }
// attempt to validate the config value // attempt to validate the config value

View file

@ -16,7 +16,8 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { cloneDeep, isEqual, isPlainObject, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { cloneDeep, isEqual, isPlainObject } from 'lodash';
import { State } from './state'; import { State } from './state';
export const stateMonitorFactory = { export const stateMonitorFactory = {

View file

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { FormattedData } from '../../../../../plugins/inspector/public'; import { FormattedData } from '../../../../../plugins/inspector/public';
import { FormatFactory } from '../../../common/field_formats/utils'; import { FormatFactory } from '../../../common/field_formats/utils';
import { TabbedTable } from '../tabify'; import { TabbedTable } from '../tabify';

View file

@ -69,18 +69,8 @@
* `appSearchSource`. * `appSearchSource`.
*/ */
import { import { setWith } from '@elastic/safer-lodash-set';
uniqueId, import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash';
uniq,
extend,
pick,
difference,
omit,
setWith,
isObject,
keys,
isFunction,
} from 'lodash';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { CoreStart } from 'kibana/public'; import { CoreStart } from 'kibana/public';
import { normalizeSortRequest } from './normalize_sort_request'; import { normalizeSortRequest } from './normalize_sort_request';

View file

@ -18,7 +18,7 @@
*/ */
import moment from 'moment'; import moment from 'moment';
import * as _ from 'lodash'; import { get, last } from 'lodash';
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
import { fetchContextProvider } from './context'; import { fetchContextProvider } from './context';
import { setServices } from '../../../../kibana_services'; import { setServices } from '../../../../kibana_services';
@ -124,9 +124,7 @@ describe('context app', function () {
).then((hits) => { ).then((hits) => {
const intervals = mockSearchSource.setField.args const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query') .filter(([property]) => property === 'query')
.map(([, { query }]) => .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
_.get(query, ['constant_score', 'filter', 'range', '@timestamp'])
);
expect( expect(
intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true)) intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true))
@ -134,7 +132,7 @@ describe('context app', function () {
// should have started at the given time // should have started at the given time
expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 3000).toISOString());
// should have ended with a half-open interval // should have ended with a half-open interval
expect(Object.keys(_.last(intervals))).toEqual(['format', 'gte']); expect(Object.keys(last(intervals))).toEqual(['format', 'gte']);
expect(intervals.length).toBeGreaterThan(1); expect(intervals.length).toBeGreaterThan(1);
expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3));
@ -162,14 +160,12 @@ describe('context app', function () {
).then((hits) => { ).then((hits) => {
const intervals = mockSearchSource.setField.args const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query') .filter(([property]) => property === 'query')
.map(([, { query }]) => .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
_.get(query, ['constant_score', 'filter', 'range', '@timestamp'])
);
// should have started at the given time // should have started at the given time
expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString()); expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString());
// should have stopped before reaching MS_PER_DAY * 1700 // should have stopped before reaching MS_PER_DAY * 1700
expect(moment(_.last(intervals).lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); expect(moment(last(intervals).lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700);
expect(intervals.length).toBeGreaterThan(1); expect(intervals.length).toBeGreaterThan(1);
expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3));
}); });

View file

@ -18,7 +18,7 @@
*/ */
import moment from 'moment'; import moment from 'moment';
import * as _ from 'lodash'; import { get, last } from 'lodash';
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
import { setServices } from '../../../../kibana_services'; import { setServices } from '../../../../kibana_services';
@ -125,9 +125,7 @@ describe('context app', function () {
).then((hits) => { ).then((hits) => {
const intervals = mockSearchSource.setField.args const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query') .filter(([property]) => property === 'query')
.map(([, { query }]) => .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
_.get(query, ['constant_score', 'filter', 'range', '@timestamp'])
);
expect( expect(
intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true)) intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true))
@ -135,7 +133,7 @@ describe('context app', function () {
// should have started at the given time // should have started at the given time
expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString());
// should have ended with a half-open interval // should have ended with a half-open interval
expect(Object.keys(_.last(intervals))).toEqual(['format', 'lte']); expect(Object.keys(last(intervals))).toEqual(['format', 'lte']);
expect(intervals.length).toBeGreaterThan(1); expect(intervals.length).toBeGreaterThan(1);
expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3));
@ -165,14 +163,12 @@ describe('context app', function () {
).then((hits) => { ).then((hits) => {
const intervals = mockSearchSource.setField.args const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query') .filter(([property]) => property === 'query')
.map(([, { query }]) => .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
_.get(query, ['constant_score', 'filter', 'range', '@timestamp'])
);
// should have started at the given time // should have started at the given time
expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString());
// should have stopped before reaching MS_PER_DAY * 2200 // should have stopped before reaching MS_PER_DAY * 2200
expect(moment(_.last(intervals).gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); expect(moment(last(intervals).gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200);
expect(intervals.length).toBeGreaterThan(1); expect(intervals.length).toBeGreaterThan(1);
expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 4)); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 4));

View file

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import * as _ from 'lodash'; import { defaultsDeep } from 'lodash';
import ace from 'brace'; import ace from 'brace';
import 'brace/mode/json'; import 'brace/mode/json';
@ -176,7 +176,7 @@ export function XJsonHighlightRules(this: any) {
oop.inherits(XJsonHighlightRules, JsonHighlightRules); oop.inherits(XJsonHighlightRules, JsonHighlightRules);
export function addToRules(otherRules: any, embedUnder: any) { export function addToRules(otherRules: any, embedUnder: any) {
otherRules.$rules = _.defaultsDeep(otherRules.$rules, jsonRules(embedUnder)); otherRules.$rules = defaultsDeep(otherRules.$rules, jsonRules(embedUnder));
otherRules.embedRules(ScriptHighlightRules, 'script-', [ otherRules.embedRules(ScriptHighlightRules, 'script-', [
{ {
token: 'punctuation.end_triple_quote', token: 'punctuation.end_triple_quote',

View file

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { FieldHook } from '../types'; import { FieldHook } from '../types';
export const unflattenObject = (object: any) => export const unflattenObject = (object: any) =>

View file

@ -26,7 +26,8 @@ import {
IRootScopeService, IRootScopeService,
} from 'angular'; } from 'angular';
import $ from 'jquery'; import $ from 'jquery';
import { cloneDeep, forOwn, get, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { cloneDeep, forOwn, get } from 'lodash';
import * as Rx from 'rxjs'; import * as Rx from 'rxjs';
import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public'; import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public';
import { History } from 'history'; import { History } from 'history';

View file

@ -26,7 +26,8 @@ import {
EuiButtonEmpty, EuiButtonEmpty,
EuiSpacer, EuiSpacer,
} from '@elastic/eui'; } from '@elastic/eui';
import { cloneDeep, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { cloneDeep } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public'; import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public';

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { getLastValue } from '../../../../../../plugins/vis_type_timeseries/common/get_last_value'; import { getLastValue } from '../../../../../../plugins/vis_type_timeseries/common/get_last_value';
import { createTickFormatter } from './tick_formatter'; import { createTickFormatter } from './tick_formatter';
@ -51,8 +52,8 @@ export const convertSeriesToVars = (series, model, dateFormat = 'lll', getConfig
}), }),
}, },
}; };
_.set(variables, varName, data); set(variables, varName, data);
_.set(variables, `${_.snakeCase(row.label)}.label`, row.label); set(variables, `${_.snakeCase(row.label)}.label`, row.label);
}); });
}); });
return variables; return variables;

View file

@ -19,7 +19,8 @@
import { getBucketsPath } from './get_buckets_path'; import { getBucketsPath } from './get_buckets_path';
import { parseInterval } from './parse_interval'; import { parseInterval } from './parse_interval';
import { set, isEmpty } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { isEmpty } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { MODEL_SCRIPTS } from './moving_fn_scripts'; import { MODEL_SCRIPTS } from './moving_fn_scripts';

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import d3 from 'd3'; import d3 from 'd3';
import { SCALE_MODES } from './scale_modes'; import { SCALE_MODES } from './scale_modes';
@ -220,7 +221,7 @@ export class AxisConfig {
} }
set(property, value) { set(property, value) {
return _.set(this._values, property, value); return set(this._values, property, value);
} }
isHorizontal() { isHorizontal() {

View file

@ -18,6 +18,7 @@
*/ */
import d3 from 'd3'; import d3 from 'd3';
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
const defaults = { const defaults = {
@ -102,6 +103,6 @@ export class ChartGrid {
} }
set(property, value) { set(property, value) {
return _.set(this._values, property, value); return set(this._values, property, value);
} }
} }

View file

@ -20,6 +20,7 @@
/** /**
* Provides vislib configuration, throws error if invalid property is accessed without providing defaults * Provides vislib configuration, throws error if invalid property is accessed without providing defaults
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { vislibTypesConfig as visTypes } from './types'; import { vislibTypesConfig as visTypes } from './types';
import { Data } from './data'; import { Data } from './data';
@ -54,6 +55,6 @@ export class VisConfig {
} }
set(property, value) { set(property, value) {
return _.set(this._values, property, value); return set(this._values, property, value);
} }
} }

View file

@ -17,6 +17,7 @@
* under the License. * under the License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
/** /**
@ -31,7 +32,7 @@ function convertHeatmapLabelColor(visState) {
if (visState.type === 'heatmap' && visState.params && !hasOverwriteColorParam) { if (visState.type === 'heatmap' && visState.params && !hasOverwriteColorParam) {
const showLabels = _.get(visState, 'params.valueAxes[0].labels.show', false); const showLabels = _.get(visState, 'params.valueAxes[0].labels.show', false);
const color = _.get(visState, 'params.valueAxes[0].labels.color', '#555'); const color = _.get(visState, 'params.valueAxes[0].labels.color', '#555');
_.set(visState, 'params.valueAxes[0].labels.overwriteColor', showLabels && color !== '#555'); set(visState, 'params.valueAxes[0].labels.overwriteColor', showLabels && color !== '#555');
} }
} }
@ -167,7 +168,7 @@ export const updateOldState = (visState) => {
if (visState.type === 'gauge' && visState.fontSize) { if (visState.type === 'gauge' && visState.fontSize) {
delete newState.fontSize; delete newState.fontSize;
_.set(newState, 'gauge.style.fontSize', visState.fontSize); set(newState, 'gauge.style.fontSize', visState.fontSize);
} }
// update old metric to the new one // update old metric to the new one

View file

@ -19,17 +19,8 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { import { set } from '@elastic/safer-lodash-set';
isPlainObject, import { isPlainObject, cloneDeep, get, isEqual, isString, merge, mergeWith, toPath } from 'lodash';
cloneDeep,
get,
set,
isEqual,
isString,
merge,
mergeWith,
toPath,
} from 'lodash';
function prepSetParams(key: PersistedStateKey, value: any, path: PersistedStatePath) { function prepSetParams(key: PersistedStateKey, value: any, path: PersistedStatePath) {
// key must be the value, set the entire state using it // key must be the value, set the entire state using it

View file

@ -223,6 +223,12 @@ module.exports = function (grunt) {
args: ['scripts/test_hardening.js'], args: ['scripts/test_hardening.js'],
}), }),
test_package_safer_lodash_set: scriptWithGithubChecks({
title: '@elastic/safer-lodash-set tests',
cmd: YARN,
args: ['--cwd', 'packages/elastic-safer-lodash-set', 'test'],
}),
apiIntegrationTests: scriptWithGithubChecks({ apiIntegrationTests: scriptWithGithubChecks({
title: 'API integration tests', title: 'API integration tests',
cmd: NODE, cmd: NODE,

View file

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

View file

@ -21,6 +21,7 @@
* Smokescreen tests for core migration logic * Smokescreen tests for core migration logic
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
import { assert } from 'chai'; import { assert } from 'chai';
import { import {
@ -56,12 +57,12 @@ export default ({ getService }) => {
const migrations = { const migrations = {
foo: { foo: {
'1.0.0': (doc) => _.set(doc, 'attributes.name', doc.attributes.name.toUpperCase()), '1.0.0': (doc) => set(doc, 'attributes.name', doc.attributes.name.toUpperCase()),
}, },
bar: { bar: {
'1.0.0': (doc) => _.set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1), '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1),
'1.3.0': (doc) => _.set(doc, 'attributes', { mynum: doc.attributes.nomnom }), '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }),
'1.9.0': (doc) => _.set(doc, 'attributes.mynum', doc.attributes.mynum * 2), '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2),
}, },
}; };
@ -172,12 +173,12 @@ export default ({ getService }) => {
const migrations = { const migrations = {
foo: { foo: {
'1.0.0': (doc) => _.set(doc, 'attributes.name', doc.attributes.name.toUpperCase()), '1.0.0': (doc) => set(doc, 'attributes.name', doc.attributes.name.toUpperCase()),
}, },
bar: { bar: {
'1.0.0': (doc) => _.set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1), '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1),
'1.3.0': (doc) => _.set(doc, 'attributes', { mynum: doc.attributes.nomnom }), '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }),
'1.9.0': (doc) => _.set(doc, 'attributes.mynum', doc.attributes.mynum * 2), '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2),
}, },
}; };
@ -187,8 +188,8 @@ export default ({ getService }) => {
await migrateIndex({ callCluster, index, migrations, mappingProperties }); await migrateIndex({ callCluster, index, migrations, mappingProperties });
mappingProperties.bar.properties.name = { type: 'keyword' }; mappingProperties.bar.properties.name = { type: 'keyword' };
migrations.foo['2.0.1'] = (doc) => _.set(doc, 'attributes.name', `${doc.attributes.name}v2`); migrations.foo['2.0.1'] = (doc) => set(doc, 'attributes.name', `${doc.attributes.name}v2`);
migrations.bar['2.3.4'] = (doc) => _.set(doc, 'attributes.name', `NAME ${doc.id}`); migrations.bar['2.3.4'] = (doc) => set(doc, 'attributes.name', `NAME ${doc.id}`);
await migrateIndex({ callCluster, index, migrations, mappingProperties }); await migrateIndex({ callCluster, index, migrations, mappingProperties });
@ -267,7 +268,7 @@ export default ({ getService }) => {
const migrations = { const migrations = {
foo: { foo: {
'1.0.0': (doc) => _.set(doc, 'attributes.name', 'LOTR'), '1.0.0': (doc) => set(doc, 'attributes.name', 'LOTR'),
}, },
}; };

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { checkLicense } from './check_license'; import { checkLicense } from './check_license';
import { import {
LICENSE_STATUS_UNAVAILABLE, LICENSE_STATUS_UNAVAILABLE,

View file

@ -6,7 +6,7 @@
import expect from '@kbn/expect'; import expect from '@kbn/expect';
import { isEsErrorFactory } from '../is_es_error_factory'; import { isEsErrorFactory } from '../is_es_error_factory';
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
class MockAbstractEsError {} class MockAbstractEsError {}

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { isPlainObject, omit, get, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { isPlainObject, omit, get } from 'lodash';
import rison from 'rison-node'; import rison from 'rison-node';
const stateTypeKeys = { const stateTypeKeys = {

View file

@ -201,6 +201,7 @@
"@elastic/maki": "6.3.0", "@elastic/maki": "6.3.0",
"@elastic/node-crypto": "1.2.1", "@elastic/node-crypto": "1.2.1",
"@elastic/numeral": "^2.5.0", "@elastic/numeral": "^2.5.0",
"@elastic/safer-lodash-set": "0.0.0",
"@kbn/babel-preset": "1.0.0", "@kbn/babel-preset": "1.0.0",
"@kbn/config-schema": "1.0.0", "@kbn/config-schema": "1.0.0",
"@kbn/i18n": "1.0.0", "@kbn/i18n": "1.0.0",

View file

@ -9,7 +9,8 @@ import { argv } from 'yargs';
import pLimit from 'p-limit'; import pLimit from 'p-limit';
import pRetry from 'p-retry'; import pRetry from 'p-retry';
import { parse, format } from 'url'; import { parse, format } from 'url';
import { unique, without, set, merge, flatten } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { unique, without, merge, flatten } from 'lodash';
import * as histogram from 'hdr-histogram-js'; import * as histogram from 'hdr-histogram-js';
import { ESSearchResponse } from '../../typings/elasticsearch'; import { ESSearchResponse } from '../../typings/elasticsearch';
import { import {

View file

@ -5,7 +5,8 @@
*/ */
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { get, has, omit, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get, has, omit } from 'lodash';
import { import {
ConfigBlockSchema, ConfigBlockSchema,
ConfigurationBlock, ConfigurationBlock,

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { groupBy, get, keyBy, set, map, sortBy } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { groupBy, get, keyBy, map, sortBy } from 'lodash';
import { ExpressionFunctionDefinition, Style } from 'src/plugins/expressions'; import { ExpressionFunctionDefinition, Style } from 'src/plugins/expressions';
// @ts-expect-error untyped local // @ts-expect-error untyped local
import { getColorsFromPalette } from '../../../../common/lib/get_colors_from_palette'; import { getColorsFromPalette } from '../../../../common/lib/get_colors_from_palette';

View file

@ -6,7 +6,8 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { compose, withProps } from 'recompose'; import { compose, withProps } from 'recompose';
import { set, get } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get } from 'lodash';
import { fromExpression, toExpression } from '@kbn/interpreter/common'; import { fromExpression, toExpression } from '@kbn/interpreter/common';
import { getAssets } from '../../state/selectors/assets'; import { getAssets } from '../../state/selectors/assets';
// @ts-expect-error untyped local // @ts-expect-error untyped local

View file

@ -6,7 +6,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get, mapValues, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get, mapValues } from 'lodash';
import { openSans } from '../../../common/lib/fonts'; import { openSans } from '../../../common/lib/fonts';
import { templateFromReactComponent } from '../../lib/template_from_react_component'; import { templateFromReactComponent } from '../../lib/template_from_react_component';
import { TextStylePicker } from '../../components/text_style_picker'; import { TextStylePicker } from '../../components/text_style_picker';

View file

@ -8,6 +8,7 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const { set } = require('@elastic/safer-lodash-set');
const lodash = require('lodash'); const lodash = require('lodash');
const LineWriter = require('./lib/line_writer'); const LineWriter = require('./lib/line_writer');
@ -49,7 +50,7 @@ function getEventLogMappings(ecsSchema, exportedProperties) {
// copy the leaf values of the properties // copy the leaf values of the properties
for (const prop of leafProperties) { for (const prop of leafProperties) {
const value = lodash.get(ecsSchema.mappings.properties, prop); const value = lodash.get(ecsSchema.mappings.properties, prop);
lodash.set(result.mappings.properties, prop, value); set(result.mappings.properties, prop, value);
} }
// set the non-leaf values as appropriate // set the non-leaf values as appropriate
@ -118,7 +119,7 @@ function augmentMappings(mappings, multiValuedProperties) {
const metaPropName = `${fullProp}.meta`; const metaPropName = `${fullProp}.meta`;
const meta = lodash.get(mappings.properties, metaPropName) || {}; const meta = lodash.get(mappings.properties, metaPropName) || {};
meta.isArray = 'true'; meta.isArray = 'true';
lodash.set(mappings.properties, metaPropName, meta); set(mappings.properties, metaPropName, meta);
} }
} }

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { set, values } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { values } from 'lodash';
import React, { useContext, useMemo } from 'react'; import React, { useContext, useMemo } from 'react';
import * as t from 'io-ts'; import * as t from 'io-ts';
import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; import { ThrowReporter } from 'io-ts/lib/ThrowReporter';

View file

@ -6,7 +6,7 @@
import { encode } from 'rison-node'; import { encode } from 'rison-node';
import uuid from 'uuid'; import uuid from 'uuid';
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { colorTransformer, MetricsExplorerColor } from '../../../../../../common/color_palette'; import { colorTransformer, MetricsExplorerColor } from '../../../../../../common/color_palette';
import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer';
import { import {

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { first, set, startsWith } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { first, startsWith } from 'lodash';
import { RequestHandlerContext } from 'src/core/server'; import { RequestHandlerContext } from 'src/core/server';
import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter';
import { InfraSourceConfiguration } from '../../../lib/sources'; import { InfraSourceConfiguration } from '../../../lib/sources';

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { isObject, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { isObject } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { InfraDatabaseSearchResponse } from '../../../lib/adapters/framework'; import { InfraDatabaseSearchResponse } from '../../../lib/adapters/framework';
import { import {

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { InfraDatabaseSearchResponse } from '../lib/adapters/framework'; import { InfraDatabaseSearchResponse } from '../lib/adapters/framework';
export const createAfterKeyHandler = ( export const createAfterKeyHandler = (

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { get, set } from 'lodash'; import { set } from '@elastic/safer-lodash-set';
import { get } from 'lodash';
import { STORAGE_KEY } from '../../../common/constants'; import { STORAGE_KEY } from '../../../common/constants';
export const tableStorageGetter = (keyPrefix) => { export const tableStorageGetter = (keyPrefix) => {

View file

@ -4,11 +4,12 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { set } from '@elastic/safer-lodash-set';
import _ from 'lodash'; import _ from 'lodash';
function addOne(obj, key) { function addOne(obj, key) {
let value = _.get(obj, key); let value = _.get(obj, key);
_.set(obj, key, ++value); set(obj, key, ++value);
} }
export function calculateShardStats(state) { export function calculateShardStats(state) {

Some files were not shown because too many files have changed in this diff Show more