Skip to content

Commit c5d741f

Browse files
authored
Use is-dom-node for DOM node checking and narrowing (#388)
1 parent b0f00d0 commit c5d741f

17 files changed

+382
-500
lines changed

package-lock.json

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
},
4040
"dependencies": {
4141
"@xmldom/xmldom": "^0.8.10",
42-
"xpath": "0.0.33"
42+
"is-dom-node": "github:xmldom/is-dom-node#v1.0.0",
43+
"xpath": "^0.0.33"
4344
},
4445
"devDependencies": {
4546
"@cjbarth/github-release-notes": "^4.1.0",

src/c14n-canonicalization.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type {
55
RenderedNamespace,
66
} from "./types";
77
import * as utils from "./utils";
8-
import * as xpath from "xpath";
8+
import * as isDomNode from "is-dom-node";
99

1010
export class C14nCanonicalization implements CanonicalizationOrTransformationAlgorithm {
1111
includeComments = false;
@@ -44,7 +44,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg
4444
let attr;
4545
const attrListToRender: Attr[] = [];
4646

47-
if (xpath.isComment(node)) {
47+
if (isDomNode.isCommentNode(node)) {
4848
return this.renderComment(node);
4949
}
5050

@@ -171,14 +171,14 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg
171171
* @param node Node
172172
*/
173173
processInner(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) {
174-
if (xpath.isComment(node)) {
174+
if (isDomNode.isCommentNode(node)) {
175175
return this.renderComment(node);
176176
}
177177
if (node.data) {
178178
return utils.encodeSpecialCharactersInText(node.data);
179179
}
180180

181-
if (xpath.isElement(node)) {
181+
if (isDomNode.isElementNode(node)) {
182182
let i;
183183
let pfxCopy;
184184
const ns = this.renderNs(

src/enveloped-signature.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as xpath from "xpath";
2+
import * as isDomNode from "is-dom-node";
23

34
import type {
45
CanonicalizationOrTransformationAlgorithm,
@@ -14,7 +15,7 @@ export class EnvelopedSignature implements CanonicalizationOrTransformationAlgor
1415
"./*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']",
1516
node,
1617
);
17-
if (xpath.isNodeLike(signature) && signature.parentNode) {
18+
if (isDomNode.isNodeLike(signature) && signature.parentNode) {
1819
signature.parentNode.removeChild(signature);
1920
}
2021
return node;
@@ -24,7 +25,7 @@ export class EnvelopedSignature implements CanonicalizationOrTransformationAlgor
2425
".//*[local-name(.)='SignatureValue']/text()",
2526
signatureNode,
2627
);
27-
if (xpath.isTextNode(expectedSignatureValue)) {
28+
if (isDomNode.isTextNode(expectedSignatureValue)) {
2829
const expectedSignatureValueData = expectedSignatureValue.data;
2930

3031
const signatures = xpath.select(
@@ -36,7 +37,7 @@ export class EnvelopedSignature implements CanonicalizationOrTransformationAlgor
3637
".//*[local-name(.)='SignatureValue']/text()",
3738
nodeSignature,
3839
);
39-
if (xpath.isTextNode(signatureValue)) {
40+
if (isDomNode.isTextNode(signatureValue)) {
4041
const signatureValueData = signatureValue.data;
4142
if (expectedSignatureValueData === signatureValueData) {
4243
if (nodeSignature.parentNode) {

src/exclusive-canonicalization.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {
44
NamespacePrefix,
55
} from "./types";
66
import * as utils from "./utils";
7-
import * as xpath from "xpath";
7+
import * as isDomNode from "is-dom-node";
88

99
function isPrefixInScope(prefixesInScope, prefix, namespaceURI) {
1010
let ret = false;
@@ -55,7 +55,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati
5555
const res: string[] = [];
5656
const attrListToRender: Attr[] = [];
5757

58-
if (xpath.isComment(node)) {
58+
if (isDomNode.isCommentNode(node)) {
5959
return this.renderComment(node);
6060
}
6161

@@ -177,14 +177,14 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati
177177
defaultNsForPrefix,
178178
inclusiveNamespacesPrefixList: string[],
179179
) {
180-
if (xpath.isComment(node)) {
180+
if (isDomNode.isCommentNode(node)) {
181181
return this.renderComment(node);
182182
}
183183
if (node.data) {
184184
return utils.encodeSpecialCharactersInText(node.data);
185185
}
186186

187-
if (xpath.isElement(node)) {
187+
if (isDomNode.isElementNode(node)) {
188188
let i;
189189
let pfxCopy;
190190
const ns = this.renderNs(

src/signed-xml.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import * as envelopedSignatures from "./enveloped-signature";
2323
import * as hashAlgorithms from "./hash-algorithms";
2424
import * as signatureAlgorithms from "./signature-algorithms";
2525
import * as crypto from "crypto";
26+
import * as isDomNode from "is-dom-node";
2627

2728
export class SignedXml {
2829
idMode?: "wssecurity";
@@ -215,7 +216,7 @@ export class SignedXml {
215216
static getCertFromKeyInfo(keyInfo?: Node | null): string | null {
216217
if (keyInfo != null) {
217218
const certs = xpath.select1(".//*[local-name(.)='X509Certificate']", keyInfo);
218-
if (xpath.isNodeLike(certs)) {
219+
if (isDomNode.isNodeLike(certs)) {
219220
return utils.derToPem(certs.textContent || "", "CERTIFICATE");
220221
}
221222
}
@@ -398,7 +399,7 @@ export class SignedXml {
398399
}
399400

400401
// @ts-expect-error FIXME: xpath types are wrong
401-
if (!xpath.isNodeLike(targetElem)) {
402+
if (!isDomNode.isNodeLike(targetElem)) {
402403
continue;
403404
}
404405

@@ -446,7 +447,7 @@ export class SignedXml {
446447
}
447448

448449
// @ts-expect-error FIXME: xpath types are wrong
449-
if (!xpath.isNodeLike(elem)) {
450+
if (!isDomNode.isNodeLike(elem)) {
450451
const validationError = new Error(
451452
`invalid signature: the signature references an element with uri ${ref.uri} but could not find such element in the xml`,
452453
);
@@ -500,7 +501,7 @@ export class SignedXml {
500501
throw new Error("could not find CanonicalizationMethod/@Algorithm element");
501502
}
502503

503-
if (xpath.isAttribute(nodes[0])) {
504+
if (isDomNode.isAttributeNode(nodes[0])) {
504505
this.canonicalizationAlgorithm = nodes[0].value as CanonicalizationAlgorithmType;
505506
}
506507

@@ -509,7 +510,7 @@ export class SignedXml {
509510
signatureNode,
510511
);
511512

512-
if (xpath.isAttribute(signatureAlgorithm)) {
513+
if (isDomNode.isAttributeNode(signatureAlgorithm)) {
513514
this.signatureAlgorithm = signatureAlgorithm.value as SignatureAlgorithmType;
514515
}
515516

@@ -531,13 +532,13 @@ export class SignedXml {
531532
signatureNode,
532533
);
533534

534-
if (xpath.isTextNode(signatureValue)) {
535+
if (isDomNode.isTextNode(signatureValue)) {
535536
this.signatureValue = signatureValue.data.replace(/\r?\n/g, "");
536537
}
537538

538539
const keyInfo = xpath.select1(".//*[local-name(.)='KeyInfo']", signatureNode);
539540

540-
if (xpath.isNodeLike(keyInfo)) {
541+
if (isDomNode.isNodeLike(keyInfo)) {
541542
this.keyInfo = keyInfo;
542543
}
543544
}
@@ -621,7 +622,7 @@ export class SignedXml {
621622
this.addReference({
622623
transforms,
623624
digestAlgorithm: digestAlgo,
624-
uri: xpath.isElement(refNode) ? utils.findAttr(refNode, "URI")?.value : undefined,
625+
uri: isDomNode.isElementNode(refNode) ? utils.findAttr(refNode, "URI")?.value : undefined,
625626
digestValue,
626627
inclusiveNamespacesPrefixList,
627628
isEmptyUri: false,
@@ -796,7 +797,7 @@ export class SignedXml {
796797

797798
const referenceNode = xpath.select1(location.reference, doc);
798799

799-
if (!xpath.isNodeLike(referenceNode)) {
800+
if (!isDomNode.isNodeLike(referenceNode)) {
800801
const err2 = new Error(
801802
`the following xpath cannot be used because it was not found: ${location.reference}`,
802803
);
@@ -949,7 +950,7 @@ export class SignedXml {
949950
let transformedXml: Node | string = canonXml;
950951

951952
transforms.forEach((transformName) => {
952-
if (xpath.isNodeLike(transformedXml)) {
953+
if (isDomNode.isNodeLike(transformedXml)) {
953954
// If, after processing, `transformedNode` is a string, we can't do anymore transforms on it
954955
const transform = this.findCanonicalizationAlgorithm(transformName);
955956
transformedXml = transform.process(transformedXml, options);

src/utils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as xpath from "xpath";
22
import type { NamespacePrefix } from "./types";
3+
import * as isDomNode from "is-dom-node";
34

45
export function isArrayHasLength(array: unknown): array is unknown[] {
56
return Array.isArray(array) && array.length > 0;
@@ -36,7 +37,7 @@ export function findChildren(node: Node | Document, localName: string, namespace
3637
for (let i = 0; i < element.childNodes.length; i++) {
3738
const child = element.childNodes[i];
3839
if (
39-
xpath.isElement(child) &&
40+
isDomNode.isElementNode(child) &&
4041
child.localName === localName &&
4142
(child.namespaceURI === namespace || namespace == null)
4243
) {
@@ -192,7 +193,7 @@ function collectAncestorNamespaces(
192193
node: Element,
193194
nsArray: NamespacePrefix[] = [],
194195
): NamespacePrefix[] {
195-
if (!xpath.isElement(node.parentNode)) {
196+
if (!isDomNode.isElementNode(node.parentNode)) {
196197
return nsArray;
197198
}
198199

@@ -229,7 +230,7 @@ function findNSPrefix(subset) {
229230
}
230231

231232
function isElementSubset(docSubset: Node[]): docSubset is Element[] {
232-
return docSubset.every((node) => xpath.isElement(node));
233+
return docSubset.every((node) => isDomNode.isElementNode(node));
233234
}
234235

235236
/**

test/c14n-non-exclusive-unit-tests.spec.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@ import { C14nCanonicalization } from "../src/c14n-canonicalization";
44
import * as xmldom from "@xmldom/xmldom";
55
import * as xpath from "xpath";
66
import * as utils from "../src/utils";
7+
import * as isDomNode from "is-dom-node";
78

89
const test_C14nCanonicalization = function (xml, xpathArg, expected) {
910
const doc = new xmldom.DOMParser().parseFromString(xml);
1011
const node = xpath.select1(xpathArg, doc);
1112
const can = new C14nCanonicalization();
12-
let result = "";
13-
14-
if (xpath.isNodeLike(node)) {
15-
result = can
16-
.process(node, {
17-
ancestorNamespaces: utils.findAncestorNs(doc, xpathArg),
18-
})
19-
.toString();
20-
}
13+
14+
isDomNode.assertIsNodeLike(node);
15+
const result = can
16+
.process(node, {
17+
ancestorNamespaces: utils.findAncestorNs(doc, xpathArg),
18+
})
19+
.toString();
2120

2221
expect(result).to.equal(expected);
2322
};

test/c14nWithComments-unit-tests.spec.ts

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@ import { ExclusiveCanonicalizationWithComments as c14nWithComments } from "../sr
44
import * as xmldom from "@xmldom/xmldom";
55
import * as xpath from "xpath";
66
import { SignedXml } from "../src/index";
7+
import * as isDomNode from "is-dom-node";
78

89
const compare = function (xml, xpathArg, expected, inclusiveNamespacesPrefixList?: string[]) {
910
const doc = new xmldom.DOMParser().parseFromString(xml);
1011
const elem = xpath.select1(xpathArg, doc);
1112
const can = new c14nWithComments();
12-
if (xpath.isElement(elem)) {
13-
const result = can.process(elem, { inclusiveNamespacesPrefixList }).toString();
14-
expect(result).to.equal(expected);
15-
} else {
16-
throw new Error("Element not found.");
17-
}
13+
isDomNode.assertIsElementNode(elem);
14+
const result = can.process(elem, { inclusiveNamespacesPrefixList }).toString();
15+
expect(result).to.equal(expected);
1816
};
1917

2018
describe("Exclusive canonicalization with comments", function () {
@@ -354,19 +352,16 @@ describe("Exclusive canonicalization with comments", function () {
354352
'<x xmlns:p="myns"><p:y><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"></ds:Signature></p:y></x>',
355353
);
356354
const node = xpath.select1("//*[local-name(.)='y']", doc);
357-
if (xpath.isNodeLike(node)) {
358-
const sig = new SignedXml();
359-
const res = sig.getCanonXml(
360-
[
361-
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
362-
"http://www.w3.org/2001/10/xml-exc-c14n#",
363-
],
364-
node,
365-
);
366-
expect(res).to.equal('<p:y xmlns:p="myns"></p:y>');
367-
} else {
368-
expect(xpath.isNodeLike(node)).to.be.true;
369-
}
355+
isDomNode.assertIsNodeLike(node);
356+
const sig = new SignedXml();
357+
const res = sig.getCanonXml(
358+
[
359+
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
360+
"http://www.w3.org/2001/10/xml-exc-c14n#",
361+
],
362+
node,
363+
);
364+
expect(res).to.equal('<p:y xmlns:p="myns"></p:y>');
370365
});
371366

372367
it("Enveloped-signature canonicalization respects current node", function () {
@@ -379,12 +374,9 @@ describe("Exclusive canonicalization with comments", function () {
379374
const node = xpath.select1("//*[local-name(.)='y']", doc);
380375
const sig = new SignedXml();
381376
const transforms = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"];
382-
if (xpath.isNodeLike(node)) {
383-
const res = sig.getCanonXml(transforms, node);
384-
expect(res).to.equal("<y/>");
385-
} else {
386-
expect(xpath.isNodeLike(node)).to.be.true;
387-
}
377+
isDomNode.assertIsNodeLike(node);
378+
const res = sig.getCanonXml(transforms, node);
379+
expect(res).to.equal("<y/>");
388380
});
389381

390382
it("The XML canonicalization method processes a node-set by imposing the following additional document order rules on the namespace and attribute nodes of each element: \

0 commit comments

Comments
 (0)