Repository navigation
Darling: close the BYO provision-roles.sql viewer column-ACL drift - #1639
Merged
Merged
Conversation
…1639) The fail-closed viewer column ACL (#1262) is authored once in C# (DarlingManagedRoles.ViewerRestrictedConfigTables) and applied two ways. Managed mode GENERATES the REVOKE-then-GRANT-columns carve from that list on every service start, so it is correct by construction. BYO mode has no service to generate anything: the operator runs Darling/tools/provision-roles.sql by hand, and its three GRANT SELECT (...) lists are a hand copy of the same columns. Nothing pinned the two together and they drifted: config_monitored_servers MISSING alert_delivery_mode_override (#1551) config_notification MISSING generic_body_template (#1506) config_notification MISSING generic_proxy (#1506) config_command in sync Because the carve is fail-CLOSED, an unlisted column is invisible, so on a BYO store a read-only viewer seat took a bare SQLSTATE 42501 on the two projections written specifically FOR it: MonitoredServersSelectSql (the Manage Servers list + sidebar reconcile) and NotificationSelectNoSecretSql (the Settings prefill, where one throw blanks every later section). Managed deployments were never affected. Only the three drifted columns are added - no grant is widened. The real defect is the unguarded hand mirror, so add an UNGATED ProvisionRolesAclDriftTests: it parses the SHIPPED .sql (copied beside the test binary by the csproj, so there is no stale second copy and no hardcoded repo path), extracts every GRANT SELECT (columns) ON schema.table TO role, and asserts set-equality against the C# list in both directions, plus the paired REVOKE and its ordering, that no secret column is ever granted, and that the script still provisions admin+viewer only (BYO deliberately has no mcp role). A meta-test mutates the real file three ways and asserts the same comparison reports each, so a parser that silently matched nothing cannot pass as "no drift". Darling.Tests: 2958 passed, 0 failed, 147 gated-live skipped (DARLING_TEST_PG cleared). Verified by hand that removing either restored column reintroduces a failing test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s-acl-drift # Conflicts: # CHANGELOG.md
erikdarlingdata
added a commit
that referenced
this pull request
Aug 31, 2026
….sql (#2138) The #1639 drift ratchet working as designed: the operator-facing provisioning script's GRANT SELECT column carve is a hand mirror of DarlingManagedRoles.ViewerRestrictedConfigTables, and ProvisionRolesAclDriftTests parses the script and asserts set-equality - so classifying the column in C# alone failed both CI jobs until the script named it too. Non-secret, exactly like is_enabled beside it. Comment kept parenthesis-free inside the GRANT list on purpose: the parser strips -- comments before matching, but the paren-scan that finds the list's end does not need the extra test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgLaaxFenZFZs1SntKQDEg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
The fail-closed viewer column ACL (#1262) is authored once in C# —
DarlingManagedRoles.ViewerRestrictedConfigTables— and applied two ways:REVOKE-then-GRANT SELECT (columns)carve from that list on every service start (BuildViewerColumnAclSql), so it is correct by construction.Darling/tools/provision-roles.sqlby hand, and that file's threeGRANT SELECT (...)lists are a hand copy of the same columns.Nothing pinned the two together, and they drifted:
NonSecretColumnsprovision-roles.sqlconfig_monitored_serversalert_delivery_mode_override(added to C# by #1551)config_commandconfig_notificationgeneric_body_template,generic_proxy(added to C# by #1506)The
mcprole is a deliberate asymmetry, not drift: managed mode applies the identical carve to a third role viaBuildViewerColumnAclSql(config, "mcp"), and BYO correctly provisions nomcprole at all (the network MCP endpoint is managed-mode-only). That is now pinned as intentional so nobody "closes" it with a half-provisioned role.Impact on BYO deployments
The carve is fail-CLOSED by design —
viewer's table-wide SELECT is revoked and only the enumerated columns re-granted — so an unlisted column is invisible, not merely unlisted. On a bring-your-own-Postgres store a read-onlyviewerseat therefore took a bare SQLSTATE42501on the two projections written specifically for it:ViewerDataService.MonitoredServersSelectSql— the Manage Servers list, which also drives the sidebar reconcile.ViewerDataService.NotificationSelectNoSecretSql— the Settings prefill, where (per its own comment) one such throw blanks every later section.Quiet in exactly the wrong direction: the columns exist, the script runs clean,
psqlas the owner sees everything, and only a read from the least-privilege role ever notices. Managed deployments were never affected.The fix
GRANT SELECT (...)lists now match the C# source of truth exactly. Only the three drifted columns were added — no grant is widened, nothing speculative (nogroup_id), and the secret sets are untouched. The step-2b comment also now names the source of truth and the guard.Darling/Darling.Tests/ProvisionRolesAclDriftTests.cs(5 tests, no database required) closes the actual defect — an unguarded hand mirror. It parses the shipped.sql, copied beside the test binary by the csproj (Link="Fixtures\provision-roles.sql"), so there is no second copy to go stale and no hardcoded repo path, and asserts:ViewerRestrictedConfigTables— a column on either side that the other lacks fails;REVOKEexists and is ordered after the blanket schema grant and before the column grant (without that ordering the carve is a silent no-op and the secrets stay readable);admin+vieweronly, never a half-provisionedmcprole.ParsedViewerAcl_Comparison_FailsOnAnInjectedDriftmutates the real file three ways — drop a column, add one, delete a whole carve — and asserts the same comparison reports each, because every way this test could silently stop working (a reformattedGRANT, a renamed file, a regex matching nothing) looks identical to "no drift".Testing
DARLING_TEST_PG= dotnet test Darling/Darling.Tests/Darling.Tests.csproj -c Debug --nologo— 2958 passed, 0 failed, 147 skipped (gated-live,DARLING_TEST_PGcleared; no database was stood up).Guard verified by hand, not just by the meta-test: removing
alert_delivery_mode_overridefrom the.sqlfailed 2 tests, restoring it went green, then removinggeneric_proxyfailed the same 2, and restoring it went green again — so both repaired tables are genuinely covered.🤖 Generated with Claude Code