From 3ca2799c90ff1f1259afa8f98ebc27e3175ae921 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 22:45:16 -0400 Subject: [PATCH] fix: tighten MxGateway Ldap:Port to 1-65535; catch IOException in path validation Defect 1: ValidateLdap used AddIfNotPositive for Port, accepting any value > 0 including 70000. Replaced with builder.Port() from the shared ZB.MOM.WW.Configuration library, which enforces the 1-65535 TCP range and emits "MxGateway:Ldap:Port must be between 1 and 65535 (was {value})". Defect 2: AddIfInvalidPath only caught ArgumentException, NotSupportedException, and PathTooLongException from Path.GetFullPath. On macOS/Linux a path containing an embedded null throws IOException, which escaped the catch block and caused Validate() to throw instead of returning a failure. Added catch (IOException). Tests: added Validate_Fails_WhenLdapPortIsZero, Validate_Fails_WhenLdapPortExceedsMaximum, and Validate_Succeeds_WhenLdapEnabledWithValidPort to cover the new range boundary. --- .../Configuration/GatewayOptionsValidator.cs | 6 ++- .../GatewayOptionsValidatorTests.cs | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs index fd104ca..fdc104f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs @@ -80,7 +80,7 @@ public sealed class GatewayOptionsValidator : OptionsValidatorBase new(); + // Returns enabled LDAP options that pass all checks except Port. + // The class defaults already satisfy the blank-field checks; we only + // override Enabled (must be true to exercise the port check) and Port. + private static LdapOptions LdapOptionsWithPort(int port) => new() + { + Enabled = true, + Port = port, + }; + + private static GatewayOptions CloneWithLdap(GatewayOptions source, LdapOptions ldap) + => new() + { + Authentication = source.Authentication, + Ldap = ldap, + Worker = source.Worker, + Sessions = source.Sessions, + Events = source.Events, + Dashboard = source.Dashboard, + Protocol = source.Protocol, + Alarms = source.Alarms, + Tls = source.Tls, + }; + private static GatewayOptions CloneWithTls(GatewayOptions source, TlsOptions tls) => new() { @@ -65,4 +88,34 @@ public sealed class GatewayOptionsValidatorTests Assert.True(result.Failed); Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:SelfSignedCertPath must not be blank.")); } + + [Fact] + public void Validate_Fails_WhenLdapPortIsZero() + { + GatewayOptions options = CloneWithLdap(ValidOptions(), LdapOptionsWithPort(0)); + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options); + Assert.True(result.Failed); + Assert.Contains( + result.Failures!, + f => f.Contains("MxGateway:Ldap:Port must be between 1 and 65535 (was 0)")); + } + + [Fact] + public void Validate_Fails_WhenLdapPortExceedsMaximum() + { + GatewayOptions options = CloneWithLdap(ValidOptions(), LdapOptionsWithPort(70000)); + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options); + Assert.True(result.Failed); + Assert.Contains( + result.Failures!, + f => f.Contains("MxGateway:Ldap:Port must be between 1 and 65535 (was 70000)")); + } + + [Fact] + public void Validate_Succeeds_WhenLdapEnabledWithValidPort() + { + GatewayOptions options = CloneWithLdap(ValidOptions(), LdapOptionsWithPort(389)); + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options); + Assert.True(result.Succeeded); + } }