diff --git a/Harden-Windows-Security Module/Main files/C#/CimInstances/TaskSchedulerHelper.cs b/Harden-Windows-Security Module/Main files/C#/CimInstances/TaskSchedulerHelper.cs index abbe61ee9..9b18c3b70 100644 --- a/Harden-Windows-Security Module/Main files/C#/CimInstances/TaskSchedulerHelper.cs +++ b/Harden-Windows-Security Module/Main files/C#/CimInstances/TaskSchedulerHelper.cs @@ -112,8 +112,8 @@ public static object Get(string taskName, string taskPath, OutputType outputType // Check if the TaskName matches the provided taskName (if specified) // and TaskPath matches the provided taskPath (if specified) - bool nameMatches = string.IsNullOrEmpty(taskName) || name == taskName; - bool pathMatches = string.IsNullOrEmpty(taskPath) || path == taskPath; + bool nameMatches = string.IsNullOrEmpty(taskName) || string.Equals(name, taskName, StringComparison.OrdinalIgnoreCase); + bool pathMatches = string.IsNullOrEmpty(taskPath) || string.Equals(path, taskPath, StringComparison.OrdinalIgnoreCase); // If both TaskName and TaskPath match the provided criteria, add the task to the matchingTasks list if (nameMatches && pathMatches) diff --git a/Harden-Windows-Security Module/Main files/C#/IniFileConverter.cs b/Harden-Windows-Security Module/Main files/C#/IniFileConverter.cs index 42a83bd3e..8521516f2 100644 --- a/Harden-Windows-Security Module/Main files/C#/IniFileConverter.cs +++ b/Harden-Windows-Security Module/Main files/C#/IniFileConverter.cs @@ -7,10 +7,10 @@ namespace HardeningModule { public class IniFileConverter { - // a helper method to parse the ini file from the output of the "Secedit /export /cfg .\security_policy.inf" + // A helper method to parse the ini file from the output of the "Secedit /export /cfg .\security_policy.inf" public static Dictionary> ConvertFromIniFile(string iniFilePath) { - var iniObject = new Dictionary>(); + var iniObject = new Dictionary>(StringComparer.OrdinalIgnoreCase); string[] lines = File.ReadAllLines(iniFilePath); string sectionName = string.Empty; @@ -21,7 +21,7 @@ public static Dictionary> ConvertFromIniFile( if (sectionMatch.Success) { sectionName = sectionMatch.Groups[1].Value; - iniObject[sectionName] = new Dictionary(); + iniObject[sectionName] = new Dictionary(StringComparer.OrdinalIgnoreCase); continue; } @@ -40,7 +40,7 @@ public static Dictionary> ConvertFromIniFile( } // Ignore blank lines or comments - if (string.IsNullOrWhiteSpace(line) || line.StartsWith(";") || line.StartsWith("#")) + if (string.IsNullOrWhiteSpace(line) || line.StartsWith(";", StringComparison.Ordinal) || line.StartsWith("#", StringComparison.Ordinal)) { continue; } diff --git a/Harden-Windows-Security Module/Main files/C#/MDMClassProcessor.cs b/Harden-Windows-Security Module/Main files/C#/MDMClassProcessor.cs index 3517d88f3..e581566f0 100644 --- a/Harden-Windows-Security Module/Main files/C#/MDMClassProcessor.cs +++ b/Harden-Windows-Security Module/Main files/C#/MDMClassProcessor.cs @@ -63,13 +63,15 @@ public partial class MDMClassProcessor { foreach (var keyValuePair in dictionary) { - // filter out the items we don't need - if (keyValuePair.Key == "Class" || keyValuePair.Key == "InstanceID" || keyValuePair.Key == "ParentID") + // Filter out the items we don't need using ordinal, case-insensitive comparison + if (String.Equals(keyValuePair.Key, "Class", StringComparison.OrdinalIgnoreCase) || + String.Equals(keyValuePair.Key, "InstanceID", StringComparison.OrdinalIgnoreCase) || + String.Equals(keyValuePair.Key, "ParentID", StringComparison.OrdinalIgnoreCase)) { continue; } - // Add the date to the list + // Add the data to the list resultsList.Add(new HardeningModule.MDMClassProcessor( keyValuePair.Key, keyValuePair.Value?.ToString(), diff --git a/Harden-Windows-Security Module/Main files/C#/RegistryEditor.cs b/Harden-Windows-Security Module/Main files/C#/RegistryEditor.cs index 34364250f..5ee7bbb45 100644 --- a/Harden-Windows-Security Module/Main files/C#/RegistryEditor.cs +++ b/Harden-Windows-Security Module/Main files/C#/RegistryEditor.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Microsoft.Win32; namespace HardeningModule @@ -8,7 +9,7 @@ public static class RegistryEditor public static void EditRegistry(string path, string key, string value, string type, string action) { // Removing the 'Registry::' prefix from the path - if (path.StartsWith("Registry::")) + if (path.StartsWith("Registry::", StringComparison.OrdinalIgnoreCase)) { path = path.Substring(10); } @@ -19,7 +20,7 @@ public static void EditRegistry(string path, string key, string value, string ty RegistryKey baseRegistryKey; - switch (baseKey.ToUpper()) + switch (baseKey.ToUpperInvariant()) { case "HKEY_LOCAL_MACHINE": { @@ -59,7 +60,7 @@ public static void EditRegistry(string path, string key, string value, string ty RegistryValueKind valueType; object convertedValue; - switch (type.ToUpper()) + switch (type.ToUpperInvariant()) { case "STRING": { @@ -70,13 +71,13 @@ public static void EditRegistry(string path, string key, string value, string ty case "DWORD": { valueType = RegistryValueKind.DWord; - convertedValue = int.Parse(value); + convertedValue = int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); break; } case "QWORD": { valueType = RegistryValueKind.QWord; - convertedValue = long.Parse(value); + convertedValue = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); break; } case "BINARY": diff --git a/Harden-Windows-Security Module/Main files/C#/SneakAndPeek.cs b/Harden-Windows-Security Module/Main files/C#/SneakAndPeek.cs new file mode 100644 index 000000000..f08c52cb6 --- /dev/null +++ b/Harden-Windows-Security Module/Main files/C#/SneakAndPeek.cs @@ -0,0 +1,33 @@ +using System; +using System.IO.Compression; +using System.Linq; +using System.Text.RegularExpressions; + +namespace HardeningModule +{ + public static class SneakAndPeek + { + /// + /// Takes a peek into a zip file and returns bool based on whether a file based on the query is found or not + /// + /// + /// + /// + public static bool Search(string query, string zipFile) + { + // Convert the query to a regular expression + string regexPattern = "^" + Regex.Escape(query).Replace("\\*", ".*") + "$"; + Regex regex = new Regex(regexPattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + // Open the zip file in read mode + using (ZipArchive zipArchive = ZipFile.OpenRead(zipFile)) + { + // Make sure the selected zip has the required file + var content = zipArchive.Entries.Where(entry => regex.IsMatch(entry.FullName)).ToList(); + + // Return true if the number of files found is greater than 0 + return content.Count > 0; + } + } + } +} diff --git a/Harden-Windows-Security Module/Main files/C#/WindowsFeatureChecker.cs b/Harden-Windows-Security Module/Main files/C#/WindowsFeatureChecker.cs index 2d66c136f..324e2046d 100644 --- a/Harden-Windows-Security Module/Main files/C#/WindowsFeatureChecker.cs +++ b/Harden-Windows-Security Module/Main files/C#/WindowsFeatureChecker.cs @@ -52,7 +52,7 @@ public static FeatureStatus CheckWindowsFeatures() private static Dictionary GetOptionalFeatureStates() { // Initialize a dictionary to store the states of optional features - var states = new Dictionary(); + var states = new Dictionary(StringComparer.OrdinalIgnoreCase); // Ensure case-insensitive key comparison // Create a ManagementObjectSearcher to query Win32_OptionalFeature using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OptionalFeature")) @@ -84,17 +84,20 @@ private static string GetCapabilityState(string capabilityName) string dismOutput = RunDismCommand($"/Online /Get-CapabilityInfo /CapabilityName:{capabilityName}"); // Check if the output contains "State : Installed" - if (dismOutput.Contains("State : Installed")) + // check if the return value is greater than or equal to 0 indicating that the substring exists in the string + if (dismOutput.IndexOf("State : Installed", StringComparison.Ordinal) >= 0) { return "Installed"; } // Check if the output contains "State : Not Present" - else if (dismOutput.Contains("State : Not Present")) + // check if the return value is greater than or equal to 0 indicating that the substring exists in the string + else if (dismOutput.IndexOf("State : Not Present", StringComparison.Ordinal) >= 0) { return "Not Present"; } // Check if the output contains "State : Staged" - else if (dismOutput.Contains("State : Staged")) + // check if the return value is greater than or equal to 0 indicating that the substring exists in the string + else if (dismOutput.IndexOf("State : Staged", StringComparison.Ordinal) >= 0) { return "Staged"; } diff --git a/Harden-Windows-Security Module/Main files/C#/WriteVerbose.cs b/Harden-Windows-Security Module/Main files/C#/WriteVerbose.cs index 3bed9bfc7..4c03976b3 100644 --- a/Harden-Windows-Security Module/Main files/C#/WriteVerbose.cs +++ b/Harden-Windows-Security Module/Main files/C#/WriteVerbose.cs @@ -15,7 +15,8 @@ public static void Write(string message) { try { - if (HardeningModule.GlobalVars.VerbosePreference == "Continue" || HardeningModule.GlobalVars.VerbosePreference == "Inquire") + if (string.Equals(HardeningModule.GlobalVars.VerbosePreference, "Continue", StringComparison.OrdinalIgnoreCase) || + string.Equals(HardeningModule.GlobalVars.VerbosePreference, "Inquire", StringComparison.OrdinalIgnoreCase)) { HardeningModule.GlobalVars.Host.UI.WriteVerboseLine(message); } diff --git a/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 b/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 index dbc205c63..ad6121f84 100644 --- a/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 +++ b/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 @@ -146,19 +146,8 @@ Function Protect-WindowsSecurity { # Create a validate script attribute and add it to the collection [System.Management.Automation.ValidateScriptAttribute]$PathToLGPO_ValidateScriptAttrib = New-Object -TypeName System.Management.Automation.ValidateScriptAttribute( { - try { - # Load the System.IO.Compression assembly - [System.Void][System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') - # Open the zip file in read mode - [System.IO.Compression.ZipArchive]$ZipArchive = [IO.Compression.ZipFile]::OpenRead("$_") - # Make sure the selected zip has the required file - if (-NOT ($ZipArchive.Entries | Where-Object -FilterScript { $_.FullName -like 'LGPO_*/LGPO.exe' })) { - Throw 'The selected Zip file does not contain the LGPO.exe which is required for the Protect-WindowsSecurity function to work properly' - } - } - finally { - # Close the handle whether the zip file is valid or not - $ZipArchive.Dispose() + if (-NOT ([HardeningModule.SneakAndPeek]::Search('LGPO_*/LGPO.exe', $_))) { + Throw 'The selected Zip file does not contain the LGPO.exe which is required for the Protect-WindowsSecurity function to work properly' } # Return true if everything is okay $true @@ -194,19 +183,8 @@ Function Protect-WindowsSecurity { # Create a validate script attribute and add it to the collection [System.Management.Automation.ValidateScriptAttribute]$PathToMSFT365AppsSecurityBaselines_ValidateScriptAttrib = New-Object -TypeName System.Management.Automation.ValidateScriptAttribute( { - try { - # Load the System.IO.Compression assembly - [System.Void][System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') - # Open the zip file in read mode - [System.IO.Compression.ZipArchive]$ZipArchive = [IO.Compression.ZipFile]::OpenRead("$_") - # Make sure the selected zip has the required file - if (-NOT ($ZipArchive.Entries | Where-Object -FilterScript { $_.FullName -like 'Microsoft 365 Apps for Enterprise*/Scripts/Baseline-LocalInstall.ps1' })) { - Throw 'The selected Zip file does not contain the Microsoft 365 Apps for Enterprise Security Baselines Baseline-LocalInstall.ps1 which is required for the Protect-WindowsSecurity function to work properly' - } - } - finally { - # Close the handle whether the zip file is valid or not - $ZipArchive.Dispose() + if (-NOT ([HardeningModule.SneakAndPeek]::Search('Microsoft 365 Apps for Enterprise*/Scripts/Baseline-LocalInstall.ps1', $_))) { + Throw 'The selected Zip file does not contain the Microsoft 365 Apps for Enterprise Security Baselines Baseline-LocalInstall.ps1 which is required for the Protect-WindowsSecurity function to work properly' } # Return true if everything is okay $true @@ -242,19 +220,8 @@ Function Protect-WindowsSecurity { # Create a validate script attribute and add it to the collection [System.Management.Automation.ValidateScriptAttribute]$PathToMSFTSecurityBaselines_ValidateScriptAttrib = New-Object -TypeName System.Management.Automation.ValidateScriptAttribute( { - try { - # Load the System.IO.Compression assembly - [System.Void][System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') - # Open the zip file in read mode - [System.IO.Compression.ZipArchive]$ZipArchive = [IO.Compression.ZipFile]::OpenRead("$_") - # Make sure the selected zip has the required file - if (-NOT ($ZipArchive.Entries | Where-Object -FilterScript { $_.FullName -like 'Windows*Security Baseline/Scripts/Baseline-LocalInstall.ps1' })) { - Throw 'The selected Zip file does not contain the Microsoft Security Baselines Baseline-LocalInstall.ps1 which is required for the Protect-WindowsSecurity function to work properly' - } - } - finally { - # Close the handle whether the zip file is valid or not - $ZipArchive.Dispose() + if (-NOT ([HardeningModule.SneakAndPeek]::Search('Windows*Security Baseline/Scripts/Baseline-LocalInstall.ps1', $_))) { + Throw 'The selected Zip file does not contain the Microsoft Security Baselines Baseline-LocalInstall.ps1 which is required for the Protect-WindowsSecurity function to work properly' } # Return true if everything is okay $true @@ -695,13 +662,7 @@ Execution Policy: $CurrentExecutionPolicy if ($Dialog.ShowDialog() -eq 'OK') { try { - # Load the System.IO.Compression assembly - [System.Void][System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') - # Open the zip file in read mode - [System.IO.Compression.ZipArchive]$ZipArchive = [IO.Compression.ZipFile]::OpenRead($Dialog.FileName) - # Make sure the selected zip has the required file - if (-NOT ($ZipArchive.Entries | Where-Object -FilterScript { $_.FullName -like 'Windows*Security Baseline/Scripts/Baseline-LocalInstall.ps1' })) { - + if (-NOT ([HardeningModule.SneakAndPeek]::Search('Windows*Security Baseline/Scripts/Baseline-LocalInstall.ps1', $Dialog.FileName))) { [HardeningModule.Logger]::LogMessage( 'The selected Zip file does not contain the Microsoft Security Baselines Baseline-LocalInstall.ps1 which is required for the Protect-WindowsSecurity function to work properly', $SyncHash.Logger, @@ -726,10 +687,6 @@ Execution Policy: $CurrentExecutionPolicy $SyncHash.Window ) } - finally { - # Close the handle whether the zip file is valid or not - $ZipArchive.Dispose() - } } }) @@ -744,13 +701,7 @@ Execution Policy: $CurrentExecutionPolicy if ($Dialog.ShowDialog() -eq 'OK') { try { - # Load the System.IO.Compression assembly - [System.Void][System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') - # Open the zip file in read mode - [System.IO.Compression.ZipArchive]$ZipArchive = [IO.Compression.ZipFile]::OpenRead($Dialog.FileName ) - # Make sure the selected zip has the required file - if (-NOT ($ZipArchive.Entries | Where-Object -FilterScript { $_.FullName -like 'Microsoft 365 Apps for Enterprise*/Scripts/Baseline-LocalInstall.ps1' })) { - + if (-NOT ([HardeningModule.SneakAndPeek]::Search('Microsoft 365 Apps for Enterprise*/Scripts/Baseline-LocalInstall.ps1', $Dialog.FileName))) { [HardeningModule.Logger]::LogMessage( 'The selected Zip file does not contain the Microsoft 365 Apps for Enterprise Security Baselines Baseline-LocalInstall.ps1 which is required for the Protect-WindowsSecurity function to work properly', $SyncHash.Logger, @@ -775,10 +726,6 @@ Execution Policy: $CurrentExecutionPolicy $SyncHash.Window ) } - finally { - # Close the handle whether the zip file is valid or not - $ZipArchive.Dispose() - } } }) @@ -793,13 +740,7 @@ Execution Policy: $CurrentExecutionPolicy if ($Dialog.ShowDialog() -eq 'OK') { try { - # Load the System.IO.Compression assembly - [System.Void][System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') - # Open the zip file in read mode - [System.IO.Compression.ZipArchive]$ZipArchive = [IO.Compression.ZipFile]::OpenRead($Dialog.FileName) - # Make sure the selected zip has the required file - if (-NOT ($ZipArchive.Entries | Where-Object -FilterScript { $_.FullName -like 'LGPO_*/LGPO.exe' })) { - + if (-NOT ([HardeningModule.SneakAndPeek]::Search('LGPO_*/LGPO.exe', $Dialog.FileName))) { [HardeningModule.Logger]::LogMessage( 'The selected Zip file does not contain the LGPO.exe which is required for the Protect-WindowsSecurity function to work properly', $SyncHash.Logger, @@ -824,10 +765,6 @@ Execution Policy: $CurrentExecutionPolicy $SyncHash.Window ) } - finally { - # Close the handle whether the zip file is valid or not - $ZipArchive.Dispose() - } } }) #Endregion Offline-Mode-Tab diff --git a/Harden-Windows-Security Module/Main files/Harden-Windows-Security-Module.psd1 b/Harden-Windows-Security Module/Main files/Harden-Windows-Security-Module.psd1 index 0830d6c5e..69d1cc601 100644 --- a/Harden-Windows-Security Module/Main files/Harden-Windows-Security-Module.psd1 +++ b/Harden-Windows-Security Module/Main files/Harden-Windows-Security-Module.psd1 @@ -2,7 +2,7 @@ # https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_module_manifests RootModule = 'Harden-Windows-Security-Module.psm1' - ModuleVersion = '0.5.2' + ModuleVersion = '0.5.3' CompatiblePSEditions = @('Core') GUID = 'afae7a0a-5eff-4a4d-9139-e1702b7ac426' Author = 'HotCakeX' diff --git a/Harden-Windows-Security Module/version.txt b/Harden-Windows-Security Module/version.txt index 2411653a5..c52db9804 100644 --- a/Harden-Windows-Security Module/version.txt +++ b/Harden-Windows-Security Module/version.txt @@ -1 +1 @@ -0.5.2 \ No newline at end of file +0.5.3 \ No newline at end of file