From 62035d52fec0aa6d115121bd962bb18ffc170d1d Mon Sep 17 00:00:00 2001 From: Zachary-Squires <122392519+Zachary-Squires@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:16:47 -0400 Subject: [PATCH 01/72] Create README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..aa68bd2e3 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Team-5 + From 794d4645d51e8f443ddd6ffc0fadf9e0f31f3e86 Mon Sep 17 00:00:00 2001 From: Zachary-Squires <122392519+Zachary-Squires@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:21:41 -0400 Subject: [PATCH 02/72] Create thing-in-docs-folder squid game --- Docs/thing-in-docs-folder | 1 + 1 file changed, 1 insertion(+) create mode 100644 Docs/thing-in-docs-folder diff --git a/Docs/thing-in-docs-folder b/Docs/thing-in-docs-folder new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/Docs/thing-in-docs-folder @@ -0,0 +1 @@ + From 4ea1a463ea02e82543daf29deba288b990110b95 Mon Sep 17 00:00:00 2001 From: Andrew-Bonner Date: Tue, 26 Aug 2025 12:47:18 -0400 Subject: [PATCH 03/72] add a file for team meeting schedule --- team_schedule | 1 + 1 file changed, 1 insertion(+) create mode 100644 team_schedule diff --git a/team_schedule b/team_schedule new file mode 100644 index 000000000..cdb403229 --- /dev/null +++ b/team_schedule @@ -0,0 +1 @@ +Meeting Times: \ No newline at end of file From aca1a74304af38172604806da536f2a66c94e632 Mon Sep 17 00:00:00 2001 From: Zachary Squires Date: Tue, 26 Aug 2025 12:48:46 -0400 Subject: [PATCH 04/72] Update thing-in-docs-folder --- Docs/thing-in-docs-folder | 1 + 1 file changed, 1 insertion(+) diff --git a/Docs/thing-in-docs-folder b/Docs/thing-in-docs-folder index 8b1378917..82008d333 100644 --- a/Docs/thing-in-docs-folder +++ b/Docs/thing-in-docs-folder @@ -1 +1,2 @@ +this is a thing in the docs folder \ No newline at end of file From 23bca3d449bebca13bc2d57051e7826e8703331a Mon Sep 17 00:00:00 2001 From: Landon Date: Mon, 7 Jul 2025 18:03:38 -0400 Subject: [PATCH 05/72] Adding the commit from 1488 called "fix: resolved PR feedback on sanitizer imports and tests" --- package.json | 2 ++ pull_request_template.md | 2 +- src/server/routes/response.js | 31 ++++++++++---------- src/server/services/csvPipeline/success.js | 34 ++++++++++------------ src/server/test/crossSite/crossSite.js | 28 ++++++++++++++++++ 5 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 src/server/test/crossSite/crossSite.js diff --git a/package.json b/package.json index 934ad9dc2..12b1b80f3 100644 --- a/package.json +++ b/package.json @@ -67,12 +67,14 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", + "dompurify": "~3.2.6", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", "express-rate-limit": "~7.2.0", "history": "~5.3.0", "ini": "~4.1.3", + "jsdom": "~26.1.0", "jsonschema": "~1.4.1", "jsonwebtoken": "~9.0.0", "lodash": "~4.17.21", diff --git a/pull_request_template.md b/pull_request_template.md index 982321649..317dc640e 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -23,4 +23,4 @@ Fixes #[issue] ## Limitations -(Describe any issues that remain or work that should still be done.) +(Describe any issues that remain or work that should still be done.) \ No newline at end of file diff --git a/src/server/routes/response.js b/src/server/routes/response.js index e50371010..2c4583157 100644 --- a/src/server/routes/response.js +++ b/src/server/routes/response.js @@ -2,32 +2,31 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - // Functions to return a code and comment from an Express request. +// Functions to return a code and comment from an Express request. + +const DOMPurify = require('../services/utils/sanitizer'); /** - * Inform the client of a success (200 OK). - * - * @param res The Express response object - * @param comment Any additional data to be returned to the client as a string + * Inform the client of a success (200 OK) with sanitized content. * + * @param {express.Response} res The Express response object. + * @param {string} comment Any additional data to be returned to the client as a string. */ - function success(res, comment = '') { - res.status(200) // 200 OK - .send(comment); +function success(res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); + res.status(200).send(safeComment); } /** - * Inform the client of a failure with provided code or 500. - * - * @param res The Express response object - * @param code The code number to send back for request - * @param comment Any additional data to be returned to the client as a string + * Inform the client of a failure with provided code or 500, using sanitized content. * + * @param {express.Response} res The Express response object. + * @param {number} code The code number to send back for the request. + * @param {string} comment Any additional data to be returned to the client as a string. */ function failure(res, code = 500, comment = '') { - res.status(code) - .send(comment); - + const safeComment = DOMPurify.sanitize(comment); + res.status(code).send(safeComment); } module.exports = { success, failure }; diff --git a/src/server/services/csvPipeline/success.js b/src/server/services/csvPipeline/success.js index 7da71555d..852f6dabd 100644 --- a/src/server/services/csvPipeline/success.js +++ b/src/server/services/csvPipeline/success.js @@ -2,35 +2,33 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const express = require('express') /* needed to resolve types in JSDoc comments */ +const express = require('express'); +const DOMPurify = require('../utils/sanitizer'); /** - * Inform the client of a success (200 OK). + * Inform the client of a success (200 OK) with sanitized HTML content. * - * @param {express.Request} req The Express request object - * @param {express.Response} res The Express response object + * @param {express.Request} req The Express request object. + * @param {express.Response} res The Express response object. * @param {string} comment Any additional data to be returned to the client. - * */ function success(req, res, comment = '') { - res.status(200) // 200 OK - .send(`

SUCCESS

${comment}`); + const safeComment = DOMPurify.sanitize(comment); + res.status(200).send(`

SUCCESS

${safeComment}`); } + /** - * Inform the client of a failure (400 OK). + * Inform the client of a failure (400 OK) with sanitized HTML content. * - * @param {express.Request} req The Express request object - * @param {express.Response} res The Express response object + * @param {express.Request} req The Express request object. + * @param {express.Response} res The Express response object. * @param {string} comment Any additional data to be returned to the client. - * */ function failure(req, res, comment = '') { - // 400 is client error. There is a small chance the insert into the DB failed - // but overlooking that. - res.status(400) - .send(`

FAILURE

${comment}`); - + const safeComment = DOMPurify.sanitize(comment); + // 400 is client error. There is a small chance the insert into the DB failed + // but overlooking that. + res.status(400).send(`

FAILURE

${safeComment}`); } - -module.exports = { success, failure }; +module.exports = { success, failure }; \ No newline at end of file diff --git a/src/server/test/crossSite/crossSite.js b/src/server/test/crossSite/crossSite.js new file mode 100644 index 000000000..63e1ed4dc --- /dev/null +++ b/src/server/test/crossSite/crossSite.js @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This file tests the functionality of the DOMPurify library. It tests for XSS +vulnerabilities in HTML of user uploaded data.*/ + +/* Run in OED Docker web container terminal/shell: + npm run testsome src/server/test/crossSite/crossSite.js */ +const { chai, mocha, expect, app, testUser } = require('../common'); + +mocha.describe('Cross site', () => { + mocha.it('test 1: tests for sanitization of HTML', async () => { + const filePath = 'src/server/test/crossSite/readings.csv'; + + const res = await chai.request(app).post('/api/csv/readings') + .field('email', testUser.username) + .field('password', testUser.password) + /* This next line should produce: + res.text:

FAILURE

CSVPipelineError: + User Error: Meter with name '' not found. */ + .field('meterName','') + .field('gzip', "no") + .attach('csvfile', 'src/server/test/crossSite/something.csv'); + expect(res.text).to.include(''); + expect(res).to.have.status(400); + }); +}); From bacc35d7b9f914743d76064c25ae161d23fa8bcb Mon Sep 17 00:00:00 2001 From: Landon Date: Wed, 7 May 2025 22:51:57 -0700 Subject: [PATCH 06/72] feat(security): centralize DOMPurify sanitizer and patch XSS in success/failure responses --- :w | 8 + package-lock.json | 413 ++++++++++++++++++++- package.json | 4 +- pull_request_template.md | 14 +- src/server/routes/response.js | 44 +-- src/server/services/csvPipeline/success.js | 58 +-- src/server/services/utils/sanitizer.js | 8 + src/server/test/crossSite.js | 38 ++ src/server/test/crossSite/something.csv | 1 + src/server/test/something.csv | 1 + 10 files changed, 527 insertions(+), 62 deletions(-) create mode 100644 :w create mode 100644 src/server/services/utils/sanitizer.js create mode 100644 src/server/test/crossSite.js create mode 100644 src/server/test/crossSite/something.csv create mode 100644 src/server/test/something.csv diff --git a/:w b/:w new file mode 100644 index 000000000..f5b249f20 --- /dev/null +++ b/:w @@ -0,0 +1,8 @@ +const { JSDOM } = require('jsdom'); +const createDOMPurify = require('dompurify'); + +const window = new JSDOM('').window; +const DOMPurify = createDOMPurify(window); + +module.exports = DOMPurify; + diff --git a/package-lock.json b/package-lock.json index 68bc1cd28..69a788536 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,14 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", + "dompurify": "~2.4.0", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", "express-rate-limit": "~7.2.0", "history": "~5.3.0", "ini": "~4.1.3", + "jsdom": "~21.1.0", "jsonschema": "~1.4.1", "jsonwebtoken": "~9.0.0", "lodash": "~4.17.21", @@ -911,6 +913,14 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@turf/area": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.5.0.tgz", @@ -1838,8 +1848,7 @@ "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" }, "node_modules/abbrev": { "version": "1.1.1", @@ -1868,7 +1877,6 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1876,6 +1884,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, "node_modules/acorn-import-assertions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", @@ -1894,6 +1911,28 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3228,6 +3267,17 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", @@ -3791,11 +3841,23 @@ "node": ">=12" } }, + "node_modules/data-urls": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", + "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^12.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -3820,6 +3882,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + }, "node_modules/decode-uri-component": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz", @@ -4023,6 +4090,23 @@ "url": "https://bevry.me/fund" } }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dompurify": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.9.tgz", + "integrity": "sha512-iHtnxYMotKgOTvxIqq677JsKHvCOkAFqj9x8Mek2zdeHW1XjuFKwjpmZeMaXQRQ8AbJZDbcRz/+r1QhwvFtmQg==" + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -4192,6 +4276,17 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/envinfo": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", @@ -4679,7 +4774,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -5739,6 +5833,17 @@ "resolved": "https://registry.npmjs.org/hsluv/-/hsluv-0.0.3.tgz", "integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==" }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5754,12 +5859,37 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", "dev": true }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -6211,6 +6341,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, "node_modules/is-string-blank": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", @@ -6332,6 +6467,79 @@ "node": ">=12.0.0" } }, + "node_modules/jsdom": { + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.2.tgz", + "integrity": "sha512-sCpFmK2jv+1sjff4u7fzft+pUh2KSUbUrEHYHyfSIbGTIcmnjyp83qg6qLwdJ/I3LpTXx33ACxeRL7Lsyc6lGQ==", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.2", + "acorn-globals": "^7.0.0", + "cssstyle": "^3.0.0", + "data-urls": "^4.0.0", + "decimal.js": "^10.4.3", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.4", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^12.0.1", + "ws": "^8.13.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/jsdom/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7386,6 +7594,11 @@ "node": ">=0.10.0" } }, + "node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7612,6 +7825,17 @@ "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", "integrity": "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==" }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -8224,6 +8448,17 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -8254,7 +8489,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -8309,6 +8543,11 @@ "node": ">=0.4.x" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8903,6 +9142,11 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/reselect": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz", @@ -9006,6 +9250,11 @@ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9063,6 +9312,17 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -9760,6 +10020,11 @@ "svg-path-bounds": "^1.0.1" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -9983,6 +10248,31 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", @@ -10194,6 +10484,14 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -10269,6 +10567,15 @@ "qs": "^6.11.2" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -10383,6 +10690,17 @@ "pbf": "^3.2.1" } }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -10409,6 +10727,14 @@ "get-canvas-context": "^1.0.1" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, "node_modules/webpack": { "version": "5.76.3", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.3.tgz", @@ -10573,6 +10899,48 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10690,6 +11058,34 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "engines": { + "node": ">=12" + } + }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", @@ -10710,6 +11106,11 @@ "node": ">=4.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 12b1b80f3..6f1eac951 100644 --- a/package.json +++ b/package.json @@ -67,14 +67,14 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", - "dompurify": "~3.2.6", + "dompurify": "~2.4.0", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", "express-rate-limit": "~7.2.0", "history": "~5.3.0", "ini": "~4.1.3", - "jsdom": "~26.1.0", + "jsdom": "~21.1.0", "jsonschema": "~1.4.1", "jsonwebtoken": "~9.0.0", "lodash": "~4.17.21", diff --git a/pull_request_template.md b/pull_request_template.md index 317dc640e..2555a2943 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,10 +1,12 @@ # Description -(Please include a summary of the change and which issue is touched on. Please also include relevant motivation and context.) +Implemented DOMPurify to sanitize HTML responses from response.js and success.js. Created sanitizer.js as a centralized, pre-configured instance of DOMPurify on the server. This also allows for implementation of sanitization elsewhere if needed in the future. These changes remediate a cross-site scripting vulnerability when users upload meter data. -Fixes #[issue] +Fixes problem report 3 from the penetration test report. -(In general, OED likes to have at least one issue associated with each pull request. Replace [issue] with the OED GitHub issue number. In the preview you will see an issue description if you hover over that number. You can create one yourself before doing this pull request. This is where details are normally given on what is being addressed. Note you should not use the word "Fixes" if it does not completely address the issue since the issue would automatically be closed on merging the pull request. In that case use "Partly Addresses #[issue].) +Developed and implemented by: +Thomas Nigro - https://github.com/tnigro45 +Landon Wivell - https://github.com/Landon-Wivell ## Type of change @@ -17,10 +19,12 @@ Fixes #[issue] (Note what you have done by placing an "x" instead of the space in the [ ] so it becomes [x]. It is hoped you do all of them.) -- [ ] I have followed the [OED pull request](https://openenergydashboard.org/developer/pr/) ideas +- [x] I have followed the [OED pull request](https://openenergydashboard.org/developer/pr/) ideas - [ ] I have removed text in ( ) from the issue request -- [ ] You acknowledge that every person contributing to this work has signed the [OED Contributing License Agreement](https://openenergydashboard.org/developer/cla/) and each author is listed in the Description section. +- [x] You acknowledge that every person contributing to this work has signed the [OED Contributing License Agreement](https://openenergydashboard.org/developer/cla/) and each author is listed in the Description section. ## Limitations +N/A + (Describe any issues that remain or work that should still be done.) \ No newline at end of file diff --git a/src/server/routes/response.js b/src/server/routes/response.js index 2c4583157..82077dd67 100644 --- a/src/server/routes/response.js +++ b/src/server/routes/response.js @@ -6,27 +6,27 @@ const DOMPurify = require('../services/utils/sanitizer'); -/** - * Inform the client of a success (200 OK) with sanitized content. - * - * @param {express.Response} res The Express response object. - * @param {string} comment Any additional data to be returned to the client as a string. - */ -function success(res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); + /** + * Inform the client of a success (200 OK) with sanitized content. + * + * @param {express.Response} res - The Express response object. + * @param {string} comment - Any additional data to be returned to the client as a string. + */ + function success(res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(200).send(safeComment); -} - -/** - * Inform the client of a failure with provided code or 500, using sanitized content. - * - * @param {express.Response} res The Express response object. - * @param {number} code The code number to send back for the request. - * @param {string} comment Any additional data to be returned to the client as a string. - */ -function failure(res, code = 500, comment = '') { - const safeComment = DOMPurify.sanitize(comment); + } + + /** + * Inform the client of a failure with provided code or 500, using sanitized content. + * + * @param {express.Response} res - The Express response object. + * @param {number} code - The code number to send back for the request. + * @param {string} comment - Any additional data to be returned to the client as a string. + */ + function failure(res, code = 500, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(code).send(safeComment); -} - -module.exports = { success, failure }; + } + + module.exports = { success, failure }; diff --git a/src/server/services/csvPipeline/success.js b/src/server/services/csvPipeline/success.js index 852f6dabd..004b2a4ed 100644 --- a/src/server/services/csvPipeline/success.js +++ b/src/server/services/csvPipeline/success.js @@ -2,33 +2,37 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const express = require('express'); -const DOMPurify = require('../utils/sanitizer'); + const express = require('express') /* needed to resolve types in JSDoc comments */ -/** - * Inform the client of a success (200 OK) with sanitized HTML content. - * - * @param {express.Request} req The Express request object. - * @param {express.Response} res The Express response object. - * @param {string} comment Any additional data to be returned to the client. - */ -function success(req, res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); + const DOMPurify = require('../utils/sanitizer'); + + /** + * Inform the client of a success (200 OK) with sanitized HTML content. + * + * @param {express.Request} req - The Express request object. + * @param {express.Response} res - The Express response object. + * @param {string} comment - Any additional data to be returned to the client. + */ + function success(req, res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(200).send(`

