chore: added DOMPurify to prevent xss (#1894)

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Dhruwang Jariwala
2024-01-23 22:35:47 +05:30
committed by GitHub
parent aa63c89a6a
commit 64db29417d
11 changed files with 213 additions and 341 deletions

View File

@@ -12,8 +12,8 @@
// Configure properties specific to VS Code.
"vscode": {
// Add the IDs of extensions you want installed when the container is created.
"extensions": ["dbaeumer.vscode-eslint"],
},
"extensions": ["dbaeumer.vscode-eslint"]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
@@ -25,5 +25,5 @@
"postAttachCommand": "pnpm dev --filter=web... --filter=demo...",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
"remoteUser": "node"
}

View File

@@ -1,11 +1,10 @@
/* import { cleanHtml } from "../../lib/cleanHtml"; */
import { cleanHtml } from "@formbricks/lib/cleanHtml";
import * as DOMPurify from "dompurify";
export default function HtmlBody({ htmlString, questionId }: { htmlString: string; questionId: string }) {
return (
<label
htmlFor={questionId}
className="fb-block fb-font-normal fb-leading-6 text-sm text-slate-500 dark:text-slate-300"
dangerouslySetInnerHTML={{ __html: cleanHtml(htmlString) }}></label>
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(htmlString) }}></label>
);
}

View File

@@ -1,97 +0,0 @@
/*!
* Sanitize an HTML string
* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
* @param {String} str The HTML string to sanitize
* @return {String} The sanitized string
*/
export function cleanHtml(str: string): string {
/**
* Convert the string to an HTML document
* @return {Node} An HTML document
*/
function stringToHTML() {
let parser = new DOMParser();
let doc = parser.parseFromString(str, "text/html");
return doc.body || document.createElement("body");
}
/**
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html: Element) {
let scripts = html.querySelectorAll("script");
scripts.forEach((script) => {
script.remove();
});
}
/**
* Check if the attribute is potentially dangerous
* @param {String} name The attribute name
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
/**
* Check if the attribute is potentially dangerous
*/
function isPossiblyDangerous(name: string, value: string): boolean {
let val = value.replace(/\s+/g, "").toLowerCase();
if (
["src", "href", "xlink:href", "srcdoc"].includes(name) &&
(val.includes("javascript:") || val.includes("data:") || val.includes("<script>"))
) {
return true;
}
if (name.startsWith("on")) {
return true;
}
return false;
}
/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem: Element) {
// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;
for (let i = atts.length - 1; i >= 0; i--) {
let { name, value } = atts[i];
if (isPossiblyDangerous(name, value)) {
elem.removeAttribute(name);
} else if (name === "srcdoc") {
// Recursively sanitize srcdoc content
elem.setAttribute(name, cleanHtml(value));
}
}
}
/**
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
/**
* Clean the HTML nodes recursively
* @param {Element} html The HTML element
*/
function clean(html: Element) {
let nodes = Array.from(html.children);
for (let node of nodes) {
removeAttributes(node);
clean(node);
}
}
// Convert the string to HTML
let html = stringToHTML();
// Sanitize it
removeScripts(html);
clean(html);
// If the user wants HTML nodes back, return them
// Otherwise, pass a sanitized string back
return html.innerHTML;
}

View File

@@ -26,13 +26,16 @@
"@mdx-js/react": "^3.0.0",
"@next/mdx": "14.0.4",
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.6",
"@sindresorhus/slugify": "^2.2.1",
"@tailwindcss/typography": "^0.5.10",
"@types/react-highlight-words": "^0.16.7",
"acorn": "^8.11.3",
"autoprefixer": "^10.4.16",
"clsx": "^2.1.0",
"fast-glob": "^3.3.2",
"@types/dompurify": "^3.0.5",
"@types/react-highlight-words": "^0.16.5",
"acorn": "^8.10.0",
"autoprefixer": "^10.4.15",
"clsx": "^2.0.0",
"fast-glob": "^3.3.1",
"flexsearch": "^0.7.31",
"framer-motion": "10.17.8",
"lottie-web": "^5.12.2",
@@ -46,8 +49,6 @@
"node-fetch": "^3.3.2",
"prism-react-renderer": "^2.3.1",
"prismjs": "^1.29.0",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.7",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-highlight-words": "^0.20.0",

