Correlate Exchange IIS failures with security accounts
All checks were successful
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
OfficeCom Sentinel Client / build-client-windows (push) Successful in 49s

This commit is contained in:
OfficeCom Codex
2026-08-05 09:39:33 +02:00
parent 7f4cd291e7
commit 6e10a71820
3 changed files with 113 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
using OCSentinelCli;
using Xunit;
namespace OCSentinelCli.Tests;
public sealed class ExchangeIisAccountCorrelatorTests
{
[Fact]
public void EnrichesAnIisFailureFromOneMatchingSecurityFailure()
{
DateTimeOffset timestamp = new(2026, 8, 5, 4, 0, 0, TimeSpan.Zero);
List<AttackEvent> events =
[
IisFailure(timestamp),
SecurityFailure(timestamp.AddSeconds(20), "user@example.test")
];
AttackEvent enriched = Assert.Single(ExchangeIisAccountCorrelator.Enrich(events), attack => attack.Source == "IIS W3C");
Assert.Equal("user@example.test", enriched.Username);
}
[Fact]
public void KeepsIisAccountUnresolvedWhenMultipleAccountsMatch()
{
DateTimeOffset timestamp = new(2026, 8, 5, 4, 0, 0, TimeSpan.Zero);
List<AttackEvent> events =
[
IisFailure(timestamp),
SecurityFailure(timestamp.AddSeconds(20), "first@example.test"),
SecurityFailure(timestamp.AddSeconds(30), "second@example.test")
];
AttackEvent unresolved = Assert.Single(ExchangeIisAccountCorrelator.Enrich(events), attack => attack.Source == "IIS W3C");
Assert.Equal("[not logged]", unresolved.Username);
}
private static AttackEvent IisFailure(DateTimeOffset timestamp) => new()
{
Timestamp = timestamp,
SourceIp = "198.51.100.8",
Username = "[not logged]",
Source = "IIS W3C",
Target = "Exchange ActiveSync login"
};
private static AttackEvent SecurityFailure(DateTimeOffset timestamp, string username) => new()
{
Timestamp = timestamp,
SourceIp = "198.51.100.8",
Username = username,
Source = "Security",
Service = "Network",
Target = "Windows login",
InstanceId = 4625
};
}