UNPKG

@winglet/json

Version:

TypeScript library for safe and efficient JSON data manipulation with RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch) compliance, featuring prototype pollution protection and immutable operations

75 lines (71 loc) 3.71 kB
'use strict'; var filter = require('@winglet/common-utils/filter'); var _enum = require('../../../enum.cjs'); var unescapePath = require('../../escape/unescapePath.cjs'); var error = require('./utils/error.cjs'); var getArrayIndex = require('./utils/getArrayIndex.cjs'); var handleArray = require('./utils/handleArray.cjs'); var handleObject = require('./utils/handleObject.cjs'); var handleRootPatch = require('./utils/handleRootPatch.cjs'); var isPrototypeModification = require('./utils/isPrototypeModification.cjs'); const applySinglePatch = (source, patch, patchIndex, strict, protectPrototype) => { if (patch.path === '' || patch.path === _enum.JSONPointer.Fragment) return handleRootPatch.handleRootPatch(source, patch, patchIndex, strict); const segments = patch.path.split('/'); let current = source; let cursor = 1; const segmentsLength = segments.length; while (cursor < segmentsLength) { let segment = unescapePath.unescapePath(segments[cursor]); if (protectPrototype && isPrototypeModification.isPrototypeModification(segment, segments, cursor)) throw new error.JsonPatchError('SECURITY_PROTOTYPE_MODIFICATION_FORBIDDEN', 'Modifying prototype properties (__proto__, constructor.prototype) is forbidden for security reasons', { patch, index: patchIndex, segment, path: segments.slice(0, cursor + 1).join('/'), operation: patch.op, }); if (cursor === segmentsLength - 1) { if (filter.isArray(current)) { const arrayIndex = getArrayIndex.getArrayIndex(segment, current); return handleArray.handleArray(patch, current, arrayIndex, source, patchIndex, strict); } else if (current && typeof current === 'object') { return handleObject.handleObject(patch, current, segment, source, patchIndex, strict); } else { throw new error.JsonPatchError('PATCH_TARGET_NOT_OBJECT', `Cannot apply ${patch.op} operation to non-object value. Target path points to: ${typeof current}`, { patch, patchIndex: patchIndex, targetValue: current, targetType: typeof current, path: patch.path, operation: patch.op, }); } } if (filter.isArray(current)) segment = getArrayIndex.getArrayIndex(segment, current); current = current[segment]; if (!current || typeof current !== 'object') { throw new error.JsonPatchError('PATCH_PATH_INVALID_INTERMEDIATE', `Cannot traverse path '${patch.path}' - intermediate value at '${segments.slice(0, cursor + 1).join('/')}' is ${current === null ? 'null' : current === undefined ? 'undefined' : typeof current}, expected object or array`, { patch, patchIndex: patchIndex, intermediateValue: current, intermediateType: current === null ? 'null' : typeof current, failedAtPath: segments.slice(0, cursor + 1).join('/'), remainingPath: segments.slice(cursor + 1).join('/'), operation: patch.op, }); } cursor++; } throw new error.JsonPatchError('PATCH_PATH_PROCESSING_ERROR', 'Unexpected error while processing patch path - this should not happen', { patch, patchIndex: patchIndex, operation: patch.op, processedSegments: cursor, totalSegments: segmentsLength, }); }; exports.applySinglePatch = applySinglePatch;