const pullVal = (obj, path, val, options = {strict: true}) => {
	if (obj === undefined || obj === null || path === undefined) {
		return obj;
	}
	
	// Clean the path
	path = clean(path);
	
	const pathParts = split(path);
	const part = pathParts.shift();

	if (part === "__proto__") return obj;
	
	if (pathParts.length) {
		// Generate the path part in the object if it does not already exist
		obj[part] = decouple(obj[part], options) || {};
		
		// Recurse - we don't need to assign obj[part] the result of this call because
		// we are modifying by reference since we haven't reached the furthest path
		// part (leaf) node yet
		pullVal(obj[part], pathParts.join("."), val, options);
	} else if (part) {
		obj[part] = decouple(obj[part], options) || [];
		
		// Recurse - this is the leaf node so assign the response to obj[part] in
		// case it is set to an immutable response
		obj[part] = pullVal(obj[part], "", val, options);
	} else {
		// The target array is the root object, pull the value
		obj = decouple(obj, options) || [];
		
		if (!(obj instanceof Array)) {
			throw("Cannot pull from a path whose leaf node is not an array!");
		}
		
		let index = -1;
		
		// Find the index of the passed value
		if (options.strict === true) {
			index = obj.indexOf(val);
		} else {
			// Do a non-strict check
			index = obj.findIndex((item) => {
				return match(item, val);
			});
		}
		
		if (index > -1) {
			// Remove the item from the array
			obj.splice(index, 1);
		}
	}
	
	return decouple(obj, options);
};
The software receives input from an upstream component that specifies multiple attributes, properties, or fields that are to be initialized or updated in an object, but it does not properly control which attributes can be modified.