Remove legacy AttackTracer repo content
Some checks failed
OfficeCom Sentinel Client / build-client (push) Has been cancelled

This commit is contained in:
OfficeCom Codex
2026-07-17 00:55:16 +02:00
parent 85e394f647
commit b5e06b885a
57 changed files with 98 additions and 3602 deletions

View File

@@ -0,0 +1,200 @@
using System.Globalization;
namespace OCSentinelCli;
internal static class VulnerabilityCorrelation
{
private static readonly string[] DeviceNameHeaders = ["device", "device_name", "hostname", "computer", "computername", "endpoint", "machine", "system"];
private static readonly string[] CveHeaders = ["cve", "cve_id", "cveid", "vulnerability", "vulnerability_id"];
private static readonly string[] SeverityHeaders = ["severity", "risk", "level"];
private static readonly string[] CvssHeaders = ["cvss", "cvss_score", "score", "base_score"];
private static readonly string[] RemediationHeaders = ["remediation", "patch", "kb", "fix", "solution"];
public static VulnerabilityCorrelationSummary LoadForMachine(string machineName, string csvPath, List<string> errors)
{
try
{
string fullPath = Path.GetFullPath(csvPath);
if (!File.Exists(fullPath))
{
errors.Add($"Vulnerability correlation file not found: {fullPath}");
return VulnerabilityCorrelationSummary.Empty(fullPath);
}
string[] lines = File.ReadAllLines(fullPath);
if (lines.Length == 0)
{
return VulnerabilityCorrelationSummary.Empty(fullPath);
}
string[] headers = SplitCsvLine(lines[0]);
var headerMap = headers
.Select((header, index) => new { Header = header.Trim(), Index = index })
.ToDictionary(static pair => Normalize(pair.Header), static pair => pair.Index, StringComparer.OrdinalIgnoreCase);
int deviceIndex = FindHeaderIndex(headerMap, DeviceNameHeaders);
int cveIndex = FindHeaderIndex(headerMap, CveHeaders);
int severityIndex = FindHeaderIndex(headerMap, SeverityHeaders);
int cvssIndex = FindHeaderIndex(headerMap, CvssHeaders);
int remediationIndex = FindHeaderIndex(headerMap, RemediationHeaders);
if (deviceIndex < 0 || cveIndex < 0)
{
errors.Add($"Vulnerability correlation file is missing a device or CVE column: {fullPath}");
return VulnerabilityCorrelationSummary.Empty(fullPath);
}
var findings = new List<VulnerabilityFinding>();
for (int i = 1; i < lines.Length; i++)
{
string line = lines[i];
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
string[] cells = SplitCsvLine(line);
string deviceName = GetCell(cells, deviceIndex);
if (!string.Equals(deviceName, machineName, StringComparison.OrdinalIgnoreCase))
{
continue;
}
string cveId = GetCell(cells, cveIndex);
if (string.IsNullOrWhiteSpace(cveId))
{
continue;
}
string severity = severityIndex >= 0 ? GetCell(cells, severityIndex) : string.Empty;
string remediation = remediationIndex >= 0 ? GetCell(cells, remediationIndex) : string.Empty;
double? cvss = TryParseDouble(cvssIndex >= 0 ? GetCell(cells, cvssIndex) : string.Empty);
findings.Add(new VulnerabilityFinding
{
DeviceName = deviceName,
CveId = cveId,
Severity = severity,
CvssScore = cvss,
Remediation = remediation
});
}
return VulnerabilityCorrelationSummary.FromFindings(fullPath, findings);
}
catch (Exception ex)
{
errors.Add($"Vulnerability correlation load failed for {csvPath}: {ex.Message}");
return VulnerabilityCorrelationSummary.Empty(Path.GetFullPath(csvPath));
}
}
public static CorrelationAssessment Assess(
string currentAlertState,
int totalEvents,
VulnerabilityCorrelationSummary vulnerabilitySummary,
ScannerConfiguration configuration)
{
string finalState = currentAlertState;
string reason = "No CVE correlation applied.";
bool hasAttacks = totalEvents > 0;
bool hasCriticalCvEs = vulnerabilitySummary.CriticalCount >= configuration.CorrelationCriticalCveThreshold;
bool hasWarningCvEs = vulnerabilitySummary.TotalCount >= configuration.CorrelationWarningCveThreshold;
if (hasAttacks && hasCriticalCvEs)
{
finalState = "critical";
reason = $"Attack activity correlated with {vulnerabilitySummary.CriticalCount} critical/high CVE findings on this endpoint.";
}
else if (hasAttacks && hasWarningCvEs && string.Equals(finalState, "ok", StringComparison.OrdinalIgnoreCase))
{
finalState = "warning";
reason = $"Attack activity correlated with {vulnerabilitySummary.TotalCount} CVE findings on this endpoint.";
}
else if (vulnerabilitySummary.TotalCount > 0)
{
reason = $"Loaded {vulnerabilitySummary.TotalCount} CVE findings for this endpoint, but no alert escalation was required.";
}
return new CorrelationAssessment
{
FinalAlertState = finalState,
CorrelationReason = reason
};
}
private static int FindHeaderIndex(Dictionary<string, int> headerMap, string[] candidates)
{
foreach (string candidate in candidates)
{
if (headerMap.TryGetValue(Normalize(candidate), out int index))
{
return index;
}
}
return -1;
}
private static string Normalize(string value)
{
return value.Trim().Replace(" ", "_").Replace("-", "_").ToLowerInvariant();
}
private static string[] SplitCsvLine(string line)
{
var result = new List<string>();
var current = new System.Text.StringBuilder();
bool inQuotes = false;
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
if (c == '"')
{
if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
{
current.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
}
else if (c == ',' && !inQuotes)
{
result.Add(current.ToString());
current.Clear();
}
else
{
current.Append(c);
}
}
result.Add(current.ToString());
return [.. result];
}
private static string GetCell(string[] cells, int index)
{
return index >= 0 && index < cells.Length ? cells[index].Trim() : string.Empty;
}
private static double? TryParseDouble(string value)
{
if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out double result))
{
return result;
}
if (double.TryParse(value, NumberStyles.Any, CultureInfo.CurrentCulture, out result))
{
return result;
}
return null;
}
}