Add firewall application along with other small improvements.#3353
Add firewall application along with other small improvements.#3353labre-rdc wants to merge 43 commits intoBornToBeRoot:mainfrom
Conversation
|
@labre-rdc thanks, i will review it when i have some time 😄 |
|
Sorry for the reference error. I’ll handle it tomorrow. |
|
No worries. I don't think I'll be able to review it until next week. |
…ratedRegex. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…o 500. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…Remove duplicates. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…f Int16.MaxValue by creating temporary scripts when necessary. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…ace. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…ences and for typed binding proxies. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…ties passed as parameter for null, empty strings or empty enumerables. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
…anges. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
… simplify merge conflicts. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
… simplify merge conflicts. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
… simplify merge conflicts. Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
Signed-off-by: Manuel Ullmann <manuel.ullmann@rediecon.com>
ee5f768 to
8dd4613
Compare
|
OK - after some rebasing fun, i will start to review 😄 |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Firewall application/module to manage Windows Defender Firewall rules (including profile integration), alongside a set of UI/validation improvements and some refactoring in helpers/converters/validators across the codebase.
Changes:
- Adds Firewall application UI (rule grid + settings) and profile support for storing/applying firewall rules.
- Introduces new validators/converters (incl. localized enum conversion) and improves validation UX (error templates, dismissable popups).
- Refactors utilities (generic
ListHelper.Modify) and improves PowerShell command execution for long commands.
Reviewed changes
Copilot reviewed 83 out of 88 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| Website/docs/changelog/next-release.md | Updates upcoming release notes (currently includes duplicate entries). |
| Website/docs/application/firewall.md | Adds documentation for the new Firewall module. |
| Source/NETworkManager/Views/ProfilesView.xaml | Adds Firewall-enabled column and adjusts empty-collection triggers. |
| Source/NETworkManager/Views/ProfileChildWindow.xaml.cs | Adds firewall focus handling + tab-selection focus logic. |
| Source/NETworkManager/Views/ProfileChildWindow.xaml | Adds Firewall tab and validation wiring for profile editor. |
| Source/NETworkManager/Views/NetworkInterfaceView.xaml | Cleans up XAML warnings and resource usage. |
| Source/NETworkManager/Views/FirewallView.xaml.cs | Adds code-behind wiring for Firewall view focus and events. |
| Source/NETworkManager/Views/FirewallView.xaml | Adds main Firewall application view layout. |
| Source/NETworkManager/Views/FirewallSettingsView.xaml.cs | Adds code-behind for Firewall settings view. |
| Source/NETworkManager/Views/FirewallSettingsView.xaml | Adds Firewall settings UI (history, max length, syntax). |
| Source/NETworkManager/ViewModels/SettingsViewModel.cs | Hooks Firewall settings view into settings navigation. |
| Source/NETworkManager/ViewModels/ProfileViewModel.cs | Adds firewall profile integration + rules extraction. |
| Source/NETworkManager/ViewModels/FirewallSettingsViewModel.cs | Implements Firewall settings state and commands. |
| Source/NETworkManager/ViewModels/FirewallRuleViewModel.cs | Adds firewall rule view-model with validation/history behavior. |
| Source/NETworkManager/Resources/Templates/ValidationErrorTemplates.xaml | Updates validation popup behavior and adds outer-offset template. |
| Source/NETworkManager/Resources/Styles/ChildWindowStyles.xaml | Fixes child window style default sizing behavior. |
| Source/NETworkManager/Resources/Styles/CheckBoxStyles.xaml | Applies validation error template to checkboxes. |
| Source/NETworkManager/ProfileDialogManager.cs | Persists firewall profile settings and rules. |
| Source/NETworkManager/NETworkManager.csproj | Adds new XAML/control compile items and references Interfaces project. |
| Source/NETworkManager/MainWindow.xaml.cs | Adds Firewall view routing (show/hide) and profile load callback. |
| Source/NETworkManager/Controls/FirewallRuleGrid.xaml.cs | Adds rule-grid behaviors: hotkeys, focus restore, history updates. |
| Source/NETworkManager/Controls/FirewallRuleGrid.xaml | Adds rule grid UI with details panel and validation bindings. |
| Source/NETworkManager/Controls/FirewallRuleEnumTranslations.cs | Adds enum translation helpers for Firewall rule comboboxes. |
| Source/NETworkManager.sln | Adds NETworkManager.Interfaces project to solution. |
| Source/NETworkManager.Validators/ProgramNameLengthValidator.cs | Adds program path length validator. |
| Source/NETworkManager.Validators/NETworkManager.Validators.csproj | Adds reference to Interfaces project. |
| Source/NETworkManager.Validators/FirewallRuleNoPipeValidator.cs | Adds validator to block ` |
| Source/NETworkManager.Validators/FirewallRuleNameValidator.cs | Adds length validator for user-defined firewall rule names. |
| Source/NETworkManager.Validators/FileNameValidator.cs | Refactors regex validation using GeneratedRegex. |
| Source/NETworkManager.Validators/EmptyOrInt32Validator.cs | Adds “empty or int32” validator wrapper. |
| Source/NETworkManager.Validators/EmptyOrFirewallPortRangeValidator.cs | Adds firewall port range validator (currently has logic issues). |
| Source/NETworkManager.Validators/EmptyOrFilePathValidator.cs | Adds “empty or file path” validator wrapper. |
| Source/NETworkManager.Validators/EmptyOrFileIsExeValidator.cs | Adds “empty or exe path” validator wrapper. |
| Source/NETworkManager.Validators/EmptyOrFileExistsValidator.cs | Refactors file-exists validation to reuse existing validator. |
| Source/NETworkManager.Validators/AnyNetworkProfileValidator.cs | Adds validator ensuring at least one network profile is selected. |
| Source/NETworkManager.Utilities/PowerShellHelper.cs | Supports long PowerShell commands by writing a temp script. |
| Source/NETworkManager.Utilities/ListHelper.cs | Refactors history list modifier into a generic method. |
| Source/NETworkManager.Utilities.WPF/ValidationHelper.cs | Adds attached-property helper to propagate validation state to VM. |
| Source/NETworkManager.Utilities.WPF/TypedBindingProxies/ProfileViewModelProxy.cs | Adds typed binding proxy (currently returns wrong Freezable type). |
| Source/NETworkManager.Utilities.WPF/TypedBindingProxies/FrameworkElementProxy.cs | Adds typed binding proxy (currently returns wrong Freezable type). |
| Source/NETworkManager.Utilities.WPF/TypedBindingProxies/FirewallViewModelProxy.cs | Adds typed binding proxy (currently returns wrong Freezable type). |
| Source/NETworkManager.Utilities.WPF/NETworkManager.Utilities.WPF.csproj | Adds reference to Interfaces project. |
| Source/NETworkManager.Utilities.WPF/BindingProxy.cs | Improves docs and uses nameof(Data) for DP registration. |
| Source/NETworkManager.Settings/SettingsViewManager.cs | Adds Firewall settings entry to settings list. |
| Source/NETworkManager.Settings/SettingsName.cs | Adds Firewall settings enum entry. |
| Source/NETworkManager.Settings/SettingsManager.cs | Inserts Firewall app into application list during upgrade (needs dedupe guard). |
| Source/NETworkManager.Settings/SettingsInfo.cs | Adds Firewall settings persistence fields. |
| Source/NETworkManager.Profiles/ProfileViewManager.cs | Adds Firewall to profile views list. |
| Source/NETworkManager.Profiles/ProfileName.cs | Adds Firewall profile name enum entry. |
| Source/NETworkManager.Profiles/ProfileInfo.cs | Adds firewall-enabled flag and rules storage to profiles. |
| Source/NETworkManager.Models/Network/NetworkProfiles.cs | Adds Windows network profile enum. |
| Source/NETworkManager.Models/Network/NetworkInterfaceInfo.cs | Adds Profiles (network profile/category) to interface info. |
| Source/NETworkManager.Models/Firewall/FirewallRuleProgram.cs | Adds program model for firewall rules (currently has persistence issue). |
| Source/NETworkManager.Models/Firewall/FirewallRuleDirection.cs | Adds firewall direction enum. |
| Source/NETworkManager.Models/Firewall/FirewallRuleAction.cs | Adds firewall action enum. |
| Source/NETworkManager.Models/Firewall/FirewallRule.cs | Adds core firewall rule model + ports string formatter. |
| Source/NETworkManager.Models/Firewall/FirewallProtocol.cs | Adds firewall protocol enum (incl. numeric protocol IDs). |
| Source/NETworkManager.Models/Firewall/FirewallPortSpecification.cs | Adds port/range model. |
| Source/NETworkManager.Models/Firewall/FirewallPortLocation.cs | Adds local/remote port location enum. |
| Source/NETworkManager.Models/Firewall/FirewallInterfaceType.cs | Adds interface-type enum for firewall rules. |
| Source/NETworkManager.Models/Firewall/Firewall.cs | Adds PowerShell-based apply/clear logic for NwM_* rules. |
| Source/NETworkManager.Models/ApplicationName.cs | Adds Firewall application enum value. |
| Source/NETworkManager.Models/ApplicationManager.cs | Adds Firewall icon mapping. |
| Source/NETworkManager.Localization/Resources/Strings.resx | Adds localization strings for Firewall UI/validation. |
| Source/NETworkManager.Interfaces/ViewModels/IProfileViewModel.cs | Adds interface used by validators/converters for profile VMs. |
| Source/NETworkManager.Interfaces/ViewModels/IFirewallViewModel.cs | Adds firewall VM interface (currently uses invalid static instance pattern). |
| Source/NETworkManager.Interfaces/ViewModels/IFirewallRuleViewModel.cs | Adds rule VM interface for validators/converters. |
| Source/NETworkManager.Interfaces/NETworkManager.Interfaces.csproj | Adds new Interfaces project. |
| Source/NETworkManager.Documentation/DocumentationManager.cs | Adds firewall doc page mapping (also adds an unused using). |
| Source/NETworkManager.Documentation/DocumentationIdentifier.cs | Adds firewall documentation identifier. |
| Source/NETworkManager.Converters/SizeFactorConverter.cs | Adds converter to scale sizes by factor. |
| Source/NETworkManager.Converters/PortRangeToPortSpecificationConverter.cs | Adds converter for port range text ⇄ structured ports (needs trimming fix). |
| Source/NETworkManager.Converters/NETworkManager.Converters.csproj | Adds reference to Interfaces project. |
| Source/NETworkManager.Converters/IntZeroToFalseConverter.cs | Adds converter for enabling/disabling buttons based on counts. |
| Source/NETworkManager.Converters/FirewallRuleProgramConverter.cs | Adds converter between program model and string path. |
| Source/NETworkManager.Converters/EnumToStringConverter.cs | Adds localized enum ⇄ string converter. |
| Source/NETworkManager.Converters/EnumToIntConverter.cs | Adds enum ⇄ index converter for comboboxes. |
| Source/NETworkManager.Converters/EmptyToIntMaxValueConverter.cs | Adds converter mapping empty to int.MaxValue and back. |
| Source/NETworkManager.Converters/CollectionPropertyVisibilityConverter.cs | Adds collection-property visibility converter for rule grid columns. |
| Source/NETworkManager.Converters/CollectionPropertyBooleanOrConverter.cs | Adds collection-property boolean “any-true” converter. |
| Source/NETworkManager.Converters/BooleansOrConverter.cs | Makes OR multi-converter null-safe. |
| Source/NETworkManager.Converters/BoolArrayToFwRuleCategoriesConverter.cs | Adds converter for translating selected network profiles to display text. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| protected override Freezable CreateInstanceCore() | ||
| { | ||
| return new BindingProxy(); | ||
| } |
There was a problem hiding this comment.
CreateInstanceCore() should return a new FirewallViewModelProxy. Returning new BindingProxy() breaks Freezable cloning/freezing behavior and can cause runtime issues in XAML resource usage. Return new FirewallViewModelProxy() instead.
| TextBoxName.Focus(); | ||
| (DataContext as ProfileViewModel) | ||
| ?.Firewall_ViewModel?.CommandExecuted += Firewall_ViewModelOnCommandExecuted; | ||
| })); |
There was a problem hiding this comment.
CommandExecuted is subscribed in ChildWindow_OnLoaded, but there’s no corresponding unsubscribe (e.g., on Unloaded/Closed). If the child window is opened multiple times, this can leak handlers and cause duplicate focus restores. Unsubscribe when the window is unloaded/closed (and consider guarding against double-subscribe).
| _viewModel = FirewallViewModel.Instance as FirewallViewModel; | ||
| DataContext = _viewModel; | ||
| _viewModel?.CommandExecuted += AnyButton_OnClick; | ||
| FirewallRuleGrid.DataContext = _viewModel; | ||
| } |
There was a problem hiding this comment.
_viewModel.CommandExecuted is subscribed in the constructor, but there’s no unsubscribe on Unloaded/dispose. If the view can be recreated or kept alive across navigation, this may retain the view via the event handler. Unsubscribe in an Unloaded handler (or use a weak event pattern).
|
|
||
| public static IFirewallViewModel? Instance { get; set; } | ||
|
|
||
| public static void SetInstance(IFirewallViewModel? viewModel) | ||
| { | ||
| Instance = viewModel; | ||
| } |
There was a problem hiding this comment.
IFirewallViewModel declares a static auto-property Instance without an implementation. Interfaces can’t hold state like this (unless using static abstract with an implementing type), so this pattern is very likely to fail compilation and/or not behave as intended. Consider moving the singleton storage to a dedicated static provider/locator class (or keep it only on FirewallViewModel) and remove Instance/SetInstance from the interface.
| public static IFirewallViewModel? Instance { get; set; } | |
| public static void SetInstance(IFirewallViewModel? viewModel) | |
| { | |
| Instance = viewModel; | |
| } |
| if (port.Contains(rangeDelimiter)) | ||
| { | ||
| var portRange = port.Split(rangeDelimiter); | ||
| if (!int.TryParse(portRange[0], out var startPort)) | ||
| return null; | ||
| if (!int.TryParse(portRange[1], out var endPort)) | ||
| return null; | ||
| resultList.Add(new FirewallPortSpecification(startPort, endPort)); | ||
| } | ||
| else | ||
| { | ||
| if (!int.TryParse(port, out var portNumber)) |
There was a problem hiding this comment.
PortRangeToPortSpecificationConverter.Convert() splits the input string but doesn’t trim tokens. Inputs like "22; 80" will fail int.TryParse(" 80"), even though the validator explicitly allows spaces. Trim/normalize each token (and each side of a range) before parsing so conversion matches validation behavior.
| if (port.Contains(rangeDelimiter)) | |
| { | |
| var portRange = port.Split(rangeDelimiter); | |
| if (!int.TryParse(portRange[0], out var startPort)) | |
| return null; | |
| if (!int.TryParse(portRange[1], out var endPort)) | |
| return null; | |
| resultList.Add(new FirewallPortSpecification(startPort, endPort)); | |
| } | |
| else | |
| { | |
| if (!int.TryParse(port, out var portNumber)) | |
| var trimmedPort = port.Trim(); | |
| if (trimmedPort.Contains(rangeDelimiter)) | |
| { | |
| var portRange = trimmedPort.Split(rangeDelimiter); | |
| if (!int.TryParse(portRange[0].Trim(), out var startPort)) | |
| return null; | |
| if (!int.TryParse(portRange[1].Trim(), out var endPort)) | |
| return null; | |
| resultList.Add(new FirewallPortSpecification(startPort, endPort)); | |
| } | |
| else | |
| { | |
| if (!int.TryParse(trimmedPort, out var portNumber)) |
| _viewModel = FirewallViewModel.Instance as FirewallViewModel; | ||
| DataContext = _viewModel; | ||
| _viewModel?.CommandExecuted += AnyButton_OnClick; | ||
| FirewallRuleGrid.DataContext = _viewModel; | ||
| } | ||
|
|
||
| #region Events | ||
| /// <summary> | ||
| /// Set data context for menus. | ||
| /// </summary> | ||
| /// <param name="sender"></param> | ||
| /// <param name="e"></param> | ||
| private void ContextMenu_Opened(object sender, RoutedEventArgs e) | ||
| { | ||
| if (sender is ContextMenu menu) | ||
| menu.DataContext = _viewModel; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Offload event for toggling view to the view model. | ||
| /// </summary> | ||
| public void OnViewHide() | ||
| { | ||
| _viewModel.OnViewHide(); | ||
| if (_viewModel.IsViewActive) | ||
| FirewallRuleGrid?.RestoreRuleGridFocus(); |
There was a problem hiding this comment.
The view model is obtained via FirewallViewModel.Instance as FirewallViewModel, but later methods (OnViewHide/OnViewVisible) call _viewModel.OnViewHide() without a null check. If the cast ever fails, this will crash. Prefer a non-nullable initialization (as ... ?? throw) or type Instance to FirewallViewModel here.
| public int MaxLengthHistory | ||
| { | ||
| get; | ||
| set | ||
| { | ||
| if (value == field) | ||
| return; | ||
| field = value; | ||
| SettingsManager.Current.Firewall_MaxLengthHistory = value; | ||
| OnPropertyChanged(); | ||
| } | ||
| } | ||
| #endregion | ||
|
|
||
| #region Constructor, load settings | ||
| /// <summary> | ||
| /// Construct the view model and load settings. | ||
| /// </summary> | ||
| private FirewallSettingsViewModel() | ||
| { | ||
| _isLoading = true; | ||
|
|
||
| LoadSettings(); | ||
| _isLoading = false; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Load the settings via <see cref="SettingsManager"/>. | ||
| /// </summary> | ||
| private void LoadSettings() | ||
| { | ||
| CombinePortHistory = SettingsManager.Current.Firewall_CombinePortHistory; | ||
| UseWindowsPortSyntax = SettingsManager.Current.Firewall_UseWindowsPortSyntax; | ||
| LocalPortsHaveItems = SettingsManager.Current.Firewall_LocalPortsHistoryConfig?.Count > 0; | ||
| RemotePortsHaveItems = SettingsManager.Current.Firewall_RemotePortsHistoryConfig?.Count > 0; | ||
| MaxLengthHistory = SettingsManager.Current.Firewall_MaxLengthHistory; | ||
| if (MaxLengthHistory is 0) | ||
| MaxLengthHistory = -1; | ||
| } |
There was a problem hiding this comment.
MaxLengthHistory writes to SettingsManager.Current.Firewall_MaxLengthHistory even while _isLoading is true, and LoadSettings() mutates the setting (0 → -1) on startup. This can unexpectedly change persisted settings without user input and the sentinel value (-1 vs int.MaxValue/0) is inconsistent with the converter used in XAML. Consider: (1) guarding the setter with _isLoading, and (2) standardizing on a single “unlimited” representation (e.g., 0 or int.MaxValue).
|
Thanks for the review. I'm currently in vacation until excluding Friday. Regarding localization: I assumed, that I can (at least) add keys as required, but apparently they are supposed to be added within Transifex. Reverting that commit was not enough, because I already tried to rebase/merge as required to stay out of merge conflicts. I will remove the keys and try to provide something importable for Transifex. Can you point me to some documentation on supported formats for key imports? I can provide them with German localization or without it. Just state your preference. Since real testing (and screenshots) was not really possible without language changes, the commit was at least temporarily required. |
Changes proposed in this pull request
Related issue(s)
Copilot generated summary
Provide a Copilot generated summary of the changes in this pull request.
Copilot summary
{generated summary}
To-Do
Contributing
By submitting this pull request, I confirm the following: