Skip to content

Commit dab80c2

Browse files
committed
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.
1 parent 15d7bac commit dab80c2

5 files changed

Lines changed: 160 additions & 99 deletions

File tree

ghost/core/core/server/web/shared/middleware/api/spam-prevention.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const handleStoreError = (err) => {
9292
// Defaults to 50 attempts per hour and locks the endpoint for an hour
9393
const globalBlock = () => {
9494
const ExpressBrute = require('express-brute');
95-
const BruteKnex = require('brute-knex');
95+
const BruteKnex = require('@tryghost/brute-knex');
9696
const db = require('../../../../data/db');
9797

9898
store = store || new BruteKnex({
@@ -121,7 +121,7 @@ const globalBlock = () => {
121121

122122
const globalReset = () => {
123123
const ExpressBrute = require('express-brute');
124-
const BruteKnex = require('brute-knex');
124+
const BruteKnex = require('@tryghost/brute-knex');
125125
const db = require('../../../../data/db');
126126

127127
store = store || new BruteKnex({
@@ -150,7 +150,7 @@ const globalReset = () => {
150150

151151
const webmentionsBlock = () => {
152152
const ExpressBrute = require('express-brute');
153-
const BruteKnex = require('brute-knex');
153+
const BruteKnex = require('@tryghost/brute-knex');
154154
const db = require('../../../../data/db');
155155

156156
store = store || new BruteKnex({
@@ -176,7 +176,7 @@ const webmentionsBlock = () => {
176176

177177
const emailPreviewBlock = () => {
178178
const ExpressBrute = require('express-brute');
179-
const BruteKnex = require('brute-knex');
179+
const BruteKnex = require('@tryghost/brute-knex');
180180
const db = require('../../../../data/db');
181181

182182
store = store || new BruteKnex({
@@ -202,7 +202,7 @@ const emailPreviewBlock = () => {
202202

203203
const membersAuth = () => {
204204
const ExpressBrute = require('express-brute');
205-
const BruteKnex = require('brute-knex');
205+
const BruteKnex = require('@tryghost/brute-knex');
206206
const db = require('../../../../data/db');
207207

208208
store = store || new BruteKnex({
@@ -235,7 +235,7 @@ const membersAuth = () => {
235235
*/
236236
const membersAuthEnumeration = () => {
237237
const ExpressBrute = require('express-brute');
238-
const BruteKnex = require('brute-knex');
238+
const BruteKnex = require('@tryghost/brute-knex');
239239
const db = require('../../../../data/db');
240240

241241
store = store || new BruteKnex({
@@ -265,7 +265,7 @@ const membersAuthEnumeration = () => {
265265

266266
const checkoutSessionGlobal = () => {
267267
const ExpressBrute = require('express-brute');
268-
const BruteKnex = require('brute-knex');
268+
const BruteKnex = require('@tryghost/brute-knex');
269269
const db = require('../../../../data/db');
270270

271271
store = store || new BruteKnex({
@@ -295,7 +295,7 @@ const checkoutSessionGlobal = () => {
295295

296296
const checkoutSessionEmail = () => {
297297
const ExpressBrute = require('express-brute');
298-
const BruteKnex = require('brute-knex');
298+
const BruteKnex = require('@tryghost/brute-knex');
299299
const db = require('../../../../data/db');
300300

301301
store = store || new BruteKnex({
@@ -325,7 +325,7 @@ const checkoutSessionEmail = () => {
325325

326326
const otcVerificationEnumeration = () => {
327327
const ExpressBrute = require('express-brute');
328-
const BruteKnex = require('brute-knex');
328+
const BruteKnex = require('@tryghost/brute-knex');
329329
const db = require('../../../../data/db');
330330

331331
store = store || new BruteKnex({
@@ -356,7 +356,7 @@ const otcVerificationEnumeration = () => {
356356

357357
const otcVerification = () => {
358358
const ExpressBrute = require('express-brute');
359-
const BruteKnex = require('brute-knex');
359+
const BruteKnex = require('@tryghost/brute-knex');
360360
const db = require('../../../../data/db');
361361

362362
store = store || new BruteKnex({
@@ -391,7 +391,7 @@ const otcVerification = () => {
391391
// Default value of 5 attempts per user+IP pair
392392
const userLogin = () => {
393393
const ExpressBrute = require('express-brute');
394-
const BruteKnex = require('brute-knex');
394+
const BruteKnex = require('@tryghost/brute-knex');
395395
const db = require('../../../../data/db');
396396

397397
store = store || new BruteKnex({
@@ -422,7 +422,7 @@ const userLogin = () => {
422422
// The endpoint is then locked for an hour
423423
const userReset = function userReset() {
424424
const ExpressBrute = require('express-brute');
425-
const BruteKnex = require('brute-knex');
425+
const BruteKnex = require('@tryghost/brute-knex');
426426
const db = require('../../../../data/db');
427427

428428
store = store || new BruteKnex({
@@ -451,7 +451,7 @@ const userReset = function userReset() {
451451

452452
const userVerification = function userVerification() {
453453
const ExpressBrute = require('express-brute');
454-
const BruteKnex = require('brute-knex');
454+
const BruteKnex = require('@tryghost/brute-knex');
455455
const db = require('../../../../data/db');
456456

457457
store = store || new BruteKnex({
@@ -477,7 +477,7 @@ const userVerification = function userVerification() {
477477

478478
const sendVerificationCode = function sendVerificationCode() {
479479
const ExpressBrute = require('express-brute');
480-
const BruteKnex = require('brute-knex');
480+
const BruteKnex = require('@tryghost/brute-knex');
481481
const db = require('../../../../data/db');
482482

483483
store = store || new BruteKnex({
@@ -505,7 +505,7 @@ const sendVerificationCode = function sendVerificationCode() {
505505
// The endpoint is then locked for an hour
506506
const privateBlog = () => {
507507
const ExpressBrute = require('express-brute');
508-
const BruteKnex = require('brute-knex');
508+
const BruteKnex = require('@tryghost/brute-knex');
509509
const db = require('../../../../data/db');
510510

511511
store = store || new BruteKnex({

ghost/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"@tryghost/admin-api-schema": "4.7.5",
9090
"@tryghost/api-framework": "catalog:",
9191
"@tryghost/bookshelf-plugins": "2.2.4",
92+
"@tryghost/brute-knex": "catalog:",
9293
"@tryghost/color-utils": "catalog:",
9394
"@tryghost/config-url-helpers": "1.0.26",
9495
"@tryghost/custom-fonts": "catalog:",
@@ -140,7 +141,6 @@
140141
"body-parser": "1.20.5",
141142
"bookshelf": "1.2.0",
142143
"bookshelf-relations": "2.8.0",
143-
"brute-knex": "4.0.1",
144144
"bson-objectid": "catalog:",
145145
"cache-manager": "4.1.0",
146146
"cache-manager-ioredis": "2.1.0",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const assert = require('node:assert/strict');
2+
const BruteKnex = require('@tryghost/brute-knex');
3+
4+
const db = require('../../../../../../core/server/data/db');
5+
const dbUtils = require('../../../../../utils/db-utils');
6+
7+
describe('BruteKnex store', function () {
8+
const key = 'brute-knex-store-test';
9+
10+
beforeAll(async function () {
11+
await dbUtils.reset();
12+
});
13+
14+
beforeEach(async function () {
15+
await dbUtils.truncate('brute');
16+
});
17+
18+
afterEach(async function () {
19+
await dbUtils.truncate('brute');
20+
});
21+
22+
it('stores, increments and resets rate limit entries through Ghost\'s brute table', async function () {
23+
const store = new BruteKnex({
24+
tablename: 'brute',
25+
createTable: false,
26+
knex: db.knex
27+
});
28+
29+
await store.ready;
30+
31+
const firstRequest = new Date('2026-01-01T00:00:00.000Z');
32+
const lastRequest = new Date('2026-01-01T00:01:00.000Z');
33+
34+
await store.set(key, {
35+
firstRequest,
36+
lastRequest,
37+
count: 3
38+
}, 60);
39+
40+
const row = await db.knex('brute').where({key}).first();
41+
assert.equal(row.key, key);
42+
assert.equal(Number(row.count), 3);
43+
44+
const storedValue = await store.get(key);
45+
assert.equal(storedValue.count, 3);
46+
assert.equal(storedValue.firstRequest.getTime(), firstRequest.getTime());
47+
assert.equal(storedValue.lastRequest.getTime(), lastRequest.getTime());
48+
49+
await store.increment(key, 60);
50+
51+
const incrementedValue = await store.get(key);
52+
assert.equal(incrementedValue.count, 4);
53+
54+
await new Promise((resolve, reject) => {
55+
store.reset(key, (err) => {
56+
if (err) {
57+
reject(err);
58+
return;
59+
}
60+
61+
resolve();
62+
});
63+
});
64+
65+
assert.equal(await store.get(key), null);
66+
assert.equal(await db.knex('brute').where({key}).first(), undefined);
67+
});
68+
});

0 commit comments

Comments
 (0)