CVE-2023-32070 (Other, published 2023-05-10T18:15Z)
Description: value: XWiki Platform is a generic wiki platform. Prior to version 14.6-rc-1, HTML rendering didn't check for dangerous attributes/attribute values. This allowed cross-site scripting (XSS) attacks via attributes and link URLs, e.g., supported in XWiki syntax. This has been patched in XWiki 14.6-rc-1. There are no known workarounds apart from upgrading to a fixed version.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/xwiki/xwiki-rendering
Files changed:
xwiki-rendering-integration-tests/src/test/resources/simple/escape/escape15.test
xwiki-rendering-integration-tests/src/test/resources/simple/group/group9.test
xwiki-rendering-integration-tests/src/test/resources/simple/horizontalline/horizontalline5.test
xwiki-rendering-integration-tests/src/test/resources/simple/italic/italic8.test
xwiki-rendering-integration-tests/src/test/resources/simple/list/definitionlist7.test
xwiki-rendering-integration-tests/src/test/resources/simple/list/list13.test
xwiki-rendering-integration-tests/src/test/resources/simple/list/list8.test
xwiki-rendering-integration-tests/src/test/resources/simple/macros/macro10.test
xwiki-rendering-integration-tests/src/test/resources/simple/paragraph/paragraph6.test
xwiki-rendering-integration-tests/src/test/resources/simple/quote/quote3.test
xwiki-rendering-integration-tests/src/test/resources/simple/section/section6.test
xwiki-rendering-integration-tests/src/test/resources/simple/strikedout/strikedout4.test
xwiki-rendering-integration-tests/src/test/resources/simple/subscript/subscript2.test
xwiki-rendering-integration-tests/src/test/resources/simple/superscript/superscript2.test
xwiki-rendering-integration-tests/src/test/resources/simple/table/table3.test
xwiki-rendering-integration-tests/src/test/resources/simple/verbatim/verbatim6.test
xwiki-rendering-integration-tests/src/test/resources/simple/verbatim/verbatim7.test
xwiki-rendering-integration-tests/src/test/resources/wiki/image/image1.test
xwiki-rendering-integration-tests/src/test/resources/wiki/image/image12.test
xwiki-rendering-integration-tests/src/test/resources/wiki/image/image8.test
xwiki-rendering-integration-tests/src/test/resources/wiki/image/image9.test
xwiki-rendering-integration-tests/src/test/resources/wiki/link/links14.test
xwiki-rendering-integration-tests/src/test/resources/wiki/link/links15.test
xwiki-rendering-integration-tests/src/test/resources/wiki/link/links19.test
xwiki-rendering-integration-tests/src/test/resources/wiki/link/links2.test
xwiki-rendering-integration-tests/src/test/resources/wiki/link/links8.test
xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/HTMLMacroXHTMLRenderer.java
xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/renderers/annotatedhtml5/HTMLMacroAnnotatedHTML5Renderer.java
xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/renderers/annotatedxhtml/HTMLMacroAnnotatedXHTMLRenderer.java
xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/renderers/html5/HTMLMacroHTML5Renderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-annotatedhtml5/src/main/java/org/xwiki/rendering/internal/renderer/html5/AnnotatedHTML5ChainingRenderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-annotatedhtml5/src/main/java/org/xwiki/rendering/internal/renderer/html5/AnnotatedHTML5Renderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-annotatedhtml5/src/test/resources/annotatedhtml50/simple/monospace/monospace2.out.txt
xwiki-rendering-syntaxes/xwiki-rendering-syntax-annotatedxhtml/src/main/java/org/xwiki/rendering/internal/renderer/xhtml/AnnotatedXHTMLChainingRenderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-annotatedxhtml/src/main/java/org/xwiki/rendering/internal/renderer/xhtml/AnnotatedXHTMLRenderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-html5/src/main/java/org/xwiki/rendering/internal/renderer/html5/HTML5ChainingRenderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-html5/src/main/java/org/xwiki/rendering/internal/renderer/html5/HTML5Renderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-html5/src/test/java/org/xwiki/rendering/internal/html5/HTML5ChainingRendererTest.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/main/java/org/xwiki/rendering/internal/parser/xhtml/wikimodel/XHTMLXWikiGeneratorListener.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/main/java/org/xwiki/rendering/internal/renderer/xhtml/XHTMLChainingRenderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/main/java/org/xwiki/rendering/internal/renderer/xhtml/XHTMLRenderer.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/test/java/org/xwiki/rendering/internal/renderer/xhtml/XHTMLChainingRendererTest.java
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/test/resources/xhtml10/simple/monospace/monospace2.inout.txt
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/test/resources/xhtml10/specific/bold/bold4.test
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/test/resources/xhtml10/specific/encoding/encoding1.test
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/test/resources/xhtml10/specific/group/group1.test
xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml5/src/test/resources/xhtml5/simple/monospace/monospace2.inout.txt
xwiki-rendering-xml/src/main/java/org/xwiki/rendering/renderer/printer/XHTMLWikiPrinter.java
Commit message: XRENDERING-663: Restrict allowed attributes in HTML rendering

* Change HTML renderers to only print allowed attributes and elements.
* Add prefix to forbidden attributes to preserve them in XWiki syntax.
* Adapt tests to expect that invalid attributes get a prefix.


