type Source = unknown; // eslint-disable-next-line @typescript-eslint/no-explicit-any type Replacer = ((this: any, key: string, value: any) => any) | (number | string)[]; type Space = string | number; /** * JSON.stringify wrapper. Some characters or substrings of stringify result are unicode-encoded to make result safe for * embedding into HTML (including CDATA strings). * In short, it should encode these: * - " });` * * will result in * * `// {"comment":"xss, here i come\u003c/script>\u003cscript>alert(69)\u003c/script>"}` * @returns {void|string} - A JSON string representing the given value or undefined if non-convertable value is passed. */ const stringify = (source: Source, replacer?: Replacer, space?: Space) => { // @ts-expect-error TS isn't smart enough for this const result = JSON.stringify(source, replacer, space); if (typeof result !== "string") { return result; } return result .replace(/<(\/?)(script)/gi, "\\u003c$1$2") // $2 here is to preserve case of the tag name .replace(/]]>/g, "]]\\u003e") .replace(/\u2028/g, "\\u2028") .replace(/\u2029/g, "\\u2029") .replace(/-->/g, "--\\u003e"); }; export { stringify }; // eslint-disable-next-line import/no-default-export export default stringify;