Conversation
… it that doesn't have to touch man files in the api/ directory.
routes/patchUpdate.js
Outdated
|
|
||
| router.route('/') | ||
| .patch(auth.checkJwt, controller.patchUpdate) | ||
| .patch(auth.checkJwt, rest.jsonContent, controller.patchUpdate) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, to fix missing rate limiting for database-backed route handlers in Express, we should use a rate-limiting middleware (such as express-rate-limit) and apply it to the specific sensitive routes or to the entire router. This middleware should be placed before the handler that performs the database access so that excessive request rates are rejected or delayed before reaching the database.
For this specific file, the best minimally invasive fix is to: (1) import express-rate-limit, (2) define a limiter instance configured for update operations (e.g., a reasonable number of PATCH/POST update requests per IP in a given time window), and (3) add this limiter as middleware to the router.route('/') chain before auth.checkJwt / rest.jsonContent / controller.patchUpdate. This ensures both the .patch and .post handlers share the same rate limiting, covering all the alert variants without changing existing business logic or response semantics. Concretely, inside routes/patchUpdate.js, add a RateLimit import near the other imports, define a patchUpdateLimiter (e.g., 100 requests per 15 minutes per IP, or a stricter value as desired), and include patchUpdateLimiter as the first middleware in the .patch and .post routes. No changes to the controller or auth logic are needed.
| @@ -1,14 +1,20 @@ | ||
| #!/usr/bin/env node | ||
| import express from 'express' | ||
| import RateLimit from 'express-rate-limit' | ||
| const router = express.Router() | ||
| //This controller will handle all MongoDB interactions. | ||
| import controller from '../db-controller.js' | ||
| import rest from '../rest.js' | ||
| import auth from '../auth/index.js' | ||
|
|
||
| const patchUpdateLimiter = RateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 update requests per windowMs | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .patch(auth.checkJwt, rest.jsonContent, controller.patchUpdate) | ||
| .post(auth.checkJwt, (req, res, next) => { | ||
| .patch(patchUpdateLimiter, auth.checkJwt, rest.jsonContent, controller.patchUpdate) | ||
| .post(patchUpdateLimiter, auth.checkJwt, (req, res, next) => { | ||
| if (rest.checkPatchOverrideSupport(req, res)) { | ||
| controller.patchUpdate(req, res, next) | ||
| } |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/bulkCreate.js
Outdated
|
|
||
| router.route('/') | ||
| .post(auth.checkJwt, controller.bulkCreate) | ||
| .post(auth.checkJwt, rest.jsonContent, controller.bulkCreate) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, you should protect expensive HTTP handlers (especially those that perform bulk database operations) by applying a rate-limiting middleware. For an Express-based router, a common approach is to use express-rate-limit and attach a limiter to the specific route or to the router as a whole. This ensures that regardless of how heavy controller.bulkCreate is, any single client can only invoke it a limited number of times in a defined time window.
For this specific file (routes/bulkCreate.js), the minimal, non-breaking fix is:
- Import
express-rate-limit(CommonJS-stylerequireis the idiomatic form for that library, but mixingimportandrequirein the same file is legal in Node when using transpilation or appropriate settings, and we must not alter existing imports). - Create a limiter instance configured for this bulk-create endpoint (for example, allowing a modest number of bulk requests per 15 minutes).
- Insert this limiter into the middleware chain of the
POSTroute beforecontroller.bulkCreate, so that requests exceeding the limit are rejected before hitting the database logic. - Do not alter the behavior of other routes or middlewares.
Concretely:
- At the top of
routes/bulkCreate.js, addconst RateLimit = require('express-rate-limit')and defineconst bulkCreateLimiter = RateLimit({ ... }). - Update line 11 so that the
.post(...)call becomes.post(auth.checkJwt, rest.jsonContent, bulkCreateLimiter, controller.bulkCreate).
| @@ -6,9 +6,15 @@ | ||
| import controller from '../db-controller.js' | ||
| import auth from '../auth/index.js' | ||
| import rest from '../rest.js' | ||
| const RateLimit = require('express-rate-limit') | ||
|
|
||
| const bulkCreateLimiter = RateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 bulk create requests per windowMs | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .post(auth.checkJwt, rest.jsonContent, controller.bulkCreate) | ||
| .post(auth.checkJwt, rest.jsonContent, bulkCreateLimiter, controller.bulkCreate) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for creating, please use POST.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/bulkUpdate.js
Outdated
|
|
||
| router.route('/') | ||
| .put(auth.checkJwt, controller.bulkUpdate) | ||
| .put(auth.checkJwt, rest.jsonContent, controller.bulkUpdate) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, to fix missing rate limiting you should introduce a rate-limiting middleware (e.g., using express-rate-limit) and apply it to routes that perform expensive operations such as database writes. The middleware should be placed before the heavy controller in the middleware chain so that excessive requests are rejected early.
For this specific route, the best minimal change is to add a rate limiter middleware function in routes/bulkUpdate.js and insert it into the .put(...) chain before controller.bulkUpdate. We will:
- Import
express-rate-limitat the top of this file. - Define a limiter instance (e.g.,
bulkUpdateLimiter) with a reasonable window and max setting, local to this router. - Update line 11 to include
bulkUpdateLimiterbetweenrest.jsonContentandcontroller.bulkUpdate.
This preserves existing authentication and JSON processing, while adding rate limiting immediately before the database-heavy handler.
Concretely:
- At the top of
routes/bulkUpdate.js, addimport rateLimit from 'express-rate-limit'. - Define a
const bulkUpdateLimiter = rateLimit({...})just after the imports, with sensible defaults (e.g., 100 requests per 15 minutes from a single IP). - Change line 11 from
.put(auth.checkJwt, rest.jsonContent, controller.bulkUpdate)to.put(auth.checkJwt, rest.jsonContent, bulkUpdateLimiter, controller.bulkUpdate).
No other behavior of the route changes, aside from rate limiting.
| @@ -6,9 +6,15 @@ | ||
| import controller from '../db-controller.js' | ||
| import auth from '../auth/index.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const bulkUpdateLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 bulk update requests per window | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .put(auth.checkJwt, rest.jsonContent, controller.bulkUpdate) | ||
| .put(auth.checkJwt, rest.jsonContent, bulkUpdateLimiter, controller.bulkUpdate) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for creating, please use PUT.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/create.js
Outdated
|
|
||
| router.route('/') | ||
| .post(auth.checkJwt, controller.create) | ||
| .post(auth.checkJwt, rest.jsonContent, controller.create) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, the fix is to ensure that requests reaching the expensive controller.create database operation are subject to a rate limit. In an Express environment, this is typically done by adding a middleware like express-rate-limit that tracks requests per IP (or per user) over a time window and rejects excess requests.
In this file, the least invasive fix that preserves existing behavior is:
- Import
express-rate-limit. - Create a rate limiter instance configured for this specific route (e.g., allowing some number of create operations per IP per time window).
- Insert this limiter into the middleware chain before
controller.createon the POST handler.
Concretely:
- At the top of
routes/create.js, add animport rateLimit from 'express-rate-limit'. - After creating the router (after
const router = express.Router()), define acreateRateLimiterwith appropriate settings (e.g., a certainwindowMs,max, and a simple JSON or text response when the limit is exceeded). - Modify line 10 so that the middleware sequence is
.post(auth.checkJwt, rest.jsonContent, createRateLimiter, controller.create).
No existing behavior for non-limited requests changes: authenticated, well-formed JSON POSTs will still reach controller.create, just with the added protection that very frequent requests will be rejected.
| @@ -5,9 +5,17 @@ | ||
| import controller from '../db-controller.js' | ||
| import auth from '../auth/index.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const createRateLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 create requests per windowMs | ||
| standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers | ||
| legacyHeaders: false, // Disable the `X-RateLimit-*` headers | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .post(auth.checkJwt, rest.jsonContent, controller.create) | ||
| .post(auth.checkJwt, rest.jsonContent, createRateLimiter, controller.create) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for creating, please use POST.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/overwrite.js
Outdated
|
|
||
| router.route('/') | ||
| .put(auth.checkJwt, controller.overwrite) | ||
| .put(auth.checkJwt, rest.jsonContent, controller.overwrite) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, fix this by adding an Express-compatible rate-limiting middleware in front of the expensive handler so that repeated requests from the same client are capped over a time window. The standard way is to use a well-known library such as express-rate-limit and apply a limiter either to this specific route or the router as a whole.
For this specific file (routes/overwrite.js), the minimal change that preserves existing behavior is:
- Import
express-rate-limit. - Define a limiter instance configured for this overwrite endpoint (for example, a modest limit like 60 requests per 15 minutes per IP, which you can later tune).
- Insert this limiter into the middleware chain for
PUT /, betweenauth.checkJwtandrest.jsonContent(or at the start). This ensures:- Authentication still happens as before.
- The handler
controller.overwriteruns only if the client is within the allowed request budget. - No other routes are affected.
Concretely:
- At the top of
routes/overwrite.js, addimport rateLimit from 'express-rate-limit'. - After creating the router (after line 3), define
const overwriteLimiter = rateLimit({ ... }). - Update line 10 to:
.put(auth.checkJwt, overwriteLimiter, rest.jsonContent, controller.overwrite)
| @@ -5,9 +5,17 @@ | ||
| import controller from '../db-controller.js' | ||
| import auth from '../auth/index.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const overwriteLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 overwrite requests per windowMs | ||
| standardHeaders: true, | ||
| legacyHeaders: false, | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .put(auth.checkJwt, rest.jsonContent, controller.overwrite) | ||
| .put(auth.checkJwt, overwriteLimiter, rest.jsonContent, controller.overwrite) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for overwriting, please use PUT to overwrite this object.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/patchSet.js
Outdated
|
|
||
| router.route('/') | ||
| .patch(auth.checkJwt, controller.patchSet) | ||
| .patch(auth.checkJwt, rest.jsonContent, controller.patchSet) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, the problem is fixed by applying a rate-limiting middleware to routes that perform expensive operations (like database access). In an Express app, a common solution is to use the express-rate-limit package to limit the number of requests per IP over a defined time window, and to attach that limiter middleware to the vulnerable route or router.
For this specific file (routes/patchSet.js), the best minimally invasive fix is:
- Import
express-rate-limitat the top of the file. - Define a limiter instance configured for a sensible rate (e.g., 100 requests per 15 minutes per IP; you can later tune this).
- Apply this limiter to the router so that all HTTP methods on
/that reachcontroller.patchSetare rate-limited.- Since all routes in this file are on the same router, adding
router.use(limiter)after creating the router will ensure that.patch,.post, and.allon this router are all rate-limited, without changing existing behavior of the handlers themselves.
- Since all routes in this file are on the same router, adding
Concretely:
- At the top of
routes/patchSet.js, addimport rateLimit from 'express-rate-limit'. - Immediately after
const router = express.Router(), defineconst patchSetLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })(or similar configuration). - Then add
router.use(patchSetLimiter)so the limiter is applied to all routes within this router.
No changes are needed to controller.patchSet or to the existing route chaining; we’re only inserting middleware in front of them.
| @@ -4,7 +4,15 @@ | ||
| import controller from '../db-controller.js' | ||
| import auth from '../auth/index.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const patchSetLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 requests per windowMs | ||
| }) | ||
|
|
||
| router.use(patchSetLimiter) | ||
|
|
||
| router.route('/') | ||
| .patch(auth.checkJwt, rest.jsonContent, controller.patchSet) | ||
| .post(auth.checkJwt, (req, res, next) => { |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/putUpdate.js
Outdated
|
|
||
| router.route('/') | ||
| .put(auth.checkJwt, controller.putUpdate) | ||
| .put(auth.checkJwt, rest.jsonContent, controller.putUpdate) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, to fix missing rate limiting on an Express route that performs database access, you introduce a rate-limiting middleware (e.g., via express-rate-limit) and apply it either globally or specifically to the sensitive route(s). The middleware should be placed before the expensive handler in the middleware chain so that abusive traffic is rejected before reaching the database.
For this specific code, the least invasive and clearest fix is:
- Import a known rate-limiting library, such as
express-rate-limit, at the top ofroutes/putUpdate.jswithout altering existing imports. - Define a limiter configured for this update route only (e.g., a reasonable per-IP limit over a time window, such as 100 requests per 15 minutes). This preserves existing functionality while adding protection.
- Insert the limiter into the route definition before
controller.putUpdate, so the middleware order becomes:auth.checkJwt,rest.jsonContent,limiter,controller.putUpdate. - Leave the
.all(...)handler and other existing behavior unchanged.
Concretely:
-
In
routes/putUpdate.js, addimport rateLimit from 'express-rate-limit'near the top. -
Add something like:
const updateLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, })
after the imports and before using
router. -
Update line 10 to:
.put(auth.checkJwt, rest.jsonContent, updateLimiter, controller.putUpdate)
No other files need to be changed to address the alert variants, since all variants refer to the same route handler.
| @@ -5,9 +5,15 @@ | ||
| import controller from '../db-controller.js' | ||
| import auth from '../auth/index.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const updateLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 update requests per windowMs | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .put(auth.checkJwt, rest.jsonContent, controller.putUpdate) | ||
| .put(auth.checkJwt, rest.jsonContent, updateLimiter, controller.putUpdate) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for updating, please use PUT to update this object.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/query.js
Outdated
|
|
||
| router.route('/') | ||
| .post(controller.query) | ||
| .post(rest.jsonContent, controller.query) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, the issue is fixed by ensuring that any route that triggers database access is protected by a rate-limiting middleware. In Express, the standard approach is to use a library such as express-rate-limit and apply a limiter either globally (via app.use) or to specific routes (by inserting the limiter in the route’s middleware chain). This prevents a client from issuing unlimited requests in a given time window.
For this specific file, the best fix with minimal behavioral change is to import express-rate-limit, define a rate limiter instance appropriate for query operations, and insert that limiter into the POST / route before rest.jsonContent and controller.query. This keeps the existing controller and JSON parsing logic untouched and only adds a protective wrapper. Concretely, in routes/query.js, we will:
- Add
import rateLimit from 'express-rate-limit'alongside the other imports. - Define a
const queryLimiter = rateLimit({ ... })with conservative defaults (e.g., 100 requests per 15 minutes per IP) near the top of the file. - Update the route definition on lines 7–8 so that
.post(rest.jsonContent, controller.query)becomes.post(queryLimiter, rest.jsonContent, controller.query).
No additional helper methods are required beyond the limiter instance, and no existing imports need to be modified.
| @@ -3,9 +3,15 @@ | ||
| //This controller will handle all MongoDB interactions. | ||
| import controller from '../db-controller.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const queryLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 requests per windowMs for this route | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .post(rest.jsonContent, controller.query) | ||
| .post(queryLimiter, rest.jsonContent, controller.query) | ||
| .head(controller.queryHeadRequest) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for requesting objects with matching properties. Please use POST.' |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/search.js
Outdated
|
|
||
| router.route('/') | ||
| .post(controller.searchAsWords) | ||
| .post(rest.eitherContent, controller.searchAsWords) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
In general, to fix missing rate limiting on Express routes that hit the database, we should introduce a rate-limiting middleware and apply it to the relevant routes before the expensive handlers. The common approach is to use a well-known library such as express-rate-limit, configure appropriate thresholds (e.g., N requests per window), and attach the limiter either to all routes in the router or only to specific sensitive endpoints.
For this file, the least invasive and clearest fix is:
- Import
express-rate-limit. - Create one or more limiter instances configured for search endpoints.
- Apply the limiter as middleware on the routes that call
controller.searchAsWordsandcontroller.searchAsPhrase, placing it beforerest.eitherContentso it executes first and can reject excessive traffic before any body parsing or DB access. - Keep existing behavior and messages intact for valid and invalid methods.
Concretely, within routes/search.js:
-
Add
import rateLimit from 'express-rate-limit'(ES module form to match existing imports). -
Define a
searchLimiterconstant, e.g.:const searchLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, standardHeaders: true, legacyHeaders: false, })
This uses standard, sensible defaults while not altering any existing functionality of the search handlers themselves.
-
Attach
searchLimiterto bothrouter.route('/')androuter.route('/phrase'), beforerest.eitherContent:router.route('/') .post(searchLimiter, rest.eitherContent, controller.searchAsWords) ...
and similarly for
/phrase.
No other files need to be changed, and the core functional behavior of the handlers remains the same except they will now be rate-limited.
| @@ -2,9 +2,17 @@ | ||
| const router = express.Router() | ||
| import controller from '../db-controller.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| const searchLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 search requests per window | ||
| standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers | ||
| legacyHeaders: false, // Disable the `X-RateLimit-*` headers | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .post(rest.eitherContent, controller.searchAsWords) | ||
| .post(searchLimiter, rest.eitherContent, controller.searchAsWords) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for search. Please use POST.' | ||
| res.status(405) | ||
| @@ -12,7 +18,7 @@ | ||
| }) | ||
|
|
||
| router.route('/phrase') | ||
| .post(rest.eitherContent, controller.searchAsPhrase) | ||
| .post(searchLimiter, rest.eitherContent, controller.searchAsPhrase) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for search. Please use POST.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
routes/search.js
Outdated
|
|
||
| router.route('/phrase') | ||
| .post(controller.searchAsPhrase) | ||
| .post(rest.eitherContent, controller.searchAsPhrase) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 3 hours ago
To fix this, add an Express rate-limiting middleware and apply it to the search routes so that requests hitting controller.searchAsPhrase (and ideally all search routes) are throttled. We should use a well-known package such as express-rate-limit rather than building custom logic. To avoid changing existing functionality, the limiter should sit before the existing middlewares (rest.eitherContent, controller methods) and simply block or delay excessive requests; normal traffic should behave exactly as before.
Concretely in routes/search.js:
- Add an import for
express-rate-limitnear the other imports. - Define a limiter instance configured for a reasonable window and maximum, e.g. 100 requests per 15 minutes per IP (matching the example in the background).
- Apply this limiter to the relevant routes. To cover both word and phrase search (and address both alert variants), apply it to the base
'/'route and the/phraseroute, by inserting it as a middleware argument beforerest.eitherContent.
No existing imports need changing; we just add a new one. No other files are modified.
| @@ -2,9 +2,16 @@ | ||
| const router = express.Router() | ||
| import controller from '../db-controller.js' | ||
| import rest from '../rest.js' | ||
| import rateLimit from 'express-rate-limit' | ||
|
|
||
| // Rate limiter for search endpoints to mitigate denial-of-service attacks | ||
| const searchLimiter = rateLimit({ | ||
| windowMs: 15 * 60 * 1000, // 15 minutes | ||
| max: 100, // limit each IP to 100 search requests per windowMs | ||
| }) | ||
|
|
||
| router.route('/') | ||
| .post(rest.eitherContent, controller.searchAsWords) | ||
| .post(searchLimiter, rest.eitherContent, controller.searchAsWords) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for search. Please use POST.' | ||
| res.status(405) | ||
| @@ -12,7 +17,7 @@ | ||
| }) | ||
|
|
||
| router.route('/phrase') | ||
| .post(rest.eitherContent, controller.searchAsPhrase) | ||
| .post(searchLimiter, rest.eitherContent, controller.searchAsPhrase) | ||
| .all((req, res, next) => { | ||
| res.statusMessage = 'Improper request method for search. Please use POST.' | ||
| res.status(405) |
| @@ -37,7 +37,8 @@ | ||
| "express-oauth2-jwt-bearer": "^1.7.4", | ||
| "express-urlrewrite": "~2.0.3", | ||
| "mongodb": "^7.1.0", | ||
| "morgan": "~1.10.1" | ||
| "morgan": "~1.10.1", | ||
| "express-rate-limit": "^8.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@jest/globals": "^30.3.0", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.3.1 | None |
…ause the endpoint is logged.
Closes #245
Closes #246
Closes #248
Closes #256
Summary
Requests with wrong or missing
Content-Typeheaders were causing 500 errors and connection resets instead of proper HTTP error responses. This was becauseexpress.json()leftreq.bodyunparsed, and controllers crashed accessingundefinedproperties — dropping the TCP connection before any response was sent.Changes
app.js— Restrictedexpress.json()to only parseapplication/jsonandapplication/ld+jsonbodies. This also fixes a latent issue whereapplication/ld+jsonbodies (core to a JSON-LD API) were never being parsed by the JSON middleware. Removed unnecessaryexpress.urlencoded()middleware (this is a JSON API, not a form-processing app).utils.js— RelocatedcreateExpressError()here fromcontrollers/utils.jsso it is available to both controllers and REST middleware. Fixed the bug (createExpressError overwrites err.code switch values unconditionally #256) where theswitch (err.code)block setstatusCode/statusMessagecorrectly, but unconditional fallback lines immediately overwrote both values. Restructured to set defaults in the initializer, then override forerr.code === 11000.controllers/utils.js— Removed the oldcreateExpressError()definition and its named export.bulk.js,crud.js,delete.js,gog.js,history.js,overwrite.js,patchSet.js,patchUnset.js,patchUpdate.js,putUpdate.js,release.js,search.js) — Migrated allcreateExpressError(err)calls toutils.createExpressError(err). Removed superfluousconsole.log().rest.js— Added three per-route Content-Type validation middlewares applied before controllers:verifyJsonContentType— requiresapplication/jsonorapplication/ld+json. Applied to create, update, patch, set, unset, overwrite, query, bulkCreate, and bulkUpdate routes.verifyTextContentType— requirestext/plain. Not currently mounted on any route but available for future use.verifyEitherContentType— requiresapplication/json,application/ld+json, ortext/plain. Applied to search routes./id, DELETE/delete, PATCH/release) do not mount any Content-Type middleware.messengererror handler.messenger's 403 handler whereerr.messagewas used instead oferror.message, causing the "Forbidden" text to be silently dropped from the response.token.slice(0, 15)to avoid exposing full credentials in responses.bulkCreate.js,bulkUpdate.js,create.js,overwrite.js,patchSet.js,patchUnset.js,patchUpdate.js,putUpdate.js,query.js,search.js) — Mounted the appropriate Content-Type middleware (rest.verifyJsonContentTypeorrest.verifyEitherContentType) on each route handler.routes/api-routes.js— Spacing fix only.routes/static.js— Removed unnecessaryexpress.urlencoded()middleware.routes/__tests__/contentType.test.js— 15+ tests covering all three middlewares: valid types, rejected types, case insensitivity, charset parameters, PUT/PATCH methods, and Content-Type smuggling via comma injection.routes/__tests__/history.test.js,since.test.js— Increased Jest timeout to 10s to fix intermittent test timeouts.routes/__tests__/*.test.js— Removedexpress.urlencoded()from all test app setups to match production configuration.