Vulnerable code:
<p><span class="wikiexternallink"><a param="value" href="refe||rence">lab&gt;&gt;el~</a></span></p>
<p><!--startwikilink:false|-|url|-|refe||rence--><span class="wikiexternallink"><a param="value" href="refe||rence">lab&gt;&gt;el~</a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|url|-|refe||rence--><span class="wikiexternallink"><a param="value" href="refe||rence">lab&gt;&gt;el~</a></span><!--stopwikilink--></p>
<div param="value"><p>some text</p></div><div param="value"><p>some text</p></div>
<div param="value"><p>some text</p></div><div param="value"><p>some text</p></div>
<hr param="value"/>
<html><hr param="value"/></html>
<p><em><span a="b">something</span></em></p>
<dl param="value"><dt>title</dt><dd>definition</dd></dl>
<dl param="value"><dt>title</dt><dd>definition</dd></dl>
<ul param="value"><li>item 1<ul param2="value2"><li>item 1.1<ul><li>item 1.1.1</li></ul></li></ul></li></ul>
<ul param="value"><li>item 1<ul param2="value2"><li>item 1.1<ul><li>item 1.1.1</li></ul></li></ul></li></ul>
<ul param1="value1" param2="value2"><li>item</li></ul>
<html><ul param1="value1" param2="value2"><li>item</li></ul></html>
<span param="value">formatmacro2</span><p><span a="b"><span param="value">formatmacro3</span> text</span></p><p><span a="b">text</span><span param="value">formatmacro4</span></p>
<!--startmacro:testformatmacro|-|--><span param="value">formatmacro2</span><!--stopmacro--><p><span a="b"><!--startmacro:testformatmacro|-|--><span param="value">formatmacro3</span><!--stopmacro--> text</span></p><p><span a="b">text</span><!--startmacro:testformatmacro|-|--><span param="value">formatmacro4</span><!--stopmacro--></p>
<!--startmacro:testformatmacro|-|--><span param="value">formatmacro2</span><!--stopmacro--><p><span a="b"><!--startmacro:testformatmacro|-|--><span param="value">formatmacro3</span><!--stopmacro--> text</span></p><p><span a="b">text</span><!--startmacro:testformatmacro|-|--><span param="value">formatmacro4</span><!--stopmacro--></p>
<p param1="value1" param2="value2"><span param3="value3">hello</span> world</p><p><span param4="valueA">hello&nbsp;</span><span param4="valueB">world</span></p><p><strong><span param5="value4">hello</span></strong><span param5="value4">&nbsp;world</span></p><p><span param6="valueA">hello&nbsp;</span><span param7="valueB">world</span></p><p>before<span param="value">x</span><strong>y</strong>after</p><p>12<strong>34<span param="value">56</span>78</strong>90</p><p><span param="value">1<em>2</em></span><em>3</em></p><p><strong><span param1="value1">1</span></strong><strong><span param2="value2">2</span></strong></p>
<blockquote param1="value1" param2="value2"><p>line</p></blockquote>
<blockquote param1="value1" param2="value2"><p>line</p></blockquote>
<h1 id="Hheader" class="wikigeneratedid" param="value"><span>header</span></h1>
<html><h1 id="Hheader" class="wikigeneratedid" param="value"><span>header</span></h1></html>
<p><del><span a="b">something</span></del></p>
<html><p><sub a="b">something</sub></p></html>
<p><sub><span a="b">something</span></sub></p>
<html><p><sup a="b">something</sup></p></html>
<p><sup><span a="b">something</span></sup></p>
<table a="b"><tr c="d"><th e="f" scope="col"><span g="h">cell1</span></th><td i="j"><span k="l">cell2</span></td></tr></table><table a="b"><tr c="d"><th e="f" scope="col"><span g="h">cell1</span></th><td i="j"><span k="l">cell2</span></td></tr></table>
<table a="b"><tr c="d"><th e="f" scope="col"><span g="h">cell1</span></th><td i="j"><span k="l">cell2</span></td></tr></table><table a="b"><tr c="d"><th e="f" scope="col"><span g="h">cell1</span></th><td i="j"><span k="l">cell2</span></td></tr></table>
<pre param="value">abc</pre>
<html><pre param="value">abc</pre></html>
<p param="value"><span param2="value2"><tt class="wikimodel-verbatim">abc</tt></span></p>
<p param="value"><span param2="value2"><tt class="wikimodel-verbatim">abc</tt></span></p>
<p><img src="imageurl" param1="value1" param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><br/><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/></p>
<p><!--startimage:false|-|attach|-|my.png--><img src="imageurl" param1="value1" param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><!--stopimage--><br/><!--startimage:false|-|attach|-|Space.ExistingPage@my.png--><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<p><!--startimage:false|-|attach|-|my.png--><img src="imageurl" param1="value1" param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><!--stopimage--><br/><!--startimage:false|-|attach|-|Space.ExistingPage@my.png--><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<img src="imageurl" param1="value1" param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><div class="figcaption"><p>Image <strong>Caption</strong></p></div><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><div class="figcaption"><p>Caption</p><ul><li>With</li><li>nested</li><li>list <span class="wikiexternallink"><a href="https://xwiki.org">and link</a></span> and [[fake link]].</li></ul></div>
<figure class="image"><img src="imageurl" param1="value1" param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><figcaption><p>Image <strong>Caption</strong></p></figcaption></figure><figure class="image"><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><figcaption><p>Caption</p><ul><li>With</li><li>nested</li><li>list <span class="wikiexternallink"><a href="https://xwiki.org">and link</a></span> and [[fake link]].</li></ul></figcaption></figure>
<p><img src="imageurl" param2="value2" id="ISpace.ExistingPage40my.png3Fparam13Dvalue1" class="wikigeneratedid" alt="Space.ExistingPage@my.png?param1=value1"/></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png?param1=value1--><img src="imageurl" param2="value2" id="ISpace.ExistingPage40my.png3Fparam13Dvalue1" class="wikigeneratedid" alt="Space.ExistingPage@my.png?param1=value1"/><!--stopimage--></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png?param1=value1--><img src="imageurl" param2="value2" id="ISpace.ExistingPage40my.png3Fparam13Dvalue1" class="wikigeneratedid" alt="Space.ExistingPage@my.png?param1=value1"/><!--stopimage--></p>
<p><img src="imageurl?param1=value1" param2="value2" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png|-|queryString="param1=value1"--><img src="imageurl?param1=value1" param2="value2" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png|-|queryString="param1=value1"--><img src="imageurl?param1=value1" param2="value2" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<p><span class="wikiexternallink"><a param="value" href="http://xwiki.org"><img src="imageurl" height="100" width="100" id="Iattach.png" class="wikigeneratedid" alt="attach.png"/></a></span></p>
<p><!--startwikilink:false|-|url|-|http://xwiki.org--><span class="wikiexternallink"><a param="value" href="http://xwiki.org"><!--startimage:false|-|attach|-|attach.png--><img src="imageurl" height="100" width="100" id="Iattach.png" class="wikigeneratedid" alt="attach.png"/><!--stopimage--></a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|url|-|http://xwiki.org--><span class="wikiexternallink"><a param="value" href="http://xwiki.org"><!--startimage:false|-|attach|-|attach.png--><img src="imageurl" height="100" width="100" id="Iattach.png" class="wikigeneratedid" alt="attach.png"/><!--stopimage--></a></span><!--stopwikilink--></p>
<p><span class="wikiattachmentlink"><a param1="value1" param2="value2" href="attachmenturl"><span class="wikigeneratedlinkcontent">my.png</span></a></span><br/><span class="wikiattachmentlink"><a href="attachmenturl"><span class="wikigeneratedlinkcontent">Space.ExistingPage@my.png</span></a></span></p>
<p><!--startwikilink:true|-|attach|-|my.png--><span class="wikiattachmentlink"><a param1="value1" param2="value2" href="attachmenturl"><span class="wikigeneratedlinkcontent">my.png</span></a></span><!--stopwikilink--><br/><!--startwikilink:true|-|attach|-|Space.ExistingPage@my.png--><span class="wikiattachmentlink"><a href="attachmenturl"><span class="wikigeneratedlinkcontent">Space.ExistingPage@my.png</span></a></span><!--stopwikilink--></p>
<p><!--startwikilink:true|-|attach|-|my.png--><span class="wikiattachmentlink"><a param1="value1" param2="value2" href="attachmenturl"><span class="wikigeneratedlinkcontent">my.png</span></a></span><!--stopwikilink--><br/><!--startwikilink:true|-|attach|-|Space.ExistingPage@my.png--><span class="wikiattachmentlink"><a href="attachmenturl"><span class="wikigeneratedlinkcontent">Space.ExistingPage@my.png</span></a></span><!--stopwikilink--></p>
<p><a href="http://reference" param1="value1">http://reference</a></p>
<p><span class="wikiexternallink"><a param1="value1" href="http://reference">http://reference</a></span></p>
<p><span class="wikilink"><a param2="value2" href="viewurl?param1=value1#anchor"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage|-|queryString="param1=value1" anchor="anchor"--><span class="wikilink"><a param2="value2" href="viewurl?param1=value1#anchor"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage|-|queryString="param1=value1" anchor="anchor"--><span class="wikilink"><a param2="value2" href="viewurl?param1=value1#anchor"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--></p>
<p><span class="wikilink"><a param1="value1" param2="value2" href="viewurl"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><br/><span class="wikicreatelink"><a param="value" href="editurl">label</a></span></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage--><span class="wikilink"><a param1="value1" param2="value2" href="viewurl"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--><br/><!--startwikilink:false|-|doc|-|Space.UnknownPage--><span class="wikicreatelink"><a param="value" href="editurl">label</a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage--><span class="wikilink"><a param1="value1" param2="value2" href="viewurl"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--><br/><!--startwikilink:false|-|doc|-|Space.UnknownPage--><span class="wikicreatelink"><a param="value" href="editurl">label</a></span><!--stopwikilink--></p>
        return new XHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, getListenerChain());
        return new AnnotatedHTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, getListenerChain());
        return new AnnotatedXHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, getListenerChain());
        return new HTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, getListenerChain());
            XHTMLImageRenderer imageRenderer, ListenerChain listenerChain)
        super(linkRenderer, imageRenderer, listenerChain);
        chain.addListener(new AnnotatedHTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, chain));
<p><span a="b" class="monospace">monospace</span></p>
        XHTMLImageRenderer imageRenderer, ListenerChain listenerChain)
        super(linkRenderer, imageRenderer, listenerChain);
        chain.addListener(new AnnotatedXHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, chain));
            XHTMLImageRenderer imageRenderer,
        super(linkRenderer, imageRenderer, listenerChain);
            Map<String, String> attributes = new HashMap<>();
            attributes.putAll(parameters);
        chain.addListener(new HTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, chain));
import org.xwiki.rendering.internal.renderer.html5.HTML5ChainingRenderer;
        chainingRenderer = new HTML5ChainingRenderer(linkRenderer, imageRenderer, listenerChain);
                    parameter.getKey().equals(CLASS_ATTRIBUTE) && parameter.getValue().equals(METADATA_CONTAINER_CLASS)
     * @param linkRenderer the object to render link events into XHTML. This is done so that it's pluggable because link
     *            rendering depends on how the underlying system wants to handle it. For example for XWiki we check if
     *            the document exists, we get the document URL, etc.
     * @param imageRenderer the object to render image events into XHTML. This is done so that it's pluggable because
     *            image rendering depends on how the underlying system wants to handle it. For example for XWiki we
     *            check if the image exists as a document attachments, we get its URL, etc.
        ListenerChain listenerChain)
            this.xhtmlWikiPrinter = new XHTMLWikiPrinter(getPrinter());
        chain.addListener(new XHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, chain));
        XHTMLChainingRenderer renderer = new XHTMLChainingRenderer(null, null, new ListenerChain());
        XHTMLChainingRenderer renderer = new XHTMLChainingRenderer(null, null, new ListenerChain());
