Data Exposure in Adonisjs with Cockroachdb
Data Exposure in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
AdonisJS, a Node.js web framework, often uses an ORM layer (such as Lucid) to interact with databases including CockroachDB. When API endpoints expose database query results without appropriate filtering or authorization, sensitive fields—such as internal identifiers, email addresses, password hashes, or role flags—can be returned to unauthenticated or low-privilege callers. This exposure typically stems from missing property-level checks, overly broad SELECT statements, or serialized models that include sensitive columns.
With CockroachDB as the backend, the risk is compounded by distributed SQL semantics: data may be replicated across nodes, and misconfigured secondary indexes or tenant boundaries can allow cross-tenant data visibility in multi-tenant deployments. If an endpoint returns a full user row from a CockroachDB table without masking sensitive columns, an attacker can harvest PII or use enumeration techniques to infer valid user identifiers. Common triggers include:
- Unfiltered query responses in REST or GraphQL resolvers that pull all columns from a CockroachDB table.
- Inadequate row-level security (RLS) policies in CockroachDB, allowing a connection user to read rows it should not access.
- Improper use of eager loading that pulls related sensitive records (e.g., roles, permissions) into the response.
middleBrick scans identify Data Exposure findings by correlating OpenAPI/Swagger definitions (including $ref resolution) with runtime responses, flagging endpoints that return sensitive data without authorization. This is especially important for APIs backed by CockroachDB where schema design and tenant isolation must align with least-privilege principles.
Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes
To mitigate Data Exposure when using AdonisJS with CockroachDB, apply targeted query controls and schema-aware authorization. The following patterns demonstrate secure practices.
1. Select only required, non-sensitive fields
Avoid selecting all columns (e.g., User.query() without constraints). Instead, explicitly pick safe fields.
const users = await User.query()
.select('id', 'username', 'email', 'created_at')
.from('users')
.where('tenant_id', tenantId);
return users;
2. Use CockroachDB row-level security (RLS) and tenant isolation
Define RLS policies on sensitive tables so that queries respect tenant boundaries. In AdonisJS, ensure your database user and connection are scoped appropriately.
-- CockroachDB SQL to enable RLS on a table
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_policy ON users
FOR ALL
USING (tenant_id = current_setting('app.tenant_id')::UUID);
-- Then in AdonisJS, set the session tenant context before querying
import { Database } from '@ioc:Adonis/Lucid/Database';
await Database.rawQuery("SET app.tenant_id = $1", [tenantId]);
const records = await Database.from('users').select('id', 'username');
3. Mask or omit sensitive columns in serialization
Customize the serializer to exclude password hashes, roles, or internal flags. With Lucid models, use computed fields and hidden attributes.
import { DateTime } from 'luxon';
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm';
import { ManyToMany, HasMany } from '@ioc:Adonis/Lucid/Relations';
export default class User extends BaseModel {
@column({ isPrimary: true })
public id: number;
@column()
public username: string;
@column()
public email: string;
@column()
public tenantId: number;
@column({ serialize: false })
public passwordHash: string;
@column({ serialize: false })
public role: string;
@column.dateTime({ autoCreate: true })
public createdAt: DateTime;
}
When returning user data, computed fields can provide safe derived values without exposing sensitive columns.
const safeUser = {
id: user.id,
username: user.username,
email: user.email,
tenantId: user.tenantId,
};
4. Validate and sanitize inputs to prevent IDOR
Ensure that object identifiers (e.g., userId) are validated and scoped to the requesting tenant before querying CockroachDB.
import { schema } from '@ioc:Adonis/Core/Validator';
const userSchema = schema.create({
userId: schema.string.optional(),
});
export default class UsersController {
public async show({ params, request, response }) {
const payload = request.validate({ schema: userSchema });
const user = await User.query()
.where('id', payload.userId || params.id)
.where('tenantId', request.$guard.tenantId)
.select('id', 'username', 'email')
.firstOrFail();
return user;
}
}
5. Use parameterized queries to avoid injection and unintended data leakage
Always parameterize inputs when building raw queries against CockroachDB to prevent injection that could lead to broader data exposure.
const result = await Database.from('transactions')
.select('id', 'amount', 'currency')
.where('tenant_id', tenantId)
.andWhere(function () {
this.where('status', '=', 'completed').orWhere('reviewed', '=', true);
});Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |