diff --git a/package-lock.json b/package-lock.json index 4e51e48f..bfdc84df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "cronstrue": "^3.12.0", "crypto-js": "^4.2.0", "csso": "^5.0.5", - "isomorphic-dompurify": "^3.0.0", + "dompurify": "3.4.11", "jq-wasm": "^1.1.0-jq-1.8.1", "js-beautify": "^1.15.4", "jsbarcode": "^3.12.3", @@ -112,6 +112,7 @@ "version": "5.1.11", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -152,6 +153,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -161,6 +163,7 @@ "version": "2.3.9", "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, "license": "MIT" }, "node_modules/@babel/code-frame": { @@ -469,6 +472,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, "license": "MIT", "dependencies": { "css-tree": "^3.0.0" @@ -481,6 +485,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, "funding": [ { "type": "github", @@ -500,6 +505,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "dev": true, "funding": [ { "type": "github", @@ -523,6 +529,7 @@ "version": "4.1.6", "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.6.tgz", "integrity": "sha512-xh1zgj5rjFV5Iqm8OAWoO0aKBkc9/tbXDde9yOhuPBXMBoRu+FljkIk6DsY8+Ric7xLotiFARcJIMniDvs2QIw==", + "dev": true, "funding": [ { "type": "github", @@ -550,6 +557,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, "funding": [ { "type": "github", @@ -572,6 +580,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", + "dev": true, "funding": [ { "type": "github", @@ -596,6 +605,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, "funding": [ { "type": "github", @@ -1244,6 +1254,7 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -4318,6 +4329,70 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.8.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.8.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", @@ -5878,6 +5953,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" @@ -6305,6 +6381,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, "license": "MIT", "dependencies": { "mdn-data": "2.27.1", @@ -6409,6 +6486,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, "license": "MIT", "dependencies": { "whatwg-mimetype": "^5.0.0", @@ -6509,6 +6587,7 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, "license": "MIT" }, "node_modules/decode-named-character-reference": { @@ -8262,6 +8341,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, "license": "MIT", "dependencies": { "@exodus/bytes": "^1.6.0" @@ -8748,6 +8828,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, "license": "MIT" }, "node_modules/is-regex": { @@ -8928,108 +9009,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, - "node_modules/isomorphic-dompurify": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-3.17.0.tgz", - "integrity": "sha512-++PY22SYWZv/kTWOr5WQ8rNznkIKj6I/SDUVZnXXM67EhRO7ScVLDdZYNxNgEsPAOA5sfpGBcqnnbLx8WJHO1g==", - "license": "MIT", - "dependencies": { - "dompurify": "^3.4.10", - "jsdom": "^29.1.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24.0.0" - } - }, - "node_modules/isomorphic-dompurify/node_modules/@asamuzakjp/dom-selector": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", - "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", - "license": "MIT", - "dependencies": { - "@asamuzakjp/generational-cache": "^1.0.1", - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/isomorphic-dompurify/node_modules/entities": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", - "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/isomorphic-dompurify/node_modules/jsdom": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^5.1.11", - "@asamuzakjp/dom-selector": "^7.1.1", - "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.3", - "@exodus/bytes": "^1.15.0", - "css-tree": "^3.2.1", - "data-urls": "^7.0.0", - "decimal.js": "^10.6.0", - "html-encoding-sniffer": "^6.0.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.3.5", - "parse5": "^8.0.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.1", - "undici": "^7.25.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^8.0.1", - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.1", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/isomorphic-dompurify/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/isomorphic-dompurify/node_modules/parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", - "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", - "license": "MIT", - "dependencies": { - "entities": "^8.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -10088,6 +10067,7 @@ "version": "2.27.1", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, "license": "CC0-1.0" }, "node_modules/merge2": { @@ -11510,6 +11490,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -12111,6 +12092,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -12311,6 +12293,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -13082,6 +13065,7 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, "license": "MIT" }, "node_modules/tailwind-merge": { @@ -13226,6 +13210,7 @@ "version": "7.0.23", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz", "integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==", + "dev": true, "license": "MIT", "dependencies": { "tldts-core": "^7.0.23" @@ -13238,6 +13223,7 @@ "version": "7.0.23", "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz", "integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==", + "dev": true, "license": "MIT" }, "node_modules/to-regex-range": { @@ -13267,6 +13253,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -13279,6 +13266,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -13574,6 +13562,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "dev": true, "license": "MIT", "engines": { "node": ">=20.18.1" @@ -14089,6 +14078,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -14111,6 +14101,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=20" @@ -14158,6 +14149,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -14167,6 +14159,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, "license": "MIT", "dependencies": { "@exodus/bytes": "^1.11.0", @@ -14466,6 +14459,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18" @@ -14484,6 +14478,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, "license": "MIT" }, "node_modules/yallist": { diff --git a/package.json b/package.json index ca8742b0..5bb277ea 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "cronstrue": "^3.12.0", "crypto-js": "^4.2.0", "csso": "^5.0.5", - "isomorphic-dompurify": "^3.0.0", + "dompurify": "3.4.11", "jq-wasm": "^1.1.0-jq-1.8.1", "js-beautify": "^1.15.4", "jsbarcode": "^3.12.3", diff --git a/scripts/gates/check-bundle-boundaries.js b/scripts/gates/check-bundle-boundaries.js index ed32839c..bc839a94 100644 --- a/scripts/gates/check-bundle-boundaries.js +++ b/scripts/gates/check-bundle-boundaries.js @@ -49,6 +49,21 @@ const HEAVY_DEPENDENCY_RULES = [ description: "remark-gfm must stay isolated to the markdown renderer template.", allowedFiles: ["src/features/tool-templates/markdown-preview-renderer.tsx"], }, + { + packageName: "dompurify", + description: "DOMPurify must stay isolated to the shared sanitizer and must not pull jsdom into route prerender chunks.", + allowedFiles: ["src/core/security/sanitize.ts"], + }, + { + packageName: "isomorphic-dompurify", + description: "isomorphic-dompurify brings jsdom/cssstyle into production analysis builds; use browser DOMPurify from the shared sanitizer instead.", + allowedFiles: [], + }, + { + packageName: "jsdom", + description: "jsdom is test-only and must not be imported by application runtime bundles.", + allowedFiles: [], + }, { packageName: "pdf-lib", description: "pdf-lib must not be imported from shared shell/core code.", @@ -74,6 +89,16 @@ const HEAVY_DEPENDENCY_RULES = [ description: "markdown rendering must not be imported from shared shell/core code.", disallowedPathPrefixes: ["src/app/", "src/core/", "src/features/tool-shell/"], }, + { + packageName: "isomorphic-dompurify", + description: "isomorphic-dompurify must not be imported by app/runtime code.", + disallowedPathPrefixes: ["src/"], + }, + { + packageName: "jsdom", + description: "jsdom must stay out of app/runtime code.", + disallowedPathPrefixes: ["src/"], + }, ]; function walk(dir) { diff --git a/src/core/security/markdown-sanitize-schema.ts b/src/core/security/markdown-sanitize-schema.ts new file mode 100644 index 00000000..e6ea3a5f --- /dev/null +++ b/src/core/security/markdown-sanitize-schema.ts @@ -0,0 +1,53 @@ +import { defaultSchema } from "rehype-sanitize" +import type { Options as RehypeSanitizeOptions } from "rehype-sanitize" + +const ACTIVE_SVG_TAGS = [ + "animate", + "animateMotion", + "animateTransform", + "foreignObject", + "iframe", + "image", + "object", + "script", + "set", + "use", +] as const + +const MARKDOWN_FORBIDDEN_HTML_TAGS = [ + "embed", + "form", + "iframe", + "object", + "script", + "textarea", +] as const + +export const MARKDOWN_SANITIZE_SCHEMA: RehypeSanitizeOptions = { + ...defaultSchema, + clobberPrefix: "byteflow-md-", + attributes: { + ...defaultSchema.attributes, + input: [ + ...((defaultSchema.attributes?.input ?? []) as NonNullable[string]), + ["checked", true], + ], + }, + protocols: { + ...defaultSchema.protocols, + cite: ["http", "https"], + href: ["http", "https", "mailto"], + longDesc: ["http", "https"], + src: ["data", "blob"], + }, + strip: [ + ...new Set([ + ...(defaultSchema.strip ?? []), + ...MARKDOWN_FORBIDDEN_HTML_TAGS, + ...ACTIVE_SVG_TAGS, + ]), + ], + tagNames: (defaultSchema.tagNames ?? []).filter( + (tagName) => ![...MARKDOWN_FORBIDDEN_HTML_TAGS, ...ACTIVE_SVG_TAGS].includes(tagName as (typeof MARKDOWN_FORBIDDEN_HTML_TAGS | typeof ACTIVE_SVG_TAGS)[number]), + ), +} diff --git a/src/core/security/sanitize.ts b/src/core/security/sanitize.ts index cefeb3f4..ccce52f3 100644 --- a/src/core/security/sanitize.ts +++ b/src/core/security/sanitize.ts @@ -1,6 +1,4 @@ -import DOMPurify from "isomorphic-dompurify" -import { defaultSchema } from "rehype-sanitize" -import type { Options as RehypeSanitizeOptions } from "rehype-sanitize" +import DOMPurify from "dompurify" const ACTIVE_SVG_TAGS = [ "animate", @@ -42,35 +40,6 @@ const ACTIVE_EVENT_ATTRIBUTES = [ "onunload", ] as const -export const MARKDOWN_SANITIZE_SCHEMA: RehypeSanitizeOptions = { - ...defaultSchema, - clobberPrefix: "byteflow-md-", - attributes: { - ...defaultSchema.attributes, - input: [ - ...((defaultSchema.attributes?.input ?? []) as NonNullable[string]), - ["checked", true], - ], - }, - protocols: { - ...defaultSchema.protocols, - cite: ["http", "https"], - href: ["http", "https", "mailto"], - longDesc: ["http", "https"], - src: ["data", "blob"], - }, - strip: [ - ...new Set([ - ...(defaultSchema.strip ?? []), - ...MARKDOWN_FORBIDDEN_HTML_TAGS, - ...ACTIVE_SVG_TAGS, - ]), - ], - tagNames: (defaultSchema.tagNames ?? []).filter( - (tagName) => ![...MARKDOWN_FORBIDDEN_HTML_TAGS, ...ACTIVE_SVG_TAGS].includes(tagName as (typeof MARKDOWN_FORBIDDEN_HTML_TAGS | typeof ACTIVE_SVG_TAGS)[number]), - ), -} - export function sanitizeHtml(html: string): string { return DOMPurify.sanitize(html, { USE_PROFILES: { html: true }, diff --git a/src/features/tool-templates/markdown-preview-renderer.tsx b/src/features/tool-templates/markdown-preview-renderer.tsx index 2135f1c5..731d7d43 100644 --- a/src/features/tool-templates/markdown-preview-renderer.tsx +++ b/src/features/tool-templates/markdown-preview-renderer.tsx @@ -5,7 +5,7 @@ import ReactMarkdown from "react-markdown" import remarkGfm from "remark-gfm" import rehypeRaw from "rehype-raw" import rehypeSanitize from "rehype-sanitize" -import { MARKDOWN_SANITIZE_SCHEMA } from "@/core/security/sanitize" +import { MARKDOWN_SANITIZE_SCHEMA } from "@/core/security/markdown-sanitize-schema" const MARKDOWN_RENDER_COMPONENTS = { h1: (props: React.HTMLAttributes & { node?: unknown }) => { diff --git a/tests/guards/html-injection-surface-guard.test.ts b/tests/guards/html-injection-surface-guard.test.ts index 827f81f2..de537dd4 100644 --- a/tests/guards/html-injection-surface-guard.test.ts +++ b/tests/guards/html-injection-surface-guard.test.ts @@ -69,7 +69,9 @@ describe("HTML injection surface guard", () => { const sanitizerSource = readSource("src/core/security/sanitize.ts") expect(logicSource).toContain("import { sanitizeSvg } from \"@/core/security/sanitize\"") - expect(sanitizerSource).toContain("import DOMPurify from \"isomorphic-dompurify\"") + expect(sanitizerSource).toContain("import DOMPurify from \"dompurify\"") + expect(sanitizerSource).not.toContain("isomorphic-dompurify") + expect(sanitizerSource).not.toContain("rehype-sanitize") expect(sanitizerSource).toContain("export function sanitizeSvg(svg: string): string") expect(sanitizerSource).toContain("export function sanitizeSvgForPreview(svg: string): string") expect(sanitizerSource).toContain("DOMPurify.sanitize(svg") @@ -84,6 +86,7 @@ describe("HTML injection surface guard", () => { const markdownPageSource = readSource("src/features/tools/markdown-preview/page.tsx") const markdownRendererSource = readSource("src/features/tool-templates/markdown-preview-renderer.tsx") const markdownExportSource = readSource("src/features/tools/markdown-preview/export.ts") + const markdownSchemaSource = readSource("src/core/security/markdown-sanitize-schema.ts") const svgRasterSource = readSource("src/features/tools/svg-to-png-converter/utils.ts") const svgStrokeSource = readSource("src/features/tools/svg-stroke-to-fill-converter/utils.ts") @@ -92,8 +95,9 @@ describe("HTML injection surface guard", () => { expect(markdownExportSource).toContain("import { sanitizeMarkdownHtml } from \"@/core/security/sanitize\"") expect(markdownExportSource).toContain("export function sanitizeMarkdownPreviewHtml") expect(markdownExportSource).toContain("const safeHtml = sanitizeMarkdownPreviewHtml(previewHtml)") - expect(markdownRendererSource).toContain("MARKDOWN_SANITIZE_SCHEMA") + expect(markdownRendererSource).toContain("import { MARKDOWN_SANITIZE_SCHEMA } from \"@/core/security/markdown-sanitize-schema\"") expect(markdownRendererSource).toContain("[rehypeSanitize, MARKDOWN_SANITIZE_SCHEMA]") + expect(markdownSchemaSource).toContain("from \"rehype-sanitize\"") expect(svgRasterSource).toContain("import { sanitizeSvgForPreview } from \"@/core/security/sanitize\"") expect(svgStrokeSource).toContain("import { sanitizeSvgForPreview } from \"@/core/security/sanitize\"") })