From f82f75a78ff44a947785aede327f803f6752a93a Mon Sep 17 00:00:00 2001 From: Juan Navarrete <86685195+jnavarrete-ep@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:28:23 -0700 Subject: [PATCH] fix(getFileIDs): prevent race condition crash on multi-file uploads encryptAndUploadFiles with >=2 file paths can crash the Node.js process with an unhandled TypeError "Cannot read properties of undefined (reading 'name')" at lib/SendSafely.js:2902. Three compounding bugs in getFileIDs together produce the crash: 1. Index/array mismatch. The non-legacy branch pushed to files in fs.stat completion order, then called handleResponse(i, ..., files) where i is the input index. When completion order != input order, files[i] was undefined. Fixed by assigning files[i] = file. 2. Broken sequential chain. Inside _loop, the line p = _p wrote to an undeclared identifier. In sloppy mode this silently created a global; the outer for loop's p never updated, so every iteration ran in parallel against the initial Promise.resolve(). Fixed by having _loop return its chained promise and having the for loop reassign p. 3. Unhandled throw. The TypeError escaped via a .then() callback chain that is not caught upstream, so consumer sendsafely.error and file.upload.error handlers never saw it. Added a defensive guard in handleResponse that raises the existing server.error event instead of throwing when files[index] is undefined. No public API, event names, or parameter shapes change. The legacy {size, data} upload path is untouched. Repo has no test suite; verified manually by calling encryptAndUploadFiles with multiple file paths. Made-with: Cursor --- lib/SendSafely.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/SendSafely.js b/lib/SendSafely.js index 42ec787..8661e3c 100644 --- a/lib/SendSafely.js +++ b/lib/SendSafely.js @@ -2899,6 +2899,13 @@ function EncryptAndUploadFile (eventHandler, request) { let files = []; myself.addEncryptionKey(packageId, serverSecret, keyCode); function handleResponse(index, parts, files, fileUtil) { + if (files[index] === undefined) { + myself.eventHandler.raise(myself.SERVER_ERROR_EVENT, { + error: 'INTERNAL_ERROR', + message: 'getFileIDs: file at index ' + index + ' is undefined' + }); + return; + } var serverFilename = (files[index].name === undefined) ? myself.defaultFileName : files[index].name; myself.responseParser.processAjaxDataRaw(myself.createFileID(packageId, directoryId, files[index], serverFilename, parts, uploadType, myself.async), function (resp) { if(resp.response === "SUCCESS") { @@ -2953,7 +2960,7 @@ function EncryptAndUploadFile (eventHandler, request) { } let _loop = function _loop(i, _p) { - _p = _p.then(function (_) { + return _p.then(function (_) { return new Promise(function (resolve) { let f = filesPath[i]; @@ -2968,18 +2975,18 @@ function EncryptAndUploadFile (eventHandler, request) { } else { let fileUtil = new FileUtil({filePath: f, callback: function(){}}); fileUtil.init().then(function(file) { - files.push(file); + files[i] = file; handleResponse(i, file.totalParts, files, fileUtil); resolve(); }); } }); }); - p = _p; }; - for (let i = 0, p = Promise.resolve(); i < filesPath.length; i++) { - _loop(i, p); + let p = Promise.resolve(); + for (let i = 0; i < filesPath.length; i++) { + p = _loop(i, p); } };