From 91b7e9541b1d230907475396006328123af7790d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 22 Oct 2026 15:47:18 -0400 Subject: [PATCH] fix(sql): withhold the JSON parser's text on a malformed driver config (R06:S-8) The factory quoted System.Text.Json's message (and attached the JsonException) into the failure that reaches InitializeFailed -> health LastError -> /hosts; a pasted connection-string literal is exactly the malformed text that lands there. The message now names the instance and the line/byte position only, as the probe and browser already do. Refs #744 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT --- .../SqlDriverFactoryExtensions.cs | 8 ++++++- .../SqlDriverFactoryTests.cs | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriverFactoryExtensions.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriverFactoryExtensions.cs index a53e4390..3bb727e5 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriverFactoryExtensions.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriverFactoryExtensions.cs @@ -194,8 +194,14 @@ public static class SqlDriverFactoryExtensions } catch (JsonException ex) { + // The parser's message AND the exception carrying it are withheld (R06:S-8), matching + // SqlDriverProbe and SqlDriverBrowser: System.Text.Json quotes the offending token, and a pasted + // connection-string literal is exactly the malformed text that lands here. This message becomes + // InitializeFailed -> health LastError -> /hosts. Line/byte position carry no content, so they stay. throw new InvalidOperationException( - $"Sql driver config for '{driverInstanceId}' is not valid JSON: {ex.Message}", ex); + $"Sql driver config for '{driverInstanceId}' is not valid JSON (line {ex.LineNumber + 1}, byte " + + $"{ex.BytePositionInLine}). The parser's message is withheld because the malformed text may " + + "carry a connection string."); } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/SqlDriverFactoryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/SqlDriverFactoryTests.cs index 65d6a281..9d1265de 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/SqlDriverFactoryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/SqlDriverFactoryTests.cs @@ -25,6 +25,28 @@ public sealed class SqlDriverFactoryTests { // ---- the plan's headline legs ---- + /// + /// R06:S-8. A malformed config used to quote System.Text.Json's message — which echoes the + /// offending token — into the exception that becomes InitializeFailed → health + /// LastError → /hosts. A pasted connection string is exactly the malformed text that + /// reaches here, so the parser text (and the inner exception carrying it) is withheld, as the probe and + /// browser already do. The message still names the instance and says what is wrong. + /// + [Fact] + public void CreateInstance_malformedJson_withholdsTheParserText() + { + const string secret = "hunter2-do-not-log"; + var ex = Should.Throw(() => + SqlDriverFactoryExtensions.CreateInstance( + "d1", $$"""{"provider":"SqlServer","connectionStringRef": Password={{secret}} }""", null)); + + ex.Message.ShouldContain("d1"); + ex.Message.ShouldContain("not valid JSON"); + ex.Message.ShouldNotContain(secret); + ex.Message.ShouldNotContain("Password"); + ex.InnerException.ShouldBeNull(); + } + [Fact] public void CreateInstance_missingConnectionStringRef_throwsActionable() => Should.Throw(() =>