Mass Assignment in Adonisjs with Dynamodb
Mass Assignment in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
Mass assignment occurs when an API accepts user-supplied input and directly maps it to model or entity properties without filtering. In AdonisJS, developers often use fill or merge to apply request body fields to an ORM model. When the backend persistence is Amazon DynamoDB, the lack of a traditional relational schema and the use of document-like attribute maps can make it easier to inadvertently persist unexpected or sensitive fields. AdonisJS does not automatically restrict which keys can be assigned, so if the code relies only on mass assignment helpers (e.g., merge or fill) without an allowlist, an attacker can inject fields such as isAdmin, role, or permissions into the DynamoDB item. This becomes a BOLA/IDOR and privilege escalation vector when authorization checks are incomplete or applied at a higher level only.
DynamoDB’s schema-less nature means any string key can be stored, so unchecked user input can create new attributes or overwrite existing ones. If your AdonisJS service uses unvalidated DTOs or directly merges request payloads into a DynamoDB document, you risk unintended data writes. For example, an item representing a user might include metadata fields used internally by your service; without an allowlist, an attacker could add or modify fields that change behavior (e.g., toggling a subscription flag or escalating privileges). The risk is compounded if authorization logic elsewhere assumes these fields are trustworthy, since DynamoDB will store exactly what was provided.
Because middleBrick tests the unauthenticated attack surface and includes checks for BOLA/IDOR and BFLA/Privilege Escalation, it can surface these issues when mass assignment paths exist in combination with DynamoDB persistence. The scanner analyzes the OpenAPI contract and runtime behavior, detecting endpoints that accept wide input objects and map them into DynamoDB without proper property-level authorization or input validation.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
To secure mass assignment with DynamoDB in AdonisJS, use strict allowlists and avoid direct merging of raw request bodies into DynamoDB documents. Prefer DTOs that explicitly define permitted fields and validate types before constructing DynamoDB attribute values. Below are concrete patterns that reduce risk.
- Define an allowlist DTO and validate before writing to DynamoDB:
import { schema } from '@ioc:Adonis/Core/Validator'
import { DateTime } from 'luxon'
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'
const userSchema = schema.create({
type: 'object',
required: ['email', 'name'],
members: {
email: schema.string.optional(),
name: schema.string.optional(),
subscriptionTier: schema.string.optional([
schema.values(['free', 'pro', 'enterprise'])
]),
settings: schema.object.optional().members({
notifications: schema.boolean.optional(),
theme: schema.string.optional()
})
}
})
export async function updateUserDynamo(userId: string, input: Record) {
const payload = validateSyncOrThrow(userSchema, input)
const client = new DynamoDBClient({})
const item: Record = {
pk: { S: `USER#${userId}` },
email: { S: payload.email },
name: { S: payload.name },
subscriptionTier: payload.subscriptionTier ? { S: payload.subscriptionTier } : undefined,
settings: payload.settings ? {
M: Object.entries(payload.settings).reduce((acc, [k, v]) => {
acc[k] = typeof v === 'boolean' ? { BOOL: v } : { S: v }
return acc
}, {} as Record)
} : undefined,
updatedAt: { S: DateTime.local().toISO() }
}
const cmd = new PutItemCommand({
TableName: process.env.USERS_TABLE,
Item: item,
ConditionExpression: 'attribute_exists(pk)'
})
await client.send(cmd)
}
- Use a computed subset for DynamoDB writes instead of merging the entire request:
import { DynamoDBClient, UpdateItemCommand } from '@aws-sdk/client-dynamodb'
export async function incrementUsageCount(userId: string, by: number) {
const client = new DynamoDBClient({})
const cmd = new UpdateItemCommand({
TableName: process.env.USERS_TABLE,
Key: { pk: { S: `USER#${userId}` } },
UpdateExpression: 'SET usageCount = if_not_exists(usageCount, :zero) + :inc',
ConditionExpression: 'attribute_exists(pk)',
ExpressionAttributeValues: {
':inc': { N: String(by) },
':zero': { N: '0' }
}
})
await client.send(cmd)
}
- For broader protection, combine middleware that prunes disallowed fields before persistence:
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export class MassAssignmentMiddleware {
public async handle({ request, response, next }: HttpContextContract) {
const allowed = new Set(['email', 'name', 'subscriptionTier', 'settings'])
const body = request.body()
const pruned: any = {}
for (const key of Object.keys(body)) {
if (allowed.has(key)) {
pruned[key] = body[key]
}
}
request.merge(pruned)
await next()
}
}
These examples ensure only expected fields reach DynamoDB, reducing the chance of privilege escalation via mass assignment. They also align with middleBrick’s checks for Input Validation and Property Authorization, producing findings that map to frameworks such as OWASP API Top 10 and SOC2.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |