diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/ExternalSystems.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/ExternalSystems.razor
index 83d75ff1..11643ba8 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/ExternalSystems.razor
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/ExternalSystems.razor
@@ -245,7 +245,8 @@
-
@dc.ConnectionString
+ @* Connection strings carry credentials — never rendered here (not even for admins). Shown only on the create/edit form. *@
+ Connection string hidden — edit to view
Max @dc.MaxRetries retries
Delay @dc.RetryDelay.TotalSeconds s
diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Design/ExternalSystemsConnectionStringHidingTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Design/ExternalSystemsConnectionStringHidingTests.cs
new file mode 100644
index 00000000..f4ee3b0c
--- /dev/null
+++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Design/ExternalSystemsConnectionStringHidingTests.cs
@@ -0,0 +1,73 @@
+using System.Security.Claims;
+using Bunit;
+using Microsoft.AspNetCore.Components.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+using NSubstitute;
+using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
+using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems;
+using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
+using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
+using ZB.MOM.WW.ScadaBridge.Security;
+using ExternalSystems = ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Design.ExternalSystems;
+
+namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Design;
+
+///
+/// Security regression guard: the Integration Definitions list (Database Connections
+/// tab) must NEVER render a connection string — it carries credentials. Connection
+/// strings are shown only on the create/edit form. This test seeds a connection whose
+/// string contains a sentinel password and asserts it is absent from the rendered
+/// markup of the list (so it never reaches the browser DOM, even for an admin/designer).
+///
+public class ExternalSystemsConnectionStringHidingTests : BunitContext
+{
+ private readonly IExternalSystemRepository _repo = Substitute.For
();
+ private readonly IInboundApiRepository _inbound = Substitute.For();
+ private readonly IDialogService _dialog = Substitute.For();
+
+ public ExternalSystemsConnectionStringHidingTests()
+ {
+ Services.AddSingleton(_repo);
+ Services.AddSingleton(_inbound);
+ Services.AddSingleton(_dialog);
+
+ var claims = new[]
+ {
+ new Claim(JwtTokenService.UsernameClaimType, "tester"),
+ new Claim(JwtTokenService.RoleClaimType, "Designer"),
+ };
+ var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
+ Services.AddSingleton(new TestAuthStateProvider(user));
+ Services.AddAuthorizationCore();
+ AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
+ }
+
+ [Fact]
+ public void DbConnectionsTab_DoesNotRenderConnectionString()
+ {
+ const string secret = "Server=db.internal;User Id=sa;Password=SuperSecret#123;";
+
+ _repo.GetAllExternalSystemsAsync(Arg.Any())
+ .Returns((IReadOnlyList)new List());
+ _repo.GetAllDatabaseConnectionsAsync(Arg.Any())
+ .Returns((IReadOnlyList)new List
+ {
+ new("AppDb", secret) { Id = 1 },
+ });
+ _inbound.GetAllApiMethodsAsync(Arg.Any())
+ .Returns((IReadOnlyList)new List());
+
+ var cut = Render();
+
+ // Wait for the async load to complete (tabs render only once _loading == false),
+ // then activate the Database Connections tab so its cards render.
+ cut.WaitForAssertion(() =>
+ Assert.Contains(cut.FindAll("button.nav-link"), b => b.TextContent.Contains("Database Connections")));
+ cut.FindAll("button.nav-link").First(b => b.TextContent.Contains("Database Connections")).Click();
+ cut.WaitForAssertion(() => Assert.Contains("AppDb", cut.Markup));
+
+ // The card renders (name visible) but the credential-bearing connection string must not.
+ Assert.DoesNotContain(secret, cut.Markup);
+ Assert.DoesNotContain("SuperSecret#123", cut.Markup);
+ }
+}