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
This commit is contained in:
Joseph Doherty
2026-10-22 15:47:18 -04:00
parent 16f401b9e7
commit 91b7e9541b
2 changed files with 29 additions and 1 deletions
@@ -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.");
}
}
@@ -25,6 +25,28 @@ public sealed class SqlDriverFactoryTests
{
// ---- the plan's headline legs ----
/// <summary>
/// <b>R06:S-8.</b> A malformed config used to quote System.Text.Json's message — which echoes the
/// offending token — into the exception that becomes <c>InitializeFailed</c> → health
/// <c>LastError</c> → <c>/hosts</c>. 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.
/// </summary>
[Fact]
public void CreateInstance_malformedJson_withholdsTheParserText()
{
const string secret = "hunter2-do-not-log";
var ex = Should.Throw<InvalidOperationException>(() =>
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<InvalidOperationException>(() =>