// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** @suppress {duplicate} */ var remoting = remoting || {}; (function() { 'use strict'; /** * This class monitors incoming XMPP stanza and records the first * error encountered. It also strips all PII from the error stanza, * so that it can be uploaded to the cloud for error analysis. * * @constructor */ remoting.XmppErrorCache = function() { /** @private {string} */ this.firstErrorStanza_ = ''; }; /** * @return {string} The first XMPP error stanza that the monitor encountered. * Returns an empty string if no errors have been encountered so far. */ remoting.XmppErrorCache.prototype.getFirstErrorStanza = function() { return this.firstErrorStanza_; }; /** * Monitor the incoming stanza for error. * * @param {Element} iqNode */ remoting.XmppErrorCache.prototype.processStanza = function(iqNode) { if (this.firstErrorStanza_ != '') { return; } // The XML structure is as follows: // // // // if (iqNode.getAttribute('type') != 'error') { return; } var strippedStanza = this.stripPII_(iqNode); this.firstErrorStanza_ = strippedStanza; }; /** * @param {Element} iqNode * @return {string} Return a string representation of |iqNode| with all PII * removed. * @private */ remoting.XmppErrorCache.prototype.stripPII_ = function(iqNode) { var parser = new DOMParser(); var outDocument = parser.parseFromString('', 'text/xml'); stripPII(iqNode, outDocument.firstElementChild); return new XMLSerializer().serializeToString(outDocument.firstElementChild); }; /** * @param {Element} node * @param {Element} outNode */ function stripPII(node, outNode) { var attributesWhiteList = new Set([ 'action', 'code', 'codec', 'creator', 'height', 'id', 'name', 'sid', 'supported-methods', 'transport', 'type', 'version', 'width', 'xmlns', ]); var nodesWhiteList = new Set([ 'jingle', 'content', 'description', 'error', 'standard-ice', 'control', 'event', 'video', 'audio', 'initial-resolution', 'authentication', 'service-unavailable' ]); // Strip PII from attributes. var attributes = node.attributes; for (var i = 0; i < attributes.length; i++) { var attribute = /** @type {Attr} */ (attributes[i]); var value = 'REDACTED'; if (attributesWhiteList.has(attribute.nodeName)) { value = attribute.nodeValue; } outNode.setAttribute(attribute.nodeName, value); } // Copy the text content. if (node.childNodes.length == 1 && node.firstChild.nodeType == Node.TEXT_NODE) { var textValue = 'REDACTED'; if (nodesWhiteList.has(node.tagName)) { textValue = node.firstChild.nodeValue; } outNode.appendChild(outNode.ownerDocument.createTextNode(textValue)); return; } // Strip PII from child nodes. var children = node.children; for (i = 0; i < children.length; i++) { var child = /** @type{Element}*/ (children[i]); var childCopy = outNode.ownerDocument.createElement(child.tagName); outNode.appendChild(childCopy); stripPII(child, childCopy); } } })();