Skip to content

Commit

Permalink
Validate nodes passed to XPath#evaluate()
Browse files Browse the repository at this point in the history
  • Loading branch information
JLRishe committed Dec 16, 2023
1 parent d5bf3d9 commit 3ce4730
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 17 deletions.
10 changes: 10 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,16 @@ describe('xpath', () => {
});
}
});

it('should reject non-nodes', () => {
for (let node of ['<n />', 0, 45, true, false, [], {}]) {
assert.throws(() => {
xpath.parse('/*').select({ node });
}, {
message: 'Context node does not appear to be a valid DOM node.'
});
}
});
});

describe('Node type tests', () => {
Expand Down
54 changes: 37 additions & 17 deletions xpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,28 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
(function (exports) {
"use strict";

// namespace nodes are not part of the DOM spec, so we use a custom nodetype for them.
// should NOT be used externally
var NAMESPACE_NODE_NODETYPE = '__namespace';

var isNil = function (x) {
return x === null || x === undefined;
};

var isValidNodeType = function (nodeType) {
return nodeType === NAMESPACE_NODE_NODETYPE ||
(Number.isInteger(nodeType)
&& nodeType >= 1
&& nodeType <= 11
);
};

var isNodeLike = function (value) {
return value
&& isValidNodeType(value.nodeType)
&& typeof value.nodeName === "string";
};

// functional helpers
function curry(func) {
var slice = Array.prototype.slice,
Expand Down Expand Up @@ -167,7 +189,7 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;

var prototypeConcat = Array.prototype.concat;

var sortNodes = function(nodes, reverse) {
var sortNodes = function (nodes, reverse) {
var ns = new XNodeSet();

ns.addArray(nodes);
Expand Down Expand Up @@ -221,7 +243,7 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
DOCUMENT_NODE: 9,
DOCUMENT_TYPE_NODE: 10,
DOCUMENT_FRAGMENT_NODE: 11,
NAMESPACE_NODE: '__namespace', // not part of DOM model
NAMESPACE_NODE: NAMESPACE_NODE_NODETYPE,
};

// XPathParser ///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1328,6 +1350,12 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
}

XPath.prototype.evaluate = function (c) {
var node = c.expressionContextNode;

if (!(isNil(node) || isNodeLike(node))) {
throw new Error("Context node does not appear to be a valid DOM node.");
}

c.contextNode = c.expressionContextNode;
c.contextSize = 1;
c.contextPosition = 1;
Expand Down Expand Up @@ -3153,7 +3181,7 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
}

// xml namespace node comes before others. namespace nodes before non-namespace nodes
if (n1.isXPathNamespace){
if (n1.isXPathNamespace) {
if (n1.nodeValue === XPath.XML_NAMESPACE_URI) {
return -1;
}
Expand Down Expand Up @@ -4494,6 +4522,7 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
this.context.caseInsensitive = XPathExpression.detectHtmlDom(n);

var result = this.xpath.evaluate(this.context);

return new XPathResult(result, t);
}

Expand Down Expand Up @@ -4904,21 +4933,21 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
DivOperation: DivOperation,
ModOperation: ModOperation,
UnaryMinusOperation: UnaryMinusOperation,

FunctionCall: FunctionCall,
VariableReference: VariableReference,

XPathContext: XPathContext,

XNodeSet: XNodeSet,
XBoolean: XBoolean,
XString: XString,
XNumber: XNumber,

NamespaceResolver: NamespaceResolver,
FunctionResolver: FunctionResolver,
VariableResolver: VariableResolver,

Utilities: Utilities,
}
);
Expand Down Expand Up @@ -4970,15 +4999,6 @@ var xpath = (typeof exports === 'undefined') ? {} : exports;
return exports.select(e, doc, true);
};

var isNodeLike = function (value) {
return value
&& typeof value.nodeType === "number"
&& Number.isInteger(value.nodeType)
&& value.nodeType >= 1
&& value.nodeType <= 11
&& typeof value.nodeName === "string";
};

var isArrayOfNodes = function (value) {
return Array.isArray(value) && value.every(isNodeLike);
};
Expand Down

0 comments on commit 3ce4730

Please sign in to comment.