Skip to content

Commit a56fa17

Browse files
committed
Fix: Firewall local/remote address IPv4 Subnetmask/CIDR validation
1 parent 8277f73 commit a56fa17

3 files changed

Lines changed: 43 additions & 12 deletions

File tree

Source/GlobalAssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
[assembly: AssemblyTrademark("")]
77
[assembly: AssemblyCulture("")]
88

9-
[assembly: AssemblyVersion("2026.3.10.0")]
10-
[assembly: AssemblyFileVersion("2026.3.10.0")]
9+
[assembly: AssemblyVersion("2026.4.26.0")]
10+
[assembly: AssemblyFileVersion("2026.4.26.0")]

Source/NETworkManager.Models/Firewall/Firewall.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Management.Automation.Runspaces;
5+
using System.Net;
56
using System.Text;
67
using System.Threading;
78
using System.Threading.Tasks;
9+
using NETworkManager.Models.Network;
10+
using NETworkManager.Utilities;
811
using SMA = System.Management.Automation;
912
using log4net;
1013

@@ -497,7 +500,9 @@ private static bool[] ParseProfile(string value)
497500

498501
/// <summary>
499502
/// Parses a comma-separated address string (e.g. <c>"192.168.1.0/24,LocalSubnet"</c>) to a
500-
/// list of address strings.
503+
/// list of address strings. PowerShell returns IPv4 subnets in subnet-mask notation
504+
/// (e.g. <c>10.8.0.0/255.255.0.0</c>); these are normalized back to CIDR. IPv6 subnets
505+
/// are already returned in CIDR notation by PowerShell and are passed through unchanged.
501506
/// Returns an empty list when the value is blank or <c>"Any"</c>.
502507
/// </summary>
503508
/// <param name="value">
@@ -508,7 +513,27 @@ private static List<string> ParseAddresses(string value)
508513
if (string.IsNullOrWhiteSpace(value) || value.Equals("Any", StringComparison.OrdinalIgnoreCase))
509514
return [];
510515

511-
return [.. value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
516+
var addresses = new List<string>();
517+
518+
foreach (var token in value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
519+
{
520+
var slashIndex = token.IndexOf('/');
521+
522+
if (slashIndex > 0)
523+
{
524+
var maskPart = token[(slashIndex + 1)..];
525+
526+
if (RegexHelper.SubnetmaskRegex().IsMatch(maskPart) && IPAddress.TryParse(maskPart, out var mask))
527+
{
528+
addresses.Add($"{token[..slashIndex]}/{Subnetmask.ConvertSubnetmaskToCidr(mask)}");
529+
continue;
530+
}
531+
}
532+
533+
addresses.Add(token);
534+
}
535+
536+
return addresses;
512537
}
513538

514539
/// <summary>

Source/NETworkManager.Validators/EmptyOrFirewallAddressValidator.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
using System.Net.Sockets;
55
using System.Windows.Controls;
66
using NETworkManager.Localization.Resources;
7+
using NETworkManager.Utilities;
78

89
namespace NETworkManager.Validators;
910

1011
/// <summary>
1112
/// Validates that the input is empty (meaning "Any") or contains semicolon-separated
12-
/// valid IPv4/IPv6 addresses, CIDR subnets, or recognized Windows Firewall keywords
13+
/// valid IPv4/IPv6 addresses, IPv4/IPv6 CIDR subnets, IPv4 subnets in subnet-mask
14+
/// notation (e.g. <c>10.0.0.0/255.0.0.0</c>), or recognized Windows Firewall keywords
1315
/// (e.g. LocalSubnet, Internet, Intranet, DNS, DHCP, WINS, DefaultGateway).
1416
/// </summary>
1517
public class EmptyOrFirewallAddressValidator : ValidationRule
@@ -41,14 +43,18 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo)
4143
if (!IPAddress.TryParse(addressPart, out var ip))
4244
return new ValidationResult(false, Strings.EnterValidFirewallAddress);
4345

44-
if (slashIndex > 0)
45-
{
46-
var prefixStr = token[(slashIndex + 1)..];
47-
var maxPrefix = ip.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32;
46+
if (slashIndex <= 0)
47+
continue;
48+
49+
var suffix = token[(slashIndex + 1)..];
50+
51+
if (ip.AddressFamily == AddressFamily.InterNetwork && RegexHelper.SubnetmaskRegex().IsMatch(suffix))
52+
continue;
4853

49-
if (!int.TryParse(prefixStr, out var prefix) || prefix < 0 || prefix > maxPrefix)
50-
return new ValidationResult(false, Strings.EnterValidFirewallAddress);
51-
}
54+
var maxPrefix = ip.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32;
55+
56+
if (!int.TryParse(suffix, out var prefix) || prefix < 0 || prefix > maxPrefix)
57+
return new ValidationResult(false, Strings.EnterValidFirewallAddress);
5258
}
5359

5460
return ValidationResult.ValidResult;

0 commit comments

Comments
 (0)