Report SecKeychainItemDelete failures via try_delete() - #258
Open
acoliver wants to merge 1 commit into
Open
Conversation
SecKeychainItem::delete() discarded the OSStatus, so a failed deletion was indistinguishable from a successful one. The sibling set_password() already checks its status with cvt(), and the crate's other two delete paths (ItemSearchOptions::delete, delete_generic_password_options) do too. Adds try_delete() returning Result<()> and deprecates delete() in favour of it, rather than changing delete()'s signature, so this can ship without a major.
This was referenced Aug 5, 2026
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.
Fixes #256.
SecKeychainItem::delete()calledSecKeychainItemDeleteand threw away the returnedOSStatus, so a deletion the OS refused was indistinguishable from one that succeeded. The status is available —security-framework-sysdeclaresSecKeychainItemDelete(itemRef: SecKeychainItemRef) -> OSStatus— it was simply dropped.It looks like an oversight rather than a decision, because the method directly above it in the same
implblock does the right thing with the same helper:and the crate's other two deletion paths check theirs too —
ItemSearchOptions::deleteanddelete_generic_password_options, bothcvt(unsafe { SecItemDelete(...) }).The change
Adds
try_delete(self) -> Result<()>and deprecatesdelete()in favour of it:Why deprecate rather than change
delete()'s signatureChanging
delete(self)to returnResult<()>is the tidier end state, but it is a semver break, and the crate is on 3.7 with no major in flight. Deprecating matches what this repo already does for API evolution —#[deprecated(note = "use set_key_type()")],#[deprecated(note = "Use SecKey::new")], and the recent "Mark deprecated" / "Deprecations" commits — and lets the fix ship in a 3.x.That timing matters here beyond tidiness:
apple-native-keyring-storecannot report failed deletions at all until this lands, becauseSecKeychainexposes no other status-returning way to delete from a specific keychain (PasswordOptionscan select the data-protection keychain but not a file-basedSecKeychain). I have the companion fix ready and it is blocked on this.If you would rather just change
delete()in a 4.0, say so and I will convert this — it is a two-line change and I have no attachment to the deprecation.try_deletefollows the stdtry_convention for a fallible variant. Happy to rename if you prefer something else; there was no existingtry_/_checkedprecedent in the crate to follow.Tests
Added
try_delete_reports_failure_temp, in the existingmod testin the same file and using the existingtemp_keychain_setup/temp_keychain_teardownhelpers. It takes two handles to the same item, deletes through the first, and asserts the second reports an error rather than silently succeeding.I confirmed it is a real regression test rather than a tautology: reverting
try_deleteto discard the status makes it fail, and restoring makes it pass.The five existing
item.delete()call sites in the test module, plus one iniostest/tests/ios_macos.rs, are updated totry_delete().expect(...). Those now assert something they previously could not.Verification (aarch64-apple-darwin)
cargo test -p security-framework --lib os::macos::passwords— 8 passed, 0 failedcargo test -p security-framework --lib— 108 passed, 6 failed; the 6 are the pre-existingauthorization::tests::*failures, identical on an unmodified checkout (107 passed / same 6 failed — the delta is my added test)cargo clippy -p security-framework --all-targets— 111 warnings, byte-identical count to the unmodified baseline, and none point at the changed linesmain.yml