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:
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
|
||||
using ZB.MOM.WW.MxGateway.Server;
|
||||
using ZB.MOM.WW.MxGateway.Server.Dashboard;
|
||||
using ZB.MOM.WW.MxGateway.Server.Metrics;
|
||||
using ZB.MOM.WW.MxGateway.Tests.Security.Authentication;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.Gateway;
|
||||
|
||||
@@ -80,8 +81,13 @@ public sealed class GatewayApplicationTests
|
||||
public async Task Build_MapsMetricsEndpoint()
|
||||
{
|
||||
// Bind an ephemeral port (:0) — xUnit runs test collections in parallel, so any
|
||||
// started-host test must avoid a fixed port to prevent a bind collision.
|
||||
await using WebApplication app = GatewayApplication.Build(["--urls=http://127.0.0.1:0"]);
|
||||
// started-host test must avoid a fixed port to prevent a bind collision. Starting the host
|
||||
// eagerly opens the auth SQLite store; the shipped config no longer carries a SqlitePath, so
|
||||
// override it to a writable temp path (the code default resolves under an unwritable
|
||||
// /usr/share on macOS). See SEC-33.
|
||||
using TempDatabaseDirectory authDir = TempDatabaseDirectory.Create(nameof(GatewayApplicationTests));
|
||||
await using WebApplication app = GatewayApplication.Build(
|
||||
["--urls=http://127.0.0.1:0", $"--MxGateway:Authentication:SqlitePath={authDir.DatabasePath()}"]);
|
||||
await app.StartAsync();
|
||||
try
|
||||
{
|
||||
@@ -258,9 +264,13 @@ public sealed class GatewayApplicationTests
|
||||
string expectedFailure)
|
||||
{
|
||||
// Bind an ephemeral port (:0) — xUnit runs test collections in parallel, so any
|
||||
// WebApplication-building test must avoid a fixed port to prevent a bind collision.
|
||||
// WebApplication-building test must avoid a fixed port to prevent a bind collision. Override
|
||||
// the auth SqlitePath to a writable temp path: startup opens the store before the injected
|
||||
// misconfiguration is validated on some paths, and the code-default path is unwritable on
|
||||
// macOS (SEC-33).
|
||||
using TempDatabaseDirectory authDir = TempDatabaseDirectory.Create(nameof(GatewayApplicationTests));
|
||||
await using WebApplication app = GatewayApplication.Build(
|
||||
[$"--{key}={value}", "--urls=http://127.0.0.1:0"]);
|
||||
[$"--{key}={value}", "--urls=http://127.0.0.1:0", $"--MxGateway:Authentication:SqlitePath={authDir.DatabasePath()}"]);
|
||||
|
||||
OptionsValidationException exception = await Assert.ThrowsAsync<OptionsValidationException>(
|
||||
() => app.StartAsync());
|
||||
|
||||
@@ -35,6 +35,11 @@ public sealed class GatewayTlsBootstrapTests
|
||||
Environment.SetEnvironmentVariable("Kestrel__Endpoints__Test__Url", "https://127.0.0.1:0");
|
||||
Environment.SetEnvironmentVariable(
|
||||
"MxGateway__Tls__SelfSignedCertPath", Path.Combine(certDir, "gw.pfx"));
|
||||
// Starting the host opens the auth SQLite store; the shipped config no longer ships a
|
||||
// SqlitePath and the code default is unwritable on macOS (/usr/share), so pin it to the
|
||||
// writable temp dir. See SEC-33.
|
||||
Environment.SetEnvironmentVariable(
|
||||
"MxGateway__Authentication__SqlitePath", Path.Combine(certDir, "gateway-auth.db"));
|
||||
|
||||
WebApplication app = GatewayApplication.Build([]);
|
||||
await app.StartAsync();
|
||||
@@ -53,6 +58,8 @@ public sealed class GatewayTlsBootstrapTests
|
||||
{
|
||||
Environment.SetEnvironmentVariable("Kestrel__Endpoints__Test__Url", null);
|
||||
Environment.SetEnvironmentVariable("MxGateway__Tls__SelfSignedCertPath", null);
|
||||
Environment.SetEnvironmentVariable("MxGateway__Authentication__SqlitePath", null);
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
Directory.Delete(certDir, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user