/** * @module @arcis/node/middleware/mass-assign * * Mass-assignment runtime guard (sdk-vectors.md tier 1 #25). * * The classic mass-assignment vulnerability: * * ```js * const user = await User.findOne({ id }); * Object.assign(user, req.body); // attacker sets req.body.is_admin = true * await user.save(); * ``` * * This middleware filters `req.body` to a per-route allowlist before * the handler runs. Two modes: * * - `'strip'` (default) — silently drop disallowed keys, continue. * - `'reject'` — return 400 with the offending key names. * * Pair it with the audit rule (`MASS-ASSIGN` in `arcis audit`) for the * static-analysis side and the route-level middleware for the runtime * side. Audit catches `Object.assign(target, req.body)` patterns at * build time; this middleware catches the runtime data flow. * * ```ts * import { massAssign } from '@arcis/node'; * * app.post('/users', * massAssign({ allow: ['email', 'password', 'name'] }), * async (req, res) => { * // req.body has been filtered — is_admin / role / created_at all gone. * const user = await User.create(req.body); * res.json(user); * }, * ); * ``` * * Default scope is top-level keys only. Nested objects pass through * untouched — that's deliberate: nested allowlists encourage * `allow: ['profile.bio', 'profile.avatar']` style strings which * become a parser, not a guard. Use a schema validator (Zod / Joi / * Arcis's `validate`) when nested filtering is required; this * middleware handles the 80% case of "filter req.body for an ORM * mass-assign call". */ import type { RequestHandler } from 'express'; export interface MassAssignOptions { /** * Allowlist of permitted top-level keys on `req.body`. Required — * a missing or empty array would silently strip every key, almost * certainly a configuration mistake. */ allow: readonly string[]; /** * Behavior when `req.body` contains a key NOT in `allow`: * - `'strip'` (default): silently drop the key, continue. * - `'reject'`: return `statusCode` (default 400) with a JSON * body listing the disallowed keys. */ mode?: 'strip' | 'reject'; /** Status code for the reject path. Default: 400. */ statusCode?: number; /** Error message in the reject body. Default: "Disallowed fields". */ message?: string; /** * Skip the filter when `req.body` is not a plain object (string, * array, FormData, etc.). Default: true. Set to false to surface * a 400 ("body must be an object") on those payloads — useful for * routes that should ONLY accept JSON objects. */ passThroughNonObjects?: boolean; } /** * Build a mass-assignment guard middleware. Runs against `req.body` * before the route handler — must be installed AFTER body-parsing * middleware (`express.json()` / `express.urlencoded()`) so `req.body` * is already populated. */ export declare function massAssign(options: MassAssignOptions): RequestHandler; export default massAssign; //# sourceMappingURL=mass-assign.d.ts.map