Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions SysManager/SysManager.Tests/PrivacyViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Author: laurentiu021 · https://github.com/laurentiu021/SystemManager
// License: MIT

using NSubstitute;
using SysManager.Services;
using SysManager.ViewModels;

Expand Down Expand Up @@ -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<IDialogService>();
dialog.Confirm(Arg.Any<string>(), Arg.Any<string>()).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<string>(), Arg.Any<string>());
// 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;
}
}
}
13 changes: 13 additions & 0 deletions SysManager/SysManager/Services/AppBlockerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions SysManager/SysManager/SysManager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<RootNamespace>SysManager</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>NU1603;NU1701</NoWarn>
<Version>1.20.0</Version>
<FileVersion>1.20.0.0</FileVersion>
<AssemblyVersion>1.20.0.0</AssemblyVersion>
<Version>1.20.1</Version>
<FileVersion>1.20.1.0</FileVersion>
<AssemblyVersion>1.20.1.0</AssemblyVersion>
<Product>SysManager</Product>
<Description>SysManager — Windows system monitoring toolkit by laurentiu021. Network, updates, health, logs, safe deep cleanup.</Description>
<PackageProjectUrl>https://github.com/laurentiu021/SystemManager</PackageProjectUrl>
Expand Down
9 changes: 9 additions & 0 deletions SysManager/SysManager/ViewModels/PrivacyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading