diff --git a/CHANGELOG.md b/CHANGELOG.md index 108dccc..e541cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [1.20.1] - 2026-06-08 + +### Fixed +- **App Blocker no longer clobbers a pre-existing debugger.** `BlockApp` wrote its IFEO `Debugger` value unconditionally, overwriting any value already present — which could break a legitimately-debugged application and was unrecoverable (Unblock only removes SysManager's own value). It now refuses to block an executable that already has an external `Debugger` set, leaving the existing configuration intact. +- **Privacy changes now require confirmation.** Applying pending privacy toggles to the registry now prompts with `DialogService.Confirm` first, stating how many changes will be written and how to revert. Declining keeps the changes pending and writes nothing. + ## [1.20.0] - 2026-06-08 ### Added diff --git a/SysManager/SysManager.Tests/PrivacyViewModelTests.cs b/SysManager/SysManager.Tests/PrivacyViewModelTests.cs index 0555a3f..d33d1e0 100644 --- a/SysManager/SysManager.Tests/PrivacyViewModelTests.cs +++ b/SysManager/SysManager.Tests/PrivacyViewModelTests.cs @@ -2,6 +2,7 @@ // Author: laurentiu021 · https://github.com/laurentiu021/SystemManager // License: MIT +using NSubstitute; using SysManager.Services; using SysManager.ViewModels; @@ -135,4 +136,30 @@ public void ApplyChanges_WithNoPending_SetsNoChangesMessage() Assert.Contains("no changes", vm.StatusMessage, StringComparison.OrdinalIgnoreCase); } + + [Fact] + public void ApplyChanges_WhenUserDeclinesConfirm_DoesNotApply_AndKeepsPending() + { + var prevDialog = DialogService.Instance; + var dialog = Substitute.For(); + dialog.Confirm(Arg.Any(), Arg.Any()).Returns(false); // user clicks "No" + DialogService.Instance = dialog; + try + { + var vm = CreateVm(); + vm.Toggles[0].IsEnabled = !vm.Toggles[0].IsEnabled; // create a pending change + var pendingBefore = vm.PendingChangeCount; + + vm.ApplyChangesCommand.Execute(null); + + dialog.Received(1).Confirm(Arg.Any(), Arg.Any()); + // Declining must NOT write to the registry: the change is still pending. + Assert.Equal(pendingBefore, vm.PendingChangeCount); + Assert.Contains("cancelled", vm.StatusMessage, StringComparison.OrdinalIgnoreCase); + } + finally + { + DialogService.Instance = prevDialog; + } + } } diff --git a/SysManager/SysManager/Services/AppBlockerService.cs b/SysManager/SysManager/Services/AppBlockerService.cs index c3d1e31..f50fa31 100644 --- a/SysManager/SysManager/Services/AppBlockerService.cs +++ b/SysManager/SysManager/Services/AppBlockerService.cs @@ -45,6 +45,19 @@ public static bool BlockApp(string exeName) if (ifeo is null) return false; using var appKey = ifeo.CreateSubKey(exeName, writable: true); + + // Never clobber a pre-existing Debugger value we did not set: it could + // belong to a legitimately-debugged app, and overwriting it would both + // break that setup and be unrecoverable (Unblock only removes OUR value). + var existingDebugger = appKey.GetValue("Debugger") as string; + if (!string.IsNullOrEmpty(existingDebugger) && + !existingDebugger.Equals(BlockerDebugger, StringComparison.OrdinalIgnoreCase)) + { + Log.Warning("Refusing to block {ExeName}: an external Debugger value is already set ({Debugger})", + exeName, existingDebugger); + return false; + } + appKey.SetValue("Debugger", BlockerDebugger, RegistryValueKind.String); Log.Information("Blocked application: {ExeName}", exeName); diff --git a/SysManager/SysManager/SysManager.csproj b/SysManager/SysManager/SysManager.csproj index 40ee42e..26b297c 100644 --- a/SysManager/SysManager/SysManager.csproj +++ b/SysManager/SysManager/SysManager.csproj @@ -10,9 +10,9 @@ SysManager true NU1603;NU1701 - 1.20.0 - 1.20.0.0 - 1.20.0.0 + 1.20.1 + 1.20.1.0 + 1.20.1.0 SysManager SysManager — Windows system monitoring toolkit by laurentiu021. Network, updates, health, logs, safe deep cleanup. https://github.com/laurentiu021/SystemManager diff --git a/SysManager/SysManager/ViewModels/PrivacyViewModel.cs b/SysManager/SysManager/ViewModels/PrivacyViewModel.cs index 327c858..071eb30 100644 --- a/SysManager/SysManager/ViewModels/PrivacyViewModel.cs +++ b/SysManager/SysManager/ViewModels/PrivacyViewModel.cs @@ -117,6 +117,15 @@ private void ApplyChanges() .Where(t => _baselineStates.TryGetValue(t, out var baseline) && baseline != t.IsEnabled) .ToList(); + if (!DialogService.Instance.Confirm( + $"Apply {changed.Count} privacy change{(changed.Count == 1 ? "" : "s")} to the Windows registry?\n\n" + + "Each toggle can be reverted by switching it back and pressing Apply again.", + "Confirm Privacy Changes")) + { + StatusMessage = "Apply cancelled."; + return; + } + _service.ApplyAll(changed); // Refresh baseline to the just-applied state.