Prevent Event Loop Blocking in Node.js
Flags synchronous fs, crypto, zlib, and child_process calls, unbounded CPU work, and ReDoS-prone regexes that stall every request in a Node.js server.
What it flags
import { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';
import type { FastifyInstance } from 'fastify';
import { findUserByEmail } from '../db/users';
type LoginBody = { email: string; password: string };
const DUMMY_SALT = randomBytes(16);
export async function authRoutes(app: FastifyInstance) {
app.post<{ Body: LoginBody }>('/login', async (req, reply) => {
const user = await findUserByEmail(req.body.email);
// Runs on the main thread: every other request waits for the hash
const hash = scryptSync(req.body.password, user?.salt ?? DUMMY_SALT, 64);
if (!user || !timingSafeEqual(hash, user.passwordHash)) {
return reply.code(401).send();
}
return { id: user.id };
});
}import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';
import type { FastifyInstance } from 'fastify';
import { findUserByEmail } from '../db/users';
type LoginBody = { email: string; password: string };
const DUMMY_SALT = randomBytes(16);
function deriveKey(password: string, salt: Buffer): Promise<Buffer> {
return new Promise((resolve, reject) => {
scrypt(password, salt, 64, (err, key) => (err ? reject(err) : resolve(key)));
});
}
export async function authRoutes(app: FastifyInstance) {
app.post<{ Body: LoginBody }>('/login', async (req, reply) => {
const user = await findUserByEmail(req.body.email);
const hash = await deriveKey(req.body.password, user?.salt ?? DUMMY_SALT);
if (!user || !timingSafeEqual(hash, user.passwordHash)) {
return reply.code(401).send();
}
return { id: user.id };
});
}Why it matters
Node.js runs your JavaScript on a single main thread, so a synchronous hash, file read, or compression call stalls every other in-flight request, health check, and timer until it returns. Under load this shows up as latency spikes on unrelated endpoints and failed health checks that restart healthy instances. The code is correct and fast when one developer tests it locally, so the cost only appears under concurrent traffic.
Agent instructions
cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.
AGENTS.md
Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.
## Prevent Event Loop Blocking in Node.js Applies to `**/*.js`, `**/*.mjs`, `**/*.cjs`, `**/*.ts`, `**/*.mts`, `**/*.cts`. Review Node.js server code, middleware, and queue consumers for work that blocks the event loop. Flag changes that: - Call synchronous fs APIs such as `readFileSync`, `writeFileSync`, `readdirSync`, or `existsSync` inside request handlers, middleware, or per-message consumer code. - Hash or derive keys on the request path with `crypto.pbkdf2Sync`, `crypto.scryptSync`, `crypto.generateKeyPairSync`, `bcrypt.hashSync`, or `bcrypt.compareSync`. - Compress or decompress request-sized data with `zlib` sync methods such as `gzipSync`, `gunzipSync`, or `brotliCompressSync`. - Run `execSync`, `execFileSync`, or `spawnSync` from `child_process` in a server process. - Add CPU-bound loops, sorts, or nested iteration whose cost grows with request or database input and has no size cap. - Apply regular expressions with nested quantifiers, such as `(a+)+` or `([a-z]+)*$`, to user-controlled strings. - Call `JSON.parse` or `JSON.stringify` on the request path for payloads that can reach many megabytes, such as full exports or unpaginated query results. Do not flag: - Synchronous calls at startup or module load, or in CLI scripts, build tooling, and tests. - Work already moved to `worker_threads`, a worker pool such as Piscina, or a background job queue. - Sync calls on small, fixed-size inputs, such as hashing a short token with `createHash`.
Checks files matching **/*.js, **/*.mjs, **/*.cjs, **/*.ts, **/*.mts, **/*.cts.
More Node.js agents
See all Node.js code review standards
Run this agent on every pull request
cubic reviews every pull request against the agents you turn on and comments on the lines that break them.