HIGH sandbox escapeexpresscockroachdb

Sandbox Escape in Express with Cockroachdb

Sandbox Escape in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability

A sandbox escape in an Express application using CockroachDB typically occurs when user-controlled input is used to construct database queries without proper validation or parameterization, allowing an attacker to break out of a restricted execution context and interact with the database or underlying system in unintended ways. This combination is notable because CockroachDB supports advanced SQL features such as JSON operators, array functions, and dynamic statement execution, which can be leveraged to chain query behaviors in ways that bypass intended isolation.

Consider an endpoint that dynamically builds SQL using string concatenation to filter JSON fields:

app.get('/users/:tenant', (req, res) => {
  const tenant = req.params.tenant;
  const query = `SELECT * FROM users WHERE metadata @> '{\"tenant\": \"${tenant}\"}'`;
  pool.query(query).then(r => res.json(r.rows));
});

If the tenant value is not strictly validated, an attacker can supply a value like ' OR true; --, causing the condition to always be true and potentially exposing rows across tenants. CockroachDB’s support for JSON path expressions and concatenation can allow an attacker to extend the query into related tables or functions, effectively escaping the logical sandbox that the application intended to enforce. This pattern violates the principle of least privilege and can lead to horizontal or vertical privilege escalation.

Another vector involves stored procedures or user-defined functions that execute dynamic SQL. CockroachDB allows executing SQL from within functions, and if those functions are invoked with attacker-controlled inputs, they can be used to run statements outside the expected permissions context:

app.post('/search', async (req, res) => {
  const { filter } = req.body;
  const result = await pool.query('SELECT run_search($1)', [filter]);
  res.json(result.rows);
});

If the backend function run_search internally uses EXECUTE without strict input validation, an attacker may inject additional commands, such as accessing system tables or invoking external network functions, leading to data exposure or SSRF-like behaviors. The interplay between Express routing, dynamic SQL generation, and CockroachDB’s extensibility increases the attack surface when secure coding practices are not consistently applied.

Input validation and schema design are critical. Without strict type checks and allowlists, the flexibility that makes CockroachDB powerful also enables subtle escape paths. For example, numeric IDs should be validated as integers, and JSON inputs should be constrained to known structures before being embedded in queries. The scanner checks included in middleBrick can detect such construction patterns and highlight missing parameterization as a high-severity finding.

Cockroachdb-Specific Remediation in Express — concrete code fixes

Remediation centers on strict input validation, parameterized queries, and avoiding dynamic SQL construction. Always treat user input as untrusted and enforce type and format constraints before using values in database operations.

  • Use parameterized queries for all user-supplied values:
app.get('/users/:tenant', async (req, res) => {
  const tenant = req.params.tenant;
  if (!/^[a-zA-Z0-9_-]+$/.test(tenant)) {
    return res.status(400).send('Invalid tenant');
  }
  const result = await pool.query(
    'SELECT * FROM users WHERE metadata @> $1',
    [{ tenant }]
  );
  res.json(result.rows);
});
  • When filtering on JSON fields, validate structure explicitly and use placeholders for values, not keys or operators:
app.get('/profiles/:id', async (req, res) => {
  const id = parseInt(req.params.id, 10);
  if (isNaN(id)) {
    return res.status(400).send('Invalid ID');
  }
  const result = await pool.query(
    'SELECT data->>\'email\' FROM profiles WHERE id = $1',
    [id]
  );
  res.json(result.rows);
});
  • Avoid dynamic SQL in stored functions invoked from Express. If dynamic behavior is required, construct the SQL safely in the application layer using a whitelist of allowed operations:
const allowedOperations = { list: 'SELECT * FROM reports', summary: 'SELECT count(*) FROM reports' };
app.post('/run', async (req, res) => {
  const { op } = req.body;
  if (!allowedOperations[op]) {
    return res.status(400).send('Unsupported operation');
  }
  const result = await pool.query(allowedOperations[op]);
  res.json(result.rows);
});
  • Enforce row-level security policies in CockroachDB separately from application logic, and use parameterized roles where applicable. Do not rely on query construction to enforce tenant isolation.

middleBrick scans can identify concatenated query patterns and missing parameterization, providing prioritized findings with severity levels and remediation guidance. The CLI can be integrated into development workflows using middlebrick scan <url>, and the GitHub Action can enforce security thresholds in CI/CD pipelines.

Frequently Asked Questions

How can I validate tenant identifiers safely in Express with CockroachDB?
Use a strict allowlist regex (e.g., /^[a-zA-Z0-9_-]+$/) and validate before building queries; prefer parameterized queries so values are never concatenated into SQL strings.
Does using JSON operators in CockroachDB increase escape risk in Express apps?
Yes, if user input is used to construct JSON paths or operators without validation. Always validate and constrain JSON structures and use parameterized values rather than dynamic string assembly.