diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index f39103e6..6a642b21 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -1,4 +1,5 @@ { "node": "18", + "installCommand": "codesandbox:install", "sandboxes": [] } diff --git a/.eslintignore b/.eslintignore index f9f44702..3f17f713 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,6 +2,7 @@ coverage dist lib CHANGELOG.md +/DOMPurify /auto-imports.d.ts /pnpm-lock.yaml !/.github diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ef17711..5634d791 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,8 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v4 + with: + submodules: true - name: Setup pnpm uses: pnpm/action-setup@v2 diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index 311f7c21..b4bc732e 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -12,6 +12,8 @@ jobs: CI_JOB_NUMBER: 1 steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Setup pnpm uses: pnpm/action-setup@v2 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..69d61b38 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "DOMPurify"] + path = DOMPurify + url = https://github.com/cure53/DOMPurify.git diff --git a/.prettierignore b/.prettierignore index aaa2ad02..8df66a17 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,4 +3,6 @@ coverage dist lib +/DOMPurify +/auto-imports.d.ts /pnpm-lock.yaml diff --git a/.stylelintignore b/.stylelintignore index c19c6f24..9957834d 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -2,8 +2,10 @@ coverage dist lib LICENSE +/DOMPurify *.json *.log +*.mts *.patch *.snap *.svg diff --git a/DOMPurify b/DOMPurify new file mode 160000 index 00000000..d1e4f216 --- /dev/null +++ b/DOMPurify @@ -0,0 +1 @@ +Subproject commit d1e4f216664cc9e1ba5498a00b7d88a9cbd4c7f0 diff --git a/dompurify.fixtures.d.mts b/dompurify.fixtures.d.mts new file mode 100644 index 00000000..5c1ab3e0 --- /dev/null +++ b/dompurify.fixtures.d.mts @@ -0,0 +1,11 @@ +declare module 'DOMPurify/test/fixtures/expect.mjs' { + export interface Fixture { + title?: string + payload: string + expected: string[] | string + } + + const fixtures: Fixture[] + + export default fixtures +} diff --git a/package.json b/package.json index d48563a4..409c6b2f 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,11 @@ "xml-sanitizer" ], "scripts": { - "build": "pnpm test && run-p build:*", + "build": "pnpm test && pnpm build-lib", + "build-lib": "run-p build:*", "build:r": "r -f cjs", "build:tsc": "tsc -p src", + "codesandbox:install": "git submodule update --init && pnpm i", "dev": "vitest", "docs:build": "w -e docs -p --publicPath /", "docs:dev": "w -e docs", @@ -41,7 +43,7 @@ "lint:es": "eslint . --cache -f friendly --max-warnings 10", "lint:style": "stylelint . --cache", "lint:tsc": "tsc --noEmit", - "prepare": "simple-git-hooks", + "prepare": "simple-git-hooks || exit 0", "release": "pnpm build && changeset publish", "serve": "sirv dist -s", "test": "vitest run --coverage", @@ -63,7 +65,7 @@ "@types/react-dom": "^18.2.17", "@types/web": "^0.0.127", "@vitest/coverage-istanbul": "^1.0.4", - "domiso": "link:", + "domiso": "link:.", "github-markdown-css": "^5.5.0", "jsdom": "^23.0.1", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4d99cbf..fdb62b68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,7 +48,7 @@ devDependencies: specifier: ^1.0.4 version: 1.0.4(vitest@1.0.4) domiso: - specifier: 'link:' + specifier: link:. version: 'link:' github-markdown-css: specifier: ^5.5.0 @@ -4865,6 +4865,14 @@ packages: resolution: {integrity: sha512-4J1l5d79hoIvsrKh5VUKVRA1aIdsOb10Hu5j3J2VfP/msDnfTdGPmNp2E1Wg+vs97Bktzo+MZePFFXSGoykYJw==} dev: true + /@types/jsdom@21.1.6: + resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} + dependencies: + '@types/node': 20.10.4 + '@types/tough-cookie': 4.0.5 + parse5: 7.1.2 + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true @@ -5043,6 +5051,10 @@ packages: resolution: {integrity: sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==} dev: true + /@types/tough-cookie@4.0.5: + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + dev: true + /@types/trusted-types@2.0.7: resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} dev: true @@ -10469,6 +10481,7 @@ packages: /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: safer-buffer: 2.1.2 dev: true diff --git a/src/index.ts b/src/index.ts index 85f863d0..724c8fa3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,39 @@ let domParser: DOMParser | undefined -const isDocument = (el: Document | Element): el is Document => - el.nodeType === el.DOCUMENT_NODE +export type DocumentOrFragment = Document | DocumentFragment + +const isDocumentOrFragment = ( + el: DocumentOrFragment | Element, +): el is DocumentOrFragment => + el.nodeType === Node.DOCUMENT_NODE || + el.nodeType === Node.DOCUMENT_FRAGMENT_NODE + +export function getTagName(el: DocumentOrFragment): undefined +export function getTagName(el: Element): string +export function getTagName(el: DocumentOrFragment | Element): string | undefined +export function getTagName(el: DocumentOrFragment | Element) { + return 'tagName' in el ? el.tagName.toLowerCase() : undefined +} + +/** + * @see https://www.w3schools.com/tags/att_form.asp + */ +export const DISALLOWED_FORM_ATTR_TAG_NAMES = + 'button,fieldset,input,label,meter,object,output,select,textarea'.split(',') const sanitizeAttributes = (el: Element) => { + const tagName = getTagName(el) const attrs = el.attributes for (let i = 0, len = attrs.length; i < len; i++) { const attr = attrs[i] - if ( - /^on/i.test(attr.name) || - /^(?:data|javascript|vbscript):/i.test(attr.value) + const { name, value } = attr + if (name === 'is') { + attr.value = '' + } else if ( + name === 'autofocus' || + (name === 'form' && DISALLOWED_FORM_ATTR_TAG_NAMES.includes(tagName)) || + /^on/i.test(name) || + /^(?:\w+script|data):/i.test(value.replaceAll(/\r?\n/g, '')) ) { el.removeAttributeNode(attr) // eslint-disable-next-line sonar/updated-loop-counter -- the attribute is removed, the index and length must be rechecked @@ -20,11 +44,16 @@ const sanitizeAttributes = (el: Element) => { return el } -const sanitizeChildren = (el: T) => { +const sanitizeChildren = (el: T) => { for (let i = 0, len = el.children.length; i < len; i++) { - const sanitized = sanitizeNode(el.children[i]) - if (sanitized == null) { - // eslint-disable-next-line sonar/updated-loop-counter -- the child is removed, the index and length must be rechecked + const item = el.children[i] + const sanitized = sanitizeNode(item, getTagName(el)) + if (sanitized === item) { + continue + } + if (sanitized == null || typeof sanitized === 'string') { + item.replaceWith(...(sanitized == null ? [] : [sanitized])) + // eslint-disable-next-line sonar/updated-loop-counter -- the child is removed or replaced by text, the index and length must be rechecked i-- len-- } @@ -32,16 +61,56 @@ const sanitizeChildren = (el: T) => { return el } +/** + * @see https://developer.mozilla.org/en-US/docs/Web/MathML/Authoring#using_mathml + */ +export const MathML_TAG_NAMES = new Set( + 'error,frac,i,multiscripts,n,o,over,padded,phantom,root,row,s,space,sqrt,style,sub,subsup,sup,table,td,text,tr,under,underover' + .split(',') + .map(it => `m${it}`), +) + function sanitizeNode(el: Document): Document -function sanitizeNode(el: Element): Element | null -function sanitizeNode(el: Document | Element) { - if (isDocument(el)) { +function sanitizeNode(el: DocumentFragment): DocumentFragment +function sanitizeNode( + el: Element, + parentTagName?: string, +): Element | string | null +function sanitizeNode( + el: DocumentOrFragment | Element, + parentTagName?: string, +) { + if (isDocumentOrFragment(el)) { return sanitizeChildren(el) } - if (['parsererror', 'script'].includes(el.tagName.toLowerCase())) { - el.remove() - return null + const tagName = getTagName(el) + + if ( + (parentTagName === 'math' && !MathML_TAG_NAMES.has(tagName)) || + // unknown HTML element + el instanceof HTMLUnknownElement || + // unknown SVG element + Object.getPrototypeOf(el) === SVGElement.prototype + ) { + return el.textContent + } + + switch (tagName) { + case 'iframe': + case 'link': + case 'meta': + case 'parsererror': + case 'script': + // eslint-disable-next-line no-fallthrough -- deprecated tags + case 'noembed': + case 'xmp': { + el.remove() + return + } + case 'template': { + sanitizeChildren((el as HTMLTemplateElement).content) + } } return sanitizeChildren(sanitizeAttributes(el)) @@ -50,23 +119,37 @@ function sanitizeNode(el: Document | Element) { export const TEXT_HTML = 'text/html' export const IMAGE_SVG_XML = 'image/svg+xml' +export interface SanitizeOptions { + type?: DOMParserSupportedType + fragment?: boolean +} + export const sanitize = ( domString: string, - type: DOMParserSupportedType = TEXT_HTML, + typeOrFragment?: DOMParserSupportedType | boolean, ) => { + const trimmed = domString.trim() + + if (!trimmed) { + return domString + } + if (!domParser) { domParser = new DOMParser() } + const { type = TEXT_HTML, fragment }: SanitizeOptions = + typeOrFragment == null || typeof typeOrFragment === 'string' + ? { type: typeOrFragment } + : { fragment: typeOrFragment } + const doc = sanitizeNode(domParser.parseFromString(domString, type)) return ( - ((type !== IMAGE_SVG_XML || - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `document.body` is unavailable in XML, see also https://github.com/microsoft/TypeScript/issues/29052#issuecomment-447998135 - !doc.body) && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- https://github.com/microsoft/TypeScript/issues/50078 - doc.documentElement?.outerHTML) || - '' + (fragment && type === TEXT_HTML + ? doc.body.innerHTML + : // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- https://github.com/microsoft/TypeScript/issues/50078 + doc.documentElement?.outerHTML) || '' ) } diff --git a/test/__snapshots__/dompurify.spec.ts.snap b/test/__snapshots__/dompurify.spec.ts.snap new file mode 100644 index 00000000..f8d6ca24 --- /dev/null +++ b/test/__snapshots__/dompurify.spec.ts.snap @@ -0,0 +1,1296 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`dompurify compatibility > 65 > 65 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 71 > 71 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 90 > 90 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 91 > 91 1`] = ` +{ + "expected": [ + "
//["'\`-->]]>]
", + "
//["'\`-->]]>]
", + ], + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 94 > 94 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 97 > 97 1`] = ` +{ + "expected": "
XXX//["'\`-->]]>]
", + "payload": "
XXX//["'\`-->]]>]
", + "result": "
XXX//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 98 > 98 1`] = ` +{ + "expected": "
", + "payload": "
", + "result": "
", +} +`; + +exports[`dompurify compatibility > 103 > 103 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
alert(47)//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 105 > 105 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 106 > 106 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 107 > 107 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 109 > 109 1`] = ` +{ + "expected": "//["'\`-->]]>]", + "payload": "//["'\`-->]]>]
", + "result": "//["'\`-->]]>]", +} +`; + +exports[`dompurify compatibility > 113 > 113 1`] = ` +{ + "expected": "
alert(57)//0//["'\`-->]]>]
", + "payload": "
alert(57)//0//["'\`-->]]>]
", + "result": "
alert(57)//0//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 117 > 117 1`] = ` +{ + "expected": "
+ + + +//["'\`-->]]>]
", + "payload": "
+ + + +//["'\`-->]]>]
", + "result": "
+ + + +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 118 > 118 1`] = ` +{ + "expected": "
// O10.10↓, OM10.0↓, GC6↓, FF + + // IE6, O10.10↓, OM10.0↓ + // IE6, O11.01↓, OM10.1↓//["'\`-->]]>]
", + "payload": "
// O10.10↓, OM10.0↓, GC6↓, FF + + // IE6, O10.10↓, OM10.0↓ + // IE6, O11.01↓, OM10.1↓//["'\`-->]]>]
", + "result": "
// O10.10↓, OM10.0↓, GC6↓, FF + + // IE6, O10.10↓, OM10.0↓ + // IE6, O11.01↓, OM10.1↓//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 120 > 120 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 121 > 121 1`] = ` +{ + "expected": "//["'\`-->]]>] +
+]>//["'\`-->]]>]
", + "payload": " +//["'\`-->]]>]
+
+]>//["'\`-->]]>]
", + "result": "//["'\`-->]]>] + +]>//["'\`-->]]>]", +} +`; + +exports[`dompurify compatibility > 125 > 125 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 127 > 127 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 128 > 128 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 130 > 130 1`] = ` +{ + "expected": "
&x;//["'\`-->]]>]
", + "payload": "
&x;//["'\`-->]]>]
", + "result": "
&x;//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 131 > 131 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 132 > 132 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 133 > 133 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 136 > 136 1`] = ` +{ + "expected": "
//["'\`-->]]>]
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 139 > 139 1`] = ` +{ + "expected": "
//["'\`-->]]>]
+ +//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
+ +//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
+ +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 141 > 141 1`] = ` +{ + "expected": "
+
+ + + + +
PRESS ENTER
//["'\`-->]]>]
", + "payload": "
+
+ + + + +
PRESS ENTER
//["'\`-->]]>]
", + "result": "
+
+ + + + +
PRESS ENTER
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 142 > 142 1`] = ` +{ + "expected": "
[A] +"> +"> +"> +[B] +"> +[C] + +[D] +<% foo>//["'\`-->]]>]
", + "payload": "
[A] +"> +"> +"> +[B] +"> +[C] + +[D] +<% foo>//["'\`-->]]>]
", + "result": "
[A] +"> +"> +"> +[B] +"> +[C] + +[D] +<% foo>//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 146 > 146 1`] = ` +{ + "expected": [ + "
+ + + +//["'\`-->]]>]
", + "
+ + + + + +//["'\`-->]]>]
", + "
+ + + +//["'\`-->]]>]
", + ], + "payload": "
+ + + +//["'\`-->]]>]
", + "result": "
+ + + +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 148 > 148 1`] = ` +{ + "expected": "
+
+ + +
+//["'\`-->]]>]
", + "payload": "
+
+ + +
+//["'\`-->]]>]
", + "result": "
+
+ + +
+//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 151 > 151 1`] = ` +{ + "expected": [ + "
//["'\`-->]]>]
", + "
//["'\`-->]]>]
", + "
//["'\`-->]]>]
", + "
//["'\`-->]]>]
", + ], + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 154 > 154 1`] = ` +{ + "expected": [ + "
//["'\`-->]]>]
", + "
//["'\`-->]]>]
", + ], + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 156 > 156 1`] = ` +{ + "expected": [ + "
+\`><img src=xx onerror=alert(108)></a> + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", + "
+\`><img src=xx onerror=alert(108)></a> + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", + "
+\`><img src=xx onerror=alert(108)></a> + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", + "
+ + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", + "
+ + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", + ], + "payload": "
+\`><img src=xx onerror=alert(108)></a> + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", + "result": "
+\`><img src=xx onerror=alert(108)></a> + +\`><img src=xx onerror=alert(2)// +\`><img src=xx onerror=alert(3)////["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 157 > 157 1`] = ` +{ + "expected": [ + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + ], + "payload": "
+ + +//["'\`-->]]>]
", + "result": "
+ + +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 158 > 158 1`] = ` +{ + "expected": [ + "
+ +//["'\`-->]]>]
", + "
+ +//["'\`-->]]>]
", + "
+ +//["'\`-->]]>]
", + "
+ +//["'\`-->]]>]
", + ], + "payload": "
+ +//["'\`-->]]>]
", + "result": "
+ +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 161 > 161 1`] = ` +{ + "expected": "
XXX//["'\`-->]]>]
+//["'\`-->]]>]
", + "payload": "
XXX//["'\`-->]]>]
+//["'\`-->]]>]
", + "result": "
XXX//["'\`-->]]>]
+//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 166 > 166 1`] = ` +{ + "expected": "//["'\`-->]]>]", + "payload": "//["'\`-->]]>]
", + "result": "//["'\`-->]]>]", +} +`; + +exports[`dompurify compatibility > 172 > 172 1`] = ` +{ + "expected": [ + "
]> //["'\`-->]]>]
", + "
]> //["'\`-->]]>]
", + "
]> //["'\`-->]]>]
", + "
]> //["'\`-->]]>]
", + "
]>//["'\`-->]]>]
", + ], + "payload": "
]> //["'\`-->]]>]
", + "result": "
]> //["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 173 > 173 1`] = ` +{ + "expected": "
+//["'\`-->]]>]
", + "payload": "
+//["'\`-->]]>]
", + "result": "
+//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 174 > 174 1`] = ` +{ + "expected": [ + "
+ +alert(127) +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + ], + "payload": "
+ +alert(127) +//["'\`-->]]>]
", + "result": "
+ +alert(127) +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 175 > 175 1`] = ` +{ + "expected": [ + "
//["'\`-->]]>]
", + "
//["'\`-->]]>]
", + "
", + ], + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 178 > 178 1`] = ` +{ + "expected": [ + "
+
+ +
+ +
+ + + + + + + +//["'\`-->]]>]
", + "
+
+ +
+ +
+ + + + + + + +//["'\`-->]]>]
", + ], + "payload": "
+
+ +
+ +
+ + + + + + + +//["'\`-->]]>]
", + "result": "
+
+ +
+ +
+ + + + + + + +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 179 > 179 1`] = ` +{ + "expected": "
//["'\`-->]]>]
", + "payload": "
//["'\`-->]]>]
", + "result": "
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > 182 > 182 1`] = ` +{ + "expected": [ + "
+ + + + +
//["'\`-->]]>]
", + "
+ + + + +
//["'\`-->]]>]
", + ], + "payload": "
+ + + + +
//["'\`-->]]>]
", + "result": "
+ + + + +
//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > Avoid over-zealous stripping of SVG filter elements (see #144) > Avoid over-zealous stripping of SVG filter elements (see #144) 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Bypass using DOM bugs when dealing with JS URIs in arbitrary attributes (II) > Bypass using DOM bugs when dealing with JS URIs in arbitrary attributes (II) 1`] = ` +{ + "expected": "


", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Bypass using event handlers and unknown attributes > Bypass using event handlers and unknown attributes 1`] = ` +{ + "expected": "", + "payload": "", +} +`; + +exports[`dompurify compatibility > Bypass using multiple unknown attributes > Bypass using multiple unknown attributes 1`] = ` +{ + "expected": "
@superevr
", + "payload": "
@superevr
", + "result": "
@superevr
", +} +`; + +exports[`dompurify compatibility > Bypass using unknown attributes III > Bypass using unknown attributes III 1`] = ` +{ + "expected": "
text
", + "payload": "
text", + "result": "
text
", +} +`; + +exports[`dompurify compatibility > Bypass using unknown attributes V > Bypass using unknown attributes V 1`] = ` +{ + "expected": "


", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > DOM Clobbering against an empty cookie > DOM Clobbering against an empty cookie 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > DOM Clobbering against document.createElement() (see #47) > DOM Clobbering against document.createElement() (see #47) 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > DOM clobbering XSS by @irsdl using attributes > DOM clobbering XSS by @irsdl using attributes 1`] = ` +{ + "expected": [ + "", + "
", + ], + "payload": "
", + "result": "
", +} +`; + +exports[`dompurify compatibility > DOM clobbering attack using activeElement > DOM clobbering attack using activeElement 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > DOM clobbering attack using name=body > DOM clobbering attack using name=body 1`] = ` +{ + "expected": "@mmrupp", + "payload": "@mmrupp", + "result": "@mmrupp", +} +`; + +exports[`dompurify compatibility > DOM clobbering attack using name=body and injecting SVG + keygen > DOM clobbering attack using name=body and injecting SVG + keygen 1`] = ` +{ + "expected": ", ", + "payload": ", ", + "result": ", ", +} +`; + +exports[`dompurify compatibility > DOM clobbering: acceptCharset > DOM clobbering: acceptCharset 1`] = ` +{ + "expected": "123", + "payload": "123", + "result": "123", +} +`; + +exports[`dompurify compatibility > DOM clobbering: getElementById > DOM clobbering: getElementById 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > DOM clobbering: hasChildNodes > DOM clobbering: hasChildNodes 1`] = ` +{ + "expected": "
", + "payload": "
", + "result": "
", +} +`; + +exports[`dompurify compatibility > DOM clobbering: location > DOM clobbering: location 1`] = ` +{ + "expected": "invisible", + "payload": "invisible", + "result": "invisible", +} +`; + +exports[`dompurify compatibility > DOM clobbering: submit > DOM clobbering: submit 1`] = ` +{ + "expected": "123", + "payload": "123", + "result": "123", +} +`; + +exports[`dompurify compatibility > Don't remove ARIA attributes if not prohibited (see #203) > Don't remove ARIA attributes if not prohibited (see #203) 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Don't remove binary attributes if considered safe (see #168) > Don't remove binary attributes if considered safe (see #168) 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Don't remove data URIs from SVG images (see #205) > Don't remove data URIs from SVG images (see #205) 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Don't remove data URIs from SVG images, with href attribute > Don't remove data URIs from SVG images, with href attribute 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Fixed an exception coming from missing clobbering protection > Fixed an exception coming from missing clobbering protection 1`] = ` +{ + "expected": [ + "", + "
", + ], + "payload": "
", + "result": "
", +} +`; + +exports[`dompurify compatibility > Image after self-closing style to trick jQuery tag-completion > Image after self-closing style to trick jQuery tag-completion 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Image after style to trick jQuery tag-completion > Image after style to trick jQuery tag-completion 1`] = ` +{ + "expected": "", + "payload": "", +} +`; + +exports[`dompurify compatibility > Image with data URI src > Image with data URI src 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Image with data URI src with whitespace > Image with data URI src with whitespace 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Img element inside noscript terminated inside attribute > Img element inside noscript terminated inside attribute 1`] = ` +{ + "expected": [ + "", + "">", + "", + "", + ], + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Img element inside noscript terminated inside comment > Img element inside noscript terminated inside comment 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Img inside style inside broken option element > Img inside style inside broken option element 1`] = ` +{ + "expected": "", + "payload": "", + "result": "", +} +`; + +exports[`dompurify compatibility > Inline SVG (data-uri) > Inline SVG (data-uri) 1`] = ` +{ + "expected": [ + "
+ +//["'\`-->]]>]
", + "
+ +//["'\`-->]]>]
", + ], + "payload": "
+ +//["'\`-->]]>]
", + "result": "
+ +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > JavaScript URIs using Unicode LS/PS I > JavaScript URIs using Unicode LS/PS I 1`] = ` +{ + "expected": "123I am a dolphin!", + "payload": "123I am a dolphin!", + "result": "123I am a dolphin!", +} +`; + +exports[`dompurify compatibility > JavaScript URIs using Unicode LS/PS II > JavaScript URIs using Unicode LS/PS II 1`] = ` +{ + "expected": "123I am a dolphin too!", + "payload": "123I am a dolphin too!", + "result": "123I am a dolphin too!", +} +`; + +exports[`dompurify compatibility > JavaScript URIs using Unicode Whitespace > JavaScript URIs using Unicode Whitespace 1`] = ` +{ + "expected": "123CLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICK", + "payload": "123CLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICK", + "result": "123CLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICKCLICK", +} +`; + +exports[`dompurify compatibility > Low-range-ASCII obfuscated JavaScript URI > Low-range-ASCII obfuscated JavaScript URI 1`] = ` +{ + "expected": "@shafigullin", + "payload": "@shafigullin", + "result": "@shafigullin", +} +`; + +exports[`dompurify compatibility > MathML > MathML 1`] = ` +{ + "expected": [ + "
CLICKME + + + + + + +//["'\`-->]]>]
", + "
CLICKME + + +CLICKME + + +CLICKMEhttp://http://google.com +//["'\`-->]]>]
", + "
CLICKME +//["'\`-->]]>]
", + ], + "payload": "
CLICKME + + +CLICKME + + +CLICKMEhttp://http://google.com +//["'\`-->]]>]
", + "result": "
CLICKME + + +CLICKME + + +CLICKMEhttp://http://google.com +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > MathML example > MathML example 1`] = ` +{ + "expected": [ + " + + a, + a, + a, + a, + a, + a + +", + " + + a, + a, + a, + a, + a, + a + +", + ], + "payload": " + + a, + a, + a, + a, + a, + a + +", + "result": " + + a, + a, + a, + a, + a, + a + +", +} +`; + +exports[`dompurify compatibility > SVG > SVG 1`] = ` +{ + "expected": [ + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + "
+ + +//["'\`-->]]>]
", + ], + "payload": "
+ + +//["'\`-->]]>]
", + "result": "
+ + +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > SVG > SVG 2`] = ` +{ + "expected": "
+ + + +//["'\`-->]]>]
", + "payload": "
+ + + +//["'\`-->]]>]
", + "result": "
+ + + +//["'\`-->]]>]
", +} +`; + +exports[`dompurify compatibility > Special esacpes in protocol handler for XSS in Blink > Special esacpes in protocol handler for XSS in Blink 1`] = ` +{ + "expected": "@shafigullin", + "payload": "@shafigullin", + "result": "@shafigullin", +} +`; + +exports[`dompurify compatibility > Test against fake-element-based namepsace-confusion abusing mXSS attacks 1/2 > Test against fake-element-based namepsace-confusion abusing mXSS attacks 1/2 1`] = ` +{ + "expected": "a", + "payload": "a