From d5a4104684011bc3932c1f1b1049bcf2930acbfe Mon Sep 17 00:00:00 2001 From: JoshuaVlantis Date: Sat, 9 May 2026 14:07:13 +0200 Subject: [PATCH] fix(linux): no-op autoUpdater on Linux to defend against feed activation (#567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap require('electron').autoUpdater on Linux with a Proxy that no-ops every method and returns chainable stubs for EventEmitter calls. The bundled app's lii() gate sets a feed URL of api.anthropic.com/api/desktop/linux/ when app.isPackaged is true (which we set unconditionally via ELECTRON_FORCE_IS_PACKAGED), and registers update-check listeners. Today this is a happy accident: Electron's Linux autoUpdater is unimplemented and the calls log "AutoUpdater is not supported on Linux" and no-op. If a future Electron implements it, every install would start hitting that feed and would either 404 or — worse — receive content the install wasn't prepared for. .deb/.rpm/AppImage updates flow through the OS package manager (or AppImageUpdate); the Anthropic feed has no Linux artifacts. The Proxy is installed via the existing Module.prototype.require interceptor, so it covers gA = require("electron"); gA.autoUpdater.X() and any equivalent destructure. getFeedURL returns '' so any code that inspects the URL gets a well-typed empty string. Verified: built deb in ubuntu:24.04 container, extracted shipped app.asar, confirmed autoUpdaterNoop block + intercept present in frame-fix-wrapper.js. Runtime test loaded the shipped wrapper with a mock electron whose autoUpdater records every real call; replayed the bundled app's setFeedURL/on/checkForUpdates/quitAndInstall/ chained .on() patterns — zero real calls were recorded, confirming the Proxy intercepts every access path. --- scripts/frame-fix-wrapper.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/scripts/frame-fix-wrapper.js b/scripts/frame-fix-wrapper.js index e53f8ad..35ed752 100644 --- a/scripts/frame-fix-wrapper.js +++ b/scripts/frame-fix-wrapper.js @@ -117,6 +117,19 @@ const LINUX_CSS = ` } `; +// autoUpdater no-op: every property access returns a chainable function +// so `.on(...).once(...).setFeedURL(...).checkForUpdates()` is harmless. +// `getFeedURL` returns '' so any code that inspects the URL gets a +// well-typed empty string rather than undefined. Defined once and reused +// across all require('electron') calls. Linux-only; macOS/Windows still +// see the real autoUpdater. See #567. +const autoUpdaterNoop = new Proxy({}, { + get(_target, prop) { + if (prop === 'getFeedURL') return () => ''; + return function chainNoop() { return autoUpdaterNoop; }; + }, +}); + // Build the patched BrowserWindow class and Menu interceptor once, // on first require('electron'), then reuse via Proxy on every access. let PatchedBrowserWindow = null; @@ -741,6 +754,23 @@ X-GNOME-Autostart-enabled=true } }); } + if (prop === 'autoUpdater' && process.platform === 'linux') { + // Force autoUpdater into a no-op on Linux. Upstream's bundled + // app code sets a feed URL of api.anthropic.com/api/desktop/linux/... + // when app.isPackaged is true (we set ELECTRON_FORCE_IS_PACKAGED=true + // unconditionally). Today this is a happy accident: Electron's Linux + // autoUpdater is unimplemented and logs "AutoUpdater is not supported + // on Linux", so the calls no-op. If a future Electron implements it, + // every install would start hitting that feed and would either 404 + // or — worse — receive content the install wasn't prepared for. + // .deb/.rpm/AppImage updates flow through the OS package manager + // (or AppImageUpdate); the Anthropic feed has no Linux artifacts. + // We replace the entire autoUpdater object with a Proxy that + // no-ops every method and returns chainable stubs for EventEmitter + // calls so listener registration in the bundled code is harmless. + // See #567. + return autoUpdaterNoop; + } return Reflect.get(target, prop, receiver); } });