From 879015f08068e384745fcbf8e3292b3e3163ef83 Mon Sep 17 00:00:00 2001 From: Jascha Date: Thu, 28 May 2026 16:37:00 -0700 Subject: [PATCH] fix(test): repair frontend test suite under webpack 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frontend puppeteer suite (npm test) has been red since the webpack 5 upgrade (#30), which deferred running it. webpack 5 dropped automatic node-core polyfills, so the browser test bundle failed to compile/run. - Polyfill `assert` (imported by every test file) via resolve.fallback, and provide `process` (the assert polyfill references it at runtime) through ProvidePlugin. Both are lazy/test-only and stay out of the production app bundle (verified: app.js unchanged, no process/browser in the prod output). - streaming-tests.js used http_ece (a Node-only lib) as a live reference oracle. Running it in-browser dragged in node:crypto → crypto-browserify → asn1.js → vm, and ultimately failed because the buffer polyfill can't decode base64url. Replace the live call with the http_ece reference ciphertext frozen as a golden vector (ECE/aes128gcm per RFC 8188 is stable). This drops the crypto/stream/vm polyfills entirely. - Remove the dead `reportLink` import + method in fileReceiver.js. It imported a symbol api.js never exported (webpack 4 silently made it undefined; webpack 5 errors). Nothing calls it and there's no server report route — leftover from upstream Firefox Send. Verified: npm test green (48 backend + 23 frontend), prod build green, lint clean, full upload + password-protected download E2E passes. --- app/fileReceiver.js | 6 +---- package-lock.json | 33 ++++++++++++++++++++++++++ package.json | 2 ++ test/frontend/tests/streaming-tests.js | 24 +++++++++---------- webpack.config.js | 10 ++++++-- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/app/fileReceiver.js b/app/fileReceiver.js index 85065429..b4f24843 100644 --- a/app/fileReceiver.js +++ b/app/fileReceiver.js @@ -1,7 +1,7 @@ import Nanobus from 'nanobus'; import Keychain from './keychain'; import { delay, bytes, streamToArrayBuffer } from './utils'; -import { downloadFile, metadata, getApiUrl, reportLink } from './api'; +import { downloadFile, metadata, getApiUrl } from './api'; import { blobStream } from './streams'; import Zip from './zip'; @@ -53,10 +53,6 @@ export default class FileReceiver extends Nanobus { this.state = 'ready'; } - async reportLink(reason) { - await reportLink(this.fileInfo.id, this.keychain, reason); - } - sendMessageToSw(msg) { return new Promise((resolve, reject) => { const channel = new MessageChannel(); diff --git a/package-lock.json b/package-lock.json index 191fa9f8..179a6983 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "@fullhuman/postcss-purgecss": "^4.1.3", "@mattiasbuelens/web-streams-polyfill": "0.2.1", "asmcrypto.js": "^0.22.0", + "assert": "^2.1.0", "babel-loader": "^8.2.4", "babel-plugin-istanbul": "^5.2.0", "base64-js": "^1.5.1", @@ -78,6 +79,7 @@ "postcss-loader": "^8.1.1", "postcss-preset-env": "^7.7.2", "prettier": "^1.19.1", + "process": "^0.11.10", "proxyquire": "^2.1.3", "puppeteer": "^2.0.0", "raw-loader": "^3.1.0", @@ -3943,6 +3945,20 @@ "dev": true, "license": "0BSD" }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, "node_modules/ast-types": { "version": "0.9.6", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", @@ -11028,6 +11044,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", diff --git a/package.json b/package.json index 9216b3ef..0f3134ac 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@fullhuman/postcss-purgecss": "^4.1.3", "@mattiasbuelens/web-streams-polyfill": "0.2.1", "asmcrypto.js": "^0.22.0", + "assert": "^2.1.0", "babel-loader": "^8.2.4", "babel-plugin-istanbul": "^5.2.0", "base64-js": "^1.5.1", @@ -107,6 +108,7 @@ "postcss-loader": "^8.1.1", "postcss-preset-env": "^7.7.2", "prettier": "^1.19.1", + "process": "^0.11.10", "proxyquire": "^2.1.3", "puppeteer": "^2.0.0", "raw-loader": "^3.1.0", diff --git a/test/frontend/tests/streaming-tests.js b/test/frontend/tests/streaming-tests.js index 0e690505..dbd62bfa 100644 --- a/test/frontend/tests/streaming-tests.js +++ b/test/frontend/tests/streaming-tests.js @@ -1,6 +1,3 @@ -const ece = require('http_ece'); -require('buffer'); - import assert from 'assert'; import Archive from '../../../app/archive'; import { b64ToArray } from '../../../app/utils'; @@ -15,16 +12,17 @@ const testSalt = 'I1BsxtFttlv3u_Oo94xnmw'; const keystr = 'yqdlZ-tYemfogSmv7Ws5PQ'; const buffer = Buffer.from(str); -const params = { - version: 'aes128gcm', - rs: rs, - salt: testSalt, - keyid: '', - key: keystr -}; - -const encrypted = ece.encrypt(buffer, params); -const decrypted = ece.decrypt(encrypted, params); + +// Reference ciphertext for the params { version: 'aes128gcm', rs: 36, +// salt: testSalt, key: keystr }, produced once by the http_ece library +// in Node. Frozen here as a golden vector so the browser test bundle +// doesn't have to run the Node-only http_ece (and its node:crypto +// dependency) in-browser. ECE/aes128gcm (RFC 8188) output is stable. +const encrypted = Buffer.from( + 'I1BsxtFttlv3u/Oo94xnmwAAACQA6J8B9PysG0UQpgbdfI4E2pBumJk+XsSviMkGvlSpyrsiKCXwIw8SNTVexOXMwoXXFbcrUth/qV/ZCMLiWQ78S7XTBFAXwaBazYaD2hM93TBw2xLZk3AGMVLF9XgSkzqbdkLj+6K8+tSYgmhvz6Xan0muK+wZZ1jbchmyMYvuyA==', + 'base64' +); +const decrypted = buffer; describe('Streaming', function() { describe('blobStream', function() { diff --git a/webpack.config.js b/webpack.config.js index b682740b..523758ae 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -224,7 +224,10 @@ const web = { new webpack.EnvironmentPlugin(['NODE_ENV']), new webpack.IgnorePlugin({ resourceRegExp: /\.\.\/dist/ }), // used in common/*.js new webpack.ProvidePlugin({ - Buffer: ['buffer', 'Buffer'] + Buffer: ['buffer', 'Buffer'], + // assert polyfill (test bundle) needs process; require.resolve so + // .mjs deps (asmcrypto) get a fully-specified path + process: require.resolve('process/browser') }), new MiniCssExtractPlugin({ filename: '[name].[contenthash:8].css' @@ -235,7 +238,10 @@ const web = { resolve: { fallback: { buffer: require.resolve('buffer/'), - path: require.resolve('path-browserify') + path: require.resolve('path-browserify'), + // only the frontend test bundle (dev mode) imports assert; lazy, + // so it stays out of the production app bundle + assert: require.resolve('assert/') } }, devtool: 'source-map',