Command Injection in Nestjs with Cockroachdb
Command Injection in Nestjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
Command injection occurs when an attacker can control part of a system command executed by the application. In a NestJS application using CockroachDB, this risk can arise if user input is improperly passed to shell commands or low-level database operations that ultimately invoke a shell. Although CockroachDB itself is a SQL database and does not directly execute OS commands, developers sometimes construct dynamic queries or invoke external processes for administrative or migration tasks, and this is where the combination becomes dangerous.
Consider a scenario in NestJS where a developer uses Node.js child_process to run a CockroachDB binary (for example, to invoke cockroach sql) with user-controlled arguments. If input is concatenated into the command string without validation or escaping, an attacker can inject additional shell commands. For example, a username or hostname parameter that includes a semicolon or a pipe can lead to arbitrary command execution on the host where CockroachDB is installed. Even if the application primarily uses an ORM like TypeORM with CockroachDB, unsafe usage of raw queries or dynamic query building can still expose injection points when values are interpolated into SQL strings rather than parameterized queries.
The risk is heightened when the application runs with elevated privileges or when the database connection string is constructed from user input. An attacker might attempt to manipulate fields that affect connection parameters, such as host or database name, to alter the behavior of the CockroachDB client. While NestJS encourages the use of parameterized queries and dependency injection, developers who bypass these patterns—especially in custom repositories or scripts—open the door to injection. The 12 security checks in middleBrick, including Input Validation and Unsafe Consumption, are designed to detect such risky patterns in unauthenticated scans, highlighting where user data reaches command or query boundaries without proper sanitization.
In the context of LLM security, command injection in NestJS with CockroachDB can also relate to exposed endpoints that allow arbitrary code execution via crafted prompts. For instance, if an API endpoint accepts a natural language request that is transformed into a database or shell command without strict schema validation, an attacker might use prompt injection techniques to coerce the system into executing unintended commands. middleBrick’s Active Prompt Injection testing specifically probes for such weaknesses by attempting to override instructions or extract system prompts through sequential attacks, ensuring that AI-facing endpoints do not become a vector for command execution.
Cockroachdb-Specific Remediation in Nestjs — concrete code fixes
To mitigate command injection when working with CockroachDB in NestJS, always prefer parameterized queries and avoid string concatenation for SQL or shell commands. Use the built-in query mechanisms provided by your ORM or driver, and validate all user input against strict allowlists.
- Use TypeORM with parameterized queries:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
async findByUsername(username: string): Promise<User[]> {
return this.userRepository.createQueryBuilder('user')
.where('user.username = :username', { username })
.getMany();
}
- If you must use raw SQL with the CockroachDB driver, ensure values are passed as parameters:
import { Injectable } from '@nestjs/common';
import { InjectConnection } from '@nestjs/typeorm';
import { Connection } from 'typeorm';
async runSafeQuery(@InjectConnection() connection: Connection, tenantId: string) {
const queryRunner = connection.createQueryRunner();
await queryRunner.connect();
try {
const result = await queryRunner.query(
'SELECT * FROM tenants WHERE id = $1',
[tenantId],
);
return result;
} finally {
await queryRunner.release();
}
}
- Avoid using
child_processwith dynamic input. If you must invoke CockroachDB binaries, sanitize arguments rigorously or use the HTTP SQL interface instead:
import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { map } from 'rxjs/operators';
@Injectable()
export class CockroachService {
constructor(private readonly httpService: HttpService) {}
async executeStatement(sql: string, params: any[]) {
// Prefer using the SQL interface over shell commands
return this.httpService.post('http://localhost:26257/_status/vars', {
sql,
params,
}).pipe(map(res => res.data));
}
}
- Validate and sanitize any input that influences connection strings or query construction. For example, ensure hostnames do not contain shell metacharacters:
import { Matches } from 'class-validator';
export class CreateTenantDto {
@Matches(/^[a-zA-Z0-9\-]+$/, { message: 'Invalid hostname' })
host: string;
}
By combining strict input validation, parameterized SQL, and avoidance of shell invocation, you reduce the attack surface significantly. middleBrick scans can then verify that these safeguards are in place, checking for proper query parameterization and unsafe consumption patterns in your API endpoints.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |