Description: Switch to using `crypto` random for boundary values
Author: Ben Shonaldmann <ben@benweissmann.com>
Origin: upstream, https://github.com/form-data/form-data/commit/3d172308
Bug: <upstream-bugtracker-url>
Bug-Debian: https://bugs.debian.org/1109551
Forwarded: not-needed
Applied-Upstream: 4.0.4, commit:3d172308
Reviewed-By: Xavier Guimard <yadd@debian.org>
Last-Update: 2025-07-24

--- a/lib/form_data.js
+++ b/lib/form_data.js
@@ -6,6 +6,7 @@
 var parseUrl = require('url').parse;
 var fs = require('fs');
 var Stream = require('stream').Stream;
+var crypto = require('crypto');
 var mime = require('mime-types');
 var asynckit = require('asynckit');
 var populate = require('./populate.js');
@@ -347,12 +348,7 @@
 FormData.prototype._generateBoundary = function() {
   // This generates a 50 character boundary similar to those used by Firefox.
   // They are optimized for boyer-moore parsing.
-  var boundary = '--------------------------';
-  for (var i = 0; i < 24; i++) {
-    boundary += Math.floor(Math.random() * 10).toString(16);
-  }
-
-  this._boundary = boundary;
+  this._boundary = '--------------------------' + crypto.randomUUID();
 };
 
 // Note: getLengthSync DOESN'T calculate streams length
--- /dev/null
+++ b/test/integration/test-boundary-prediction.js
@@ -0,0 +1,57 @@
+var common = require('../common');
+var assert = common.assert;
+var FormData = require(common.dir.lib + '/form_data');
+var predictV8Randomness = require('predict-v8-randomness');
+
+var initialSequence = [
+  Math.random(),
+  Math.random(),
+  Math.random(),
+  Math.random(),
+];
+var predictor = new predictV8Randomness.Predictor(initialSequence);
+
+predictor.predictNext(24).then(function (next24RandomOutputs) {
+  var predictedBoundary = next24RandomOutputs
+    .map(function (v) {
+      return Math.floor(v * 10).toString(16);
+    })
+    .join('');
+
+  var boundaryIntro = '----------------------------';
+
+  var payload =
+    'zzz\r\n' +
+    boundaryIntro +
+    predictedBoundary +
+    '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n' +
+    boundaryIntro +
+    predictedBoundary +
+    '--\r\n';
+
+  var FIELDS = {
+    my_field: {
+      value: payload,
+    },
+  };
+
+  // count total
+  var fieldsPassed = Object.keys(FIELDS).length;
+
+  // prepare form-receiving http server
+  var server = common.testFields(FIELDS, function (fields) {
+    fieldsPassed = fields;
+  });
+
+  server.listen(common.port, function () {
+    var form = new FormData();
+
+    common.actions.populateFields(form, FIELDS);
+
+    common.actions.submit(form, server);
+  });
+
+  process.on('exit', function () {
+    assert.strictEqual(fieldsPassed, 0);
+  });
+});
