From 9b643a43b5cd79f0d8a2e0015e9d2c0d0fec8578 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Sun, 29 Dec 2024 09:33:17 +0200 Subject: [PATCH] Strip unnecessary whitespace from Stanza elements --- CHANGELOG.md | 5 +++++ src/stanza.js | 4 ++-- src/types/index.d.ts | 1 + src/types/utils.d.ts | 5 +++++ src/utils.js | 25 +++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf88fc..6864a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Strophe.js Change Log +## Version 3.1.1 - (Unreleased) + +- Strip unnecessary whitespace from `Stanza` elements +- Bugfix. Handle `null` and `undefined` values inside `stx` tagged template literals. + ## Version 3.1.0 - (2024-12-16) - **Security Fix**: Escape values passed to the `stx` tagged template literal diff --git a/src/stanza.js b/src/stanza.js index e7f1bb8..64daf63 100644 --- a/src/stanza.js +++ b/src/stanza.js @@ -1,6 +1,6 @@ import Builder from './builder.js'; import log from './log.js'; -import { getFirstElementChild, getParserError, xmlHtmlNode, xmlescape } from './utils.js'; +import { getFirstElementChild, getParserError, stripWhitespace, xmlHtmlNode, xmlescape } from './utils.js'; /** * A Stanza represents a XML element used in XMPP (commonly referred to as stanzas). @@ -61,7 +61,7 @@ export class Stanza extends Builder { throw new Error(`Parser Error: ${parserError}`); } - const node = getFirstElementChild(doc); + const node = stripWhitespace(getFirstElementChild(doc)); if ( ['message', 'iq', 'presence'].includes(node.nodeName.toLowerCase()) && node.namespaceURI !== 'jabber:client' && diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 655def8..a48f75a 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -161,6 +161,7 @@ export const Strophe: { }): void; xmlGenerator(): Document; xmlTextNode(text: string): Text; + stripWhitespace(stanza: Element): Element; xmlHtmlNode(text: string): XMLDocument; getParserError(doc: XMLDocument): string | null; getFirstElementChild(el: XMLDocument): Element; diff --git a/src/types/utils.d.ts b/src/types/utils.d.ts index 4d4f2a4..92d3c38 100644 --- a/src/types/utils.d.ts +++ b/src/types/utils.d.ts @@ -57,6 +57,11 @@ export function xmlGenerator(): Document; * @return {Text} - A new XML DOM text node. */ export function xmlTextNode(text: string): Text; +/** + * @param {Element} stanza + * @return {Element} + */ +export function stripWhitespace(stanza: Element): Element; /** * Creates an XML DOM node. * @param {string} text - The contents of the XML element. diff --git a/src/utils.js b/src/utils.js index 9a2165a..ee7454f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -184,6 +184,31 @@ export function xmlTextNode(text) { return xmlGenerator().createTextNode(text); } +/** + * @param {Element} stanza + * @return {Element} + */ +export function stripWhitespace(stanza) { + const childNodes = Array.from(stanza.childNodes); + if (childNodes.length === 1 && childNodes[0].nodeType === ElementType.TEXT) { + // If the element has only one child and it's a text node, we assume + // it's significant and don't remove it, even if it's only whitespace. + return stanza; + } + childNodes.forEach((node) => { + if (node.nodeName.toLowerCase() === 'body') { + // We don't remove anything inside elements + return; + } + if (node.nodeType === ElementType.TEXT && !(/\S/).test(node.nodeValue)) { + stanza.removeChild(node); + } else if (node.nodeType === ElementType.NORMAL) { + stripWhitespace(/** @type {Element} */ (node)); + } + }); + return stanza; +} + /** * Creates an XML DOM node. * @param {string} text - The contents of the XML element.