<p><tt><span a="b">monospace</span></tt></p>
<p><strong a="b" data-xwiki-parameter="value">something</strong></p>
<p><strong><span a="b" data-xwiki-parameter="value">something</span></strong></p>
<p>&nbsp;&tilde;&euro;</p><p param1="val&#252;e">&#160;&#732;&#8364;</p>
<p>&nbsp;˜€</p><p param1="valüe">&nbsp;˜€</p>
<div a="b" data-xwiki-parameter="value">something</div>
<div a="b" data-xwiki-parameter="value"><p>something</p></div>
<p><span a="b" class="monospace">monospace</span></p>
     * Use it to specify that the current element to print is standalone.
     * This value might be used to know if the first space should be printed with a simple space or a {@code &nbsp;}
     * entity. Note that the standalone value is automatically reset after first printing of a space, or when a text
     * is printed.
        handleSpaceWhenStartElement();
        super.printXMLElement(name);
        handleSpaceWhenStartElement();
        super.printXMLElement(name, attributes);
        handleSpaceWhenStartElement();
        super.printXMLElement(name, attributes);
        handleSpaceWhenStartElement();
        super.printXMLStartElement(name);
        handleSpaceWhenStartElement();
        super.printXMLStartElement(name, attributes);
        handleSpaceWhenStartElement();
        super.printXMLStartElement(name, attributes);
        handleSpaceWhenStartElement();
        super.printXMLStartElement(name, attributes);
        handleSpaceWhenEndlement();
        super.printXMLEndElement(name);
        this.elementEnded = true;

