From 30ad6be0b7a69bdfe574c34a60f15408f770d268 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Fri, 29 Jul 2022 14:24:51 +0200 Subject: [PATCH 1/4] rename scan to filter --- index.js | 13 ++++++------- types/index.d.ts | 22 +++++----------------- types/index.test-d.ts | 8 ++++---- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/index.js b/index.js index 7508655..5c2cd67 100755 --- a/index.js +++ b/index.js @@ -55,12 +55,10 @@ function parse (text, reviver, options) { } // Scan result for proto keys - scan(obj, { protoAction, constructorAction }) - - return obj + return filter(obj, { protoAction, constructorAction }) } -function scan (obj, { protoAction = 'error', constructorAction = 'error' } = {}) { +function filter (obj, { protoAction = 'error', constructorAction = 'error' } = {}) { let next = [obj] while (next.length) { @@ -94,11 +92,12 @@ function scan (obj, { protoAction = 'error', constructorAction = 'error' } = {}) } } } + return obj } -function safeParse (text, reviver) { +function safeParse (text, reviver, options) { try { - return parse(text, reviver) + return parse(text, reviver, options) } catch (ignoreError) { return null } @@ -106,6 +105,6 @@ function safeParse (text, reviver) { module.exports = { parse, - scan, + scan: filter, safeParse } diff --git a/types/index.d.ts b/types/index.d.ts index 0c15f9e..1c1336a 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -5,30 +5,17 @@ export type ParseOptions = { * - `'remove'` - deletes any `__proto__` keys from the result object. * - `'ignore'` - skips all validation (same as calling `JSON.parse()` directly). */ - protoAction?: 'error' | 'remove' | 'ignore', + protoAction?: 'error' | 'remove' | 'ignore'; /** * What to do when a `constructor` key is found. * - `'error'` - throw a `SyntaxError` when a `constructor.prototype` key is found. This is the default value. * - `'remove'` - deletes any `constructor` keys from the result object. * - `'ignore'` - skips all validation (same as calling `JSON.parse()` directly). */ - constructorAction?: 'error' | 'remove' | 'ignore', + constructorAction?: 'error' | 'remove' | 'ignore'; } -export type ScanOptions = { - /** - * What to do when a `__proto__` key is found. - * - `'error'` - throw a `SyntaxError` when a `__proto__` key is found. This is the default value. - * - `'remove'` - deletes any `__proto__` keys from the input `obj`. - */ - protoAction?: 'error' | 'remove', - /** - * What to do when a `constructor` key is found. - * - `'error'` - throw a `SyntaxError` when a `constructor.prototype` key is found. This is the default value. - * - `'remove'` - deletes any `constructor` keys from the input `obj`. - */ - constructorAction?: 'error' | 'remove', -} +export type ScanOptions = ParseOptions type Reviver = (this: any, key: string, value: any) => any @@ -56,5 +43,6 @@ export function safeParse(text: string | Buffer, reviver?: Reviver | null): any * * @param obj The object being scanned. * @param options Optional configuration object. + * @returns The object, or `null` if onError is set to `nullify` */ -export function scan(obj: any, options?: ScanOptions): void +export function scan(obj: {[key: string]: any }, options?: ParseOptions): any diff --git a/types/index.test-d.ts b/types/index.test-d.ts index cfccbc9..89751c8 100644 --- a/types/index.test-d.ts +++ b/types/index.test-d.ts @@ -13,10 +13,10 @@ sjson.safeParse('"test"', null) sjson.safeParse('"test"') expectError(sjson.safeParse(null)) -sjson.scan('"test"', { protoAction: 'remove' }) -expectError(sjson.scan('"test"', { protoAction: 'ignore' })) -sjson.scan('"test"', { constructorAction: 'error' }) -expectError(sjson.scan('"test"', { constructorAction: 'ignore' })) +sjson.scan({}, { protoAction: 'remove' }) +sjson.scan({}, { protoAction: 'ignore' }) +sjson.scan({}, { constructorAction: 'error' }) +sjson.scan({}, { constructorAction: 'ignore' }) declare const input: Buffer sjson.parse(input) From 99edbe23bdcf0659a024d129d703ee5c111b1c71 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Fri, 29 Jul 2022 16:38:02 +0200 Subject: [PATCH 2/4] improve performance of safeParse --- index.js | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 5c2cd67..efdc5d7 100755 --- a/index.js +++ b/index.js @@ -10,14 +10,9 @@ function parse (text, reviver, options) { if (reviver !== null && typeof reviver === 'object') { options = reviver reviver = undefined - } else { - options = {} } } - const protoAction = options.protoAction || 'error' - const constructorAction = options.constructorAction || 'error' - if (hasBuffer && Buffer.isBuffer(text)) { text = text.toString() } @@ -30,13 +25,16 @@ function parse (text, reviver, options) { // Parse normally, allowing exceptions const obj = JSON.parse(text, reviver) - // options: 'error' (default) / 'remove' / 'ignore' - if (protoAction === 'ignore' && constructorAction === 'ignore') { + // Ignore null and non-objects + if (obj === null || typeof obj !== 'object') { return obj } - // Ignore null and non-objects - if (obj === null || typeof obj !== 'object') { + const protoAction = options && options.protoAction || 'error' + const constructorAction = options && options.constructorAction || 'error' + + // options: 'error' (default) / 'remove' / 'ignore' + if (protoAction === 'ignore' && constructorAction === 'ignore') { return obj } @@ -55,10 +53,10 @@ function parse (text, reviver, options) { } // Scan result for proto keys - return filter(obj, { protoAction, constructorAction }) + return filter(obj, { protoAction, constructorAction, safe: options && options.safe }) } -function filter (obj, { protoAction = 'error', constructorAction = 'error' } = {}) { +function filter (obj, { protoAction = 'error', constructorAction = 'error', safe } = {}) { let next = [obj] while (next.length) { @@ -67,7 +65,9 @@ function filter (obj, { protoAction = 'error', constructorAction = 'error' } = { for (const node of nodes) { if (protoAction !== 'ignore' && Object.prototype.hasOwnProperty.call(node, '__proto__')) { // Avoid calling node.hasOwnProperty directly - if (protoAction === 'error') { + if (safe === true) { + return null + } else if (protoAction === 'error') { throw new SyntaxError('Object contains forbidden prototype property') } @@ -77,7 +77,9 @@ function filter (obj, { protoAction = 'error', constructorAction = 'error' } = { if (constructorAction !== 'ignore' && Object.prototype.hasOwnProperty.call(node, 'constructor') && Object.prototype.hasOwnProperty.call(node.constructor, 'prototype')) { // Avoid calling node.hasOwnProperty directly - if (constructorAction === 'error') { + if (safe === true) { + return null + } else if (constructorAction === 'error') { throw new SyntaxError('Object contains forbidden prototype property') } @@ -87,7 +89,7 @@ function filter (obj, { protoAction = 'error', constructorAction = 'error' } = { for (const key in node) { const value = node[key] if (value && typeof value === 'object') { - next.push(node[key]) + next.push(value) } } } @@ -95,9 +97,9 @@ function filter (obj, { protoAction = 'error', constructorAction = 'error' } = { return obj } -function safeParse (text, reviver, options) { +function safeParse (text, reviver) { try { - return parse(text, reviver, options) + return parse(text, reviver, { safe: true }) } catch (ignoreError) { return null } From 96ecd109e80b9dae85a101eac8f09045fef15958 Mon Sep 17 00:00:00 2001 From: Uzlopak Date: Fri, 29 Jul 2022 18:08:33 +0200 Subject: [PATCH 3/4] Fix Linting Issue --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index efdc5d7..bf55ca7 100755 --- a/index.js +++ b/index.js @@ -30,8 +30,8 @@ function parse (text, reviver, options) { return obj } - const protoAction = options && options.protoAction || 'error' - const constructorAction = options && options.constructorAction || 'error' + const protoAction = (options && options.protoAction) || 'error' + const constructorAction = (options && options.constructorAction) || 'error' // options: 'error' (default) / 'remove' / 'ignore' if (protoAction === 'ignore' && constructorAction === 'ignore') { From e678c92fe07dec020f41abda4fa660496143fae4 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Fri, 29 Jul 2022 20:20:49 +0200 Subject: [PATCH 4/4] add typing test for Array --- types/index.d.ts | 2 +- types/index.test-d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 1c1336a..fee2eb9 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -45,4 +45,4 @@ export function safeParse(text: string | Buffer, reviver?: Reviver | null): any * @param options Optional configuration object. * @returns The object, or `null` if onError is set to `nullify` */ -export function scan(obj: {[key: string]: any }, options?: ParseOptions): any +export function scan(obj: {[key: string | number]: any }, options?: ParseOptions): any diff --git a/types/index.test-d.ts b/types/index.test-d.ts index 89751c8..200ad9c 100644 --- a/types/index.test-d.ts +++ b/types/index.test-d.ts @@ -17,6 +17,7 @@ sjson.scan({}, { protoAction: 'remove' }) sjson.scan({}, { protoAction: 'ignore' }) sjson.scan({}, { constructorAction: 'error' }) sjson.scan({}, { constructorAction: 'ignore' }) +sjson.scan(new Array(), {}) declare const input: Buffer sjson.parse(input)