SUCCESS

${safeComment}`); -} - - -/** - * Inform the client of a failure (400 OK) with sanitized HTML content. - * - * @param {express.Request} req The Express request object. - * @param {express.Response} res The Express response object. - * @param {string} comment Any additional data to be returned to the client. - */ -function failure(req, res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); - // 400 is client error. There is a small chance the insert into the DB failed - // but overlooking that. + } + + + /** + * Inform the client of a failure (400 OK) with sanitized HTML content. + * + * @param {express.Request} req - The Express request object. + * @param {express.Response} res - The Express response object. + * @param {string} comment - Any additional data to be returned to the client. + */ + function failure(req, res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(400).send(`

FAILURE

${safeComment}`); -} -module.exports = { success, failure }; \ No newline at end of file + } + + + module.exports = { success, failure }; + + + \ No newline at end of file diff --git a/src/server/services/utils/sanitizer.js b/src/server/services/utils/sanitizer.js new file mode 100644 index 000000000..3b5ccc536 --- /dev/null +++ b/src/server/services/utils/sanitizer.js @@ -0,0 +1,8 @@ +const { JSDOM } = require('jsdom'); +const createDOMPurify = require('dompurify'); + +// Create jsdom window application and initialize +const window = new JSDOM('').window; +const DOMPurify = createDOMPurify(window); + +module.exports = DOMPurify; diff --git a/src/server/test/crossSite.js b/src/server/test/crossSite.js new file mode 100644 index 000000000..3143b9b17 --- /dev/null +++ b/src/server/test/crossSite.js @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This file tests ???. */ + +// Run in OED Docker web container terminal/shell: npm run testsome src/server/test/crossSite.js + +const { chai, mocha, expect, app, testUser } = require('./common'); + +mocha.describe('Cross site', () => { + mocha.it('test 1', async () => { + const res = await chai.request(app).post('/api/csv/readings') + .field('email', testUser.username) + .field('password', testUser.password) + .field('meterName', "<img src/onerror=alert(document.domain)>") + .field('timeSort', "increasing") + .field('duplications', "") + .field('cumulative', "no") + .field('cumulativeReset', "yes") + .field('cumulativeResetStart', "") + .field('cumulativeResetEnd', "") + .field('lengthGap', "") + .field('lengthVariation', "") + .field('endOnly', "no") + .field('gzip', "no") + .field('headerRow', "yes") + .field('refreshReadings', "no") + .field('update', "no") + .field('honorDst', "no") + .field('relaxedParsing', "no") + .field('csvfile', "file") + .attach('CSV', 'src/server/test/something.csv'); + console.log('res.text: ', res.text); + // expect(res).to.have.status(500); should be a 500 code after all fields are inputed + //expect(res).to.be.json; + }); +}); diff --git a/src/server/test/crossSite/something.csv b/src/server/test/crossSite/something.csv new file mode 100644 index 000000000..3fde4e202 --- /dev/null +++ b/src/server/test/crossSite/something.csv @@ -0,0 +1 @@ +1,2,3 \ No newline at end of file diff --git a/src/server/test/something.csv b/src/server/test/something.csv new file mode 100644 index 000000000..3fde4e202 --- /dev/null +++ b/src/server/test/something.csv @@ -0,0 +1 @@ +1,2,3 \ No newline at end of file From 979c3de4271befddac30fc9afbd6a512d24eb155 Mon Sep 17 00:00:00 2001 From: Zachary-Squires Date: Tue, 30 Sep 2025 17:35:04 -0400 Subject: [PATCH 07/72] Added the commit from 1488 called "feat(security): centralize DOMPurify sanitizer and patch XSS in success/failure responses" --- docker-compose.yml | 0 src/scripts/backupScript.sh | 0 src/scripts/checkHeader.sh | 0 src/scripts/checkMeters.sh | 0 src/scripts/checkTypescript.sh | 0 src/scripts/devcheck.sh | 0 src/scripts/devstart.sh | 0 src/scripts/installOED.sh | 0 src/scripts/sendLogEmailCron.bash | 0 src/scripts/updateEgaugeMetersOEDCron.bash | 0 src/scripts/updateMamacMetersOEDCron.bash | 0 src/scripts/updateOED.sh | 0 src/server/migrations/scripts/dumpDatabase.sh | 0 src/server/migrations/scripts/restoreDatabase.sh | 0 src/server/test/rateTesting.sh | 0 15 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 docker-compose.yml mode change 100755 => 100644 src/scripts/backupScript.sh mode change 100755 => 100644 src/scripts/checkHeader.sh mode change 100755 => 100644 src/scripts/checkMeters.sh mode change 100755 => 100644 src/scripts/checkTypescript.sh mode change 100755 => 100644 src/scripts/devcheck.sh mode change 100755 => 100644 src/scripts/devstart.sh mode change 100755 => 100644 src/scripts/installOED.sh mode change 100755 => 100644 src/scripts/sendLogEmailCron.bash mode change 100755 => 100644 src/scripts/updateEgaugeMetersOEDCron.bash mode change 100755 => 100644 src/scripts/updateMamacMetersOEDCron.bash mode change 100755 => 100644 src/scripts/updateOED.sh mode change 100755 => 100644 src/server/migrations/scripts/dumpDatabase.sh mode change 100755 => 100644 src/server/migrations/scripts/restoreDatabase.sh mode change 100755 => 100644 src/server/test/rateTesting.sh diff --git a/docker-compose.yml b/docker-compose.yml old mode 100755 new mode 100644 diff --git a/src/scripts/backupScript.sh b/src/scripts/backupScript.sh old mode 100755 new mode 100644 diff --git a/src/scripts/checkHeader.sh b/src/scripts/checkHeader.sh old mode 100755 new mode 100644 diff --git a/src/scripts/checkMeters.sh b/src/scripts/checkMeters.sh old mode 100755 new mode 100644 diff --git a/src/scripts/checkTypescript.sh b/src/scripts/checkTypescript.sh old mode 100755 new mode 100644 diff --git a/src/scripts/devcheck.sh b/src/scripts/devcheck.sh old mode 100755 new mode 100644 diff --git a/src/scripts/devstart.sh b/src/scripts/devstart.sh old mode 100755 new mode 100644 diff --git a/src/scripts/installOED.sh b/src/scripts/installOED.sh old mode 100755 new mode 100644 diff --git a/src/scripts/sendLogEmailCron.bash b/src/scripts/sendLogEmailCron.bash old mode 100755 new mode 100644 diff --git a/src/scripts/updateEgaugeMetersOEDCron.bash b/src/scripts/updateEgaugeMetersOEDCron.bash old mode 100755 new mode 100644 diff --git a/src/scripts/updateMamacMetersOEDCron.bash b/src/scripts/updateMamacMetersOEDCron.bash old mode 100755 new mode 100644 diff --git a/src/scripts/updateOED.sh b/src/scripts/updateOED.sh old mode 100755 new mode 100644 diff --git a/src/server/migrations/scripts/dumpDatabase.sh b/src/server/migrations/scripts/dumpDatabase.sh old mode 100755 new mode 100644 diff --git a/src/server/migrations/scripts/restoreDatabase.sh b/src/server/migrations/scripts/restoreDatabase.sh old mode 100755 new mode 100644 diff --git a/src/server/test/rateTesting.sh b/src/server/test/rateTesting.sh old mode 100755 new mode 100644 From 7cfcb790f7278fdc807a81afd9a9c246aea62628 Mon Sep 17 00:00:00 2001 From: Landon Date: Mon, 7 Jul 2025 18:03:38 -0400 Subject: [PATCH 08/72] fix: resolved PR feedback on sanitizer imports and tests --- :w | 8 --- package.json | 4 +- pull_request_template.md | 14 ++---- src/server/routes/response.js | 44 ++++++++-------- src/server/services/csvPipeline/success.js | 58 ++++++++++------------ src/server/test/crossSite.js | 38 -------------- 6 files changed, 56 insertions(+), 110 deletions(-) delete mode 100644 :w delete mode 100644 src/server/test/crossSite.js diff --git a/:w b/:w deleted file mode 100644 index f5b249f20..000000000 --- a/:w +++ /dev/null @@ -1,8 +0,0 @@ -const { JSDOM } = require('jsdom'); -const createDOMPurify = require('dompurify'); - -const window = new JSDOM('').window; -const DOMPurify = createDOMPurify(window); - -module.exports = DOMPurify; - diff --git a/package.json b/package.json index 6f1eac951..12b1b80f3 100644 --- a/package.json +++ b/package.json @@ -67,14 +67,14 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", - "dompurify": "~2.4.0", + "dompurify": "~3.2.6", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", "express-rate-limit": "~7.2.0", "history": "~5.3.0", "ini": "~4.1.3", - "jsdom": "~21.1.0", + "jsdom": "~26.1.0", "jsonschema": "~1.4.1", "jsonwebtoken": "~9.0.0", "lodash": "~4.17.21", diff --git a/pull_request_template.md b/pull_request_template.md index 2555a2943..317dc640e 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,12 +1,10 @@ # Description -Implemented DOMPurify to sanitize HTML responses from response.js and success.js. Created sanitizer.js as a centralized, pre-configured instance of DOMPurify on the server. This also allows for implementation of sanitization elsewhere if needed in the future. These changes remediate a cross-site scripting vulnerability when users upload meter data. +(Please include a summary of the change and which issue is touched on. Please also include relevant motivation and context.) -Fixes problem report 3 from the penetration test report. +Fixes #[issue] -Developed and implemented by: -Thomas Nigro - https://github.com/tnigro45 -Landon Wivell - https://github.com/Landon-Wivell +(In general, OED likes to have at least one issue associated with each pull request. Replace [issue] with the OED GitHub issue number. In the preview you will see an issue description if you hover over that number. You can create one yourself before doing this pull request. This is where details are normally given on what is being addressed. Note you should not use the word "Fixes" if it does not completely address the issue since the issue would automatically be closed on merging the pull request. In that case use "Partly Addresses #[issue].) ## Type of change @@ -19,12 +17,10 @@ Landon Wivell - https://github.com/Landon-Wivell (Note what you have done by placing an "x" instead of the space in the [ ] so it becomes [x]. It is hoped you do all of them.) -- [x] I have followed the [OED pull request](https://openenergydashboard.org/developer/pr/) ideas +- [ ] I have followed the [OED pull request](https://openenergydashboard.org/developer/pr/) ideas - [ ] I have removed text in ( ) from the issue request -- [x] You acknowledge that every person contributing to this work has signed the [OED Contributing License Agreement](https://openenergydashboard.org/developer/cla/) and each author is listed in the Description section. +- [ ] You acknowledge that every person contributing to this work has signed the [OED Contributing License Agreement](https://openenergydashboard.org/developer/cla/) and each author is listed in the Description section. ## Limitations -N/A - (Describe any issues that remain or work that should still be done.) \ No newline at end of file diff --git a/src/server/routes/response.js b/src/server/routes/response.js index 82077dd67..2c4583157 100644 --- a/src/server/routes/response.js +++ b/src/server/routes/response.js @@ -6,27 +6,27 @@ const DOMPurify = require('../services/utils/sanitizer'); - /** - * Inform the client of a success (200 OK) with sanitized content. - * - * @param {express.Response} res - The Express response object. - * @param {string} comment - Any additional data to be returned to the client as a string. - */ - function success(res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); +/** + * Inform the client of a success (200 OK) with sanitized content. + * + * @param {express.Response} res The Express response object. + * @param {string} comment Any additional data to be returned to the client as a string. + */ +function success(res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(200).send(safeComment); - } - - /** - * Inform the client of a failure with provided code or 500, using sanitized content. - * - * @param {express.Response} res - The Express response object. - * @param {number} code - The code number to send back for the request. - * @param {string} comment - Any additional data to be returned to the client as a string. - */ - function failure(res, code = 500, comment = '') { - const safeComment = DOMPurify.sanitize(comment); +} + +/** + * Inform the client of a failure with provided code or 500, using sanitized content. + * + * @param {express.Response} res The Express response object. + * @param {number} code The code number to send back for the request. + * @param {string} comment Any additional data to be returned to the client as a string. + */ +function failure(res, code = 500, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(code).send(safeComment); - } - - module.exports = { success, failure }; +} + +module.exports = { success, failure }; diff --git a/src/server/services/csvPipeline/success.js b/src/server/services/csvPipeline/success.js index 004b2a4ed..852f6dabd 100644 --- a/src/server/services/csvPipeline/success.js +++ b/src/server/services/csvPipeline/success.js @@ -2,37 +2,33 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - const express = require('express') /* needed to resolve types in JSDoc comments */ +const express = require('express'); +const DOMPurify = require('../utils/sanitizer'); - const DOMPurify = require('../utils/sanitizer'); - - /** - * Inform the client of a success (200 OK) with sanitized HTML content. - * - * @param {express.Request} req - The Express request object. - * @param {express.Response} res - The Express response object. - * @param {string} comment - Any additional data to be returned to the client. - */ - function success(req, res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); +/** + * Inform the client of a success (200 OK) with sanitized HTML content. + * + * @param {express.Request} req The Express request object. + * @param {express.Response} res The Express response object. + * @param {string} comment Any additional data to be returned to the client. + */ +function success(req, res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); res.status(200).send(`