Fixed code:
<p><span class="wikiexternallink"><a data-xwiki-translated-attribute-param="value" href="refe||rence">lab&gt;&gt;el~</a></span></p>
<p><!--startwikilink:false|-|url|-|refe||rence--><span class="wikiexternallink"><a data-xwiki-translated-attribute-param="value" href="refe||rence">lab&gt;&gt;el~</a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|url|-|refe||rence--><span class="wikiexternallink"><a data-xwiki-translated-attribute-param="value" href="refe||rence">lab&gt;&gt;el~</a></span><!--stopwikilink--></p>
<div data-xwiki-translated-attribute-param="value"><p>some text</p></div><div data-xwiki-translated-attribute-param="value"><p>some text</p></div>
<div data-xwiki-translated-attribute-param="value"><p>some text</p></div><div data-xwiki-translated-attribute-param="value"><p>some text</p></div>
<hr data-xwiki-translated-attribute-param="value"/>
<html><hr data-xwiki-translated-attribute-param="value"/></html>
<p><em><span data-xwiki-translated-attribute-a="b">something</span></em></p>
<dl data-xwiki-translated-attribute-param="value"><dt>title</dt><dd>definition</dd></dl>
<dl data-xwiki-translated-attribute-param="value"><dt>title</dt><dd>definition</dd></dl>
<ul data-xwiki-translated-attribute-param="value"><li>item 1<ul data-xwiki-translated-attribute-param2="value2"><li>item 1.1<ul><li>item 1.1.1</li></ul></li></ul></li></ul>
<ul data-xwiki-translated-attribute-param="value"><li>item 1<ul data-xwiki-translated-attribute-param2="value2"><li>item 1.1<ul><li>item 1.1.1</li></ul></li></ul></li></ul>
<ul data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2"><li>item</li></ul>
<html><ul data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2"><li>item</li></ul></html>
<span data-xwiki-translated-attribute-param="value">formatmacro2</span><p><span data-xwiki-translated-attribute-a="b"><span data-xwiki-translated-attribute-param="value">formatmacro3</span> text</span></p><p><span data-xwiki-translated-attribute-a="b">text</span><span data-xwiki-translated-attribute-param="value">formatmacro4</span></p>
<!--startmacro:testformatmacro|-|--><span data-xwiki-translated-attribute-param="value">formatmacro2</span><!--stopmacro--><p><span data-xwiki-translated-attribute-a="b"><!--startmacro:testformatmacro|-|--><span data-xwiki-translated-attribute-param="value">formatmacro3</span><!--stopmacro--> text</span></p><p><span data-xwiki-translated-attribute-a="b">text</span><!--startmacro:testformatmacro|-|--><span data-xwiki-translated-attribute-param="value">formatmacro4</span><!--stopmacro--></p>
<!--startmacro:testformatmacro|-|--><span data-xwiki-translated-attribute-param="value">formatmacro2</span><!--stopmacro--><p><span data-xwiki-translated-attribute-a="b"><!--startmacro:testformatmacro|-|--><span data-xwiki-translated-attribute-param="value">formatmacro3</span><!--stopmacro--> text</span></p><p><span data-xwiki-translated-attribute-a="b">text</span><!--startmacro:testformatmacro|-|--><span data-xwiki-translated-attribute-param="value">formatmacro4</span><!--stopmacro--></p>
<p data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2"><span data-xwiki-translated-attribute-param3="value3">hello</span> world</p><p><span data-xwiki-translated-attribute-param4="valueA">hello&nbsp;</span><span data-xwiki-translated-attribute-param4="valueB">world</span></p><p><strong><span data-xwiki-translated-attribute-param5="value4">hello</span></strong><span data-xwiki-translated-attribute-param5="value4">&nbsp;world</span></p><p><span data-xwiki-translated-attribute-param6="valueA">hello&nbsp;</span><span data-xwiki-translated-attribute-param7="valueB">world</span></p><p>before<span data-xwiki-translated-attribute-param="value">x</span><strong>y</strong>after</p><p>12<strong>34<span data-xwiki-translated-attribute-param="value">56</span>78</strong>90</p><p><span data-xwiki-translated-attribute-param="value">1<em>2</em></span><em>3</em></p><p><strong><span data-xwiki-translated-attribute-param1="value1">1</span></strong><strong><span data-xwiki-translated-attribute-param2="value2">2</span></strong></p>
<blockquote data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2"><p>line</p></blockquote>
<blockquote data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2"><p>line</p></blockquote>
<h1 id="Hheader" class="wikigeneratedid" data-xwiki-translated-attribute-param="value"><span>header</span></h1>
<html><h1 id="Hheader" class="wikigeneratedid" data-xwiki-translated-attribute-param="value"><span>header</span></h1></html>
<p><del><span data-xwiki-translated-attribute-a="b">something</span></del></p>
<html><p><sub data-xwiki-translated-attribute-a="b">something</sub></p></html>
<p><sub><span data-xwiki-translated-attribute-a="b">something</span></sub></p>
<html><p><sup data-xwiki-translated-attribute-a="b">something</sup></p></html>
<p><sup><span data-xwiki-translated-attribute-a="b">something</span></sup></p>
<table data-xwiki-translated-attribute-a="b"><tr data-xwiki-translated-attribute-c="d"><th data-xwiki-translated-attribute-e="f" scope="col"><span data-xwiki-translated-attribute-g="h">cell1</span></th><td data-xwiki-translated-attribute-i="j"><span k="l">cell2</span></td></tr></table><table data-xwiki-translated-attribute-a="b"><tr data-xwiki-translated-attribute-c="d"><th data-xwiki-translated-attribute-e="f" scope="col"><span data-xwiki-translated-attribute-g="h">cell1</span></th><td data-xwiki-translated-attribute-i="j"><span k="l">cell2</span></td></tr></table>
<table data-xwiki-translated-attribute-a="b"><tr data-xwiki-translated-attribute-c="d"><th data-xwiki-translated-attribute-e="f" scope="col"><span data-xwiki-translated-attribute-g="h">cell1</span></th><td data-xwiki-translated-attribute-i="j"><span k="l">cell2</span></td></tr></table><table data-xwiki-translated-attribute-a="b"><tr data-xwiki-translated-attribute-c="d"><th data-xwiki-translated-attribute-e="f" scope="col"><span data-xwiki-translated-attribute-g="h">cell1</span></th><td data-xwiki-translated-attribute-i="j"><span k="l">cell2</span></td></tr></table>
<pre data-xwiki-translated-attribute-param="value">abc</pre>
<html><pre data-xwiki-translated-attribute-param="value">abc</pre></html>
<p data-xwiki-translated-attribute-param="value"><span data-xwiki-translated-attribute-param2="value2"><tt class="wikimodel-verbatim">abc</tt></span></p>
<p data-xwiki-translated-attribute-param="value"><span data-xwiki-translated-attribute-param2="value2"><tt class="wikimodel-verbatim">abc</tt></span></p>
<p><img src="imageurl" data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><br/><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/></p>
<p><!--startimage:false|-|attach|-|my.png--><img src="imageurl" data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><!--stopimage--><br/><!--startimage:false|-|attach|-|Space.ExistingPage@my.png--><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<p><!--startimage:false|-|attach|-|my.png--><img src="imageurl" data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><!--stopimage--><br/><!--startimage:false|-|attach|-|Space.ExistingPage@my.png--><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<img src="imageurl" data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><div class="figcaption"><p>Image <strong>Caption</strong></p></div><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><div class="figcaption"><p>Caption</p><ul><li>With</li><li>nested</li><li>list <span class="wikiexternallink"><a href="https://xwiki.org">and link</a></span> and [[fake link]].</li></ul></div>
<figure class="image"><img src="imageurl" data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" id="Imy.png" class="wikigeneratedid" alt="my.png"/><figcaption><p>Image <strong>Caption</strong></p></figcaption></figure><figure class="image"><img src="imageurl" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><figcaption><p>Caption</p><ul><li>With</li><li>nested</li><li>list <span class="wikiexternallink"><a href="https://xwiki.org">and link</a></span> and [[fake link]].</li></ul></figcaption></figure>
<p><img src="imageurl" data-xwiki-translated-attribute-param2="value2" id="ISpace.ExistingPage40my.png3Fparam13Dvalue1" class="wikigeneratedid" alt="Space.ExistingPage@my.png?param1=value1"/></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png?param1=value1--><img src="imageurl" data-xwiki-translated-attribute-param2="value2" id="ISpace.ExistingPage40my.png3Fparam13Dvalue1" class="wikigeneratedid" alt="Space.ExistingPage@my.png?param1=value1"/><!--stopimage--></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png?param1=value1--><img src="imageurl" data-xwiki-translated-attribute-param2="value2" id="ISpace.ExistingPage40my.png3Fparam13Dvalue1" class="wikigeneratedid" alt="Space.ExistingPage@my.png?param1=value1"/><!--stopimage--></p>
<p><img src="imageurl?param1=value1" data-xwiki-translated-attribute-param2="value2" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png|-|queryString="param1=value1"--><img src="imageurl?param1=value1" data-xwiki-translated-attribute-param2="value2" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<p><!--startimage:false|-|attach|-|Space.ExistingPage@my.png|-|queryString="param1=value1"--><img src="imageurl?param1=value1" data-xwiki-translated-attribute-param2="value2" id="ISpace.ExistingPage40my.png" class="wikigeneratedid" alt="Space.ExistingPage@my.png"/><!--stopimage--></p>
<p><span class="wikiexternallink"><a data-xwiki-translated-attribute-param="value" href="http://xwiki.org"><img src="imageurl" height="100" width="100" id="Iattach.png" class="wikigeneratedid" alt="attach.png"/></a></span></p>
<p><!--startwikilink:false|-|url|-|http://xwiki.org--><span class="wikiexternallink"><a data-xwiki-translated-attribute-param="value" href="http://xwiki.org"><!--startimage:false|-|attach|-|attach.png--><img src="imageurl" height="100" width="100" id="Iattach.png" class="wikigeneratedid" alt="attach.png"/><!--stopimage--></a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|url|-|http://xwiki.org--><span class="wikiexternallink"><a data-xwiki-translated-attribute-param="value" href="http://xwiki.org"><!--startimage:false|-|attach|-|attach.png--><img src="imageurl" height="100" width="100" id="Iattach.png" class="wikigeneratedid" alt="attach.png"/><!--stopimage--></a></span><!--stopwikilink--></p>
<p><span class="wikiattachmentlink"><a data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" href="attachmenturl"><span class="wikigeneratedlinkcontent">my.png</span></a></span><br/><span class="wikiattachmentlink"><a href="attachmenturl"><span class="wikigeneratedlinkcontent">Space.ExistingPage@my.png</span></a></span></p>
<p><!--startwikilink:true|-|attach|-|my.png--><span class="wikiattachmentlink"><a data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" href="attachmenturl"><span class="wikigeneratedlinkcontent">my.png</span></a></span><!--stopwikilink--><br/><!--startwikilink:true|-|attach|-|Space.ExistingPage@my.png--><span class="wikiattachmentlink"><a href="attachmenturl"><span class="wikigeneratedlinkcontent">Space.ExistingPage@my.png</span></a></span><!--stopwikilink--></p>
<p><!--startwikilink:true|-|attach|-|my.png--><span class="wikiattachmentlink"><a data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" href="attachmenturl"><span class="wikigeneratedlinkcontent">my.png</span></a></span><!--stopwikilink--><br/><!--startwikilink:true|-|attach|-|Space.ExistingPage@my.png--><span class="wikiattachmentlink"><a href="attachmenturl"><span class="wikigeneratedlinkcontent">Space.ExistingPage@my.png</span></a></span><!--stopwikilink--></p>
<p><a href="http://reference" data-xwiki-translated-attribute-param1="value1">http://reference</a></p>
<p><span class="wikiexternallink"><a data-xwiki-translated-attribute-param1="value1" href="http://reference">http://reference</a></span></p>
<p><span class="wikilink"><a data-xwiki-translated-attribute-param2="value2" href="viewurl?param1=value1#anchor"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage|-|queryString="param1=value1" anchor="anchor"--><span class="wikilink"><a data-xwiki-translated-attribute-param2="value2" href="viewurl?param1=value1#anchor"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage|-|queryString="param1=value1" anchor="anchor"--><span class="wikilink"><a data-xwiki-translated-attribute-param2="value2" href="viewurl?param1=value1#anchor"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--></p>
<p><span class="wikilink"><a data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" href="viewurl"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><br/><span class="wikicreatelink"><a data-xwiki-translated-attribute-param="value" href="editurl">label</a></span></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage--><span class="wikilink"><a data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" href="viewurl"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--><br/><!--startwikilink:false|-|doc|-|Space.UnknownPage--><span class="wikicreatelink"><a data-xwiki-translated-attribute-param="value" href="editurl">label</a></span><!--stopwikilink--></p>
<p><!--startwikilink:false|-|doc|-|Space.ExistingPage--><span class="wikilink"><a data-xwiki-translated-attribute-param1="value1" data-xwiki-translated-attribute-param2="value2" href="viewurl"><span class="wikigeneratedlinkcontent">Space.ExistingPage</span></a></span><!--stopwikilink--><br/><!--startwikilink:false|-|doc|-|Space.UnknownPage--><span class="wikicreatelink"><a data-xwiki-translated-attribute-param="value" href="editurl">label</a></span><!--stopwikilink--></p>
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        return new XHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, this.htmlElementSanitizer,
            getListenerChain());
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        return new AnnotatedHTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, this.htmlElementSanitizer,
            getListenerChain());
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        return new AnnotatedXHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, this.htmlElementSanitizer,
            getListenerChain());
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        return new HTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, this.htmlElementSanitizer,
            getListenerChain());
