Compare commits
5 Commits
50d79ed1c0
...
0083ce3560
| Author | SHA1 | Date | |
|---|---|---|---|
| 0083ce3560 | |||
| 8e12f99432 | |||
| 59805f8551 | |||
| 2a0faeab6f | |||
| 86d48ff8a5 |
+28
-18
@@ -86,32 +86,42 @@
|
||||
<PackageVersion Include="ZB.MOM.WW.Auth.AspNetCore" Version="0.1.5" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Audit" Version="0.1.0" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Theme" Version="0.3.1" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets" Version="0.1.2" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Abstractions" Version="0.1.2" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Ui" Version="0.1.2" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets" Version="0.2.1" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Abstractions" Version="0.2.1" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Ui" Version="0.2.1" />
|
||||
<PackageVersion Include="ZB.MOM.WW.Secrets.Replicator.SqlServer" Version="0.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Targeted NuGet audit suppression (NOT a blanket NuGetAudit=false).
|
||||
|
||||
GHSA-2m69-gcr7-jv3q (NU1903, high) is on the NATIVE library SQLitePCLRaw.lib.e_sqlite3
|
||||
2.1.11, pulled in TRANSITIVELY by Microsoft.Data.Sqlite / EntityFrameworkCore.Sqlite.
|
||||
The current MS-supported package line still references it: Microsoft.Data.Sqlite.Core
|
||||
10.0.9 (latest 10.0.x as of 2026-06) declares `SQLitePCLRaw.core 2.1.11`, so a version
|
||||
bump within the supported line does NOT clear it. The only patched native lib is the
|
||||
SQLitePCLRaw 3.x line — force-overriding the whole SQLitePCLRaw family to 3.x under a
|
||||
Microsoft.Data.Sqlite built and pinned for 2.1.x is unsupported and runtime-risky for the
|
||||
entire site-local SQLite layer (S&F buffer, operation tracking, native-alarm state).
|
||||
|
||||
Risk assessment: SQLite here is internal, local-only storage on site nodes (+ tests); it is
|
||||
not an externally-reachable SQL surface for untrusted input, so practical exposure is low.
|
||||
Suppressing ONLY this advisory (auditing stays ON for everything else) lets the full
|
||||
solution AND the docker in-container restore build cleanly without the blanket
|
||||
`/p:NuGetAudit=false` workaround. REVISIT and remove this suppression once Microsoft.Data.Sqlite
|
||||
ships a 10.0.x that references a patched (3.x) SQLitePCLRaw bundle.
|
||||
FIXED PROPERLY 2026-07-18, replacing a NuGetAuditSuppress that had been masking it. The
|
||||
suppression's stated rationale — "the only patched native lib is the SQLitePCLRaw 3.x line" —
|
||||
was WRONG: 2.1.12 patches this advisory within the 2.1.x line, so no risky force-override of
|
||||
the whole family to 3.x is needed. Verified empirically: with the suppression removed and the
|
||||
pin in place, a forced restore reports no NU1903.
|
||||
|
||||
Why the pin and not a Microsoft.Data.Sqlite bump: bumping Sqlite does NOT clear it (even
|
||||
10.0.10 still references 2.1.11). Pinning the native lib is the fix. Same approach as
|
||||
ZB.MOM.WW.Secrets and ZB.MOM.WW.LocalDb.
|
||||
|
||||
Why this mattered more than it looked: only the Host project resolved a patched 2.1.12 (via
|
||||
ZB.MOM.WW.Auth.ApiKeys). AuditLog, SiteEventLogging, SiteRuntime, StoreAndForward and the
|
||||
test projects — the whole site-local SQLite layer — were still resolving the vulnerable
|
||||
2.1.11, and the suppression hid that everywhere.
|
||||
|
||||
Delivered as an explicit <PackageReference> in each SQLite-consuming project rather than via
|
||||
CentralPackageTransitivePinningEnabled. Enabling transitive pinning globally was tried first
|
||||
and rejected: it makes every centrally-defined version a hard ceiling for transitive
|
||||
resolution too, which immediately demanded bumps to Google.Protobuf (3.29.3->3.34.1),
|
||||
Grpc.Net.Client (2.71.0->2.76.0), Microsoft.Data.SqlClient (6.0.2->6.1.1) and Newtonsoft.Json
|
||||
(13.0.3->13.0.4). Those may well be worth doing, but they are a gRPC/data-access change to a
|
||||
production SCADA platform and belong in their own reviewed commit — not smuggled in under a
|
||||
SQLite security fix.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<NuGetAuditSuppress Include="https://github.com/advisories/GHSA-2m69-gcr7-jv3q" />
|
||||
<PackageVersion Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.1.12" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
in this project, so Akka is an explicit dependency. -->
|
||||
<PackageReference Include="Akka" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
|
||||
@@ -179,7 +179,11 @@ try
|
||||
.GetValue<bool>(nameof(ZB.MOM.WW.ScadaBridge.Security.Auth.AuthDisableLoginOptions.AllowOutsideDevelopment)));
|
||||
builder.Services.AddSecurity(disableLogin);
|
||||
builder.Services.AddCentralUI();
|
||||
builder.Services.AddZbSecrets(builder.Configuration, "Secrets");
|
||||
// Local SQLite store by default; a local store replicating against a shared SQL-Server hub
|
||||
// only when Secrets:Replication:Enabled is true AND a hub connection string is present.
|
||||
// See SecretsRegistration for why the gate exists (eager options validation) and why the
|
||||
// hub connection string can never itself come from the hub.
|
||||
builder.Services.AddScadaBridgeSecrets(builder.Configuration);
|
||||
// Secrets UI authorization: adds the named policies secrets:manage + secrets:reveal
|
||||
// (role-based) consumed by the /admin/secrets page. AddSecretsAuthorization only ADDS
|
||||
// these two policies via Configure<AuthorizationOptions> — it composes additively with
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
using ZB.MOM.WW.Secrets.DependencyInjection;
|
||||
using ZB.MOM.WW.Secrets.Replicator.SqlServer.DependencyInjection;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Host;
|
||||
|
||||
/// <summary>
|
||||
/// Single composition-root entry point for the host container's secret store, shared by the
|
||||
/// central-role registrations in <c>Program.cs</c> and the site-role registrations in
|
||||
/// <see cref="SiteServiceRegistration"/> so both roles wire secrets identically.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Clustered replication is <b>opt-in and off by default</b>. ScadaBridge is hub-and-spoke — one
|
||||
/// central cluster plus N separate site clusters — and hub mode gives each node a LOCAL store that
|
||||
/// syncs bidirectionally with a shared central SQL hub, so a site keeps resolving secrets straight
|
||||
/// through a WAN outage.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The gate is not a style preference. <c>AddZbSecretsSqlServerReplication</c> validates its options
|
||||
/// EAGERLY at registration time, so calling it unconditionally would throw at startup on every node
|
||||
/// where <c>Secrets:SqlServer:ConnectionString</c> is unset — which is every dev box, every docker
|
||||
/// node and every existing deployment. Both the explicit <c>Secrets:Replication:Enabled</c> flag and
|
||||
/// a non-blank connection string are therefore required before the SQL-Server package is touched at
|
||||
/// all; otherwise this is byte-for-byte the plain local-SQLite registration it has always been.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Bootstrap constraint.</b> The hub connection string is itself a secret, and it can never come
|
||||
/// from the hub — a node cannot read the hub to learn how to reach the hub. It must arrive from
|
||||
/// outside the replicated set: an environment variable, or a <c>${secret:}</c> reference that is
|
||||
/// seeded in that node's own LOCAL store (the pre-host expander in <c>Program.cs</c> runs against a
|
||||
/// plain local SQLite store before the host container exists, so such a reference does resolve).
|
||||
/// That expander is deliberately left un-replicated for exactly this reason.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static class SecretsRegistration
|
||||
{
|
||||
/// <summary>Configuration section holding the core secrets options.</summary>
|
||||
public const string SecretsSectionPath = "Secrets";
|
||||
|
||||
/// <summary>Configuration section holding the SQL-Server hub options.</summary>
|
||||
public const string SqlServerSectionPath = "Secrets:SqlServer";
|
||||
|
||||
/// <summary>Configuration key gating clustered replication. Absent or false = off.</summary>
|
||||
public const string ReplicationEnabledKey = "Secrets:Replication:Enabled";
|
||||
|
||||
/// <summary>Configuration key holding the shared SQL-Server hub connection string.</summary>
|
||||
public const string HubConnectionStringKey = "Secrets:SqlServer:ConnectionString";
|
||||
|
||||
/// <summary>
|
||||
/// Registers the host container's secret store: a plain local SQLite store by default, or a
|
||||
/// local store replicating against a shared SQL-Server hub when replication is explicitly
|
||||
/// enabled and a hub connection string is present.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register into.</param>
|
||||
/// <param name="config">Application configuration for options binding.</param>
|
||||
/// <returns>The same <paramref name="services"/> instance, for chaining.</returns>
|
||||
public static IServiceCollection AddScadaBridgeSecrets(
|
||||
this IServiceCollection services,
|
||||
IConfiguration config)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(config);
|
||||
|
||||
var enabled = config.GetValue<bool>(ReplicationEnabledKey);
|
||||
var connectionString = config[HubConnectionStringKey];
|
||||
|
||||
if (enabled && !string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
// Registers the validated hub options, decorates ISecretStore with
|
||||
// ReplicatingSecretStore, and adds the hub-schema migration + bidirectional sweep
|
||||
// hosted services. Calls AddZbSecrets internally — do NOT also call it here.
|
||||
return services.AddZbSecretsSqlServerReplication(
|
||||
config, SecretsSectionPath, SqlServerSectionPath);
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
// Asked for replication but gave nothing to replicate against. Falling back to the
|
||||
// local-only store keeps the node up, but it is silently NOT participating in the
|
||||
// cluster, so say so loudly rather than letting it look healthy.
|
||||
Serilog.Log.Warning(
|
||||
"{Key} is true but {ConnKey} is empty — clustered secret replication is DISABLED "
|
||||
+ "and this node's secrets are local-only. The hub connection string cannot come "
|
||||
+ "from the hub itself; supply it via environment variable or seed it in this "
|
||||
+ "node's local store.",
|
||||
ReplicationEnabledKey,
|
||||
HubConnectionStringKey);
|
||||
}
|
||||
|
||||
return services.AddZbSecrets(config, SecretsSectionPath);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,11 @@ public static class SiteServiceRegistration
|
||||
var siteDbPath = config["ScadaBridge:Database:SiteDbPath"] ?? "site.db";
|
||||
services.AddSiteRuntime($"Data Source={siteDbPath}");
|
||||
services.AddDataConnectionLayer();
|
||||
services.AddZbSecrets(config, "Secrets");
|
||||
// Local SQLite store by default; a local store replicating against a shared SQL-Server hub
|
||||
// only when Secrets:Replication:Enabled is true AND a hub connection string is present.
|
||||
// Hub mode matters most here: a site node keeps resolving secrets from its local store
|
||||
// straight through a WAN outage to central. See SecretsRegistration.
|
||||
services.AddScadaBridgeSecrets(config);
|
||||
// Adapter that surfaces the site id to
|
||||
// StoreAndForwardService through DI WITHOUT introducing a
|
||||
// StoreAndForward → HealthMonitoring project-reference cycle. Must be
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<PackageReference Include="ZB.MOM.WW.Secrets" />
|
||||
<PackageReference Include="ZB.MOM.WW.Secrets.Abstractions" />
|
||||
<PackageReference Include="ZB.MOM.WW.Secrets.Ui" />
|
||||
<PackageReference Include="ZB.MOM.WW.Secrets.Replicator.SqlServer" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -13,7 +13,15 @@
|
||||
"SqlitePath": "scadabridge-secrets.db",
|
||||
"MasterKey": { "Source": "Environment", "EnvVarName": "ZB_SECRETS_MASTER_KEY" },
|
||||
"RunMigrationsOnStartup": true,
|
||||
"ResolveCacheTtl": "00:00:30"
|
||||
"ResolveCacheTtl": "00:00:30",
|
||||
"Replication": { "Enabled": false },
|
||||
"SqlServer": {
|
||||
"ConnectionString": "",
|
||||
"SchemaName": "zbsecrets",
|
||||
"SyncInterval": "00:00:30",
|
||||
"SyncOnStartup": true,
|
||||
"_comment": "Hub connstr must be seeded in the LOCAL store (or supplied via env) - it cannot come from the hub itself. Enable via Secrets:Replication:Enabled."
|
||||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"Using": [
|
||||
|
||||
+3
@@ -10,6 +10,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Akka" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
<PackageReference Include="Akka.Streams" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" />
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Akka" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
-->
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" VersionOverride="6.1.1" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
|
||||
<!--
|
||||
M4 Bundle E (Task E3): Microsoft.Extensions.Configuration.Json,
|
||||
|
||||
+3
@@ -13,6 +13,9 @@
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
||||
+3
@@ -19,6 +19,9 @@
|
||||
-->
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" VersionOverride="6.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" />
|
||||
|
||||
+3
@@ -11,6 +11,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using ZB.MOM.WW.Secrets.Abstractions;
|
||||
using ZB.MOM.WW.Secrets.Replication;
|
||||
using ZB.MOM.WW.Secrets.Sqlite;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Host.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// DI-resolution tests for <see cref="SecretsRegistration.AddScadaBridgeSecrets"/>.
|
||||
/// <para>
|
||||
/// These deliberately BUILD a container and RESOLVE out of it rather than asserting over
|
||||
/// <see cref="ServiceDescriptor"/>s. A decorator registration can look perfectly correct as a
|
||||
/// descriptor list and still throw on the first resolve because the undecorated concrete store it
|
||||
/// depends on is missing — that exact gap shipped once in this library, with every descriptor-level
|
||||
/// unit test green. Resolution is the only assertion that catches it.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Registration must not need a reachable SQL Server: the SQL-Server package validates its options
|
||||
/// eagerly, but validation only checks that the connection string is non-empty and the schema name
|
||||
/// is a legal identifier. Nothing connects until a sweep or a write runs, and these tests do
|
||||
/// neither — so the dummy connection string below never resolves to a real host.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class SecretsReplicationWiringTests
|
||||
{
|
||||
// Syntactically valid, deliberately unreachable. Never connected to by these tests.
|
||||
private const string DummyConnectionString =
|
||||
"Server=unused;Database=x;Integrated Security=true;";
|
||||
|
||||
private static IConfiguration BuildConfig(params (string Key, string Value)[] overrides)
|
||||
{
|
||||
var settings = new Dictionary<string, string?>
|
||||
{
|
||||
["Secrets:SqlitePath"] = Path.Combine(Path.GetTempPath(), "sb-secrets-wiring-test.db"),
|
||||
["Secrets:MasterKey:Source"] = "Environment",
|
||||
["Secrets:MasterKey:EnvVarName"] = "ZB_SECRETS_MASTER_KEY",
|
||||
["Secrets:RunMigrationsOnStartup"] = "false",
|
||||
};
|
||||
|
||||
foreach ((string key, string value) in overrides)
|
||||
{
|
||||
settings[key] = value;
|
||||
}
|
||||
|
||||
return new ConfigurationBuilder().AddInMemoryCollection(settings).Build();
|
||||
}
|
||||
|
||||
private static ServiceProvider BuildProvider(IConfiguration config)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddScadaBridgeSecrets(config);
|
||||
return services.BuildServiceProvider(validateScopes: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Any hosted service contributed by the SQL-Server replicator package. Asserted by assembly
|
||||
/// rather than by type because <c>SqlServerHubMigrationHostedService</c> is internal to it.
|
||||
/// </summary>
|
||||
private static bool IsReplicatorHostedService(ServiceDescriptor descriptor) =>
|
||||
descriptor.ServiceType == typeof(IHostedService)
|
||||
&& descriptor.ImplementationType?.Assembly.GetName().Name
|
||||
== "ZB.MOM.WW.Secrets.Replicator.SqlServer";
|
||||
|
||||
[Fact]
|
||||
public void ReplicationDisabledByDefault_ResolvesPlainSqliteStore()
|
||||
{
|
||||
using ServiceProvider provider = BuildProvider(BuildConfig());
|
||||
|
||||
var store = provider.GetRequiredService<ISecretStore>();
|
||||
|
||||
Assert.IsType<SqliteSecretStore>(store);
|
||||
Assert.IsNotType<ReplicatingSecretStore>(store);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplicationDisabledByDefault_RegistersNoReplicatorHostedServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddScadaBridgeSecrets(BuildConfig());
|
||||
|
||||
Assert.DoesNotContain(services, IsReplicatorHostedService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplicationEnabledWithoutConnectionString_StaysOnPlainSqliteStore()
|
||||
{
|
||||
// The gate requires BOTH the flag and a connection string. Enabling the flag alone must not
|
||||
// reach the SQL-Server package, whose options validation would throw at registration and
|
||||
// take the whole node down at startup.
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
|
||||
IConfiguration config = BuildConfig(("Secrets:Replication:Enabled", "true"));
|
||||
|
||||
services.AddScadaBridgeSecrets(config);
|
||||
|
||||
using ServiceProvider provider = services.BuildServiceProvider(validateScopes: true);
|
||||
|
||||
Assert.IsType<SqliteSecretStore>(provider.GetRequiredService<ISecretStore>());
|
||||
Assert.DoesNotContain(services, IsReplicatorHostedService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplicationEnabledWithConnectionString_ResolvesReplicatingStore()
|
||||
{
|
||||
IConfiguration config = BuildConfig(
|
||||
("Secrets:Replication:Enabled", "true"),
|
||||
("Secrets:SqlServer:ConnectionString", DummyConnectionString));
|
||||
|
||||
using ServiceProvider provider = BuildProvider(config);
|
||||
|
||||
Assert.IsType<ReplicatingSecretStore>(provider.GetRequiredService<ISecretStore>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplicationEnabledWithConnectionString_UndecoratedLocalStoreAlsoResolves()
|
||||
{
|
||||
// ReplicatingSecretStore is constructed from the CONCRETE SqliteSecretStore, not from
|
||||
// ISecretStore (which is the decorator itself — that would recurse). If the concrete
|
||||
// registration were ever dropped, the decorator would fail on first resolve. This is the
|
||||
// exact defect that shipped once, so it gets its own test.
|
||||
IConfiguration config = BuildConfig(
|
||||
("Secrets:Replication:Enabled", "true"),
|
||||
("Secrets:SqlServer:ConnectionString", DummyConnectionString));
|
||||
|
||||
using ServiceProvider provider = BuildProvider(config);
|
||||
|
||||
Assert.NotNull(provider.GetRequiredService<SqliteSecretStore>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplicationEnabledWithConnectionString_RegistersReplicatorHostedServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
|
||||
IConfiguration config = BuildConfig(
|
||||
("Secrets:Replication:Enabled", "true"),
|
||||
("Secrets:SqlServer:ConnectionString", DummyConnectionString));
|
||||
|
||||
services.AddScadaBridgeSecrets(config);
|
||||
|
||||
// Both the hub-schema migration service and the bidirectional sweep.
|
||||
Assert.Equal(2, services.Count(IsReplicatorHostedService));
|
||||
}
|
||||
}
|
||||
+3
@@ -16,6 +16,9 @@
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
||||
+3
@@ -19,6 +19,9 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
|
||||
+3
@@ -11,6 +11,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<!-- Microsoft.Extensions.* and Microsoft.AspNetCore.Authorization are provided
|
||||
by the Microsoft.AspNetCore.App shared framework referenced above (added so
|
||||
HttpAuditActorAccessorTests can build DefaultHttpContext), so they are no
|
||||
|
||||
+3
@@ -12,6 +12,9 @@
|
||||
<PackageReference Include="Akka.TestKit.Xunit2" />
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
|
||||
+3
@@ -12,6 +12,9 @@
|
||||
<PackageReference Include="Akka.TestKit.Xunit2" />
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
|
||||
+3
@@ -18,6 +18,9 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<!-- Security pin: GHSA-2m69-gcr7-jv3q. Microsoft.Data.Sqlite pulls the
|
||||
vulnerable native 2.1.11; 2.1.12 patches it. Bumping Sqlite does not help. -->
|
||||
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
||||
Reference in New Issue
Block a user