SUCCESS

${safeComment}`); - } - - - /** - * Inform the client of a failure (400 OK) with sanitized HTML content. - * - * @param {express.Request} req - The Express request object. - * @param {express.Response} res - The Express response object. - * @param {string} comment - Any additional data to be returned to the client. - */ - function failure(req, res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); +} + + +/** + * Inform the client of a failure (400 OK) with sanitized HTML content. + * + * @param {express.Request} req The Express request object. + * @param {express.Response} res The Express response object. + * @param {string} comment Any additional data to be returned to the client. + */ +function failure(req, res, comment = '') { + const safeComment = DOMPurify.sanitize(comment); + // 400 is client error. There is a small chance the insert into the DB failed + // but overlooking that. res.status(400).send(`

FAILURE

${safeComment}`); - } - - - module.exports = { success, failure }; - - - \ No newline at end of file +} +module.exports = { success, failure }; \ No newline at end of file diff --git a/src/server/test/crossSite.js b/src/server/test/crossSite.js deleted file mode 100644 index 3143b9b17..000000000 --- a/src/server/test/crossSite.js +++ /dev/null @@ -1,38 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This file tests ???. */ - -// Run in OED Docker web container terminal/shell: npm run testsome src/server/test/crossSite.js - -const { chai, mocha, expect, app, testUser } = require('./common'); - -mocha.describe('Cross site', () => { - mocha.it('test 1', async () => { - const res = await chai.request(app).post('/api/csv/readings') - .field('email', testUser.username) - .field('password', testUser.password) - .field('meterName', "<img src/onerror=alert(document.domain)>") - .field('timeSort', "increasing") - .field('duplications', "") - .field('cumulative', "no") - .field('cumulativeReset', "yes") - .field('cumulativeResetStart', "") - .field('cumulativeResetEnd', "") - .field('lengthGap', "") - .field('lengthVariation', "") - .field('endOnly', "no") - .field('gzip', "no") - .field('headerRow', "yes") - .field('refreshReadings', "no") - .field('update', "no") - .field('honorDst', "no") - .field('relaxedParsing', "no") - .field('csvfile', "file") - .attach('CSV', 'src/server/test/something.csv'); - console.log('res.text: ', res.text); - // expect(res).to.have.status(500); should be a 500 code after all fields are inputed - //expect(res).to.be.json; - }); -}); From e642e0ec0f994c65ac8db3426bf55f48c6bfb80d Mon Sep 17 00:00:00 2001 From: Zachary-Squires Date: Tue, 30 Sep 2025 18:07:59 -0400 Subject: [PATCH 09/72] Ran npm install, should bring jsdom and dompurify up to date. package-lock.json and package.json should match now. --- package-lock.json | 460 ++++++++++++++++++++++++---------------------- 1 file changed, 245 insertions(+), 215 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69a788536..f30b6cc83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,14 +18,14 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", - "dompurify": "~2.4.0", + "dompurify": "~3.2.6", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", "express-rate-limit": "~7.2.0", "history": "~5.3.0", "ini": "~4.1.3", - "jsdom": "~21.1.0", + "jsdom": "~26.1.0", "jsonschema": "~1.4.1", "jsonwebtoken": "~9.0.0", "lodash": "~4.17.21", @@ -100,6 +100,18 @@ "node": ">=0.10.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", @@ -188,6 +200,111 @@ "findup": "bin/findup.js" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -913,14 +1030,6 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, "node_modules/@turf/area": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.5.0.tgz", @@ -1383,6 +1492,12 @@ "@types/node": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -1848,7 +1963,8 @@ "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true }, "node_modules/abbrev": { "version": "1.1.1", @@ -1877,6 +1993,7 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1884,15 +2001,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, "node_modules/acorn-import-assertions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", @@ -1911,26 +2019,12 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/ajv": { @@ -3268,14 +3362,15 @@ } }, "node_modules/cssstyle": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", - "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dependencies": { - "rrweb-cssom": "^0.6.0" + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/csstype": { @@ -3842,16 +3937,15 @@ } }, "node_modules/data-urls": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", - "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/debug": { @@ -4090,22 +4184,13 @@ "url": "https://bevry.me/fund" } }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/dompurify": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.9.tgz", - "integrity": "sha512-iHtnxYMotKgOTvxIqq677JsKHvCOkAFqj9x8Mek2zdeHW1XjuFKwjpmZeMaXQRQ8AbJZDbcRz/+r1QhwvFtmQg==" + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/dotenv": { "version": "16.4.5", @@ -4774,6 +4859,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -5834,14 +5920,14 @@ "integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==" }, "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dependencies": { - "whatwg-encoding": "^2.0.0" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/http-errors": { @@ -5860,16 +5946,15 @@ } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/https-browserify": { @@ -5879,15 +5964,15 @@ "dev": true }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/iconv-lite": { @@ -6468,42 +6553,36 @@ } }, "node_modules/jsdom": { - "version": "21.1.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.2.tgz", - "integrity": "sha512-sCpFmK2jv+1sjff4u7fzft+pUh2KSUbUrEHYHyfSIbGTIcmnjyp83qg6qLwdJ/I3LpTXx33ACxeRL7Lsyc6lGQ==", - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", - "cssstyle": "^3.0.0", - "data-urls": "^4.0.0", - "decimal.js": "^10.4.3", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.4", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.6.0", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.1", - "ws": "^8.13.0", - "xml-name-validator": "^4.0.0" + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { - "canvas": "^2.5.0" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -6511,35 +6590,6 @@ } } }, - "node_modules/jsdom/node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/jsdom/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -6856,6 +6906,11 @@ "get-func-name": "^2.0.1" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, "node_modules/make-event-props": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-1.6.2.tgz", @@ -8448,17 +8503,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -8543,11 +8587,6 @@ "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9142,11 +9181,6 @@ "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, "node_modules/reselect": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz", @@ -9251,9 +9285,9 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==" }, "node_modules/run-parallel": { "version": "1.2.0", @@ -10182,6 +10216,22 @@ "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -10249,28 +10299,25 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dependencies": { - "punycode": "^2.3.0" + "punycode": "^2.3.1" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/ts-api-utils": { @@ -10484,14 +10531,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -10567,15 +10606,6 @@ "qs": "^6.11.2" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -10691,14 +10721,14 @@ } }, "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dependencies": { - "xml-name-validator": "^4.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/watchpack": { @@ -10900,14 +10930,14 @@ } }, "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dependencies": { "iconv-lite": "0.6.3" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/whatwg-encoding/node_modules/iconv-lite": { @@ -10922,23 +10952,23 @@ } }, "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dependencies": { - "tr46": "^4.1.1", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/which": { @@ -11079,11 +11109,11 @@ } }, "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/xml2js": { From 7cb2438ba8feb29ebe19e2aa7c6cc9b3f775e1f4 Mon Sep 17 00:00:00 2001 From: Zachary-Squires Date: Tue, 7 Oct 2025 17:18:33 -0400 Subject: [PATCH 10/72] Restore executable permissions on scripts and binaries --- src/scripts/backupScript.sh | 0 src/scripts/checkHeader.sh | 0 src/scripts/checkMeters.sh | 0 src/scripts/checkTypescript.sh | 0 src/scripts/devcheck.sh | 0 src/scripts/devstart.sh | 0 src/scripts/installOED.sh | 0 src/scripts/updateOED.sh | 0 src/server/migrations/scripts/dumpDatabase.sh | 0 src/server/migrations/scripts/restoreDatabase.sh | 0 src/server/test/rateTesting.sh | 0 11 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/scripts/backupScript.sh mode change 100644 => 100755 src/scripts/checkHeader.sh mode change 100644 => 100755 src/scripts/checkMeters.sh mode change 100644 => 100755 src/scripts/checkTypescript.sh mode change 100644 => 100755 src/scripts/devcheck.sh mode change 100644 => 100755 src/scripts/devstart.sh mode change 100644 => 100755 src/scripts/installOED.sh mode change 100644 => 100755 src/scripts/updateOED.sh mode change 100644 => 100755 src/server/migrations/scripts/dumpDatabase.sh mode change 100644 => 100755 src/server/migrations/scripts/restoreDatabase.sh mode change 100644 => 100755 src/server/test/rateTesting.sh diff --git a/src/scripts/backupScript.sh b/src/scripts/backupScript.sh old mode 100644 new mode 100755 diff --git a/src/scripts/checkHeader.sh b/src/scripts/checkHeader.sh old mode 100644 new mode 100755 diff --git a/src/scripts/checkMeters.sh b/src/scripts/checkMeters.sh old mode 100644 new mode 100755 diff --git a/src/scripts/checkTypescript.sh b/src/scripts/checkTypescript.sh old mode 100644 new mode 100755 diff --git a/src/scripts/devcheck.sh b/src/scripts/devcheck.sh old mode 100644 new mode 100755 diff --git a/src/scripts/devstart.sh b/src/scripts/devstart.sh old mode 100644 new mode 100755 diff --git a/src/scripts/installOED.sh b/src/scripts/installOED.sh old mode 100644 new mode 100755 diff --git a/src/scripts/updateOED.sh b/src/scripts/updateOED.sh old mode 100644 new mode 100755 diff --git a/src/server/migrations/scripts/dumpDatabase.sh b/src/server/migrations/scripts/dumpDatabase.sh old mode 100644 new mode 100755 diff --git a/src/server/migrations/scripts/restoreDatabase.sh b/src/server/migrations/scripts/restoreDatabase.sh old mode 100644 new mode 100755 diff --git a/src/server/test/rateTesting.sh b/src/server/test/rateTesting.sh old mode 100644 new mode 100755 From c24d3c43be0b5be6d929eb6ced4034338abca548 Mon Sep 17 00:00:00 2001 From: Zachary-Squires Date: Tue, 7 Oct 2025 18:07:20 -0400 Subject: [PATCH 11/72] Normalize all line endings to LF --- .gitattributes | 6 +- ...5-20_mu_kWh_gu_kWh_st_-inf_et_inf_bd_7.csv | 22 +-- ..._ri_15_mu_kW_gu_kW_st_-inf_et_inf_bd_1.csv | 152 +++++++++--------- ..._15_mu_kWh_gu_kWh_st_-inf_et_inf_bd_76.csv | 2 +- ...15_mu_kWh_gu_kgCO2_st_-inf_et_inf_bd_1.csv | 152 +++++++++--------- ...i_15-20_mu_kWh_gu_kgCO2_st_-inf_et_inf.csv | 152 +++++++++--------- 6 files changed, 245 insertions(+), 241 deletions(-) diff --git a/.gitattributes b/.gitattributes index 198a7c455..b6bbb7711 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,4 +14,8 @@ *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain -*.RTF diff=astextplain \ No newline at end of file +*.RTF diff=astextplain +*.sh text eol=lf +*.js text eol=lf +*.ts text eol=lf +*.json text eol=lf \ No newline at end of file diff --git a/src/server/test/web/readingsData/expected_bar_group_ri_15-20_mu_kWh_gu_kWh_st_-inf_et_inf_bd_7.csv b/src/server/test/web/readingsData/expected_bar_group_ri_15-20_mu_kWh_gu_kWh_st_-inf_et_inf_bd_7.csv index 04befedd0..145260dc2 100644 --- a/src/server/test/web/readingsData/expected_bar_group_ri_15-20_mu_kWh_gu_kWh_st_-inf_et_inf_bd_7.csv +++ b/src/server/test/web/readingsData/expected_bar_group_ri_15-20_mu_kWh_gu_kWh_st_-inf_et_inf_bd_7.csv @@ -1,11 +1,11 @@ -reading,start time,end time -57744.5416303119,2022-08-23 00:00:00,2022-08-30 00:00:00 -58457.1956496669,2022-08-30 00:00:00,2022-09-06 00:00:00 -59276.647957841,2022-09-06 00:00:00,2022-09-13 00:00:00 -58879.1789750532,2022-09-13 00:00:00,2022-09-20 00:00:00 -58647.346330365,2022-09-20 00:00:00,2022-09-27 00:00:00 -58324.4920477014,2022-09-27 00:00:00,2022-10-04 00:00:00 -58177.8818425628,2022-10-04 00:00:00,2022-10-11 00:00:00 -58573.7982237878,2022-10-11 00:00:00,2022-10-18 00:00:00 -58642.2652124796,2022-10-18 00:00:00,2022-10-25 00:00:00 -58818.8114100034,2022-10-25 00:00:00,2022-11-01 00:00:00 +reading,start time,end time +57744.5416303119,2022-08-23 00:00:00,2022-08-30 00:00:00 +58457.1956496669,2022-08-30 00:00:00,2022-09-06 00:00:00 +59276.647957841,2022-09-06 00:00:00,2022-09-13 00:00:00 +58879.1789750532,2022-09-13 00:00:00,2022-09-20 00:00:00 +58647.346330365,2022-09-20 00:00:00,2022-09-27 00:00:00 +58324.4920477014,2022-09-27 00:00:00,2022-10-04 00:00:00 +58177.8818425628,2022-10-04 00:00:00,2022-10-11 00:00:00 +58573.7982237878,2022-10-11 00:00:00,2022-10-18 00:00:00 +58642.2652124796,2022-10-18 00:00:00,2022-10-25 00:00:00 +58818.8114100034,2022-10-25 00:00:00,2022-11-01 00:00:00 diff --git a/src/server/test/web/readingsData/expected_bar_ri_15_mu_kW_gu_kW_st_-inf_et_inf_bd_1.csv b/src/server/test/web/readingsData/expected_bar_ri_15_mu_kW_gu_kW_st_-inf_et_inf_bd_1.csv index cbf8834e3..548120624 100644 --- a/src/server/test/web/readingsData/expected_bar_ri_15_mu_kW_gu_kW_st_-inf_et_inf_bd_1.csv +++ b/src/server/test/web/readingsData/expected_bar_ri_15_mu_kW_gu_kW_st_-inf_et_inf_bd_1.csv @@ -1,76 +1,76 @@ -reading,start time,end time -1244.4732821245,2022-08-18 00:00:00,2022-08-19 00:00:00 -988.315804920483,2022-08-19 00:00:00,2022-08-20 00:00:00 -1229.16913704567,2022-08-20 00:00:00,2022-08-21 00:00:00 -1214.90679769041,2022-08-21 00:00:00,2022-08-22 00:00:00 -1141.42617179595,2022-08-22 00:00:00,2022-08-23 00:00:00 -1173.22097628136,2022-08-23 00:00:00,2022-08-24 00:00:00 -1172.76622813581,2022-08-24 00:00:00,2022-08-25 00:00:00 -1217.29080157992,2022-08-25 00:00:00,2022-08-26 00:00:00 -1078.14650822547,2022-08-26 00:00:00,2022-08-27 00:00:00 -1195.102845216,2022-08-27 00:00:00,2022-08-28 00:00:00 -1156.49973049494,2022-08-28 00:00:00,2022-08-29 00:00:00 -1112.47501131559,2022-08-29 00:00:00,2022-08-30 00:00:00 -1192.86644903414,2022-08-30 00:00:00,2022-08-31 00:00:00 -1161.02765232201,2022-08-31 00:00:00,2022-09-01 00:00:00 -1152.18197026441,2022-09-01 00:00:00,2022-09-02 00:00:00 -1272.45260726068,2022-09-02 00:00:00,2022-09-03 00:00:00 -1217.82966175226,2022-09-03 00:00:00,2022-09-04 00:00:00 -1176.84658742283,2022-09-04 00:00:00,2022-09-05 00:00:00 -1125.57284174782,2022-09-05 00:00:00,2022-09-06 00:00:00 -1194.22329876519,2022-09-06 00:00:00,2022-09-07 00:00:00 -1171.71190809283,2022-09-07 00:00:00,2022-09-08 00:00:00 -1130.54158593037,2022-09-08 00:00:00,2022-09-09 00:00:00 -1236.92056317258,2022-09-09 00:00:00,2022-09-10 00:00:00 -1142.10449794792,2022-09-10 00:00:00,2022-09-11 00:00:00 -1340.16170552738,2022-09-11 00:00:00,2022-09-12 00:00:00 -1182.05404123472,2022-09-12 00:00:00,2022-09-13 00:00:00 -1248.84451712363,2022-09-13 00:00:00,2022-09-14 00:00:00 -1137.33538285512,2022-09-14 00:00:00,2022-09-15 00:00:00 -1221.31552272137,2022-09-15 00:00:00,2022-09-16 00:00:00 -1143.33626142129,2022-09-16 00:00:00,2022-09-17 00:00:00 -1225.3967032627,2022-09-17 00:00:00,2022-09-18 00:00:00 -1286.93091007234,2022-09-18 00:00:00,2022-09-19 00:00:00 -1110.56503545471,2022-09-19 00:00:00,2022-09-20 00:00:00 -1340.33530846461,2022-09-20 00:00:00,2022-09-21 00:00:00 -1123.21972183704,2022-09-21 00:00:00,2022-09-22 00:00:00 -1249.65113803395,2022-09-22 00:00:00,2022-09-23 00:00:00 -1131.23559326555,2022-09-23 00:00:00,2022-09-24 00:00:00 -1142.76238111977,2022-09-24 00:00:00,2022-09-25 00:00:00 -1210.92514071449,2022-09-25 00:00:00,2022-09-26 00:00:00 -1150.76698467275,2022-09-26 00:00:00,2022-09-27 00:00:00 -1152.78225650689,2022-09-27 00:00:00,2022-09-28 00:00:00 -1215.20127016486,2022-09-28 00:00:00,2022-09-29 00:00:00 -1273.79854318597,2022-09-29 00:00:00,2022-09-30 00:00:00 -1198.53270778934,2022-09-30 00:00:00,2022-10-01 00:00:00 -1165.80837300627,2022-10-01 00:00:00,2022-10-02 00:00:00 -1180.22585254585,2022-10-02 00:00:00,2022-10-03 00:00:00 -1112.62184179863,2022-10-03 00:00:00,2022-10-04 00:00:00 -1217.71687161273,2022-10-04 00:00:00,2022-10-05 00:00:00 -1199.38826568772,2022-10-05 00:00:00,2022-10-06 00:00:00 -1160.1762157385,2022-10-06 00:00:00,2022-10-07 00:00:00 -1269.25906753997,2022-10-07 00:00:00,2022-10-08 00:00:00 -1167.40579266569,2022-10-08 00:00:00,2022-10-09 00:00:00 -1058.18319043406,2022-10-09 00:00:00,2022-10-10 00:00:00 -1211.93520880573,2022-10-10 00:00:00,2022-10-11 00:00:00 -1152.7344011911,2022-10-11 00:00:00,2022-10-12 00:00:00 -1259.0249731428,2022-10-12 00:00:00,2022-10-13 00:00:00 -1192.12826970549,2022-10-13 00:00:00,2022-10-14 00:00:00 -1208.21671287911,2022-10-14 00:00:00,2022-10-15 00:00:00 -1155.22102759493,2022-10-15 00:00:00,2022-10-16 00:00:00 -1178.49984441914,2022-10-16 00:00:00,2022-10-17 00:00:00 -1221.29509976931,2022-10-17 00:00:00,2022-10-18 00:00:00 -1162.54298706786,2022-10-18 00:00:00,2022-10-19 00:00:00 -1128.36869126957,2022-10-19 00:00:00,2022-10-20 00:00:00 -1162.34721422175,2022-10-20 00:00:00,2022-10-21 00:00:00 -1243.61079923689,2022-10-21 00:00:00,2022-10-22 00:00:00 -1187.32006552077,2022-10-22 00:00:00,2022-10-23 00:00:00 -1203.7441556701,2022-10-23 00:00:00,2022-10-24 00:00:00 -1223.62767515703,2022-10-24 00:00:00,2022-10-25 00:00:00 -1347.85241257491,2022-10-25 00:00:00,2022-10-26 00:00:00 -1135.57752734976,2022-10-26 00:00:00,2022-10-27 00:00:00 -1091.91787954098,2022-10-27 00:00:00,2022-10-28 00:00:00 -1203.63545481652,2022-10-28 00:00:00,2022-10-29 00:00:00 -1349.13987250313,2022-10-29 00:00:00,2022-10-30 00:00:00 -1210.55315436926,2022-10-30 00:00:00,2022-10-31 00:00:00 -1072.65000056083,2022-10-31 00:00:00,2022-11-01 00:00:00 +reading,start time,end time +1244.4732821245,2022-08-18 00:00:00,2022-08-19 00:00:00 +988.315804920483,2022-08-19 00:00:00,2022-08-20 00:00:00 +1229.16913704567,2022-08-20 00:00:00,2022-08-21 00:00:00 +1214.90679769041,2022-08-21 00:00:00,2022-08-22 00:00:00 +1141.42617179595,2022-08-22 00:00:00,2022-08-23 00:00:00 +1173.22097628136,2022-08-23 00:00:00,2022-08-24 00:00:00 +1172.76622813581,2022-08-24 00:00:00,2022-08-25 00:00:00 +1217.29080157992,2022-08-25 00:00:00,2022-08-26 00:00:00 +1078.14650822547,2022-08-26 00:00:00,2022-08-27 00:00:00 +1195.102845216,2022-08-27 00:00:00,2022-08-28 00:00:00 +1156.49973049494,2022-08-28 00:00:00,2022-08-29 00:00:00 +1112.47501131559,2022-08-29 00:00:00,2022-08-30 00:00:00 +1192.86644903414,2022-08-30 00:00:00,2022-08-31 00:00:00 +1161.02765232201,2022-08-31 00:00:00,2022-09-01 00:00:00 +1152.18197026441,2022-09-01 00:00:00,2022-09-02 00:00:00 +1272.45260726068,2022-09-02 00:00:00,2022-09-03 00:00:00 +1217.82966175226,2022-09-03 00:00:00,2022-09-04 00:00:00 +1176.84658742283,2022-09-04 00:00:00,2022-09-05 00:00:00 +1125.57284174782,2022-09-05 00:00:00,2022-09-06 00:00:00 +1194.22329876519,2022-09-06 00:00:00,2022-09-07 00:00:00 +1171.71190809283,2022-09-07 00:00:00,2022-09-08 00:00:00 +1130.54158593037,2022-09-08 00:00:00,2022-09-09 00:00:00 +1236.92056317258,2022-09-09 00:00:00,2022-09-10 00:00:00 +1142.10449794792,2022-09-10 00:00:00,2022-09-11 00:00:00 +1340.16170552738,2022-09-11 00:00:00,2022-09-12 00:00:00 +1182.05404123472,2022-09-12 00:00:00,2022-09-13 00:00:00 +1248.84451712363,2022-09-13 00:00:00,2022-09-14 00:00:00 +1137.33538285512,2022-09-14 00:00:00,2022-09-15 00:00:00 +1221.31552272137,2022-09-15 00:00:00,2022-09-16 00:00:00 +1143.33626142129,2022-09-16 00:00:00,2022-09-17 00:00:00 +1225.3967032627,2022-09-17 00:00:00,2022-09-18 00:00:00 +1286.93091007234,2022-09-18 00:00:00,2022-09-19 00:00:00 +1110.56503545471,2022-09-19 00:00:00,2022-09-20 00:00:00 +1340.33530846461,2022-09-20 00:00:00,2022-09-21 00:00:00 +1123.21972183704,2022-09-21 00:00:00,2022-09-22 00:00:00 +1249.65113803395,2022-09-22 00:00:00,2022-09-23 00:00:00 +1131.23559326555,2022-09-23 00:00:00,2022-09-24 00:00:00 +1142.76238111977,2022-09-24 00:00:00,2022-09-25 00:00:00 +1210.92514071449,2022-09-25 00:00:00,2022-09-26 00:00:00 +1150.76698467275,2022-09-26 00:00:00,2022-09-27 00:00:00 +1152.78225650689,2022-09-27 00:00:00,2022-09-28 00:00:00 +1215.20127016486,2022-09-28 00:00:00,2022-09-29 00:00:00 +1273.79854318597,2022-09-29 00:00:00,2022-09-30 00:00:00 +1198.53270778934,2022-09-30 00:00:00,2022-10-01 00:00:00 +1165.80837300627,2022-10-01 00:00:00,2022-10-02 00:00:00 +1180.22585254585,2022-10-02 00:00:00,2022-10-03 00:00:00 +1112.62184179863,2022-10-03 00:00:00,2022-10-04 00:00:00 +1217.71687161273,2022-10-04 00:00:00,2022-10-05 00:00:00 +1199.38826568772,2022-10-05 00:00:00,2022-10-06 00:00:00 +1160.1762157385,2022-10-06 00:00:00,2022-10-07 00:00:00 +1269.25906753997,2022-10-07 00:00:00,2022-10-08 00:00:00 +1167.40579266569,2022-10-08 00:00:00,2022-10-09 00:00:00 +1058.18319043406,2022-10-09 00:00:00,2022-10-10 00:00:00 +1211.93520880573,2022-10-10 00:00:00,2022-10-11 00:00:00 +1152.7344011911,2022-10-11 00:00:00,2022-10-12 00:00:00 +1259.0249731428,2022-10-12 00:00:00,2022-10-13 00:00:00 +1192.12826970549,2022-10-13 00:00:00,2022-10-14 00:00:00 +1208.21671287911,2022-10-14 00:00:00,2022-10-15 00:00:00 +1155.22102759493,2022-10-15 00:00:00,2022-10-16 00:00:00 +1178.49984441914,2022-10-16 00:00:00,2022-10-17 00:00:00 +1221.29509976931,2022-10-17 00:00:00,2022-10-18 00:00:00 +1162.54298706786,2022-10-18 00:00:00,2022-10-19 00:00:00 +1128.36869126957,2022-10-19 00:00:00,2022-10-20 00:00:00 +1162.34721422175,2022-10-20 00:00:00,2022-10-21 00:00:00 +1243.61079923689,2022-10-21 00:00:00,2022-10-22 00:00:00 +1187.32006552077,2022-10-22 00:00:00,2022-10-23 00:00:00 +1203.7441556701,2022-10-23 00:00:00,2022-10-24 00:00:00 +1223.62767515703,2022-10-24 00:00:00,2022-10-25 00:00:00 +1347.85241257491,2022-10-25 00:00:00,2022-10-26 00:00:00 +1135.57752734976,2022-10-26 00:00:00,2022-10-27 00:00:00 +1091.91787954098,2022-10-27 00:00:00,2022-10-28 00:00:00 +1203.63545481652,2022-10-28 00:00:00,2022-10-29 00:00:00 +1349.13987250313,2022-10-29 00:00:00,2022-10-30 00:00:00 +1210.55315436926,2022-10-30 00:00:00,2022-10-31 00:00:00 +1072.65000056083,2022-10-31 00:00:00,2022-11-01 00:00:00 diff --git a/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kWh_st_-inf_et_inf_bd_76.csv b/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kWh_st_-inf_et_inf_bd_76.csv index f3f6b539f..d33df9d25 100644 --- a/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kWh_st_-inf_et_inf_bd_76.csv +++ b/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kWh_st_-inf_et_inf_bd_76.csv @@ -1 +1 @@ -reading,start time,end time +reading,start time,end time diff --git a/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kgCO2_st_-inf_et_inf_bd_1.csv b/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kgCO2_st_-inf_et_inf_bd_1.csv index 9ae94a6d4..4fd725d25 100644 --- a/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kgCO2_st_-inf_et_inf_bd_1.csv +++ b/src/server/test/web/readingsData/expected_bar_ri_15_mu_kWh_gu_kgCO2_st_-inf_et_inf_bd_1.csv @@ -1,76 +1,76 @@ -reading,start time,end time -3529.32622810508,2022-08-18 00:00:00,2022-08-19 00:00:00 -2802.86362275449,2022-08-19 00:00:00,2022-08-20 00:00:00 -3485.92367266153,2022-08-20 00:00:00,2022-08-21 00:00:00 -3445.47567824999,2022-08-21 00:00:00,2022-08-22 00:00:00 -3237.08462321332,2022-08-22 00:00:00,2022-08-23 00:00:00 -3327.25468873395,2022-08-23 00:00:00,2022-08-24 00:00:00 -3325.96502299317,2022-08-24 00:00:00,2022-08-25 00:00:00 -3452.23671328065,2022-08-25 00:00:00,2022-08-26 00:00:00 -3057.62349732744,2022-08-26 00:00:00,2022-08-27 00:00:00 -3389.31166903257,2022-08-27 00:00:00,2022-08-28 00:00:00 -3279.83323568366,2022-08-28 00:00:00,2022-08-29 00:00:00 -3154.97913209101,2022-08-29 00:00:00,2022-08-30 00:00:00 -3382.96924946082,2022-08-30 00:00:00,2022-08-31 00:00:00 -3292.67442198523,2022-08-31 00:00:00,2022-09-01 00:00:00 -3267.58806766986,2022-09-01 00:00:00,2022-09-02 00:00:00 -3608.67559419129,2022-09-02 00:00:00,2022-09-03 00:00:00 -3453.76492072941,2022-09-03 00:00:00,2022-09-04 00:00:00 -3337.53692193113,2022-09-04 00:00:00,2022-09-05 00:00:00 -3192.12457919682,2022-09-05 00:00:00,2022-09-06 00:00:00 -3386.81727529807,2022-09-06 00:00:00,2022-09-07 00:00:00 -3322.97497135126,2022-09-07 00:00:00,2022-09-08 00:00:00 -3206.21593769853,2022-09-08 00:00:00,2022-09-09 00:00:00 -3507.90671715743,2022-09-09 00:00:00,2022-09-10 00:00:00 -3239.0083561803,2022-09-10 00:00:00,2022-09-11 00:00:00 -3800.69859687566,2022-09-11 00:00:00,2022-09-12 00:00:00 -3352.30526094167,2022-09-12 00:00:00,2022-09-13 00:00:00 -3541.72305056262,2022-09-13 00:00:00,2022-09-14 00:00:00 -3225.48314577712,2022-09-14 00:00:00,2022-09-15 00:00:00 -3463.6508224378,2022-09-15 00:00:00,2022-09-16 00:00:00 -3242.50163739077,2022-09-16 00:00:00,2022-09-17 00:00:00 -3475.22505045303,2022-09-17 00:00:00,2022-09-18 00:00:00 -3649.73606096515,2022-09-18 00:00:00,2022-09-19 00:00:00 -3149.56244054955,2022-09-19 00:00:00,2022-09-20 00:00:00 -3801.19093480563,2022-09-20 00:00:00,2022-09-21 00:00:00 -3185.45113112985,2022-09-21 00:00:00,2022-09-22 00:00:00 -3544.01062746427,2022-09-22 00:00:00,2022-09-23 00:00:00 -3208.1841425011,2022-09-23 00:00:00,2022-09-24 00:00:00 -3240.87411285567,2022-09-24 00:00:00,2022-09-25 00:00:00 -3434.1836990663,2022-09-25 00:00:00,2022-09-26 00:00:00 -3263.57516853191,2022-09-26 00:00:00,2022-09-27 00:00:00 -3269.29047945354,2022-09-27 00:00:00,2022-09-28 00:00:00 -3446.31080218755,2022-09-28 00:00:00,2022-09-29 00:00:00 -3612.4926684754,2022-09-29 00:00:00,2022-09-30 00:00:00 -3399.03875929056,2022-09-30 00:00:00,2022-10-01 00:00:00 -3306.23254584579,2022-10-01 00:00:00,2022-10-02 00:00:00 -3347.12051782004,2022-10-02 00:00:00,2022-10-03 00:00:00 -3155.39554334092,2022-10-03 00:00:00,2022-10-04 00:00:00 -3453.44504789371,2022-10-04 00:00:00,2022-10-05 00:00:00 -3401.46512149036,2022-10-05 00:00:00,2022-10-06 00:00:00 -3290.25974783438,2022-10-06 00:00:00,2022-10-07 00:00:00 -3599.61871554336,2022-10-07 00:00:00,2022-10-08 00:00:00 -3310.76282799991,2022-10-08 00:00:00,2022-10-09 00:00:00 -3001.00752807099,2022-10-09 00:00:00,2022-10-10 00:00:00 -3437.04825217304,2022-10-10 00:00:00,2022-10-11 00:00:00 -3269.15476177796,2022-10-11 00:00:00,2022-10-12 00:00:00 -3570.59482383298,2022-10-12 00:00:00,2022-10-13 00:00:00 -3380.87577288478,2022-10-13 00:00:00,2022-10-14 00:00:00 -3426.50259772515,2022-10-14 00:00:00,2022-10-15 00:00:00 -3276.20683425922,2022-10-15 00:00:00,2022-10-16 00:00:00 -3342.22555877267,2022-10-16 00:00:00,2022-10-17 00:00:00 -3463.59290294577,2022-10-17 00:00:00,2022-10-18 00:00:00 -3296.97191132445,2022-10-18 00:00:00,2022-10-19 00:00:00 -3200.05360844051,2022-10-19 00:00:00,2022-10-20 00:00:00 -3296.41669953288,2022-10-20 00:00:00,2022-10-21 00:00:00 -3526.88022663582,2022-10-21 00:00:00,2022-10-22 00:00:00 -3367.23970581691,2022-10-22 00:00:00,2022-10-23 00:00:00 -3413.81842548041,2022-10-23 00:00:00,2022-10-24 00:00:00 -3470.20808674533,2022-10-24 00:00:00,2022-10-25 00:00:00 -3822.50944206245,2022-10-25 00:00:00,2022-10-26 00:00:00 -3220.49786756392,2022-10-26 00:00:00,2022-10-27 00:00:00 -3096.67910637821,2022-10-27 00:00:00,2022-10-28 00:00:00 -3413.51014985965,2022-10-28 00:00:00,2022-10-29 00:00:00 -3826.16067841889,2022-10-29 00:00:00,2022-10-30 00:00:00 -3433.12874579122,2022-10-30 00:00:00,2022-10-31 00:00:00 -3042.03540159051,2022-10-31 00:00:00,2022-11-01 00:00:00 +reading,start time,end time +3529.32622810508,2022-08-18 00:00:00,2022-08-19 00:00:00 +2802.86362275449,2022-08-19 00:00:00,2022-08-20 00:00:00 +3485.92367266153,2022-08-20 00:00:00,2022-08-21 00:00:00 +3445.47567824999,2022-08-21 00:00:00,2022-08-22 00:00:00 +3237.08462321332,2022-08-22 00:00:00,2022-08-23 00:00:00 +3327.25468873395,2022-08-23 00:00:00,2022-08-24 00:00:00 +3325.96502299317,2022-08-24 00:00:00,2022-08-25 00:00:00 +3452.23671328065,2022-08-25 00:00:00,2022-08-26 00:00:00 +3057.62349732744,2022-08-26 00:00:00,2022-08-27 00:00:00 +3389.31166903257,2022-08-27 00:00:00,2022-08-28 00:00:00 +3279.83323568366,2022-08-28 00:00:00,2022-08-29 00:00:00 +3154.97913209101,2022-08-29 00:00:00,2022-08-30 00:00:00 +3382.96924946082,2022-08-30 00:00:00,2022-08-31 00:00:00 +3292.67442198523,2022-08-31 00:00:00,2022-09-01 00:00:00 +3267.58806766986,2022-09-01 00:00:00,2022-09-02 00:00:00 +3608.67559419129,2022-09-02 00:00:00,2022-09-03 00:00:00 +3453.76492072941,2022-09-03 00:00:00,2022-09-04 00:00:00 +3337.53692193113,2022-09-04 00:00:00,2022-09-05 00:00:00 +3192.12457919682,2022-09-05 00:00:00,2022-09-06 00:00:00 +3386.81727529807,2022-09-06 00:00:00,2022-09-07 00:00:00 +3322.97497135126,2022-09-07 00:00:00,2022-09-08 00:00:00 +3206.21593769853,2022-09-08 00:00:00,2022-09-09 00:00:00 +3507.90671715743,2022-09-09 00:00:00,2022-09-10 00:00:00 +3239.0083561803,2022-09-10 00:00:00,2022-09-11 00:00:00 +3800.69859687566,2022-09-11 00:00:00,2022-09-12 00:00:00 +3352.30526094167,2022-09-12 00:00:00,2022-09-13 00:00:00 +3541.72305056262,2022-09-13 00:00:00,2022-09-14 00:00:00 +3225.48314577712,2022-09-14 00:00:00,2022-09-15 00:00:00 +3463.6508224378,2022-09-15 00:00:00,2022-09-16 00:00:00 +3242.50163739077,2022-09-16 00:00:00,2022-09-17 00:00:00 +3475.22505045303,2022-09-17 00:00:00,2022-09-18 00:00:00 +3649.73606096515,2022-09-18 00:00:00,2022-09-19 00:00:00 +3149.56244054955,2022-09-19 00:00:00,2022-09-20 00:00:00 +3801.19093480563,2022-09-20 00:00:00,2022-09-21 00:00:00 +3185.45113112985,2022-09-21 00:00:00,2022-09-22 00:00:00 +3544.01062746427,2022-09-22 00:00:00,2022-09-23 00:00:00 +3208.1841425011,2022-09-23 00:00:00,2022-09-24 00:00:00 +3240.87411285567,2022-09-24 00:00:00,2022-09-25 00:00:00 +3434.1836990663,2022-09-25 00:00:00,2022-09-26 00:00:00 +3263.57516853191,2022-09-26 00:00:00,2022-09-27 00:00:00 +3269.29047945354,2022-09-27 00:00:00,2022-09-28 00:00:00 +3446.31080218755,2022-09-28 00:00:00,2022-09-29 00:00:00 +3612.4926684754,2022-09-29 00:00:00,2022-09-30 00:00:00 +3399.03875929056,2022-09-30 00:00:00,2022-10-01 00:00:00 +3306.23254584579,2022-10-01 00:00:00,2022-10-02 00:00:00 +3347.12051782004,2022-10-02 00:00:00,2022-10-03 00:00:00 +3155.39554334092,2022-10-03 00:00:00,2022-10-04 00:00:00 +3453.44504789371,2022-10-04 00:00:00,2022-10-05 00:00:00 +3401.46512149036,2022-10-05 00:00:00,2022-10-06 00:00:00 +3290.25974783438,2022-10-06 00:00:00,2022-10-07 00:00:00 +3599.61871554336,2022-10-07 00:00:00,2022-10-08 00:00:00 +3310.76282799991,2022-10-08 00:00:00,2022-10-09 00:00:00 +3001.00752807099,2022-10-09 00:00:00,2022-10-10 00:00:00 +3437.04825217304,2022-10-10 00:00:00,2022-10-11 00:00:00 +3269.15476177796,2022-10-11 00:00:00,2022-10-12 00:00:00 +3570.59482383298,2022-10-12 00:00:00,2022-10-13 00:00:00 +3380.87577288478,2022-10-13 00:00:00,2022-10-14 00:00:00 +3426.50259772515,2022-10-14 00:00:00,2022-10-15 00:00:00 +3276.20683425922,2022-10-15 00:00:00,2022-10-16 00:00:00 +3342.22555877267,2022-10-16 00:00:00,2022-10-17 00:00:00 +3463.59290294577,2022-10-17 00:00:00,2022-10-18 00:00:00 +3296.97191132445,2022-10-18 00:00:00,2022-10-19 00:00:00 +3200.05360844051,2022-10-19 00:00:00,2022-10-20 00:00:00 +3296.41669953288,2022-10-20 00:00:00,2022-10-21 00:00:00 +3526.88022663582,2022-10-21 00:00:00,2022-10-22 00:00:00 +3367.23970581691,2022-10-22 00:00:00,2022-10-23 00:00:00 +3413.81842548041,2022-10-23 00:00:00,2022-10-24 00:00:00 +3470.20808674533,2022-10-24 00:00:00,2022-10-25 00:00:00 +3822.50944206245,2022-10-25 00:00:00,2022-10-26 00:00:00 +3220.49786756392,2022-10-26 00:00:00,2022-10-27 00:00:00 +3096.67910637821,2022-10-27 00:00:00,2022-10-28 00:00:00 +3413.51014985965,2022-10-28 00:00:00,2022-10-29 00:00:00 +3826.16067841889,2022-10-29 00:00:00,2022-10-30 00:00:00 +3433.12874579122,2022-10-30 00:00:00,2022-10-31 00:00:00 +3042.03540159051,2022-10-31 00:00:00,2022-11-01 00:00:00 diff --git a/src/server/test/web/readingsData/expected_line_group_ri_15-20_mu_kWh_gu_kgCO2_st_-inf_et_inf.csv b/src/server/test/web/readingsData/expected_line_group_ri_15-20_mu_kWh_gu_kgCO2_st_-inf_et_inf.csv index 06e25ffeb..42c3b0b99 100644 --- a/src/server/test/web/readingsData/expected_line_group_ri_15-20_mu_kWh_gu_kgCO2_st_-inf_et_inf.csv +++ b/src/server/test/web/readingsData/expected_line_group_ri_15-20_mu_kWh_gu_kgCO2_st_-inf_et_inf.csv @@ -1,76 +1,76 @@ -reading,start time,end time -252.892148928677,2022-08-18 00:00:00,2022-08-19 00:00:00 -223.337293871196,2022-08-19 00:00:00,2022-08-20 00:00:00 -255.680774535246,2022-08-20 00:00:00,2022-08-21 00:00:00 -250.846348838948,2022-08-21 00:00:00,2022-08-22 00:00:00 -242.698017028994,2022-08-22 00:00:00,2022-08-23 00:00:00 -249.367853797597,2022-08-23 00:00:00,2022-08-24 00:00:00 -251.85569690265,2022-08-24 00:00:00,2022-08-25 00:00:00 -247.295309798749,2022-08-25 00:00:00,2022-08-26 00:00:00 -230.662860940612,2022-08-26 00:00:00,2022-08-27 00:00:00 -248.74858156857,2022-08-27 00:00:00,2022-08-28 00:00:00 -245.705851370349,2022-08-28 00:00:00,2022-08-29 00:00:00 -232.233846283603,2022-08-29 00:00:00,2022-08-30 00:00:00 -253.700622881239,2022-08-30 00:00:00,2022-08-31 00:00:00 -243.767850197022,2022-08-31 00:00:00,2022-09-01 00:00:00 -238.183704532081,2022-09-01 00:00:00,2022-09-02 00:00:00 -257.094979610576,2022-09-02 00:00:00,2022-09-03 00:00:00 -257.777319080649,2022-09-03 00:00:00,2022-09-04 00:00:00 -246.512286453807,2022-09-04 00:00:00,2022-09-05 00:00:00 -229.886225395202,2022-09-05 00:00:00,2022-09-06 00:00:00 -250.624023704122,2022-09-06 00:00:00,2022-09-07 00:00:00 -251.627138465283,2022-09-07 00:00:00,2022-09-08 00:00:00 -241.828457022283,2022-09-08 00:00:00,2022-09-09 00:00:00 -249.185273607622,2022-09-09 00:00:00,2022-09-10 00:00:00 -245.574982720773,2022-09-10 00:00:00,2022-09-11 00:00:00 -265.766825732167,2022-09-11 00:00:00,2022-09-12 00:00:00 -246.524273835636,2022-09-12 00:00:00,2022-09-13 00:00:00 -253.532426577597,2022-09-13 00:00:00,2022-09-14 00:00:00 -238.701910075936,2022-09-14 00:00:00,2022-09-15 00:00:00 -251.286709376873,2022-09-15 00:00:00,2022-09-16 00:00:00 -241.148694861167,2022-09-16 00:00:00,2022-09-17 00:00:00 -254.640177033644,2022-09-17 00:00:00,2022-09-18 00:00:00 -256.318865345186,2022-09-18 00:00:00,2022-09-19 00:00:00 -243.760295617627,2022-09-19 00:00:00,2022-09-20 00:00:00 -269.72814149219,2022-09-20 00:00:00,2022-09-21 00:00:00 -239.535250123288,2022-09-21 00:00:00,2022-09-22 00:00:00 -257.968922072573,2022-09-22 00:00:00,2022-09-23 00:00:00 -236.884293822186,2022-09-23 00:00:00,2022-09-24 00:00:00 -236.457969882856,2022-09-24 00:00:00,2022-09-25 00:00:00 -251.908275731723,2022-09-25 00:00:00,2022-09-26 00:00:00 -240.057503051384,2022-09-26 00:00:00,2022-09-27 00:00:00 -238.170393268663,2022-09-27 00:00:00,2022-09-28 00:00:00 -248.098895622592,2022-09-28 00:00:00,2022-09-29 00:00:00 -256.175410700896,2022-09-29 00:00:00,2022-09-30 00:00:00 -248.813852738315,2022-09-30 00:00:00,2022-10-01 00:00:00 -243.496274125415,2022-10-01 00:00:00,2022-10-02 00:00:00 -249.047244409498,2022-10-02 00:00:00,2022-10-03 00:00:00 -239.200631710465,2022-10-03 00:00:00,2022-10-04 00:00:00 -248.790428309162,2022-10-04 00:00:00,2022-10-05 00:00:00 -244.894279966184,2022-10-05 00:00:00,2022-10-06 00:00:00 -250.473477472761,2022-10-06 00:00:00,2022-10-07 00:00:00 -256.267119601032,2022-10-07 00:00:00,2022-10-08 00:00:00 -243.485674905646,2022-10-08 00:00:00,2022-10-09 00:00:00 -229.262270537384,2022-10-09 00:00:00,2022-10-10 00:00:00 -245.49834197354,2022-10-10 00:00:00,2022-10-11 00:00:00 -242.651090400592,2022-10-11 00:00:00,2022-10-12 00:00:00 -256.465170820487,2022-10-12 00:00:00,2022-10-13 00:00:00 -251.210520208321,2022-10-13 00:00:00,2022-10-14 00:00:00 -247.116940873415,2022-10-14 00:00:00,2022-10-15 00:00:00 -238.967408502432,2022-10-15 00:00:00,2022-10-16 00:00:00 -244.104502447371,2022-10-16 00:00:00,2022-10-17 00:00:00 -249.851989275115,2022-10-17 00:00:00,2022-10-18 00:00:00 -243.749304861483,2022-10-18 00:00:00,2022-10-19 00:00:00 -235.366010252579,2022-10-19 00:00:00,2022-10-20 00:00:00 -248.940021970046,2022-10-20 00:00:00,2022-10-21 00:00:00 -253.6622840378,2022-10-21 00:00:00,2022-10-22 00:00:00 -240.301488632524,2022-10-22 00:00:00,2022-10-23 00:00:00 -248.852387944817,2022-10-23 00:00:00,2022-10-24 00:00:00 -261.518753786087,2022-10-24 00:00:00,2022-10-25 00:00:00 -269.97043892657,2022-10-25 00:00:00,2022-10-26 00:00:00 -236.241477685152,2022-10-26 00:00:00,2022-10-27 00:00:00 -238.243667723802,2022-10-27 00:00:00,2022-10-28 00:00:00 -249.593138158714,2022-10-28 00:00:00,2022-10-29 00:00:00 -265.822445194516,2022-10-29 00:00:00,2022-10-30 00:00:00 -246.706333347691,2022-10-30 00:00:00,2022-10-31 00:00:00 -231.028219367406,2022-10-31 00:00:00,2022-11-01 00:00:00 +reading,start time,end time +252.892148928677,2022-08-18 00:00:00,2022-08-19 00:00:00 +223.337293871196,2022-08-19 00:00:00,2022-08-20 00:00:00 +255.680774535246,2022-08-20 00:00:00,2022-08-21 00:00:00 +250.846348838948,2022-08-21 00:00:00,2022-08-22 00:00:00 +242.698017028994,2022-08-22 00:00:00,2022-08-23 00:00:00 +249.367853797597,2022-08-23 00:00:00,2022-08-24 00:00:00 +251.85569690265,2022-08-24 00:00:00,2022-08-25 00:00:00 +247.295309798749,2022-08-25 00:00:00,2022-08-26 00:00:00 +230.662860940612,2022-08-26 00:00:00,2022-08-27 00:00:00 +248.74858156857,2022-08-27 00:00:00,2022-08-28 00:00:00 +245.705851370349,2022-08-28 00:00:00,2022-08-29 00:00:00 +232.233846283603,2022-08-29 00:00:00,2022-08-30 00:00:00 +253.700622881239,2022-08-30 00:00:00,2022-08-31 00:00:00 +243.767850197022,2022-08-31 00:00:00,2022-09-01 00:00:00 +238.183704532081,2022-09-01 00:00:00,2022-09-02 00:00:00 +257.094979610576,2022-09-02 00:00:00,2022-09-03 00:00:00 +257.777319080649,2022-09-03 00:00:00,2022-09-04 00:00:00 +246.512286453807,2022-09-04 00:00:00,2022-09-05 00:00:00 +229.886225395202,2022-09-05 00:00:00,2022-09-06 00:00:00 +250.624023704122,2022-09-06 00:00:00,2022-09-07 00:00:00 +251.627138465283,2022-09-07 00:00:00,2022-09-08 00:00:00 +241.828457022283,2022-09-08 00:00:00,2022-09-09 00:00:00 +249.185273607622,2022-09-09 00:00:00,2022-09-10 00:00:00 +245.574982720773,2022-09-10 00:00:00,2022-09-11 00:00:00 +265.766825732167,2022-09-11 00:00:00,2022-09-12 00:00:00 +246.524273835636,2022-09-12 00:00:00,2022-09-13 00:00:00 +253.532426577597,2022-09-13 00:00:00,2022-09-14 00:00:00 +238.701910075936,2022-09-14 00:00:00,2022-09-15 00:00:00 +251.286709376873,2022-09-15 00:00:00,2022-09-16 00:00:00 +241.148694861167,2022-09-16 00:00:00,2022-09-17 00:00:00 +254.640177033644,2022-09-17 00:00:00,2022-09-18 00:00:00 +256.318865345186,2022-09-18 00:00:00,2022-09-19 00:00:00 +243.760295617627,2022-09-19 00:00:00,2022-09-20 00:00:00 +269.72814149219,2022-09-20 00:00:00,2022-09-21 00:00:00 +239.535250123288,2022-09-21 00:00:00,2022-09-22 00:00:00 +257.968922072573,2022-09-22 00:00:00,2022-09-23 00:00:00 +236.884293822186,2022-09-23 00:00:00,2022-09-24 00:00:00 +236.457969882856,2022-09-24 00:00:00,2022-09-25 00:00:00 +251.908275731723,2022-09-25 00:00:00,2022-09-26 00:00:00 +240.057503051384,2022-09-26 00:00:00,2022-09-27 00:00:00 +238.170393268663,2022-09-27 00:00:00,2022-09-28 00:00:00 +248.098895622592,2022-09-28 00:00:00,2022-09-29 00:00:00 +256.175410700896,2022-09-29 00:00:00,2022-09-30 00:00:00 +248.813852738315,2022-09-30 00:00:00,2022-10-01 00:00:00 +243.496274125415,2022-10-01 00:00:00,2022-10-02 00:00:00 +249.047244409498,2022-10-02 00:00:00,2022-10-03 00:00:00 +239.200631710465,2022-10-03 00:00:00,2022-10-04 00:00:00 +248.790428309162,2022-10-04 00:00:00,2022-10-05 00:00:00 +244.894279966184,2022-10-05 00:00:00,2022-10-06 00:00:00 +250.473477472761,2022-10-06 00:00:00,2022-10-07 00:00:00 +256.267119601032,2022-10-07 00:00:00,2022-10-08 00:00:00 +243.485674905646,2022-10-08 00:00:00,2022-10-09 00:00:00 +229.262270537384,2022-10-09 00:00:00,2022-10-10 00:00:00 +245.49834197354,2022-10-10 00:00:00,2022-10-11 00:00:00 +242.651090400592,2022-10-11 00:00:00,2022-10-12 00:00:00 +256.465170820487,2022-10-12 00:00:00,2022-10-13 00:00:00 +251.210520208321,2022-10-13 00:00:00,2022-10-14 00:00:00 +247.116940873415,2022-10-14 00:00:00,2022-10-15 00:00:00 +238.967408502432,2022-10-15 00:00:00,2022-10-16 00:00:00 +244.104502447371,2022-10-16 00:00:00,2022-10-17 00:00:00 +249.851989275115,2022-10-17 00:00:00,2022-10-18 00:00:00 +243.749304861483,2022-10-18 00:00:00,2022-10-19 00:00:00 +235.366010252579,2022-10-19 00:00:00,2022-10-20 00:00:00 +248.940021970046,2022-10-20 00:00:00,2022-10-21 00:00:00 +253.6622840378,2022-10-21 00:00:00,2022-10-22 00:00:00 +240.301488632524,2022-10-22 00:00:00,2022-10-23 00:00:00 +248.852387944817,2022-10-23 00:00:00,2022-10-24 00:00:00 +261.518753786087,2022-10-24 00:00:00,2022-10-25 00:00:00 +269.97043892657,2022-10-25 00:00:00,2022-10-26 00:00:00 +236.241477685152,2022-10-26 00:00:00,2022-10-27 00:00:00 +238.243667723802,2022-10-27 00:00:00,2022-10-28 00:00:00 +249.593138158714,2022-10-28 00:00:00,2022-10-29 00:00:00 +265.822445194516,2022-10-29 00:00:00,2022-10-30 00:00:00 +246.706333347691,2022-10-30 00:00:00,2022-10-31 00:00:00 +231.028219367406,2022-10-31 00:00:00,2022-11-01 00:00:00 From ad31fd0501021270a4642802bb44f6892e7a4346 Mon Sep 17 00:00:00 2001 From: Zachary-Squires Date: Tue, 4 Nov 2025 17:41:29 -0500 Subject: [PATCH 12/72] Change to not allow default postgres or secret token in OED production. Changed the installOED.sh file to generate randomized values for OED_TOKEN_SECRET and POSTGRES_PASSWORD, which are then stored in .env file. Changed docker.compose.yml to draw from the .env file if variables exist there. When in dev mode with the default values the user will be warned in the console when OED is started. Also implemented a warning for the mailing variables, if the method is set and a default remains in the rest the user will be warned in production. --- docker-compose.yml | 5 +-- src/scripts/installOED.sh | 74 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1b354cf39..5c7352985 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,8 @@ services: environment: # Custom PGDATA per recommendations from official Docker page - PGDATA=/var/lib/postgresql/data/pgdata - - POSTGRES_PASSWORD=pleaseChange # default postgres password that should be changed for security. + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-pleaseChange} # In development password may (and should be) be manually changed. + # In production it will be automatically changed and stored in .env volumes: - ./postgres-data:/var/lib/postgresql/data/pgdata healthcheck: @@ -37,7 +38,7 @@ services: - OED_DB_PASSWORD=opened - OED_DB_HOST=database # Docker will set this hostname - OED_DB_PORT=5432 - - OED_TOKEN_SECRET=? + - OED_TOKEN_SECRET=${OED_TOKEN_SECRET:-?} #Automatically generated when OED is run in production - OED_LOG_FILE=log.txt - OED_MAIL_METHOD=none # Method of sending mail. Supports "secure-smtp", "none". Case insensitive. - OED_MAIL_SMTP=smtp.example.com # Edit this diff --git a/src/scripts/installOED.sh b/src/scripts/installOED.sh index 1bef1b7a5..f83461317 100755 --- a/src/scripts/installOED.sh +++ b/src/scripts/installOED.sh @@ -188,11 +188,77 @@ printf "%s\n" "OED install finished" if [ "$dostart" == "yes" ]; then if [ "$production" == "yes" ] || [ "$OED_PRODUCTION" == "yes" ]; then printf "%s\n" "Starting OED in production mode" + if [ -z "$OED_MAIL_METHOD" ] || [ "$OED_MAIL_METHOD" != "none" ]; then + if [ "$OED_MAIL_SMTP" = "smtp.example.com" ] || \ + [ "$OED_MAIL_SMTP_PORT" = "465" ] || \ + [ "$OED_MAIL_IDENT" = "someone@example.com" ] || \ + [ "$OED_MAIL_CREDENTIAL" = "credential" ] || \ + [ "$OED_MAIL_FROM" = "mydomain@example.com" ] || \ + [ "$OED_MAIL_TO" = "someone@example.com" ] || \ + [ "$OED_MAIL_ORG" = "My Organization Name" ]; then + printf "\n********************************************************************************\n" + printf "* WARNING: You have set your mail method but one or more of the mail environment variables are still set to the default value!*\n" + printf "********************************************************************************\n\n" + fi + fi + if [ -z "$OED_TOKEN_SECRET" ] || [ "$OED_TOKEN_SECRET" = "?" ]; then + printf "\nNo valid OED_TOKEN_SECRET detected. Generating a secure random secret...\n" + + # Generate 32 bytes of random data and convert to 64-character hex + OED_TOKEN_SECRET=$(openssl rand -hex 32) + export OED_TOKEN_SECRET + + printf "Generated OED_TOKEN_SECRET: %s\n" "$OED_TOKEN_SECRET" + + # Save to .env for future runs + if [ -f ".env" ]; then + if grep -q "^OED_TOKEN_SECRET=" .env; then + sed -i "s/^OED_TOKEN_SECRET=.*/OED_TOKEN_SECRET=$OED_TOKEN_SECRET/" .env + else + echo "OED_TOKEN_SECRET=$OED_TOKEN_SECRET" >> .env + fi + else + echo "OED_TOKEN_SECRET=$OED_TOKEN_SECRET" > .env + fi + fi + + if [ -z "$POSTGRES_PASSWORD" ] || [ "$POSTGRES_PASSWORD" = "pleaseChange" ]; then + printf "\n No valid PostgreSQL password detected. Generating a secure random password...\n" + POSTGRES_PASSWORD=$(openssl rand -base64 12) + export POSTGRES_PASSWORD + printf "Generated PostgreSQL password: %s\n" "$POSTGRES_PASSWORD" + printf "\n Make sure to save or change this value" + + # Save to .env + if [ -f ".env" ]; then + if grep -q "^POSTGRES_PASSWORD=" .env; then + sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$POSTGRES_PASSWORD/" .env + else + echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env + fi + else + echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" > .env + fi + fi npm run start - else - printf "%s\n" "Starting OED in development mode" + elif [ "$OED_PRODUCTION" == "no" ]; then + if [ -z "$OED_TOKEN_SECRET" ] || [ "$OED_TOKEN_SECRET" = "?" ]; then + printf "\n********************************************************************************\n" + printf "WARNING: YOU ARE USING OED IN DEVELOPMENT MODE WITH THE DEFAULT OED_TOKEN_SECRET SET IN docker-compose.yml IF THIS IS NOT INTENTIONAL GO THERE TO CHANGE IT.\n" + printf "********************************************************************************\n\n" + fi + if [ -z "$POSTGRES_PASSWORD" ] || [ "$POSTGRES_PASSWORD" = "pleaseChange" ]; then + printf "\n********************************************************************************\n" + printf "* WARNING: YOU ARE USING OED IN DEVELOPMENT MODE WITH THE DEFAULT POSTGRESQL PASSWORD SET IN docker-compose.yml IF THIS IS NOT INTENTIONAL GO THERE TO CHANGE IT. *\n" + printf "********************************************************************************\n\n" + fi + printf "%s\n" "Starting OED in development mode." ./src/scripts/devstart.sh - fi + else + printf "\nFailure: Invalid or missing enviroment configuration." + printf "\nSet OED_PRODUCTION to 'yes' for production or 'no' for development." + exit 10 + fi else - printf "%s\n" "Not starting OED due to --nostart" + printf "%s\n" "Not starting OED due to --nostart." fi From 3c3b6ccaf1850663e4b312f4f10dd86aa31756d1 Mon Sep 17 00:00:00 2001 From: Zach-O-Bates Date: Tue, 4 Nov 2025 20:43:38 -0500 Subject: [PATCH 13/72] Fix PR suggestions --- docker-compose.yml | 2 +- package-lock.json | 9 +++++---- package.json | 2 +- pull_request_template.md | 2 +- src/server/routes/response.js | 8 ++++---- src/server/services/csvPipeline/success.js | 14 +++++++------- src/server/services/utils/sanitizer.js | 4 ++++ src/server/test/crossSite/crossSite.js | 12 +++++++++--- src/server/test/something.csv | 1 - 9 files changed, 32 insertions(+), 22 deletions(-) delete mode 100644 src/server/test/something.csv diff --git a/docker-compose.yml b/docker-compose.yml index 5c7352985..ab180fd9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: web: # Configuration variables for the app. environment: - - OED_PRODUCTION=no + - OED_PRODUCTION=yes # Set this value to yes or no, other values will result in a configuration error - OED_SERVER_PORT=3000 - OED_DB_USER=oed - OED_DB_DATABASE=oed diff --git a/package-lock.json b/package-lock.json index f30b6cc83..cad5cec6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", - "dompurify": "~3.2.6", + "dompurify": "~3.3.0", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", @@ -4185,9 +4185,10 @@ } }, "node_modules/dompurify": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", - "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", + "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" } diff --git a/package.json b/package.json index 12b1b80f3..1c598e0c5 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "csv": "~5.3.2", "csv-stringify": "~5.6.5", "d3": "~7.8.5", - "dompurify": "~3.2.6", + "dompurify": "~3.3.0", "dotenv": "~16.4.5", "escape-html": "~1.0.3", "express": "~4.19.2", diff --git a/pull_request_template.md b/pull_request_template.md index 317dc640e..982321649 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -23,4 +23,4 @@ Fixes #[issue] ## Limitations -(Describe any issues that remain or work that should still be done.) \ No newline at end of file +(Describe any issues that remain or work that should still be done.) diff --git a/src/server/routes/response.js b/src/server/routes/response.js index 2c4583157..6ba547309 100644 --- a/src/server/routes/response.js +++ b/src/server/routes/response.js @@ -13,8 +13,8 @@ const DOMPurify = require('../services/utils/sanitizer'); * @param {string} comment Any additional data to be returned to the client as a string. */ function success(res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); - res.status(200).send(safeComment); + const safeComment = DOMPurify.sanitize(comment); + res.status(200).send(safeComment); } /** @@ -25,8 +25,8 @@ function success(res, comment = '') { * @param {string} comment Any additional data to be returned to the client as a string. */ function failure(res, code = 500, comment = '') { - const safeComment = DOMPurify.sanitize(comment); - res.status(code).send(safeComment); + const safeComment = DOMPurify.sanitize(comment); + res.status(code).send(safeComment); } module.exports = { success, failure }; diff --git a/src/server/services/csvPipeline/success.js b/src/server/services/csvPipeline/success.js index 852f6dabd..7b8945b2a 100644 --- a/src/server/services/csvPipeline/success.js +++ b/src/server/services/csvPipeline/success.js @@ -13,11 +13,10 @@ const DOMPurify = require('../utils/sanitizer'); * @param {string} comment Any additional data to be returned to the client. */ function success(req, res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); - res.status(200).send(`

