A critical path traversal vulnerability tracked as CVE-2026-21440 has been disclosed in @adonisjs/bodyparser, a core file-upload handling package used across AdonisJS applications. Assigned a CVSS v3.1 score of 9.2, the flaw allows unauthenticated remote attackers to write arbitrary files anywhere on the server filesystem — a primitive that trivially escalates to remote code execution through cron job injection, SSH key planting, or web shell deployment. All applications running @adonisjs/bodyparser version 10.1.1 or earlier, or any 11.0.0-next pre-release before next.6, are affected and should be patched immediately.

Vulnerability Overview

The root cause lives in the MultipartFile.move() method. When a developer calls this method without explicitly supplying a sanitized output filename via the options.name parameter, the library falls back to this.clientName — the raw, attacker-controlled filename submitted in the multipart request. That unsanitized string is then passed directly into path.join(), which does not strip leading ../ sequences. Because the overwrite option defaults to true, the final fs.move() call silently overwrites any existing file at the resolved path.

The vulnerability is not in a fringe edge case — it fires whenever a developer calls file.move() without an explicit name, which is the pattern shown in the official AdonisJS documentation examples.

The relevant vulnerable code path is reproduced below for clarity:

async move(location: string, options?: { name?: string; overwrite?: boolean }): Promise<void> {
  const fileName = options?.name || this.clientName   // ← unsanitized client input
  const filePath = path.join(location, fileName)       // ← traversal sequences preserved
  await fs.move(this.tmpPath, filePath, { overwrite: options?.overwrite ?? true })
}

CVE Details at a Glance

  • CVE ID: CVE-2026-21440
  • CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory
  • CVSS v3.1 Score: 9.2 (Critical)
  • Attack Vector: Network
  • Attack Complexity: Low
  • Privileges Required: None
  • User Interaction: None
  • GHSA Advisory: GHSA-gvq6-hvvp-h34h

Affected Versions and Patch Status

  • @adonisjs/bodyparser ≤ 10.1.1 — vulnerable; patched in 10.1.2
  • @adonisjs/bodyparser 11.0.0-next.1 through 11.0.0-next.5 — vulnerable; patched in 11.0.0-next.6

If your project lists any version in these ranges in package.json or package-lock.json, treat it as compromised until updated and audited.

Attack Flow: From File Upload to Root

Exploitation requires no credentials and no special browser interaction. An attacker crafts a standard multipart HTTP POST request and sets the filename field in the Content-Disposition header to a directory traversal payload such as ../../etc/cron.d/pwned. When the vulnerable application calls file.move(app.tmpPath()) without specifying a safe name, the path resolves outside the upload directory and the attacker-controlled file content is written to the resolved destination.

A minimal one-line proof of concept using curl illustrates how low the bar is:

curl -X POST http://target:3333/upload \
  -F "file=@payload.txt;filename=../../tmp/pwned.txt"

From arbitrary file write, the escalation paths are numerous:

  • Cron job injection — write a file to /etc/cron.d/ containing a reverse shell command
  • SSH key injection — append to /root/.ssh/authorized_keys if the server runs as root
  • Web shell deployment — overwrite a static asset in the public web root
  • Configuration overwrite — tamper with .env, database configs, or application secrets

Detection: Identifying Vulnerable Code in Your Codebase

Grep your controllers and middleware for calls to file.move() that do not pass a hardcoded or programmatically sanitized name option. The following pattern is vulnerable:

// ❌ vulnerable — clientName flows into filesystem path unmodified
const file = request.file('upload')
await file.move(app.tmpPath())

You can also run a dependency audit to confirm your installed version:

npm list @adonisjs/bodyparser

Any output showing a version at or below 10.1.1 in a production or staging environment is a critical finding that warrants immediate remediation.

Remediation: Secure Code Pattern

The fix is straightforward. Always supply a server-generated filename when calling file.move(), and explicitly set overwrite: false to prevent silent clobbers:

import { cuid } from '@adonisjs/core/helpers'
import path from 'node:path'

// ✅ secure — filename is server-generated, client input never touches the path
const file = request.file('upload')
if (file) {
  const ext = path.extname(file.clientName).toLowerCase()
  const safeName = `${cuid()}${ext}`
  await file.move(app.tmpPath(), { name: safeName, overwrite: false })
}

Beyond the immediate code fix, apply these defense-in-depth controls:

  • Update immediately — upgrade to @adonisjs/bodyparser@10.1.2 or 11.0.0-next.6
  • Extension allowlisting — validate file extensions against a strict allowlist before moving
  • Filesystem isolation — run the application in a container with the upload directory mounted as a separate, minimal-permission volume
  • Principle of least privilege — ensure the web server process cannot write to sensitive system directories
  • WAF rules — add rules to reject multipart requests whose filename fields contain ../, ..\\, or null bytes at the edge

Timeline

  • 2026-01-02 — Patch released in @adonisjs/bodyparser 10.1.2 and 11.0.0-next.6
  • 2026-01-02 — GHSA-gvq6-hvvp-h34h published
  • 2026-01-02 — CVE-2026-21440 assigned and published by NVD

Conclusion

CVE-2026-21440 is a textbook example of why client-supplied input must never be trusted for filesystem operations. The vulnerability pattern — using a raw client filename as a path component — is easy to introduce and hard to spot in code review, especially when the framework documentation does not prominently warn against it. The CVSS 9.2 score reflects the brutal simplicity of exploitation: no authentication, no user interaction, no special conditions. If your application uses @adonisjs/bodyparser, auditing your file.move() call sites and updating to the patched version should be your highest-priority security task this week. Supply chain hygiene tools such as npm audit and automated dependency update bots like Dependabot or Renovate will help ensure you are never caught holding a vulnerable version of this package again.