VYPR
Critical severityNVD Advisory· Published Aug 16, 2023· Updated Oct 8, 2024

CVE-2023-38894

CVE-2023-38894

Description

Prototype pollution in tree-kit's extend function allows remote code execution via crafted input.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Prototype pollution in tree-kit's extend function allows remote code execution via crafted input.

A prototype pollution vulnerability exists in the extend function of the tree-kit npm package (versions before 0.7.5) when the unflat option is enabled [1][2]. This flaw allows an attacker to pollute the prototype of Object.prototype by injecting properties through a crafted object passed to the function [2].

Exploitation occurs when an attacker-controlled object, such as JSON data parsed by the application, is used as the source argument in treekit.extend({unflat: true}, target, source). For example, passing {'constructor.prototype.polluted': true} can set {}.polluted to true [2]. No authentication is required if the application exposes this functionality to untrusted input.

The impact includes remote code execution (RCE), denial of service via overridden built-in functions like toString, privilege escalation, and cross-site scripting (XSS) [2]. The vulnerability was discovered by Peter Samarin using Jazzer.js as part of OSS-Fuzz [2].

The maintainer released a fix in version 0.7.5, which is available via the commit [4]. Users are strongly advised to upgrade to version 0.7.5 or later to mitigate the risk [2][4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
tree-kitnpm
< 0.7.50.7.5

Affected products

2

Patches

1
61bf10cf0dbd

Fix prototype pollution in .extend() when the 'unflat' option is set

https://github.com/cronvel/tree-kitCédric RonvelJul 21, 2023via ghsa
12 files changed · +471 269
  • browser/tree-kit.js+164 132 modified
    @@ -205,7 +205,7 @@ function toPathArray( path ) {
     
     	if ( ! path ) { return EMPTY_PATH ; }
     	if ( typeof path === 'string' ) {
    -		return path[ path.length - 1 ] === '.' ? path.slice( 0 , -1 ).split( '.' ) : path.split( '.' ) ;
    +		return path[ path.length - 1 ] === '.' ? path.slice( 0 , - 1 ).split( '.' ) : path.split( '.' ) ;
     	}
     
     	throw new TypeError( '[tree.dotPath]: the path argument should be a string or an array' ) ;
    @@ -332,7 +332,7 @@ dotPath.dec = ( object , path , value ) => {
     	var pointer = pave( object , pathArray ) ;
     
     	if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
    -	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = -1 ; }
    +	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }
     
     	return value ;
     } ;
    @@ -393,7 +393,7 @@ dotPath.delete = ( object , path ) => {
     
     	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
     
    -	var pointer = walk( object , pathArray , -1 ) ;
    +	var pointer = walk( object , pathArray , - 1 ) ;
     
     	if ( ! pointer || typeof pointer !== 'object' ) { return false ; }
     
    @@ -620,10 +620,7 @@ module.exports = extend ;
     
     
     function extendOne( runtime , options , target , source , mask ) {
    -	var j , jmax , path ,
    -		sourceKeys , sourceKey , sourceValue , sourceValueIsObject , sourceValueProto , sourceDescriptor ,
    -		targetKey , targetPointer , targetValue , targetValueIsObject ,
    -		indexOfSource = -1 ;
    +	var sourceKeys , sourceKey ;
     
     	// Max depth check
     	if ( options.maxDepth && runtime.depth > options.maxDepth ) {
    @@ -636,158 +633,193 @@ function extendOne( runtime , options , target , source , mask ) {
     		runtime.references.targets.push( target ) ;
     	}
     
    -	if ( options.own ) {
    +	// 'unflat' mode computing
    +	if ( options.unflat && runtime.depth === 0 ) {
    +		for ( sourceKey in source ) {
    +			runtime.unflatKeys = sourceKey.split( options.unflat ) ;
    +			runtime.unflatIndex = 0 ;
    +			runtime.unflatFullKey = sourceKey ;
    +			extendOneKV( runtime , options , target , source , runtime.unflatKeys[ runtime.unflatIndex ] , mask ) ;
    +		}
    +
    +		delete runtime.unflatKeys ;
    +		delete runtime.unflatIndex ;
    +		delete runtime.unflatFullKey ;
    +	}
    +	else if ( options.own ) {
     		if ( options.nonEnum ) { sourceKeys = Object.getOwnPropertyNames( source ) ; }
     		else { sourceKeys = Object.keys( source ) ; }
    +
    +		for ( sourceKey of sourceKeys ) {
    +			extendOneKV( runtime , options , target , source , sourceKey , mask ) ;
    +		}
     	}
    -	else { sourceKeys = source ; }
    +	else {
    +		for ( sourceKey in source ) {
    +			extendOneKV( runtime , options , target , source , sourceKey , mask ) ;
    +		}
    +	}
    +}
     
    -	for ( sourceKey in sourceKeys ) {
    -		if ( options.own ) { sourceKey = sourceKeys[ sourceKey ] ; }
     
    -		// OMG, this DEPRECATED __proto__ shit is still alive and can be used to hack anything ><
    -		if ( sourceKey === '__proto__' ) { continue ; }
     
    -		// If descriptor is on, get it now
    -		if ( options.descriptor ) {
    -			sourceDescriptor = Object.getOwnPropertyDescriptor( source , sourceKey ) ;
    -			sourceValue = sourceDescriptor.value ;
    +function extendOneKV( runtime , options , target , source , sourceKey , mask ) {
    +	// OMG, this DEPRECATED __proto__ shit is still alive and can be used to hack anything ><
    +	if ( sourceKey === '__proto__' ) { return ; }
    +
    +	let sourceValue , sourceDescriptor , sourceValueProto ;
    +
    +	if ( runtime.unflatKeys ) {
    +		if ( runtime.unflatIndex < runtime.unflatKeys.length - 1 ) {
    +			sourceValue = {} ;
     		}
     		else {
    -			// We have to trigger an eventual getter only once
    -			sourceValue = source[ sourceKey ] ;
    +			sourceValue = source[ runtime.unflatFullKey ] ;
     		}
    +	}
    +	else if ( options.descriptor ) {
    +		// If descriptor is on, get it now
    +		sourceDescriptor = Object.getOwnPropertyDescriptor( source , sourceKey ) ;
    +		sourceValue = sourceDescriptor.value ;
    +	}
    +	else {
    +		// We have to trigger an eventual getter only once
    +		sourceValue = source[ sourceKey ] ;
    +	}
     
    -		targetPointer = target ;
    -		targetKey = runtime.prefix + sourceKey ;
    -
    -		// Do not copy if property is a function and we don't want them
    -		if ( options.nofunc && typeof sourceValue === 'function' ) { continue ; }
    -
    -		// 'unflat' mode computing
    -		if ( options.unflat && runtime.depth === 0 ) {
    -			path = sourceKey.split( options.unflat ) ;
    -			jmax = path.length - 1 ;
    +	let targetKey = runtime.prefix + sourceKey ;
    +
    +	// Do not copy if property is a function and we don't want them
    +	if ( options.nofunc && typeof sourceValue === 'function' ) { return ; }
    +
    +	// Again, trigger an eventual getter only once
    +	let targetValue = target[ targetKey ] ;
    +	let targetValueIsObject = targetValue && ( typeof targetValue === 'object' || typeof targetValue === 'function' ) ;
    +	let sourceValueIsObject = sourceValue && ( typeof sourceValue === 'object' || typeof sourceValue === 'function' ) ;
    +
    +	if (
    +		( options.deep || runtime.unflatKeys )
    +		&& sourceValue
    +		&& ( typeof sourceValue === 'object' || ( options.deepFunc && typeof sourceValue === 'function' ) )
    +		&& ( ! options.descriptor || ! sourceDescriptor.get )
    +		// not a condition we just cache sourceValueProto now... ok it's trashy ><
    +		&& ( ( sourceValueProto = Object.getPrototypeOf( sourceValue ) ) || true )
    +		&& ( ! ( options.deep instanceof Set ) || options.deep.has( sourceValueProto ) )
    +		&& ( ! options.immutables || ! options.immutables.has( sourceValueProto ) )
    +		&& ( ! options.preserve || targetValueIsObject )
    +		&& ( ! mask || targetValueIsObject )
    +	) {
    +		let indexOfSource = options.circular ? runtime.references.sources.indexOf( sourceValue ) : - 1 ;
    +
    +		if ( options.flat ) {
    +			// No circular references reconnection when in 'flat' mode
    +			if ( indexOfSource >= 0 ) { return ; }
    +
    +			extendOne(
    +				{
    +					depth: runtime.depth + 1 ,
    +					prefix: runtime.prefix + sourceKey + options.flat ,
    +					references: runtime.references
    +				} ,
    +				options , target , sourceValue , mask
    +			) ;
    +		}
    +		else {
    +			if ( indexOfSource >= 0 ) {
    +				// Circular references reconnection...
    +				targetValue = runtime.references.targets[ indexOfSource ] ;
    +
    +				if ( options.descriptor ) {
    +					Object.defineProperty( target , targetKey , {
    +						value: targetValue ,
    +						enumerable: sourceDescriptor.enumerable ,
    +						writable: sourceDescriptor.writable ,
    +						configurable: sourceDescriptor.configurable
    +					} ) ;
    +				}
    +				else {
    +					target[ targetKey ] = targetValue ;
    +				}
     
    -			if ( jmax ) {
    -				for ( j = 0 ; j < jmax ; j ++ ) {
    -					if ( ! targetPointer[ path[ j ] ] ||
    -						( typeof targetPointer[ path[ j ] ] !== 'object' &&
    -							typeof targetPointer[ path[ j ] ] !== 'function' ) ) {
    -						targetPointer[ path[ j ] ] = {} ;
    -					}
    +				return ;
    +			}
     
    -					targetPointer = targetPointer[ path[ j ] ] ;
    +			if ( ! targetValueIsObject || ! Object.prototype.hasOwnProperty.call( target , targetKey ) ) {
    +				if ( Array.isArray( sourceValue ) ) { targetValue = [] ; }
    +				else if ( options.proto ) { targetValue = Object.create( sourceValueProto ) ; }
    +				else if ( options.inherit ) { targetValue = Object.create( sourceValue ) ; }
    +				else { targetValue = {} ; }
    +
    +				if ( options.descriptor ) {
    +					Object.defineProperty( target , targetKey , {
    +						value: targetValue ,
    +						enumerable: sourceDescriptor.enumerable ,
    +						writable: sourceDescriptor.writable ,
    +						configurable: sourceDescriptor.configurable
    +					} ) ;
    +				}
    +				else {
    +					target[ targetKey ] = targetValue ;
     				}
    -
    -				targetKey = runtime.prefix + path[ jmax ] ;
     			}
    -		}
    +			else if ( options.proto && Object.getPrototypeOf( targetValue ) !== sourceValueProto ) {
    +				Object.setPrototypeOf( targetValue , sourceValueProto ) ;
    +			}
    +			else if ( options.inherit && Object.getPrototypeOf( targetValue ) !== sourceValue ) {
    +				Object.setPrototypeOf( targetValue , sourceValue ) ;
    +			}
     
    -		// Again, trigger an eventual getter only once
    -		targetValue = targetPointer[ targetKey ] ;
    -		targetValueIsObject = targetValue && ( typeof targetValue === 'object' || typeof targetValue === 'function' ) ;
    -		sourceValueIsObject = sourceValue && ( typeof sourceValue === 'object' || typeof sourceValue === 'function' ) ;
    -
    -
    -		if ( options.deep	// eslint-disable-line no-constant-condition
    -			&& sourceValue
    -			&& ( typeof sourceValue === 'object' || ( options.deepFunc && typeof sourceValue === 'function' ) )
    -			&& ( ! options.descriptor || ! sourceDescriptor.get )
    -			// not a condition we just cache sourceValueProto now... ok it's trashy ><
    -			&& ( ( sourceValueProto = Object.getPrototypeOf( sourceValue ) ) || true )
    -			&& ( ! ( options.deep instanceof Set ) || options.deep.has( sourceValueProto ) )
    -			&& ( ! options.immutables || ! options.immutables.has( sourceValueProto ) )
    -			&& ( ! options.preserve || targetValueIsObject )
    -			&& ( ! mask || targetValueIsObject )
    -		) {
     			if ( options.circular ) {
    -				indexOfSource = runtime.references.sources.indexOf( sourceValue ) ;
    +				runtime.references.sources.push( sourceValue ) ;
    +				runtime.references.targets.push( targetValue ) ;
     			}
     
    -			if ( options.flat ) {
    -				// No circular references reconnection when in 'flat' mode
    -				if ( indexOfSource >= 0 ) { continue ; }
    -
    -				extendOne(
    -					{ depth: runtime.depth + 1 , prefix: runtime.prefix + sourceKey + options.flat , references: runtime.references } ,
    -					options , targetPointer , sourceValue , mask
    +			if ( runtime.unflatKeys && runtime.unflatIndex < runtime.unflatKeys.length - 1 ) {
    +				// Finish unflatting this property
    +				let nextSourceKey = runtime.unflatKeys[ runtime.unflatIndex + 1 ] ;
    +
    +				extendOneKV(
    +					{
    +						depth: runtime.depth ,
    +						unflatKeys: runtime.unflatKeys ,
    +						unflatIndex: runtime.unflatIndex + 1 ,
    +						unflatFullKey: runtime.unflatFullKey ,
    +						unflatValue: runtime.unflatValue ,
    +						prefix: '' ,
    +						references: runtime.references
    +					} ,
    +					options , targetValue , source , nextSourceKey , mask
     				) ;
     			}
     			else {
    -				if ( indexOfSource >= 0 ) {
    -					// Circular references reconnection...
    -					targetValue = runtime.references.targets[ indexOfSource ] ;
    -
    -					if ( options.descriptor ) {
    -						Object.defineProperty( targetPointer , targetKey , {
    -							value: targetValue ,
    -							enumerable: sourceDescriptor.enumerable ,
    -							writable: sourceDescriptor.writable ,
    -							configurable: sourceDescriptor.configurable
    -						} ) ;
    -					}
    -					else {
    -						targetPointer[ targetKey ] = targetValue ;
    -					}
    -
    -					continue ;
    -				}
    -
    -				if ( ! targetValueIsObject || ! Object.prototype.hasOwnProperty.call( targetPointer , targetKey ) ) {
    -					if ( Array.isArray( sourceValue ) ) { targetValue = [] ; }
    -					else if ( options.proto ) { targetValue = Object.create( sourceValueProto ) ; }	// jshint ignore:line
    -					else if ( options.inherit ) { targetValue = Object.create( sourceValue ) ; }
    -					else { targetValue = {} ; }
    -
    -					if ( options.descriptor ) {
    -						Object.defineProperty( targetPointer , targetKey , {
    -							value: targetValue ,
    -							enumerable: sourceDescriptor.enumerable ,
    -							writable: sourceDescriptor.writable ,
    -							configurable: sourceDescriptor.configurable
    -						} ) ;
    -					}
    -					else {
    -						targetPointer[ targetKey ] = targetValue ;
    -					}
    -				}
    -				else if ( options.proto && Object.getPrototypeOf( targetValue ) !== sourceValueProto ) {
    -					Object.setPrototypeOf( targetValue , sourceValueProto ) ;
    -				}
    -				else if ( options.inherit && Object.getPrototypeOf( targetValue ) !== sourceValue ) {
    -					Object.setPrototypeOf( targetValue , sourceValue ) ;
    -				}
    -
    -				if ( options.circular ) {
    -					runtime.references.sources.push( sourceValue ) ;
    -					runtime.references.targets.push( targetValue ) ;
    -				}
    -
     				// Recursively extends sub-object
     				extendOne(
    -					{ depth: runtime.depth + 1 , prefix: '' , references: runtime.references } ,
    +					{
    +						depth: runtime.depth + 1 ,
    +						prefix: '' ,
    +						references: runtime.references
    +					} ,
     					options , targetValue , sourceValue , mask
     				) ;
     			}
     		}
    -		else if ( mask && ( targetValue === undefined || targetValueIsObject || sourceValueIsObject ) ) {
    -			// Do not create new value, and so do not delete source's properties that were not moved.
    -			// We also do not overwrite object with non-object, and we don't overwrite non-object with object (preserve hierarchy)
    -			continue ;
    -		}
    -		else if ( options.preserve && targetValue !== undefined ) {
    -			// Do not overwrite, and so do not delete source's properties that were not moved
    -			continue ;
    -		}
    -		else if ( ! options.inherit ) {
    -			if ( options.descriptor ) { Object.defineProperty( targetPointer , targetKey , sourceDescriptor ) ; }
    -			else { targetPointer[ targetKey ] = targetValue = sourceValue ; }
    -		}
    -
    -		// Delete owned property of the source object
    -		if ( options.move ) { delete source[ sourceKey ] ; }
     	}
    +	else if ( mask && ( targetValue === undefined || targetValueIsObject || sourceValueIsObject ) ) {
    +		// Do not create new value, and so do not delete source's properties that were not moved.
    +		// We also do not overwrite object with non-object, and we don't overwrite non-object with object (preserve hierarchy)
    +		return ;
    +	}
    +	else if ( options.preserve && targetValue !== undefined ) {
    +		// Do not overwrite, and so do not delete source's properties that were not moved
    +		return ;
    +	}
    +	else if ( ! options.inherit ) {
    +		if ( options.descriptor ) { Object.defineProperty( target , targetKey , sourceDescriptor ) ; }
    +		else { target[ targetKey ] = targetValue = sourceValue ; }
    +	}
    +
    +	// Delete owned property of the source object
    +	if ( options.move ) { delete source[ sourceKey ] ; }
     }
     
     
    @@ -1009,7 +1041,7 @@ treePath.op = function( type , object , path , value ) {
     			return pointer[ key ] ;
     		case 'dec' :
     			if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
    -			else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = -1 ; }
    +			else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }
     			return pointer[ key ] ;
     		case 'append' :
     			if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
    
  • browser/tree-kit.min.js+1 1 modified
    @@ -1 +1 @@
    -(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.treeKit=e()}})(function(){var e,t,r;return function(){function e(t,r,n){function i(f,u){if(!r[f]){if(!t[f]){var a="function"==typeof require&&require;if(!u&&a)return a(f,!0);if(o)return o(f,!0);var s=new Error("Cannot find module '"+f+"'");throw s.code="MODULE_NOT_FOUND",s}var c=r[f]={exports:{}};t[f][0].call(c.exports,function(e){var r=t[f][1][e];return i(r||e)},c,c.exports,e,t,r,n)}return r[f].exports}for(var o="function"==typeof require&&require,f=0;f<n.length;f++)i(n[f]);return i}return e}()({1:[function(e,t,r){"use strict";const n={};t.exports=n;n.extend=e("./extend.js");n.clone=e("./clone.js");n.path=e("./path.js");n.dotPath=e("./dotPath.js")},{"./clone.js":2,"./dotPath.js":3,"./extend.js":4,"./path.js":5}],2:[function(e,t,r){"use strict";function n(e,t){var r=Object.getPrototypeOf(e);if(n.opaque.has(r)){return n.opaque.get(r)(e)}var i,o,f,u,a,s,c=[{source:e,target:Array.isArray(e)?[]:Object.create(r)}],p=c[0].target,d=new Map;d.set(e,p);while(u=c.shift()){f=Object.getOwnPropertyNames(u.source);for(i=0;i<f.length;i++){o=Object.getOwnPropertyDescriptor(u.source,f[i]);if(!o.value||typeof o.value!=="object"){Object.defineProperty(u.target,f[i],o);continue}a=o.value;if(t){if(d.has(a)){o.value=d.get(a);Object.defineProperty(u.target,f[i],o);continue}}s=Object.getPrototypeOf(o.value);if(n.opaque.has(s)){o.value=n.opaque.get(s)(o.value);Object.defineProperty(u.target,f[i],o);continue}o.value=Array.isArray(a)?[]:Object.create(s);if(t){d.set(a,o.value)}Object.defineProperty(u.target,f[i],o);c.push({source:a,target:o.value})}}return p}t.exports=n;n.opaque=new Map;n.opaque.set(Date.prototype,e=>new Date(e))},{}],3:[function(e,t,r){"use strict";const n={};t.exports=n;const i=[];const o="This would cause prototype pollution";function f(e){if(Array.isArray(e)){return e}if(!e){return i}if(typeof e==="string"){return e[e.length-1]==="."?e.slice(0,-1).split("."):e.split(".")}throw new TypeError("[tree.dotPath]: the path argument should be a string or an array")}function u(e,t,r=0){var n,i,f,u=e;for(n=0,i=t.length+r;n<i;n++){f=t[n];if(typeof f==="object"||f==="__proto__"||typeof u==="function"){throw new Error(o)}if(!u||typeof u!=="object"){return undefined}u=u[f]}return u}function a(e,t){var r,n,i,f=e;for(r=0,n=t.length-1;r<n;r++){i=t[r];if(typeof i==="object"||i==="__proto__"||typeof f[i]==="function"){throw new Error(o)}if(!f[i]||typeof f[i]!=="object"){f[i]={}}f=f[i]}return f}n.get=((e,t)=>u(e,f(t)));n.set=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);u[i]=r;return r});n.define=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(!(i in u)){u[i]=r}return r});n.inc=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(typeof u[i]==="number"){u[i]++}else if(!u[i]||typeof u[i]!=="object"){u[i]=1}return r});n.dec=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(typeof u[i]==="number"){u[i]--}else if(!u[i]||typeof u[i]!=="object"){u[i]=-1}return r});n.concat=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(!u[i]){u[i]=r}else if(Array.isArray(u[i])&&Array.isArray(r)){u[i]=u[i].concat(r)}return r});n.insert=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(!u[i]){u[i]=r}else if(Array.isArray(u[i])&&Array.isArray(r)){u[i]=r.concat(u[i])}return r});n.delete=((e,t)=>{var r=f(t),n=r[r.length-1];if(typeof n==="object"||n==="__proto__"){throw new Error(o)}var i=u(e,r,-1);if(!i||typeof i!=="object"){return false}return delete i[n]});n.autoPush=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(u[i]===undefined){u[i]=r}else if(Array.isArray(u[i])){u[i].push(r)}else{u[i]=[u[i],r]}return u[i]});n.append=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(!u[i]){u[i]=[r]}else if(Array.isArray(u[i])){u[i].push(r)}return u[i]});n.prepend=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=f(t),i=n[n.length-1];if(typeof i==="object"||i==="__proto__"){throw new Error(o)}var u=a(e,n);if(!u[i]){u[i]=[r]}else if(Array.isArray(u[i])){u[i].unshift(r)}return u[i]})},{}],4:[function(e,t,r){"use strict";function n(e,t,...r){var n,o,f=false,u=r.length;if(!u){return t}if(!e||typeof e!=="object"){e={}}var a={depth:0,prefix:""};if(e.deep){if(Array.isArray(e.deep)){e.deep=new Set(e.deep)}else if(!(e.deep instanceof Set)){e.deep=true}}if(e.immutables){if(Array.isArray(e.immutables)){e.immutables=new Set(e.immutables)}else if(!(e.immutables instanceof Set)){delete e.immutables}}if(!e.maxDepth&&e.deep&&!e.circular){e.maxDepth=100}if(e.deepFunc){e.deep=true}if(e.flat){e.deep=true;e.proto=false;e.inherit=false;e.unflat=false;if(typeof e.flat!=="string"){e.flat="."}}if(e.unflat){e.deep=false;e.proto=false;e.inherit=false;e.flat=false;if(typeof e.unflat!=="string"){e.unflat="."}}if(e.inherit){e.own=true;e.proto=false}else if(e.proto){e.own=true}if(!t||typeof t!=="object"&&typeof t!=="function"){f=true}if(!e.skipRoot&&(e.inherit||e.proto)){for(n=u-1;n>=0;n--){o=r[n];if(o&&(typeof o==="object"||typeof o==="function")){if(e.inherit){if(f){t=Object.create(o)}else{Object.setPrototypeOf(t,o)}}else if(e.proto){if(f){t=Object.create(Object.getPrototypeOf(o))}else{Object.setPrototypeOf(t,Object.getPrototypeOf(o))}}break}}}else if(f){t={}}a.references={sources:[],targets:[]};for(n=0;n<u;n++){o=r[n];if(!o||typeof o!=="object"&&typeof o!=="function"){continue}i(a,e,t,o,e.mask<=n+1)}return t}t.exports=n;function i(e,t,r,n,o){var f,u,a,s,c,p,d,l,y,b,h,j,g,w=-1;if(t.maxDepth&&e.depth>t.maxDepth){throw new Error("[tree] extend(): max depth reached("+t.maxDepth+")")}if(t.circular){e.references.sources.push(n);e.references.targets.push(r)}if(t.own){if(t.nonEnum){s=Object.getOwnPropertyNames(n)}else{s=Object.keys(n)}}else{s=n}for(c in s){if(t.own){c=s[c]}if(c==="__proto__"){continue}if(t.descriptor){y=Object.getOwnPropertyDescriptor(n,c);p=y.value}else{p=n[c]}h=r;b=e.prefix+c;if(t.nofunc&&typeof p==="function"){continue}if(t.unflat&&e.depth===0){a=c.split(t.unflat);u=a.length-1;if(u){for(f=0;f<u;f++){if(!h[a[f]]||typeof h[a[f]]!=="object"&&typeof h[a[f]]!=="function"){h[a[f]]={}}h=h[a[f]]}b=e.prefix+a[u]}}j=h[b];g=j&&(typeof j==="object"||typeof j==="function");d=p&&(typeof p==="object"||typeof p==="function");if(t.deep&&p&&(typeof p==="object"||t.deepFunc&&typeof p==="function")&&(!t.descriptor||!y.get)&&((l=Object.getPrototypeOf(p))||true)&&(!(t.deep instanceof Set)||t.deep.has(l))&&(!t.immutables||!t.immutables.has(l))&&(!t.preserve||g)&&(!o||g)){if(t.circular){w=e.references.sources.indexOf(p)}if(t.flat){if(w>=0){continue}i({depth:e.depth+1,prefix:e.prefix+c+t.flat,references:e.references},t,h,p,o)}else{if(w>=0){j=e.references.targets[w];if(t.descriptor){Object.defineProperty(h,b,{value:j,enumerable:y.enumerable,writable:y.writable,configurable:y.configurable})}else{h[b]=j}continue}if(!g||!Object.prototype.hasOwnProperty.call(h,b)){if(Array.isArray(p)){j=[]}else if(t.proto){j=Object.create(l)}else if(t.inherit){j=Object.create(p)}else{j={}}if(t.descriptor){Object.defineProperty(h,b,{value:j,enumerable:y.enumerable,writable:y.writable,configurable:y.configurable})}else{h[b]=j}}else if(t.proto&&Object.getPrototypeOf(j)!==l){Object.setPrototypeOf(j,l)}else if(t.inherit&&Object.getPrototypeOf(j)!==p){Object.setPrototypeOf(j,p)}if(t.circular){e.references.sources.push(p);e.references.targets.push(j)}i({depth:e.depth+1,prefix:"",references:e.references},t,j,p,o)}}else if(o&&(j===undefined||g||d)){continue}else if(t.preserve&&j!==undefined){continue}else if(!t.inherit){if(t.descriptor){Object.defineProperty(h,b,y)}else{h[b]=j=p}}if(t.move){delete n[c]}}}},{}],5:[function(e,t,r){"use strict";const n={};t.exports=n;const i="This would cause prototype pollution";n.op=function(e,t,r,n){var o,f,u,a,s,c=false,p=false,d,l=true;if(!t||typeof t!=="object"){return}if(typeof r==="string"){if(r){f=r.match(/([.#[\]]|[^.#[\]]+)/g)}else{f=[""]}if(f[0]==="."){f.unshift("")}if(f[f.length-1]==="."){f.push("")}}else if(Array.isArray(r)){f=r;p=true}else{throw new TypeError("[tree.path] ."+e+"(): the path argument should be a string or an array")}switch(e){case"get":case"delete":d=false;break;case"set":case"define":case"inc":case"dec":case"append":case"prepend":case"concat":case"insert":case"autoPush":d=true;break;default:throw new TypeError("[tree.path] .op(): wrong type of operation '"+e+"'")}a=t;u=f.length-1;for(o=0;o<=u;o++){if(p){if(s===undefined){s=f[o];if(typeof s==="object"||s==="__proto__"){throw new Error(i)}continue}if(typeof a[s]==="function"){throw new Error(i)}if(!a[s]||typeof a[s]!=="object"){if(!d){return undefined}a[s]={}}a=a[s];s=f[o];if(typeof s==="object"||s==="__proto__"){throw new Error(i)}continue}else if(f[o]==="."){c=false;if(s===undefined){if(!l){l=true;continue}s=""}if(typeof a[s]==="function"){throw new Error(i)}if(!a[s]||typeof a[s]!=="object"){if(!d){return undefined}a[s]={}}a=a[s];l=true;continue}else if(f[o]==="#"||f[o]==="["){c=true;l=false;if(s===undefined){if(!Array.isArray(a)){return undefined}continue}if(typeof a[s]==="function"){throw new Error(i)}if(!a[s]||!Array.isArray(a[s])){if(!d){return undefined}a[s]=[]}a=a[s];continue}else if(f[o]==="]"){l=false;continue}l=false;if(!c){s=f[o];if(typeof s==="object"||s==="__proto__"){throw new Error(i)}continue}switch(f[o]){case"length":s="length";break;case"first":s=0;break;case"last":s=a.length-1;if(s<0){s=0}break;case"next":if(!d){return undefined}s=a.length;break;case"insert":if(!d){return undefined}a.unshift(undefined);s=0;break;default:s=parseInt(f[o],10)}}switch(e){case"get":return a[s];case"delete":if(c&&typeof s==="number"){a.splice(s,1)}else{delete a[s]}return;case"set":a[s]=n;return a[s];case"define":if(!(s in a)){a[s]=n}return a[s];case"inc":if(typeof a[s]==="number"){a[s]++}else if(!a[s]||typeof a[s]!=="object"){a[s]=1}return a[s];case"dec":if(typeof a[s]==="number"){a[s]--}else if(!a[s]||typeof a[s]!=="object"){a[s]=-1}return a[s];case"append":if(!a[s]){a[s]=[n]}else if(Array.isArray(a[s])){a[s].push(n)}return a[s];case"prepend":if(!a[s]){a[s]=[n]}else if(Array.isArray(a[s])){a[s].unshift(n)}return a[s];case"concat":if(!a[s]){a[s]=n}else if(Array.isArray(a[s])&&Array.isArray(n)){a[s]=a[s].concat(n)}return a[s];case"insert":if(!a[s]){a[s]=n}else if(Array.isArray(a[s])&&Array.isArray(n)){a[s]=n.concat(a[s])}return a[s];case"autoPush":if(a[s]===undefined){a[s]=n}else if(Array.isArray(a[s])){a[s].push(n)}else{a[s]=[a[s],n]}return a[s]}};n.get=n.op.bind(undefined,"get");n.delete=n.op.bind(undefined,"delete");n.set=n.op.bind(undefined,"set");n.define=n.op.bind(undefined,"define");n.inc=n.op.bind(undefined,"inc");n.dec=n.op.bind(undefined,"dec");n.append=n.op.bind(undefined,"append");n.prepend=n.op.bind(undefined,"prepend");n.concat=n.op.bind(undefined,"concat");n.insert=n.op.bind(undefined,"insert");n.autoPush=n.op.bind(undefined,"autoPush");n.prototype={get:function(e){return n.get(this,e)},delete:function(e){return n.delete(this,e)},set:function(e,t){return n.set(this,e,t)},define:function(e,t){return n.define(this,e,t)},inc:function(e,t){return n.inc(this,e,t)},dec:function(e,t){return n.dec(this,e,t)},append:function(e,t){return n.append(this,e,t)},prepend:function(e,t){return n.prepend(this,e,t)},concat:function(e,t){return n.concat(this,e,t)},insert:function(e,t){return n.insert(this,e,t)},autoPush:function(e,t){return n.autoPush(this,e,t)}};n.upgrade=function(e){Object.defineProperties(e,{get:{value:n.op.bind(undefined,"get",e)},delete:{value:n.op.bind(undefined,"delete",e)},set:{value:n.op.bind(undefined,"set",e)},define:{value:n.op.bind(undefined,"define",e)},inc:{value:n.op.bind(undefined,"inc",e)},dec:{value:n.op.bind(undefined,"dec",e)},append:{value:n.op.bind(undefined,"append",e)},prepend:{value:n.op.bind(undefined,"prepend",e)},concat:{value:n.op.bind(undefined,"concat",e)},insert:{value:n.op.bind(undefined,"insert",e)},autoPush:{value:n.op.bind(undefined,"autoPush",e)}})}},{}]},{},[1])(1)});
    \ No newline at end of file
    +(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.treeKit=e()}})(function(){var e,t,r;return function(){function e(t,r,n){function f(o,u){if(!r[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var s=new Error("Cannot find module '"+o+"'");throw s.code="MODULE_NOT_FOUND",s}var p=r[o]={exports:{}};t[o][0].call(p.exports,function(e){var r=t[o][1][e];return f(r||e)},p,p.exports,e,t,r,n)}return r[o].exports}for(var i="function"==typeof require&&require,o=0;o<n.length;o++)f(n[o]);return f}return e}()({1:[function(e,t,r){"use strict";const n={};t.exports=n;n.extend=e("./extend.js");n.clone=e("./clone.js");n.path=e("./path.js");n.dotPath=e("./dotPath.js")},{"./clone.js":2,"./dotPath.js":3,"./extend.js":4,"./path.js":5}],2:[function(e,t,r){"use strict";function n(e,t){var r=Object.getPrototypeOf(e);if(n.opaque.has(r)){return n.opaque.get(r)(e)}var f,i,o,u,a,s,p=[{source:e,target:Array.isArray(e)?[]:Object.create(r)}],c=p[0].target,l=new Map;l.set(e,c);while(u=p.shift()){o=Object.getOwnPropertyNames(u.source);for(f=0;f<o.length;f++){i=Object.getOwnPropertyDescriptor(u.source,o[f]);if(!i.value||typeof i.value!=="object"){Object.defineProperty(u.target,o[f],i);continue}a=i.value;if(t){if(l.has(a)){i.value=l.get(a);Object.defineProperty(u.target,o[f],i);continue}}s=Object.getPrototypeOf(i.value);if(n.opaque.has(s)){i.value=n.opaque.get(s)(i.value);Object.defineProperty(u.target,o[f],i);continue}i.value=Array.isArray(a)?[]:Object.create(s);if(t){l.set(a,i.value)}Object.defineProperty(u.target,o[f],i);p.push({source:a,target:i.value})}}return c}t.exports=n;n.opaque=new Map;n.opaque.set(Date.prototype,e=>new Date(e))},{}],3:[function(e,t,r){"use strict";const n={};t.exports=n;const f=[];const i="This would cause prototype pollution";function o(e){if(Array.isArray(e)){return e}if(!e){return f}if(typeof e==="string"){return e[e.length-1]==="."?e.slice(0,-1).split("."):e.split(".")}throw new TypeError("[tree.dotPath]: the path argument should be a string or an array")}function u(e,t,r=0){var n,f,o,u=e;for(n=0,f=t.length+r;n<f;n++){o=t[n];if(typeof o==="object"||o==="__proto__"||typeof u==="function"){throw new Error(i)}if(!u||typeof u!=="object"){return undefined}u=u[o]}return u}function a(e,t){var r,n,f,o=e;for(r=0,n=t.length-1;r<n;r++){f=t[r];if(typeof f==="object"||f==="__proto__"||typeof o[f]==="function"){throw new Error(i)}if(!o[f]||typeof o[f]!=="object"){o[f]={}}o=o[f]}return o}n.get=((e,t)=>u(e,o(t)));n.set=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);u[f]=r;return r});n.define=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(!(f in u)){u[f]=r}return r});n.inc=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(typeof u[f]==="number"){u[f]++}else if(!u[f]||typeof u[f]!=="object"){u[f]=1}return r});n.dec=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(typeof u[f]==="number"){u[f]--}else if(!u[f]||typeof u[f]!=="object"){u[f]=-1}return r});n.concat=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(!u[f]){u[f]=r}else if(Array.isArray(u[f])&&Array.isArray(r)){u[f]=u[f].concat(r)}return r});n.insert=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(!u[f]){u[f]=r}else if(Array.isArray(u[f])&&Array.isArray(r)){u[f]=r.concat(u[f])}return r});n.delete=((e,t)=>{var r=o(t),n=r[r.length-1];if(typeof n==="object"||n==="__proto__"){throw new Error(i)}var f=u(e,r,-1);if(!f||typeof f!=="object"){return false}return delete f[n]});n.autoPush=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(u[f]===undefined){u[f]=r}else if(Array.isArray(u[f])){u[f].push(r)}else{u[f]=[u[f],r]}return u[f]});n.append=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(!u[f]){u[f]=[r]}else if(Array.isArray(u[f])){u[f].push(r)}return u[f]});n.prepend=((e,t,r)=>{if(!e||typeof e!=="object"){return undefined}var n=o(t),f=n[n.length-1];if(typeof f==="object"||f==="__proto__"){throw new Error(i)}var u=a(e,n);if(!u[f]){u[f]=[r]}else if(Array.isArray(u[f])){u[f].unshift(r)}return u[f]})},{}],4:[function(e,t,r){"use strict";function n(e,t,...r){var n,i,o=false,u=r.length;if(!u){return t}if(!e||typeof e!=="object"){e={}}var a={depth:0,prefix:""};if(e.deep){if(Array.isArray(e.deep)){e.deep=new Set(e.deep)}else if(!(e.deep instanceof Set)){e.deep=true}}if(e.immutables){if(Array.isArray(e.immutables)){e.immutables=new Set(e.immutables)}else if(!(e.immutables instanceof Set)){delete e.immutables}}if(!e.maxDepth&&e.deep&&!e.circular){e.maxDepth=100}if(e.deepFunc){e.deep=true}if(e.flat){e.deep=true;e.proto=false;e.inherit=false;e.unflat=false;if(typeof e.flat!=="string"){e.flat="."}}if(e.unflat){e.deep=false;e.proto=false;e.inherit=false;e.flat=false;if(typeof e.unflat!=="string"){e.unflat="."}}if(e.inherit){e.own=true;e.proto=false}else if(e.proto){e.own=true}if(!t||typeof t!=="object"&&typeof t!=="function"){o=true}if(!e.skipRoot&&(e.inherit||e.proto)){for(n=u-1;n>=0;n--){i=r[n];if(i&&(typeof i==="object"||typeof i==="function")){if(e.inherit){if(o){t=Object.create(i)}else{Object.setPrototypeOf(t,i)}}else if(e.proto){if(o){t=Object.create(Object.getPrototypeOf(i))}else{Object.setPrototypeOf(t,Object.getPrototypeOf(i))}}break}}}else if(o){t={}}a.references={sources:[],targets:[]};for(n=0;n<u;n++){i=r[n];if(!i||typeof i!=="object"&&typeof i!=="function"){continue}f(a,e,t,i,e.mask<=n+1)}return t}t.exports=n;function f(e,t,r,n,f){var o,u;if(t.maxDepth&&e.depth>t.maxDepth){throw new Error("[tree] extend(): max depth reached("+t.maxDepth+")")}if(t.circular){e.references.sources.push(n);e.references.targets.push(r)}if(t.unflat&&e.depth===0){for(u in n){e.unflatKeys=u.split(t.unflat);e.unflatIndex=0;e.unflatFullKey=u;i(e,t,r,n,e.unflatKeys[e.unflatIndex],f)}delete e.unflatKeys;delete e.unflatIndex;delete e.unflatFullKey}else if(t.own){if(t.nonEnum){o=Object.getOwnPropertyNames(n)}else{o=Object.keys(n)}for(u of o){i(e,t,r,n,u,f)}}else{for(u in n){i(e,t,r,n,u,f)}}}function i(e,t,r,n,o,u){if(o==="__proto__"){return}let a,s,p;if(e.unflatKeys){if(e.unflatIndex<e.unflatKeys.length-1){a={}}else{a=n[e.unflatFullKey]}}else if(t.descriptor){s=Object.getOwnPropertyDescriptor(n,o);a=s.value}else{a=n[o]}let c=e.prefix+o;if(t.nofunc&&typeof a==="function"){return}let l=r[c];let d=l&&(typeof l==="object"||typeof l==="function");let y=a&&(typeof a==="object"||typeof a==="function");if((t.deep||e.unflatKeys)&&a&&(typeof a==="object"||t.deepFunc&&typeof a==="function")&&(!t.descriptor||!s.get)&&((p=Object.getPrototypeOf(a))||true)&&(!(t.deep instanceof Set)||t.deep.has(p))&&(!t.immutables||!t.immutables.has(p))&&(!t.preserve||d)&&(!u||d)){let y=t.circular?e.references.sources.indexOf(a):-1;if(t.flat){if(y>=0){return}f({depth:e.depth+1,prefix:e.prefix+o+t.flat,references:e.references},t,r,a,u)}else{if(y>=0){l=e.references.targets[y];if(t.descriptor){Object.defineProperty(r,c,{value:l,enumerable:s.enumerable,writable:s.writable,configurable:s.configurable})}else{r[c]=l}return}if(!d||!Object.prototype.hasOwnProperty.call(r,c)){if(Array.isArray(a)){l=[]}else if(t.proto){l=Object.create(p)}else if(t.inherit){l=Object.create(a)}else{l={}}if(t.descriptor){Object.defineProperty(r,c,{value:l,enumerable:s.enumerable,writable:s.writable,configurable:s.configurable})}else{r[c]=l}}else if(t.proto&&Object.getPrototypeOf(l)!==p){Object.setPrototypeOf(l,p)}else if(t.inherit&&Object.getPrototypeOf(l)!==a){Object.setPrototypeOf(l,a)}if(t.circular){e.references.sources.push(a);e.references.targets.push(l)}if(e.unflatKeys&&e.unflatIndex<e.unflatKeys.length-1){let r=e.unflatKeys[e.unflatIndex+1];i({depth:e.depth,unflatKeys:e.unflatKeys,unflatIndex:e.unflatIndex+1,unflatFullKey:e.unflatFullKey,unflatValue:e.unflatValue,prefix:"",references:e.references},t,l,n,r,u)}else{f({depth:e.depth+1,prefix:"",references:e.references},t,l,a,u)}}}else if(u&&(l===undefined||d||y)){return}else if(t.preserve&&l!==undefined){return}else if(!t.inherit){if(t.descriptor){Object.defineProperty(r,c,s)}else{r[c]=l=a}}if(t.move){delete n[o]}}},{}],5:[function(e,t,r){"use strict";const n={};t.exports=n;const f="This would cause prototype pollution";n.op=function(e,t,r,n){var i,o,u,a,s,p=false,c=false,l,d=true;if(!t||typeof t!=="object"){return}if(typeof r==="string"){if(r){o=r.match(/([.#[\]]|[^.#[\]]+)/g)}else{o=[""]}if(o[0]==="."){o.unshift("")}if(o[o.length-1]==="."){o.push("")}}else if(Array.isArray(r)){o=r;c=true}else{throw new TypeError("[tree.path] ."+e+"(): the path argument should be a string or an array")}switch(e){case"get":case"delete":l=false;break;case"set":case"define":case"inc":case"dec":case"append":case"prepend":case"concat":case"insert":case"autoPush":l=true;break;default:throw new TypeError("[tree.path] .op(): wrong type of operation '"+e+"'")}a=t;u=o.length-1;for(i=0;i<=u;i++){if(c){if(s===undefined){s=o[i];if(typeof s==="object"||s==="__proto__"){throw new Error(f)}continue}if(typeof a[s]==="function"){throw new Error(f)}if(!a[s]||typeof a[s]!=="object"){if(!l){return undefined}a[s]={}}a=a[s];s=o[i];if(typeof s==="object"||s==="__proto__"){throw new Error(f)}continue}else if(o[i]==="."){p=false;if(s===undefined){if(!d){d=true;continue}s=""}if(typeof a[s]==="function"){throw new Error(f)}if(!a[s]||typeof a[s]!=="object"){if(!l){return undefined}a[s]={}}a=a[s];d=true;continue}else if(o[i]==="#"||o[i]==="["){p=true;d=false;if(s===undefined){if(!Array.isArray(a)){return undefined}continue}if(typeof a[s]==="function"){throw new Error(f)}if(!a[s]||!Array.isArray(a[s])){if(!l){return undefined}a[s]=[]}a=a[s];continue}else if(o[i]==="]"){d=false;continue}d=false;if(!p){s=o[i];if(typeof s==="object"||s==="__proto__"){throw new Error(f)}continue}switch(o[i]){case"length":s="length";break;case"first":s=0;break;case"last":s=a.length-1;if(s<0){s=0}break;case"next":if(!l){return undefined}s=a.length;break;case"insert":if(!l){return undefined}a.unshift(undefined);s=0;break;default:s=parseInt(o[i],10)}}switch(e){case"get":return a[s];case"delete":if(p&&typeof s==="number"){a.splice(s,1)}else{delete a[s]}return;case"set":a[s]=n;return a[s];case"define":if(!(s in a)){a[s]=n}return a[s];case"inc":if(typeof a[s]==="number"){a[s]++}else if(!a[s]||typeof a[s]!=="object"){a[s]=1}return a[s];case"dec":if(typeof a[s]==="number"){a[s]--}else if(!a[s]||typeof a[s]!=="object"){a[s]=-1}return a[s];case"append":if(!a[s]){a[s]=[n]}else if(Array.isArray(a[s])){a[s].push(n)}return a[s];case"prepend":if(!a[s]){a[s]=[n]}else if(Array.isArray(a[s])){a[s].unshift(n)}return a[s];case"concat":if(!a[s]){a[s]=n}else if(Array.isArray(a[s])&&Array.isArray(n)){a[s]=a[s].concat(n)}return a[s];case"insert":if(!a[s]){a[s]=n}else if(Array.isArray(a[s])&&Array.isArray(n)){a[s]=n.concat(a[s])}return a[s];case"autoPush":if(a[s]===undefined){a[s]=n}else if(Array.isArray(a[s])){a[s].push(n)}else{a[s]=[a[s],n]}return a[s]}};n.get=n.op.bind(undefined,"get");n.delete=n.op.bind(undefined,"delete");n.set=n.op.bind(undefined,"set");n.define=n.op.bind(undefined,"define");n.inc=n.op.bind(undefined,"inc");n.dec=n.op.bind(undefined,"dec");n.append=n.op.bind(undefined,"append");n.prepend=n.op.bind(undefined,"prepend");n.concat=n.op.bind(undefined,"concat");n.insert=n.op.bind(undefined,"insert");n.autoPush=n.op.bind(undefined,"autoPush");n.prototype={get:function(e){return n.get(this,e)},delete:function(e){return n.delete(this,e)},set:function(e,t){return n.set(this,e,t)},define:function(e,t){return n.define(this,e,t)},inc:function(e,t){return n.inc(this,e,t)},dec:function(e,t){return n.dec(this,e,t)},append:function(e,t){return n.append(this,e,t)},prepend:function(e,t){return n.prepend(this,e,t)},concat:function(e,t){return n.concat(this,e,t)},insert:function(e,t){return n.insert(this,e,t)},autoPush:function(e,t){return n.autoPush(this,e,t)}};n.upgrade=function(e){Object.defineProperties(e,{get:{value:n.op.bind(undefined,"get",e)},delete:{value:n.op.bind(undefined,"delete",e)},set:{value:n.op.bind(undefined,"set",e)},define:{value:n.op.bind(undefined,"define",e)},inc:{value:n.op.bind(undefined,"inc",e)},dec:{value:n.op.bind(undefined,"dec",e)},append:{value:n.op.bind(undefined,"append",e)},prepend:{value:n.op.bind(undefined,"prepend",e)},concat:{value:n.op.bind(undefined,"concat",e)},insert:{value:n.op.bind(undefined,"insert",e)},autoPush:{value:n.op.bind(undefined,"autoPush",e)}})}},{}]},{},[1])(1)});
    \ No newline at end of file
    
  • CHANGELOG+6 0 modified
    @@ -1,4 +1,10 @@
     
    +v0.7.5
    +------
    +
    +Fix prototype pollution in .extend() when the 'unflat' option is set
    +
    +
     v0.7.4
     ------
     
    
  • .eslintrc.js+5 3 modified
    @@ -2,13 +2,15 @@ module.exports = {
     	'root': true ,
     	'env': {
     		'browser': true ,
    +		'node': true ,
     		'es6': true ,
    -		'node': true
    +		'es2022': true
     	} ,
     	'parserOptions': {
    -		'ecmaVersion': 2020
    +		'ecmaVersion': 2022
     	} ,
     	'extends': [ 'eslint:recommended' ] ,
    +	'ignorePatterns': [ "*.min.js" ] ,
     	'rules': {
     		
     		/*
    @@ -81,7 +83,7 @@ module.exports = {
     			'words': true ,
     			'nonwords': true ,
     			'overrides': {
    -				'-': false ,
    +				//'-': false ,
     			}
     		} ] ,
     		'space-in-parens': [ 'error' , 'always' , {
    
  • lib/dotPath.js+3 3 modified
    @@ -51,7 +51,7 @@ function toPathArray( path ) {
     
     	if ( ! path ) { return EMPTY_PATH ; }
     	if ( typeof path === 'string' ) {
    -		return path[ path.length - 1 ] === '.' ? path.slice( 0 , -1 ).split( '.' ) : path.split( '.' ) ;
    +		return path[ path.length - 1 ] === '.' ? path.slice( 0 , - 1 ).split( '.' ) : path.split( '.' ) ;
     	}
     
     	throw new TypeError( '[tree.dotPath]: the path argument should be a string or an array' ) ;
    @@ -178,7 +178,7 @@ dotPath.dec = ( object , path , value ) => {
     	var pointer = pave( object , pathArray ) ;
     
     	if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
    -	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = -1 ; }
    +	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }
     
     	return value ;
     } ;
    @@ -239,7 +239,7 @@ dotPath.delete = ( object , path ) => {
     
     	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
     
    -	var pointer = walk( object , pathArray , -1 ) ;
    +	var pointer = walk( object , pathArray , - 1 ) ;
     
     	if ( ! pointer || typeof pointer !== 'object' ) { return false ; }
     
    
  • lib/extend.js+160 128 modified
    @@ -150,10 +150,7 @@ module.exports = extend ;
     
     
     function extendOne( runtime , options , target , source , mask ) {
    -	var j , jmax , path ,
    -		sourceKeys , sourceKey , sourceValue , sourceValueIsObject , sourceValueProto , sourceDescriptor ,
    -		targetKey , targetPointer , targetValue , targetValueIsObject ,
    -		indexOfSource = -1 ;
    +	var sourceKeys , sourceKey ;
     
     	// Max depth check
     	if ( options.maxDepth && runtime.depth > options.maxDepth ) {
    @@ -166,157 +163,192 @@ function extendOne( runtime , options , target , source , mask ) {
     		runtime.references.targets.push( target ) ;
     	}
     
    -	if ( options.own ) {
    +	// 'unflat' mode computing
    +	if ( options.unflat && runtime.depth === 0 ) {
    +		for ( sourceKey in source ) {
    +			runtime.unflatKeys = sourceKey.split( options.unflat ) ;
    +			runtime.unflatIndex = 0 ;
    +			runtime.unflatFullKey = sourceKey ;
    +			extendOneKV( runtime , options , target , source , runtime.unflatKeys[ runtime.unflatIndex ] , mask ) ;
    +		}
    +
    +		delete runtime.unflatKeys ;
    +		delete runtime.unflatIndex ;
    +		delete runtime.unflatFullKey ;
    +	}
    +	else if ( options.own ) {
     		if ( options.nonEnum ) { sourceKeys = Object.getOwnPropertyNames( source ) ; }
     		else { sourceKeys = Object.keys( source ) ; }
    +
    +		for ( sourceKey of sourceKeys ) {
    +			extendOneKV( runtime , options , target , source , sourceKey , mask ) ;
    +		}
    +	}
    +	else {
    +		for ( sourceKey in source ) {
    +			extendOneKV( runtime , options , target , source , sourceKey , mask ) ;
    +		}
     	}
    -	else { sourceKeys = source ; }
    +}
     
    -	for ( sourceKey in sourceKeys ) {
    -		if ( options.own ) { sourceKey = sourceKeys[ sourceKey ] ; }
     
    -		// OMG, this DEPRECATED __proto__ shit is still alive and can be used to hack anything ><
    -		if ( sourceKey === '__proto__' ) { continue ; }
     
    -		// If descriptor is on, get it now
    -		if ( options.descriptor ) {
    -			sourceDescriptor = Object.getOwnPropertyDescriptor( source , sourceKey ) ;
    -			sourceValue = sourceDescriptor.value ;
    +function extendOneKV( runtime , options , target , source , sourceKey , mask ) {
    +	// OMG, this DEPRECATED __proto__ shit is still alive and can be used to hack anything ><
    +	if ( sourceKey === '__proto__' ) { return ; }
    +
    +	let sourceValue , sourceDescriptor , sourceValueProto ;
    +
    +	if ( runtime.unflatKeys ) {
    +		if ( runtime.unflatIndex < runtime.unflatKeys.length - 1 ) {
    +			sourceValue = {} ;
     		}
     		else {
    -			// We have to trigger an eventual getter only once
    -			sourceValue = source[ sourceKey ] ;
    +			sourceValue = source[ runtime.unflatFullKey ] ;
     		}
    +	}
    +	else if ( options.descriptor ) {
    +		// If descriptor is on, get it now
    +		sourceDescriptor = Object.getOwnPropertyDescriptor( source , sourceKey ) ;
    +		sourceValue = sourceDescriptor.value ;
    +	}
    +	else {
    +		// We have to trigger an eventual getter only once
    +		sourceValue = source[ sourceKey ] ;
    +	}
     
    -		targetPointer = target ;
    -		targetKey = runtime.prefix + sourceKey ;
    -
    -		// Do not copy if property is a function and we don't want them
    -		if ( options.nofunc && typeof sourceValue === 'function' ) { continue ; }
    -
    -		// 'unflat' mode computing
    -		if ( options.unflat && runtime.depth === 0 ) {
    -			path = sourceKey.split( options.unflat ) ;
    -			jmax = path.length - 1 ;
    +	let targetKey = runtime.prefix + sourceKey ;
    +
    +	// Do not copy if property is a function and we don't want them
    +	if ( options.nofunc && typeof sourceValue === 'function' ) { return ; }
    +
    +	// Again, trigger an eventual getter only once
    +	let targetValue = target[ targetKey ] ;
    +	let targetValueIsObject = targetValue && ( typeof targetValue === 'object' || typeof targetValue === 'function' ) ;
    +	let sourceValueIsObject = sourceValue && ( typeof sourceValue === 'object' || typeof sourceValue === 'function' ) ;
    +
    +	if (
    +		( options.deep || runtime.unflatKeys )
    +		&& sourceValue
    +		&& ( typeof sourceValue === 'object' || ( options.deepFunc && typeof sourceValue === 'function' ) )
    +		&& ( ! options.descriptor || ! sourceDescriptor.get )
    +		// not a condition we just cache sourceValueProto now... ok it's trashy ><
    +		&& ( ( sourceValueProto = Object.getPrototypeOf( sourceValue ) ) || true )
    +		&& ( ! ( options.deep instanceof Set ) || options.deep.has( sourceValueProto ) )
    +		&& ( ! options.immutables || ! options.immutables.has( sourceValueProto ) )
    +		&& ( ! options.preserve || targetValueIsObject )
    +		&& ( ! mask || targetValueIsObject )
    +	) {
    +		let indexOfSource = options.circular ? runtime.references.sources.indexOf( sourceValue ) : - 1 ;
    +
    +		if ( options.flat ) {
    +			// No circular references reconnection when in 'flat' mode
    +			if ( indexOfSource >= 0 ) { return ; }
    +
    +			extendOne(
    +				{
    +					depth: runtime.depth + 1 ,
    +					prefix: runtime.prefix + sourceKey + options.flat ,
    +					references: runtime.references
    +				} ,
    +				options , target , sourceValue , mask
    +			) ;
    +		}
    +		else {
    +			if ( indexOfSource >= 0 ) {
    +				// Circular references reconnection...
    +				targetValue = runtime.references.targets[ indexOfSource ] ;
    +
    +				if ( options.descriptor ) {
    +					Object.defineProperty( target , targetKey , {
    +						value: targetValue ,
    +						enumerable: sourceDescriptor.enumerable ,
    +						writable: sourceDescriptor.writable ,
    +						configurable: sourceDescriptor.configurable
    +					} ) ;
    +				}
    +				else {
    +					target[ targetKey ] = targetValue ;
    +				}
     
    -			if ( jmax ) {
    -				for ( j = 0 ; j < jmax ; j ++ ) {
    -					if ( ! targetPointer[ path[ j ] ] ||
    -						( typeof targetPointer[ path[ j ] ] !== 'object' &&
    -							typeof targetPointer[ path[ j ] ] !== 'function' ) ) {
    -						targetPointer[ path[ j ] ] = {} ;
    -					}
    +				return ;
    +			}
     
    -					targetPointer = targetPointer[ path[ j ] ] ;
    +			if ( ! targetValueIsObject || ! Object.prototype.hasOwnProperty.call( target , targetKey ) ) {
    +				if ( Array.isArray( sourceValue ) ) { targetValue = [] ; }
    +				else if ( options.proto ) { targetValue = Object.create( sourceValueProto ) ; }
    +				else if ( options.inherit ) { targetValue = Object.create( sourceValue ) ; }
    +				else { targetValue = {} ; }
    +
    +				if ( options.descriptor ) {
    +					Object.defineProperty( target , targetKey , {
    +						value: targetValue ,
    +						enumerable: sourceDescriptor.enumerable ,
    +						writable: sourceDescriptor.writable ,
    +						configurable: sourceDescriptor.configurable
    +					} ) ;
    +				}
    +				else {
    +					target[ targetKey ] = targetValue ;
     				}
    -
    -				targetKey = runtime.prefix + path[ jmax ] ;
     			}
    -		}
    +			else if ( options.proto && Object.getPrototypeOf( targetValue ) !== sourceValueProto ) {
    +				Object.setPrototypeOf( targetValue , sourceValueProto ) ;
    +			}
    +			else if ( options.inherit && Object.getPrototypeOf( targetValue ) !== sourceValue ) {
    +				Object.setPrototypeOf( targetValue , sourceValue ) ;
    +			}
     
    -		// Again, trigger an eventual getter only once
    -		targetValue = targetPointer[ targetKey ] ;
    -		targetValueIsObject = targetValue && ( typeof targetValue === 'object' || typeof targetValue === 'function' ) ;
    -		sourceValueIsObject = sourceValue && ( typeof sourceValue === 'object' || typeof sourceValue === 'function' ) ;
    -
    -
    -		if ( options.deep	// eslint-disable-line no-constant-condition
    -			&& sourceValue
    -			&& ( typeof sourceValue === 'object' || ( options.deepFunc && typeof sourceValue === 'function' ) )
    -			&& ( ! options.descriptor || ! sourceDescriptor.get )
    -			// not a condition we just cache sourceValueProto now... ok it's trashy ><
    -			&& ( ( sourceValueProto = Object.getPrototypeOf( sourceValue ) ) || true )
    -			&& ( ! ( options.deep instanceof Set ) || options.deep.has( sourceValueProto ) )
    -			&& ( ! options.immutables || ! options.immutables.has( sourceValueProto ) )
    -			&& ( ! options.preserve || targetValueIsObject )
    -			&& ( ! mask || targetValueIsObject )
    -		) {
     			if ( options.circular ) {
    -				indexOfSource = runtime.references.sources.indexOf( sourceValue ) ;
    +				runtime.references.sources.push( sourceValue ) ;
    +				runtime.references.targets.push( targetValue ) ;
     			}
     
    -			if ( options.flat ) {
    -				// No circular references reconnection when in 'flat' mode
    -				if ( indexOfSource >= 0 ) { continue ; }
    -
    -				extendOne(
    -					{ depth: runtime.depth + 1 , prefix: runtime.prefix + sourceKey + options.flat , references: runtime.references } ,
    -					options , targetPointer , sourceValue , mask
    +			if ( runtime.unflatKeys && runtime.unflatIndex < runtime.unflatKeys.length - 1 ) {
    +				// Finish unflatting this property
    +				let nextSourceKey = runtime.unflatKeys[ runtime.unflatIndex + 1 ] ;
    +
    +				extendOneKV(
    +					{
    +						depth: runtime.depth ,
    +						unflatKeys: runtime.unflatKeys ,
    +						unflatIndex: runtime.unflatIndex + 1 ,
    +						unflatFullKey: runtime.unflatFullKey ,
    +						unflatValue: runtime.unflatValue ,
    +						prefix: '' ,
    +						references: runtime.references
    +					} ,
    +					options , targetValue , source , nextSourceKey , mask
     				) ;
     			}
     			else {
    -				if ( indexOfSource >= 0 ) {
    -					// Circular references reconnection...
    -					targetValue = runtime.references.targets[ indexOfSource ] ;
    -
    -					if ( options.descriptor ) {
    -						Object.defineProperty( targetPointer , targetKey , {
    -							value: targetValue ,
    -							enumerable: sourceDescriptor.enumerable ,
    -							writable: sourceDescriptor.writable ,
    -							configurable: sourceDescriptor.configurable
    -						} ) ;
    -					}
    -					else {
    -						targetPointer[ targetKey ] = targetValue ;
    -					}
    -
    -					continue ;
    -				}
    -
    -				if ( ! targetValueIsObject || ! Object.prototype.hasOwnProperty.call( targetPointer , targetKey ) ) {
    -					if ( Array.isArray( sourceValue ) ) { targetValue = [] ; }
    -					else if ( options.proto ) { targetValue = Object.create( sourceValueProto ) ; }	// jshint ignore:line
    -					else if ( options.inherit ) { targetValue = Object.create( sourceValue ) ; }
    -					else { targetValue = {} ; }
    -
    -					if ( options.descriptor ) {
    -						Object.defineProperty( targetPointer , targetKey , {
    -							value: targetValue ,
    -							enumerable: sourceDescriptor.enumerable ,
    -							writable: sourceDescriptor.writable ,
    -							configurable: sourceDescriptor.configurable
    -						} ) ;
    -					}
    -					else {
    -						targetPointer[ targetKey ] = targetValue ;
    -					}
    -				}
    -				else if ( options.proto && Object.getPrototypeOf( targetValue ) !== sourceValueProto ) {
    -					Object.setPrototypeOf( targetValue , sourceValueProto ) ;
    -				}
    -				else if ( options.inherit && Object.getPrototypeOf( targetValue ) !== sourceValue ) {
    -					Object.setPrototypeOf( targetValue , sourceValue ) ;
    -				}
    -
    -				if ( options.circular ) {
    -					runtime.references.sources.push( sourceValue ) ;
    -					runtime.references.targets.push( targetValue ) ;
    -				}
    -
     				// Recursively extends sub-object
     				extendOne(
    -					{ depth: runtime.depth + 1 , prefix: '' , references: runtime.references } ,
    +					{
    +						depth: runtime.depth + 1 ,
    +						prefix: '' ,
    +						references: runtime.references
    +					} ,
     					options , targetValue , sourceValue , mask
     				) ;
     			}
     		}
    -		else if ( mask && ( targetValue === undefined || targetValueIsObject || sourceValueIsObject ) ) {
    -			// Do not create new value, and so do not delete source's properties that were not moved.
    -			// We also do not overwrite object with non-object, and we don't overwrite non-object with object (preserve hierarchy)
    -			continue ;
    -		}
    -		else if ( options.preserve && targetValue !== undefined ) {
    -			// Do not overwrite, and so do not delete source's properties that were not moved
    -			continue ;
    -		}
    -		else if ( ! options.inherit ) {
    -			if ( options.descriptor ) { Object.defineProperty( targetPointer , targetKey , sourceDescriptor ) ; }
    -			else { targetPointer[ targetKey ] = targetValue = sourceValue ; }
    -		}
    -
    -		// Delete owned property of the source object
    -		if ( options.move ) { delete source[ sourceKey ] ; }
     	}
    +	else if ( mask && ( targetValue === undefined || targetValueIsObject || sourceValueIsObject ) ) {
    +		// Do not create new value, and so do not delete source's properties that were not moved.
    +		// We also do not overwrite object with non-object, and we don't overwrite non-object with object (preserve hierarchy)
    +		return ;
    +	}
    +	else if ( options.preserve && targetValue !== undefined ) {
    +		// Do not overwrite, and so do not delete source's properties that were not moved
    +		return ;
    +	}
    +	else if ( ! options.inherit ) {
    +		if ( options.descriptor ) { Object.defineProperty( target , targetKey , sourceDescriptor ) ; }
    +		else { target[ targetKey ] = targetValue = sourceValue ; }
    +	}
    +
    +	// Delete owned property of the source object
    +	if ( options.move ) { delete source[ sourceKey ] ; }
     }
     
    
  • lib/path.js+1 1 modified
    @@ -215,7 +215,7 @@ treePath.op = function( type , object , path , value ) {
     			return pointer[ key ] ;
     		case 'dec' :
     			if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
    -			else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = -1 ; }
    +			else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }
     			return pointer[ key ] ;
     		case 'append' :
     			if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
    
  • .npmignore+57 0 added
    @@ -0,0 +1,57 @@
    +# +++ .gitignore
    +# Specific #
    +############
    +
    +*.local.*
    +*.local
    +*.log
    +*.html.gz
    +*.css.gz
    +*.js.gz
    +.spellcast
    +build
    +_build
    +_templates
    +_static
    +
    +
    +# gitignore / Node.gitignore #
    +##############################
    +lib-cov
    +lcov.info
    +*.seed
    +*.log
    +*.csv
    +*.dat
    +*.out
    +*.pid
    +*.gz
    +
    +pids
    +logs
    +results
    +build
    +.grunt
    +package-lock.json
    +
    +node_modules
    +
    +
    +# OS generated files #
    +######################
    +.DS_Store
    +.DS_Store?
    +._*
    +.Spotlight-V100
    +.Trashes
    +Icon?
    +ehthumbs.db
    +Thumbs.db
    +
    +
    +
    +# --- .gitignore
    +test
    +log
    +sample
    +wfm.json
    
  • package.json+1 1 modified
    @@ -1,6 +1,6 @@
     {
       "name": "tree-kit",
    -  "version": "0.7.4",
    +  "version": "0.7.5",
       "description": "Tree utilities which provides a full-featured extend and object-cloning facility, and various tools to deal with nested object structures.",
       "main": "lib/tree.js",
       "directories": {
    
  • test/dotPath-test.js+3 0 modified
    @@ -611,6 +611,7 @@ describe( "Tree's array dot-path on objects" , () => {
     describe( ".dotPath() security issues" , () => {
     
     	it( "Prototype pollution using .__proto__" , () => {
    +		delete Object.prototype.hack ;
     		expect( () => path.set( {} , '__proto__.hack' , 'hacked' ) ).to.throw() ;
     		expect( Object.prototype.hack ).to.be.undefined() ;
     		expect( () => path.set( {} , '__proto__' , 'hacked' ) ).to.throw() ;
    @@ -620,6 +621,7 @@ describe( ".dotPath() security issues" , () => {
     	} ) ;
     
     	it( "Prototype pollution using a path array: [['__proto__']]" , () => {
    +		delete Object.prototype.hack ;
     		expect( () => path.set( {} , [['__proto__'],'hack'] , 'hacked' ) ).to.throw() ;
     		expect( Object.prototype.hack ).to.be.undefined() ;
     		expect( () => path.set( {} , '__proto__' , 'hacked' ) ).to.throw() ;
    @@ -629,6 +631,7 @@ describe( ".dotPath() security issues" , () => {
     	} ) ;
     
     	it( "Prototype pollution using .constructor" , () => {
    +		delete Object.prototype.hack ;
     		expect( () => path.set( {} , 'constructor.prototype' , 'hacked' ) ).to.throw() ;
     		expect( () => path.set( {} , 'constructor.prototype.hack' , 'hacked' ) ).to.throw() ;
     		expect( Object.prototype.hack ).to.be.undefined() ;
    
  • test/extend-test.js+67 0 modified
    @@ -816,6 +816,40 @@ describe( "extend()" , () => {
     	it( "with 'unflat' option" , () => {
     		var e , o ;
     		
    +		e = {} ;
    +		extend( { unflat: true } , e , { 'subtree.a': 'value' } ) ;
    +		expect( e ).to.equal( {
    +			subtree: {
    +				a: 'value' ,
    +			}
    +		} ) ;
    +		extend( { unflat: true } , e , { 'subtree.b': 'value2' } ) ;
    +		expect( e ).to.equal( {
    +			subtree: {
    +				a: 'value' ,
    +				b: 'value2' ,
    +			}
    +		} ) ;
    +		extend( { unflat: true } , e , { 'subtree.a': 'replaced' } ) ;
    +		expect( e ).to.equal( {
    +			subtree: {
    +				a: 'replaced' ,
    +				b: 'value2' ,
    +			}
    +		} ) ;
    +		extend( { unflat: true } , e , { 'subtree2.subtree.c': 'value3' } ) ;
    +		expect( e ).to.equal( {
    +			subtree: {
    +				a: 'replaced' ,
    +				b: 'value2' ,
    +			} ,
    +			subtree2: {
    +				subtree: {
    +					c: 'value3' ,
    +				}
    +			}
    +		} ) ;
    +
     		o = {
     			three: 3 ,
     			four: '4' ,
    @@ -1012,6 +1046,39 @@ describe( "extend()" , () => {
     	} ) ;
     	
     	it( "with 'skipRoot' option" ) ;
    +	
     } ) ;
     
     
    +
    +describe( ".extend() security issues" , () => {
    +
    +	it( "Prototype pollution using .__proto__" , () => {
    +		delete Object.prototype.hack ;
    +		var o = {} ;
    +		extend( { deep: true } , o , { __proto__: { prototype: { hack: "hacked" } } } ) ;
    +		expect( Object.prototype.hack ).to.be.undefined() ;
    +	} ) ;
    +
    +	it( "Prototype pollution using .constructor" , () => {
    +		delete Object.prototype.hack ;
    +		var o = {} ;
    +		extend( { deep: true } , o , { constructor: { prototype: { hack: "hacked" } } } ) ;
    +		expect( Object.prototype.hack ).to.be.undefined() ;
    +	} ) ;
    +
    +	it( "Prototype pollution using 'unflat' option and using .__proto__" , () => {
    +		delete Object.prototype.hack ;
    +		var o = {} ;
    +		extend( { unflat: true } , o , { "__proto__.hack": "hacked" } ) ;
    +		expect( Object.prototype.hack ).to.be.undefined() ;
    +	} ) ;
    +
    +	it( "Prototype pollution using 'unflat' option and using .constructor" , () => {
    +		delete Object.prototype.hack ;
    +		var o = {} ;
    +		extend( { unflat: true } , o , { "constructor.prototype.hack": "hacked" } ) ;
    +		expect( Object.prototype.hack ).to.be.undefined() ;
    +	} ) ;
    +} ) ;
    +
    
  • test/path-test.js+3 0 modified
    @@ -940,6 +940,7 @@ describe( "Tree's array path on objects" , function() {
     describe( ".path() security issues" , () => {
     
     	it( "Prototype pollution using .__proto__" , () => {
    +		delete Object.prototype.hack ;
     		expect( () => path.set( {} , '__proto__.hack' , 'hacked' ) ).to.throw() ;
     		expect( Object.prototype.hack ).to.be.undefined() ;
     		expect( () => path.set( {} , '__proto__' , 'hacked' ) ).to.throw() ;
    @@ -949,6 +950,7 @@ describe( ".path() security issues" , () => {
     	} ) ;
     
     	it( "Prototype pollution using a path array: [['__proto__']]" , () => {
    +		delete Object.prototype.hack ;
     		expect( () => path.set( {} , [['__proto__'],'hack'] , 'hacked' ) ).to.throw() ;
     		expect( Object.prototype.hack ).to.be.undefined() ;
     		expect( () => path.set( {} , '__proto__' , 'hacked' ) ).to.throw() ;
    @@ -958,6 +960,7 @@ describe( ".path() security issues" , () => {
     	} ) ;
     
     	it( "Prototype pollution using .constructor" , () => {
    +		delete Object.prototype.hack ;
     		expect( () => path.set( {} , 'constructor.prototype' , 'hacked' ) ).to.throw() ;
     		expect( () => path.set( {} , 'constructor.prototype.hack' , 'hacked' ) ).to.throw() ;
     		expect( Object.prototype.hack ).to.be.undefined() ;
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.