View File

@@ -1,97 +0,0 @@
/*!
* Sanitize an HTML string
* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
* @param {String} str The HTML string to sanitize
* @return {String} The sanitized string
*/
export function cleanHtml(str: string): string {
/**
* Convert the string to an HTML document
* @return {Node} An HTML document
*/
function stringToHTML() {
let parser = new DOMParser();
let doc = parser.parseFromString(str, "text/html");
return doc.body || document.createElement("body");
}
/**
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html: Element) {
let scripts = html.querySelectorAll("script");
scripts.forEach((script) => {
script.remove();
});
}
/**
* Check if the attribute is potentially dangerous
* @param {String} name The attribute name
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
/**
* Check if the attribute is potentially dangerous
*/
function isPossiblyDangerous(name: string, value: string): boolean {
let val = value.replace(/\s+/g, "").toLowerCase();
if (
["src", "href", "xlink:href", "srcdoc"].includes(name) &&
(val.includes("javascript:") || val.includes("data:") || val.includes("<script>"))
) {
return true;
}
if (name.startsWith("on")) {
return true;
}
return false;
}
/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem: Element) {
// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;
for (let i = atts.length - 1; i >= 0; i--) {
let { name, value } = atts[i];
if (isPossiblyDangerous(name, value)) {
elem.removeAttribute(name);
} else if (name === "srcdoc") {
// Recursively sanitize srcdoc content
elem.setAttribute(name, cleanHtml(value));
}
}
}
/**
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
/**
* Clean the HTML nodes recursively
* @param {Element} html The HTML element
*/
function clean(html: Element) {
let nodes = Array.from(html.children);
for (let node of nodes) {
removeAttributes(node);
clean(node);
}
}
// Convert the string to HTML
let html = stringToHTML();
// Sanitize it
removeScripts(html);
clean(html);
// If the user wants HTML nodes back, return them
// Otherwise, pass a sanitized string back
return html.innerHTML;
}

View File

@@ -3,6 +3,6 @@
"include": ["."],
"exclude": ["dist", "build", "node_modules"],
"compilerOptions": {
"downlevelIteration": true,
},
"downlevelIteration": true
}
}

View File

@@ -33,23 +33,24 @@
"clean": "rimraf .turbo node_modules dist"
},
"devDependencies": {
"@calcom/embed-snippet": "1.1.2",
"@formbricks/lib": "workspace:*",
"@formbricks/tsconfig": "workspace:*",
"@formbricks/types": "workspace:*",
"@preact/preset-vite": "^2.8.1",
"isomorphic-dompurify": "^2.2.0",
"autoprefixer": "^10.4.16",
"concurrently": "8.2.2",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "latest",
"postcss": "^8.4.33",
"preact": "^10.19.3",
"react-date-picker": "^10.6.0",
"serve": "14.2.1",
"tailwindcss": "^3.4.1",
"terser": "^5.26.0",
"vite": "^5.0.12",
"vite-plugin-dts": "^3.7.0",
"vite-tsconfig-paths": "^4.2.3",
"serve": "14.2.1",
"concurrently": "8.2.2",
"@calcom/embed-snippet": "1.1.2"
"vite-tsconfig-paths": "^4.2.3"
}
}

View File