SUCCESS

${safeComment}`); + const safeComment = DOMPurify.sanitize(comment); + res.status(200).send(`

SUCCESS

${safeComment}`); } - /** * Inform the client of a failure (400 OK) with sanitized HTML content. * @@ -26,9 +25,10 @@ function success(req, res, comment = '') { * @param {string} comment Any additional data to be returned to the client. */ function failure(req, res, comment = '') { - const safeComment = DOMPurify.sanitize(comment); - // 400 is client error. There is a small chance the insert into the DB failed - // but overlooking that. - res.status(400).send(`

FAILURE

${safeComment}`); + const safeComment = DOMPurify.sanitize(comment); + // 400 is client error. There is a small chance the insert into the DB failed + // but overlooking that. + res.status(400).send(`

FAILURE

${safeComment}`); } + module.exports = { success, failure }; \ No newline at end of file diff --git a/src/server/services/utils/sanitizer.js b/src/server/services/utils/sanitizer.js index 3b5ccc536..f2ade6189 100644 --- a/src/server/services/utils/sanitizer.js +++ b/src/server/services/utils/sanitizer.js @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + const { JSDOM } = require('jsdom'); const createDOMPurify = require('dompurify'); diff --git a/src/server/test/crossSite/crossSite.js b/src/server/test/crossSite/crossSite.js index 63e1ed4dc..7d4936950 100644 --- a/src/server/test/crossSite/crossSite.js +++ b/src/server/test/crossSite/crossSite.js @@ -10,7 +10,7 @@ vulnerabilities in HTML of user uploaded data.*/ const { chai, mocha, expect, app, testUser } = require('../common'); mocha.describe('Cross site', () => { - mocha.it('test 1: tests for sanitization of HTML', async () => { + mocha.it('Test for sanitization of HTML', async () => { const filePath = 'src/server/test/crossSite/readings.csv'; const res = await chai.request(app).post('/api/csv/readings') @@ -22,7 +22,13 @@ mocha.describe('Cross site', () => { .field('meterName','') .field('gzip', "no") .attach('csvfile', 'src/server/test/crossSite/something.csv'); - expect(res.text).to.include(''); - expect(res).to.have.status(400); + if (res.status !== 400){ + expect(res.text).to.include(''); + process.exit(1) + } + + if (!res.text.includes(' { handleStringChange(e); }} + onChange={e => {handleStringChange(e);}} value={state.identifier} /> @@ -423,7 +423,7 @@ export default function EditUnitModalComponent(props: EditUnitModalComponentProp name='name' type='text' autoComplete='on' - onChange={e => { handleStringChange(e); }} + onChange={e => {handleStringChange(e);}} value={state.name} invalid={state.name === ''} /> @@ -441,18 +441,16 @@ export default function EditUnitModalComponent(props: EditUnitModalComponentProp id='typeOfUnit' name='typeOfUnit' type='select' - onChange={e => { handleStringChange(e); }} + onChange={e => {handleStringChange(e);}} value={state.typeOfUnit} invalid={state.typeOfUnit !== UnitType.suffix && state.suffix !== ''} > {Object.keys(UnitType).map(key => { - const isMeter = key === UnitType.meter; - const disableMeter = isMeter && inConversions(); return ( @@ -474,7 +472,7 @@ export default function EditUnitModalComponent(props: EditUnitModalComponentProp type='select' value={state.unitRepresent} disabled={inConversions()} - onChange={e => { handleStringChange(e); }} + onChange={e => {handleStringChange(e);}} > {Object.keys(UnitRepresentType).map(key => { return ( @@ -496,7 +494,7 @@ export default function EditUnitModalComponent(props: EditUnitModalComponentProp name='displayable' type='select' value={state.displayable} - onChange={e => { handleStringChange(e); }} + onChange={e => {handleStringChange(e);}} invalid={ state.displayable !== DisplayableType.none && (state.typeOfUnit === UnitType.meter || state.suffix !== '') @@ -534,7 +532,7 @@ export default function EditUnitModalComponent(props: EditUnitModalComponentProp name='preferredDisplay' type='select' value={state.preferredDisplay.toString()} - onChange={e => { handleBooleanChange(e); }}> + onChange={e => {handleBooleanChange(e);}}> {Object.keys(TrueFalseType).map(key => { return (