From 804eee20fdd2f34a4f61dd7220a07d4064db52e3 Mon Sep 17 00:00:00 2001 From: Meik Date: Sun, 29 Mar 2026 22:39:35 +0200 Subject: [PATCH] Add NTFS folder whitelist support --- LiamNtfs/C4IT.LIAM.Ntfs.cs | 142 ++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 11 deletions(-) diff --git a/LiamNtfs/C4IT.LIAM.Ntfs.cs b/LiamNtfs/C4IT.LIAM.Ntfs.cs index 0c08329..0aa268d 100644 --- a/LiamNtfs/C4IT.LIAM.Ntfs.cs +++ b/LiamNtfs/C4IT.LIAM.Ntfs.cs @@ -371,14 +371,16 @@ namespace C4IT.LIAM foreach (var childPath in GetServerRootChildPaths(parentClassification.NormalizedPath)) { var childClassification = ClassifyPath(childPath); - if (!ShouldIncludeDataArea(childClassification)) + if (!ShouldTraverseDataArea(childClassification)) continue; - var childDataArea = await BuildDataAreaAsync(childClassification); - if (childDataArea == null) - continue; + if (ShouldIncludeDataArea(childClassification)) + { + var childDataArea = await BuildDataAreaAsync(childClassification); + if (childDataArea != null) + children.Add(childDataArea); + } - children.Add(childDataArea); if (depth > 1) children.AddRange(await GetChildDataAreasAsync(childClassification, depth - 1)); } @@ -393,14 +395,16 @@ namespace C4IT.LIAM foreach (var entry in folderEntries.Values.OfType()) { var childClassification = ClassifyPath(entry.Path); - if (!ShouldIncludeDataArea(childClassification)) + if (!ShouldTraverseDataArea(childClassification)) continue; - var childDataArea = await BuildDataAreaAsync(childClassification, entry); - if (childDataArea == null) - continue; + if (ShouldIncludeDataArea(childClassification)) + { + var childDataArea = await BuildDataAreaAsync(childClassification, entry); + if (childDataArea != null) + children.Add(childDataArea); + } - children.Add(childDataArea); if (depth > 1) children.AddRange(await GetChildDataAreasAsync(childClassification, depth - 1)); } @@ -436,9 +440,44 @@ namespace C4IT.LIAM return false; } + if (!IsWhitelistedFolderPath(classification, true, out matchingConfigurationKey, out matchingRule)) + { + LogEntry($"Skip NTFS path '{classification.NormalizedPath}' because no AdditionalConfiguration whitelist matched", LogLevels.Debug); + return false; + } + return true; } + private bool ShouldTraverseDataArea(cNtfsPathClassification classification) + { + if (classification == null) + return false; + + string matchingConfigurationKey; + string matchingRule; + if (IsBlacklistedFolderPath(classification, out matchingConfigurationKey, out matchingRule)) + { + LogEntry($"Skip NTFS subtree '{classification.NormalizedPath}' due to AdditionalConfiguration rule '{matchingConfigurationKey}={matchingRule}'", LogLevels.Debug); + return false; + } + + if (HasAdditionalConfigurationValues("NtfsIncludeFolderNames")) + return true; + + if (!HasAdditionalConfigurationValues("NtfsIncludeRelativePaths")) + return true; + + if (classification.Kind != eNtfsPathKind.Folder) + return true; + + if (IsWhitelistedFolderPath(classification, true, out matchingConfigurationKey, out matchingRule)) + return true; + + LogEntry($"Skip NTFS subtree '{classification.NormalizedPath}' because it is outside AdditionalConfiguration whitelist 'NtfsIncludeRelativePaths'", LogLevels.Debug); + return false; + } + private bool MatchesDataAreaRegEx(string displayName) { if (string.IsNullOrEmpty(this.DataAreaRegEx)) @@ -479,6 +518,61 @@ namespace C4IT.LIAM return false; } + private bool IsWhitelistedFolderPath(cNtfsPathClassification classification, bool allowRelativePathAncestorMatches, out string matchingConfigurationKey, out string matchingRule) + { + matchingConfigurationKey = null; + matchingRule = null; + + if (classification == null || classification.Kind != eNtfsPathKind.Folder) + return true; + + var hasIncludeFolderNames = HasAdditionalConfigurationValues("NtfsIncludeFolderNames"); + var hasIncludeRelativePaths = HasAdditionalConfigurationValues("NtfsIncludeRelativePaths"); + if (!hasIncludeFolderNames && !hasIncludeRelativePaths) + return true; + + foreach (var includedFolderName in GetAdditionalConfigurationValues("NtfsIncludeFolderNames")) + { + if (!MatchesAdditionalConfigurationPattern(classification.DisplayName, includedFolderName)) + continue; + + matchingConfigurationKey = "NtfsIncludeFolderNames"; + matchingRule = includedFolderName; + return true; + } + + var relativePath = GetRelativePathFromRoot(classification.NormalizedPath); + foreach (var includedRelativePath in GetAdditionalConfigurationValues("NtfsIncludeRelativePaths")) + { + if (!MatchesAdditionalConfigurationPattern(relativePath, includedRelativePath)) + continue; + + matchingConfigurationKey = "NtfsIncludeRelativePaths"; + matchingRule = includedRelativePath; + return true; + } + + if (allowRelativePathAncestorMatches) + { + foreach (var includedRelativePath in GetAdditionalConfigurationValues("NtfsIncludeRelativePaths")) + { + if (!IsRelativePathAncestorOfPattern(relativePath, includedRelativePath)) + continue; + + matchingConfigurationKey = "NtfsIncludeRelativePaths"; + matchingRule = includedRelativePath; + return true; + } + } + + return false; + } + + private bool HasAdditionalConfigurationValues(string key) + { + return GetAdditionalConfigurationValues(key).Any(); + } + private IEnumerable GetAdditionalConfigurationValues(string key) { if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key)) @@ -531,6 +625,29 @@ namespace C4IT.LIAM return Regex.IsMatch(normalizedValue, regexPattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } + private bool IsRelativePathAncestorOfPattern(string relativePath, string pattern) + { + var normalizedRelativePath = (relativePath ?? string.Empty).Trim().Replace('/', '\\').Trim('\\'); + if (string.IsNullOrWhiteSpace(normalizedRelativePath) || string.IsNullOrWhiteSpace(pattern)) + return false; + + var prefixSegments = new List(); + foreach (var patternSegment in pattern.Trim().Replace('/', '\\').Trim('\\').Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (patternSegment.Contains("*")) + break; + + prefixSegments.Add(patternSegment); + } + + if (prefixSegments.Count == 0) + return false; + + var normalizedPatternPrefix = string.Join("\\", prefixSegments); + return normalizedPatternPrefix.Equals(normalizedRelativePath, StringComparison.OrdinalIgnoreCase) + || normalizedPatternPrefix.StartsWith(normalizedRelativePath + "\\", StringComparison.OrdinalIgnoreCase); + } + private List GetDfsObjectPrefixes(string path) { var normalizedPath = NormalizeUncPath(path); @@ -847,7 +964,10 @@ namespace C4IT.LIAM string matchingConfigurationKey; string matchingRule; - return !IsBlacklistedFolderPath(classification, out matchingConfigurationKey, out matchingRule); + if (IsBlacklistedFolderPath(classification, out matchingConfigurationKey, out matchingRule)) + return false; + + return IsWhitelistedFolderPath(classification, false, out matchingConfigurationKey, out matchingRule); } private IEnumerable BuildSecurityGroupTemplates()