Insufficient Logging in Adonisjs with Dynamodb
Insufficient Logging in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
When an AdonisJS application uses the AWS SDK to interact with DynamoDB, insufficient logging creates a blind spot that can hide both application misuse and configuration issues. AdonisJS relies on Node.js runtime behavior and the underlying SDK calls; if those calls are not explicitly recorded, you lose visibility into request parameters, authorization outcomes, and error conditions.
DynamoDB itself does not retain application-level logs. Instead, it emits operational metrics and low-level call traces via AWS CloudTrail and Amazon CloudWatch. If AdonisJS does not log request IDs, key identifiers (such as the DynamoDB key), and the SDK response status, correlation across CloudTrail, application logs, and monitoring tools becomes difficult. This gap is especially relevant for unauthenticated attack surfaces scanned by middleBrick, where missing context can delay detection of enumeration or data exposure patterns.
Consider a route that fetches a user record by ID without validating input or logging the incoming identifier. If the DynamoDB call fails due to throttling or a permission issue, the AdonisJS handler may return a generic error without recording the request payload or the AWS SDK error code. An attacker can probe this behavior to infer existence of records or to test for privilege escalation, while defenders lack the necessary audit trail. middleBrick’s checks for BOLA/IDOR and Input Validation highlight the risk, but they also show that logging must capture the intent and result of each DynamoDB interaction.
Specific gaps include missing logs for authorization decisions, missing structured context for each DynamoDB operation (such as table name and key), and missing capture of SDK errors like ConditionalCheckFailedException. Without these, incident responders cannot reconstruct the sequence of events that led to data exposure or privilege escalation. MiddleBrick’s findings for Data Exposure and Authorization emphasize the need to instrument the application so that every critical operation is recorded with sufficient detail for audit and alerting.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
To address insufficient logging when using DynamoDB in AdonisJS, instrument each SDK call with structured logs that include the operation, table, key, request parameters (sanitized), response metadata, and any errors. Use a consistent correlation ID so that logs from AdonisJS, CloudWatch, and CloudTrail can be correlated during investigations.
Below are concrete examples using the AWS SDK for JavaScript v3 within an AdonisJS controller and an IoC provider. These examples log key details while avoiding logging sensitive payloads such as passwords or tokens.
// resources/controllers/user.controller.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { DynamoDBClient, GetItemCommand, PutItemCommand } from '@aws-sdk/client-dynamodb'
import { marshall, unmarshall } from '@aws-sdk/util-dynamodb'
import { v4 as uuidv4 } from 'uuid'
export default class UsersController {
private readonly client = new DynamoDBClient({})
public async getUser({ request, response, logger }: HttpContextContract) {
const userId = request.param('id')
const correlationId = request.correlationId || uuidv4()
logger.info('fetch-user-start', { correlationId, userId, table: 'users' })
const command = new GetItemCommand({
TableName: 'users',
Key: marshall({ id: userId })
})
try {
const response = await this.client.send(command)
if (!response.Item) {
logger.warn('fetch-user-not-found', { correlationId, userId, table: 'users' })
return response
}
logger.info('fetch-user-success', { correlationId, userId, table: 'users' })
return unmarshall(response.Item)
} catch (error: any) {
logger.error('fetch-user-error', {
correlationId,
userId,
table: 'users',
code: error.name,
message: error.message
})
return response.badRequest({ error: 'unable_to_fetch_user' })
}
}
public async createUser({ request, response, logger }: HttpContextContract) {
const payload = request.only(['id', 'email', 'role'])
const correlationId = request.correlationId || uuidv4()
logger.info('create-user-start', { correlationId, payload })
const command = new PutItemCommand({
TableName: 'users',
Item: marshall({
id: payload.id,
email: payload.email,
role: payload.role,
createdAt: new Date().toISOString()
})
})
try {
await this.client.send(command)
logger.info('create-user-success', { correlationId, payload })
return response.created({ id: payload.id })
} catch (error: any) {
logger.error('create-user-error', {
correlationId,
payload,
code: error.name,
message: error.message
})
return response.badRequest({ error: 'unable_to_create_user' })
}
}
}
Register a scoped logger in AdonisJS that automatically enriches entries with a correlation ID, and ensure that the correlation ID is propagated across async SDK calls. This makes it possible to tie CloudWatch log events to specific DynamoDB operations and to middleBrick findings for Authorization and Data Exposure.
// start/hooks.ts
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
import { v4 as uuidv4 } from 'uuid'
export const hooks = {
async onRequest({ request }: { request: any }) {
request.correlationId = request.header('x-correlation-id') || uuidv4()
}
}
// providers/logger_provider.ts
import { IocResolver } from '@ioc:Adonis/Core/Resolver'
import { Logger } from '@poppinss/dev-utils'
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class LoggerProvider {
constructor(protected app: ApplicationContract) {}
public register() {
this.app.container.singleton('logger', () => {
const logger = new Logger('app')
return {
info(msg, meta) { logger.info(msg, meta) },
warn(msg, meta) { logger.warn(msg, meta) },
error(msg, meta) { logger.error(msg, meta) }
}
})
}
}
Use middleware to attach the correlation ID to responses and to log summary information for each request, including DynamoDB calls made during the request lifecycle. This aligns with middleBrick’s recommendations for Instrumentation and helps ensure that findings related to Authorization, Data Exposure, and Input Validation are actionable.
// middleware/correlation.ts
import { Exception } from '@poppinss/utils'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export async function correlation(ctx: HttpContextContract, next: () => Promise) {
const correlationId = ctx.request.correlationId
await next()
ctx.response.header('x-correlation-id', correlationId)
ctx.logger.info('request-summary', {
correlationId,
method: ctx.request.method(),
url: ctx.request.url(),
status: ctx.response.status,
dynamoCalls: ctx.extra?.dynamoCalls || 0
})
}
For production, configure structured logging to a central system and set retention policies that align with compliance requirements. This ensures that middleBrick’s findings for Logging map to real, queryable evidence during audits.