Introduction
The server/package-lock.json file in this repository pins dozens of transitive and direct dependencies, including form-data, which sits quietly in the dependency tree at version ^2.3.3 — visible right next to the handlebars entry that was also patched in this pull request. While form-data looks like a boring utility library (it just builds multipart/form-data request bodies for HTTP clients), a flaw deep inside its boundary-generation logic earned it a critical CVE: CVE-2025-7783.
The problem is deceptively simple: to separate the different fields inside a multipart HTTP body, form-data needs to generate a unique "boundary" string. Older versions of the library generated that boundary using Math.random(). That single design decision is why this vulnerability matters — Math.random() is not cryptographically secure, and any value derived from it can, under the right conditions, be predicted or brute-forced by an attacker who controls or observes enough requests.
If your Node.js server uses form-data (directly, or transitively through libraries like axios, node-fetch, or request) to build outbound HTTP requests containing user-supplied data, this vulnerability is directly relevant to you.
The Vulnerability Explained
At a conceptual level, the vulnerable code inside form-data looked something like this:
// Simplified representation of the vulnerable pattern
function getBoundary() {
return '--------------------------' + Math.random().toString(16);
}
This boundary string is inserted into the Content-Type header (multipart/form-data; boundary=...) and repeated between each field in the request body to tell the receiving server where one field ends and the next begins.
The issue is that Math.random():
- Is a non-cryptographic PRNG — it is designed for speed, not unpredictability.
- Can have its internal state inferred by observing a sequence of outputs, in some JS engine implementations.
- Produces boundaries that are far more guessable than a value generated from
crypto.randomBytes().
Why this is dangerous in server/package-lock.json
Look at the dependency declaration visible in the diff for this PR:
"form-data": "^2.3.3",
This pin sits in the exact same package.json/package-lock.json files that were touched to fix the handlebars CVE. That's important: dependency files are living attack surfaces. Every entry in package-lock.json is a promise about exactly which version of code — and exactly which known CVEs — ship with your server.
Example attack scenario: Imagine your Node.js backend uses form-data to relay a file upload or webhook payload to a downstream API. If an attacker can predict or influence the boundary string used in that outgoing multipart request, they could:
- Craft a payload whose content collides with the predictable boundary, effectively injecting additional "fields" into the request body that the receiving server did not expect.
- Use this to perform request smuggling — sneaking extra parameters or headers past validation logic that assumes boundaries are unique and unguessable.
- In multi-tenant or proxy scenarios, potentially cause cross-request data leakage if boundary predictability interacts poorly with connection reuse or caching layers.
None of this requires exotic tooling — an attacker only needs to observe enough boundary values (e.g., from logs, timing, or repeated requests) to start narrowing down the PRNG's likely output space.
The Fix
The remediation for CVE-2025-7783 is to upgrade form-data to a version where the boundary generator uses Node's crypto module instead of Math.random(). Conceptually, the fixed logic looks like this:
Before (vulnerable):
function getBoundary() {
return '--------------------------' + Math.random().toString(16);
}
After (fixed):
const { randomBytes } = require('crypto');
function getBoundary() {
return '--------------------------' + randomBytes(16).toString('hex');
}
This mirrors exactly the kind of change applied in this PR's lockfile diff — a version bump plus a refreshed integrity hash, such as we can see for the sibling fix to handlebars:
- "handlebars": "^4.7.7",
+ "handlebars": "^4.7.9",
- "version": "4.7.7",
- "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+ "version": "4.7.9",
+ "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
The same pattern applies to form-data: the entry in server/package.json ("form-data": "^2.3.3") must be bumped to a version line that resolves to a patched release, and server/package-lock.json needs its resolved URL, version, and integrity hash refreshed to match. Both package.json and package-lock.json must be updated together — updating one without the other leaves npm ci installing the old, vulnerable version regardless of what package.json says.
Why both files matter:
- package.json declares the acceptable version range your team intends to allow.
- package-lock.json pins the exact resolved version and hash that gets installed in CI/CD and production. If this file still points to 2.3.3's tarball and integrity hash, the vulnerable boundary-generation code ships regardless of the semver range in package.json.
Prevention & Best Practices
- Never use
Math.random()for anything security-relevant. Boundaries, tokens, session identifiers, CSRF tokens, and password reset codes must always come fromcrypto.randomBytes(),crypto.randomInt(), or an equivalent CSPRNG. - Run
npm audit/npm audit fixregularly as part of CI, so known-vulnerable transitive dependencies likeform-datasurface before they reach production. - Use SCA (software composition analysis) scanning — tools like Trivy, Snyk, or GitHub Dependabot continuously diff your lockfile against known CVE databases, exactly how this issue was flagged.
- Pin and review lockfiles carefully. A
package-lock.jsondiff that only bumps a patch version can still fix a critical CVE — don't dismiss small-looking diffs during code review. - Audit transitive dependencies, not just direct ones.
form-datais often pulled in indirectly through HTTP client libraries; it's easy to miss in a manual dependency review.
Key Takeaways
form-datapinned at^2.3.3inserver/package-lock.jsonrelied onMath.random()to generate multipart boundaries — a textbook CWE-338 weakness.- Predictable boundaries in multipart requests can enable boundary-collision and request-smuggling style attacks against downstream services.
- Fixing this requires updating both
server/package.json(the version range) andserver/package-lock.json(the resolved version + integrity hash) — matching the exact pattern used to remediate the neighboringhandlebarsCVE in this same PR. - Dependency-level vulnerabilities hide in files most developers rarely open by hand — automated SCA scanning is essential to catch them.
- Any library generating identifiers, tokens, or boundaries for security-sensitive protocols should be audited for use of a cryptographically secure RNG.
How Orbis AppSec Detected This
- Source: The multipart boundary value generated internally by the
form-datalibrary whenever the server builds an outgoing multipart/form-data HTTP request (e.g., file uploads, webhook relays). - Sink: The boundary-generation function inside
form-data's internals, which is embedded via the dependency pinned inserver/package-lock.json("form-data": "^2.3.3"). - Missing control: No use of a cryptographically secure random number generator (
crypto.randomBytes) — the library relied onMath.random(), a weak PRNG unsuitable for security-sensitive values. - CWE: CWE-338 — Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).
- Fix: Upgrade the
form-datadependency declaration and lockfile entry to a patched version that generates boundaries using Node'scryptomodule.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
CVE-2025-7783 is a good reminder that "boring" utility dependencies — like a package that just formats multipart HTTP bodies — can carry critical, exploitable weaknesses when they touch anything resembling randomness or identifiers. The vulnerable Math.random()-based boundary generator in form-data, pinned at ^2.3.3 in server/package-lock.json, is a small piece of code with outsized security implications. The fix is straightforward — bump the dependency, refresh the lockfile hash, and let a proper CSPRNG do the job it was designed for. Treat every dependency version pin in your lockfile as a security decision, not just a build detail.