Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
日本語の概要は準備中です。原文の説明を表示しています。
Use when reviewing Node.js or TypeScript server code - Express/Fastify/Nest handlers, Next.js server actions and route handlers, child_process, ORM raw queries, prototype pollution, template engines, JWT and outbound HTTP clients
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
TypeScript types vanish at runtime: req.query.id: string can arrive as an array or an object. Most Node findings come from that gap, from exec with a template string, from raw-query escape hatches in otherwise safe ORMs, and — in Next.js — from forgetting that a server action is a public endpoint.
// ❌ ?id[$ne]=x or ?id[]=a&id[]=b gives an object or array, not a string
const doc = await Orders.findOne({ id: req.query.id as string, owner: user.id });
// ✅ validate at the boundary (zod/valibot/class-validator) and coerce
const { id } = z.object({ id: z.string().uuid() }).parse(req.query);
Length, includes and equality checks on an unvalidated query value can be bypassed the same way.
// ❌ shell
exec(`convert ${file} out.png`);
spawn("git", ["clone", url], { shell: true });
// ✅ no shell, end of options
execFile("git", ["clone", "--", url, dir]);
Also check the env option for request-controlled keys (NODE_OPTIONS=--require …).
| Library | Unsafe | Safe |
|---|---|---|
| Prisma | $queryRawUnsafe, $executeRawUnsafe, Prisma.raw() | $queryRaw tagged template, model methods |
| TypeORM | query("… " + x), .where(\id = ${id}`)` | .where("id = :id", { id }) |
| Sequelize | sequelize.query(\… ${x}`), literal(x)` | replacements / bind |
| Knex | knex.raw(\… ${x}`)` | knex.raw("… ?", [x]) |
| Drizzle | sql.raw(x) | sql\… ${x}`` |
Mongo/Mongoose: operator injection from JSON bodies (section 1), $where.
"use server" file is a callable POST endpoint, whether or not a page renders a form for it. Each must authenticate and authorize inside the action.// ❌ the page is admin-only, the action is not
"use server";
export async function deleteUser(id: string) { await db.user.delete({ where: { id } }); }
// ✅
export async function deleteUser(id: string) {
const session = await auth();
if (session?.user.role !== "admin") throw new Error("forbidden");
await db.user.delete({ where: { id } });
}
middleware.ts: middleware matchers miss paths, and Next.js < 15.2.3 / 14.2.25 / 13.5.9 / 12.3.5 let an x-middleware-subrequest header skip middleware entirely (CVE-2025-29927). Check in the handler or data layer too.NEXT_PUBLIC_* variables are shipped to the browser.Report only with a gadget: a recursive merge/set of user JSON (lodash.merge, _.set with user paths, hand-written deep merge) where __proto__ or constructor.prototype reaches a property later read for an auth decision, a child_process option (shell, env), or a template option.
// ❌ deepMerge(defaults, req.body) with {"__proto__": {"isAdmin": true}}
// ✅ validate the body against a schema first; build lookups with Object.create(null) or Map
<%- user %>, Handlebars {{{ user }}}, Pug != user → XSS.res.render(view, req.query) or { ...req.body } as locals: template options ride along (EJS outputFunctionName/escapeFunction, CVE-2022-29078 class) → RCE.Handlebars.compile, pug.compile, ejs.render.vm/vm.runInNewContext is not a security boundary; vm2 is discontinued after sandbox escapes. Running user code in either is CRITICAL when reachable.eval, new Function, require(userPath), dynamic import(userPath).js-yaml 3.x load() instantiates JavaScript types (!!js/function) — safeLoad or js-yaml 4's load.node-serialize unserialize on input.baseURL and sends the configured auth headers to that host (CVE-2025-27152 class) — validate that user-supplied paths are relative.jsonwebtoken: jwt.decode does not verify; jwt.verify without algorithms.crypto.timingSafeEqual for MACs (throws on length mismatch — compare lengths first); Math.random for tokens.app.set("trust proxy", true) trusts every hop's X-Forwarded-For.express.static(".") or express.static(__dirname) serves source and .env.res.sendFile(path.join(dir, name)) instead of res.sendFile(name, { root: dir }).file.originalname used as the stored path.app.use(authMiddleware) runs without it.$queryRaw tagged templates, TypeORM parameters or execFile arrays as injection.JSON.parse as unsafe deserialization.exec(, query(, raw(, $queryRawUnsafe(.shell: true."use server" files with exported mutations and no auth() call inside them.as string on req.query / req.params values used in a query.merge(, defaultsDeep(, set( fed with req.body.Node.js security best practices · Next.js data security · CVE-2025-29927, CVE-2025-27152, CVE-2022-29078 · CWE-78, 89, 1321 · OWASP Top 10:2025
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
日本語の概要は準備中です。原文の説明を表示しています。
Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping
日本語の概要は準備中です。原文の説明を表示しています。
Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal
日本語の概要は準備中です。原文の説明を表示しています。
Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task
日本語の概要は準備中です。原文の説明を表示しています。
How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.
日本語の概要は準備中です。原文の説明を表示しています。
Use when deciding whether a request needs an analiz task before implementation - the conditions that require the architect's analysis versus going straight to implementation
日本語の概要は準備中です。原文の説明を表示しています。