Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 2.0.8 VS 2.2.7 different output on the same input #532

Closed
albanx opened this issue Apr 23, 2021 · 16 comments
Closed

Version 2.0.8 VS 2.2.7 different output on the same input #532

albanx opened this issue Apr 23, 2021 · 16 comments

Comments

@albanx
Copy link

albanx commented Apr 23, 2021

BUG: version 2.0.8 VS 2.2.7 different behaviour on the same input when running from phantomJS tests.

Background & Context

Consider this HTML to sanitize:

<allowStyleAsFirstTag/><span class="btn" href="" data-cwdb="%7B%22confirmation%22%3A%22Some%20confirmation%20here%22%2C%22action%22%3A%7B%22html%22%3A%22%23%23%23%20HTML%20content%20%23%23%23%22%7D%2C%22display%22%3A%22widget%22%7D">Test button</span>

When running on casper / phantonJS the version 2.0.8 works fine and returns the full html stripping out just <allowStyleAsFirstTag/>

The last version of Dompurify 2.2.7 instead removes all HTML returning an empty string.

Bug

Remove HTML that shouldn't be removed on specific browser engines.

Input

<allowStyleAsFirstTag/><span class="btn" href="" data-cwdb="%7B%22confirmation%22%3A%22Some%20confirmation%20here%22%2C%22action%22%3A%7B%22html%22%3A%22%23%23%23%20HTML%20content%20%23%23%23%22%7D%2C%22display%22%3A%22widget%22%7D">Test button</span>

Given output

EMPTY

Expected output

<span class="btn" href="" data-cwdb="%7B%22confirmation%22%3A%22Some%20confirmation%20here%22%2C%22action%22%3A%7B%22html%22%3A%22%23%23%23%20HTML%20content%20%23%23%23%22%7D%2C%22display%22%3A%22widget%22%7D">Test button</span>

Digging into the code all comes to this lines of DOMPurify 2.2.7:

              if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
                    console.error('===========>>>KEEP_CONTENT', KEEP_CONTENT);
                    var parentNode = getParentNode(currentNode);
                    var childNodes = getChildNodes(currentNode);

                    if (childNodes && parentNode) {
                        var childCount = childNodes.length;

                        for (var i = childCount - 1; i >= 0; --i) {
                            parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
                        }
                    }
                }

Replacing the above with the old method in 2.0.8, works fine:

                /* Keep content except for black-listed elements */
                if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
                    try {
                        var htmlToInsert = currentNode.innerHTML;
                        currentNode.insertAdjacentHTML(
                            'AfterEnd',
                            trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert
                        );
                    } catch (error) {}
                }

Any thoughts?

Thanks
Alban

@albanx
Copy link
Author

albanx commented Apr 23, 2021

from some further investigation I believe that the bug consist in these lines:

var parentNode = getParentNode(currentNode);
var childNodes = getChildNodes(currentNode);

The lookupGetter method is failing for some reasons.

By replacing with

                    var parentNode = currentNode.parentNode;
                    var childNodes = currentNode.childNodes;

it works as expected

@albanx albanx changed the title Version 2.0.8 VS 2.2.7 different output on the same input when running from phantomJS tests Version 2.0.8 VS 2.2.7 different output on the same input Apr 23, 2021
@cure53
Copy link
Owner

cure53 commented Apr 23, 2021

Heya, this should address your problem afaics :)

#502

@albanx
Copy link
Author

albanx commented Apr 23, 2021

what is the reason to use a wrapper function getParentNode instead of going directly to the property parentNode?

@cure53
Copy link
Owner

cure53 commented Apr 23, 2021

I think possible DOM Clobbering was the reason here, cc @securitum-mb

@albanx
Copy link
Author

albanx commented Apr 23, 2021

I am trying to integrate the proposal here #502 as polyfill in the phantomJS, but seems not workinig

@cure53
Copy link
Owner

cure53 commented Apr 23, 2021

I don't think there is much we can (or want to) do here.

PhantomJS has been discontinued, there is known vulnerabilities as well - it makes no sense for us to change anything in the core to support PhantomJS.

@albanx
Copy link
Author

albanx commented Apr 23, 2021

Unfortunaately we have a lot of legacy tests, we need still to maintain in the transition process that will take a long time. In the mean time this is a local patch I am using to solve the issue:

var parentNode = getParentNode(currentNode) || currentNode.parentNode;
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;

IYO is this a valid patch without introducing xss attacks?

@cure53
Copy link
Owner

cure53 commented Apr 23, 2021

I think it might in the worst case enable DOM clobbering, but not sure about XSS.

@cure53
Copy link
Owner

cure53 commented Apr 26, 2021

Closing this for now, nothing - afaics - we can do here.

@cure53 cure53 closed this as completed Apr 26, 2021
@albanx
Copy link
Author

albanx commented Apr 26, 2021

I wouldn't close this issue simply because it breaks the backward compatibility. I think this patch is valid:

var parentNode = getParentNode(currentNode) || currentNode.parentNode;
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;

the code should fall in the second case only in not supporting browsers.

@cure53
Copy link
Owner

cure53 commented Apr 27, 2021

Yeah, I think this might be a valid patch indeed. Let me try and run some tests.

cure53 added a commit that referenced this issue Apr 27, 2021
Added element getter fallback for older browsers
@cure53
Copy link
Owner

cure53 commented Apr 27, 2021

@albanx Please check, does this do the trick for you?

@albanx
Copy link
Author

albanx commented Apr 27, 2021

Yes, looks good

@cure53
Copy link
Owner

cure53 commented Apr 27, 2021

Cool, thanks for being adamant!

@albanx
Copy link
Author

albanx commented Apr 29, 2021

Just tested the new version. It introduced another break for phantomJS, exactly this line:

doc = implementation.createDocument(NAMESPACE, 'template', null);

Seems that phantomJS does not support createDocument with parameters.

By replacing with the old line doc = implementation.createHTMLDocument(''); works fine.
We are in path of depracation and as temporary measure I can use this patch so I am not suggesting any more fix.

@cure53
Copy link
Owner

cure53 commented Apr 29, 2021

This extra parameter is needed by MSIE10 and MSIE11 - things will break without it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants