diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/package.json b/package.json index ec95042..153dfb7 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "bench": "pnpm build && node ./bench.cjs", "build": "unbuild", "dev": "vitest dev", - "lint": "eslint --ext .ts .", + "lint": "eslint --ext .ts . && prettier -c src test", + "lint:fix": "eslint --ext .ts . --fix && prettier -w src test", "release": "pnpm test && pnpm build && standard-version && git push --follow-tags && pnpm publish", "test": "pnpm lint && vitest run --coverage" }, @@ -32,6 +33,7 @@ "benchmark": "^2.1.4", "eslint": "^8.42.0", "eslint-config-unjs": "^0.2.1", + "prettier": "^2.8.8", "secure-json-parse": "^2.7.0", "standard-version": "^9.5.0", "typescript": "^5.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34bd533..8d98ba3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,7 @@ specifiers: benchmark: ^2.1.4 eslint: ^8.42.0 eslint-config-unjs: ^0.2.1 + prettier: ^2.8.8 secure-json-parse: ^2.7.0 standard-version: ^9.5.0 typescript: ^5.1.3 @@ -18,6 +19,7 @@ devDependencies: benchmark: 2.1.4 eslint: 8.42.0 eslint-config-unjs: 0.2.1_tizxnkcvjrb4cldxgwq5h3lj5u + prettier: 2.8.8 secure-json-parse: 2.7.0 standard-version: 9.5.0 typescript: 5.1.3 @@ -3515,6 +3517,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier/2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-bytes/6.1.0: resolution: {integrity: sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==} engines: {node: ^14.13.1 || >=16.0.0} diff --git a/src/index.ts b/src/index.ts index 1ee7b4e..33b0e88 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,22 @@ // https://github.com/fastify/secure-json-parse // https://github.com/hapijs/bourne -const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/; -const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/; +const suspectProtoRx = + /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/; +const suspectConstructorRx = + /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/; const JsonSigRx = /^\s*["[{]|^\s*-?\d[\d.]{0,14}\s*$/; -function jsonParseTransform (key: string, value: any): any { +function jsonParseTransform(key: string, value: any): any { if (key === "__proto__") { return; } - if (key === "constructor" && value && typeof value === "object" && ("prototype" in value)) { + if ( + key === "constructor" && + value && + typeof value === "object" && + "prototype" in value + ) { // Has possible malicious prototype return; } @@ -17,22 +24,34 @@ function jsonParseTransform (key: string, value: any): any { } export type Options = { - strict?: boolean -} + strict?: boolean; +}; -export default function destr (value: any, options: Options = {}): any { +export default function destr(value: any, options: Options = {}): any { if (typeof value !== "string") { return value; } const _lval = value.toLowerCase().trim(); - if (_lval === "true") { return true; } - if (_lval === "false") { return false; } + if (_lval === "true") { + return true; + } + if (_lval === "false") { + return false; + } // eslint-disable-next-line unicorn/no-null - if (_lval === "null") { return null; } - if (_lval === "nan") { return Number.NaN; } - if (_lval === "infinity") { return Number.POSITIVE_INFINITY; } - if (_lval === "undefined") { return undefined; } + if (_lval === "null") { + return null; + } + if (_lval === "nan") { + return Number.NaN; + } + if (_lval === "infinity") { + return Number.POSITIVE_INFINITY; + } + if (_lval === "undefined") { + return undefined; + } if (!JsonSigRx.test(value)) { if (options.strict) { diff --git a/test/index.test.ts b/test/index.test.ts index a032de6..811a498 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -12,7 +12,7 @@ describe("destr", () => { /* eslint-disable-next-line unicorn/no-null */ { input: null }, { input: Number.POSITIVE_INFINITY }, - { input: undefined } + { input: undefined }, ]; for (const testCase of testCases) { @@ -55,18 +55,18 @@ describe("destr", () => { it("parses with surrounding spaces", () => { expect(destr(" true ")).toBe(true); expect(destr(" -123 ")).toStrictEqual(-123); - expect(destr(" { \"test\": 123 } ")).toStrictEqual({ test: 123 }); + expect(destr(' { "test": 123 } ')).toStrictEqual({ test: 123 }); }); it("parses valid JSON texts", () => { const testCases = [ { input: "{}", output: {} }, { input: "[]", output: [] }, - { input: "{ \"key\": \"value\" }", output: { key: "value" } }, - { input: "{ \"constructor\": \"value\" }", output: { constructor: "value" } }, + { input: '{ "key": "value" }', output: { key: "value" } }, + { input: '{ "constructor": "value" }', output: { constructor: "value" } }, // eslint-disable-next-line unicorn/no-null { input: '{ "constructor": null }', output: { constructor: null } }, - { input: "[1,2,3]", output: [1, 2, 3] } + { input: "[1,2,3]", output: [1, 2, 3] }, ]; for (const testCase of testCases) { @@ -78,7 +78,7 @@ describe("destr", () => { const testCases = [ { input: '{ "__proto__": {} }', output: {} }, { input: '{ "constructor": { "prototype": {} } }', output: {} }, - { input: '{ "constructor": { "prototype": null } }', output: {} } + { input: '{ "constructor": { "prototype": null } }', output: {} }, ]; for (const testCase of testCases) { @@ -92,7 +92,7 @@ describe("destr", () => { { input: "[ " }, { input: '" ' }, { input: "[1,2,3]?" }, - { input: "invalid JSON text" } + { input: "invalid JSON text" }, ]; for (const testCase of testCases) { @@ -106,11 +106,13 @@ describe("destr", () => { { input: "[ ", output: "Unexpected end of JSON input" }, { input: '" ', output: "Unexpected end of JSON input" }, { input: "[1,2,3]?", output: "Unexpected token" }, - { input: "invalid JSON text", output: "Invalid JSON" } + { input: "invalid JSON text", output: "Invalid JSON" }, ]; for (const testCase of testCases) { - expect(() => destr(testCase.input, { strict: true })).toThrowError(testCase.output || ""); + expect(() => destr(testCase.input, { strict: true })).toThrowError( + testCase.output || "" + ); } }); });