From 085872a258d90488d616daadeb059c4f37a26d16 Mon Sep 17 00:00:00 2001 From: Aidan Daly <74743624+dalyaidan1@users.noreply.github.com> Date: Sun, 3 Nov 2024 07:52:40 -0500 Subject: [PATCH 1/4] allow the creation of large files via blob --- index.js | 92 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index 647e473..2a36834 100644 --- a/index.js +++ b/index.js @@ -25,41 +25,81 @@ function FileSystemAdapter(options) { } } -FileSystemAdapter.prototype.createFile = function(filename, data) { +FileSystemAdapter.prototype.createFile = function (filename, data) { const filepath = this._getLocalFilePath(filename); const stream = fs.createWriteStream(filepath); return new Promise((resolve, reject) => { try { - if (this._encryptionKey !== null) { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv( - algorithm, - this._encryptionKey, - iv - ); - const encryptedResult = Buffer.concat([ - cipher.update(data), - cipher.final(), - iv, - cipher.getAuthTag(), - ]); - stream.write(encryptedResult); - stream.end(); - stream.on('finish', function() { - resolve(data); - }); + const iv = this._encryptionKey !== null ? crypto.randomBytes(16) : null; + + const cipher = + this._encryptionKey !== null && iv + ? crypto.createCipheriv(this._algorithm, this._encryptionKey, iv) + : null; + + // when working with a Blob, it could be over the max size of a buffer, so we need to stream it + if (data instanceof Blob) { + let readableStream = data.stream(); + + // may come in as a web stream, so we need to convert it to a node stream + if (readableStream instanceof ReadableStream) { + readableStream = Readable.fromWeb(readableStream); + } + + if (cipher && iv) { + // we need to stream the data through the cipher + const cipherTransform = new Transform({ + transform(chunk, encoding, callback) { + try { + const encryptedChunk = cipher.update(chunk); + callback(null, encryptedChunk); + } catch (err) { + callback(err); + } + }, + // at the end we need to push the final cipher text, iv, and auth tag + flush(callback) { + try { + this.push(cipher.final()); + this.push(iv); + this.push(cipher.getAuthTag()); + callback(); + } catch (err) { + callback(err); + } + }, + }); + // pipe the stream through the cipher and then to the main stream + readableStream + .pipe(cipherTransform) + .on("error", reject) + .pipe(stream) + .on("error", reject); + } else { + // if we don't have a cipher, we can just pipe the stream to the main stream + readableStream.pipe(stream).on("error", reject); + } } else { - stream.write(data); + if (cipher && iv) { + const encryptedResult = Buffer.concat([ + cipher.update(data), + cipher.final(), + iv, + cipher.getAuthTag(), + ]); + stream.write(encryptedResult); + } else { + stream.write(data); + } stream.end(); - stream.on('finish', function() { - resolve(data); - }); } - } catch(err) { - return reject(err); + stream.on("finish", resolve); + stream.on("error", reject); + } catch (e) { + reject(e); } }); -} +}; FileSystemAdapter.prototype.deleteFile = function(filename) { const filepath = this._getLocalFilePath(filename); From 5e672578f40ded162d089183fe69f3af3ffc1d3c Mon Sep 17 00:00:00 2001 From: Aidan Daly <74743624+dalyaidan1@users.noreply.github.com> Date: Sun, 3 Nov 2024 08:08:33 -0500 Subject: [PATCH 2/4] add import --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 2a36834..2e41d1e 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ const path = require('path'); const pathSep = require('path').sep; const crypto = require("crypto"); const algorithm = 'aes-256-gcm'; +const { Readable, Transform } = require('stream') function FileSystemAdapter(options) { options = options || {}; From 46c926c572c2ac0af1014dda19728de66e5cd528 Mon Sep 17 00:00:00 2001 From: Aidan Daly <74743624+dalyaidan1@users.noreply.github.com> Date: Sun, 3 Nov 2024 08:19:41 -0500 Subject: [PATCH 3/4] fix bad check --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 2e41d1e..ea467db 100644 --- a/index.js +++ b/index.js @@ -31,11 +31,11 @@ FileSystemAdapter.prototype.createFile = function (filename, data) { const stream = fs.createWriteStream(filepath); return new Promise((resolve, reject) => { try { - const iv = this._encryptionKey !== null ? crypto.randomBytes(16) : null; + const iv = this._encryptionKey ? crypto.randomBytes(16) : null; const cipher = - this._encryptionKey !== null && iv - ? crypto.createCipheriv(this._algorithm, this._encryptionKey, iv) + this._encryptionKey && iv + ? crypto.createCipheriv(algorithm, this._encryptionKey, iv) : null; // when working with a Blob, it could be over the max size of a buffer, so we need to stream it From dcb6242a8bcffc58e25635861d617df50f11c78f Mon Sep 17 00:00:00 2001 From: Aidan Daly <74743624+dalyaidan1@users.noreply.github.com> Date: Sun, 3 Nov 2024 08:28:29 -0500 Subject: [PATCH 4/4] add test --- spec/secureFiles.spec.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/secureFiles.spec.js b/spec/secureFiles.spec.js index 0cbcc01..897a139 100644 --- a/spec/secureFiles.spec.js +++ b/spec/secureFiles.spec.js @@ -306,4 +306,20 @@ describe('File encryption tests', () => { const encryptedData2 = fs.readFileSync(filePath2); expect(encryptedData2.toString('utf-8')).not.toEqual(oldEncryptedData2); }, 5000); + + it('should handle blobs on creation', async function() { + const adapter = new FileSystemAdapter({ + encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD' + }); + const filename = 'file2.txt'; + const filePath = 'files/' + filename; + const data = new Blob(['hello world']); + await adapter.createFile(filename, data); + const result = await adapter.getFileData(filename); + expect(result instanceof Buffer).toBe(true); + expect(result.toString('utf-8')).toEqual('hello world'); + const fileData = fs.readFileSync(filePath); + expect(fileData.toString('utf-8')).not.toEqual('hello world'); + }, 5000); + })