You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Managed role provisioning re-sends every role's plaintext password each start; a raising DO block puts all three in the store log, which viewer can read #3910
Managed-mode role provisioning re-sends every role's plaintext password on every service start: CREATE ROLE ... PASSWORD '<pw>' inside the guard's DO block, then ALTER ROLE admin/viewer/mcp LOGIN NOSUPERUSER PASSWORD '<pw>' (Darling/PerformanceMonitor.Darling.Service/DarlingManagedRoles.cs, sections 1 and 1b). Any surface that records statement text can therefore capture the live credentials of all three login roles.
This predates #3899 and was surfaced by the adversarial review of its PR (#3904). That PR shields its own surface (pg_stat_statements.track_utility = off, a role-DDL-refusing reader, a scrub), but the root cause is the plaintext on the wire. Every other text-capturing surface still sees it.
The concrete exposure today
If an unmarked role of the same name exists (for example, an operator created their own mcp), the DO block raises on every start.
With the default log_min_error_statement = error, PostgreSQL then writes STATEMENT: plus the whole DO text, carrying all three live passwords, to the store's own log.
StoreLogClassifier retains that ERROR under unclassified, continuation lines included. StoreLogSweep keeps it for 400 days.
A viewer seat can read it through get_store_log or /api/read/get_store_log. That is enough to log in as admin, the config writer.
Any operator-enabled statement logging on the owner role (log_statement = 'ddl', log_min_duration_statement = 0, auto_explain) produces the same capture without an error.
Fix shape
Send a SCRAM-SHA-256 verifier, not the password. PostgreSQL stores a pre-hashed SCRAM-SHA-256$<iter>:<salt>$<StoredKey>:<ServerKey> value as-is when it is sent as the PASSWORD string. The verifier can be computed client-side with PBKDF2-HMAC-SHA256 and the RFC 5802/7677 key derivation; Rfc2898DeriveBytes.Pbkdf2 covers the expensive half. The passwords are 32 random alphanumerics (~190 bits), so a leaked verifier is not brute-forceable, and every capture surface then carries a hash instead of a credential.
Skip an unchanged re-assert. Provisioning runs as a superuser and can read pg_authid.rolpassword, so it can check whether the stored verifier already matches the credential file, and only re-send the password when it does not.
Activity
The store's statement statistics survive their review: ALTER SYSTEM a…
Role provisioning sends SCRAM-SHA-256 verifiers, never a password, an…
erikdarlingdata commentedon Sep 22, 2026
Closed by the watcher: delivered in PR #3940, merged to dev.