improved matching with exclude pattern

This commit is contained in:
Oliver Kieselbach
2026-04-02 23:13:21 +02:00
parent cc42a22fcb
commit 7763563c76
4 changed files with 71 additions and 4 deletions
+2 -1
View File
@@ -15,7 +15,8 @@
{
"normalizedVendor": "microsoft",
"normalizedProduct": "microsoft edge",
"displayNamePatterns": ["microsoft edge", "edge", "microsoft edge webview2", "webview2 runtime"],
"displayNamePatterns": ["microsoft edge", "microsoft edge webview2", "webview2 runtime"],
"excludePatterns": ["edge update"],
"publisherPatterns": ["microsoft", "microsoft corporation"],
"cpeVendor": "microsoft",
"cpeProduct": "edge_chromium",
@@ -1 +1 @@
802
803
@@ -544,6 +544,50 @@ public class VulnerabilityCorrelationServiceTests
Assert.Equal("cpe:2.3:a:cisco:secure_client", result!.CpeUri);
}
[Fact]
public void FindCpeMapping_ExcludePattern_PreventsMatch()
{
// "Microsoft Edge Update" should NOT match edge_chromium because of excludePatterns
var svc = CreateServiceWithMappings(new List<CpeMappingEntry>
{
new CpeMappingEntry
{
NormalizedVendor = "microsoft",
NormalizedProduct = "microsoft edge",
DisplayNamePatterns = new List<string> { "microsoft edge" },
ExcludePatterns = new List<string> { "edge update" },
CpeUri = "cpe:2.3:a:microsoft:edge_chromium",
CpeProduct = "edge_chromium"
}
});
// "Microsoft Edge Update" contains "microsoft edge" but also "edge update" → excluded
var result = svc.FindCpeMapping("microsoft", "microsoft edge update", "Microsoft Edge Update");
Assert.Null(result);
}
[Fact]
public void FindCpeMapping_ExcludePattern_DoesNotAffectLegitMatch()
{
var svc = CreateServiceWithMappings(new List<CpeMappingEntry>
{
new CpeMappingEntry
{
NormalizedVendor = "microsoft",
NormalizedProduct = "microsoft edge",
DisplayNamePatterns = new List<string> { "microsoft edge" },
ExcludePatterns = new List<string> { "edge update" },
CpeUri = "cpe:2.3:a:microsoft:edge_chromium",
CpeProduct = "edge_chromium"
}
});
// Real Edge browser → should still match (no exclude pattern hit)
var result = svc.FindCpeMapping("microsoft", "microsoft edge", "Microsoft Edge");
Assert.NotNull(result);
Assert.Equal("high", result!.MatchConfidence); // Exact match on normalizedProduct
}
// -----------------------------------------------------------------------
// MergeReports
// -----------------------------------------------------------------------
@@ -278,9 +278,11 @@ namespace AutopilotMonitor.Functions.Services.Vulnerability
// 2. Pattern match on displayNamePatterns
var displayNameLower = (displayName ?? "").ToLowerInvariant();
var normalizedNameLower = (normalizedName ?? "").ToLowerInvariant();
var patternMatch = _cpeMappings.FirstOrDefault(m =>
m.DisplayNamePatterns != null &&
m.DisplayNamePatterns.Any(p => displayNameLower.Contains(p.ToLowerInvariant())));
m.DisplayNamePatterns.Any(p => displayNameLower.Contains(p.ToLowerInvariant())) &&
!IsExcluded(m, displayNameLower, normalizedNameLower));
if (patternMatch != null)
{
patternMatch.MatchConfidence = "medium";
@@ -294,7 +296,8 @@ namespace AutopilotMonitor.Functions.Services.Vulnerability
m.PublisherPatterns != null &&
m.PublisherPatterns.Any(p => string.Equals(p, normalizedVendor, StringComparison.OrdinalIgnoreCase)) &&
!string.IsNullOrWhiteSpace(normalizedName) &&
normalizedName.Contains(m.CpeProduct?.Replace("_", " ") ?? "", StringComparison.OrdinalIgnoreCase));
normalizedName.Contains(m.CpeProduct?.Replace("_", " ") ?? "", StringComparison.OrdinalIgnoreCase) &&
!IsExcluded(m, displayNameLower, normalizedNameLower));
if (vendorMatch != null)
{
vendorMatch.MatchConfidence = "medium";
@@ -305,6 +308,24 @@ namespace AutopilotMonitor.Functions.Services.Vulnerability
return null;
}
/// <summary>
/// Returns true if the mapping's ExcludePatterns match the display name or normalized name.
/// Used to prevent false-positive CPE matches (e.g. "Microsoft Edge Update" ≠ Edge browser).
/// </summary>
private static bool IsExcluded(CpeMappingEntry mapping, string displayNameLower, string normalizedNameLower)
{
if (mapping.ExcludePatterns == null || mapping.ExcludePatterns.Count == 0)
return false;
foreach (var pattern in mapping.ExcludePatterns)
{
var p = pattern.ToLowerInvariant();
if (displayNameLower.Contains(p) || normalizedNameLower.Contains(p))
return true;
}
return false;
}
// -----------------------------------------------------------------------
// CVE Lookup (with Table Storage caching via IVulnerabilityRepository)
// -----------------------------------------------------------------------
@@ -1043,6 +1064,7 @@ namespace AutopilotMonitor.Functions.Services.Vulnerability
public string? NormalizedVendor { get; set; }
public string? NormalizedProduct { get; set; }
public List<string> DisplayNamePatterns { get; set; } = new List<string>();
public List<string> ExcludePatterns { get; set; } = new List<string>();
public List<string> PublisherPatterns { get; set; } = new List<string>();
public string? CpeVendor { get; set; }
public string? CpeProduct { get; set; }