import org.xwiki.xml.html.HTMLElementSanitizer;
     * @param htmlElementSanitizer the HTML element sanitizer to use
            XHTMLImageRenderer imageRenderer, HTMLElementSanitizer htmlElementSanitizer, ListenerChain listenerChain)
        super(linkRenderer, imageRenderer, htmlElementSanitizer, listenerChain);
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        chain.addListener(new AnnotatedHTML5ChainingRenderer(this.linkRenderer, this.imageRenderer,
            this.htmlElementSanitizer, chain));
<p><span data-xwiki-translated-attribute-a="b" class="monospace">monospace</span></p>
import org.xwiki.xml.html.HTMLElementSanitizer;
     * @param htmlElementSanitizer the sanitizer to use for sanitizing HTML elements and attributes
        XHTMLImageRenderer imageRenderer, HTMLElementSanitizer htmlElementSanitizer, ListenerChain listenerChain)
        super(linkRenderer, imageRenderer, htmlElementSanitizer, listenerChain);
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        chain.addListener(new AnnotatedXHTMLChainingRenderer(this.linkRenderer, this.imageRenderer,
            this.htmlElementSanitizer, chain));
import java.util.LinkedHashMap;
import org.xwiki.xml.html.HTMLElementSanitizer;
     * @param htmlElementSanitizer the sanitizer to use for sanitizing HTML elements and attributes
            XHTMLImageRenderer imageRenderer, HTMLElementSanitizer htmlElementSanitizer,
        super(linkRenderer, imageRenderer, htmlElementSanitizer, listenerChain);
            Map<String, String> attributes = new LinkedHashMap<>(parameters);
