Security
Field blacklisting (response field exclusion)
Use disabledFields to permanently exclude sensitive fields from all GET responses, including when the model is populated as a sub-document from another model. This takes precedence over ?select= query params.
// password is NEVER returned — not in list, findById, or populate
hiroki.importModel(User, {
disabledFields: ['password', 'ssn'],
});This is the mirror opposite of allowedFields: use it when it's easier to blacklist a few sensitive fields than to whitelist every field you want to expose.
Populate respects disabledFields
When another model populates a reference to User, the disabled fields are automatically excluded from the populated sub-document:
hiroki.importModel(User, { disabledFields: ['password'] });
hiroki.importModel(Post); // Post has { author: { ref: 'User' } }
// GET /api/posts?populate=author
// → populated author will NOT contain passwordField whitelisting (mass-assignment protection)
Without a whitelist, any field sent in a POST/PUT body reaches the adapter:
// Risk: user sends { name: 'Alice', isAdmin: true }
// isAdmin gets persisted if it's in the schema
hiroki.importModel(User, {
allowedFields: ['name', 'email'], // isAdmin, role, etc. are silently stripped
});Filtering happens before hooks, so beforeCreate / beforeUpdate only see allowed fields.
Query sanitization
Hiroki automatically blocks prototype-pollution vectors in URL parameters:
# These are silently dropped:
GET /api/users?where[__proto__]=bad
GET /api/users?conditions[constructor]=badQuery limits
Default limits prevent abuse via oversized queries:
| Limit | Default | Description |
|---|---|---|
maxFilters | 20 | Max where[] entries per request |
maxInValues | 100 | Max values in a single $in/$nin array |
maxRegexLength | 200 | Max $regex string length (ReDoS protection) |
Override per-model:
hiroki.importModel(User, {
queryLimits: { maxFilters: 5, maxRegexLength: 50 },
});Requests exceeding limits return 400 Bad Request with code: 'QUERY_LIMIT_EXCEEDED'.
Auth integration
Use middleware to protect specific resources:
import { BadRequestError } from 'hiroki';
import type { HirokiMiddleware } from 'hiroki';
const requireAuth: HirokiMiddleware = async (ctx, next) => {
const token = getTokenFromContext(ctx);
if (!verifyToken(token)) {
throw new BadRequestError('Authentication required', 'UNAUTHORIZED');
}
return next();
};
hiroki.importModel(User, { middleware: [requireAuth] });Disabling dangerous methods
Protect destructive operations at the config level:
hiroki.importModel(User, {
disabledMethod: ['DELETE'],
});Disabled methods return 405 Method Not Allowed.