@@ -1,11 +1,27 @@
import { cleanHtml } from "@/lib/cleanHtml";
import { useEffect, useState } from "react";
interface HtmlBodyProps {
htmlString: string | undefined;
questionId: string;
}
export default function HtmlBody({ htmlString, questionId }: HtmlBodyProps) {
const [safeHtml, setSafeHtml] = useState("");
useEffect(() => {
if (htmlString) {
import("isomorphic-dompurify").then((DOMPurify) => {
setSafeHtml(DOMPurify.sanitize(htmlString));
});
}
}, [htmlString]);
export default function HtmlBody({ htmlString, questionId }: { htmlString?: string; questionId: string }) {
if (!htmlString) return null;
return (
<label
htmlFor={questionId}
className="fb-htmlbody" // styles are in global.css
dangerouslySetInnerHTML={{ __html: cleanHtml(htmlString) }}></label>
dangerouslySetInnerHTML={{ __html: safeHtml }}></label>
);
}

View File

@@ -1,97 +0,0 @@
/*!
* Sanitize an HTML string
* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
* @param {String} str The HTML string to sanitize
* @return {String} The sanitized string
*/
export function cleanHtml(str: string): string {
/**
* Convert the string to an HTML document
* @return {Node} An HTML document
*/
function stringToHTML() {
let parser = new DOMParser();
let doc = parser.parseFromString(str, "text/html");
return doc.body || document.createElement("body");
}
/**
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html: Element) {
let scripts = html.querySelectorAll("script");
scripts.forEach((script) => {
script.remove();
});
}
/**
* Check if the attribute is potentially dangerous
* @param {String} name The attribute name
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
/**
* Check if the attribute is potentially dangerous
*/
function isPossiblyDangerous(name: string, value: string): boolean {
let val = value.replace(/\s+/g, "").toLowerCase();
if (
["src", "href", "xlink:href", "srcdoc"].includes(name) &&
(val.includes("javascript:") || val.includes("data:") || val.includes("<script>"))
) {
return true;
}
if (name.startsWith("on")) {
return true;
}
return false;
}
/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem: Element) {
// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;
for (let i = atts.length - 1; i >= 0; i--) {
let { name, value } = atts[i];
if (isPossiblyDangerous(name, value)) {
elem.removeAttribute(name);
} else if (name === "srcdoc") {
// Recursively sanitize srcdoc content
elem.setAttribute(name, cleanHtml(value));
}
}
}
/**
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
/**
* Clean the HTML nodes recursively
* @param {Element} html The HTML element
*/
function clean(html: Element) {
let nodes = Array.from(html.children);
for (let node of nodes) {
removeAttributes(node);
clean(node);
}
}
// Convert the string to HTML
let html = stringToHTML();
// Sanitize it
removeScripts(html);
clean(html);
// If the user wants HTML nodes back, return them
// Otherwise, pass a sanitized string back
return html.innerHTML;
}

View File

@@ -3,6 +3,6 @@
"include": ["."],
"exclude": ["build", "node_modules"],
"compilerOptions": {
"lib": ["ES2021.String"],
},
"lib": ["ES2021.String"]
}
}

198
pnpm-lock.yaml generated
View File

