fix(SEC-33,SEC-34): host-meaningful path rooting; verification-cache invalidate race
SEC-33: make rooting host-meaningful and stop shipping foreign-platform literals. - Delete IsRootedForAnyPlatform; AddIfNotRooted now uses Path.IsPathRooted (current OS). - Promote AddIfNotRooted/AddIfInvalidPath to shared GatewayConfigPathRules so the new Galaxy validator reuses them and the two validators cannot drift. - Remove Authentication:SqlitePath and Galaxy:SnapshotCachePath Windows literals from appsettings.json; the CommonApplicationData-derived code defaults take over. The Galaxy default is seeded as a configuration value before AddZbGalaxyRepository (SnapshotCachePath is init-only, so a PostConfigure mutation cannot compile). - New GalaxyRepositoryOptionsValidator (ValidateOnStart) enforces a valid, host-rooted SnapshotCachePath when PersistSnapshot is true. - Root-cause the stray junk-named auth DB: host start eagerly builds AuthSqliteConnectionFactory; under the non-rooted Windows literal on macOS SQLite wrote it relative to the test bin CWD. The three real-host-start tests now pin SqlitePath to a temp path. SEC-34: verification cache Invalidate-vs-in-flight-repopulation race closed with a per-key generation counter (bump-before-evict, snapshot-then-recheck). The expiry cap (window 2) takes the documented fallback: the library verification identity carries no ExpiresUtc, so the cache cannot cap at the key's expiry (donor-library ask). GWC-24 rider: cap MxGateway:Events:QueueCapacity at int.MaxValue/2 so the derived checked(2 * EventChannelCapacity) in WorkerClient cannot overflow at session creation. SEC-35 (doc-only): note IsProduction() env-name semantics in GatewayConfiguration.md. Docs updated same commit (GatewayConfiguration.md, Authentication.md) and tracking registers/change-log flipped (00-tracking.md, 40-security-dashboard.md).
This commit is contained in:
@@ -598,6 +598,61 @@ public sealed class GatewayOptionsValidatorTests
|
||||
f => f.Contains("MxGateway:Authentication:SqlitePath") && f.Contains("rooted"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies rooting is host-meaningful (SEC-33): a Windows drive-qualified literal fails on a
|
||||
/// Unix host (where it is not rooted) rather than being blessed and written as a junk-named
|
||||
/// relative file. On Windows the same literal is genuinely rooted and passes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Validate_SqlitePath_RootingIsHostMeaningful()
|
||||
{
|
||||
const string windowsLiteral = @"C:\ProgramData\MxGateway\gateway-auth.db";
|
||||
GatewayOptions options = CloneWithAuthentication(
|
||||
ValidOptions(),
|
||||
new AuthenticationOptions { SqlitePath = windowsLiteral });
|
||||
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
Assert.True(result.Succeeded);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(result.Failed);
|
||||
Assert.Contains(
|
||||
result.Failures!,
|
||||
f => f.Contains("MxGateway:Authentication:SqlitePath") && f.Contains("rooted"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies MxGateway:Events:QueueCapacity above int.MaxValue/2 fails (GWC-24 rider): the value
|
||||
/// flows into WorkerClient as checked(2 * EventChannelCapacity), which would otherwise overflow.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Validate_Fails_WhenQueueCapacityExceedsUpperBound()
|
||||
{
|
||||
GatewayOptions options = CloneWithEvents(
|
||||
ValidOptions(),
|
||||
new EventOptions { QueueCapacity = (int.MaxValue / 2) + 1 });
|
||||
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options);
|
||||
Assert.True(result.Failed);
|
||||
Assert.Contains(
|
||||
result.Failures!,
|
||||
f => f.Contains("MxGateway:Events:QueueCapacity"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies MxGateway:Events:QueueCapacity at exactly int.MaxValue/2 passes (boundary).</summary>
|
||||
[Fact]
|
||||
public void Validate_Succeeds_WhenQueueCapacityAtUpperBound()
|
||||
{
|
||||
GatewayOptions options = CloneWithEvents(
|
||||
ValidOptions(),
|
||||
new EventOptions { QueueCapacity = int.MaxValue / 2 });
|
||||
ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options);
|
||||
Assert.True(result.Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>Verifies a non-rooted <see cref="TlsOptions.SelfSignedCertPath"/> fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_Fails_WhenSelfSignedCertPathNotRooted()
|
||||
|
||||
Reference in New Issue
Block a user