import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        chain.addListener(new HTML5ChainingRenderer(this.linkRenderer, this.imageRenderer, this.htmlElementSanitizer,
            chain));
import org.xwiki.rendering.internal.renderer.html5.HTML5ChainingRenderer;
        chainingRenderer = new HTML5ChainingRenderer(linkRenderer, imageRenderer, null, listenerChain);
import java.util.LinkedHashMap;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.xwiki.rendering.renderer.printer.XHTMLWikiPrinter;
                parameter.getKey().equals(CLASS_ATTRIBUTE) && parameter.getValue().equals(METADATA_CONTAINER_CLASS)

    @Override
    protected Map<String, String> convertParameters(WikiParameters params)
    {
        return maybeRemoveAttributePrefix(super.convertParameters(params));
    }

    @Override
    protected Pair<Map<String, String>, Map<String, String>> convertAndSeparateParameters(WikiParameters params)
    {
        Pair<Map<String, String>, Map<String, String>> result = super.convertAndSeparateParameters(params);
        Map<String, String> genericParameters = result.getRight();

        if (!genericParameters.isEmpty()) {
            genericParameters = maybeRemoveAttributePrefix(genericParameters);
        }

        return new ImmutablePair<>(result.getLeft(), genericParameters);
    }

    private Map<String, String> maybeRemoveAttributePrefix(Map<String, String> attributes)
    {
        Map<String, String> result;

        if (!attributes.isEmpty()) {
            result = new LinkedHashMap<>();

            for (Map.Entry<String, String> entry : attributes.entrySet()) {
                String newKey;
                if (entry.getKey().startsWith(XHTMLWikiPrinter.TRANSLATED_ATTRIBUTE_PREFIX)) {
                    newKey = entry.getKey().substring(XHTMLWikiPrinter.TRANSLATED_ATTRIBUTE_PREFIX.length());
                } else {
                    newKey = entry.getKey();
                }

                result.put(newKey, entry.getValue());
            }
        } else {
            result = attributes;
        }

        return result;
    }
import org.xwiki.xml.html.HTMLElementSanitizer;
    private final HTMLElementSanitizer htmlElementSanitizer;

     * @param linkRenderer the object to render link events into XHTML. This is done so that it's pluggable because
     *     link rendering depends on how the underlying system wants to handle it. For example for XWiki we check if the
     *     document exists, we get the document URL, etc.
     * @param imageRenderer the object to render image events into XHTML. This is done so that it's pluggable
     *     because image rendering depends on how the underlying system wants to handle it. For example for XWiki we
     *     check if the image exists as a document attachments, we get its URL, etc.
     * @param htmlElementSanitizer the sanitizer for XHTML elements
        HTMLElementSanitizer htmlElementSanitizer, ListenerChain listenerChain)
        this.htmlElementSanitizer = htmlElementSanitizer;
            this.xhtmlWikiPrinter = new XHTMLWikiPrinter(getPrinter(), getHtmlElementSanitizer());
    protected HTMLElementSanitizer getHtmlElementSanitizer()
    {
        return this.htmlElementSanitizer;
    }

