CVE-2025-46653 Overview
CVE-2025-46653 affects node-formidable (Formidable), a widely used Node.js multipart form parser. Versions 2.1.0 through 3.x before 3.5.3 rely on the hexoid library to generate filenames for uploaded content. The hexoid package is documented as not cryptographically secure, which means generated identifiers can be predicted under certain conditions. An attacker who can guess the filename of untrusted executable content placed in an upload directory may be able to access it. The maintainers note a scenario in which only the last two characters of a hexoid string need to be guessed, although this is rarely relevant in production deployments. The advisory clarifies that typical applications do not allow attackers to upload and execute their own content through this behavior.
Critical Impact
Predictable filename generation through a non-cryptographic random source [CWE-338] may permit limited information disclosure of uploaded files in Formidable-based applications.
Affected Products
- node-formidable Formidable 2.1.0 through 2.x (Node.js)
- node-formidable Formidable 3.x prior to 3.5.3 (Node.js)
- Applications embedding vulnerable Formidable versions for multipart upload handling
Discovery Timeline
- 2025-04-26 - CVE-2025-46653 published to NVD
- 2026-05-13 - Last updated in NVD database
Technical Details for CVE-2025-46653
Vulnerability Analysis
Formidable parses multipart form data and persists uploaded files using filenames produced by the hexoid library. The library generates pseudo-random hexadecimal identifiers but explicitly disclaims cryptographic strength. When applications expose the upload directory or otherwise let attackers infer parts of a generated filename, an attacker may predict the remaining characters and access the stored file. The maintainers describe a corner case in which only the last two characters of a hexoid string must be guessed, reducing the search space to a small set of attempts. This weakness maps to [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator. Real-world exploitation depends on application behavior, including how upload paths are exposed and whether uploaded content is executable.
Root Cause
The root cause is the use of hexoid(25) to mint filenames for untrusted content. hexoid is optimized for speed rather than unpredictability and does not draw from a cryptographically secure entropy source. Applications that treat the generated filename as a security boundary inherit that weakness.
Attack Vector
An attacker submits or anticipates uploads through a Formidable-backed endpoint, then attempts to retrieve the resulting file by guessing its filename. The attack requires network access, low privileges, and high attack complexity, since the attacker must reason about the random output and the application must expose the upload location.
// Patch in src/Formidable.js (commit 022c2c5577dfe14d2947f10909d81b03b6070bf5)
// Replaces hexoid with @paralleldrive/cuid2 for stronger, fingerprint-bound IDs
import { init as cuid2init } from '@paralleldrive/cuid2';
import dezalgo from 'dezalgo';
import { EventEmitter } from 'node:events';
import fsPromises from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { StringDecoder } from 'node:string_decoder';
import once from 'once';
import FormidableError, * as errors from './FormidableError.js';
import PersistentFile from './PersistentFile.js';
import VolatileFile from './VolatileFile.js';
import DummyParser from './parsers/Dummy.js';
import MultipartParser from './parsers/Multipart.js';
import { json, multipart, octetstream, querystring } from './plugins/index.js';
const CUID2_FINGERPRINT = `${process.env.NODE_ENV}-${os.platform()}-${os.hostname()}-${os.machine()}`;
const createId = cuid2init({ length: 25, fingerprint: CUID2_FINGERPRINT.toLowerCase() });
// Removed: const toHexoId = hexoid(25);
Source: GitHub Commit 022c2c5
Detection Methods for CVE-2025-46653
Indicators of Compromise
- Repeated HTTP GET requests against upload directories with sequentially varying short suffixes on otherwise similar filenames.
- Web server access logs showing brute-force enumeration patterns against paths managed by Formidable's uploadDir.
- Presence of Formidable versions between 2.1.0 and 3.5.2 in package-lock.json or npm ls formidable output.
Detection Strategies
- Inventory Node.js services and dependency manifests for formidable versions matching the vulnerable range.
- Use Software Composition Analysis (SCA) tools to flag transitive dependencies pulling vulnerable Formidable releases.
- Inspect application code for routes that serve files directly from Formidable's upload directory without authorization checks.
Monitoring Recommendations
- Alert on high request rates against static paths that map to Formidable upload locations.
- Log all file write operations from the Node.js process and correlate with HTTP request IDs to spot scan-and-fetch patterns.
- Monitor for unexpected executable file types (.js, .php, .sh) being written by application processes handling uploads.
How to Mitigate CVE-2025-46653
Immediate Actions Required
- Upgrade formidable to version 3.5.3 or later, which replaces hexoid with @paralleldrive/cuid2 for filename generation.
- Audit all routes that expose Formidable upload directories and add authentication or signed-URL access controls.
- Ensure uploaded files are stored outside any web-served document root and never executed by the application server.
Patch Information
The maintainers fixed the issue in Formidable 3.5.3 via commit 022c2c5577dfe14d2947f10909d81b03b6070bf5. The patch removes hexoid and initializes a cuid2 generator with a host-derived fingerprint to produce 25-character collision-resistant identifiers. See the GitHub Changelog Entry and the Zast AI Vulnerability Report for additional details.
Workarounds
- Override Formidable's filename option to generate names using crypto.randomBytes() from Node.js core.
- Place uploads in a directory that is not directly browsable and require authenticated, server-mediated retrieval.
- Strip or normalize executable extensions on upload and set restrictive file permissions on the storage directory.
# Configuration example: upgrade Formidable and verify the installed version
npm install formidable@^3.5.3
npm ls formidable
# Override filename generation with a CSPRNG when custom logic is required
cat <<'EOF' > upload.js
import formidable from 'formidable';
import crypto from 'node:crypto';
const form = formidable({
uploadDir: '/var/app/uploads',
keepExtensions: false,
filename: () => crypto.randomBytes(16).toString('hex'),
});
EOF
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


