Azure-sdk-for-js: Content Security Policy error in XML parsing in Chrome

Created on 18 Jan 2021  路  8Comments  路  Source: Azure/azure-sdk-for-js

Describe the bug
The Azure SDK for JavaScript includes a utility module for XML parsing. As part of initializing the module, it attempts to parse an invalid XML string at https://github.com/Azure/azure-sdk-for-js/blob/0f1c73f76331d23efb61c9f636ee2d2338b8e113/sdk/core/core-http/src/util/xml.browser.ts#L35
In most browsers, this returns a document containing a <parseerror> element describing the error. The error document contains inline styles which the browser interprets. In version 86.0 and newer of Chrome (https://bugs.chromium.org/p/chromium/issues/detail?id=1148221), the error document inherits the Content Security Policy from the owner document. Therefore, on pages with a Content Security Policy with a style-src directive not including unsafe-inline, Azure SDK for JavaScript causes a CSP error to be reported and shown in the console.

To Reproduce
Steps to reproduce the behavior:

  1. Set up a page with a Content Security Policy with a style-src directive not containing unsafe-inline, for example style-src: 'self'.
  2. Load the Azure SDK for JavaScript.
  3. Open page in Google Chrome version 86.0 or newer.
  4. Look for Content Security Policy error in console.

Expected behavior
No Content Security Policy error should be shown in the console.

Screenshots
Content Security Policy error shown in console

Additional context
I'm unsure if and when the issue in Chrome will be fixed. Until it is fixed, the XML parser module will necessarily cause a Content Security Policy error when parsing an invalid XML string on sites with the relevant policy. However, since XML parsing errors are likely not expected during normal usage of the library, it would still be beneficial to prevent the CSP error on load to save developer effort in debugging it.

Potential fixes could be to:

  1. Lazily testing for browser behavior when first parsing error is encountered, instead of at module load.
  2. Removing the check for browser behavior completely and always querying for the parsererror element, even in Internet Explorer.
Azure.Core Client bug customer-reported needs-team-attention

All 8 comments

@kimsey0 Thank you for the report! We will looking into this shortly.

note: I was able to repro by adding the following to my html

<meta http-equiv="Content-Security-Policy" content="style-src 'self'">

When our module is loaded, we try to parse an invalid xml document then save the namespace of the element in the parsed result to errorNS. In Chrome and Firefox DomParser.parseFromString() returns a document with the expected element. In IE it throws an DOMException of SyntaxError but we ignore the error thus didn't set errorNS.

After every time we parse an xml document, we check whether it contains errors by using the saved error namespace errorNS to search for elements with same namespace and name of parsererror.

I agree with @kimsey0 that we could remove the browser check since in IE, an error would have been thrown from parsing in the error cases, we wouldn't even get to check whether errorNS is set

we probably still to search using the same namespace, because there's a possibility that element is part of a valid xml document.

Unfortunately, Chrome and Firefox return different error documents. Firefox has the parsererror element as the root and in namespace http://www.mozilla.org/newlayout/xml/parsererror.xml, while Chrome has it wrapped inside html and body elements and with namespace http://www.w3.org/1999/xhtml.

I think every solution is going to be a workaround. One possibility could be to do a non-namespace-aware (getElementsByTagName) search for parserelement when parsing and then, if any are found, check each of their namespaces against a lazily computed (and potentially cached) error namespace.

Basically, something like this:

let errorNS = "";
function throwIfError(dom: Document): void {
  const parserErrors = dom.getElementsByTagName("parsererror");
  if (parserErrors.length) {
    if (!errorNS) {
      try {
        errorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0]
          .namespaceURI!;
      } catch (ignored) {
        // Most browsers will return a document containing <parsererror>, but IE will throw.
      }
    }

    for (let i = 0; i < parserErrors.length; i++) {
      if (parserErrors[i].namespaceURI === errorNS) {
        throw new Error(parserErrors[i].innerHTML);
      }
    }
  }
}

One possibility could be to do a non-namespace-aware (getElementsByTagName) search for parserelement when parsing and then, if any are found, check each of their namespaces against a lazily computed (and potentially cached) error namespace.

Yes, this is exactly the same as I planned to do. Also thanks for the code snippets @kimsey0!

This has been published in @azure/core-http v1.2.3

Thank you! We upgraded earlier today (but have some unrelated CORS errors, #13658).

Was this page helpful?
0 / 5 - 0 ratings