Repository navigation
Fix fail-soft secret listing decryption - #652
akashjadon04 wants to merge 1 commit into
Conversation
|
@claude Please review this assigned PR for correctness and compare its implementation/coverage with #621. In particular check its migration timestamp against merged #647 and its broad error handler versus systemic key/configuration failures. This is a single review request; consolidation is being evaluated with contributor credit and retained tests. |
…ression coverage Preserve the additional SQL listing cases from PR risa-labs-inc#652 by Akash Jadon. Original resilient listing implementation by Rushikeshiitb remains in preceding commits. Co-authored-by: Akash Jadon <contact@evolnex.digital>
|
Claude finished @kshivang's task in 6m 42s —— View job Review: fail-soft secret listing decryption
1. Blocker: the migration version collides with one already on
|
|
@akashjadon04 Your change correctly preserves the installed desktop password contract and adds useful assertions for valid search/shared passwords, recovery-code arrays, direct-share attribution and helper access denial. I compared the actual code: all three listing RPC bodies are equivalent to #621 after normalization; the desktop mixed-row case is already equivalently covered there. I retained your complete 17-assertion SQL suite byte-for-byte in #621 ( Replacement: #621 . It also fixes the shared catch-all handler problem: missing/malformed/empty keys and privilege/missing-function/internal errors propagate instead of presenting a successful blank page. It uses migration version 20260915000000, avoiding this PR's 20260914000000 collision with merged #647, and retains merged #651's migration repairs. No live database was changed. Closing this PR as a duplicate with your implementation intent and additional tests preserved in the replacement. This is consolidation, not a claim that your useful contribution is unnecessary. #621 still requires its current-head platform CI and requested Claude review before a dev merge; older green checks are not being reused. Exactly one Claude review was requested here; no repeated review loop is needed for the superseded implementation. |
- Widen try_decrypt_text handler to the data_exception / external_routine_invocation_exception condition names (explicit class match, covers 22P05); systemic faults still propagate. - Correct the migration header runbook: signal is an empty password plus the SQLSTATE-only server warning, never a NULL after the RPC COALESCE. - Note password_encrypted stays mapped to strict decrypt_text in the rotation script on purpose (rotation must fail closed). - Cross-reference the two overlapping pgTAP suites; credit Akash Jadon (risa-labs-inc#652). - Add a SecretDecodingTest case asserting a null password fails to decode, which is why the listing RPCs COALESCE a failed decryption to an empty string.
…ailures Includes desktop regression coverage from risa-labs-inc#621 by Rushikeshiitb and review repairs by kshivang; related independent risa-labs-inc#652 work by akashjadon04 informed the review.
…668) * fix(secrets): make the secret listing RPCs fail soft on one bad row BossConsole#620: get_user_secrets, search_user_secrets and get_user_secrets_with_shared each decrypt password_encrypted and recovery_codes_encrypted with a bare call to decrypt_text inside a set-returning query. decrypt_text raises on an undecryptable value - a wrong key after a partial key rotation, storage corruption, or an HMAC/padding mismatch - and one raised row aborts the whole query, so the caller sees every one of their secrets fail to load rather than just the bad one. twofa_secret already reads through safe_decrypt_twofa_secret (NULL on failure). recovery_codes_encrypted already had a safe wrapper too - safe_decrypt_recovery_codes, whose own doc comment says "used internally by get_user_secrets functions" - but whichever later migration touched these RPCs stopped calling it and fell back to raw decrypt_text, so it sat unused. password_encrypted never had a safe wrapper at all. Adds try_decrypt_text(text), mirroring safe_decrypt_recovery_codes's BEGIN/EXCEPTION WHEN OTHERS THEN RETURN NULL shape for a scalar result, locked down the same way safe_decrypt_twofa_secret is (no client role gets EXECUTE). Routes the password read in all three RPCs through it and the recovery-codes read back through the existing safe wrapper. One corrupt row now surfaces as a blanked field - password NULL, recovery_codes [] - while every other secret in the page still loads. Deliberately not named safe_decrypt_*: ops/rotate_master_encryption_key.sql's re-encrypt loop treats every function matching that name pattern as a mapped bespoke-format adapter and refuses to run if one is unmapped. try_decrypt_text wraps the same plain envelope that script already re-encrypts directly, so it must stay outside that sweep. Scoped to the three user-facing listing RPCs only; the operator-run backfill/rotation paths keep using raw decrypt_text and should still fail closed. * Fix installed-client compatibility and preserve systemic decryption failures Includes desktop regression coverage from #621 by Rushikeshiitb and review repairs by kshivang; related independent #652 work by akashjadon04 informed the review. --------- Co-authored-by: Shivang <shivang.iitk@gmail.com>
…668) * fix(secrets): make the secret listing RPCs fail soft on one bad row BossConsole#620: get_user_secrets, search_user_secrets and get_user_secrets_with_shared each decrypt password_encrypted and recovery_codes_encrypted with a bare call to decrypt_text inside a set-returning query. decrypt_text raises on an undecryptable value - a wrong key after a partial key rotation, storage corruption, or an HMAC/padding mismatch - and one raised row aborts the whole query, so the caller sees every one of their secrets fail to load rather than just the bad one. twofa_secret already reads through safe_decrypt_twofa_secret (NULL on failure). recovery_codes_encrypted already had a safe wrapper too - safe_decrypt_recovery_codes, whose own doc comment says "used internally by get_user_secrets functions" - but whichever later migration touched these RPCs stopped calling it and fell back to raw decrypt_text, so it sat unused. password_encrypted never had a safe wrapper at all. Adds try_decrypt_text(text), mirroring safe_decrypt_recovery_codes's BEGIN/EXCEPTION WHEN OTHERS THEN RETURN NULL shape for a scalar result, locked down the same way safe_decrypt_twofa_secret is (no client role gets EXECUTE). Routes the password read in all three RPCs through it and the recovery-codes read back through the existing safe wrapper. One corrupt row now surfaces as a blanked field - password NULL, recovery_codes [] - while every other secret in the page still loads. Deliberately not named safe_decrypt_*: ops/rotate_master_encryption_key.sql's re-encrypt loop treats every function matching that name pattern as a mapped bespoke-format adapter and refuses to run if one is unmapped. try_decrypt_text wraps the same plain envelope that script already re-encrypts directly, so it must stay outside that sweep. Scoped to the three user-facing listing RPCs only; the operator-run backfill/rotation paths keep using raw decrypt_text and should still fail closed. * Fix installed-client compatibility and preserve systemic decryption failures Includes desktop regression coverage from #621 by Rushikeshiitb and review repairs by kshivang; related independent #652 work by akashjadon04 informed the review. --------- Co-authored-by: Shivang <shivang.iitk@gmail.com>
…668) * fix(secrets): make the secret listing RPCs fail soft on one bad row BossConsole#620: get_user_secrets, search_user_secrets and get_user_secrets_with_shared each decrypt password_encrypted and recovery_codes_encrypted with a bare call to decrypt_text inside a set-returning query. decrypt_text raises on an undecryptable value - a wrong key after a partial key rotation, storage corruption, or an HMAC/padding mismatch - and one raised row aborts the whole query, so the caller sees every one of their secrets fail to load rather than just the bad one. twofa_secret already reads through safe_decrypt_twofa_secret (NULL on failure). recovery_codes_encrypted already had a safe wrapper too - safe_decrypt_recovery_codes, whose own doc comment says "used internally by get_user_secrets functions" - but whichever later migration touched these RPCs stopped calling it and fell back to raw decrypt_text, so it sat unused. password_encrypted never had a safe wrapper at all. Adds try_decrypt_text(text), mirroring safe_decrypt_recovery_codes's BEGIN/EXCEPTION WHEN OTHERS THEN RETURN NULL shape for a scalar result, locked down the same way safe_decrypt_twofa_secret is (no client role gets EXECUTE). Routes the password read in all three RPCs through it and the recovery-codes read back through the existing safe wrapper. One corrupt row now surfaces as a blanked field - password NULL, recovery_codes [] - while every other secret in the page still loads. Deliberately not named safe_decrypt_*: ops/rotate_master_encryption_key.sql's re-encrypt loop treats every function matching that name pattern as a mapped bespoke-format adapter and refuses to run if one is unmapped. try_decrypt_text wraps the same plain envelope that script already re-encrypts directly, so it must stay outside that sweep. Scoped to the three user-facing listing RPCs only; the operator-run backfill/rotation paths keep using raw decrypt_text and should still fail closed. * Fix installed-client compatibility and preserve systemic decryption failures Includes desktop regression coverage from #621 by Rushikeshiitb and review repairs by kshivang; related independent #652 work by akashjadon04 informed the review. --------- Co-authored-by: Shivang <shivang.iitk@gmail.com>
Fixes #620
Summary
Verification
git diff --checkReview focus
Please verify the fail-soft behavior remains scoped to listing reads and that no secret ciphertext or error details are exposed in logs.