[SO] Harden structural selection logic (#218073)

## Summary

Adds a basic sanity check before pulling a value from the target object.
Under the hood `lodash` will drive this check by running `Object.
hasOwnProperty` for each segment of the path we are querying. Guards
against theoretical attacks from SOs that might allow ill-defined
structures in documents.


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Jean-Louis Leysens 2025-04-14 14:06:35 +02:00 committed by GitHub
parent 47090f198e
commit c10cd622e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 1 deletions

View file

@ -71,6 +71,22 @@ describe('pickValuesBasedOnStructure', () => {
expect(pickValuesBasedOnStructure(a, b)).toEqual({ v1: 'b', v2: [], v3: {} });
});
test('special case: only selects own properties', () => {
const source = {};
Object.defineProperty(source, '__proto__', { enumerable: true, value: 1 });
const target = {};
expect(pickValuesBasedOnStructure(source, target)).toEqual({});
});
test('special case: only selects own properties deeply', () => {
const poisened = {};
Object.defineProperty(poisened, '__proto__', { enumerable: true, value: 1 });
Object.defineProperty(poisened, 'a', { enumerable: true, value: 1 });
const source = { arr: [poisened] };
const target = { arr: [{ a: 1 }] };
expect(pickValuesBasedOnStructure(source, target)).toEqual({ arr: [{ a: 1 }] });
});
test('can extract structure map when present in target', () => {
/**
* The `keys` `Arbitrary` represents words with possible numbers like `loremv123`

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { isPlainObject, get } from 'lodash';
import { isPlainObject, get, has } from 'lodash';
import { set } from '@kbn/safer-lodash-set';
export function getFlattenedKeys(obj: object): string[] {
@ -62,6 +62,7 @@ export function pickValuesBasedOnStructure(structuralSource: object, target: obj
const paths = getFlattenedKeys(structuralSource);
const result: object = {};
for (const path of paths) {
if (!has(target, path)) continue;
const value = get(target, path);
if (Array.isArray(value)) {
set(result, path, []);