@@ -107,7 +107,7 @@ importers:
specifier: ^1.1.2
version: 1.1.2(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-tooltip':
specifier: ^1.0.7
specifier: ^1.0.6
version: 1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@sindresorhus/slugify':
specifier: ^2.2.1
@@ -115,20 +115,23 @@ importers:
'@tailwindcss/typography':
specifier: ^0.5.10
version: 0.5.10(tailwindcss@3.4.0)
'@types/dompurify':
specifier: ^3.0.5
version: 3.0.5
'@types/react-highlight-words':
specifier: ^0.16.7
specifier: ^0.16.5
version: 0.16.7
acorn:
specifier: ^8.11.3
specifier: ^8.10.0
version: 8.11.3
autoprefixer:
specifier: ^10.4.16
specifier: ^10.4.15
version: 10.4.16(postcss@8.4.33)
clsx:
specifier: ^2.1.0
specifier: ^2.0.0
version: 2.1.0
fast-glob:
specifier: ^3.3.2
specifier: ^3.3.1
version: 3.3.2
flexsearch:
specifier: ^0.7.31
@@ -450,7 +453,7 @@ importers:
version: 9.1.0(eslint@8.56.0)
eslint-config-turbo:
specifier: latest
version: 1.11.2(eslint@8.56.0)
version: 1.11.3(eslint@8.56.0)
terser:
specifier: ^5.26.0
version: 5.26.0
@@ -533,7 +536,7 @@ importers:
version: 9.1.0(eslint@8.56.0)
eslint-config-turbo:
specifier: latest
version: 1.11.2(eslint@8.56.0)
version: 1.11.3(eslint@8.56.0)
eslint-plugin-react:
specifier: 7.33.2
version: 7.33.2(eslint@8.56.0)
@@ -593,7 +596,7 @@ importers:
version: 9.1.0(eslint@8.56.0)
eslint-config-turbo:
specifier: latest
version: 1.11.2(eslint@8.56.0)
version: 1.11.3(eslint@8.56.0)
isomorphic-fetch:
specifier: ^3.0.0
version: 3.0.0
@@ -744,7 +747,10 @@ importers:
version: 9.1.0(eslint@8.56.0)
eslint-config-turbo:
specifier: latest
version: 1.11.2(eslint@8.56.0)
version: 1.11.3(eslint@8.56.0)
isomorphic-dompurify:
specifier: ^2.2.0
version: 2.3.0
postcss:
specifier: ^8.4.33
version: 8.4.33
@@ -8930,6 +8936,11 @@ packages:
resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==}
dev: true
/@types/dompurify@3.0.5:
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
dependencies:
'@types/trusted-types': 2.0.7
/@types/escodegen@0.0.6:
resolution: {integrity: sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==}
dev: true
@@ -9189,7 +9200,7 @@ packages:
/@types/react-highlight-words@0.16.7:
resolution: {integrity: sha512-+upXTIaRd3rGvh1aDQSs9z5X+sV3UM6Jrmjk03GN2GXl4v/+iOJKQj2LZHo6Vp2IoTvMdtxgME26feqo12xXLg==}
dependencies:
'@types/react': 18.2.46
'@types/react': 18.2.47
dev: false
/@types/react-redux@7.1.33:
@@ -9201,14 +9212,6 @@ packages:
redux: 4.2.1
dev: false
/@types/react@18.2.46:
resolution: {integrity: sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w==}
dependencies:
'@types/prop-types': 15.7.11
'@types/scheduler': 0.16.8
csstype: 3.1.3
dev: false
/@types/react@18.2.47:
resolution: {integrity: sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==}
dependencies:
@@ -9253,6 +9256,9 @@ packages:
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
dev: true
/@types/trusted-types@2.0.7:
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
/@types/unist@2.0.10:
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@@ -9826,7 +9832,6 @@ packages:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false
/aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
@@ -11246,6 +11251,13 @@ packages:
cssom: 0.3.8
dev: true
/cssstyle@4.0.1:
resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
engines: {node: '>=18'}
dependencies:
rrweb-cssom: 0.6.0
dev: true
/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@@ -11289,6 +11301,14 @@ packages:
whatwg-url: 11.0.0
dev: true
/data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
dev: true
/date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
@@ -11565,6 +11585,10 @@ packages:
dependencies:
domelementtype: 2.3.0
/dompurify@3.0.8:
resolution: {integrity: sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ==}
dev: true
/domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
dependencies:
@@ -11993,13 +12017,13 @@ packages:
eslint: 8.56.0
dev: true
/eslint-config-turbo@1.11.2(eslint@8.56.0):
resolution: {integrity: sha512-vqbyCH6kCHFoIAWUmGL61c0BfUQNz0XAl2RzAnEkSQ+PLXvEvuV2HsvL51UOzyyElfJlzZuh9T4BvUqb5KR9Eg==}
/eslint-config-turbo@1.11.3(eslint@8.56.0):
resolution: {integrity: sha512-v7CHpAHodBKlj+r+R3B2DJlZbCjpZLnK7gO/vCRk/Lc+tlD/f04wM6rmHlerevOlchtmwARilRLBnmzNLffTyQ==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
eslint: 8.56.0
eslint-plugin-turbo: 1.11.2(eslint@8.56.0)
eslint-plugin-turbo: 1.11.3(eslint@8.56.0)
dev: true
/eslint-import-resolver-node@0.3.9:
@@ -12183,8 +12207,8 @@ packages:
- typescript
dev: true
/eslint-plugin-turbo@1.11.2(eslint@8.56.0):
resolution: {integrity: sha512-U6DX+WvgGFiwEAqtOjm4Ejd9O4jsw8jlFNkQi0ywxbMnbiTie+exF4Z0F/B1ajtjjeZkBkgRnlU+UkoraBN+bw==}
/eslint-plugin-turbo@1.11.3(eslint@8.56.0):
resolution: {integrity: sha512-R5ftTTWQzEYaKzF5g6m/MInCU8pIN+2TLL+S50AYBr1enwUovdZmnZ1HDwFMaxIjJ8x5ah+jvAzql5IJE9VWaA==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
@@ -13285,6 +13309,13 @@ packages:
whatwg-encoding: 2.0.0
dev: true
/html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
dependencies:
whatwg-encoding: 3.1.1
dev: true
/html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
dev: true
@@ -13340,6 +13371,16 @@ packages:
- supports-color
dev: true
/http-proxy-agent@7.0.0:
resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
/https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
@@ -13357,7 +13398,6 @@ packages:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false
/human-id@1.0.2:
resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==}
@@ -13825,6 +13865,20 @@ packages:
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
/isomorphic-dompurify@2.3.0:
resolution: {integrity: sha512-FCoKY4/mW/jnn/+VgE7wXGC2D/RXzVCAmGYuGWEuZXtyWnwmE2100caciIv+RbHk90q9LA0OW5IBn2f+ywHtww==}
engines: {node: '>=18'}
dependencies:
'@types/dompurify': 3.0.5
dompurify: 3.0.8
jsdom: 24.0.0
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- utf-8-validate
dev: true
/isomorphic-fetch@3.0.0:
resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==}
dependencies:
@@ -14491,6 +14545,42 @@ packages:
- utf-8-validate
dev: true
/jsdom@24.0.0:
resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
engines: {node: '>=18'}
peerDependencies:
canvas: ^2.11.2
peerDependenciesMeta:
canvas:
optional: true
dependencies:
cssstyle: 4.0.1
data-urls: 5.0.0
decimal.js: 10.4.3
form-data: 4.0.0
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.0
https-proxy-agent: 7.0.2
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.7
parse5: 7.1.2
rrweb-cssom: 0.6.0
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 4.1.3
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
ws: 8.16.0
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: true
/jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@@ -17957,6 +18047,10 @@ packages:
fsevents: 2.3.3
dev: true
/rrweb-cssom@0.6.0:
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
dev: true
/rtl-css-js@1.16.1:
resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==}
dependencies:
@@ -19024,6 +19118,13 @@ packages:
punycode: 2.3.1
dev: true
/tr46@5.0.0:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
dependencies:
punycode: 2.3.1
dev: true
/tree-cli@0.6.7:
resolution: {integrity: sha512-jfnB5YKY6Glf6bsFmQ9W97TtkPVLnHsjOR6ZdRf4zhyFRQeLheasvzE5XBJI2Hxt7ZyMyIbXUV7E2YPZbixgtA==}
engines: {node: '>=8.10.9'}
@@ -19915,6 +20016,13 @@ packages:
xml-name-validator: 4.0.0
dev: true
/w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
dependencies:
xml-name-validator: 5.0.0
dev: true
/walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
dependencies:
@@ -20006,6 +20114,13 @@ packages:
iconv-lite: 0.6.3
dev: true
/whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
dependencies:
iconv-lite: 0.6.3
dev: true
/whatwg-fetch@3.6.20:
resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==}
dev: true
@@ -20015,6 +20130,11 @@ packages:
engines: {node: '>=12'}
dev: true
/whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
dev: true
/whatwg-url@11.0.0:
resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
engines: {node: '>=12'}
@@ -20023,6 +20143,14 @@ packages:
webidl-conversions: 7.0.0
dev: true
/whatwg-url@14.0.0:
resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
engines: {node: '>=18'}
dependencies:
tr46: 5.0.0
webidl-conversions: 7.0.0
dev: true
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
@@ -20200,6 +20328,19 @@ packages:
utf-8-validate:
optional: true
/ws@8.16.0:
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: true
/xlsx@0.18.5:
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
engines: {node: '>=0.8'}
@@ -20219,6 +20360,11 @@ packages:
engines: {node: '>=12'}
dev: true
/xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
dev: true
/xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
dev: true