From 4755c33d67c784ee680bcc83a3b81e93c898a49a Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 13 May 2025 10:29:14 +0000 Subject: [PATCH 1/2] fix: do not patch DOMException The DOMException implementation in Domino was legacy and has been removed. This code now uses the standard version of `DOMException`, which is also available natively in Node.js. Related to: https://github.com/angular/angular-cli/issues/30294 --- .prettierrc | 5 ++ lib/DOMException.js | 134 ------------------------------------ lib/Element.js | 7 +- lib/impl.js | 1 - lib/utils.js | 163 ++++++++++++++++++++++++++++++++------------ 5 files changed, 126 insertions(+), 184 deletions(-) create mode 100644 .prettierrc delete mode 100644 lib/DOMException.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..22e9e40 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 100, + "quoteProps": "preserve", + "singleQuote": true +} diff --git a/lib/DOMException.js b/lib/DOMException.js deleted file mode 100644 index f0d8988..0000000 --- a/lib/DOMException.js +++ /dev/null @@ -1,134 +0,0 @@ -"use strict"; -module.exports = DOMException; - -var INDEX_SIZE_ERR = 1; -var HIERARCHY_REQUEST_ERR = 3; -var WRONG_DOCUMENT_ERR = 4; -var INVALID_CHARACTER_ERR = 5; -var NO_MODIFICATION_ALLOWED_ERR = 7; -var NOT_FOUND_ERR = 8; -var NOT_SUPPORTED_ERR = 9; -var INVALID_STATE_ERR = 11; -var SYNTAX_ERR = 12; -var INVALID_MODIFICATION_ERR = 13; -var NAMESPACE_ERR = 14; -var INVALID_ACCESS_ERR = 15; -var TYPE_MISMATCH_ERR = 17; -var SECURITY_ERR = 18; -var NETWORK_ERR = 19; -var ABORT_ERR = 20; -var URL_MISMATCH_ERR = 21; -var QUOTA_EXCEEDED_ERR = 22; -var TIMEOUT_ERR = 23; -var INVALID_NODE_TYPE_ERR = 24; -var DATA_CLONE_ERR = 25; - -// Code to name -var names = [ - null, // No error with code 0 - 'INDEX_SIZE_ERR', - null, // historical - 'HIERARCHY_REQUEST_ERR', - 'WRONG_DOCUMENT_ERR', - 'INVALID_CHARACTER_ERR', - null, // historical - 'NO_MODIFICATION_ALLOWED_ERR', - 'NOT_FOUND_ERR', - 'NOT_SUPPORTED_ERR', - 'INUSE_ATTRIBUTE_ERR', // historical - 'INVALID_STATE_ERR', - 'SYNTAX_ERR', - 'INVALID_MODIFICATION_ERR', - 'NAMESPACE_ERR', - 'INVALID_ACCESS_ERR', - null, // historical - 'TYPE_MISMATCH_ERR', - 'SECURITY_ERR', - 'NETWORK_ERR', - 'ABORT_ERR', - 'URL_MISMATCH_ERR', - 'QUOTA_EXCEEDED_ERR', - 'TIMEOUT_ERR', - 'INVALID_NODE_TYPE_ERR', - 'DATA_CLONE_ERR', -]; - -// Code to message -// These strings are from the 13 May 2011 Editor's Draft of DOM Core. -// http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html -// Copyright © 2011 W3C® (MIT, ERCIM, Keio), All Rights Reserved. -// Used under the terms of the W3C Document License: -// http://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231 -var messages = [ - null, // No error with code 0 - 'INDEX_SIZE_ERR (1): the index is not in the allowed range', - null, - 'HIERARCHY_REQUEST_ERR (3): the operation would yield an incorrect nodes model', - 'WRONG_DOCUMENT_ERR (4): the object is in the wrong Document, a call to importNode is required', - 'INVALID_CHARACTER_ERR (5): the string contains invalid characters', - null, - 'NO_MODIFICATION_ALLOWED_ERR (7): the object can not be modified', - 'NOT_FOUND_ERR (8): the object can not be found here', - 'NOT_SUPPORTED_ERR (9): this operation is not supported', - 'INUSE_ATTRIBUTE_ERR (10): setAttributeNode called on owned Attribute', - 'INVALID_STATE_ERR (11): the object is in an invalid state', - 'SYNTAX_ERR (12): the string did not match the expected pattern', - 'INVALID_MODIFICATION_ERR (13): the object can not be modified in this way', - 'NAMESPACE_ERR (14): the operation is not allowed by Namespaces in XML', - 'INVALID_ACCESS_ERR (15): the object does not support the operation or argument', - null, - 'TYPE_MISMATCH_ERR (17): the type of the object does not match the expected type', - 'SECURITY_ERR (18): the operation is insecure', - 'NETWORK_ERR (19): a network error occurred', - 'ABORT_ERR (20): the user aborted an operation', - 'URL_MISMATCH_ERR (21): the given URL does not match another URL', - 'QUOTA_EXCEEDED_ERR (22): the quota has been exceeded', - 'TIMEOUT_ERR (23): a timeout occurred', - 'INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation', - 'DATA_CLONE_ERR (25): the object can not be cloned.' -]; - -// Name to code -var constants = { - INDEX_SIZE_ERR: INDEX_SIZE_ERR, - DOMSTRING_SIZE_ERR: 2, // historical - HIERARCHY_REQUEST_ERR: HIERARCHY_REQUEST_ERR, - WRONG_DOCUMENT_ERR: WRONG_DOCUMENT_ERR, - INVALID_CHARACTER_ERR: INVALID_CHARACTER_ERR, - NO_DATA_ALLOWED_ERR: 6, // historical - NO_MODIFICATION_ALLOWED_ERR: NO_MODIFICATION_ALLOWED_ERR, - NOT_FOUND_ERR: NOT_FOUND_ERR, - NOT_SUPPORTED_ERR: NOT_SUPPORTED_ERR, - INUSE_ATTRIBUTE_ERR: 10, // historical - INVALID_STATE_ERR: INVALID_STATE_ERR, - SYNTAX_ERR: SYNTAX_ERR, - INVALID_MODIFICATION_ERR: INVALID_MODIFICATION_ERR, - NAMESPACE_ERR: NAMESPACE_ERR, - INVALID_ACCESS_ERR: INVALID_ACCESS_ERR, - VALIDATION_ERR: 16, // historical - TYPE_MISMATCH_ERR: TYPE_MISMATCH_ERR, - SECURITY_ERR: SECURITY_ERR, - NETWORK_ERR: NETWORK_ERR, - ABORT_ERR: ABORT_ERR, - URL_MISMATCH_ERR: URL_MISMATCH_ERR, - QUOTA_EXCEEDED_ERR: QUOTA_EXCEEDED_ERR, - TIMEOUT_ERR: TIMEOUT_ERR, - INVALID_NODE_TYPE_ERR: INVALID_NODE_TYPE_ERR, - DATA_CLONE_ERR: DATA_CLONE_ERR -}; - -function DOMException(code) { - Error.call(this); - Error.captureStackTrace(this, this.constructor); - this.code = code; - this.message = messages[code]; - this.name = names[code]; -} -DOMException.prototype.__proto__ = Error.prototype; - -// Initialize the constants on DOMException and DOMException.prototype -for(var c in constants) { - var v = { value: constants[c] }; - Object.defineProperty(DOMException, c, v); - Object.defineProperty(DOMException.prototype, c, v); -} diff --git a/lib/Element.js b/lib/Element.js index 0b481d2..e9d5794 100644 --- a/lib/Element.js +++ b/lib/Element.js @@ -9,7 +9,6 @@ var Node = require('./Node'); var NodeList = require('./NodeList'); var NodeUtils = require('./NodeUtils'); var FilteredElementList = require('./FilteredElementList'); -var DOMException = require('./DOMException'); var DOMTokenList = require('./DOMTokenList'); var select = require('./select'); var ContainerNode = require('./ContainerNode'); @@ -683,7 +682,7 @@ Element.prototype = Object.create(ContainerNode.prototype, { setAttributeNode: { value: function setAttributeNode(attr) { if (attr.ownerElement !== null && attr.ownerElement !== this) { - throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR); + utils.InUseAttributeError(); } var result = null; var oldAttrs = this._attrsByQName[attr.name]; @@ -692,7 +691,7 @@ Element.prototype = Object.create(ContainerNode.prototype, { if (oldAttrs.some(function(a) { return a===attr; })) { return attr; } else if (attr.ownerElement !== null) { - throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR); + utils.InUseAttributeError(); } oldAttrs.forEach(function(a) { this.removeAttributeNode(a); }, this); result = oldAttrs[0]; @@ -703,7 +702,7 @@ Element.prototype = Object.create(ContainerNode.prototype, { setAttributeNodeNS: { value: function setAttributeNodeNS(attr) { if (attr.ownerElement !== null) { - throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR); + utils.InUseAttributeError(); } var ns = attr.namespaceURI; var key = (ns === null ? '' : ns) + '|' + attr.localName; diff --git a/lib/impl.js b/lib/impl.js index cae967c..1f6a0a6 100644 --- a/lib/impl.js +++ b/lib/impl.js @@ -5,7 +5,6 @@ exports = module.exports = { CSSStyleDeclaration: require('./CSSStyleDeclaration'), CharacterData: require('./CharacterData'), Comment: require('./Comment'), - DOMException: require('./DOMException'), DOMImplementation: require('./DOMImplementation'), DOMTokenList: require('./DOMTokenList'), Document: require('./Document'), diff --git a/lib/utils.js b/lib/utils.js index 9bf6b7a..39a4471 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,7 +1,5 @@ -"use strict"; -var DOMException = require('./DOMException'); -var ERR = DOMException; -var isApiWritable = require("./config").isApiWritable; +'use strict'; +var isApiWritable = require('./config').isApiWritable; exports.NAMESPACE = { HTML: 'http://www.w3.org/1999/xhtml', @@ -9,55 +7,130 @@ exports.NAMESPACE = { XMLNS: 'http://www.w3.org/2000/xmlns/', MATHML: 'http://www.w3.org/1998/Math/MathML', SVG: 'http://www.w3.org/2000/svg', - XLINK: 'http://www.w3.org/1999/xlink' + XLINK: 'http://www.w3.org/1999/xlink', }; // // Shortcut functions for throwing errors of various types. // -exports.IndexSizeError = function() { throw new DOMException(ERR.INDEX_SIZE_ERR); }; -exports.HierarchyRequestError = function() { throw new DOMException(ERR.HIERARCHY_REQUEST_ERR); }; -exports.WrongDocumentError = function() { throw new DOMException(ERR.WRONG_DOCUMENT_ERR); }; -exports.InvalidCharacterError = function() { throw new DOMException(ERR.INVALID_CHARACTER_ERR); }; -exports.NoModificationAllowedError = function() { throw new DOMException(ERR.NO_MODIFICATION_ALLOWED_ERR); }; -exports.NotFoundError = function() { throw new DOMException(ERR.NOT_FOUND_ERR); }; -exports.NotSupportedError = function() { throw new DOMException(ERR.NOT_SUPPORTED_ERR); }; -exports.InvalidStateError = function() { throw new DOMException(ERR.INVALID_STATE_ERR); }; -exports.SyntaxError = function() { throw new DOMException(ERR.SYNTAX_ERR); }; -exports.InvalidModificationError = function() { throw new DOMException(ERR.INVALID_MODIFICATION_ERR); }; -exports.NamespaceError = function() { throw new DOMException(ERR.NAMESPACE_ERR); }; -exports.InvalidAccessError = function() { throw new DOMException(ERR.INVALID_ACCESS_ERR); }; -exports.TypeMismatchError = function() { throw new DOMException(ERR.TYPE_MISMATCH_ERR); }; -exports.SecurityError = function() { throw new DOMException(ERR.SECURITY_ERR); }; -exports.NetworkError = function() { throw new DOMException(ERR.NETWORK_ERR); }; -exports.AbortError = function() { throw new DOMException(ERR.ABORT_ERR); }; -exports.UrlMismatchError = function() { throw new DOMException(ERR.URL_MISMATCH_ERR); }; -exports.QuotaExceededError = function() { throw new DOMException(ERR.QUOTA_EXCEEDED_ERR); }; -exports.TimeoutError = function() { throw new DOMException(ERR.TIMEOUT_ERR); }; -exports.InvalidNodeTypeError = function() { throw new DOMException(ERR.INVALID_NODE_TYPE_ERR); }; -exports.DataCloneError = function() { throw new DOMException(ERR.DATA_CLONE_ERR); }; - -exports.nyi = function() { - throw new Error("NotYetImplemented"); -}; - -exports.shouldOverride = function() { - throw new Error("Abstract function; should be overriding in subclass."); -}; - -exports.assert = function(expr, msg) { +exports.IndexSizeError = () => { + throw new DOMException('The index is not in the allowed range', 'IndexSizeError'); +}; + +exports.HierarchyRequestError = () => { + throw new DOMException('The node tree hierarchy is not correct', 'HierarchyRequestError'); +}; + +exports.WrongDocumentError = () => { + throw new DOMException('The object is in the wrong Document', 'WrongDocumentError'); +}; + +exports.InvalidCharacterError = () => { + throw new DOMException('The string contains invalid characters', 'InvalidCharacterError'); +}; + +exports.NoModificationAllowedError = () => { + throw new DOMException('The object cannot be modified', 'NoModificationAllowedError'); +}; + +exports.NotFoundError = () => { + throw new DOMException('The object can not be found here', 'NotFoundError'); +}; + +exports.NotSupportedError = () => { + throw new DOMException('The operation is not supported', 'NotSupportedError'); +}; + +exports.InvalidStateError = () => { + throw new DOMException('The object is in an invalid state', 'InvalidStateError'); +}; + +exports.SyntaxError = () => { + throw new DOMException('The string did not match the expected pattern', 'SyntaxError'); +}; + +exports.InvalidModificationError = () => { + throw new DOMException('The object can not be modified in this way', 'InvalidModificationError'); +}; + +exports.NamespaceError = () => { + throw new DOMException('The operation is not allowed by Namespaces in XML', 'NamespaceError'); +}; + +exports.InvalidAccessError = () => { + throw new DOMException( + 'The object does not support the operation or argument', + 'InvalidAccessError' + ); +}; + +exports.TypeMismatchError = () => { + throw new DOMException( + 'The type of the object does not match the expected type', + 'TypeMismatchError' + ); +}; + +exports.SecurityError = () => { + throw new DOMException('The operation is insecure', 'SecurityError'); +}; + +exports.NetworkError = () => { + throw new DOMException('A network error occurred', 'NetworkError'); +}; + +exports.AbortError = () => { + throw new DOMException('The operation was aborted', 'AbortError'); +}; + +exports.UrlMismatchError = () => { + throw new DOMException('The given URL does not match another URL', 'URLMismatchError'); +}; + +exports.QuotaExceededError = () => { + throw new DOMException('The quota has been exceeded', 'QuotaExceededError'); +}; + +exports.TimeoutError = () => { + throw new DOMException('The operation timed out', 'TimeoutError'); +}; + +exports.InvalidNodeTypeError = () => { + throw new DOMException('The node is of an invalid type', 'InvalidNodeTypeError'); +}; + +exports.DataCloneError = () => { + throw new DOMException('The object can not be cloned', 'DataCloneError'); +}; + +exports.InUseAttributeError = () => { + throw new DOMException('The attribute is already in use', 'InUseAttributeError'); +}; + +exports.nyi = function () { + throw new Error('NotYetImplemented'); +}; + +exports.shouldOverride = function () { + throw new Error('Abstract function; should be overriding in subclass.'); +}; + +exports.assert = function (expr, msg) { if (!expr) { - throw new Error("Assertion failed: " + (msg || "") + "\n" + new Error().stack); + throw new Error('Assertion failed: ' + (msg || '') + '\n' + new Error().stack); } }; -exports.expose = function(src, c) { +exports.expose = function (src, c) { for (var n in src) { - Object.defineProperty(c.prototype, n, { value: src[n], writable: isApiWritable }); + Object.defineProperty(c.prototype, n, { + value: src[n], + writable: isApiWritable, + }); } }; -exports.merge = function(a, b) { +exports.merge = function (a, b) { for (var n in b) { a[n] = b[n]; } @@ -67,19 +140,19 @@ exports.merge = function(a, b) { // to be passed to sort(). Assumes that the array being sorted does not // contain duplicates. And that all nodes are connected and comparable. // Clever code by ppk via jeresig. -exports.documentOrder = function(n,m) { +exports.documentOrder = function (n, m) { /* jshint bitwise: false */ return 3 - (n.compareDocumentPosition(m) & 6); }; -exports.toASCIILowerCase = function(s) { - return s.replace(/[A-Z]+/g, function(c) { +exports.toASCIILowerCase = function (s) { + return s.replace(/[A-Z]+/g, function (c) { return c.toLowerCase(); }); }; -exports.toASCIIUpperCase = function(s) { - return s.replace(/[a-z]+/g, function(c) { +exports.toASCIIUpperCase = function (s) { + return s.replace(/[a-z]+/g, function (c) { return c.toUpperCase(); }); }; From 5be51e5ccda4793eb7a221a911aeb166d6cbfcf9 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 13 May 2025 10:38:34 +0000 Subject: [PATCH 2/2] ci: fix broken CI --- .nvmrc | 2 +- test/xss.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.nvmrc b/.nvmrc index 5b0ad74..b8ffd70 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.13.0 \ No newline at end of file +22.15.0 diff --git a/test/xss.js b/test/xss.js index aa513a5..faf1984 100644 --- a/test/xss.js +++ b/test/xss.js @@ -28,7 +28,7 @@ let browser; let incognito; exports.before = async function() { - browser = await puppeteer.launch({headless:"new"}); + browser = await puppeteer.launch({headless:"new", args: ['--no-sandbox', '--disable-gpu']}); incognito = await browser.createIncognitoBrowserContext(); }