Zip Slip in Adonisjs with Cockroachdb
Zip Slip in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when user-supplied paths are concatenated without proper validation, enabling writes or reads outside the intended directory. In AdonisJS applications that use CockroachDB, the risk emerges not from CockroachDB itself, but from how file system operations are orchestrated around database-driven workflows. For example, an API endpoint might accept a backupId parameter, look up associated file metadata in CockroachDB, and then stream a file to the client using a path built from the record’s stored filename. If the stored filename contains sequences like ../, and the application uses naive path joining (e.g., path.join(baseDir, filename)), an attacker who can influence the database content—perhaps via a compromised admin UI or an insecure import routine—can cause the server to traverse directories and access sensitive files such as /etc/passwd.
Because CockroachDB is often used in distributed environments where schemas are versioned and data migrations are common, developers may inadvertently trust data stored in the database as safe. A migration might copy filenames from an old system into a file_records table without sanitization. Later, an AdonisJS controller that serves backups might use these filenames directly. Even with parameterized SQL queries preventing injection into the SQL layer, the application logic remains vulnerable: the attacker does not need to exploit SQL injection; they only need to ensure a malicious filename exists in the CockroachDB table. The unauthenticated attack surface exposed by AdonisJS routes—tested by middleBrick across 12 security checks including Input Validation and Property Authorization—can highlight such risky path construction patterns.
Consider an endpoint GET /backups/:id that retrieves a file. The controller might do a CockroachDB query to fetch the filename, then use Node’s fs.createReadStream to return the file. If the filename is ../../../secrets/.env and the code does not normalize and validate the resolved path, the stream escapes the backup directory. MiddleBrick’s checks for BOLA/IDOR and Input Validation would flag this if the filename is user-controlled or insufficiently constrained. The presence of CockroachDB does not mitigate this; it simply means the malicious data may persist longer and spread across nodes, increasing the impact of a successful Zip Slip.
Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on strict path validation and isolation of file operations from database values. In AdonisJS, use the path module’s resolve and relative checks to ensure the final path remains within the intended directory. Avoid using raw filenames from CockroachDB directly in filesystem operations. Instead, maintain a mapping between database IDs and sanitized filenames stored in a private directory, or use a hash-based filename scheme.
Secure controller example with CockroachDB query
import { Contract } from '@ioc:Adonis/Core/Contract'
import Database from '@ioc:Adonis/Lucid/Database'
import fs from 'fs'
import path from 'path'
export default class BackupsController {
public async serveBackup({ params, response }: { params: { id: number }, response: Contract.Response }) {
// Fetch file record from CockroachDB using parameterized query
const record = await Database.from('file_records')
.select('id', 'safe_filename', 'content_type')
.where('id', params.id)
.limit(1)
if (!record.length) {
response.status(404).send('Not found')
return
}
const baseDir = '/srv/app/backups'
const filename = record[0].safe_filename // Assume this is sanitized on insert
const resolved = path.resolve(baseDir, filename)
// Ensure the resolved path is within baseDir
if (!resolved.startsWith(path.resolve(baseDir))) {
response.status(400).send('Invalid path')
return
}
response.header('Content-Type', record[0].content_type)
return response.sendFile(resolved)
}
}
On the database side, ensure that filenames inserted into CockroachDB are sanitized. Use a deterministic naming scheme such as uuidv4() stored alongside a safe filename column. The following CockroachDB schema and insert example illustrate this approach:
-- CockroachDB schema
CREATE TABLE file_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
safe_filename STRING NOT NULL,
original_name STRING,
content_type STRING,
created_at TIMESTAMP DEFAULT now()
);
-- Insert with sanitized filename
INSERT INTO file_records (safe_filename, original_name, content_type)
VALUES (replace(gen_random_uuid()::STRING, '-', ''), 'report.pdf', 'application/pdf');
In AdonisJS, you can further harden the system by using a Hooks middleware that validates paths against a whitelist of allowed directories and by enabling middleBrick’s Pro plan for continuous monitoring of file-related Input Validation and Property Authorization findings. This ensures that any new route or migration that introduces risky path handling is caught before deployment.