Xml External Entities in Adonisjs with Cockroachdb
Xml External Entities in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
XML External Entity (XXE) injection occurs when an application processes XML input that references external entities, allowing an attacker to disclose files, perform SSRF, or conduct denial-of-service. In AdonisJS, this risk arises when XML parsing libraries (such as xml2js or fast-xml-parser) are configured to resolve external entities and the parsed data influences database interactions with CockroachDB.
When AdonisJS routes accept XML payloads—often via file uploads or HTTP request bodies—and forward values to CockroachDB queries (for example, constructing dynamic query conditions or logging), untrusted XML entities can expand to sensitive filesystem paths, internal service endpoints, or resource-intensive expansions. CockroachDB does not introduce the injection, but the combination of an XML parser that resolves external references and database operations that incorporate parsed values can expose internal data or amplify impact through SSRF or excessive resource consumption.
Consider an endpoint that imports user-provided XML configuration into application settings and stores metadata into CockroachDB:
const { parse } = require('fast-xml-parser');
const { Base } = require('@ioc:Adonis/Lucid/Orm');
const Config = use('App/Models/Config');
async function storeConfig(xmlBody) {
const options = { ignoreAttributes: false };
const jsonObj = parse(xmlBody, options);
// If external entities are allowed, jsonObj could reference files or internal URLs
const record = await Config.create({
name: jsonObj.config.name,
value: jsonObj.config.value,
});
return record;
}
If fast-xml-parser is not explicitly configured to disallow external entities, an attacker can supply:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM 'file:///etc/passwd' > ]>
<config><name>test</name><value>&xxe;</value></config>
The parsed value may disclose file contents when the XML is processed before reaching AdonisJS routes. If those values are later used in CockroachDB operations, such as filtering or logging, the exposure can be chained with database queries to infer existence of resources or trigger internal network probes.
Another scenario involves dynamic SQL-like query building where parsed XML fields are concatenated into strings executed against CockroachDB via a driver that does not enforce strict parameterization. Although AdonisJS’s Lucid ORM encourages parameterized queries, raw query usage or concatenation in migrations or seeders can reintroduce risk:
const { Pool } = require('pg'); // CockroachDB wire-compatible driver
const { parse } = require('fast-xml-parser');
async function runRawQuery(xmlBody) {
const jsonObj = parse(xmlBody);
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// Unsafe concatenation: external entity expansion could alter query structure
const query = `SELECT * FROM settings WHERE key = '${jsonObj.config.key}'`;
const client = await pool.connect();
try {
const res = await client.query(query);
return res.rows;
} finally {
client.release();
}
}
Here, if the XML parser resolves entities, jsonObj.config.key could expand to a large payload or a network resource, leading to SSRF or denial-of-service when sent to CockroachDB. While CockroachDB itself does not process XML, the vulnerability chain spans parsing, application logic, and database interaction.
To summarize, the risk in the AdonisJS + CockroachDB context emerges from insecure XML parsing combined with dynamic query construction. CockroachDB’s behavior is neutral, but the application layer determines whether external entities are resolved and how untrusted data reaches the database.
Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on disabling external entity resolution in XML parsers and ensuring database interactions use strict parameterization. Below are concrete, safe patterns for AdonisJS applications that work with CockroachDB.
1. Secure XML parsing with external entity disabled
Configure parsers to reject DOCTYPE and external entities explicitly. For fast-xml-parser, set allowBooleanAttributes, ignoreAttributes, and crucially processEntities to false:
const { parse } = require('fast-xml-parser');
function safeParseXML(xmlBody) {
const options = {
ignoreAttributes: true,
allowBooleanAttributes: false,
processEntities: false, // Disables external entity expansion
validate: false,
isArray: (tagName) => false,
};
return parse(xmlBody, options);
}
// Usage in route handler
Route.post('/import', async ({ request }) => {
const xmlBody = request.input('xml');
const config = safeParseXML(xmlBody);
await Config.create({
name: config.config.name,
value: config.config.value,
});
return { status: 'ok' };
});
For libraries that do not support processEntities, prefer an XML parser with secure-by-default behavior or pre-process the XML to strip DOCTYPE declarations.
2. Parameterized database interactions
Always use parameterized queries or the query builder’s binding mechanisms when incorporating parsed values into CockroachDB operations. With Lucid ORM:
const Config = use('App/Models/Config');
Route.post('/config', async ({ request }) => {
const body = request.only(['name', 'value']);
// Lucid’s create uses parameterized inserts by default
const record = await Config.create(body);
return record;
});
For raw queries with the CockroachDB-compatible driver, use placeholders and pass values separately:
const { Pool } = require('pg');
async function findSetting(key) {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const client = await pool.connect();
try {
// Use $1-style placeholders (PostgreSQL wire protocol)
const res = await client.query('SELECT * FROM settings WHERE key = $1', [key]);
return res.rows;
} finally {
client.release();
}
}
This prevents concatenation of attacker-controlled values into SQL strings, mitigating XXE-induced query manipulation.
3. Input validation and schema enforcement
Validate XML-derived data against a strict schema before it reaches persistence. For example, enforce string patterns and lengths:
const validator = use('Validator');
Route.post('/config', async ({ request }) => {
const xmlBody = request.input('xml');
const config = safeParseXML(xmlBody);
const validation = await validator.validateAll(config.config, {
name: 'required|string|max:255',
value: 'required|string|max:1024',
});
if (validation.fails()) {
return response.badRequest(validation.messages());
}
await Config.create(validation.validatedData);
return { status: 'ok' };
});
Reject unexpected entity expansions by ensuring the parser does not follow external references. Combine this with runtime monitoring for unusual resource usage when processing large XML payloads.
4. Operational safeguards
Limit the size of incoming XML payloads at the HTTP layer to reduce denial-of-service risk. In AdonisJS, this can be done via middleware before XML parsing:
const MAX_XML_SIZE = 1024 * 1024; // 1 MB
Route.post('/import', async ({ request }, next) => {
const contentLength = request.headers()['content-length'];
if (contentLength && parseInt(contentLength, 10) > MAX_XML_SIZE) {
return response.badRequest('Payload too large');
}
await next();
}, async (ctx) => {
const xmlBody = ctx.request.input('xml');
const config = safeParseXML(xmlBody);
await Config.create(config.config);
});
These measures reduce the likelihood that external entity expansion will affect file reads, network calls, or database interactions with CockroachDB.
By combining secure parsing, strict parameterization, and input validation, you eliminate the conditions required for XXE to impact your AdonisJS application and its CockroachDB backend.