import org.xwiki.xml.html.HTMLElementSanitizer;
    @Inject
    private HTMLElementSanitizer htmlElementSanitizer;

        chain.addListener(new XHTMLChainingRenderer(this.linkRenderer, this.imageRenderer, this.htmlElementSanitizer,
            chain));
        XHTMLChainingRenderer renderer = new XHTMLChainingRenderer(null, null, null, new ListenerChain());
        XHTMLChainingRenderer renderer = new XHTMLChainingRenderer(null, null, null, new ListenerChain());
<p><tt><span data-xwiki-translated-attribute-a="b">monospace</span></tt></p>
<p><strong data-xwiki-translated-attribute-a="b" data-xwiki-parameter="value">something</strong></p>
<p><strong><span data-xwiki-translated-attribute-a="b" data-xwiki-parameter="value">something</span></strong></p>
<p>&nbsp;&tilde;&euro;</p><p data-xwiki-translated-attribute-param1="val&#252;e">&#160;&#732;&#8364;</p>
<p>&nbsp;˜€</p><p data-xwiki-translated-attribute-param1="valüe">&nbsp;˜€</p>
<div data-xwiki-translated-attribute-a="b" data-xwiki-parameter="value">something</div>
<div data-xwiki-translated-attribute-a="b" data-xwiki-parameter="value"><p>something</p></div>
<p><span data-xwiki-translated-attribute-a="b" class="monospace">monospace</span></p>
import java.util.Arrays;
import java.util.LinkedHashMap;
import org.xml.sax.helpers.AttributesImpl;
import org.xwiki.stability.Unstable;
import org.xwiki.xml.html.HTMLElementSanitizer;
    /**
     * Prefix that is used for invalid/disallowed attributes.
     *
     * @since 14.6RC1
     */
    @Unstable
    public static final String TRANSLATED_ATTRIBUTE_PREFIX = "data-xwiki-translated-attribute-";

    /**
     * The sanitizer used to restrict allowed elements and attributes, can be null (no restrictions).
     *
     * @since 14.6RC1
     */
    protected final HTMLElementSanitizer htmlElementSanitizer;

    {
        this(printer, null);
    }

    /**
     * @param printer the object to which to write the XHTML output to
     * @param htmlElementSanitizer the sanitizer to use for sanitizing elements and attributes
     */
    public XHTMLWikiPrinter(WikiPrinter printer, HTMLElementSanitizer htmlElementSanitizer)
        this.htmlElementSanitizer = htmlElementSanitizer;
     * Use it to specify that the current element to print is standalone. This value might be used to know if the first
     * space should be printed with a simple space or a {@code &nbsp;} entity. Note that the standalone value is
     * automatically reset after first printing of a space, or when a text is printed.
     *
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLElement(name);
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLElement(name, cleanAttributes(name, attributes));
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLElement(name, cleanAttributes(name, attributes));
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLStartElement(name);
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLStartElement(name, cleanAttributes(name, attributes));
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLStartElement(name, cleanAttributes(name, attributes));
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenStartElement();
            super.printXMLStartElement(name, cleanAttributes(name, attributes));
        }
        if (this.htmlElementSanitizer == null || this.htmlElementSanitizer.isElementAllowed(name)) {
            handleSpaceWhenEndlement();
            super.printXMLEndElement(name);
            this.elementEnded = true;
        }
    private Map<String, String> cleanAttributes(String elementName, Map<String, String> attributes)
    {
        Map<String, String> cleanAttributes;

        if (this.htmlElementSanitizer == null || attributes == null) {
            cleanAttributes = attributes;
        } else {
            cleanAttributes = new LinkedHashMap<>();
            for (Map.Entry<String, String> e : attributes.entrySet()) {
                if (this.htmlElementSanitizer.isAttributeAllowed(elementName, e.getKey(), e.getValue())) {
                    cleanAttributes.put(e.getKey(), e.getValue());
                } else {
                    cleanAttributes.put(TRANSLATED_ATTRIBUTE_PREFIX + e.getKey(), e.getValue());
                }
            }
        }

        return cleanAttributes;
    }

    private String[][] cleanAttributes(String elementName, String[][] attributes)
    {
        String[][] allowedAttributes;
        if (this.htmlElementSanitizer == null || attributes == null) {
            allowedAttributes = attributes;
        } else {
            allowedAttributes = Arrays.stream(attributes)
                .map(entry -> {
                    if (this.htmlElementSanitizer.isAttributeAllowed(elementName, entry[0], entry[1])) {
                        return entry;
                    } else {
                        return new String[] { TRANSLATED_ATTRIBUTE_PREFIX + entry[0], entry[1] };
                    }
                })
                .toArray(String[][]::new);
        }

        return allowedAttributes;
    }

    private Attributes cleanAttributes(String elementName, Attributes attributes)
    {
        Attributes allowedAttribute;

        if (this.htmlElementSanitizer == null || attributes == null) {
            allowedAttribute = attributes;
        } else {
            allowedAttribute = new AttributesImpl();

            for (int i = 0; i < attributes.getLength(); ++i) {
                if (this.htmlElementSanitizer.isAttributeAllowed(elementName, attributes.getQName(i),
                    attributes.getValue(i)))
                {
                    ((AttributesImpl) allowedAttribute).addAttribute(null, null, attributes.getQName(i),
                        null, attributes.getValue(i));
                } else {
                    ((AttributesImpl) allowedAttribute).addAttribute(null, null,
                        TRANSLATED_ATTRIBUTE_PREFIX + attributes.getQName(i), null, attributes.getValue(i));
                }
            }
        }

        return allowedAttribute;
    }