Out Of Bounds Write in Feathersjs with Cockroachdb
Out Of Bounds Write in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
An Out Of Bounds Write occurs when application logic allows data to be written outside the intended memory or data structure boundaries. In a Feathersjs service backed by Cockroachdb, this typically manifests through unchecked array indices, numeric identifiers, or length fields that are directly influenced by client-supplied input. Because Feathersjs is a JavaScript framework, the runtime is susceptible to JavaScript type coercion and unsafe integer handling when constructing queries or buffers. When these unchecked values are used to index into arrays, slice buffers, or parameterize low-level database operations that ultimately interact with Cockroachdb, the boundary checks may be bypassed.
With Cockroachdb, the risk is amplified in scenarios where Feathersjs dynamically constructs SQL-like queries (via an ORM or query builder) using user-controlled offsets or sizes. For example, a service might accept a position and length from the request to manipulate a stored JSONB array field. If the service performs arithmetic such as position + length without validating that the result stays within the array bounds, an attacker can craft a request that writes data beyond the allocated array region. Cockroachdb will execute the statement as provided, potentially corrupting adjacent data rows or causing unexpected mutations in other columns. This is not a Cockroachdb bug; it is a reflection of unchecked client input being translated into executable statements.
Moreover, Feathersjs hooks and custom middleware that pre-process payloads can inadvertently pass malformed or oversized data to Cockroachdb. If a hook modifies an array field by concatenating user input without verifying the resulting length, the service may attempt to write beyond the original allocation when the updated document is persisted. Because Cockroachdb stores JSONB data with its own internal boundaries, an out-of-bounds write can corrupt the document structure, leading to partial updates or serialization errors that are difficult to trace. The combination of Feathersjs’s flexibility in data handling and Cockroachdb’s strict SQL semantics creates a narrow window where unchecked client data can bypass expected constraints.
Real-world attack patterns include manipulating offset and limit parameters in paginated endpoints, or abusing numeric fields that control buffer sizes in binary-like stored data. An attacker may also exploit missing validation in patch operations (PATCH) where only a subset of fields are updated, but the indices or positions within those fields are not sanitized. Since middleBrick scans unauthenticated attack surfaces and checks input validation, such misconfigurations would be surfaced as high-severity findings under the Input Validation and Property Authorization checks, with remediation guidance emphasizing strict bounds checking and type validation before data reaches Cockroachdb.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on enforcing strict input validation and ensuring all arithmetic involving indices and lengths is bounded before being passed to Cockroachdb. Below are concrete code examples demonstrating safe practices in a Feathersjs service.
1. Validate array bounds before update
Assume a service stores an array of items in a Cockroachdb JSONB column. Before modifying the array, verify indices against the actual length.
// services/items/items.class.js
const { Service } = require('feathersjs');
class ArrayItemService extends Service {
async update(id, data) {
const item = await super.get(id);
const array = item.payload || [];
// Ensure requested indices are within bounds
if (data.position < 0 || data.position >= array.length) {
throw new Error('Position out of bounds');
}
if (data.position + data.patch.length > array.length) {
throw new Error('Patch extends beyond array length');
}
// Perform in-place patch safely
for (let i = 0; i < data.patch.length; i++) {
array[data.position + i] = data.patch[i];
}
return super.update(id, { payload: array });
}
}
module.exports = function () {
const app = this;
app.use('/array-items', new ArrayItemService({ Model: /* Cockroachdb model */ }));
};
2. Use parameterized queries with explicit limits
When manipulating JSONB arrays via SQL, use Cockroachdb’s built-in functions with validated parameters.
// services/data/data.class.js
const { Service } = require('feathersjs');
const { pool } = require('../db'); // Cockroachdb connection pool
class SafeDataService extends Service {
async patch(id, data) {
const client = await pool.connect();
try {
// Validate offset and length explicitly
const offset = Number(data.offset) || 0;
const length = Number(data.length) || 1;
if (offset < 0 || length <= 0 || offset + length > 1000) {
throw new Error('Invalid offset or length');
}
const query = `
UPDATE documents
SET payload = jsonb_set(
payload,
ARRAY['data', $1::text],
(payload->'data'->>$1::text)::jsonb || $2::jsonb
)
WHERE id = $3 AND array_length(payload->'data', 1) >= $4;
`;
await client.query(query, [offset, data.patch, id, offset + length]);
return super.update(id, data);
} finally {
client.release();
}
}
}
module.exports = function () {
const app = this;
app.use('/safe-data', new SafeDataService({ Model: /* Cockroachdb model */ }));
};
3. Enforce numeric constraints in hooks
Add a before hook to sanitize inputs globally for a service.
// hooks/validate-bounds.js
module.exports = function () {
return async context => {
const { offset = 0, limit = 10 } = context.data || {};
if (!Number.isInteger(offset) || !Number.isInteger(limit)) {
throw new Error('Offset and limit must be integers');
}
if (offset < 0 || limit <= 0 || limit > 100) {
throw new Error('Offset or limit out of allowed range');
}
// Store validated values for downstream use
context.meta = context.meta || {};
context.meta.bounds = { offset, limit };
return context;
};
};
// In service setup
app.use('/bounded', new SomeService({ Model }));
app.service('bounded').hooks({
before: {
all: [require('./hooks/validate-bounds')()]
}
});