Null Pointer Dereference in Express with Cockroachdb
Null Pointer Dereference in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability
A null pointer dereference in an Express service that uses CockroachDB typically occurs when application code attempts to access properties or invoke methods on a value that should represent a row but is instead null or undefined. Because CockroachDB is wire-compatible with PostgreSQL, many Node.js drivers return null for rows that do not exist, and JavaScript developers must explicitly handle this case. If a route handler destructures a row or accesses row.column without verifying the row exists, an exception is thrown at runtime, which can crash the request handler, expose stack traces, or trigger uncontrolled fallback behavior that leads to information disclosure or inconsistent API responses.
With unauthenticated scanning, middleBrick tests endpoints that rely on URL parameters or query inputs to fetch records from CockroachDB. For example, an endpoint like /users/:id may execute a query such as SELECT * FROM users WHERE id = $1. If the provided :id does not match any row, the query result may be null. When the handler does not check for null and proceeds to read row.email or passes the row to a serializer, the runtime throws a null pointer dereference. This becomes an observable vulnerability when the error propagates to the client as a 500 response, revealing implementation details or enabling denial of service. The scanner validates whether responses are stable across invalid or missing identifiers and whether exceptions are caught and normalized.
In a typical Express + CockroachDB stack, the following patterns increase risk: missing validation after db.query, unchecked return values from helper data access layers, and assumptions that foreign key relationships guarantee row existence. The scanner’s checks for BOLA/IDOR and Input Validation specifically probe whether endpoints safely handle missing or manipulated identifiers. Proper mitigation requires explicit null checks, structured error handling, and defensive coding before using query results in downstream logic.
Cockroachdb-Specific Remediation in Express — concrete code fixes
To prevent null pointer dereference, always verify that a row exists before accessing its properties. Below are concrete, working examples for Express routes using the pg driver to interact with CockroachDB.
Safe query handling with explicit null checks
const express = require('express');
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const app = express();
app.get('/users/:id', async (req, res) => {
const { id } = req.params;
try {
const result = await pool.query('SELECT id, email, name FROM users WHERE id = $1', [id]);
const user = result.rows[0];
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ id: user.id, email: user.email, name: user.name });
} catch (err) {
console.error('Database error:', err);
return res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Using a data access helper with centralized validation
Encapsulate database interactions to enforce null checks and consistent error handling across routes.
// db/users.js
async function getUserById(pool, id) {
const result = await pool.query('SELECT id, email, username FROM users WHERE id = $1', [id]);
return result.rows[0]; // may be null
}
module.exports = { getUserById };
// routes/user.js
const express = require('express');
const { pool } = require('../db');
const { getUserById } = require('../db/users');
const router = express.Router();
router.get('/:id', async (req, res) => {
const user = await getUserById(pool, req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
module.exports = router;
Handling arrays and ensuring safe iteration
When queries return arrays, validate length before accessing indices. For related records, avoid assuming nested objects exist.
app.get('/users/:id/posts', async (req, res) => {
const result = await pool.query(
'SELECT p.id, p.title FROM posts p WHERE p.user_id = $1',
[req.params.id]
);
if (!result.rows || result.rows.length === 0) {
return res.status(404).json({ error: 'No posts found for this user' });
}
res.json(result.rows);
});
Leveraging middleware for consistent error handling
Use an error-handling middleware to catch unexpected exceptions and avoid leaking internal details.
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
res.status(500).json({ error: 'Internal server error' });
});