From d63cb4f4338359587d989d6950f2f6ca1ab1fa22 Mon Sep 17 00:00:00 2001 From: Aileen Booker Date: Fri, 26 Jun 2026 16:52:44 +0400 Subject: [PATCH] Changed Ghost to use scoped brute-knex no ref Swapping to the maintained @tryghost package removes the deprecated unscoped dependency while keeping the rate limiter wired through the real Ghost brute table. --- .../shared/middleware/api/spam-prevention.js | 30 ++-- ghost/core/package.json | 2 +- .../middleware/api/brute-knex-store.test.js | 68 +++++++ pnpm-lock.yaml | 168 +++++++++--------- pnpm-workspace.yaml | 4 + 5 files changed, 171 insertions(+), 101 deletions(-) create mode 100644 ghost/core/test/integration/web/shared/middleware/api/brute-knex-store.test.js diff --git a/ghost/core/core/server/web/shared/middleware/api/spam-prevention.js b/ghost/core/core/server/web/shared/middleware/api/spam-prevention.js index 722d9a31174..867eda93d2b 100644 --- a/ghost/core/core/server/web/shared/middleware/api/spam-prevention.js +++ b/ghost/core/core/server/web/shared/middleware/api/spam-prevention.js @@ -92,7 +92,7 @@ const handleStoreError = (err) => { // Defaults to 50 attempts per hour and locks the endpoint for an hour const globalBlock = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -121,7 +121,7 @@ const globalBlock = () => { const globalReset = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -150,7 +150,7 @@ const globalReset = () => { const webmentionsBlock = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -176,7 +176,7 @@ const webmentionsBlock = () => { const emailPreviewBlock = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -202,7 +202,7 @@ const emailPreviewBlock = () => { const membersAuth = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -235,7 +235,7 @@ const membersAuth = () => { */ const membersAuthEnumeration = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -265,7 +265,7 @@ const membersAuthEnumeration = () => { const checkoutSessionGlobal = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -295,7 +295,7 @@ const checkoutSessionGlobal = () => { const checkoutSessionEmail = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -325,7 +325,7 @@ const checkoutSessionEmail = () => { const otcVerificationEnumeration = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -356,7 +356,7 @@ const otcVerificationEnumeration = () => { const otcVerification = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -391,7 +391,7 @@ const otcVerification = () => { // Default value of 5 attempts per user+IP pair const userLogin = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -422,7 +422,7 @@ const userLogin = () => { // The endpoint is then locked for an hour const userReset = function userReset() { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -451,7 +451,7 @@ const userReset = function userReset() { const userVerification = function userVerification() { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -477,7 +477,7 @@ const userVerification = function userVerification() { const sendVerificationCode = function sendVerificationCode() { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ @@ -505,7 +505,7 @@ const sendVerificationCode = function sendVerificationCode() { // The endpoint is then locked for an hour const privateBlog = () => { const ExpressBrute = require('express-brute'); - const BruteKnex = require('brute-knex'); + const BruteKnex = require('@tryghost/brute-knex'); const db = require('../../../../data/db'); store = store || new BruteKnex({ diff --git a/ghost/core/package.json b/ghost/core/package.json index 801ab6c7f90..4bd49255f1d 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -89,6 +89,7 @@ "@tryghost/admin-api-schema": "4.7.5", "@tryghost/api-framework": "catalog:", "@tryghost/bookshelf-plugins": "2.2.4", + "@tryghost/brute-knex": "catalog:", "@tryghost/color-utils": "catalog:", "@tryghost/config-url-helpers": "1.0.26", "@tryghost/custom-fonts": "catalog:", @@ -140,7 +141,6 @@ "body-parser": "1.20.5", "bookshelf": "1.2.0", "bookshelf-relations": "2.8.0", - "brute-knex": "4.0.1", "bson-objectid": "catalog:", "cache-manager": "4.1.0", "cache-manager-ioredis": "2.1.0", diff --git a/ghost/core/test/integration/web/shared/middleware/api/brute-knex-store.test.js b/ghost/core/test/integration/web/shared/middleware/api/brute-knex-store.test.js new file mode 100644 index 00000000000..1381590a110 --- /dev/null +++ b/ghost/core/test/integration/web/shared/middleware/api/brute-knex-store.test.js @@ -0,0 +1,68 @@ +const assert = require('node:assert/strict'); +const BruteKnex = require('@tryghost/brute-knex'); + +const db = require('../../../../../../core/server/data/db'); +const dbUtils = require('../../../../../utils/db-utils'); + +describe('BruteKnex store', function () { + const key = 'brute-knex-store-test'; + + beforeAll(async function () { + await dbUtils.reset(); + }); + + beforeEach(async function () { + await dbUtils.truncate('brute'); + }); + + afterEach(async function () { + await dbUtils.truncate('brute'); + }); + + it('stores, increments and resets rate limit entries through Ghost\'s brute table', async function () { + const store = new BruteKnex({ + tablename: 'brute', + createTable: false, + knex: db.knex + }); + + await store.ready; + + const firstRequest = new Date('2026-01-01T00:00:00.000Z'); + const lastRequest = new Date('2026-01-01T00:01:00.000Z'); + + await store.set(key, { + firstRequest, + lastRequest, + count: 3 + }, 60); + + const row = await db.knex('brute').where({key}).first(); + assert.equal(row.key, key); + assert.equal(Number(row.count), 3); + + const storedValue = await store.get(key); + assert.equal(storedValue.count, 3); + assert.equal(storedValue.firstRequest.getTime(), firstRequest.getTime()); + assert.equal(storedValue.lastRequest.getTime(), lastRequest.getTime()); + + await store.increment(key, 60); + + const incrementedValue = await store.get(key); + assert.equal(incrementedValue.count, 4); + + await new Promise((resolve, reject) => { + store.reset(key, (err) => { + if (err) { + reject(err); + return; + } + + resolve(); + }); + }); + + assert.equal(await store.get(key), null); + assert.equal(await db.knex('brute').where({key}).first(), undefined); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f0cb8b2812..89669f75640 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,9 @@ catalogs: '@tryghost/api-framework': specifier: 3.2.4 version: 3.2.4 + '@tryghost/brute-knex': + specifier: 3.0.0 + version: 3.0.0 '@tryghost/color-utils': specifier: 0.2.19 version: 0.2.19 @@ -363,7 +366,7 @@ overrides: '@xmldom/xmldom@<0.8.13': ^0.9.0 perf-primitives: ^0.0.6 -packageExtensionsChecksum: sha256-gFsvptlcVXY90ntXWh9ANUP1/LYyeqXjWoDGw6BCfOY= +packageExtensionsChecksum: sha256-aGXfyR5OhgQdlJik2sZhr7IqWKd0dDYwxxfaa9l7YzY= importers: @@ -2577,6 +2580,9 @@ importers: '@tryghost/bookshelf-plugins': specifier: 2.2.4 version: 2.2.4 + '@tryghost/brute-knex': + specifier: 'catalog:' + version: 3.0.0(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7) '@tryghost/color-utils': specifier: 'catalog:' version: 0.2.19 @@ -2730,9 +2736,6 @@ importers: bookshelf-relations: specifier: 2.8.0 version: 2.8.0(bookshelf@1.2.0(knex@2.4.2(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7))) - brute-knex: - specifier: 4.0.1 - version: 4.0.1(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0) bson-objectid: specifier: 'catalog:' version: 2.0.4 @@ -8983,6 +8986,10 @@ packages: '@tryghost/bookshelf-transaction-events@2.2.3': resolution: {integrity: sha512-JPqrqka6Tje12TK9dHV/ethm4qfGwRzbPLjkBG79XRpB6QKDwB5NGAXY98bYKJz+w6fu8BKcn84o8r+t3MwRZg==} + '@tryghost/brute-knex@3.0.0': + resolution: {integrity: sha512-NT3LGUAgMPpGBPD8CBhJ/dcfZatHT2bs+wy2DKA5ugyq7cXayn2suH1bjWkzJs/yd52nQNZgp0xYAVMegf/L3w==} + engines: {node: '>=20.20.0'} + '@tryghost/bunyan-rotating-filestream@0.0.7': resolution: {integrity: sha512-dswM+dxG8J7WpVoSjzAdoWXqqB5Dg0C2T7Zh6eoUvl5hkA8yWWJi/fS4jNXlHF700lWQ0g8/t+leJ7SGSWd+aw==} @@ -11313,10 +11320,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - brute-knex@4.0.1: - resolution: {integrity: sha512-rV2tY8amv+2ERYNNC7voCl1A4Mh+s2IvyyDo3DAMKhaR4ME8r+4t9MH0Fgqjpe1ievESYX9Pes7gf05LBBUCRA==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -11848,9 +11851,6 @@ packages: resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} engines: {node: '>=18'} - colorette@1.1.0: - resolution: {integrity: sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==} - colorette@1.2.1: resolution: {integrity: sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==} @@ -16386,16 +16386,16 @@ packages: engines: {node: ^14.18.0 || ^16.13.0 || ^18.12.1 || ^20.11.1 || ^22.13.1} hasBin: true - knex@0.20.15: - resolution: {integrity: sha512-WHmvgfQfxA5v8pyb9zbskxCS1L1WmYgUbwBhHojlkmdouUOazvroUWlCr6KIKMQ8anXZh1NXOOtIUMnxENZG5Q==} - engines: {node: '>=8'} + knex@0.21.21: + resolution: {integrity: sha512-cjw5qO1EzVKjbywcVa61IQJMLt7PfYBRI/2NwCA/B9beXgbw652wDNLz+JM+UKKNsfwprq0ugYqBYc9q4JN36A==} + engines: {node: '>=10'} hasBin: true peerDependencies: - mssql: ^6.1.0 + mssql: ^6.2.1 mysql: ^2.18.1 mysql2: ^2.1.0 - pg: ^7.18.2 - sqlite3: ^4.1.1 + pg: ^8.3.0 + sqlite3: ^5.0.0 peerDependenciesMeta: mssql: optional: true @@ -16408,18 +16408,20 @@ packages: sqlite3: optional: true - knex@0.21.21: - resolution: {integrity: sha512-cjw5qO1EzVKjbywcVa61IQJMLt7PfYBRI/2NwCA/B9beXgbw652wDNLz+JM+UKKNsfwprq0ugYqBYc9q4JN36A==} - engines: {node: '>=10'} + knex@2.4.2: + resolution: {integrity: sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==} + engines: {node: '>=12'} hasBin: true peerDependencies: - mssql: ^6.2.1 - mysql: ^2.18.1 - mysql2: ^2.1.0 - pg: ^8.3.0 - sqlite3: ^5.0.0 + better-sqlite3: '*' + mysql: '*' + mysql2: '*' + pg: '*' + pg-native: '*' + sqlite3: '*' + tedious: '*' peerDependenciesMeta: - mssql: + better-sqlite3: optional: true mysql: optional: true @@ -16427,11 +16429,15 @@ packages: optional: true pg: optional: true + pg-native: + optional: true sqlite3: optional: true + tedious: + optional: true - knex@2.4.2: - resolution: {integrity: sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==} + knex@2.5.1: + resolution: {integrity: sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==} engines: {node: '>=12'} hasBin: true peerDependencies: @@ -18532,15 +18538,15 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - pg-connection-string@2.1.0: - resolution: {integrity: sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg==} - pg-connection-string@2.4.0: resolution: {integrity: sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==} pg-connection-string@2.5.0: resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} + pg-connection-string@2.6.1: + resolution: {integrity: sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==} + pg-connection-string@2.6.2: resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} @@ -21036,10 +21042,6 @@ packages: resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} engines: {node: '>=18'} - tarn@2.0.0: - resolution: {integrity: sha512-7rNMCZd3s9bhQh47ksAQd92ADFcJUjjbyOvyFjNLwTPpGieFHMC84S+LOzw0fx1uh6hnDz/19r8CPMnIjJlMMA==} - engines: {node: '>=8.0.0'} - tarn@3.0.2: resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} engines: {node: '>=8.0.0'} @@ -21801,11 +21803,6 @@ packages: deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true - uuid@7.0.3: - resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). - hasBin: true - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). @@ -28878,6 +28875,22 @@ snapshots: '@tryghost/bookshelf-transaction-events@2.2.3': {} + '@tryghost/brute-knex@3.0.0(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)': + dependencies: + express-brute: 1.0.1(express@4.22.2) + knex: 2.5.1(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7) + lodash: 4.18.1 + transitivePeerDependencies: + - better-sqlite3 + - express + - mysql + - mysql2 + - pg + - pg-native + - sqlite3 + - supports-color + - tedious + '@tryghost/bunyan-rotating-filestream@0.0.7': dependencies: long-timeout: 0.1.1 @@ -28928,7 +28941,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@tryghost/domain-events@3.2.5': {} + '@tryghost/domain-events@3.2.5': + dependencies: + '@tryghost/logging': 4.2.1(supports-color@5.5.0) + transitivePeerDependencies: + - '@75lb/nature' + - supports-color '@tryghost/elasticsearch@3.0.29(supports-color@5.5.0)': dependencies: @@ -32846,19 +32864,6 @@ snapshots: node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) - brute-knex@4.0.1(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0): - dependencies: - express-brute: 1.0.1(express@4.22.2) - knex: 0.20.15(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0) - transitivePeerDependencies: - - express - - mssql - - mysql - - mysql2 - - pg - - sqlite3 - - supports-color - bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -33485,8 +33490,6 @@ snapshots: color-convert: 3.1.3 color-string: 2.1.4 - colorette@1.1.0: {} - colorette@1.2.1: {} colorette@1.4.0: {} @@ -40161,29 +40164,6 @@ snapshots: - supports-color - tedious - knex@0.20.15(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0): - dependencies: - colorette: 1.1.0 - commander: 4.1.1 - debug: 4.4.3(supports-color@5.5.0) - esm: 3.2.25 - getopts: 2.2.5 - inherits: 2.0.4 - interpret: 2.2.0 - liftoff: 3.1.0 - lodash: 4.18.1 - mkdirp: 0.5.6 - pg-connection-string: 2.1.0 - tarn: 2.0.0 - tildify: 2.0.0 - uuid: 7.0.3 - v8flags: 3.2.0 - optionalDependencies: - mysql2: 3.22.5(@types/node@22.19.21) - sqlite3: 5.1.7 - transitivePeerDependencies: - - supports-color - knex@0.21.21(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7): dependencies: colorette: 1.2.1 @@ -40248,6 +40228,28 @@ snapshots: transitivePeerDependencies: - supports-color + knex@2.5.1(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7): + dependencies: + colorette: 2.0.19 + commander: 10.0.1 + debug: 4.3.4 + escalade: 3.2.0 + esm: 3.2.25 + get-package-type: 0.1.0 + getopts: 2.3.0 + interpret: 2.2.0 + lodash: 4.18.1 + pg-connection-string: 2.6.1 + rechoir: 0.8.0 + resolve-from: 5.0.0 + tarn: 3.0.2 + tildify: 2.0.0 + optionalDependencies: + mysql2: 3.22.5(@types/node@22.19.21) + sqlite3: 5.1.7 + transitivePeerDependencies: + - supports-color + knex@3.1.0(mysql2@3.22.5(@types/node@25.9.1)): dependencies: colorette: 2.0.19 @@ -42829,12 +42831,12 @@ snapshots: performance-now@2.1.0: {} - pg-connection-string@2.1.0: {} - pg-connection-string@2.4.0: {} pg-connection-string@2.5.0: {} + pg-connection-string@2.6.1: {} + pg-connection-string@2.6.2: {} picocolors@0.2.1: {} @@ -45934,8 +45936,6 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - tarn@2.0.0: {} - tarn@3.0.2: {} tdigest@0.1.2: @@ -46767,8 +46767,6 @@ snapshots: uuid@3.4.0: {} - uuid@7.0.3: {} - uuid@8.3.2: {} uuid@9.0.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b75a6b81f5c..80b5c328ab8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -48,6 +48,7 @@ catalog: '@testing-library/jest-dom': 6.9.1 '@testing-library/react': 14.3.1 '@tryghost/api-framework': 3.2.4 + '@tryghost/brute-knex': 3.0.0 '@tryghost/color-utils': 0.2.19 '@tryghost/custom-fonts': 1.0.11 '@tryghost/debug': 2.2.3 @@ -188,6 +189,9 @@ overrides: perf-primitives: ^0.0.6 packageExtensions: + '@tryghost/domain-events@3.2.5': + dependencies: + '@tryghost/logging': 'catalog:' '@doist/react-interpolate@2.2.1': dependencies: '@babel/runtime': ^7.26.10