From 04c1415f6db4158a110945845fb497507567e1e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:29:29 +0000 Subject: [PATCH 01/18] Bump lodash from 4.17.21 to 4.17.23 in /src/ElectronNET.Host Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/ElectronNET.Host/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ElectronNET.Host/package-lock.json b/src/ElectronNET.Host/package-lock.json index db517ef..734b80e 100644 --- a/src/ElectronNET.Host/package-lock.json +++ b/src/ElectronNET.Host/package-lock.json @@ -1614,9 +1614,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, "node_modules/lodash.escaperegexp": { From f4da9588c983294e361e9f4d163d3819feb046f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:55:31 +0000 Subject: [PATCH 02/18] Bump lodash in /src/ElectronNET.WebApp/ElectronHostHook Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../ElectronHostHook/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json index 4582c86..6f98c01 100644 --- a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json +++ b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json @@ -1425,9 +1425,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "node_modules/lodash.defaults": { "version": "4.2.0", @@ -3162,9 +3162,9 @@ } }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "lodash.defaults": { "version": "4.2.0", From 8fc197b32a37b2e5ec0a62967135fd9666c25540 Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Wed, 18 Mar 2026 20:12:06 +0100 Subject: [PATCH 03/18] Proposed fix for releaseNotes serialization #1039 --- src/ElectronNET.Host/api/autoUpdater.js | 10 ++++++++++ src/ElectronNET.Host/api/autoUpdater.js.map | 2 +- src/ElectronNET.Host/api/autoUpdater.ts | 11 +++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ElectronNET.Host/api/autoUpdater.js b/src/ElectronNET.Host/api/autoUpdater.js index c33c8bd..457f089 100644 --- a/src/ElectronNET.Host/api/autoUpdater.js +++ b/src/ElectronNET.Host/api/autoUpdater.js @@ -1,6 +1,11 @@ "use strict"; const electron_updater_1 = require("electron-updater"); let electronSocket; +function normalize(updateInfo) { + if (typeof updateInfo?.releaseNotes === "string") { + updateInfo.releaseNotes = [updateInfo.releaseNotes]; + } +} module.exports = (socket) => { electronSocket = socket; socket.on("register-autoUpdater-error", (id) => { @@ -15,11 +20,13 @@ module.exports = (socket) => { }); socket.on("register-autoUpdater-update-available", (id) => { electron_updater_1.autoUpdater.on("update-available", (updateInfo) => { + normalize(updateInfo); electronSocket.emit("autoUpdater-update-available" + id, updateInfo); }); }); socket.on("register-autoUpdater-update-not-available", (id) => { electron_updater_1.autoUpdater.on("update-not-available", (updateInfo) => { + normalize(updateInfo); electronSocket.emit("autoUpdater-update-not-available" + id, updateInfo); }); }); @@ -30,6 +37,7 @@ module.exports = (socket) => { }); socket.on("register-autoUpdater-update-downloaded", (id) => { electron_updater_1.autoUpdater.on("update-downloaded", (updateInfo) => { + normalize(updateInfo); electronSocket.emit("autoUpdater-update-downloaded" + id, updateInfo); }); }); @@ -89,6 +97,7 @@ module.exports = (socket) => { electron_updater_1.autoUpdater .checkForUpdatesAndNotify() .then((updateCheckResult) => { + normalize(updateCheckResult?.updateInfo); electronSocket.emit("autoUpdater-checkForUpdatesAndNotify-completed" + guid, updateCheckResult); }) .catch((error) => { @@ -99,6 +108,7 @@ module.exports = (socket) => { electron_updater_1.autoUpdater .checkForUpdates() .then((updateCheckResult) => { + normalize(updateCheckResult?.updateInfo); electronSocket.emit("autoUpdater-checkForUpdates-completed" + guid, updateCheckResult); }) .catch((error) => { diff --git a/src/ElectronNET.Host/api/autoUpdater.js.map b/src/ElectronNET.Host/api/autoUpdater.js.map index 194f190..3387d04 100644 --- a/src/ElectronNET.Host/api/autoUpdater.js.map +++ b/src/ElectronNET.Host/api/autoUpdater.js.map @@ -1 +1 @@ -{"version":3,"file":"autoUpdater.js","sourceRoot":"","sources":["autoUpdater.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autoUpdater.js","sourceRoot":"","sources":["autoUpdater.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/ElectronNET.Host/api/autoUpdater.ts b/src/ElectronNET.Host/api/autoUpdater.ts index 64c5105..7b9fab6 100644 --- a/src/ElectronNET.Host/api/autoUpdater.ts +++ b/src/ElectronNET.Host/api/autoUpdater.ts @@ -3,6 +3,12 @@ import { autoUpdater } from "electron-updater"; let electronSocket: Socket; +function normalize(updateInfo) { + if (typeof updateInfo?.releaseNotes === "string") { + updateInfo.releaseNotes = [updateInfo.releaseNotes]; + } +} + export = (socket: Socket) => { electronSocket = socket; @@ -20,12 +26,14 @@ export = (socket: Socket) => { socket.on("register-autoUpdater-update-available", (id) => { autoUpdater.on("update-available", (updateInfo) => { + normalize(updateInfo); electronSocket.emit("autoUpdater-update-available" + id, updateInfo); }); }); socket.on("register-autoUpdater-update-not-available", (id) => { autoUpdater.on("update-not-available", (updateInfo) => { + normalize(updateInfo); electronSocket.emit("autoUpdater-update-not-available" + id, updateInfo); }); }); @@ -38,6 +46,7 @@ export = (socket: Socket) => { socket.on("register-autoUpdater-update-downloaded", (id) => { autoUpdater.on("update-downloaded", (updateInfo) => { + normalize(updateInfo); electronSocket.emit("autoUpdater-update-downloaded" + id, updateInfo); }); }); @@ -143,6 +152,7 @@ export = (socket: Socket) => { autoUpdater .checkForUpdatesAndNotify() .then((updateCheckResult) => { + normalize(updateCheckResult?.updateInfo); electronSocket.emit( "autoUpdater-checkForUpdatesAndNotify-completed" + guid, updateCheckResult, @@ -160,6 +170,7 @@ export = (socket: Socket) => { autoUpdater .checkForUpdates() .then((updateCheckResult) => { + normalize(updateCheckResult?.updateInfo); electronSocket.emit( "autoUpdater-checkForUpdates-completed" + guid, updateCheckResult, From c9945a87a97c7d2accaf74e20d5c1619a867b2a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 10:29:48 +0000 Subject: [PATCH 04/18] Bump flatted from 3.3.3 to 3.4.2 in /src/ElectronNET.Host Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/ElectronNET.Host/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ElectronNET.Host/package-lock.json b/src/ElectronNET.Host/package-lock.json index 8d2a04e..1b7d89d 100644 --- a/src/ElectronNET.Host/package-lock.json +++ b/src/ElectronNET.Host/package-lock.json @@ -1201,9 +1201,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, From 4c48588ce2814b5b54dd11f29b84ccac0717b764 Mon Sep 17 00:00:00 2001 From: Aeon Date: Wed, 25 Mar 2026 02:37:20 +0100 Subject: [PATCH 05/18] Fix HasShadow not being true by default --- src/ElectronNET.API/API/Entities/BrowserWindowOptions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ElectronNET.API/API/Entities/BrowserWindowOptions.cs b/src/ElectronNET.API/API/Entities/BrowserWindowOptions.cs index fc44edf..c87d8ab 100644 --- a/src/ElectronNET.API/API/Entities/BrowserWindowOptions.cs +++ b/src/ElectronNET.API/API/Entities/BrowserWindowOptions.cs @@ -215,7 +215,8 @@ namespace ElectronNET.API.Entities /// /// Whether window should have a shadow. Default is true. /// - public bool HasShadow { get; set; } + [DefaultValue(true)] + public bool HasShadow { get; set; } = true; /// /// Forces using dark theme for the window, only works on some GTK+3 desktop environments. Default is false. From 11f71feeb8c10bcf0718587de269dbc2190cccd4 Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Sat, 9 May 2026 17:03:32 +0200 Subject: [PATCH 06/18] Automatic Port Selection and Secured Communication (#1038) Adds dynamic OS-selected socket port handling and secured Electron/.NET communication. The dotnet-first startup flow now generates the auth token on the .NET side and passes it to Electron through an environment variable. Electron reports the selected socket port through a temporary startup-info file, avoiding any dependency on parsing Electron console output. Also avoids logging backend startup parameters because they include the auth token. --- .../Bridge/SocketIOConnection.cs | 13 +- src/ElectronNET.API/Common/ProcessRunner.cs | 17 ++ src/ElectronNET.API/ElectronNetRuntime.cs | 3 + .../RuntimeControllerDotNetFirst.cs | 15 +- .../RuntimeControllerElectronFirst.cs | 11 +- .../ElectronProcess/ElectronProcessActive.cs | 146 ++++++++++++++-- .../SocketBridge/SocketBridgeService.cs | 6 +- src/ElectronNET.API/Runtime/StartupManager.cs | 14 ++ .../API/WebHostBuilderExtensions.cs | 15 +- .../ElectronAuthenticationMiddleware.cs | 100 +++++++++++ .../RuntimeControllerAspNetBase.cs | 24 ++- .../RuntimeControllerAspNetDotnetFirst.cs | 20 +-- .../RuntimeControllerAspNetElectronFirst.cs | 15 +- .../Services/ElectronAuthenticationService.cs | 53 ++++++ .../IElectronAuthenticationService.cs | 24 +++ src/ElectronNET.Host/api/browserWindows.js | 10 +- .../api/browserWindows.js.map | 2 +- src/ElectronNET.Host/api/browserWindows.ts | 11 +- src/ElectronNET.Host/main.js | 160 +++++++++++------- src/ElectronNET.Host/package.json | 1 - src/ElectronNET/build/package.template.json | 1 - 21 files changed, 533 insertions(+), 128 deletions(-) create mode 100644 src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs create mode 100644 src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs create mode 100644 src/ElectronNET.AspNet/Services/IElectronAuthenticationService.cs diff --git a/src/ElectronNET.API/Bridge/SocketIOConnection.cs b/src/ElectronNET.API/Bridge/SocketIOConnection.cs index 7bf8567..36ea2e5 100644 --- a/src/ElectronNET.API/Bridge/SocketIOConnection.cs +++ b/src/ElectronNET.API/Bridge/SocketIOConnection.cs @@ -3,10 +3,12 @@ namespace ElectronNET.API; using System; +using System.Collections.Generic; using System.Threading.Tasks; using ElectronNET.API.Serialization; using SocketIO.Serializer.SystemTextJson; using SocketIO = SocketIOClient.SocketIO; +using SocketIOOptions = SocketIOClient.SocketIOOptions; internal class SocketIOConnection : ISocketConnection { @@ -14,9 +16,16 @@ internal class SocketIOConnection : ISocketConnection private readonly object _lockObj = new object(); private bool _isDisposed; - public SocketIOConnection(string uri) + public SocketIOConnection(string uri, string authorization) { - _socket = new SocketIO(uri); + var opts = string.IsNullOrEmpty(authorization) ? new SocketIOOptions() : new SocketIOOptions + { + ExtraHeaders = new Dictionary + { + ["authorization"] = authorization + }, + }; + _socket = new SocketIO(uri, opts); _socket.Serializer = new SystemTextJsonSerializer(ElectronJson.Options); // Use default System.Text.Json serializer from SocketIOClient. // Outgoing args are normalized to camelCase via SerializeArg in Emit. diff --git a/src/ElectronNET.API/Common/ProcessRunner.cs b/src/ElectronNET.API/Common/ProcessRunner.cs index 5ac7612..a8b9806 100644 --- a/src/ElectronNET.API/Common/ProcessRunner.cs +++ b/src/ElectronNET.API/Common/ProcessRunner.cs @@ -1,6 +1,7 @@ namespace ElectronNET.Common { using System; + using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -26,6 +27,8 @@ private readonly StringBuilder stdOut = new StringBuilder(4 * 1024); private readonly StringBuilder stdErr = new StringBuilder(4 * 1024); + public event EventHandler LineReceived; + private volatile ManualResetEvent stdOutEvent; private volatile ManualResetEvent stdErrEvent; private volatile Stopwatch stopwatch; @@ -109,6 +112,11 @@ public int? LastExitCode { get; private set; } public bool Run(string exeFileName, string commandLineArgs, string workingDirectory) + { + return this.Run(exeFileName, commandLineArgs, workingDirectory, null); + } + + public bool Run(string exeFileName, string commandLineArgs, string workingDirectory, IDictionary environmentVariables) { this.CommandLine = commandLineArgs; this.WorkingFolder = workingDirectory; @@ -126,6 +134,14 @@ WorkingDirectory = workingDirectory }; + if (environmentVariables != null) + { + foreach (var kv in environmentVariables) + { + startInfo.EnvironmentVariables[kv.Key] = kv.Value; + } + } + return this.Run(startInfo); } @@ -571,6 +587,7 @@ if (e.Data != null) { Console.WriteLine("|| " + e.Data); + LineReceived?.Invoke(this, e.Data); } else { diff --git a/src/ElectronNET.API/ElectronNetRuntime.cs b/src/ElectronNET.API/ElectronNetRuntime.cs index e38fd93..3a285c4 100644 --- a/src/ElectronNET.API/ElectronNetRuntime.cs +++ b/src/ElectronNET.API/ElectronNetRuntime.cs @@ -16,6 +16,7 @@ internal const int DefaultWebPort = 8001; internal const string ElectronPortArgumentName = "electronPort"; internal const string ElectronPidArgumentName = "electronPID"; + internal const string ElectronAuthTokenArgumentName = "electronAuthToken"; /// Initializes the class. static ElectronNetRuntime() @@ -26,6 +27,8 @@ public static string ElectronExtraArguments { get; set; } + public static string ElectronAuthToken { get; internal set; } + public static int? ElectronSocketPort { get; internal set; } public static int? AspNetWebPort { get; internal set; } diff --git a/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerDotNetFirst.cs b/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerDotNetFirst.cs index 0ceaeb8..9a71fcd 100644 --- a/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerDotNetFirst.cs +++ b/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerDotNetFirst.cs @@ -13,7 +13,6 @@ { private ElectronProcessBase electronProcess; private SocketBridgeService socketBridge; - private int? port; public RuntimeControllerDotNetFirst() { @@ -41,19 +40,13 @@ var isUnPacked = ElectronNetRuntime.StartupMethod.IsUnpackaged(); var electronBinaryName = ElectronNetRuntime.ElectronExecutable; var args = string.Format("{0} {1}", ElectronNetRuntime.ElectronExtraArguments, Environment.CommandLine).Trim(); - this.port = ElectronNetRuntime.ElectronSocketPort; - - if (!this.port.HasValue) - { - this.port = PortHelper.GetFreePort(ElectronNetRuntime.DefaultSocketPort); - ElectronNetRuntime.ElectronSocketPort = this.port; - } + var port = ElectronNetRuntime.ElectronSocketPort ?? 0; Console.Error.WriteLine("[StartCore]: isUnPacked: {0}", isUnPacked); Console.Error.WriteLine("[StartCore]: electronBinaryName: {0}", electronBinaryName); Console.Error.WriteLine("[StartCore]: args: {0}", args); - this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, this.port.Value); + this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, port); this.electronProcess.Ready += this.ElectronProcess_Ready; this.electronProcess.Stopped += this.ElectronProcess_Stopped; @@ -63,8 +56,10 @@ private void ElectronProcess_Ready(object sender, EventArgs e) { + var port = ElectronNetRuntime.ElectronSocketPort.Value; + var token = ElectronNetRuntime.ElectronAuthToken; this.TransitionState(LifetimeState.Started); - this.socketBridge = new SocketBridgeService(this.port!.Value); + this.socketBridge = new SocketBridgeService(port, token); this.socketBridge.Ready += this.SocketBridge_Ready; this.socketBridge.Stopped += this.SocketBridge_Stopped; this.socketBridge.Start(); diff --git a/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerElectronFirst.cs b/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerElectronFirst.cs index 436ba92..a0ba3c6 100644 --- a/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerElectronFirst.cs +++ b/src/ElectronNET.API/Runtime/Controllers/RuntimeControllerElectronFirst.cs @@ -11,7 +11,6 @@ { private ElectronProcessBase electronProcess; private SocketBridgeService socketBridge; - private int? port; public RuntimeControllerElectronFirst() { @@ -36,12 +35,8 @@ protected override Task StartCore() { - this.port = ElectronNetRuntime.ElectronSocketPort; - - if (!this.port.HasValue) - { - throw new Exception("No port has been specified by Electron!"); - } + var port = ElectronNetRuntime.ElectronSocketPort.Value; + var token = ElectronNetRuntime.ElectronAuthToken; if (!ElectronNetRuntime.ElectronProcessId.HasValue) { @@ -49,7 +44,7 @@ } this.TransitionState(LifetimeState.Starting); - this.socketBridge = new SocketBridgeService(this.port!.Value); + this.socketBridge = new SocketBridgeService(port, token); this.socketBridge.Ready += this.SocketBridge_Ready; this.socketBridge.Stopped += this.SocketBridge_Stopped; this.socketBridge.Start(); diff --git a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs index b2d32a9..c74dad8 100644 --- a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs +++ b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs @@ -1,10 +1,15 @@ namespace ElectronNET.Runtime.Services.ElectronProcess { using System; + using System.Collections.Generic; using System.ComponentModel; + using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; + using System.Security.Cryptography; + using System.Text.Json; + using System.Threading; using System.Threading.Tasks; using ElectronNET.Common; using ElectronNET.Runtime.Data; @@ -15,6 +20,9 @@ [Localizable(false)] internal class ElectronProcessActive : ElectronProcessBase { + private const string AuthTokenEnvVar = "ELECTRONNET_AUTH_TOKEN"; + private const string StartupInfoEnvVar = "ELECTRONNET_STARTUP_INFO"; + private readonly bool isUnpackaged; private readonly string electronBinaryName; private readonly string extraArguments; @@ -88,8 +96,23 @@ workingDir = dir.FullName; } + // Generate the auth token on the .NET side (256 bit entropy) and pass it + // to Electron via an environment variable. Electron will report the + // OS-selected port via a temporary handshake file - this avoids any + // dependency on parsing Electron's console output. + var authToken = CreateAuthToken(); + var startupInfoPath = Path.Combine( + Path.GetTempPath(), + $"electronnet-startup-{Environment.ProcessId}-{Guid.NewGuid():N}.json"); + // We don't await this in order to let the state transition to "Starting" - Task.Run(async () => await this.StartInternal(startCmd, args, workingDir).ConfigureAwait(false)); + Task.Run(async () => await this.StartInternal(startCmd, args, workingDir, authToken, startupInfoPath).ConfigureAwait(false)); + } + + private static string CreateAuthToken() + { + var bytes = RandomNumberGenerator.GetBytes(32); + return Convert.ToHexString(bytes).ToLowerInvariant(); } private void CheckRuntimeIdentifier() @@ -101,7 +124,6 @@ } var osPart = buildInfoRid.Split('-').First(); - var mismatch = false; switch (osPart) @@ -155,20 +177,56 @@ return Task.CompletedTask; } - private async Task StartInternal(string startCmd, string args, string directoriy) + private async Task StartInternal(string startCmd, string args, string directoriy, string authToken, string startupInfoPath) { + var tcs = new TaskCompletionSource(); + using var cts = new CancellationTokenSource(2 * 60_000); // cancel after 2 minutes + using var _ = cts.Token.Register(() => + { + // Time is over - let's kill the process and move on + this.process.Cancel(); + // We don't want to raise exceptions here - just pass the barrier + tcs.TrySetResult(); + }); + + void Monitor_SocketIO_Failure(object sender, EventArgs e) + { + // We don't want to raise exceptions here - just pass the barrier + if (tcs.Task.IsCompleted) + { + this.Process_Exited(sender, e); + } + else + { + tcs.TrySetResult(); + } + } + try { - await Task.Delay(10.ms()).ConfigureAwait(false); - Console.Error.WriteLine("[StartInternal]: startCmd: {0}", startCmd); Console.Error.WriteLine("[StartInternal]: args: {0}", args); this.process = new ProcessRunner("ElectronRunner"); - this.process.ProcessExited += this.Process_Exited; - this.process.Run(startCmd, args, directoriy); + this.process.ProcessExited += Monitor_SocketIO_Failure; - await Task.Delay(500.ms()).ConfigureAwait(false); + var env = new Dictionary + { + [AuthTokenEnvVar] = authToken, + [StartupInfoEnvVar] = startupInfoPath, + }; + + this.process.Run(startCmd, args, directoriy, env); + + // Wait for Electron to write the startup-info file (or for the process to die / timeout). + var waitTask = WaitForStartupInfoAsync(startupInfoPath, cts.Token); + var completed = await Task.WhenAny(waitTask, tcs.Task).ConfigureAwait(false); + + int port = 0; + if (completed == waitTask && waitTask.Status == TaskStatus.RanToCompletion) + { + port = waitTask.Result; + } Console.Error.WriteLine("[StartInternal]: after run:"); @@ -178,11 +236,18 @@ Console.Error.WriteLine("[StartInternal]: Process is not running: " + this.process.StandardOutput); Task.Run(() => this.TransitionState(LifetimeState.Stopped)); - - throw new Exception("Failed to launch the Electron process."); } - - this.TransitionState(LifetimeState.Ready); + else if (port > 0) + { + ElectronNetRuntime.ElectronAuthToken = authToken; + ElectronNetRuntime.ElectronSocketPort = port; + this.TransitionState(LifetimeState.Ready); + } + else + { + Console.Error.WriteLine("[StartInternal]: Did not receive Electron startup info before process exit/timeout."); + Task.Run(() => this.TransitionState(LifetimeState.Stopped)); + } } catch (Exception ex) { @@ -191,6 +256,63 @@ Console.Error.WriteLine("[StartInternal]: Exception: " + ex); throw; } + finally + { + try + { + if (File.Exists(startupInfoPath)) + { + File.Delete(startupInfoPath); + } + } + catch + { + // best effort cleanup + } + } + } + + private static async Task WaitForStartupInfoAsync(string startupInfoPath, CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + if (File.Exists(startupInfoPath)) + { + var json = await File.ReadAllTextAsync(startupInfoPath, cancellationToken).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(json)) + { + using var doc = JsonDocument.Parse(json); + if (doc.RootElement.TryGetProperty("port", out var portElement) && + portElement.TryGetInt32(out var port) && + port > 0) + { + return port; + } + } + } + } + catch (JsonException) + { + // File may be partially written / racing with the writer - retry. + } + catch (IOException) + { + // Same - transient races on file access; retry. + } + + try + { + await Task.Delay(50, cancellationToken).ConfigureAwait(false); + } + catch (TaskCanceledException) + { + break; + } + } + + return 0; } private void Process_Exited(object sender, EventArgs e) diff --git a/src/ElectronNET.API/Runtime/Services/SocketBridge/SocketBridgeService.cs b/src/ElectronNET.API/Runtime/Services/SocketBridge/SocketBridgeService.cs index 7200f49..4530a65 100644 --- a/src/ElectronNET.API/Runtime/Services/SocketBridge/SocketBridgeService.cs +++ b/src/ElectronNET.API/Runtime/Services/SocketBridge/SocketBridgeService.cs @@ -8,12 +8,14 @@ internal class SocketBridgeService : LifetimeServiceBase { private readonly int socketPort; + private readonly string authorization; private readonly string socketUrl; private SocketIOConnection socket; - public SocketBridgeService(int socketPort) + public SocketBridgeService(int socketPort, string authorization) { this.socketPort = socketPort; + this.authorization = authorization; this.socketUrl = $"http://localhost:{this.socketPort}"; } @@ -23,7 +25,7 @@ protected override Task StartCore() { - this.socket = new SocketIOConnection(this.socketUrl); + this.socket = new SocketIOConnection(this.socketUrl, this.authorization); this.socket.BridgeConnected += this.Socket_BridgeConnected; this.socket.BridgeDisconnected += this.Socket_BridgeDisconnected; Task.Run(this.Connect); diff --git a/src/ElectronNET.API/Runtime/StartupManager.cs b/src/ElectronNET.API/Runtime/StartupManager.cs index 91fed9a..e7ceff1 100644 --- a/src/ElectronNET.API/Runtime/StartupManager.cs +++ b/src/ElectronNET.API/Runtime/StartupManager.cs @@ -106,6 +106,20 @@ Console.WriteLine("Electron Process ID: " + result); } } + + var authTokenArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronAuthTokenArgumentName, StringComparison.OrdinalIgnoreCase)); + + if (authTokenArg != null) + { + var parts = authTokenArg.Split('=', StringSplitOptions.TrimEntries); + + if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1])) + { + var result = parts[1]; + ElectronNetRuntime.ElectronAuthToken = result; + Console.WriteLine("Use Auth Token: " + result); + } + } } private void SetElectronExecutable() diff --git a/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs b/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs index 26d524f..553ffda 100644 --- a/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs +++ b/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs @@ -1,6 +1,7 @@ namespace ElectronNET.API { using System; + using System.Diagnostics; using System.IO; using System.Threading.Tasks; using ElectronNET.AspNet; @@ -10,6 +11,7 @@ using ElectronNET.Runtime.Helpers; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; /// /// Provides extension methods for to enable Electron.NET @@ -66,23 +68,26 @@ // work as expected, see issue #952 Environment.SetEnvironmentVariable("ELECTRON_RUN_AS_NODE", null); - var webPort = PortHelper.GetFreePort(ElectronNetRuntime.AspNetWebPort ?? ElectronNetRuntime.DefaultWebPort); - ElectronNetRuntime.AspNetWebPort = webPort; + var webPort = ElectronNetRuntime.AspNetWebPort ?? 0; // check for the content folder if its exists in base director otherwise no need to include // It was used before because we are publishing the project which copies everything to bin folder and contentroot wwwroot was folder there. // now we have implemented the live reload if app is run using /watch then we need to use the default project path. + + // For port 0 (dynamic port assignment), Kestrel requires binding to specific IP (127.0.0.1) not localhost + var host = webPort == 0 ? "127.0.0.1" : "localhost"; + if (Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}\\wwwroot")) { builder = builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory) - .UseUrls("http://localhost:" + webPort); + .UseUrls($"http://{host}:{webPort}"); } else { - builder = builder.UseUrls("http://localhost:" + webPort); + builder = builder.UseUrls($"http://{host}:{webPort}"); } - builder = builder.ConfigureServices(services => + builder = builder.ConfigureServices((context, services) => { services.AddTransient(); services.AddSingleton(); diff --git a/src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs b/src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs new file mode 100644 index 0000000..2303288 --- /dev/null +++ b/src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs @@ -0,0 +1,100 @@ +namespace ElectronNET.AspNet.Middleware +{ + using System; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Logging; + using ElectronNET.AspNet.Services; + + /// + /// Middleware that validates authentication for all Electron requests. + /// Checks for authentication cookie or token query parameter on first request. + /// Sets HttpOnly cookie for subsequent requests. + /// + /// Security Model: + /// - First request includes token as query parameter (?token=guid) + /// - Middleware validates token and sets secure HttpOnly cookie + /// - Subsequent requests use cookie (no token in URL) + /// - HTTP endpoints protected + /// + public class ElectronAuthenticationMiddleware + { + private readonly RequestDelegate _next; + private readonly IElectronAuthenticationService _authService; + private readonly ILogger _logger; + private const string AuthCookieName = "ElectronAuth"; + + public ElectronAuthenticationMiddleware( + RequestDelegate next, + IElectronAuthenticationService authService, + ILogger logger) + { + _next = next; + _authService = authService; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + var path = context.Request.Path.Value; + + // Check if authentication cookie exists + var authCookie = context.Request.Cookies[AuthCookieName]; + + if (!string.IsNullOrEmpty(authCookie)) + { + // Cookie present - validate it + if (_authService.ValidateToken(authCookie)) + { + await _next(context); + return; + } + else + { + // Invalid cookie - reject + _logger.LogWarning("Authentication failed: Invalid cookie for path {Path} from {RemoteIp}", path, context.Connection.RemoteIpAddress); + context.Response.StatusCode = 401; + await context.Response.WriteAsync("Unauthorized: Invalid authentication"); + return; + } + } + + // No cookie - check for token in query string (first-time authentication) + var token = context.Request.Query["token"].ToString(); + + if (!string.IsNullOrEmpty(token)) + { + if (_authService.ValidateToken(token)) + { + // Valid token - set cookie for future requests + _logger.LogInformation("Authentication successful: Setting cookie for path {Path}", path); + + context.Response.Cookies.Append(AuthCookieName, token, new CookieOptions + { + HttpOnly = true, // Prevent JavaScript access (XSS protection) + SameSite = SameSiteMode.Strict, // CSRF protection + Path = "/", // Valid for all routes + Secure = false, // False because localhost is HTTP + IsEssential = true // Required for app to function + }); + + await _next(context); + return; + } + else + { + // Invalid token - reject + _logger.LogWarning("Authentication failed: Invalid token (prefix: {TokenPrefix}...) for path {Path} from {RemoteIp}", token.Length > 8 ? token.Substring(0, 8) : token, path, context.Connection.RemoteIpAddress); + context.Response.StatusCode = 401; + await context.Response.WriteAsync("Unauthorized: Invalid authentication"); + return; + } + } + + // Neither cookie nor valid token present - reject + _logger.LogWarning("Authentication failed: No cookie or token provided for path {Path} from {RemoteIp}", path, context.Connection.RemoteIpAddress); + context.Response.StatusCode = 401; + await context.Response.WriteAsync("Unauthorized: Authentication required"); + } + } +} \ No newline at end of file diff --git a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetBase.cs b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetBase.cs index ab807f3..b7d2f33 100644 --- a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetBase.cs +++ b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetBase.cs @@ -1,8 +1,13 @@ namespace ElectronNET.AspNet.Runtime { using System; + using System.Linq; using System.Threading.Tasks; + using Microsoft.AspNetCore.Hosting.Server; + using Microsoft.AspNetCore.Hosting.Server.Features; + using Microsoft.Extensions.DependencyInjection; using ElectronNET.API; + using ElectronNET.AspNet.Services; using ElectronNET.Common; using ElectronNET.Runtime.Controllers; using ElectronNET.Runtime.Data; @@ -10,12 +15,16 @@ internal abstract class RuntimeControllerAspNetBase : RuntimeControllerBase { + private readonly IServer server; private readonly AspNetLifetimeAdapter aspNetLifetimeAdapter; + private readonly IElectronAuthenticationService authenticationService; private SocketBridgeService socketBridge; - protected RuntimeControllerAspNetBase(AspNetLifetimeAdapter aspNetLifetimeAdapter) + protected RuntimeControllerAspNetBase(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null) { + this.server = server; this.aspNetLifetimeAdapter = aspNetLifetimeAdapter; + this.authenticationService = authenticationService; this.aspNetLifetimeAdapter.Ready += this.AspNetLifetimeAdapter_Ready; this.aspNetLifetimeAdapter.Stopping += this.AspNetLifetimeAdapter_Stopping; this.aspNetLifetimeAdapter.Stopped += this.AspNetLifetimeAdapter_Stopped; @@ -38,9 +47,9 @@ } } - protected void CreateSocketBridge(int port) + protected void CreateSocketBridge(int port, string authorization) { - this.socketBridge = new SocketBridgeService(port); + this.socketBridge = new SocketBridgeService(port, authorization); this.socketBridge.Ready += this.SocketBridge_Ready; this.socketBridge.Stopped += this.SocketBridge_Stopped; this.socketBridge.Start(); @@ -52,6 +61,15 @@ this.ElectronProcess.IsReady() && this.aspNetLifetimeAdapter.IsReady()) { + var token = ElectronNetRuntime.ElectronAuthToken; + var serverAddressesFeature = this.server.Features.Get(); + var address = serverAddressesFeature.Addresses.First(); + var uri = new Uri(address); + + // Only if somebody registered an IElectronAuthenticationService service - otherwise we do not care + this.authenticationService?.SetExpectedToken(token); + ElectronNetRuntime.AspNetWebPort = uri.Port; + this.TransitionState(LifetimeState.Ready); Task.Run(this.RunReadyCallback); } diff --git a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirst.cs b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirst.cs index 4c76291..7a94732 100644 --- a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirst.cs +++ b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirst.cs @@ -2,6 +2,9 @@ { using System; using System.Threading.Tasks; + using System.Security.Principal; + using Microsoft.AspNetCore.Hosting.Server; + using ElectronNET.AspNet.Services; using ElectronNET.Common; using ElectronNET.Runtime.Data; using ElectronNET.Runtime.Helpers; @@ -10,9 +13,8 @@ internal class RuntimeControllerAspNetDotnetFirst : RuntimeControllerAspNetBase { private ElectronProcessBase electronProcess; - private int? port; - public RuntimeControllerAspNetDotnetFirst(AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(aspNetLifetimeAdapter) + public RuntimeControllerAspNetDotnetFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter, authenticationService) { } @@ -23,15 +25,9 @@ var isUnPacked = ElectronNetRuntime.StartupMethod.IsUnpackaged(); var electronBinaryName = ElectronNetRuntime.ElectronExecutable; var args = Environment.CommandLine; - this.port = ElectronNetRuntime.ElectronSocketPort; + var port = ElectronNetRuntime.ElectronSocketPort ?? 0; - if (!this.port.HasValue) - { - this.port = PortHelper.GetFreePort(ElectronNetRuntime.DefaultSocketPort); - ElectronNetRuntime.ElectronSocketPort = this.port; - } - - this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, this.port.Value); + this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, port); this.electronProcess.Ready += this.ElectronProcess_Ready; this.electronProcess.Stopped += this.ElectronProcess_Stopped; @@ -46,8 +42,10 @@ private void ElectronProcess_Ready(object sender, EventArgs e) { + var port = ElectronNetRuntime.ElectronSocketPort.Value; + var token = ElectronNetRuntime.ElectronAuthToken; this.TransitionState(LifetimeState.Started); - this.CreateSocketBridge(this.port!.Value); + this.CreateSocketBridge(port, token); } private void ElectronProcess_Stopped(object sender, EventArgs e) diff --git a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetElectronFirst.cs b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetElectronFirst.cs index c9eb069..757507d 100644 --- a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetElectronFirst.cs +++ b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetElectronFirst.cs @@ -2,15 +2,16 @@ { using System; using System.Threading.Tasks; + using Microsoft.AspNetCore.Hosting.Server; + using ElectronNET.AspNet.Services; using ElectronNET.Runtime.Data; using ElectronNET.Runtime.Services.ElectronProcess; internal class RuntimeControllerAspNetElectronFirst : RuntimeControllerAspNetBase { private ElectronProcessBase electronProcess; - private int? port; - public RuntimeControllerAspNetElectronFirst(AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(aspNetLifetimeAdapter) + public RuntimeControllerAspNetElectronFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter, authenticationService) { } @@ -18,19 +19,15 @@ protected override Task StartCore() { - this.port = ElectronNetRuntime.ElectronSocketPort; - - if (!this.port.HasValue) - { - throw new Exception("No port has been specified by Electron!"); - } + var port = ElectronNetRuntime.ElectronSocketPort.Value; + var token = ElectronNetRuntime.ElectronAuthToken; if (!ElectronNetRuntime.ElectronProcessId.HasValue) { throw new Exception("No electronPID has been specified by Electron!"); } - this.CreateSocketBridge(this.port!.Value); + this.CreateSocketBridge(port, token); this.electronProcess = new ElectronProcessPassive(ElectronNetRuntime.ElectronProcessId.Value); this.electronProcess.Stopped += this.ElectronProcess_Stopped; diff --git a/src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs b/src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs new file mode 100644 index 0000000..9d0fbb0 --- /dev/null +++ b/src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs @@ -0,0 +1,53 @@ +namespace ElectronNET.AspNet.Services +{ + /// + /// Implementation of authentication service for Electron clients. + /// Stores and validates the authentication token to ensure only the spawned Electron process can connect. + /// + public class ElectronAuthenticationService : IElectronAuthenticationService + { + private string _expectedToken; + private readonly object _lock = new object(); + + /// + public void SetExpectedToken(string token) + { + lock (_lock) + { + _expectedToken = token; + } + } + + /// + public bool ValidateToken(string token) + { + if (string.IsNullOrEmpty(token)) + return false; + + lock (_lock) + { + if (string.IsNullOrEmpty(_expectedToken)) + return false; + + // Constant-time comparison to prevent timing attacks + return ConstantTimeEquals(token, _expectedToken); + } + } + + /// + /// Performs constant-time string comparison to prevent timing attacks. + /// + private static bool ConstantTimeEquals(string a, string b) + { + if (a == null || b == null || a.Length != b.Length) + return false; + + var result = 0; + for (int i = 0; i < a.Length; i++) + { + result |= a[i] ^ b[i]; + } + return result == 0; + } + } +} \ No newline at end of file diff --git a/src/ElectronNET.AspNet/Services/IElectronAuthenticationService.cs b/src/ElectronNET.AspNet/Services/IElectronAuthenticationService.cs new file mode 100644 index 0000000..0208242 --- /dev/null +++ b/src/ElectronNET.AspNet/Services/IElectronAuthenticationService.cs @@ -0,0 +1,24 @@ +namespace ElectronNET.AspNet.Services +{ + /// + /// Service for validating authentication tokens from Electron clients. + /// Used to ensure only the Electron process spawned by this .NET instance can connect. + /// + public interface IElectronAuthenticationService + { + /// + /// Sets the expected authentication token for this instance. + /// Should be called when launching Electron with the generated token. + /// + /// The authentication token + void SetExpectedToken(string token); + + /// + /// Validates an incoming token against the expected token. + /// Uses constant-time comparison to prevent timing attacks. + /// + /// The token to validate + /// True if token is valid, false otherwise + bool ValidateToken(string token); + } +} \ No newline at end of file diff --git a/src/ElectronNET.Host/api/browserWindows.js b/src/ElectronNET.Host/api/browserWindows.js index 5bda56f..3c425ff 100644 --- a/src/ElectronNET.Host/api/browserWindows.js +++ b/src/ElectronNET.Host/api/browserWindows.js @@ -277,7 +277,15 @@ module.exports = (socket, app) => { } }); if (loadUrl) { - window.loadURL(loadUrl); + // Append authentication token to initial URL if available + const token = global["authToken"]; + if (token) { + const separator = loadUrl.includes("?") ? "&" : "?"; + window.loadURL(`${loadUrl}${separator}token=${token}`); + } + else { + window.loadURL(loadUrl); + } } if (app.commandLine.hasSwitch("clear-cache") && app.commandLine.getSwitchValue("clear-cache")) { diff --git a/src/ElectronNET.Host/api/browserWindows.js.map b/src/ElectronNET.Host/api/browserWindows.js.map index 3048689..523cd6f 100644 --- a/src/ElectronNET.Host/api/browserWindows.js.map +++ b/src/ElectronNET.Host/api/browserWindows.js.map @@ -1 +1 @@ -{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/ElectronNET.Host/api/browserWindows.ts b/src/ElectronNET.Host/api/browserWindows.ts index a8cd92d..5bccc42 100644 --- a/src/ElectronNET.Host/api/browserWindows.ts +++ b/src/ElectronNET.Host/api/browserWindows.ts @@ -6,6 +6,7 @@ import { browserViewMediateService } from "./browserView"; const windows: Electron.BrowserWindow[] = (global["browserWindows"] = global["browserWindows"] || []) as Electron.BrowserWindow[]; + let readyToShowWindowsIds: number[] = []; let window; @@ -308,7 +309,15 @@ export = (socket: Socket, app: Electron.App) => { }); if (loadUrl) { - window.loadURL(loadUrl); + // Append authentication token to initial URL if available + const token = global["authToken"]; + + if (token) { + const separator = loadUrl.includes("?") ? "&" : "?"; + window.loadURL(`${loadUrl}${separator}token=${token}`); + } else { + window.loadURL(loadUrl); + } } if ( diff --git a/src/ElectronNET.Host/main.js b/src/ElectronNET.Host/main.js index 9ea21de..ebf5fbe 100644 --- a/src/ElectronNET.Host/main.js +++ b/src/ElectronNET.Host/main.js @@ -1,10 +1,14 @@ const { app } = require('electron'); const { BrowserWindow } = require('electron'); -const { protocol } = require('electron'); +const { createServer } = require('http'); +const { randomUUID } = require('crypto'); +const { Server } = require('socket.io'); +const { platform } = require('os'); const path = require('path'); +const fs = require('fs'); const cProcess = require('child_process').spawn; -const portscanner = require('portscanner'); const { imageSize } = require('image-size'); + let io, server, browserWindows, ipc, apiProcess, loadURL; let appApi, menu, dialogApi, notification, tray, webContents; let globalShortcut, shellApi, screen, clipboard, autoUpdater; @@ -16,20 +20,26 @@ let nativeTheme; let dock; let launchFile; let launchUrl; -let processApi; let manifestJsonFileName = 'package.json'; let unpackedelectron = false; let unpackeddotnet = false; let dotnetpacked = false; let electronforcedport; +let electronUrl; +// Auth token: prefer the value provided by the .NET host via environment variable +// (dotnet-first startup). Fall back to a freshly generated token so Electron can +// still be launched stand-alone (e.g. for debugging). +let authToken = process.env.ELECTRONNET_AUTH_TOKEN || randomUUID().split('-').join(''); +// Path to a temporary handshake file. When set by the .NET host, Electron writes +// the OS-selected socket port into this file so .NET does not have to parse the +// console output. +const startupInfoPath = process.env.ELECTRONNET_STARTUP_INFO; if (app.commandLine.hasSwitch('manifest')) { manifestJsonFileName = app.commandLine.getSwitchValue('manifest'); } -console.log('Entry!!!: '); - if (app.commandLine.hasSwitch('unpackedelectron')) { unpackedelectron = true; } @@ -41,7 +51,14 @@ else if (app.commandLine.hasSwitch('dotnetpacked')) { } if (app.commandLine.hasSwitch('electronforcedport')) { - electronforcedport = app.commandLine.getSwitchValue('electronforcedport'); + electronforcedport = +app.commandLine.getSwitchValue('electronforcedport'); +} + +// Store in global for access by browser windows +global.authToken = authToken; + +if (app.commandLine.hasSwitch('electronurl')) { + electronUrl = app.commandLine.getSwitchValue('electronurl'); } // Custom startup hook: look for custom_main.js and invoke its onStartup(host) if present. @@ -73,7 +90,7 @@ let manifestJsonFilePath = path.join(currentPath, manifestJsonFileName); // if running unpackedelectron, lets change the path if (unpackedelectron || unpackeddotnet) { - console.log('unpackedelectron! dir: ' + currentPath); + console.debug('Running in unpackaged mode, dir: ' + currentPath); manifestJsonFilePath = path.join(currentPath, manifestJsonFileName); currentBinPath = path.join(currentPath, '../'); // go to project directory @@ -153,44 +170,38 @@ app.on('ready', () => { } if (electronforcedport) { - console.log('Electron Socket IO (forced) Port: ' + electronforcedport); + console.info('Electron Socket IO (forced) Port: ' + electronforcedport); startSocketApiBridge(electronforcedport); - return; + } else { + console.info('Electron Socket dynamic IO Port'); + startSocketApiBridge(0); } - - // Added default port as configurable for port restricted environments. - let defaultElectronPort = 8000; - if (manifestJsonFile.electronPort) { - defaultElectronPort = manifestJsonFile.electronPort; - } - - // hostname needs to be localhost, otherwise Windows Firewall will be triggered. - portscanner.findAPortNotInUse(defaultElectronPort, 65535, 'localhost', function (error, port) { - console.log('Electron Socket IO Port: ' + port); - startSocketApiBridge(port); - }); }); app.on('quit', async (event, exitCode) => { - try { - server.close(); - server.closeAllConnections(); - } catch (e) { - console.error(e); - } - - try { - apiProcess?.kill(); - } catch (e) { - console.error(e); - } - - try { - if (io && typeof io.close === 'function') { - io.close(); + if (server) { + try { + server.close(); + server.closeAllConnections(); + } catch (e) { + console.error(e); + } + } + + if (apiProcess) { + try { + apiProcess.kill(); + } catch (e) { + console.error(e); + } + } + + if (io && io.close) { + try { + io.close(); + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); } }); @@ -246,9 +257,7 @@ function startSplashScreen() { // it's an image, so we can compute the desired splash screen size imageSize(imageFile, (error, dimensions) => { if (error) { - console.log(`load splashscreen error:`); - console.error(error); - + console.error(`load splashscreen error:`, error); throw new Error(error.message); } @@ -259,9 +268,9 @@ function startSplashScreen() { function startSocketApiBridge(port) { // instead of 'require('socket.io')(port);' we need to use this workaround // otherwise the Windows Firewall will be triggered - console.log('Electron Socket: starting...'); - server = require('http').createServer(); - const { Server } = require('socket.io'); + console.debug('Electron Socket: starting...'); + server = createServer(); + const host = !port ? '127.0.0.1' : 'localhost'; let hostHook; io = new Server({ pingTimeout: 60000, // in ms, default is 5000 @@ -269,14 +278,34 @@ function startSocketApiBridge(port) { }); io.attach(server); - server.listen(port, 'localhost'); + server.listen(port, host); server.on('listening', function () { - console.log('Electron Socket: listening on port %s at %s', server.address().port, server.address().address); + const addr = server.address(); + console.info(`Electron Socket: listening on port ${addr.port} at ${addr.address}`); + + // If the .NET host requested a startup-info handshake, write the selected + // port atomically (tmp + rename) so .NET can pick it up without parsing + // our console output. The auth token is intentionally NOT written to disk + // - the .NET host already knows it (it generated it). + if (startupInfoPath) { + try { + const payload = JSON.stringify({ port: addr.port, pid: process.pid }); + const tmp = `${startupInfoPath}.tmp`; + const writeOptions = platform() === 'win32' + ? { encoding: 'utf8' } + : { encoding: 'utf8', mode: 0o600 }; + fs.writeFileSync(tmp, payload, writeOptions); + fs.renameSync(tmp, startupInfoPath); + } catch (err) { + console.error('Failed to write Electron startup info file:', err); + } + } + // Now that socket connection is established, we can guarantee port will not be open for portscanner if (unpackedelectron) { - startAspCoreBackendUnpackaged(port); + startAspCoreBackendUnpackaged(addr.port); } else if (!unpackeddotnet && !dotnetpacked) { - startAspCoreBackend(port); + startAspCoreBackend(addr.port); } }); @@ -286,9 +315,16 @@ function startSocketApiBridge(port) { // @ts-ignore io.on('connection', (socket) => { - console.log('Electron Socket: connected!'); + console.info('Electron Socket: connected!'); + + if (authToken && socket.request.headers.authorization !== authToken) { + console.warn('Electron Socket authentication failed!'); + socket.disconnect(true); + return; + } + socket.on('disconnect', function (reason) { - console.log('Got disconnect! Reason: ' + reason); + console.debug('Got disconnect! Reason: ' + reason); try { ////console.log('requireCache'); ////console.log(require.cache['electron-host-hook']); @@ -308,7 +344,7 @@ function startSocketApiBridge(port) { global['electronsocket'].setMaxListeners(0); } - console.log('Electron Socket: loading components...'); + console.debug('Electron Socket: loading components...'); if (appApi === undefined) appApi = require('./api/app')(socket, app); if (browserWindows === undefined) browserWindows = require('./api/browserWindows')(socket, app); @@ -369,7 +405,7 @@ function startSocketApiBridge(port) { console.error(error.message); } - console.log('Electron Socket: startup complete.'); + console.info('Electron Socket: startup complete.'); }); } @@ -383,23 +419,24 @@ function startAspCoreBackend(electronPort) { envParam, `/electronPort=${electronPort}`, `/electronPID=${process.pid}`, + `/electronAuthToken=${authToken}`, // forward user supplied args (avoid duplicate environment) ...forwardedArgs.filter(a => !(envParam && a.startsWith('--environment='))) ].filter(p => p); let binaryFile = manifestJsonFile.executable; - const os = require('os'); - if (os.platform() === 'win32') { + if (platform() === 'win32') { binaryFile = binaryFile + '.exe'; } let binFilePath = path.join(currentBinPath, binaryFile); var options = { cwd: currentBinPath }; - console.log('Starting backend with parameters:', parameters.join(' ')); + // Do not log the parameters: they include the auth token. + console.debug('Starting backend.'); apiProcess = cProcess(binFilePath, parameters, options); apiProcess.stdout.on('data', (data) => { - console.log(`stdout: ${data.toString()}`); + console.debug(`stdout: ${data.toString()}`); }); } } @@ -414,22 +451,23 @@ function startAspCoreBackendUnpackaged(electronPort) { envParam, `/electronPort=${electronPort}`, `/electronPID=${process.pid}`, + `/electronAuthToken=${authToken}`, ...forwardedArgs.filter(a => !(envParam && a.startsWith('--environment='))) ].filter(p => p); let binaryFile = manifestJsonFile.executable; - const os = require('os'); - if (os.platform() === 'win32') { + if (platform() === 'win32') { binaryFile = binaryFile + '.exe'; } let binFilePath = path.join(currentBinPath, binaryFile); var options = { cwd: currentBinPath }; - console.log('Starting backend (unpackaged) with parameters:', parameters.join(' ')); + // Do not log the parameters: they include the auth token. + console.debug('Starting backend (unpackaged).'); apiProcess = cProcess(binFilePath, parameters, options); apiProcess.stdout.on('data', (data) => { - console.log(`stdout: ${data.toString()}`); + console.debug(`stdout: ${data.toString()}`); }); } } diff --git a/src/ElectronNET.Host/package.json b/src/ElectronNET.Host/package.json index 95bfc01..519dff5 100644 --- a/src/ElectronNET.Host/package.json +++ b/src/ElectronNET.Host/package.json @@ -19,7 +19,6 @@ "dasherize": "^2.0.0", "electron-host-hook": "file:./ElectronHostHook", "image-size": "^1.2.1", - "portscanner": "^2.2.0", "electron-updater": "^6.6.2", "socket.io": "^4.8.1" }, diff --git a/src/ElectronNET/build/package.template.json b/src/ElectronNET/build/package.template.json index 0f55948..fa5bca1 100644 --- a/src/ElectronNET/build/package.template.json +++ b/src/ElectronNET/build/package.template.json @@ -35,7 +35,6 @@ "dasherize": "^2.0.0", "electron-updater": "^6.6.2", "image-size": "^1.2.1", - "portscanner": "^2.2.0", "socket.io": "^4.8.1", "electron-host-hook": "file:./ElectronHostHook" }, From 833d288891801b9abff7da05233cc065704c7ee4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 17:24:25 +0200 Subject: [PATCH 07/18] Bump lodash from 4.17.23 to 4.18.1 in /src/ElectronNET.Host (#1059) * Bump lodash in /src/ElectronNET.WebApp/ElectronHostHook (#1056) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump lodash from 4.17.23 to 4.18.1 in /src/ElectronNET.Host Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/ElectronNET.Host/package-lock.json | 6 +++--- src/ElectronNET.WebApp/ElectronHostHook/package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ElectronNET.Host/package-lock.json b/src/ElectronNET.Host/package-lock.json index 1b7d89d..ebcb90d 100644 --- a/src/ElectronNET.Host/package-lock.json +++ b/src/ElectronNET.Host/package-lock.json @@ -1601,9 +1601,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.escaperegexp": { diff --git a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json index 5425de0..818fe60 100644 --- a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json +++ b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json @@ -1438,9 +1438,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.defaults": { From 89f721d1b39e9f082223d151a1666704832d5a36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 17:26:55 +0200 Subject: [PATCH 08/18] Bump brace-expansion from 1.1.12 to 1.1.14 in /src/ElectronNET.WebApp/ElectronHostHook (#1060) * Bump lodash in /src/ElectronNET.WebApp/ElectronHostHook (#1056) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump brace-expansion in /src/ElectronNET.WebApp/ElectronHostHook Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.14. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.14) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.14 dependency-type: indirect ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/ElectronNET.WebApp/ElectronHostHook/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json index 818fe60..6328231 100644 --- a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json +++ b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json @@ -500,9 +500,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", From e9b3ba0addd91f6591a728ccb00e8be9f8cb86f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 17:30:27 +0200 Subject: [PATCH 09/18] Bump socket.io-parser in /src/ElectronNET.WebApp/ElectronHostHook (#1044) Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.5 to 4.2.6. - [Release notes](https://github.com/socketio/socket.io/releases) - [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.6) --- updated-dependencies: - dependency-name: socket.io-parser dependency-version: 4.2.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: Florian Rappl Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/ElectronNET.WebApp/ElectronHostHook/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json index 6328231..7444a60 100644 --- a/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json +++ b/src/ElectronNET.WebApp/ElectronHostHook/package-lock.json @@ -1856,9 +1856,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", - "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", From 30b7b1d251959525f76d445abdd953b81078d531 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 17:30:45 +0200 Subject: [PATCH 10/18] Bump socket.io-parser in /src/ElectronNET.Host/ElectronHostHook (#1043) Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.4 to 4.2.6. - [Release notes](https://github.com/socketio/socket.io/releases) - [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.4...socket.io-parser@4.2.6) --- updated-dependencies: - dependency-name: socket.io-parser dependency-version: 4.2.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: Florian Rappl Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../ElectronHostHook/package-lock.json | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ElectronNET.Host/ElectronHostHook/package-lock.json b/src/ElectronNET.Host/ElectronHostHook/package-lock.json index fa31f12..103054d 100644 --- a/src/ElectronNET.Host/ElectronHostHook/package-lock.json +++ b/src/ElectronNET.Host/ElectronHostHook/package-lock.json @@ -203,18 +203,35 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "debug": "~4.4.1" }, "engines": { "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", From d7e1a098809b18ae92416ad273f270edbe874384 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 9 May 2026 18:17:59 +0200 Subject: [PATCH 11/18] fix: correct ReleaseNotes deserialization for AutoUpdater (fixes #1039) (#1061) * fix: correct ReleaseNotes deserialization for AutoUpdater (fixes #1039) - TypeScript normalize() now maps string releaseNotes to { note } objects and also handles arrays of strings (both cases were broken in PR #1041) - Added ReleaseNotesConverter (JsonConverter) as defensive C# layer that handles all shapes: null, string, string[], object[] - Added [JsonConverter] attribute on UpdateInfo.ReleaseNotes - Added unit tests (no Electron required) covering all four input shapes * chore: sync generated autoUpdater.js and .map with updated TypeScript source * refactor: address Copilot PR review comments - Fix namespace: ElectronNET.Converter -> ElectronNET.API.Converter - Replace JsonDocument.ParseValue() with JsonSerializer.Deserialize() for cleaner, allocation-free object array parsing - Fix Write(): empty ReleaseNoteInfo[] now serializes as [] instead of null - Use Array.Empty() in UpdateInfo default initializer - Add tests: Serialize_WithEmptyReleaseNotes and Serialize_WithNullReleaseNotes --- .../API/Entities/UpdateInfo.cs | 9 +- .../Converter/ReleaseNotesConverter.cs | 91 ++++++++++++++ src/ElectronNET.Host/api/autoUpdater.js | 5 +- src/ElectronNET.Host/api/autoUpdater.js.map | 2 +- src/ElectronNET.Host/api/autoUpdater.ts | 6 +- .../Tests/UpdateInfoSerializationTests.cs | 115 ++++++++++++++++++ 6 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 src/ElectronNET.API/Converter/ReleaseNotesConverter.cs create mode 100644 src/ElectronNET.IntegrationTests/Tests/UpdateInfoSerializationTests.cs diff --git a/src/ElectronNET.API/API/Entities/UpdateInfo.cs b/src/ElectronNET.API/API/Entities/UpdateInfo.cs index c3b6a45..d076b4d 100644 --- a/src/ElectronNET.API/API/Entities/UpdateInfo.cs +++ b/src/ElectronNET.API/API/Entities/UpdateInfo.cs @@ -1,4 +1,8 @@ -namespace ElectronNET.API.Entities +using System; +using System.Text.Json.Serialization; +using ElectronNET.API.Converter; + +namespace ElectronNET.API.Entities { /// /// @@ -24,7 +28,8 @@ /// /// Gets or sets the release notes. /// - public ReleaseNoteInfo[] ReleaseNotes { get; set; } = new ReleaseNoteInfo[0]; + [JsonConverter(typeof(ReleaseNotesConverter))] + public ReleaseNoteInfo[] ReleaseNotes { get; set; } = Array.Empty(); /// /// Gets or sets the release date. diff --git a/src/ElectronNET.API/Converter/ReleaseNotesConverter.cs b/src/ElectronNET.API/Converter/ReleaseNotesConverter.cs new file mode 100644 index 0000000..aaaab5d --- /dev/null +++ b/src/ElectronNET.API/Converter/ReleaseNotesConverter.cs @@ -0,0 +1,91 @@ +using ElectronNET.API.Entities; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ElectronNET.API.Converter; + +/// +/// Handles the polymorphic shape of releaseNotes coming from electron-builder. +/// Depending on the updater.fullChangelog setting, electron-builder sends: +/// - null → when there are no notes +/// - "some string" → plain string (FullChangelog = false, default) +/// - ["note A", "note B"] → array of strings (after broken normalize in older TS) +/// - [{ version, note }, ...] → array of objects (FullChangelog = true) +/// All forms are normalised to ReleaseNoteInfo[] so the C# model stays clean. +/// See: https://github.com/ElectronNET/Electron.NET/issues/1039 +/// +public class ReleaseNotesConverter : JsonConverter +{ + // Ensure the converter is called even when the JSON token is null, + // so we can return an empty array instead of null. + public override bool HandleNull => true; + + public override ReleaseNoteInfo[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.Null: + return Array.Empty(); + + case JsonTokenType.String: + // Plain string: "Some release notes" + return new[] { new ReleaseNoteInfo { Note = reader.GetString() } }; + + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType == JsonTokenType.String) + { + // Array of strings: ["Note A", "Note B"] + list.Add(new ReleaseNoteInfo { Note = reader.GetString() }); + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + // Array of objects: [{ "version": "1.0", "note": "..." }] + var entry = JsonSerializer.Deserialize(ref reader, options) ?? new ReleaseNoteInfo(); + list.Add(entry); + } + else + { + reader.Skip(); + } + } + return list.ToArray(); + + default: + throw new JsonException($"Unexpected token {reader.TokenType} when reading releaseNotes."); + } + } + + public override void Write(Utf8JsonWriter writer, ReleaseNoteInfo[] value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + return; + } + + if (value.Length == 0) + { + writer.WriteStartArray(); + writer.WriteEndArray(); + return; + } + + writer.WriteStartArray(); + foreach (var item in value) + { + writer.WriteStartObject(); + if (item.Version is not null) + { + writer.WriteString("version", item.Version); + } + writer.WriteString("note", item.Note); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } +} diff --git a/src/ElectronNET.Host/api/autoUpdater.js b/src/ElectronNET.Host/api/autoUpdater.js index 457f089..84cc568 100644 --- a/src/ElectronNET.Host/api/autoUpdater.js +++ b/src/ElectronNET.Host/api/autoUpdater.js @@ -3,7 +3,10 @@ const electron_updater_1 = require("electron-updater"); let electronSocket; function normalize(updateInfo) { if (typeof updateInfo?.releaseNotes === "string") { - updateInfo.releaseNotes = [updateInfo.releaseNotes]; + updateInfo.releaseNotes = [{ note: updateInfo.releaseNotes }]; + } + else if (Array.isArray(updateInfo?.releaseNotes)) { + updateInfo.releaseNotes = updateInfo.releaseNotes.map((entry) => typeof entry === "string" ? { note: entry } : entry); } } module.exports = (socket) => { diff --git a/src/ElectronNET.Host/api/autoUpdater.js.map b/src/ElectronNET.Host/api/autoUpdater.js.map index 3387d04..2e3dc29 100644 --- a/src/ElectronNET.Host/api/autoUpdater.js.map +++ b/src/ElectronNET.Host/api/autoUpdater.js.map @@ -1 +1 @@ -{"version":3,"file":"autoUpdater.js","sourceRoot":"","sources":["autoUpdater.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autoUpdater.js","sourceRoot":"","sources":["autoUpdater.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/ElectronNET.Host/api/autoUpdater.ts b/src/ElectronNET.Host/api/autoUpdater.ts index 7b9fab6..d4b34f0 100644 --- a/src/ElectronNET.Host/api/autoUpdater.ts +++ b/src/ElectronNET.Host/api/autoUpdater.ts @@ -5,7 +5,11 @@ let electronSocket: Socket; function normalize(updateInfo) { if (typeof updateInfo?.releaseNotes === "string") { - updateInfo.releaseNotes = [updateInfo.releaseNotes]; + updateInfo.releaseNotes = [{ note: updateInfo.releaseNotes }]; + } else if (Array.isArray(updateInfo?.releaseNotes)) { + updateInfo.releaseNotes = updateInfo.releaseNotes.map((entry) => + typeof entry === "string" ? { note: entry } : entry, + ); } } diff --git a/src/ElectronNET.IntegrationTests/Tests/UpdateInfoSerializationTests.cs b/src/ElectronNET.IntegrationTests/Tests/UpdateInfoSerializationTests.cs new file mode 100644 index 0000000..613e366 --- /dev/null +++ b/src/ElectronNET.IntegrationTests/Tests/UpdateInfoSerializationTests.cs @@ -0,0 +1,115 @@ +namespace ElectronNET.IntegrationTests.Tests +{ + using System; + using System.Text.Json; + using System.Text.Json.Serialization; + using ElectronNET.API.Entities; + + /// + /// Unit tests for UpdateInfo JSON deserialization. + /// Tests the fix for issue #1039: releaseNotes arrives as string or string[] from electron-builder + /// when FullChangelog is false (default), but the C# model expects ReleaseNoteInfo[]. + /// No Electron runtime is required for these tests. + /// + public class UpdateInfoSerializationTests + { + // camelCase + ignore null — mirrors ElectronJson.Options used in production + private static readonly JsonSerializerOptions Options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + + // electron-builder sends a plain string when FullChangelog = false (default) + [Fact] + public void Deserialize_WithStringReleaseNotes_ShouldConvertToSingleEntry() + { + var json = """{"version":"1.2.3","releaseNotes":"Some release notes"}"""; + + var result = JsonSerializer.Deserialize(json, Options); + + result.Should().NotBeNull(); + result.ReleaseNotes.Should().HaveCount(1); + result.ReleaseNotes[0].Note.Should().Be("Some release notes"); + } + + // After the (incorrect) TypeScript normalize: string → ["string"] which is an array of strings, + // not an array of ReleaseNoteInfo objects. The C# model must handle this too. + [Fact] + public void Deserialize_WithArrayOfStringReleaseNotes_ShouldConvertToEntries() + { + var json = """{"version":"1.2.3","releaseNotes":["Note A","Note B"]}"""; + + var result = JsonSerializer.Deserialize(json, Options); + + result.Should().NotBeNull(); + result.ReleaseNotes.Should().HaveCount(2); + result.ReleaseNotes[0].Note.Should().Be("Note A"); + result.ReleaseNotes[1].Note.Should().Be("Note B"); + } + + // When FullChangelog = true, electron-builder sends proper objects; this must keep working. + [Fact] + public void Deserialize_WithProperReleaseNoteObjects_ShouldDeserializeNormally() + { + var json = """{"version":"1.2.3","releaseNotes":[{"version":"1.2.3","note":"Proper note"}]}"""; + + var result = JsonSerializer.Deserialize(json, Options); + + result.Should().NotBeNull(); + result.ReleaseNotes.Should().HaveCount(1); + result.ReleaseNotes[0].Version.Should().Be("1.2.3"); + result.ReleaseNotes[0].Note.Should().Be("Proper note"); + } + + // Null releaseNotes should result in an empty array (matching the default value). + [Fact] + public void Deserialize_WithNullReleaseNotes_ShouldReturnEmptyArray() + { + var json = """{"version":"1.2.3","releaseNotes":null}"""; + + var result = JsonSerializer.Deserialize(json, Options); + + result.Should().NotBeNull(); + result.ReleaseNotes.Should().NotBeNull(); + result.ReleaseNotes.Should().BeEmpty(); + } + + // Absent releaseNotes field should keep the default empty array. + [Fact] + public void Deserialize_WithMissingReleaseNotes_ShouldReturnEmptyArray() + { + var json = """{"version":"1.2.3"}"""; + + var result = JsonSerializer.Deserialize(json, Options); + + result.Should().NotBeNull(); + result.ReleaseNotes.Should().NotBeNull(); + result.ReleaseNotes.Should().BeEmpty(); + } + + // Empty array must serialize as [] not null, so round-trips and downstream + // consumers don't receive unexpected null for a non-null array value. + [Fact] + public void Serialize_WithEmptyReleaseNotes_ShouldProduceEmptyArray() + { + var updateInfo = new UpdateInfo { Version = "1.2.3", ReleaseNotes = Array.Empty() }; + + var json = JsonSerializer.Serialize(updateInfo, Options); + + json.Should().Contain("\"releaseNotes\":[]"); + } + + // Null value: with DefaultIgnoreCondition.WhenWritingNull the property is + // omitted entirely at the serializer level (before Write() is called). + [Fact] + public void Serialize_WithNullReleaseNotes_ShouldOmitProperty() + { + var updateInfo = new UpdateInfo { Version = "1.2.3", ReleaseNotes = null }; + + var json = JsonSerializer.Serialize(updateInfo, Options); + + json.Should().NotContain("releaseNotes"); + } + } +} From 50c9076af62979274247c9491561e6cb75ba8e5e Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 9 May 2026 22:08:36 +0200 Subject: [PATCH 12/18] =?UTF-8?q?fix:=20replace=20ReadAllLines=20with=20Re?= =?UTF-8?q?adAllText=20in=20MigrationChecks.targets=E2=80=A6=20(#1062)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: replace ReadAllLines with ReadAllText in MigrationChecks.targets (fixes #1035) System.IO.File::ReadAllLines is not available as an MSBuild property function on all platforms (e.g. macOS GitHub Actions), causing MSB4185. ReadAllText is universally supported by MSBuild and sufficient for the regex check that detects 'electron' references in a root package.json (ELECTRON008). The intermediate ItemGroup for line accumulation is no longer needed. * test: strengthen migration checks test with exit code assertion and remove reserved MSBuildProjectDirectory override * test: locate MigrationChecks.targets via directory walk for path robustness * test: replace em-dashes with ASCII hyphens to avoid bidi/hidden Unicode warning * test: split build test into clean and electron-containing package.json scenarios --- .../Tests/MigrationChecksTargetsTests.cs | 193 ++++++++++++++++++ .../build/ElectronNET.MigrationChecks.targets | 9 +- 2 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 src/ElectronNET.IntegrationTests/Tests/MigrationChecksTargetsTests.cs diff --git a/src/ElectronNET.IntegrationTests/Tests/MigrationChecksTargetsTests.cs b/src/ElectronNET.IntegrationTests/Tests/MigrationChecksTargetsTests.cs new file mode 100644 index 0000000..93b1817 --- /dev/null +++ b/src/ElectronNET.IntegrationTests/Tests/MigrationChecksTargetsTests.cs @@ -0,0 +1,193 @@ +using System.Diagnostics; + +namespace ElectronNET.IntegrationTests.Tests; + +/// +/// Unit tests for ElectronNET.MigrationChecks.targets - no Electron runtime required. +/// Covers GitHub issue #1035: System.IO.File.ReadAllLines is not available as an MSBuild +/// property function on all platforms (e.g. macOS GitHub Actions), causing MSB4185. +/// +public class MigrationChecksTargetsTests +{ + private static readonly string TargetsFilePath = FindTargetsFile(); + + /// + /// Walks up the directory tree from until it finds + /// the migration checks targets file. This is robust against varying output paths + /// (with or without RID subfolder, debug/release, etc.). + /// + private static string FindTargetsFile() + { + const string RelativeFromRepoRoot = + "src/ElectronNET/build/ElectronNET.MigrationChecks.targets"; + const string RelativeFromSrc = + "ElectronNET/build/ElectronNET.MigrationChecks.targets"; + + var dir = new DirectoryInfo(AppContext.BaseDirectory); + while (dir != null) + { + var fromRepoRoot = Path.Combine(dir.FullName, RelativeFromRepoRoot); + if (File.Exists(fromRepoRoot)) + { + return Path.GetFullPath(fromRepoRoot); + } + + var fromSrc = Path.Combine(dir.FullName, RelativeFromSrc); + if (File.Exists(fromSrc)) + { + return Path.GetFullPath(fromSrc); + } + + dir = dir.Parent; + } + + throw new FileNotFoundException( + "Could not locate ElectronNET.MigrationChecks.targets by walking up from " + + $"'{AppContext.BaseDirectory}'."); + } + + // ----------------------------------------------------------------------- + // Content-level test (RED before fix, GREEN after fix on ALL platforms) + // ----------------------------------------------------------------------- + + [Fact] + public void MigrationChecksTargets_ShouldNotUseReadAllLines() + { + // The file must exist - if this fails the path constant above is wrong. + File.Exists(TargetsFilePath).Should().BeTrue( + $"targets file must exist at '{TargetsFilePath}'"); + + var content = File.ReadAllText(TargetsFilePath); + + // System.IO.File::ReadAllLines is not in the MSBuild property-function + // whitelist on all platforms (MSB4185 on macOS GitHub Actions, see #1035). + // ReadAllText must be used instead. + content.Should().NotContain( + "::ReadAllLines(", + "because ReadAllLines is not available as an MSBuild property function on all " + + "platforms. Use ReadAllText instead (GitHub issue #1035)."); + } + + // ----------------------------------------------------------------------- + // Functional build test - verifies no MSB4185 at runtime + // (RED on platforms where ReadAllLines is restricted, GREEN after fix) + // ----------------------------------------------------------------------- + + [Fact] + public async Task MigrationChecksTargets_BuildWithCleanPackageJson_ShouldSucceedWithoutMSB4185() + { + // Positive case: a package.json that does NOT mention electron. + // The migration check must successfully read the file via ReadAllText + // (the code path fixed by issue #1035) without producing MSB4185. + + var tempDir = CreateTempProjectDirectory(); + try + { + await File.WriteAllTextAsync( + Path.Combine(tempDir, "package.json"), + """{ "devDependencies": { "vite": "^5.0.0" } }"""); + + await WriteMinimalCsprojAsync(tempDir); + + var (exitCode, output) = await RunDotnetBuildAsync(tempDir); + + exitCode.Should().Be(0, + $"the build must succeed when the package.json contains no electron references. " + + $"Full build output:\n{output}"); + + output.Should().NotContain( + "MSB4185", + $"ReadAllLines must not be used as an MSBuild property function. " + + $"Full build output:\n{output}"); + } + finally + { + Directory.Delete(tempDir, recursive: true); + } + } + + [Fact] + public async Task MigrationChecksTargets_BuildWithPackageJsonContainingElectron_ShouldEmitELECTRON008WarningWithoutMSB4185() + { + // Negative case: a package.json that DOES contain "electron". + // The migration check must still read the file successfully (no MSB4185) + // and must emit the expected ELECTRON008 warning. ELECTRON008 is a + // , not an , so the build itself still succeeds. + + var tempDir = CreateTempProjectDirectory(); + try + { + await File.WriteAllTextAsync( + Path.Combine(tempDir, "package.json"), + """{ "devDependencies": { "electron": "^30.0.0" } }"""); + + await WriteMinimalCsprojAsync(tempDir); + + var (exitCode, output) = await RunDotnetBuildAsync(tempDir); + + exitCode.Should().Be(0, + $"ELECTRON008 is a Warning (not an Error) so the build itself must still " + + $"succeed. Full build output:\n{output}"); + + output.Should().NotContain( + "MSB4185", + $"ReadAllLines must not be used as an MSBuild property function. " + + $"Full build output:\n{output}"); + + output.Should().Contain( + "ELECTRON008", + $"the migration check must still detect electron references in package.json " + + $"after the ReadAllText migration. Full build output:\n{output}"); + } + finally + { + Directory.Delete(tempDir, recursive: true); + } + } + + // ----------------------------------------------------------------------- + // Helpers + // ----------------------------------------------------------------------- + + private static string CreateTempProjectDirectory() + { + var tempDir = Path.Combine(Path.GetTempPath(), $"electron-net-test-{Guid.NewGuid():N}"); + Directory.CreateDirectory(tempDir); + return tempDir; + } + + private static Task WriteMinimalCsprojAsync(string tempDir) + { + // A minimal csproj that only imports the migration checks targets to keep the + // build fast. Note: MSBuildProjectDirectory is a reserved MSBuild property and + // must not be redefined manually; MSBuild sets it automatically to the folder + // of the csproj (which is tempDir here). + var targetsPathEscaped = TargetsFilePath.Replace("'", "'"); + return File.WriteAllTextAsync( + Path.Combine(tempDir, "TestApp.csproj"), + $""" + + + + + """); + } + + private static async Task<(int ExitCode, string Output)> RunDotnetBuildAsync(string workingDirectory) + { + var psi = new ProcessStartInfo("dotnet", "build --nologo -v:minimal") + { + WorkingDirectory = workingDirectory, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + }; + + using var process = Process.Start(psi)!; + var stdOut = await process.StandardOutput.ReadToEndAsync(); + var stdErr = await process.StandardError.ReadToEndAsync(); + await process.WaitForExitAsync(); + + return (process.ExitCode, stdOut + stdErr); + } +} diff --git a/src/ElectronNET/build/ElectronNET.MigrationChecks.targets b/src/ElectronNET/build/ElectronNET.MigrationChecks.targets index 666c178..ab138a5 100644 --- a/src/ElectronNET/build/ElectronNET.MigrationChecks.targets +++ b/src/ElectronNET/build/ElectronNET.MigrationChecks.targets @@ -73,12 +73,11 @@ EXCEPTION: - - <_RootPackageJsonLines Include="$([System.IO.File]::ReadAllLines('$(MSBuildProjectDirectory)\package.json'))" /> - - - <_RootPackageJsonContent>@(_RootPackageJsonLines, ' ') + + <_RootPackageJsonContent>$([System.IO.File]::ReadAllText('$(MSBuildProjectDirectory)\package.json')) <_RootPackageJsonHasElectron>false <_RootPackageJsonHasElectron Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('$(_RootPackageJsonContent)', 'electron', System.Text.RegularExpressions.RegexOptions.IgnoreCase))">true From fe939e0fda5532cec62023795422d0c748326769 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 9 May 2026 22:26:43 +0200 Subject: [PATCH 13/18] ci: stop hanging PR workflows and remove duplicate runs (#1063) - Build and Publish now only triggers on push to main/develop (was: every push), so feature-branch pushes no longer run a second full matrix in parallel to PR Validation. - integration-tests concurrency group is now scoped per calling workflow (github.workflow + github.ref), preventing PR Validation and Build and Publish from canceling each other's reusable test runs. - Added timeout-minutes (25 per matrix job, 15 per dotnet test step, 10 for summary job) so a hung Electron process no longer keeps a runner busy until the 6h default timeout, which previously required manual cancel. - Added --blame-hang --blame-hang-timeout 5min to dotnet test so hung tests produce a dump and fail fast instead of stalling. - Removed pointless 0-20s random sleep step at the start of every matrix job. --- .github/workflows/Build and Publish.yml | 4 +++- .github/workflows/integration-tests.yml | 19 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/Build and Publish.yml b/.github/workflows/Build and Publish.yml index 17bd8ca..920c26d 100644 --- a/.github/workflows/Build and Publish.yml +++ b/.github/workflows/Build and Publish.yml @@ -1,6 +1,8 @@ name: Build and Publish -on: [push] +on: + push: + branches: [main, develop] env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 89ad3c9..45f0fc4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -4,13 +4,14 @@ on: workflow_call: concurrency: - group: integration-tests-${{ github.ref }} + group: integration-tests-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: tests: name: ${{ matrix.os }} API-${{ matrix.electronVersion }} runs-on: ${{ matrix.os }} + timeout-minutes: 25 strategy: fail-fast: false matrix: @@ -42,13 +43,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Random delay (0-20 seconds) - shell: bash - run: | - DELAY=$((RANDOM % 21)) - echo "Waiting for $DELAY seconds..." - sleep $DELAY - - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -79,27 +73,31 @@ jobs: - name: Run tests (Linux) if: runner.os == 'Linux' continue-on-error: true + timeout-minutes: 15 run: | mkdir -p test-results xvfb-run -a dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj \ -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} \ --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" \ --logger "console;verbosity=detailed" \ + --blame-hang --blame-hang-timeout 5min \ --results-directory test-results - name: Run tests (Windows) if: runner.os == 'Windows' continue-on-error: true + timeout-minutes: 15 run: | New-Item -ItemType Directory -Force -Path test-results | Out-Null - dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results + dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --blame-hang --blame-hang-timeout 5min --results-directory test-results - name: Run tests (macOS) if: runner.os == 'macOS' continue-on-error: true + timeout-minutes: 15 run: | mkdir -p test-results - dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results + dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --blame-hang --blame-hang-timeout 5min --results-directory test-results - name: Upload raw test results if: always() @@ -114,6 +112,7 @@ jobs: runs-on: ubuntu-24.04 if: always() needs: [tests] + timeout-minutes: 10 permissions: actions: read From 979b25ffe799d2badb05c42fde39c02d199c074d Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Sun, 10 May 2026 19:09:51 +0200 Subject: [PATCH 14/18] Exclude file-system based transfer from #1038 (#1064) --- src/ElectronNET.API/Common/ProcessRunner.cs | 14 -- .../ElectronProcess/ElectronProcessActive.cs | 128 ++++-------------- src/ElectronNET.Host/main.js | 35 +---- 3 files changed, 28 insertions(+), 149 deletions(-) diff --git a/src/ElectronNET.API/Common/ProcessRunner.cs b/src/ElectronNET.API/Common/ProcessRunner.cs index a8b9806..d561020 100644 --- a/src/ElectronNET.API/Common/ProcessRunner.cs +++ b/src/ElectronNET.API/Common/ProcessRunner.cs @@ -1,7 +1,6 @@ namespace ElectronNET.Common { using System; - using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -112,11 +111,6 @@ public int? LastExitCode { get; private set; } public bool Run(string exeFileName, string commandLineArgs, string workingDirectory) - { - return this.Run(exeFileName, commandLineArgs, workingDirectory, null); - } - - public bool Run(string exeFileName, string commandLineArgs, string workingDirectory, IDictionary environmentVariables) { this.CommandLine = commandLineArgs; this.WorkingFolder = workingDirectory; @@ -134,14 +128,6 @@ WorkingDirectory = workingDirectory }; - if (environmentVariables != null) - { - foreach (var kv in environmentVariables) - { - startInfo.EnvironmentVariables[kv.Key] = kv.Value; - } - } - return this.Run(startInfo); } diff --git a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs index c74dad8..4d77857 100644 --- a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs +++ b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs @@ -1,14 +1,11 @@ namespace ElectronNET.Runtime.Services.ElectronProcess { using System; - using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; - using System.Security.Cryptography; - using System.Text.Json; using System.Threading; using System.Threading.Tasks; using ElectronNET.Common; @@ -20,8 +17,7 @@ [Localizable(false)] internal class ElectronProcessActive : ElectronProcessBase { - private const string AuthTokenEnvVar = "ELECTRONNET_AUTH_TOKEN"; - private const string StartupInfoEnvVar = "ELECTRONNET_STARTUP_INFO"; + private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at .* using ([a-f0-9]+)$"); private readonly bool isUnpackaged; private readonly string electronBinaryName; @@ -96,23 +92,8 @@ workingDir = dir.FullName; } - // Generate the auth token on the .NET side (256 bit entropy) and pass it - // to Electron via an environment variable. Electron will report the - // OS-selected port via a temporary handshake file - this avoids any - // dependency on parsing Electron's console output. - var authToken = CreateAuthToken(); - var startupInfoPath = Path.Combine( - Path.GetTempPath(), - $"electronnet-startup-{Environment.ProcessId}-{Guid.NewGuid():N}.json"); - // We don't await this in order to let the state transition to "Starting" - Task.Run(async () => await this.StartInternal(startCmd, args, workingDir, authToken, startupInfoPath).ConfigureAwait(false)); - } - - private static string CreateAuthToken() - { - var bytes = RandomNumberGenerator.GetBytes(32); - return Convert.ToHexString(bytes).ToLowerInvariant(); + Task.Run(async () => await this.StartInternal(startCmd, args, workingDir).ConfigureAwait(false)); } private void CheckRuntimeIdentifier() @@ -177,7 +158,7 @@ return Task.CompletedTask; } - private async Task StartInternal(string startCmd, string args, string directoriy, string authToken, string startupInfoPath) + private async Task StartInternal(string startCmd, string args, string directory) { var tcs = new TaskCompletionSource(); using var cts = new CancellationTokenSource(2 * 60_000); // cancel after 2 minutes @@ -189,6 +170,23 @@ tcs.TrySetResult(); }); + void Read_SocketIO_Parameters(object sender, string line) + { + // Look for "Electron Socket: listening on port %s at ..." + var match = extractor.Match(line); + + if (match?.Success ?? false) + { + var port = int.Parse(match.Groups[1].Value); + var token = match.Groups[2].Value; + + this.process.LineReceived -= Read_SocketIO_Parameters; + ElectronNetRuntime.ElectronAuthToken = token; + ElectronNetRuntime.ElectronSocketPort = port; + tcs.SetResult(); + } + } + void Monitor_SocketIO_Failure(object sender, EventArgs e) { // We don't want to raise exceptions here - just pass the barrier @@ -209,24 +207,10 @@ this.process = new ProcessRunner("ElectronRunner"); this.process.ProcessExited += Monitor_SocketIO_Failure; + this.process.LineReceived += Read_SocketIO_Parameters; + this.process.Run(startCmd, args, directoriy); - var env = new Dictionary - { - [AuthTokenEnvVar] = authToken, - [StartupInfoEnvVar] = startupInfoPath, - }; - - this.process.Run(startCmd, args, directoriy, env); - - // Wait for Electron to write the startup-info file (or for the process to die / timeout). - var waitTask = WaitForStartupInfoAsync(startupInfoPath, cts.Token); - var completed = await Task.WhenAny(waitTask, tcs.Task).ConfigureAwait(false); - - int port = 0; - if (completed == waitTask && waitTask.Status == TaskStatus.RanToCompletion) - { - port = waitTask.Result; - } + await tcs.Task.ConfigureAwait(false); Console.Error.WriteLine("[StartInternal]: after run:"); @@ -237,16 +221,9 @@ Task.Run(() => this.TransitionState(LifetimeState.Stopped)); } - else if (port > 0) - { - ElectronNetRuntime.ElectronAuthToken = authToken; - ElectronNetRuntime.ElectronSocketPort = port; - this.TransitionState(LifetimeState.Ready); - } else { - Console.Error.WriteLine("[StartInternal]: Did not receive Electron startup info before process exit/timeout."); - Task.Run(() => this.TransitionState(LifetimeState.Stopped)); + this.TransitionState(LifetimeState.Ready); } } catch (Exception ex) @@ -256,63 +233,6 @@ Console.Error.WriteLine("[StartInternal]: Exception: " + ex); throw; } - finally - { - try - { - if (File.Exists(startupInfoPath)) - { - File.Delete(startupInfoPath); - } - } - catch - { - // best effort cleanup - } - } - } - - private static async Task WaitForStartupInfoAsync(string startupInfoPath, CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - if (File.Exists(startupInfoPath)) - { - var json = await File.ReadAllTextAsync(startupInfoPath, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace(json)) - { - using var doc = JsonDocument.Parse(json); - if (doc.RootElement.TryGetProperty("port", out var portElement) && - portElement.TryGetInt32(out var port) && - port > 0) - { - return port; - } - } - } - } - catch (JsonException) - { - // File may be partially written / racing with the writer - retry. - } - catch (IOException) - { - // Same - transient races on file access; retry. - } - - try - { - await Task.Delay(50, cancellationToken).ConfigureAwait(false); - } - catch (TaskCanceledException) - { - break; - } - } - - return 0; } private void Process_Exited(object sender, EventArgs e) diff --git a/src/ElectronNET.Host/main.js b/src/ElectronNET.Host/main.js index ebf5fbe..09b3b01 100644 --- a/src/ElectronNET.Host/main.js +++ b/src/ElectronNET.Host/main.js @@ -27,14 +27,7 @@ let unpackeddotnet = false; let dotnetpacked = false; let electronforcedport; let electronUrl; -// Auth token: prefer the value provided by the .NET host via environment variable -// (dotnet-first startup). Fall back to a freshly generated token so Electron can -// still be launched stand-alone (e.g. for debugging). -let authToken = process.env.ELECTRONNET_AUTH_TOKEN || randomUUID().split('-').join(''); -// Path to a temporary handshake file. When set by the .NET host, Electron writes -// the OS-selected socket port into this file so .NET does not have to parse the -// console output. -const startupInfoPath = process.env.ELECTRONNET_STARTUP_INFO; +let authToken = randomUUID().split('-').join(''); if (app.commandLine.hasSwitch('manifest')) { manifestJsonFileName = app.commandLine.getSwitchValue('manifest'); @@ -281,25 +274,7 @@ function startSocketApiBridge(port) { server.listen(port, host); server.on('listening', function () { const addr = server.address(); - console.info(`Electron Socket: listening on port ${addr.port} at ${addr.address}`); - - // If the .NET host requested a startup-info handshake, write the selected - // port atomically (tmp + rename) so .NET can pick it up without parsing - // our console output. The auth token is intentionally NOT written to disk - // - the .NET host already knows it (it generated it). - if (startupInfoPath) { - try { - const payload = JSON.stringify({ port: addr.port, pid: process.pid }); - const tmp = `${startupInfoPath}.tmp`; - const writeOptions = platform() === 'win32' - ? { encoding: 'utf8' } - : { encoding: 'utf8', mode: 0o600 }; - fs.writeFileSync(tmp, payload, writeOptions); - fs.renameSync(tmp, startupInfoPath); - } catch (err) { - console.error('Failed to write Electron startup info file:', err); - } - } + console.info(`Electron Socket: listening on port ${addr.port} at ${addr.address} using ${authToken}`); // Now that socket connection is established, we can guarantee port will not be open for portscanner if (unpackedelectron) { @@ -431,8 +406,7 @@ function startAspCoreBackend(electronPort) { let binFilePath = path.join(currentBinPath, binaryFile); var options = { cwd: currentBinPath }; - // Do not log the parameters: they include the auth token. - console.debug('Starting backend.'); + console.debug('Starting backend with parameters:', parameters.join(' ')); apiProcess = cProcess(binFilePath, parameters, options); apiProcess.stdout.on('data', (data) => { @@ -462,8 +436,7 @@ function startAspCoreBackendUnpackaged(electronPort) { let binFilePath = path.join(currentBinPath, binaryFile); var options = { cwd: currentBinPath }; - // Do not log the parameters: they include the auth token. - console.debug('Starting backend (unpackaged).'); + console.debug('Starting backend (unpackaged) with parameters:', parameters.join(' ')); apiProcess = cProcess(binFilePath, parameters, options); apiProcess.stdout.on('data', (data) => { From fee38069537dfe850f3cefb7b60c99c90ddb4546 Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Mon, 25 May 2026 20:37:42 +0200 Subject: [PATCH 15/18] Removed flag --- .github/workflows/retry-test-jobs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/retry-test-jobs.yml b/.github/workflows/retry-test-jobs.yml index fc7600b..4e9dde7 100644 --- a/.github/workflows/retry-test-jobs.yml +++ b/.github/workflows/retry-test-jobs.yml @@ -27,7 +27,7 @@ jobs: run: | echo "Inspecting jobs of workflow run $RUN_ID in $REPO" - jobs_json="$(gh api -R $REPO repos/$REPO/actions/runs/$RUN_ID/jobs)" + jobs_json="$(gh api repos/$REPO/actions/runs/$RUN_ID/jobs)" echo "Jobs and conclusions:" echo "$jobs_json" | jq '.jobs[] | {name: .name, conclusion: .conclusion}' From af5e70c000412b06cdab5e7e879a3f66a656c9ce Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Mon, 25 May 2026 20:40:18 +0200 Subject: [PATCH 16/18] Fixed missing namespace reference --- .../Runtime/Services/ElectronProcess/ElectronProcessActive.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs index 4d77857..b7c65ed 100644 --- a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs +++ b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; + using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using ElectronNET.Common; @@ -208,7 +209,7 @@ this.process = new ProcessRunner("ElectronRunner"); this.process.ProcessExited += Monitor_SocketIO_Failure; this.process.LineReceived += Read_SocketIO_Parameters; - this.process.Run(startCmd, args, directoriy); + this.process.Run(startCmd, args, directory); await tcs.Task.ConfigureAwait(false); From c4251955d94b7eabcae89745ed6443d0b4e57e2a Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Mon, 25 May 2026 20:56:21 +0200 Subject: [PATCH 17/18] Updated changelog --- Changelog.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 15fbf51..d1d740a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,11 @@ ## ElectronNET.Core -- tbd +- Updated internal facade from `SocketIoFacade` to `ISocketConnection` (#1037) +- Fixed `HasShadow` is not `true` by default (#1049) @AeonSake +- Fixed missing `ReadAllLines` in MSBuild execution (#1035) +- Fixed serialization of release notes for the updater (#1041) +- Added automatic port selection and secured IPC communication (#1038) # 0.4.1 From 558e582789dada31f84bdbaaaea21c86a18b9553 Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Mon, 25 May 2026 21:18:00 +0200 Subject: [PATCH 18/18] Updated available lists --- .github/CONTRIBUTING.md | 14 ++- .../build/ElectronNETRules.Project.xaml | 108 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 526a993..8bd7203 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -134,4 +134,16 @@ TL;DR: Unless there is a technical reason (e.g., a crucial new API not being ava We pretty much release whenever we have something new (i.e., do fixes such as a 0.1.1, or add new features, such as a 0.2.0) quite quickly. -We will go for a 1.0.0 release of this as early as ~mid of January 2026 (unless we find some critical things or want to extend the beta phase for ElectronNET.Core). This should be sufficient time to get some user input and have enough experience to call it stable. +We will go for a 1.0.0 release of this as early as ~mid of June 2026 (unless we find some critical things or want to extend the beta phase for ElectronNET.Core). This should be sufficient time to get some user input and have enough experience to call it stable. + +## Updating Electron Versions + +The releases of Electron are found on the [releases.electronjs.org](https://releases.electronjs.org/release?page=1) website. + +You can update the `src\ElectronNET\build\ElectronNETRules.Project.xaml` file with new entries using the following script (run it in the browser's console when being on the Electron Releases website): + +```js +[...new Set([...document.querySelectorAll('tbody tr a[href^="/release/v"]')].map(m => m.getAttribute('href').replace('/release/v', '')))].map(m => ``).sort().join('\n') +``` + +Alternatively, use the website's information to feed into an AI agent of your choice. diff --git a/src/ElectronNET/build/ElectronNETRules.Project.xaml b/src/ElectronNET/build/ElectronNETRules.Project.xaml index 5354bc4..add7c3a 100644 --- a/src/ElectronNET/build/ElectronNETRules.Project.xaml +++ b/src/ElectronNET/build/ElectronNETRules.Project.xaml @@ -338,6 +338,7 @@ + @@ -354,6 +355,14 @@ + + + + + + + + @@ -361,6 +370,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +