HIGH nosql injectionadonisjscockroachdb

Nosql Injection in Adonisjs with Cockroachdb

Nosql Injection in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability

AdonisJS is a Node.js web framework that encourages structured query building, yet developers can still construct unsafe queries when working with CockroachDB. CockroachDB is PostgreSQL-wire compatible, and AdonisJS typically interacts with it through an ORM layer that translates JavaScript objects and query-builder chains into SQL. When input is concatenated into query conditions or passed through dynamic operators, the abstraction can leak, allowing attacker-controlled values to alter query structure.

Consider a search endpoint that builds a query from request query parameters. An unsanitized username parameter can be injected into a JSONB or object-based filter, changing the logical intent of the query. For example, an attacker could supply a value like {"$or": [{"id": "1"}]} that shifts the query from a simple equality check to a logical bypass. Because CockroachDB supports rich query semantics and AdonisJS query builders can map JavaScript objects directly, the framework may pass these values through to the database without strict schema enforcement.

Specific patterns that increase risk include dynamic where objects, query.where() chains that incorporate user input as keys, and serialization/deserialization of objects that are later used in filters. If the application uses raw query snippets or merges user input into object paths (e.g., query.where(field, value) where field is user-supplied), the boundary between data and command blurs. This enables NoSQL-style logic manipulation, such as changing comparison operators or injecting additional clauses, which can lead to authentication bypass or unauthorized data access.

In a typical AdonisJS project using CockroachDB, the query builder may resemble an ORM, but under the hood it ultimately produces SQL. A NoSQL Injection here does not mean CockroachDB is interpreting JavaScript; it means that malformed input changes the semantics of the generated SQL in unintended ways, such as injecting tautologies or modifying field comparisons. Attack patterns include supplying operators like $gt, $in, or logical combinators that the query builder may not sanitize, especially when using dynamic keys or passing raw JSON through endpoints that build filters programmatically.

Compliance mappings such as OWASP API Top 10 (A03:2023 Injection) and SOC2 controls highlight the importance of validating and sanitizing all inputs. middleBrick scans detect these issues by correlating OpenAPI/Swagger specifications with runtime behavior, identifying endpoints that accept complex objects or dynamic query structures without strict schema validation. The scanner flags inputs that could modify query logic and provides remediation guidance to enforce type validation, allowlisting, and strict query builder usage.

Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes

Remediation centers on strict input validation, avoiding dynamic field names in query builders, and using parameterized conditions. Always treat user input as data, never as part of the query structure. The following patterns demonstrate secure approaches when working with AdonisJS and CockroachDB.

1. Use parameterized query builder conditions

Instead of injecting raw objects or dynamic keys, define explicit fields and use bound parameters. This ensures that values are always treated as data.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'

export default class UsersController {
  public async index({ request }: HttpContextContract) {
    const username = request.input('username')
    // Safe: field name is hardcoded, value is parameterized
    const users = await User.query()
      .where('username', username)
      .exec()
    return users
  }
}

2. Validate and allowlist object keys when filtering

If filtering on JSONB columns, validate keys against an allowlist and map them to known columns. Do not directly use user-supplied keys in where clauses.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'
import schema from '@ioc:Adonis/Core/Validator'

export async function filterUsersByJsonb(ctx: HttpContextContract) {
  const allowedKeys = ['status', 'role', 'country']
  const payload = await schema.validate({
    schema: schema.object({
      filters: schema.object({
        status: schema.string.optional(),
        role: schema.string.optional(),
        country: schema.string.optional(),
      }).optional(),
    }),
    data: ctx.request.body(),
  })

  const { filters } = payload
  const query = User.query()

  if (filters) {
    if (allowedKeys.includes('status') && filters.status) {
      query.where('settings', 'status', filters.status)
    }
    if (allowedKeys.includes('role') && filters.role) {
      query.where('settings', 'role', filters.role)
    }
    if (allowedKeys.includes('country') && filters.country) {
      // Use ->> for JSONB text extraction in CockroachDB
      query.whereRaw('settings->>'country' = $1', [filters.country])
    }
  }

  return query.exec()
}

3. Avoid dynamic operators and raw concatenation

Never allow user input to specify operator names or to construct raw condition fragments. Use enumerated values mapped to safe operators server-side.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'

export async function searchUsers(ctx: HttpContextContract) {
  const field = ctx.request.input('field') // e.g., "username"
  const value = ctx.request.input('value') // e.g., "alice"
  const operator = ctx.request.input('op')  // e.g., "equals"

  const allowedFields = ['username', 'email', 'status']
  const allowedOps = {
    equals: (col: string, val: string) => ({ [col]: val }),
    contains: (col: string, val: string) => ({ [col]: { $like: `%${val}%` } }),
  }

  if (!allowedFields.includes(field) || !allowedOps[operator]) {
    ctx.response.badRequest({ error: 'Invalid parameters' })
    return
  }

  const condition = allowedOps[operator](field, value)
  const users = await User.query().where(condition).exec()
  return users
}

4. Use schema validation for incoming objects

When APIs accept objects for query or body parameters, validate structure and types rigorously. This prevents unexpected keys or nested operators from being interpreted as query directives.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { schema } from '@ioc:Adonis/Core/Validator'

const filterSchema = schema.object({
  status: schema.string.optional(),
  minScore: schema.number.optional(),
})

export async function getWithValidation(ctx: HttpContextContract) {
  const payload = await schema.validate({
    schema: filterSchema,
    data: ctx.request.body(),
  })

  const query = User.query()
  if (payload.status) {
    query.where('status', payload.status)
  }
  if (payload.minScore) {
    query.where('score', '>=', payload.minScore)
  }
  return query.exec()
}

5. Prefer parameterized raw queries when necessary

If you must use raw SQL, always use parameterized bindings instead of string interpolation to prevent injection.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export async function rawWhere(ctx: HttpContextContract) {
  const country = ctx.request.input('country')
  const users = await Database.from('users')
    .whereRaw('settings->>'country' = $1', [country])
    .exec()
  return users
}

Frequently Asked Questions

Can NoSQL Injection affect read-only endpoints in AdonisJS with CockroachDB?
Yes. Even read-only endpoints can be affected if user input alters the logic of generated queries, potentially exposing more data than intended or bypassing access controls.
Does using an ORM in AdonisJS fully prevent NoSQL Injection when working with CockroachDB?
Not automatically. ORMs reduce risk but can still pass unsafe objects through if dynamic keys or unvalidated input are used. Strict validation and avoiding dynamic query structures are essential.