diff --git a/README.md b/README.md index 18efb6d..ed4e959 100644 --- a/README.md +++ b/README.md @@ -28,16 +28,16 @@ Import into your Node.js project: ```js // CommonJS -const { destr } = require("destr"); +const { destr, safeDestr } = require("destr"); // ESM -import { destr } from "destr"; +import { destr, safeDestr } from "destr"; ``` ### Deno ```js -import { destr } from "https://deno.land/x/destr/src/index.ts"; +import { destr, safeDestr } from "https://deno.land/x/destr/src/index.ts"; console.log(destr('{ "deno": "yay" }')); ``` @@ -98,14 +98,14 @@ destr(input); ### Strict Mode -If `{ strict: true }` passed as second argument, `destr` will throw an error if the input is not a valid JSON string or parsing fails. (non string values and built-ins will be still returned as-is) +When using `safeDestr` it will throw an error if the input is not a valid JSON string or parsing fails. (non string values and built-ins will be still returned as-is) ```js // Returns "[foo" -destr("[foo"); +safeDestr("[foo"); // Throws an error -destr("[foo", { strict: true }); +safeDestr("[foo", { strict: true }); ``` ## Benchmarks diff --git a/lib/index.cjs b/lib/index.cjs index 40e9c78..8d4fa4b 100644 --- a/lib/index.cjs +++ b/lib/index.cjs @@ -1,6 +1,7 @@ -const { destr } = require("../dist/index.cjs"); +const { destr, safeDestr } = require("../dist/index.cjs"); // Allow mixed default and named exports destr.destr = destr; +destr.safeDestr = safeDestr; module.exports = destr; diff --git a/src/index.ts b/src/index.ts index bb8253b..8581d94 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,13 +60,16 @@ export function destr(value: any, options: Options = {}): T { if (!JsonSigRx.test(value)) { if (options.strict) { - throw new SyntaxError("Invalid JSON"); + throw new SyntaxError("[destr] Invalid JSON"); } return value as T; } try { if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) { + if (options.strict) { + throw new Error("[destr] Possible prototype pollution"); + } return JSON.parse(value, jsonParseTransform); } return JSON.parse(value); @@ -78,4 +81,8 @@ export function destr(value: any, options: Options = {}): T { } } +export function safeDestr(value: any, options: Options = {}): T { + return destr(value, { ...options, strict: true }); +} + export default destr; diff --git a/test/index.test.ts b/test/index.test.ts index e9fc69e..6fc4b03 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,5 +1,5 @@ import { expect, it, describe, vi } from "vitest"; -import { destr } from "../src"; +import { destr, safeDestr } from "../src"; describe("destr", () => { it("returns the passed value if it's not a string", () => { @@ -107,7 +107,7 @@ describe("destr", () => { } }); - it("returns the passed string if it's a invalid JSON text and `strict` option is set `false`", () => { + it("returns the passed string if it's a invalid JSON text by default", () => { const testCases = [ { input: "{ " }, { input: "[ " }, @@ -121,7 +121,7 @@ describe("destr", () => { } }); - it("throws an error if it's a invalid JSON texts and `strict` option is set `true`", () => { + it("throws an error if it's a invalid JSON texts with safeDestr", () => { const testCases = [ { input: "{ ", output: "Unexpected end of JSON input" }, { input: "[ ", output: "Unexpected end of JSON input" }, @@ -131,7 +131,7 @@ describe("destr", () => { ]; for (const testCase of testCases) { - expect(() => destr(testCase.input, { strict: true })).toThrowError( + expect(() => safeDestr(testCase.input)).toThrowError( testCase.output || "" ); }