From d02c02664bc2b38d18710eef4cdc79208b5b5de5 Mon Sep 17 00:00:00 2001 From: Bernie White Date: Fri, 1 Nov 2024 02:19:00 +1000 Subject: [PATCH] Fixes empty string handling in Azure.NSG.LateralTraversal #3130 (#3158) --- docs/CHANGELOG-v1.md | 4 ++ .../Network/NetworkSecurityGroupEvaluator.cs | 4 +- .../NetworkSecurityGroupEvaluatorTests.cs | 54 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/PSRule.Rules.Azure.Tests/NetworkSecurityGroupEvaluatorTests.cs diff --git a/docs/CHANGELOG-v1.md b/docs/CHANGELOG-v1.md index d656432c1c4..c479894e80d 100644 --- a/docs/CHANGELOG-v1.md +++ b/docs/CHANGELOG-v1.md @@ -29,6 +29,10 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers ## Unreleased +- Bug fixes: + - Fixed evaluation of `Azure.NSG.LateralTraversal` with empty string properties by @BernieWhite. + [#3130](https://github.com/Azure/PSRule.Rules.Azure/issues/3130) + ## v1.40.0-B0029 (pre-release) What's changed since v1.39.3: diff --git a/src/PSRule.Rules.Azure/Data/Network/NetworkSecurityGroupEvaluator.cs b/src/PSRule.Rules.Azure/Data/Network/NetworkSecurityGroupEvaluator.cs index aeac9f326d8..cf38e536359 100644 --- a/src/PSRule.Rules.Azure/Data/Network/NetworkSecurityGroupEvaluator.cs +++ b/src/PSRule.Rules.Azure/Data/Network/NetworkSecurityGroupEvaluator.cs @@ -12,7 +12,7 @@ namespace PSRule.Rules.Azure.Data.Network; /// /// A basic implementation of an evaluator for checking NSG rules. /// -internal sealed partial class NetworkSecurityGroupEvaluator : INetworkSecurityGroupEvaluator +internal sealed class NetworkSecurityGroupEvaluator : INetworkSecurityGroupEvaluator { private const string PROPERTIES = "properties"; private const string DIRECTION = "direction"; @@ -76,7 +76,7 @@ private static SecurityRule GetRule(PSObject o) if (o.TryProperty(propertyName, out string[] value) && value.Length > 0) return value; - return o.TryProperty(propertyName, out string s) && s != ANY ? [s] : null; + return o.TryProperty(propertyName, out string s) && s != ANY && !string.IsNullOrEmpty(s) ? [s] : null; } } diff --git a/tests/PSRule.Rules.Azure.Tests/NetworkSecurityGroupEvaluatorTests.cs b/tests/PSRule.Rules.Azure.Tests/NetworkSecurityGroupEvaluatorTests.cs new file mode 100644 index 00000000000..c3c2f956f17 --- /dev/null +++ b/tests/PSRule.Rules.Azure.Tests/NetworkSecurityGroupEvaluatorTests.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; + +namespace PSRule.Rules.Azure.Data.Network; + +#nullable enable + +public sealed class NetworkSecurityGroupEvaluatorTests +{ + [Fact] + public void Outbound_WhenMatchingRule_ShouldReturnAccess() + { + var evaluator = new NetworkSecurityGroupEvaluator(); + evaluator.With([Rule(access: "Deny", destinationAddressPrefix: "VirtualNetwork", destinationPortRanges: ["3389"])]); + + Assert.Equal(Access.Deny, evaluator.Outbound("VirtualNetwork", 3389)); + } + + [Fact] + public void With_WhenDestinationPortRangeIsEmptyString_ShouldUseDestinationPortRanges() + { + var evaluator = new NetworkSecurityGroupEvaluator(); + evaluator.With([Rule(access: "Deny", destinationPortRange: "", destinationPortRanges: ["80", "443"])]); + + Assert.Equal(Access.Deny, evaluator.Outbound("Virtual Network", 443)); + } + + #region Helper methods + + private static PSObject Rule(string direction = "Outbound", string access = "Allow", string protocol = "Tcp", string? destinationAddressPrefix = default, string? destinationPortRange = default, string[]? destinationPortRanges = default) + { + var properties = new PSObject(); + properties.Properties.Add(new PSNoteProperty("direction", direction)); + properties.Properties.Add(new PSNoteProperty("access", access)); + properties.Properties.Add(new PSNoteProperty("protocol", protocol)); + properties.Properties.Add(new PSNoteProperty("destinationAddressPrefix", destinationAddressPrefix)); + + if (destinationPortRange != null) + properties.Properties.Add(new PSNoteProperty("destinationPortRange", destinationPortRange)); + + if (destinationPortRanges != null) + properties.Properties.Add(new PSNoteProperty("destinationPortRanges", destinationPortRanges)); + + var result = new PSObject(); + result.Properties.Add(new PSNoteProperty("properties", properties)); + return result; + } + + #endregion Helper methods +} + +#nullable restore