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
Hardening and test-coverage findings in keep-mobile.
Read this before picking anything up. The original list held twenty-nine entries. A manual pass on 2026-07-30 read the code for each: eleven no longer described the tree, two were the same defect filed twice, and three of the wrong ones were errors I introduced during that pass itself, from a search that reports absence for anything it cannot match. Everything below was checked by reading the code rather than searching for it.
Entries here name where the code looks wrong. They are not instructions. Read the code before acting on one, and if it does not say what the entry says, correct the entry.
Open
No hook validates a signing request on mobile — Installing hooks replaces the set rather than composing with it, so this crate's hooks are the entire pre-sign policy and the ones that would refuse a raw or unstructured request never run. This is the reason the request label reaching the approval prompt is unvalidated, and requiring a structured body would also make the verified case the norm rather than the exception. The largest item on this list.
Approval prompt still shows no amount or destination — The structured metadata is hardcoded empty on the co-sign path. Those fields can be read from the verified body rather than from anything the requester asserts, which would turn an approval from a message-type label into the actual spend being approved. The label and the requesting peer now cross the boundary correctly and the label is marked verified or claimed; this is what remains.
No cert pinning on the bunker relay path — Retained with a correction: an earlier version of this entry, and my own audit comment repeating it, said pinning was absent. It is present. The bunker start path pins its relays through the same helper the initialization path uses and holds the pin lock across the call. What is worth checking is whether every relay path does, not this one.
get_active_share hides storage failures — Returns Option, so a storage error is indistinguishable from no active share. The trait method it calls returns Option too, so closing this is an FFI contract change and needs the Android side in lockstep.
frost_generate and frost_split have no rate limit or concurrency guard — Key generation runs unguarded.
FROST peer signing path has no per-peer limit — Bounded only by a global pending-request cap, so one peer can fill the queue.
Audit entry details carries no guidance against secrets — Bounded in length, with nothing saying what may go in it.
clear_entries takes only a confirmation string — No authorization beyond a magic word, and the trait is foreign-implemented.
Plaintext mnemonic persisted without application-layer encryption — Two entries described this; it is one. Stored as bytes to secure storage relying on the platform layer alone.
Seed words cross the FFI as an unzeroizable string — Two entries described this; it is one. The foreign caller cannot wipe what it receives.
Mnemonic is lost on restore from backup — The backup share carries no mnemonic and restore hardcodes none, so seed words do not survive a backup and restore cycle.
Deserialized audit entries are exported without re-verifying the chain — Export converts loaded entries without checking linkage.
Audit verify returns early on length before the constant-time compare — The comparison itself is constant time and the lengths are structural rather than secret, so this is a documentation gap rather than a leak. Worth a note saying so, since the next reader will otherwise either "fix" the lengths pointlessly or assume the compare is unsafe.
Audit chain can be dropped or truncated by a hostile storage implementation — The storage trait is foreign-implemented and entries are not individually signed.
do_start_bunker branch selection untested — The local versus network branch has no test.
Removed as already fixed
The now-milliseconds truncation is gone with the function that held it. The NIP-46 error mapper handles user-rejected and capacity-exceeded explicitly rather than letting them fall through. The From<KeepError> implementation that was inconsistent with it no longer exists anywhere in the crate. The rate-limit map has a hard cap with a test that pushes past it. Callback URL schemes are an explicit allowlist, with a comment recording why domain allowlisting is not meaningful when the callback comes from the calling app. The x-only prefix assumption is documented at the one place it occurs, explaining that an off-curve value fails later at the point of use before any secret is derived; two entries described that single line. Node initialization degrades to in-memory sessions rather than failing. The restore path keeps the recorded backed-up state instead of marking everything backed up. Audit timestamps can no longer move backwards along the chain, on both chains rather than only the one this list named.
Removed as wrong
NIP-49 export has no round-trip test. It has two, and the first is precisely the round trip: it exports, decrypts with the same password, checks the signing share comes back, and checks a different password fails. My error, same search flaw as above.
Duplicate rate limiting with the velocity tracker. The entry says the premise is weak in its own text. The two track different domains, per-package request counts and Bitcoin spend amounts.
Mnemonic copy before zeroize leaves an allocator residual. The entry marks itself informational and concludes the pattern is acceptable.
Hardening and test-coverage findings in
keep-mobile.Read this before picking anything up. The original list held twenty-nine entries. A manual pass on 2026-07-30 read the code for each: eleven no longer described the tree, two were the same defect filed twice, and three of the wrong ones were errors I introduced during that pass itself, from a search that reports absence for anything it cannot match. Everything below was checked by reading the code rather than searching for it.
Entries here name where the code looks wrong. They are not instructions. Read the code before acting on one, and if it does not say what the entry says, correct the entry.
Open
get_active_sharehides storage failures — ReturnsOption, so a storage error is indistinguishable from no active share. The trait method it calls returnsOptiontoo, so closing this is an FFI contract change and needs the Android side in lockstep.frost_generateandfrost_splithave no rate limit or concurrency guard — Key generation runs unguarded.detailscarries no guidance against secrets — Bounded in length, with nothing saying what may go in it.clear_entriestakes only a confirmation string — No authorization beyond a magic word, and the trait is foreign-implemented.verifyreturns early on length before the constant-time compare — The comparison itself is constant time and the lengths are structural rather than secret, so this is a documentation gap rather than a leak. Worth a note saying so, since the next reader will otherwise either "fix" the lengths pointlessly or assume the compare is unsafe.do_start_bunkerbranch selection untested — The local versus network branch has no test.Removed as already fixed
The now-milliseconds truncation is gone with the function that held it. The NIP-46 error mapper handles user-rejected and capacity-exceeded explicitly rather than letting them fall through. The
From<KeepError>implementation that was inconsistent with it no longer exists anywhere in the crate. The rate-limit map has a hard cap with a test that pushes past it. Callback URL schemes are an explicit allowlist, with a comment recording why domain allowlisting is not meaningful when the callback comes from the calling app. The x-only prefix assumption is documented at the one place it occurs, explaining that an off-curve value fails later at the point of use before any secret is derived; two entries described that single line. Node initialization degrades to in-memory sessions rather than failing. The restore path keeps the recorded backed-up state instead of marking everything backed up. Audit timestamps can no longer move backwards along the chain, on both chains rather than only the one this list named.Removed as wrong
NIP-49 export has no round-trip test. It has two, and the first is precisely the round trip: it exports, decrypts with the same password, checks the signing share comes back, and checks a different password fails. My error, same search flaw as above.
Duplicate rate limiting with the velocity tracker. The entry says the premise is weak in its own text. The two track different domains, per-package request counts and Bitcoin spend amounts.
Mnemonic copy before zeroize leaves an allocator residual. The entry marks itself informational and concludes the pattern is acceptable.