chromium/chrome/browser/resources/gaia_auth_host/safe_xml_utils.js

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/** @const */
const BEGIN_CERTIFICATE = '-----BEGIN CERTIFICATE-----';

/** @const */
const END_CERTIFICATE = '-----END CERTIFICATE-----';

let xmlPolicy = null;

/**
 * With Trusted Types Content Security Policy enabled, we cannot parseFromString
 * on untrusted content types. This class encapsulates and abstracts parsing
 * of XML strings to reduce the risk of introducing unsafe sink assignments in
 * other contexts.
 */
export class SafeXMLUtils {
  constructor(xmlString) {
    if (xmlPolicy === null) {
      // This policy is only meant to be used for extracting value out of XML.
      // Policy not to be used from outside of this file.
      xmlPolicy = window.trustedTypes.createPolicy('xml-policy', {
        createHTML: (s) => (s),
      });
    }
    const xml = xmlPolicy.createHTML(xmlString);
    const parser = new DOMParser();
    this.xmlDoc = parser.parseFromString(xml, 'text/xml');
  }

  /**
   * Return x509certificate in pem-format which is extracted from our xml
   * and will be used to record SAML provider.
   * @return {string|null} The X509Certificate if one exists, null otherwise.
   */
  getX509Certificate() {
    let certificate = this.xmlDoc.getElementsByTagName('ds:X509Certificate');
    if (!certificate || certificate.length === 0) {
      // tag 'ds:X509Certificate' doesn't exist
      certificate = this.xmlDoc.getElementsByTagName('X509Certificate');
    }

    if (certificate && certificate.length > 0 && certificate[0].childNodes &&
        certificate[0].childNodes[0] &&
        certificate[0].childNodes[0].nodeValue) {
      certificate = certificate[0].childNodes[0].nodeValue;
      return BEGIN_CERTIFICATE + '\n' + certificate.trim() + '\n' +
          END_CERTIFICATE + '\n';
    }

    return null;
  }

  /**
   * Extracts a string from the given XML DOM, using the given query selector.
   * @param {string} querySelectorStr The query selector to find the string.
   * @return {string} The extracted string (empty if failed to extract).
   */
  extractStringFromXml(querySelectorStr) {
    const element = this.xmlDoc.querySelector(querySelectorStr);
    return (element && element.textContent) ? element.textContent : '';
  }
}