From 4ee1cb87709698b58213f27fdf2cf587866295ba Mon Sep 17 00:00:00 2001 From: jshemas Date: Mon, 26 Oct 2026 22:27:56 -0400 Subject: [PATCH 01/12] updating tests --- tests/integration/fetch.spec.ts | 10 +++++----- tests/integration/redirect.spec.ts | 3 ++- tests/integration/spotify.spec.ts | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/integration/fetch.spec.ts b/tests/integration/fetch.spec.ts index 553fa57..ba5018f 100644 --- a/tests/integration/fetch.spec.ts +++ b/tests/integration/fetch.spec.ts @@ -36,7 +36,7 @@ describe('fetch', function () { }); it('setting a timeout', function () { - return ogs({ url: 'https://releases.ubuntu.com/23.04/ubuntu-23.04-desktop-amd64.iso', timeout: 3 }) + return ogs({ url: 'https://releases.ubuntu.com/26.10/ubuntu-26.10-desktop-amd64.iso', timeout: 3 }) .then(function () { expect().fail('this should not happen'); }) @@ -45,7 +45,7 @@ describe('fetch', function () { console.log('result:', result); expect(error).to.be.eql(true); expect(result.success).to.be.eql(false); - expect(result.requestUrl).to.be.eql('https://releases.ubuntu.com/23.04/ubuntu-23.04-desktop-amd64.iso'); + expect(result.requestUrl).to.be.eql('https://releases.ubuntu.com/26.10/ubuntu-26.10-desktop-amd64.iso'); expect(result.error).to.eql('The operation was aborted due to timeout'); expect(result.errorDetails.toString()).to.eql('TimeoutError: The operation was aborted due to timeout'); expect(result).to.have.all.keys( @@ -60,7 +60,7 @@ describe('fetch', function () { // https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout // eslint-disable-next-line mocha/no-skipped-tests it.skip('setting a timeout - using AbortSignal.timeout()', function () { - return ogs({ url: 'https://releases.ubuntu.com/23.04/ubuntu-23.04-desktop-amd64.iso', fetchOptions: { signal: AbortSignal.timeout(3000) } }) + return ogs({ url: 'https://releases.ubuntu.com/26.10/ubuntu-26.10-desktop-amd64.iso', fetchOptions: { signal: AbortSignal.timeout(3000) } }) .then(function () { expect().fail('this should not happen'); }) @@ -86,7 +86,7 @@ describe('fetch', function () { it.skip('setting a timeout - using controller.abort()', function () { const controller = new AbortController(); setTimeout(() => controller.abort(), 3000); - return ogs({ url: 'https://releases.ubuntu.com/23.04/ubuntu-23.04-desktop-amd64.iso', fetchOptions: { signal: controller.signal } }) + return ogs({ url: 'https://releases.ubuntu.com/26.10/ubuntu-26.10-desktop-amd64.iso', fetchOptions: { signal: controller.signal } }) .then(function () { expect().fail('this should not happen'); }) @@ -95,7 +95,7 @@ describe('fetch', function () { console.log('result:', result); expect(error).to.be.eql(true); expect(result.success).to.be.eql(false); - expect(result.requestUrl).to.be.eql('https://releases.ubuntu.com/23.04/ubuntu-23.04-desktop-amd64.iso'); + expect(result.requestUrl).to.be.eql('https://releases.ubuntu.com/26.10/ubuntu-26.10-desktop-amd64.iso'); expect(result.error).to.eql('The operation was aborted due to timeout'); expect(result.errorDetails.toString()).to.eql('Error: The operation was aborted due to timeout'); expect(result).to.have.all.keys( diff --git a/tests/integration/redirect.spec.ts b/tests/integration/redirect.spec.ts index 5a8fd79..ec89cb9 100644 --- a/tests/integration/redirect.spec.ts +++ b/tests/integration/redirect.spec.ts @@ -90,7 +90,8 @@ describe('redirect', function () { expect(response).to.be.an('Response'); }); }); - it('forbes page', function () { + // forbes is blocking requests, will need to find way to bypass this + it.skip('forbes page', function () { return ogs({ url: 'https://www.forbes.com/sites/kenkam/2017/09/28/3-stocks-like-apple-was-10-years-ago-tesla-nvidia-and-alibaba/#2636f6c2f0fa', }).then(function ({ error, result, response }) { diff --git a/tests/integration/spotify.spec.ts b/tests/integration/spotify.spec.ts index 7a489c2..3bfe681 100644 --- a/tests/integration/spotify.spec.ts +++ b/tests/integration/spotify.spec.ts @@ -14,14 +14,14 @@ describe('spotify', function () { expect(result.alIosAppName).to.be.eql('Spotify'); expect(result.alIosAppStoreId).to.be.eql('324684580'); expect(result.alIosUrl).to.be.eql('spotify://album/5EBGCvO6upi3GNknMVe9x9'); - expect(result.ogTitle).to.be.eql('ye - Album by Kanye West | Spotify'); + expect(result.ogTitle).to.be.eql('Listen to ye - Album by Kanye West | Spotify'); // expect(result.ogDescription).to.be.eql('Album · Kanye West · 2018 · 7 songs'); // expect(result.favicon).to.be.eql('https://open.scdn.co/cdn/images/favicon.0c211e2e.png'); // expect(result.ogUrl).to.be.eql('https://open.spotify.com/album/5EBGCvO6upi3GNknMVe9x9'); expect(result.ogType).to.be.eql('music.album'); expect(result.musicMusician).to.be.eql('https://open.spotify.com/artist/5K4W6rqBFWDnAN6FQUkS6x'); expect(result.musicReleaseDate).to.be.eql('2018-06-01'); - expect(result.twitterTitle).to.be.eql('ye - Album by Kanye West | Spotify'); + expect(result.twitterTitle).to.be.eql('Listen to ye - Album by Kanye West | Spotify'); expect(result.twitterSite).to.be.eql('@spotify'); // expect(result.twitterDescription).to.be.eql('Album · Kanye West · 2018 · 7 songs'); expect(result.twitterCard).to.be.eql('summary'); @@ -122,7 +122,7 @@ describe('spotify', function () { // expect(result.favicon).to.be.eql('https://open.scdn.co/cdn/images/favicon.0c211e2e.png'); expect(result.ogDescription).to.be.an('string').and.to.not.be.empty; expect(result.ogUrl).to.be.eql('https://open.spotify.com/artist/5K4W6rqBFWDnAN6FQUkS6x'); - expect(result.ogType).to.be.eql('profile'); + expect(result.ogType).to.be.eql('website'); expect(result.twitterTitle).to.be.eql('Kanye West'); expect(result.twitterSite).to.be.eql('@spotify'); expect(result.twitterDescription).to.be.an('string').and.to.not.be.empty; From be368f1f2991cb41dbe1c2b6ff02b7dc968aacc0 Mon Sep 17 00:00:00 2001 From: jshemas Date: Mon, 26 Oct 2026 22:46:47 -0400 Subject: [PATCH 02/12] updating dependencies --- package-lock.json | 407 +++++++++++++++++++++++----------------------- package.json | 14 +- 2 files changed, 211 insertions(+), 210 deletions(-) diff --git a/package-lock.json b/package-lock.json index d91e368..db55803 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,25 +11,25 @@ "dependencies": { "chardet": "^2.2.0", "cheerio": "^1.2.0", - "iconv-lite": "^0.7.2", - "undici": "^7.28.0" + "iconv-lite": "^0.7.3", + "undici": "^7.30.1" }, "devDependencies": { - "@snyk/protect": "^1.1305.2", + "@snyk/protect": "^1.1308.3", "@types/mocha": "^10.0.10", "@types/node": "^20.19.43", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", - "chai": "^6.2.2", - "eslint": "^8.57.0", + "chai": "^6.3.0", + "eslint": "^8.57.1", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-mocha": "^10.5.0", "eslint-plugin-promise": "^7.2.1", - "mocha": "^11.7.6", + "mocha": "^11.8.0", "nyc": "^18.0.0", - "sinon": "^22.0.0", + "sinon": "^22.1.0", "ts-mocha": "^11.1.0", "typescript": "5.5.4" }, @@ -111,14 +111,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -197,9 +197,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "dev": true, "license": "MIT", "engines": { @@ -227,27 +227,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -272,18 +272,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", + "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", + "@babel/parser": "^7.29.9", "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -291,13 +291,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-string-parser": "^7.29.11", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { @@ -331,9 +331,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -384,9 +384,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -434,9 +434,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -498,9 +498,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", "dev": true, "license": "MIT", "engines": { @@ -568,9 +568,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "dev": true, "license": "MIT", "dependencies": { @@ -676,9 +676,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -791,9 +791,9 @@ } }, "node_modules/@snyk/protect": { - "version": "1.1305.2", - "resolved": "https://registry.npmjs.org/@snyk/protect/-/protect-1.1305.2.tgz", - "integrity": "sha512-CCur62EuioUoSHAEJeeJDe13K0SzSMJh8ngjZfNv1VZE+fyQD2GIVkZox9rl2qCmN1CuSDFGErp9QpXp7Z034A==", + "version": "1.1308.3", + "resolved": "https://registry.npmjs.org/@snyk/protect/-/protect-1.1308.3.tgz", + "integrity": "sha512-AiNVAHTJMJlZGuhScIUz4JX8lxw98gY0/5D6UkUp1rgwc0tOATmmEbPFSnVmApl/pCaQlPr0jpMjCfxniccGeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -804,9 +804,9 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", "dev": true, "license": "MIT", "peer": true @@ -1053,16 +1053,16 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz", - "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", "dev": true, "license": "ISC" }, "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", "dev": true, "license": "MIT", "bin": { @@ -1206,18 +1206,18 @@ } }, "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.2.0.tgz", + "integrity": "sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "es-shim-unscopables": "^1.1.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" }, @@ -1354,9 +1354,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.40", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz", - "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==", + "version": "2.11.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.33.tgz", + "integrity": "sha512-yiVKDX+qCMRKBE084lXvq9EcwFUhKZ2nU8TK1EQQuOgPNR/lmbGxXOeDdlGUqC2Zz11Cs9rk1d/8NfgBmlkt0w==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1373,9 +1373,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "dev": true, "license": "MIT", "dependencies": { @@ -1403,9 +1403,9 @@ "license": "ISC" }, "node_modules/browserslist": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", - "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "version": "4.29.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.5.tgz", + "integrity": "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==", "dev": true, "funding": [ { @@ -1423,11 +1423,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.38", - "caniuse-lite": "^1.0.30001799", - "electron-to-chromium": "^1.5.376", - "node-releases": "^2.0.48", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.33", + "caniuse-lite": "^1.0.30001819", + "electron-to-chromium": "^1.5.464", + "node-releases": "^2.0.60", + "update-browserslist-db": "^1.3.4" }, "bin": { "browserslist": "cli.js" @@ -1523,9 +1523,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001799", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", - "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", "dev": true, "funding": [ { @@ -1544,9 +1544,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -2053,9 +2053,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.378", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", - "integrity": "sha512-VinvOAuuPmdD1guEgGv5f2Qp7/vlfqOrUOMYNnOD4wj3pit8kRsQHzfIf6teyUGWo15Tg5+bOJaRunvyltpVWQ==", + "version": "1.5.465", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.465.tgz", + "integrity": "sha512-zg4iUpbeGw7YNZsswc2xKr2uiBex2d8dELAXo92Icw3but1ULbrvK8USe6wVLaWjgJnKBARv0pchx9Owktx6wQ==", "dev": true, "license": "ISC" }, @@ -2254,13 +2254,14 @@ } }, "node_modules/es-to-primitive": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.3.tgz", - "integrity": "sha512-3OJmZ3m4VNhTWEfn/bPv7yoSd/7Kx0MYdm27o7SqgS45l03O4purW3zO4B/4JroAWHluvmQaXqrPXFtT4IKYEw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", "dev": true, "license": "MIT", "dependencies": { "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "is-callable": "^1.2.7", "is-date-object": "^1.1.0", @@ -2428,9 +2429,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.13.0.tgz", - "integrity": "sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", "dev": true, "license": "MIT", "dependencies": { @@ -2490,9 +2491,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -2679,9 +2680,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -2832,9 +2833,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", "dev": true, "license": "ISC", "dependencies": { @@ -2928,9 +2929,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -3422,9 +3423,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -3603,13 +3604,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.3" + "hasown": "^2.0.4" }, "engines": { "node": ">= 0.4" @@ -4081,16 +4082,16 @@ } }, "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/istanbul-lib-processinfo/node_modules/glob": { @@ -4112,9 +4113,9 @@ } }, "node_modules/istanbul-lib-processinfo/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -4122,13 +4123,13 @@ } }, "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -4258,9 +4259,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -4513,9 +4514,9 @@ } }, "node_modules/mocha": { - "version": "11.7.6", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.6.tgz", - "integrity": "sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.8.0.tgz", + "integrity": "sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==", "dev": true, "license": "MIT", "dependencies": { @@ -4622,9 +4623,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", - "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", "dev": true, "license": "MIT", "engines": { @@ -4696,16 +4697,16 @@ } }, "node_modules/nyc/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/nyc/node_modules/cliui": { @@ -4773,9 +4774,9 @@ } }, "node_modules/nyc/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -4783,13 +4784,13 @@ } }, "node_modules/nyc/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -5097,13 +5098,14 @@ } }, "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.6", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" }, @@ -5646,9 +5648,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -5989,9 +5991,9 @@ } }, "node_modules/sinon": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz", - "integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.1.0.tgz", + "integrity": "sha512-n1ajF2rBWMTtEwbKcw4UdFg4nCnDdq/U6RDoxtOd7oapOlRoJ5ynwFx60owROyhDpA9QhMZi0pCO/xtmwFjG7w==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6065,16 +6067,16 @@ } }, "node_modules/spawn-wrap/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/spawn-wrap/node_modules/foreground-child": { @@ -6110,9 +6112,9 @@ } }, "node_modules/spawn-wrap/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -6120,13 +6122,13 @@ } }, "node_modules/spawn-wrap/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -6242,9 +6244,9 @@ "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", "dev": true, "license": "MIT", "engines": { @@ -6432,16 +6434,16 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/test-exclude/node_modules/glob": { @@ -6463,9 +6465,9 @@ } }, "node_modules/test-exclude/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -6473,13 +6475,13 @@ } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -6714,19 +6716,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.5.tgz", + "integrity": "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", + "call-bind": "^1.0.9", + "for-each": "^0.3.5", "gopd": "^1.2.0", - "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" + "reflect.getprototypeof": "^1.0.10" }, "engines": { "node": ">= 0.4" @@ -6800,9 +6801,9 @@ } }, "node_modules/undici": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -6816,9 +6817,9 @@ "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", "dev": true, "funding": [ { @@ -6989,9 +6990,9 @@ "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.22", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", - "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.24.tgz", + "integrity": "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7087,9 +7088,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 808a2ed..5890654 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "dependencies": { "chardet": "^2.2.0", "cheerio": "^1.2.0", - "iconv-lite": "^0.7.2", - "undici": "^7.28.0" + "iconv-lite": "^0.7.3", + "undici": "^7.30.1" }, "files": [ "/dist", @@ -48,21 +48,21 @@ "CHANGELOG.md" ], "devDependencies": { - "@snyk/protect": "^1.1305.2", + "@snyk/protect": "^1.1308.3", "@types/mocha": "^10.0.10", "@types/node": "^20.19.43", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", - "chai": "^6.2.2", - "eslint": "^8.57.0", + "chai": "^6.3.0", + "eslint": "^8.57.1", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-mocha": "^10.5.0", "eslint-plugin-promise": "^7.2.1", - "mocha": "^11.7.6", + "mocha": "^11.8.0", "nyc": "^18.0.0", - "sinon": "^22.0.0", + "sinon": "^22.1.0", "ts-mocha": "^11.1.0", "typescript": "5.5.4" }, From 83f169e3c35efc8cad8e1f14e89aa666846e9d29 Mon Sep 17 00:00:00 2001 From: jshemas Date: Mon, 26 Oct 2026 23:12:13 -0400 Subject: [PATCH 03/12] adding SSRF protection --- README.md | 2 + lib/isPublicAddress.ts | 95 +++++++++++++++++++ lib/request.ts | 136 ++++++++++++++++++++++++++-- tests/unit/isPublicAddress.spec.ts | 41 +++++++++ tests/unit/openGraphScraper.spec.ts | 50 ++++++++++ types/lib/isPublicAddress.d.ts | 2 + types/lib/openGraphScraper.d.ts | 2 +- types/lib/request.d.ts | 2 +- 8 files changed, 319 insertions(+), 11 deletions(-) create mode 100644 lib/isPublicAddress.ts create mode 100644 tests/unit/isPublicAddress.spec.ts create mode 100644 types/lib/isPublicAddress.d.ts diff --git a/README.md b/README.md index d1ac103..42ed4d2 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Check the return for a ```success``` flag. If success is set to true, then the u Note: `open-graph-scraper` uses the [Fetch API](https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#fetch) for requests and most of [Fetch's options](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) should work as `open-graph-scraper`'s `fetchOptions` options. +For SSRF protection, requests are limited to HTTP(S) destinations whose resolved IP addresses are public. Redirect destinations are checked before they are requested. Redirect handling cannot be overridden and custom Undici dispatchers in `fetchOptions` are ignored because they could bypass these protections. + ## Types And Import Example ```javascript diff --git a/lib/isPublicAddress.ts b/lib/isPublicAddress.ts new file mode 100644 index 0000000..19c42f5 --- /dev/null +++ b/lib/isPublicAddress.ts @@ -0,0 +1,95 @@ +import { isIP } from 'node:net'; +import { lookup } from 'node:dns/promises'; + +function isBlockedIPv4(address: string) { + const octets = address.split('.').map(Number); + const [first, second, third] = octets; + + return first === 0 + || first === 10 + || (first === 100 && second >= 64 && second <= 127) + || first === 127 + || (first === 169 && second === 254) + || (first === 172 && second >= 16 && second <= 31) + || (first === 192 && second === 0 && third === 0) + || (first === 192 && second === 0 && third === 2) + || (first === 192 && second === 88 && third === 99) + || (first === 192 && second === 168) + || (first === 198 && (second === 18 || second === 19)) + || (first === 198 && second === 51 && third === 100) + || (first === 203 && second === 0 && third === 113) + || first >= 224; +} + +function parseIPv6(address: string) { + let normalized = address.toLowerCase(); + const ipv4Suffix = normalized.match(/(\d{1,3}(?:\.\d{1,3}){3})$/)?.[1]; + + if (ipv4Suffix) { + const octets = ipv4Suffix.split('.').map(Number); + if (octets.some((octet) => octet > 255)) return undefined; + const high = ((octets[0] ?? 0) * 256) + (octets[1] ?? 0); + const low = ((octets[2] ?? 0) * 256) + (octets[3] ?? 0); + normalized = normalized.replace(ipv4Suffix, `${high.toString(16)}:${low.toString(16)}`); + } + + const halves = normalized.split('::'); + if (halves.length > 2) return undefined; + const left = halves[0] ? halves[0].split(':') : []; + const right = halves[1] ? halves[1].split(':') : []; + const zeroCount = 8 - left.length - right.length; + if ((halves.length === 1 && zeroCount !== 0) || (halves.length === 2 && zeroCount < 1)) return undefined; + + const groups = [ + ...left, + ...Array.from({ length: zeroCount }, () => '0'), + ...right, + ].map((group) => Number.parseInt(group, 16)); + if (groups.length !== 8 || groups.some((group) => !Number.isInteger(group) || group < 0 || group > 0xffff)) { + return undefined; + } + + return groups; +} + +function isBlockedIPv6(address: string) { + const groups = parseIPv6(address); + if (!groups) return true; + + const first = groups[0] ?? 0; + const second = groups[1] ?? 0; + + return Math.floor(first / 0x2000) !== 1 + || (first === 0x2001 && second <= 0x01ff) + || (first === 0x2001 && second === 0x0db8) + || first === 0x2002 + || (first === 0x3fff && second <= 0x0fff); +} + +export function isPublicAddress(address: string) { + const version = isIP(address); + if (version === 4) return !isBlockedIPv4(address); + if (version === 6) return !isBlockedIPv6(address); + return false; +} + +export async function assertPublicUrl(input: string) { + const url = new URL(input); + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new Error('Only HTTP and HTTPS URLs are allowed'); + } + + const hostname = url.hostname.replace(/^\[|\]$/g, ''); + if (hostname.includes('%')) throw new Error('URL must not contain a scoped IP address'); + + const version = isIP(hostname); + if (version) { + if (!isPublicAddress(hostname)) throw new Error('Requests to non-public IP addresses are not allowed'); + return; + } + + const addresses = await lookup(hostname, { all: true, verbatim: true }); + if (addresses.length === 0 || addresses.some(({ address }) => !isPublicAddress(address))) { + throw new Error('Requests to hosts resolving to non-public IP addresses are not allowed'); + } +} diff --git a/lib/request.ts b/lib/request.ts index ef41e8f..eac1008 100644 --- a/lib/request.ts +++ b/lib/request.ts @@ -1,8 +1,125 @@ -import { fetch } from 'undici'; +import { lookup as dnsLookup } from 'node:dns'; +import { + Agent, fetch, getGlobalDispatcher, Headers, type RequestInit, +} from 'undici'; import { decode } from 'iconv-lite'; import { CheerioAPI, load } from 'cheerio'; import chardet from 'chardet'; import type { OpenGraphScraperOptions } from './types'; +import { assertPublicUrl, isPublicAddress } from './isPublicAddress'; + +const redirectStatuses = new Set([301, 302, 303, 307, 308]); +const initialGlobalDispatcher = getGlobalDispatcher(); + +function createRequestDispatcher() { + const globalDispatcher = getGlobalDispatcher(); + if (globalDispatcher !== initialGlobalDispatcher) { + return { dispatcher: globalDispatcher, close: () => Promise.resolve() }; + } + + const dispatcher = new Agent({ + connect: { + lookup(hostname, options, callback) { + dnsLookup(hostname, { all: true, verbatim: true }, (error, addresses) => { + if (error) { + callback(error, '', 0); + return; + } + if (addresses.some(({ address }) => !isPublicAddress(address))) { + callback(Object.assign(new Error('Requests to hosts resolving to non-public IP addresses are not allowed'), { + code: 'EACCES', + }), '', 0); + return; + } + + let family: number | undefined; + switch (options.family) { + case 'IPv4': + family = 4; + break; + case 'IPv6': + family = 6; + break; + case undefined: + family = undefined; + break; + default: + family = options.family; + } + const matchingAddresses = addresses.filter(({ family: addressFamily }) => ( + family === undefined || family === 0 || addressFamily === family + )); + if (matchingAddresses.length === 0) { + callback(Object.assign(new Error('No public IP addresses found for host'), { code: 'ENOTFOUND' }), '', 0); + return; + } + if (options.all) { + callback(null, matchingAddresses); + return; + } + + const address = matchingAddresses[0]; + if (!address) { + callback(Object.assign(new Error('No public IP addresses found for host'), { code: 'ENOTFOUND' }), '', 0); + return; + } + callback(null, address.address, address.family); + }); + }, + }, + }); + return { dispatcher, close: () => dispatcher.close() }; +} + +async function fetchWithPublicDestinationGuard(url: string, options: RequestInit) { + const redirectMode = options.redirect ?? 'follow'; + async function fetchRedirect(currentUrl: string, redirects: number, requestOptions: RequestInit): Promise { + await assertPublicUrl(currentUrl); + const response = await fetch(currentUrl, { ...requestOptions, redirect: 'manual' }); + const location = response.headers.get('location'); + if (!location || !redirectStatuses.has(response.status)) return response; + if (redirectMode === 'manual') return response; + if (redirectMode === 'error') { + await response.body?.cancel(); + throw new Error('Request was redirected'); + } + if (redirects >= 20) { + await response.body?.cancel(); + throw new Error('Maximum redirect count exceeded'); + } + + const nextUrl = new URL(location, currentUrl).toString(); + const nextOptions: RequestInit = { ...requestOptions }; + const headers = new Headers(requestOptions.headers); + const currentOrigin = new URL(currentUrl).origin; + const nextOrigin = new URL(nextUrl).origin; + + if (currentOrigin !== nextOrigin) { + headers.delete('authorization'); + headers.delete('cookie'); + headers.delete('cookie2'); + headers.delete('host'); + headers.delete('proxy-authorization'); + } + + const method = (requestOptions.method ?? 'GET').toUpperCase(); + if (((response.status === 301 || response.status === 302) && method === 'POST') + || (response.status === 303 && method !== 'GET' && method !== 'HEAD')) { + nextOptions.method = 'GET'; + delete nextOptions.body; + headers.delete('content-encoding'); + headers.delete('content-language'); + headers.delete('content-location'); + headers.delete('content-type'); + } + + nextOptions.headers = headers; + await response.body?.cancel(); + return fetchRedirect(nextUrl, redirects + 1, nextOptions); + } + + return fetchRedirect(url, 0, options); +} /** * checks if an element exists @@ -47,6 +164,7 @@ function getCharset(body: string, buffer: ArrayBuffer, $: CheerioAPI) { export default async function requestAndResultsFormatter(options: OpenGraphScraperOptions) { let body; let response; + const requestDispatcher = createRequestDispatcher(); try { // eslint-disable-next-line no-control-regex const isLatin1 = /^[\u0000-\u00ff]{0,}$/; @@ -54,14 +172,12 @@ export default async function requestAndResultsFormatter(options: OpenGraphScrap let url = options.url ?? ''; if (!isLatin1.test(url)) url = encodeURI(url); - response = await fetch( - url ?? '', - { - signal: AbortSignal.timeout((options.timeout ?? 10) * 1000), - ...options.fetchOptions, - headers: { Origin: url ?? '', Accept: 'text/html', ...options.fetchOptions?.headers }, - }, - ); + response = await fetchWithPublicDestinationGuard(url, { + signal: AbortSignal.timeout((options.timeout ?? 10) * 1000), + ...options.fetchOptions, + dispatcher: requestDispatcher.dispatcher, + headers: { Origin: url, Accept: 'text/html', ...options.fetchOptions?.headers }, + }); const bodyArrayBuffer = await response.arrayBuffer(); const bodyText = Buffer.from(bodyArrayBuffer).toString('utf-8'); @@ -111,6 +227,8 @@ export default async function requestAndResultsFormatter(options: OpenGraphScrap } catch (error) { if (error instanceof Error && error.message === 'fetch failed') throw error.cause; throw error; + } finally { + await requestDispatcher.close(); } return { body, response }; diff --git a/tests/unit/isPublicAddress.spec.ts b/tests/unit/isPublicAddress.spec.ts new file mode 100644 index 0000000..554c87d --- /dev/null +++ b/tests/unit/isPublicAddress.spec.ts @@ -0,0 +1,41 @@ +import { expect } from 'chai'; +import { assertPublicUrl, isPublicAddress } from '../../lib/isPublicAddress'; + +describe('public address validation', function () { + it('allows public IPv4 and IPv6 addresses', function () { + expect(isPublicAddress('8.8.8.8')).to.equal(true); + expect(isPublicAddress('2001:4860:4860::8888')).to.equal(true); + }); + + it('blocks private, loopback, link-local, reserved, and mapped IP addresses', function () { + [ + '0.1.2.3', + '10.0.0.1', + '100.64.0.1', + '127.0.0.1', + '169.254.169.254', + '172.16.0.1', + '192.168.1.1', + '198.18.0.1', + '224.0.0.1', + '::', + '::1', + '::ffff:127.0.0.1', + 'fc00::1', + 'fe80::1', + '2001:db8::1', + '2002::1', + 'ff02::1', + ].forEach((address) => expect(isPublicAddress(address), address).to.equal(false)); + }); + + it('rejects non-HTTP protocols and private IP literals', async function () { + const protocolError = await assertPublicUrl('file:///etc/passwd') + .then(() => undefined, (error: Error) => error.message); + const addressError = await assertPublicUrl('http://127.0.0.1/') + .then(() => undefined, (error: Error) => error.message); + + expect(protocolError).to.equal('Only HTTP and HTTPS URLs are allowed'); + expect(addressError).to.contain('non-public IP addresses'); + }); +}); diff --git a/tests/unit/openGraphScraper.spec.ts b/tests/unit/openGraphScraper.spec.ts index f7169bd..bd7c5d3 100644 --- a/tests/unit/openGraphScraper.spec.ts +++ b/tests/unit/openGraphScraper.spec.ts @@ -70,6 +70,56 @@ describe('return ogs', function () { }); context('should be able to hit site and find OG title info', function () { + it('follows redirects to public IP addresses', function () { + mockAgent.get('http://1.1.1.1') + .intercept({ path: '/' }) + .reply(302, '', { headers: { location: 'http://8.8.8.8/' } }); + mockAgent.get('http://8.8.8.8') + .intercept({ path: '/' }) + .reply(200, basicHTML); + + return ogs({ url: 'http://1.1.1.1' }) + .then(function ({ error, result }) { + expect(error).to.equal(false); + expect(result.ogTitle).to.equal('test page'); + }); + }); + + it('blocks redirects to private IP addresses', function () { + mockAgent.get('http://1.1.1.1') + .intercept({ path: '/' }) + .reply(302, '', { headers: { location: 'http://127.0.0.1/' } }); + + return ogs({ url: 'http://1.1.1.1' }) + .then(function () { + expect().fail('the redirect should have been blocked'); + }) + .catch(function ({ error, result }) { + expect(error).to.equal(true); + expect(result.error).to.contain('non-public IP addresses'); + }); + }); + + it('does not allow fetch options to re-enable automatic redirects', function () { + mockAgent.get('http://1.1.1.1') + .intercept({ path: '/' }) + .reply(302, '', { headers: { location: 'http://127.0.0.1/' } }); + + const bypassDispatcher = new MockAgent(); + return ogs({ + url: 'http://1.1.1.1', + fetchOptions: { redirect: 'follow', dispatcher: bypassDispatcher }, + }) + .then(function () { + expect().fail('the redirect should have been blocked'); + }) + .catch(function ({ error, result }) { + expect(error).to.equal(true); + expect(result.error).to.contain('non-public IP addresses'); + }) + .finally(() => bypassDispatcher.close()); + }); + it('using just a url', function () { mockAgent.get('http://www.test.com') .intercept({ path: '/' }) diff --git a/types/lib/isPublicAddress.d.ts b/types/lib/isPublicAddress.d.ts new file mode 100644 index 0000000..e06c404 --- /dev/null +++ b/types/lib/isPublicAddress.d.ts @@ -0,0 +1,2 @@ +export declare function isPublicAddress(address: string): boolean; +export declare function assertPublicUrl(input: string): Promise; diff --git a/types/lib/openGraphScraper.d.ts b/types/lib/openGraphScraper.d.ts index 70cf930..251bea1 100644 --- a/types/lib/openGraphScraper.d.ts +++ b/types/lib/openGraphScraper.d.ts @@ -14,6 +14,6 @@ export default function setOptionsAndReturnOpenGraphResults(ogsOptions: OpenGrap html: string; } | { ogObject: import("./types").OgObjectInternal; - response: import("undici").Response; + response: Response; html: string; }>; diff --git a/types/lib/request.d.ts b/types/lib/request.d.ts index e66286c..eea5217 100644 --- a/types/lib/request.d.ts +++ b/types/lib/request.d.ts @@ -8,5 +8,5 @@ import type { OpenGraphScraperOptions } from './types'; */ export default function requestAndResultsFormatter(options: OpenGraphScraperOptions): Promise<{ body: string; - response: import("undici").Response; + response: Response; }>; From df0ee15234f2bfe45b1b47c0ef71de365e886ac8 Mon Sep 17 00:00:00 2001 From: jshemas Date: Mon, 26 Oct 2026 23:42:45 -0400 Subject: [PATCH 04/12] adding a Security Policy --- SECURITY.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a51342e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,26 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 6.x.x | :white_check_mark: | +| < 5.0 | :x: | + +## Reporting a Vulnerability + +We value the insights of the security community and encourage responsible disclosure. If you discover a vulnerability, please do not use public GitHub issues, discussions, or pull requests to report it. + +### How to Disclose +Instead of a public post, please report vulnerabilities through the following channels: + +* **GitHub Private Vulnerability Reporting:** Navigate to the "Security" tab of this repository, select "Vulnerability reporting" from the sidebar, and click "Report a vulnerability". + +### What to Include +To help us understand and fix the issue quickly, please include: +1. **Description:** The type of issue (e.g., SQL injection, Remote Code Execution). +2. **Location:** The affected file paths, branches, or direct URLs. +3. **Steps to Reproduce:** A clear description or proof-of-concept (PoC) script. +4. **Impact:** How an attacker could exploit this vulnerability. + +Thank you for helping keep our software secure! From 5d23282ab0c9875eace3440e23a1820ef59215ca Mon Sep 17 00:00:00 2001 From: jshemas Date: Mon, 26 Oct 2026 23:55:03 -0400 Subject: [PATCH 05/12] =?UTF-8?q?Always=20uses=20OGS=E2=80=99s=20DNS-guard?= =?UTF-8?q?ed=20Undici=20Agent,=20rather=20than=20switching=20to=20a=20glo?= =?UTF-8?q?bal=20dispatcher=20that=20may=20have=20changed=20after=20module?= =?UTF-8?q?=20initialization.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- lib/request.ts | 8 +------- tests/unit/openGraphScraper.spec.ts | 29 ++++++++++++++++++++++++++++- tests/unit/static.spec.ts | 11 ++++++++++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 42ed4d2..ead322b 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Check the return for a ```success``` flag. If success is set to true, then the u Note: `open-graph-scraper` uses the [Fetch API](https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#fetch) for requests and most of [Fetch's options](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) should work as `open-graph-scraper`'s `fetchOptions` options. -For SSRF protection, requests are limited to HTTP(S) destinations whose resolved IP addresses are public. Redirect destinations are checked before they are requested. Redirect handling cannot be overridden and custom Undici dispatchers in `fetchOptions` are ignored because they could bypass these protections. +For SSRF protection, requests are limited to HTTP(S) destinations whose resolved IP addresses are public. Redirect destinations are checked before they are requested. Redirect handling cannot be overridden, and custom Undici dispatchers supplied in `fetchOptions` or configured globally are ignored because they could bypass these protections. ## Types And Import Example diff --git a/lib/request.ts b/lib/request.ts index eac1008..9a9bb72 100644 --- a/lib/request.ts +++ b/lib/request.ts @@ -1,6 +1,6 @@ import { lookup as dnsLookup } from 'node:dns'; import { - Agent, fetch, getGlobalDispatcher, Headers, type RequestInit, + Agent, fetch, Headers, type RequestInit, } from 'undici'; import { decode } from 'iconv-lite'; import { CheerioAPI, load } from 'cheerio'; @@ -9,14 +9,8 @@ import type { OpenGraphScraperOptions } from './types'; import { assertPublicUrl, isPublicAddress } from './isPublicAddress'; const redirectStatuses = new Set([301, 302, 303, 307, 308]); -const initialGlobalDispatcher = getGlobalDispatcher(); function createRequestDispatcher() { - const globalDispatcher = getGlobalDispatcher(); - if (globalDispatcher !== initialGlobalDispatcher) { - return { dispatcher: globalDispatcher, close: () => Promise.resolve() }; - } - const dispatcher = new Agent({ connect: { lookup(hostname, options, callback) { diff --git a/tests/unit/openGraphScraper.spec.ts b/tests/unit/openGraphScraper.spec.ts index bd7c5d3..97258f5 100644 --- a/tests/unit/openGraphScraper.spec.ts +++ b/tests/unit/openGraphScraper.spec.ts @@ -2,7 +2,10 @@ import { expect } from 'chai'; import sinon from 'sinon'; import chardet from 'chardet'; -import { MockAgent, setGlobalDispatcher } from 'undici'; +import undici = require('undici'); +import { + getGlobalDispatcher, MockAgent, setGlobalDispatcher, +} from 'undici'; import { encode } from 'iconv-lite'; import ogs from '../../index'; @@ -57,16 +60,25 @@ const encodingHTML = ` const sandbox = sinon.createSandbox(); const mockAgent = new MockAgent(); +const originalFetch = undici.fetch; +const originalGlobalDispatcher = getGlobalDispatcher(); +let requestDispatchers: unknown[] = []; describe('return ogs', function () { beforeEach(function () { setGlobalDispatcher(mockAgent); mockAgent.disableNetConnect(); + requestDispatchers = []; + sandbox.stub(undici, 'fetch').callsFake((input, init) => { + requestDispatchers.push(init?.dispatcher); + return originalFetch(input, { ...init, dispatcher: mockAgent }); + }); }); afterEach(function () { sandbox.restore(); mockAgent.enableNetConnect(); + setGlobalDispatcher(originalGlobalDispatcher); }); context('should be able to hit site and find OG title info', function () { @@ -120,6 +132,21 @@ describe('return ogs', function () { .finally(() => bypassDispatcher.close()); }); + it('does not use a global dispatcher changed after module load', function () { + const bypassDispatcher = new MockAgent(); + setGlobalDispatcher(bypassDispatcher); + mockAgent.get('http://www.test.com') + .intercept({ path: '/' }) + .reply(200, basicHTML); + + return ogs({ url: 'http://www.test.com' }) + .then(function ({ result }) { + expect(result.success).to.equal(true); + expect(requestDispatchers[0]).not.to.equal(bypassDispatcher); + }) + .finally(() => bypassDispatcher.close()); + }); + it('using just a url', function () { mockAgent.get('http://www.test.com') .intercept({ path: '/' }) diff --git a/tests/unit/static.spec.ts b/tests/unit/static.spec.ts index 25af81a..32a4989 100644 --- a/tests/unit/static.spec.ts +++ b/tests/unit/static.spec.ts @@ -2,22 +2,31 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { encode } from 'iconv-lite'; -import { MockAgent, setGlobalDispatcher } from 'undici'; +import undici = require('undici'); +import { + getGlobalDispatcher, MockAgent, setGlobalDispatcher, +} from 'undici'; import ogs from '../../index'; const sandbox = sinon.createSandbox(); const mockAgent = new MockAgent(); +const originalFetch = undici.fetch; +const originalGlobalDispatcher = getGlobalDispatcher(); describe('static check meta tags', function () { beforeEach(function () { setGlobalDispatcher(mockAgent); mockAgent.disableNetConnect(); + sandbox.stub(undici, 'fetch').callsFake((input, init) => ( + originalFetch(input, { ...init, dispatcher: mockAgent }) + )); }); afterEach(function () { sandbox.restore(); mockAgent.enableNetConnect(); + setGlobalDispatcher(originalGlobalDispatcher); }); it('check one off tags', function () { From 1a3514366b7fc374c7513bde85ec761aab11d8ea Mon Sep 17 00:00:00 2001 From: jshemas Date: Tue, 27 Oct 2026 00:08:42 -0400 Subject: [PATCH 06/12] Removed ts-mocha and replaced it with a direct ts-node dev dependency. --- package-lock.json | 80 ++++++----------------------------------------- package.json | 6 ++-- 2 files changed, 12 insertions(+), 74 deletions(-) diff --git a/package-lock.json b/package-lock.json index db55803..80ed000 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "mocha": "^11.8.0", "nyc": "^18.0.0", "sinon": "^22.1.0", - "ts-mocha": "^11.1.0", + "ts-node": "^10.9.2", "typescript": "5.5.4" }, "engines": { @@ -310,7 +310,6 @@ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -324,7 +323,6 @@ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -808,32 +806,28 @@ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -1088,7 +1082,6 @@ "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "acorn": "^8.11.0" }, @@ -1178,8 +1171,7 @@ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", @@ -1752,8 +1744,7 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -4440,8 +4431,7 @@ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -6540,36 +6530,12 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-11.1.0.tgz", - "integrity": "sha512-yT7FfzNRCu8ZKkYvAOiH01xNma/vLq6Vit7yINKYFNVP8e5UyrYXSOMIipERTpzVKJQ4Qcos5bQo1tNERNZevQ==", - "dev": true, - "license": "MIT", - "bin": { - "ts-mocha": "bin/ts-mocha" - }, - "engines": { - "node": ">= 6.X.X" - }, - "peerDependencies": { - "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X || ^11.X.X", - "ts-node": "^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X", - "tsconfig-paths": "^4.X.X" - }, - "peerDependenciesMeta": { - "tsconfig-paths": { - "optional": true - } - } - }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -6614,36 +6580,10 @@ "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.3.1" } }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6862,8 +6802,7 @@ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/whatwg-encoding": { "version": "3.1.1", @@ -7272,7 +7211,6 @@ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 5890654..2204159 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "ci": "npm run eslint && npm run build && npm run build:declaration && npm run test", "eslint:fix": "eslint . --ext .js,.ts --fix", "eslint": "eslint . --ext .js,.ts", - "mocha:int": "ts-mocha --recursive \"./tests/integration/**/*.spec.ts\" --timeout 10000", - "mocha:unit": "nyc --reporter=html --reporter=text --exclude=tests/ ts-mocha --recursive \"./tests/unit/**/*.spec.ts\"", + "mocha:int": "mocha --require ts-node/register/transpile-only --recursive \"./tests/integration/**/*.spec.ts\" --timeout 10000", + "mocha:unit": "nyc --reporter=html --reporter=text --exclude=tests/ mocha --require ts-node/register/transpile-only --recursive \"./tests/unit/**/*.spec.ts\"", "prepare": "npm run snyk-protect && npm run build", "snyk-protect": "snyk-protect", "test": "npm run mocha:unit && npm run mocha:int" @@ -63,7 +63,7 @@ "mocha": "^11.8.0", "nyc": "^18.0.0", "sinon": "^22.1.0", - "ts-mocha": "^11.1.0", + "ts-node": "^10.9.2", "typescript": "5.5.4" }, "repository": { From 94e36ae7a959bfcaee74c16b9ad819aae8fd7836 Mon Sep 17 00:00:00 2001 From: jshemas Date: Tue, 27 Oct 2026 00:10:58 -0400 Subject: [PATCH 07/12] updating mocha --- package-lock.json | 1428 +++++++++++++-------------------------------- package.json | 2 +- 2 files changed, 403 insertions(+), 1027 deletions(-) diff --git a/package-lock.json b/package-lock.json index 80ed000..73101fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-mocha": "^10.5.0", "eslint-plugin-promise": "^7.2.1", - "mocha": "^11.8.0", + "mocha": "^12.0.3", "nyc": "^18.0.0", "sinon": "^22.1.0", "ts-node": "^10.9.2", @@ -477,53 +477,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -729,17 +682,6 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1611,16 +1553,16 @@ } }, "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "readdirp": "^5.0.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -1637,58 +1579,15 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, "node_modules/color-convert": { @@ -1931,9 +1830,9 @@ } }, "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -2036,13 +1935,6 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/electron-to-chromium": { "version": "1.5.465", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.465.tgz", @@ -2051,9 +1943,9 @@ "license": "ISC" }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, @@ -2515,19 +2407,6 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-import/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/eslint-plugin-import/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -2551,29 +2430,6 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-import/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, "node_modules/eslint-plugin-mocha": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", @@ -2894,14 +2750,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -2959,6 +2818,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -3129,22 +3001,18 @@ } }, "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3163,6 +3031,45 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -3365,16 +3272,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3793,16 +3690,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -4062,133 +3949,49 @@ "node": "20 || >=22" } }, - "node_modules/istanbul-lib-processinfo/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "balanced-match": "^4.0.2" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "20 || >=22" + "node": ">=10" } }, - "node_modules/istanbul-lib-processinfo/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/lru-cache": { - "version": "11.5.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", - "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", - "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "glob": "^13.0.3", - "package-json-from-dist": "^1.0.1" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" + "semver": "^7.5.3" }, "engines": { "node": ">=10" @@ -4226,22 +4029,6 @@ "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4373,23 +4160,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4504,40 +4274,139 @@ } }, "node_modules/mocha": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.8.0.tgz", - "integrity": "sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-12.0.3.tgz", + "integrity": "sha512-beammieDdiWdkjANWeWPpkxUROYGi5FN88fPOkLiticz7HTg3Y+Pho3EYE6SUdcuWnZ3h2WD8EXCA55JF4o6wg==", "dev": true, "license": "MIT", "dependencies": { "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", + "chokidar": "^5.0.0", "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", + "diff": "^9.0.0", + "find-up-simple": "^1.0.1", + "glob": "^13.0.0", + "is-path-inside": "^4.0.0", + "is-unicode-supported": "^0.1.0", + "js-yaml": "^5.0.0", + "minimatch": "^10.2.2", "ms": "^2.1.3", "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", + "serialize-javascript": "^7.1.1", + "strip-json-comments": "^5.0.3", "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" + "workerpool": "^10.0.0" }, "bin": { - "_mocha": "bin/_mocha", "mocha": "bin/mocha.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.4.tgz", + "integrity": "sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "PSF-2.0" + }, + "node_modules/mocha/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/mocha/node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.5.0.tgz", + "integrity": "sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^3.0.3" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mocha/node_modules/supports-color": { @@ -4676,48 +4545,6 @@ "node": "20 || >=22" } }, - "node_modules/nyc/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/nyc/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -4732,24 +4559,6 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4763,32 +4572,6 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/lru-cache": { - "version": "11.5.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", - "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/nyc/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -4800,150 +4583,52 @@ }, "engines": { "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/rimraf": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", - "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "glob": "^13.0.3", - "package-json-from-dist": "^1.0.1" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nyc/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/nyc/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, "engines": { "node": ">=8" } }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/nyc/node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=6" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/object-inspect": { @@ -5284,28 +4969,31 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/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==", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } }, "node_modules/path-type": { "version": "4.0.0", @@ -5477,24 +5165,14 @@ "dev": true, "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">= 20.19.0" }, "funding": { "type": "individual", @@ -5727,27 +5405,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -5803,13 +5460,13 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.1.4.tgz", + "integrity": "sha512-FqlEuoTq/zRmA4Lg6aOqyH3lCHbUtCB0bmHCeFniX2GBdDqMJM3FwdvEhlE5Pim1dlt2FfBdVPmUNNiGhhKqpw==", "dev": true, "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">=20.0.0" } }, "node_modules/set-blocking": { @@ -5968,17 +5625,11 @@ } }, "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "ISC" }, "node_modules/sinon": { "version": "22.1.0", @@ -5997,16 +5648,6 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/diff": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", - "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6046,29 +5687,6 @@ "node": ">=8" } }, - "node_modules/spawn-wrap/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/spawn-wrap/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/spawn-wrap/node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -6083,67 +5701,6 @@ "node": ">=8.0.0" } }, - "node_modules/spawn-wrap/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/spawn-wrap/node_modules/lru-cache": { - "version": "11.5.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", - "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/spawn-wrap/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/spawn-wrap/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/spawn-wrap/node_modules/rimraf": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", @@ -6164,13 +5721,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/spawn-wrap/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6180,86 +5730,31 @@ }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.2.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/string.prototype.trim": { @@ -6335,20 +5830,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -6436,34 +5917,6 @@ "node": "20 || >=22" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/lru-cache": { - "version": "11.5.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", - "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/test-exclude/node_modules/minimatch": { "version": "10.2.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", @@ -6480,23 +5933,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6584,6 +6020,42 @@ "node": ">=0.3.1" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6961,35 +6433,16 @@ } }, "node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-10.0.3.tgz", + "integrity": "sha512-6z2Iis68Wqth93/G/wJP9u+R3O+d2XTlgWChGCwuT1qLbBsOYueGRZuJ++v3mtDP5KjYdy+WzvWC+VWETSVXJA==", "dev": true, "license": "Apache-2.0" }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "license": "MIT", "dependencies": { @@ -6997,77 +6450,10 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7088,22 +6474,12 @@ "typedarray-to-buffer": "^3.1.5" } }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } + "license": "ISC" }, "node_modules/yallist": { "version": "3.1.1", @@ -7113,93 +6489,93 @@ "license": "ISC" }, "node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "p-locate": "^4.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" diff --git a/package.json b/package.json index 2204159..c98040b 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-mocha": "^10.5.0", "eslint-plugin-promise": "^7.2.1", - "mocha": "^11.8.0", + "mocha": "^12.0.3", "nyc": "^18.0.0", "sinon": "^22.1.0", "ts-node": "^10.9.2", From 2c585f7bbe39b644f4aff3a9b9976d86963ec1bb Mon Sep 17 00:00:00 2001 From: jshemas Date: Tue, 27 Oct 2026 00:30:02 -0400 Subject: [PATCH 08/12] Blacklist now applies to redirect destinations. --- README.md | 2 +- index.ts | 2 +- lib/openGraphScraper.ts | 4 ++-- lib/request.ts | 8 ++++++-- lib/types.ts | 2 +- lib/utils.ts | 11 +++++++++++ tests/unit/openGraphScraper.spec.ts | 15 +++++++++++++++ types/index.d.ts | 2 +- types/lib/types.d.ts | 2 +- types/lib/utils.d.ts | 8 ++++++++ 10 files changed, 47 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ead322b..b3388a4 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Check the return for a ```success``` flag. If success is set to true, then the u | html | You can pass in an HTML string to run ogs on it. (use without options.url) | | | | fetchOptions | Options that are used by the Fetch API | {} | | | timeout | Request timeout for Fetch (Default is 10 seconds) | 10 | | -| blacklist | Pass in an array of sites you don't want ogs to run on. | [] | | +| blacklist | Pass in site values to block, including redirect destinations. | [] | | | onlyGetOpenGraphInfo | Only fetch open graph info and don't fall back on anything else. Also accepts an array of properties for which no fallback should be used | false | | | customMetaTags | Here you can define custom meta tags you want to scrape. | [] | | | urlValidatorSettings | Sets the options used by validator.js for testing the URL | [Here](https://github.com/jshemas/openGraphScraper/blob/master/lib/utils.ts#L4-L17) | | diff --git a/index.ts b/index.ts index d615ed8..35ac963 100644 --- a/index.ts +++ b/index.ts @@ -16,7 +16,7 @@ import type { * @param {object} [options.customMetaTags] - Here you can define custom meta tags you want to scrape. * @param {object} [options.fetchOptions] - Sets the options used by fetch for the http requests * @param {object} [options.urlValidatorSettings] - Sets the options used by validator.js for testing the URL - * @param {string[]} [options.blacklist] - Pass in an array of sites you don't want ogs to run on. + * @param {string[]} [options.blacklist] - Pass in site values to block, including redirect destinations. * @param {string} [options.html] - You can pass in an HTML string to run ogs on it. (use without options.url) * @param {number} [options.timeout] - Number of seconds before the fetch request ends. (default is 10 seconds) * @param {string} options.url - URL of the site. (Required) diff --git a/lib/openGraphScraper.ts b/lib/openGraphScraper.ts index d404ab9..1b71eb9 100644 --- a/lib/openGraphScraper.ts +++ b/lib/openGraphScraper.ts @@ -2,6 +2,7 @@ import extractMetaTags from './extract'; import requestAndResultsFormatter from './request'; import { defaultUrlValidatorSettings, + isBlacklistedUrl, isCustomMetaTagsValid, isThisANonHTMLUrl, optionSetup, @@ -38,8 +39,7 @@ export default async function setOptionsAndReturnOpenGraphResults(ogsOptions: Op // trying to limit non html pages if (isThisANonHTMLUrl(options.url)) throw new Error('Must scrape an HTML page'); - // eslint-disable-next-line max-len - if (options?.blacklist?.some((blacklistedHostname) => options.url?.includes(blacklistedHostname))) { + if (isBlacklistedUrl(options.url, options.blacklist)) { throw new Error('Host name has been black listed'); } diff --git a/lib/request.ts b/lib/request.ts index 9a9bb72..23b57ad 100644 --- a/lib/request.ts +++ b/lib/request.ts @@ -7,6 +7,7 @@ import { CheerioAPI, load } from 'cheerio'; import chardet from 'chardet'; import type { OpenGraphScraperOptions } from './types'; import { assertPublicUrl, isPublicAddress } from './isPublicAddress'; +import { isBlacklistedUrl } from './utils'; const redirectStatuses = new Set([301, 302, 303, 307, 308]); @@ -65,9 +66,12 @@ function createRequestDispatcher() { return { dispatcher, close: () => dispatcher.close() }; } -async function fetchWithPublicDestinationGuard(url: string, options: RequestInit) { +async function fetchWithPublicDestinationGuard(url: string, options: RequestInit, blacklist: string[] = []) { const redirectMode = options.redirect ?? 'follow'; async function fetchRedirect(currentUrl: string, redirects: number, requestOptions: RequestInit): Promise { + if (isBlacklistedUrl(currentUrl, blacklist)) { + throw new Error('Host name has been black listed'); + } await assertPublicUrl(currentUrl); const response = await fetch(currentUrl, { ...requestOptions, redirect: 'manual' }); const location = response.headers.get('location'); @@ -171,7 +175,7 @@ export default async function requestAndResultsFormatter(options: OpenGraphScrap ...options.fetchOptions, dispatcher: requestDispatcher.dispatcher, headers: { Origin: url, Accept: 'text/html', ...options.fetchOptions?.headers }, - }); + }, options.blacklist); const bodyArrayBuffer = await response.arrayBuffer(); const bodyText = Buffer.from(bodyArrayBuffer).toString('utf-8'); diff --git a/lib/types.ts b/lib/types.ts index 99fcc6b..78958af 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -22,7 +22,7 @@ export type OnlyGetOpenGraphInfoItem = 'image' | 'title' | 'description' | 'loca * * @typeParam {string} url - URL of the site. (Required) * @typeParam {string} [html] - You can pass in an HTML string to run ogs on it. (use without options.url) - * @typeParam {string[]} [blacklist] - Pass in an array of sites you don't want ogs to run on. + * @typeParam {string[]} [blacklist] - Pass in site values to block, including redirect destinations. * @typeParam {boolean | OnlyGetOpenGraphInfoItem[]} [onlyGetOpenGraphInfo] - Only fetch open graph info and don't fall back on anything else. * @typeParam {CustomMetaTags} [customMetaTags] - Here you can define custom meta tags you want to scrape. * @typeParam {Request} [fetchOptions] - The options passed into fetch. diff --git a/lib/utils.ts b/lib/utils.ts index ee73b9f..7343da2 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -54,6 +54,17 @@ export function validateAndFormatURL(url: string, urlValidatorSettings: Validato return { url: isUrlValid(url, urlValidatorSettings) ? coerceUrl(url) : null }; } +/** + * Checks whether a URL contains a blacklisted value. + * + * @param {string} url - url to be checked + * @param {string[]} blacklist - values that should be blocked + * @return {boolean} whether the URL is blacklisted + */ +export function isBlacklistedUrl(url: string, blacklist: string[] = []): boolean { + return blacklist.some((blacklistedHostname) => url.includes(blacklistedHostname)); +} + /** * Finds the image type from a given url * diff --git a/tests/unit/openGraphScraper.spec.ts b/tests/unit/openGraphScraper.spec.ts index 97258f5..2163847 100644 --- a/tests/unit/openGraphScraper.spec.ts +++ b/tests/unit/openGraphScraper.spec.ts @@ -112,6 +112,21 @@ describe('return ogs', function () { }); }); + it('blocks redirects to blacklisted URLs', function () { + mockAgent.get('http://1.1.1.1') + .intercept({ path: '/' }) + .reply(302, '', { headers: { location: 'http://8.8.8.8/' } }); + + return ogs({ url: 'http://1.1.1.1', blacklist: ['8.8.8.8'] }) + .then(function () { + expect().fail('the redirect should have been blocked'); + }) + .catch(function ({ error, result }) { + expect(error).to.equal(true); + expect(result.error).to.equal('Host name has been black listed'); + }); + }); + it('does not allow fetch options to re-enable automatic redirects', function () { mockAgent.get('http://1.1.1.1') .intercept({ path: '/' }) diff --git a/types/index.d.ts b/types/index.d.ts index f1a707c..15dd33b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -9,7 +9,7 @@ import type { ErrorResult, OpenGraphScraperOptions, SuccessResult } from './lib/ * @param {object} [options.customMetaTags] - Here you can define custom meta tags you want to scrape. * @param {object} [options.fetchOptions] - Sets the options used by fetch for the http requests * @param {object} [options.urlValidatorSettings] - Sets the options used by validator.js for testing the URL - * @param {string[]} [options.blacklist] - Pass in an array of sites you don't want ogs to run on. + * @param {string[]} [options.blacklist] - Pass in site values to block, including redirect destinations. * @param {string} [options.html] - You can pass in an HTML string to run ogs on it. (use without options.url) * @param {number} [options.timeout] - Number of seconds before the fetch request ends. (default is 10 seconds) * @param {string} options.url - URL of the site. (Required) diff --git a/types/lib/types.d.ts b/types/lib/types.d.ts index 16bc8a7..5f60752 100644 --- a/types/lib/types.d.ts +++ b/types/lib/types.d.ts @@ -17,7 +17,7 @@ export type OnlyGetOpenGraphInfoItem = 'image' | 'title' | 'description' | 'loca * * @typeParam {string} url - URL of the site. (Required) * @typeParam {string} [html] - You can pass in an HTML string to run ogs on it. (use without options.url) - * @typeParam {string[]} [blacklist] - Pass in an array of sites you don't want ogs to run on. + * @typeParam {string[]} [blacklist] - Pass in site values to block, including redirect destinations. * @typeParam {boolean | OnlyGetOpenGraphInfoItem[]} [onlyGetOpenGraphInfo] - Only fetch open graph info and don't fall back on anything else. * @typeParam {CustomMetaTags} [customMetaTags] - Here you can define custom meta tags you want to scrape. * @typeParam {Request} [fetchOptions] - The options passed into fetch. diff --git a/types/lib/utils.d.ts b/types/lib/utils.d.ts index 766c65b..7111750 100644 --- a/types/lib/utils.d.ts +++ b/types/lib/utils.d.ts @@ -33,6 +33,14 @@ export declare function isUrlValid(url: string, urlValidatorSettings: ValidatorS export declare function validateAndFormatURL(url: string, urlValidatorSettings: ValidatorSettings): { url: string | null; }; +/** + * Checks whether a URL contains a blacklisted value. + * + * @param {string} url - url to be checked + * @param {string[]} blacklist - values that should be blocked + * @return {boolean} whether the URL is blacklisted + */ +export declare function isBlacklistedUrl(url: string, blacklist?: string[]): boolean; /** * Finds the image type from a given url * From dc0fe063447be87b9be91dced5e4dd60f5a3178c Mon Sep 17 00:00:00 2001 From: jshemas Date: Tue, 27 Oct 2026 00:54:05 -0400 Subject: [PATCH 09/12] adding missing unit test cases --- tests/unit/isPublicAddress.spec.ts | 30 +++++++ tests/unit/openGraphScraper.spec.ts | 61 ++++++++++++- tests/unit/request.spec.ts | 134 ++++++++++++++++++++++++++++ tests/unit/utils.spec.ts | 68 ++++++++++++++ 4 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 tests/unit/request.spec.ts diff --git a/tests/unit/isPublicAddress.spec.ts b/tests/unit/isPublicAddress.spec.ts index 554c87d..6b7481a 100644 --- a/tests/unit/isPublicAddress.spec.ts +++ b/tests/unit/isPublicAddress.spec.ts @@ -29,6 +29,29 @@ describe('public address validation', function () { ].forEach((address) => expect(isPublicAddress(address), address).to.equal(false)); }); + it('returns false for malformed or non-IP input', function () { + [ + '', + 'example.com', + '1.2.3.4.5', + '256.1.1.1', + ':::', + '2001:db8:::1', + '2001:db8:zzzz::1', + ].forEach((address) => expect(isPublicAddress(address), address).to.equal(false)); + }); + + it('blocks reserved IPv4 and IPv6 ranges not already covered', function () { + [ + '0.0.0.0', + '192.0.0.1', + '198.51.100.1', + '203.0.113.1', + '2001:1::1', + '3fff:0fff::1', + ].forEach((address) => expect(isPublicAddress(address), address).to.equal(false)); + }); + it('rejects non-HTTP protocols and private IP literals', async function () { const protocolError = await assertPublicUrl('file:///etc/passwd') .then(() => undefined, (error: Error) => error.message); @@ -38,4 +61,11 @@ describe('public address validation', function () { expect(protocolError).to.equal('Only HTTP and HTTPS URLs are allowed'); expect(addressError).to.contain('non-public IP addresses'); }); + + it('rejects hostnames that resolve to a non-public IP', async function () { + const privateLookupError = await assertPublicUrl('http://localhost/') + .then(() => undefined, (error: Error) => error.message); + + expect(privateLookupError).to.contain('non-public IP addresses'); + }); }); diff --git a/tests/unit/openGraphScraper.spec.ts b/tests/unit/openGraphScraper.spec.ts index 2163847..8bc2fef 100644 --- a/tests/unit/openGraphScraper.spec.ts +++ b/tests/unit/openGraphScraper.spec.ts @@ -63,13 +63,31 @@ const mockAgent = new MockAgent(); const originalFetch = undici.fetch; const originalGlobalDispatcher = getGlobalDispatcher(); let requestDispatchers: unknown[] = []; +let fetchStub: sinon.SinonStub; + +function assertRequestFailure(code: string) { + mockAgent.get('http://www.testerror.com') + .intercept({ path: '/' }) + .replyWithError(Object.assign(new Error('request failed'), { code })); + + return ogs({ url: 'www.testerror.com' }) + .then(function () { + expect('').to.be.eql('this should not happen'); + }) + .catch(function (data) { + expect(data.error).to.be.eql(true); + expect(data.result.error).to.eql('Page not found'); + expect(data.result.errorDetails.toString()).to.eql('Error: Page not found'); + expect(data.result.success).to.eql(false); + }); +} describe('return ogs', function () { beforeEach(function () { setGlobalDispatcher(mockAgent); mockAgent.disableNetConnect(); requestDispatchers = []; - sandbox.stub(undici, 'fetch').callsFake((input, init) => { + fetchStub = sandbox.stub(undici, 'fetch').callsFake((input, init) => { requestDispatchers.push(init?.dispatcher); return originalFetch(input, { ...init, dispatcher: mockAgent }); }); @@ -465,6 +483,19 @@ describe('return ogs', function () { expect(data.response).to.be.a('response'); }); }); + + it('when passing in invalid custom tags', function () { + return ogs({ html: basicHTML, customMetaTags: [{} as never] }) + .then(function () { + expect('').to.be.eql('this should not happen'); + }) + .catch(function (data) { + expect(data.error).to.be.eql(true); + expect(data.result.error).to.eql('Invalid Custom Meta Tags'); + expect(data.result.errorDetails.toString()).to.eql('Error: Invalid Custom Meta Tags'); + expect(data.result.success).to.eql(false); + }); + }); }); context('should return the proper error data', function () { @@ -553,6 +584,34 @@ describe('return ogs', function () { }); }); + it('when the request failure code is ENOTFOUND', function () { + return assertRequestFailure('ENOTFOUND'); + }); + + it('when the request failure code is EHOSTUNREACH', function () { + return assertRequestFailure('EHOSTUNREACH'); + }); + + it('when the request failure code is ENETUNREACH', function () { + return assertRequestFailure('ENETUNREACH'); + }); + + it('when the request is aborted', function () { + const abortError = Object.assign(new Error('request aborted'), { name: 'AbortError' }); + fetchStub.rejects(Object.assign(new Error('fetch failed'), { cause: abortError })); + + return ogs({ url: 'www.testerror.com' }) + .then(function () { + expect('').to.be.eql('this should not happen'); + }) + .catch(function (data) { + expect(data.error).to.be.eql(true); + expect(data.result.error).to.eql('The operation was aborted due to timeout'); + expect(data.result.errorDetails.toString()).to.eql('Error: The operation was aborted due to timeout'); + expect(data.result.success).to.eql(false); + }); + }); + it('when the request sends a 408 error', function () { mockAgent.get('http://www.testerror.com') .intercept({ path: '/' }) diff --git a/tests/unit/request.spec.ts b/tests/unit/request.spec.ts new file mode 100644 index 0000000..e847f26 --- /dev/null +++ b/tests/unit/request.spec.ts @@ -0,0 +1,134 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import undici = require('undici'); +import requestAndResultsFormatter from '../../lib/request'; + +const sandbox = sinon.createSandbox(); + +describe('request and results formatter', function () { + afterEach(function () { + sandbox.restore(); + }); + + it('returns a redirect response when redirect mode is manual', async function () { + const fetchStub = sandbox.stub(undici, 'fetch').resolves(new Response('redirect body', { + status: 302, + headers: { location: 'http://8.8.8.8/next' }, + })); + + const { response } = await requestAndResultsFormatter({ + url: 'http://1.1.1.1', + fetchOptions: { redirect: 'manual' }, + }); + + expect(response.status).to.equal(302); + expect(fetchStub.calledOnce).to.equal(true); + }); + + it('rejects redirects when redirect mode is error', async function () { + sandbox.stub(undici, 'fetch').resolves(new Response('', { + status: 302, + headers: { location: 'http://8.8.8.8/next' }, + })); + + let error: Error | undefined; + try { + await requestAndResultsFormatter({ + url: 'http://1.1.1.1', + fetchOptions: { redirect: 'error' }, + }); + } catch (caught) { + error = caught as Error; + } + + expect(error?.message).to.equal('Request was redirected'); + }); + + it('follows cross-origin POST redirects without forwarding sensitive headers or the body', async function () { + const fetchStub = sandbox.stub(undici, 'fetch'); + fetchStub.onFirstCall().resolves(new Response('', { + status: 302, + headers: { location: 'https://8.8.8.8/next' }, + })); + fetchStub.onSecondCall().resolves(new Response('ok')); + + await requestAndResultsFormatter({ + url: 'http://1.1.1.1', + fetchOptions: { + method: 'POST', + body: 'request body', + headers: { + authorization: 'secret', + cookie: 'session=secret', + cookie2: 'legacy=secret', + host: '1.1.1.1', + 'proxy-authorization': 'proxy-secret', + 'content-type': 'text/plain', + 'x-custom-header': 'retained', + }, + }, + }); + + expect(fetchStub.calledTwice).to.equal(true); + const secondOptions = fetchStub.secondCall.args[1]; + const headers = new Headers(secondOptions?.headers); + expect(secondOptions?.method).to.equal('GET'); + expect(secondOptions?.body).to.equal(undefined); + [ + 'authorization', + 'cookie', + 'cookie2', + 'host', + 'proxy-authorization', + 'content-type', + ].forEach((header) => expect(headers.has(header), header).to.equal(false)); + expect(headers.get('x-custom-header')).to.equal('retained'); + }); + + it('changes non-GET and non-HEAD methods to GET for 303 redirects', async function () { + const fetchStub = sandbox.stub(undici, 'fetch'); + fetchStub.onFirstCall().resolves(new Response('', { + status: 303, + headers: { location: '/next' }, + })); + fetchStub.onSecondCall().resolves(new Response('ok')); + + await requestAndResultsFormatter({ + url: 'http://1.1.1.1', + fetchOptions: { method: 'PUT', body: 'request body' }, + }); + + const secondOptions = fetchStub.secondCall.args[1]; + expect(secondOptions?.method).to.equal('GET'); + expect(secondOptions?.body).to.equal(undefined); + }); + + it('stops after the maximum number of redirects', async function () { + const fetchStub = sandbox.stub(undici, 'fetch').callsFake(async () => new Response('', { + status: 302, + headers: { location: 'http://1.1.1.1/next' }, + })); + + let error: Error | undefined; + try { + await requestAndResultsFormatter({ url: 'http://1.1.1.1' }); + } catch (caught) { + error = caught as Error; + } + + expect(error?.message).to.equal('Maximum redirect count exceeded'); + expect(fetchStub.callCount).to.equal(21); + }); + + it('uses the charset from a named charset meta tag', async function () { + const body = Buffer.from( + 'caf\xe9', + 'latin1', + ); + sandbox.stub(undici, 'fetch').resolves(new Response(body)); + + const { body: decodedBody } = await requestAndResultsFormatter({ url: 'http://1.1.1.1' }); + + expect(decodedBody).to.contain('café'); + }); +}); diff --git a/tests/unit/utils.spec.ts b/tests/unit/utils.spec.ts index 9bb932f..631aebe 100644 --- a/tests/unit/utils.spec.ts +++ b/tests/unit/utils.spec.ts @@ -2,10 +2,13 @@ import { expect } from 'chai'; /* eslint-disable mocha/no-setup-in-describe */ import { + defaultUrlValidatorSettings as defaultSettings, findImageTypeFromUrl, + isBlacklistedUrl, isCustomMetaTagsValid, isImageTypeValid, isThisANonHTMLUrl, + isUrlValid, optionSetup, removeNestedUndefinedValues, unescapeScriptText, @@ -26,7 +29,31 @@ const validateUrl = (urls, valid, message, urlValidatorSettings) => { }; describe('utils', function () { + describe('isUrlValid', function () { + it('returns true for a valid URL', function () { + expect(isUrlValid('https://example.com', defaultSettings)).to.eql(true); + }); + + it('returns false for an empty or invalid URL', function () { + expect(isUrlValid('', defaultSettings)).to.eql(false); + expect(isUrlValid('not a URL', defaultSettings)).to.eql(false); + }); + + it('returns false when the value is not a string', function () { + // @ts-ignore + expect(isUrlValid(null, defaultSettings)).to.eql(false); + }); + }); + describe('validateAndFormatURL', function () { + it('adds the http protocol when the URL does not include one', function () { + expect(validateAndFormatURL('example.com', defaultSettings).url).to.eql('http://example.com'); + }); + + it('preserves the protocol when the URL already includes one', function () { + expect(validateAndFormatURL('https://example.com', defaultSettings).url).to.eql('https://example.com'); + }); + context('validing URLs', function () { const defaultUrlValidatorSettings = { allow_fragments: true, @@ -158,6 +185,21 @@ describe('utils', function () { }); }); + describe('isBlacklistedUrl', function () { + it('returns true when the URL contains a blacklisted value', function () { + expect(isBlacklistedUrl('https://example.com/path', ['example.com'])).to.eql(true); + }); + + it('returns false when no blacklist value matches', function () { + expect(isBlacklistedUrl('https://example.com/path', ['other.com'])).to.eql(false); + }); + + it('returns false when the blacklist is omitted or empty', function () { + expect(isBlacklistedUrl('https://example.com/path')).to.eql(false); + expect(isBlacklistedUrl('https://example.com/path', [])).to.eql(false); + }); + }); + describe('findImageTypeFromUrl', function () { it('foobar.com/image.png?test=true', function () { const type = findImageTypeFromUrl('foobar.com/image.png?test=true'); @@ -254,6 +296,12 @@ describe('utils', function () { }); describe('isCustomMetaTagsValid', function () { + it('when passing a non-array into isCustomMetaTagsValid', function () { + // @ts-ignore + const response = isCustomMetaTagsValid({}); + expect(response).to.eql(false); + }); + it('when passing a valid custom tag into isCustomMetaTagsValid', function () { const response = isCustomMetaTagsValid([{ multiple: false, @@ -287,6 +335,26 @@ describe('utils', function () { expect(response).to.eql(false); }); + it('when passing a custom tag missing multiple into isCustomMetaTagsValid', function () { + // @ts-ignore + const response = isCustomMetaTagsValid([{ + property: 'foo', + fieldName: 'fooTag', + }]); + expect(response).to.eql(false); + }); + + it('when passing a custom tag with invalid multiple and property types into isCustomMetaTagsValid', function () { + const response = isCustomMetaTagsValid([{ + // @ts-ignore + multiple: 'false', + // @ts-ignore + property: true, + fieldName: 'fooTag', + }]); + expect(response).to.eql(false); + }); + it('when passing a valid and invalid custom tag into isCustomMetaTagsValid', function () { // @ts-ignore const response = isCustomMetaTagsValid([{ From 295653647fafab477845413bd8ee3124d080b51a Mon Sep 17 00:00:00 2001 From: jshemas Date: Thu, 29 Oct 2026 19:16:38 -0400 Subject: [PATCH 10/12] updating changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae95265..010229e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## 6.12.0 +- Added URL and IP validation to block non-HTTP(S) URLs and private, loopback, link-local, reserved, and mapped IP addresses. See `lib/isPublicAddress.ts`. +- Updated `lib/request.ts` to check DNS results at connection time and validate each redirect destination. Caller-provided fetch options can’t restore automatic redirect handling or replace the guarded dispatcher. +- `lib/request.ts`: Always uses OGS’s DNS-guarded Undici Agent, rather than switching to a global dispatcher that may have changed after module initialization. +- Blacklist now applies to redirect destinations. +- Updating dependencies to fix npm vulnerabilities + +## 6.12.0 + - CI/CD now supports node26! - Updating dependencies to fix npm vulnerabilities From 3760241ae14ec853aeb7156ae895e6b8fdc839b2 Mon Sep 17 00:00:00 2001 From: jshemas Date: Thu, 29 Oct 2026 19:17:26 -0400 Subject: [PATCH 11/12] 6.13.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 73101fe..3c92bdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-graph-scraper", - "version": "6.12.0", + "version": "6.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-graph-scraper", - "version": "6.12.0", + "version": "6.13.0", "license": "MIT", "dependencies": { "chardet": "^2.2.0", diff --git a/package.json b/package.json index c98040b..3f57900 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "open-graph-scraper", "description": "Node.js scraper module for Open Graph and Twitter Card info", - "version": "6.12.0", + "version": "6.13.0", "license": "MIT", "main": "./dist/cjs/index.js", "types": "./types/index.d.ts", From b8960b062117a2279c05f3b491a6ff8648dad413 Mon Sep 17 00:00:00 2001 From: jshemas Date: Thu, 29 Oct 2026 19:17:47 -0400 Subject: [PATCH 12/12] 6.13.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 010229e..c30954b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 6.12.0 +## 6.13.0 - Added URL and IP validation to block non-HTTP(S) URLs and private, loopback, link-local, reserved, and mapped IP addresses. See `lib/isPublicAddress.ts`. - Updated `lib/request.ts` to check DNS results at connection time and validate each redirect destination. Caller-provided fetch options can’t restore automatic redirect handling or replace the guarded dispatcher.