Skip to content

Commit

Permalink
Add a Stanza.unsafeXML directive
Browse files Browse the repository at this point in the history
  • Loading branch information
jcbrand committed Dec 16, 2024
1 parent fc821da commit 172fea3
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 3 deletions.
18 changes: 16 additions & 2 deletions src/builder.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ElementType, NS } from './constants.js';
import { copyElement, createHtml, xmlElement, xmlGenerator, xmlTextNode, xmlescape } from './utils.js';
import { copyElement, createHtml, toElement, xmlElement, xmlGenerator, xmlTextNode, xmlescape } from './utils.js';

/**
* Create a {@link Strophe.Builder}
Expand Down Expand Up @@ -103,6 +103,19 @@ class Builder {
this.#attrs = attrs;
}

/**
* Creates a new Builder object from an XML string.
* @param {string} str
* @returns {Builder}
* @example const stanza = Builder.fromString('<presence from="juliet@example.com/chamber"></presence>');
*/
static fromString(str) {
const el = toElement(str);
const b = new Builder('');
b.#nodeTree = el;
return b;
}

buildTree() {
return xmlElement(this.#name, this.#attrs);
}
Expand Down Expand Up @@ -288,7 +301,8 @@ class Builder {
const xmlGen = xmlGenerator();
try {
impNode = xmlGen.importNode !== undefined;
} catch (e) { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
} catch (e) {
impNode = false;
}

Expand Down
24 changes: 23 additions & 1 deletion src/stanza.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Builder from './builder.js';
import log from './log.js';
import { getFirstElementChild, getParserError, xmlHtmlNode, xmlescape } from './utils.js';


/**
* A Stanza represents a XML element used in XMPP (commonly referred to as stanzas).
*/
Expand All @@ -28,6 +27,29 @@ export class Stanza extends Builder {
}

/**
* A directive which can be used to pass a string of XML as a value to the
* stx tagged template literal.
*
* It's considered "unsafe" because it can pose a security risk if used with
* untrusted input.
*
* @param {string} string
* @returns {Builder}
* @example
* const status = '<status>I am busy!</status>';
* const pres = stx`
* <presence from='juliet@example.com/chamber' id='pres1'>
* <show>dnd</show>
* ${unsafeXML(status)}
* </presence>`;
* connection.send(pres);
*/
static unsafeXML(string) {
return Builder.fromString(string);
}

/**
* Turns the passed-in string into an XML Element.
* @param {string} string
* @param {boolean} [throwErrorIfInvalidNS]
* @returns {Element}
Expand Down
7 changes: 7 additions & 0 deletions src/types/builder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ export default Builder;
* // </iq>
*/
declare class Builder {
/**
* Creates a new Builder object from an XML string.
* @param {string} str
* @returns {Builder}
* @example const stanza = Builder.fromString('<presence from='juliet@example.com/chamber'></presence>');
*/
static fromString(str: string): Builder;
/**
* Render a DOM element and all descendants to a String.
* @param {Element|Builder} elem - A DOM element.
Expand Down
1 change: 1 addition & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export const Strophe: {
warn(msg: string): void;
error(msg: string): void;
fatal(msg: string): void;
toElement(string: string, throwErrorIfInvalidNS?: boolean): Element;
handleError(e: Error): void;
utf16to8(str: string): string;
xorArrayBuffers(x: ArrayBufferLike, y: ArrayBufferLike): ArrayBuffer;
Expand Down
20 changes: 20 additions & 0 deletions src/types/stanza.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,26 @@ export function stx(strings: string[], ...values: any[]): Stanza;
*/
export class Stanza extends Builder {
/**
* A directive which can be used to pass a string of XML as a value to the
* stx tagged template literal.
*
* It's considered "unsafe" because it can pose a security risk if used with
* untrusted input.
*
* @param {string} string
* @returns {Builder}
* @example
* const status = '<status>I am busy!</status>';
* const pres = stx`
* <presence from='juliet@example.com/chamber' id='pres1'>
* <show>dnd</show>
* ${unsafeXML(status)
* </presence>`;
* connection.send(pres);
*/
static unsafeXML(string: string): Builder;
/**
* Turns the passed-in string into an XML Element.
* @param {string} string
* @param {boolean} [throwErrorIfInvalidNS]
* @returns {Element}
Expand Down
7 changes: 7 additions & 0 deletions src/types/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Takes a string and turns it into an XML Element.
* @param {string} string
* @param {boolean} [throwErrorIfInvalidNS]
* @returns {Element}
*/
export function toElement(string: string, throwErrorIfInvalidNS?: boolean): Element;
/**
* Properly logs an error to the console
* @param {Error} e
Expand Down
29 changes: 29 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,35 @@ import log from './log.js';
import * as shims from './shims.js';
import { ElementType, PARSE_ERROR_NS, XHTML } from './constants.js';

/**
* Takes a string and turns it into an XML Element.
* @param {string} string
* @param {boolean} [throwErrorIfInvalidNS]
* @returns {Element}
*/
export function toElement(string, throwErrorIfInvalidNS) {
const doc = xmlHtmlNode(string);
const parserError = getParserError(doc);
if (parserError) {
throw new Error(`Parser Error: ${parserError}`);
}

const node = getFirstElementChild(doc);
if (
['message', 'iq', 'presence'].includes(node.nodeName.toLowerCase()) &&
node.namespaceURI !== 'jabber:client' &&
node.namespaceURI !== 'jabber:server'
) {
const err_msg = `Invalid namespaceURI ${node.namespaceURI}`;
if (throwErrorIfInvalidNS) {
throw new Error(err_msg);
} else {
log.error(err_msg);
}
}
return node;
}

/**
* Properly logs an error to the console
* @param {Error} e
Expand Down

0 comments on commit 172fea3

Please sign in to comment.