UNPKG

xml-crypto

Version:

Xml digital signature and encryption library for Node.js

230 lines 8.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.C14nCanonicalizationWithComments = exports.C14nCanonicalization = void 0; const utils = require("./utils"); const isDomNode = require("@xmldom/is-dom-node"); class C14nCanonicalization { constructor() { this.includeComments = false; this.includeComments = false; } attrCompare(a, b) { if (!a.namespaceURI && b.namespaceURI) { return -1; } if (!b.namespaceURI && a.namespaceURI) { return 1; } const left = a.namespaceURI + a.localName; const right = b.namespaceURI + b.localName; if (left === right) { return 0; } else if (left < right) { return -1; } else { return 1; } } nsCompare(a, b) { const attr1 = a.prefix; const attr2 = b.prefix; if (attr1 === attr2) { return 0; } return attr1.localeCompare(attr2); } renderAttrs(node) { let i; let attr; const attrListToRender = []; if (isDomNode.isCommentNode(node)) { return this.renderComment(node); } if (node.attributes) { for (i = 0; i < node.attributes.length; ++i) { attr = node.attributes[i]; //ignore namespace definition attributes if (attr.name.indexOf("xmlns") === 0) { continue; } attrListToRender.push(attr); } } attrListToRender.sort(this.attrCompare); const res = attrListToRender.map((attr) => { return ` ${attr.name}="${utils.encodeSpecialCharactersInAttribute(attr.value)}"`; }); return res.join(""); } /** * Create the string of all namespace declarations that should appear on this element * * @param node The node we now render * @param prefixesInScope The prefixes defined on this node parents which are a part of the output set * @param defaultNs The current default namespace * @param defaultNsForPrefix * @param ancestorNamespaces Import ancestor namespaces if it is specified * @api private */ renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) { let i; let attr; const res = []; let newDefaultNs = defaultNs; const nsListToRender = []; const currNs = node.namespaceURI || ""; //handle the namespace of the node itself if (node.prefix) { if (prefixesInScope.indexOf(node.prefix) === -1) { nsListToRender.push({ prefix: node.prefix, namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix], }); prefixesInScope.push(node.prefix); } } else if (defaultNs !== currNs) { //new default ns newDefaultNs = node.namespaceURI || ""; res.push(' xmlns="', newDefaultNs, '"'); } //handle the attributes namespace if (node.attributes) { for (i = 0; i < node.attributes.length; ++i) { attr = node.attributes[i]; //handle all prefixed attributes that are included in the prefix list and where //the prefix is not defined already. New prefixes can only be defined by `xmlns:`. if (attr.prefix === "xmlns" && prefixesInScope.indexOf(attr.localName) === -1) { nsListToRender.push({ prefix: attr.localName, namespaceURI: attr.value }); prefixesInScope.push(attr.localName); } //handle all prefixed attributes that are not xmlns definitions and where //the prefix is not defined already if (attr.prefix && prefixesInScope.indexOf(attr.prefix) === -1 && attr.prefix !== "xmlns" && attr.prefix !== "xml") { nsListToRender.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI }); prefixesInScope.push(attr.prefix); } } } if (utils.isArrayHasLength(ancestorNamespaces)) { // Remove namespaces which are already present in nsListToRender for (const ancestorNamespace of ancestorNamespaces) { let alreadyListed = false; for (const nsToRender of nsListToRender) { if (nsToRender.prefix === ancestorNamespace.prefix && nsToRender.namespaceURI === ancestorNamespace.namespaceURI) { alreadyListed = true; } } if (!alreadyListed) { nsListToRender.push(ancestorNamespace); } } } nsListToRender.sort(this.nsCompare); //render namespaces res.push(...nsListToRender.map((attr) => { if (attr.prefix) { return ` xmlns:${attr.prefix}="${attr.namespaceURI}"`; } return ` xmlns="${attr.namespaceURI}"`; })); return { rendered: res.join(""), newDefaultNs }; } /** * @param node Node */ processInner(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) { if (isDomNode.isCommentNode(node)) { return this.renderComment(node); } if (node.data) { return utils.encodeSpecialCharactersInText(node.data); } if (isDomNode.isElementNode(node)) { let i; let pfxCopy; const ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces); const res = ["<", node.tagName, ns.rendered, this.renderAttrs(node), ">"]; for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); res.push(this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, [])); } res.push("</", node.tagName, ">"); return res.join(""); } throw new Error(`Unable to canonicalize node type: ${node.nodeType}`); } // Thanks to deoxxa/xml-c14n for comment renderer renderComment(node) { if (!this.includeComments) { return ""; } const isOutsideDocument = node.ownerDocument === node.parentNode; let isBeforeDocument = false; let isAfterDocument = false; if (isOutsideDocument) { let nextNode = node; let previousNode = node; while (nextNode !== null) { if (nextNode === node.ownerDocument.documentElement) { isBeforeDocument = true; break; } nextNode = nextNode.nextSibling; } while (previousNode !== null) { if (previousNode === node.ownerDocument.documentElement) { isAfterDocument = true; break; } previousNode = previousNode.previousSibling; } } const afterDocument = isAfterDocument ? "\n" : ""; const beforeDocument = isBeforeDocument ? "\n" : ""; const encodedText = utils.encodeSpecialCharactersInText(node.data); return `${afterDocument}<!--${encodedText}-->${beforeDocument}`; } /** * Perform canonicalization of the given node * * @param node * @api public */ process(node, options) { options = options || {}; const defaultNs = options.defaultNs || ""; const defaultNsForPrefix = options.defaultNsForPrefix || {}; const ancestorNamespaces = options.ancestorNamespaces || []; const prefixesInScope = []; for (let i = 0; i < ancestorNamespaces.length; i++) { prefixesInScope.push(ancestorNamespaces[i].prefix); } const res = this.processInner(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces); return res; } getAlgorithmName() { return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; } } exports.C14nCanonicalization = C14nCanonicalization; /** * Add c14n#WithComments here (very simple subclass) */ class C14nCanonicalizationWithComments extends C14nCanonicalization { constructor() { super(); this.includeComments = true; } getAlgorithmName() { return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; } } exports.C14nCanonicalizationWithComments = C14nCanonicalizationWithComments; //# sourceMappingURL=c14n-canonicalization.js.map