Conversation
Follow-up to #1217 (Least Authority A2 Suggestion 3), which hardened the traits on WalletSeed but left the decode paths as they were. - try_from_hex_str validates the length (32/64/128 hex chars) before decoding rather than decoding first and checking the byte count after, so untrusted input is rejected on size alone. - Both hex paths accept an optional 0x prefix. - Neither allocates: hex::decode_to_slice writes straight into the fixed-size array behind the variant, so seed bytes never sit in an intermediate Vec that outlives Zeroize. - add_addresses returns Result<(), MaintenanceUpdateError> and rejects mismatched slices. The zip added in #1217 removed the indexing panic but silently dropped the tail of the longer slice. No production callers, so nothing downstream changes. Both ledger_8 and ledger_9 copies of types.rs get the identical change. Assisted-by: Claude:claude-opus-5 claude-code Claude-Session: https://claude.ai/code/session_019K95xJqbnxsbyyRY7pESNJ
Assisted-by: Claude:claude-opus-5 claude-code
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6527889720
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let mut out = [0u8; N]; | ||
| hex::decode_to_slice(hex_str, &mut out)?; |
There was a problem hiding this comment.
Zeroize the temporary decode buffers
When a correctly sized seed contains an invalid nibble after a valid prefix, hex::decode_to_slice has already written the preceding decoded bytes into out before returning an error. The ? path then drops this plain array without zeroizing it, leaving partial seed material in stack memory; decode_lazy_hex has the same problem when either half fails. Wrap these temporary arrays in Zeroizing or explicitly zeroize them on every error path in both ledger-version copies.
Useful? React with 👍 / 👎.
Individual remediation per .github/dco.yml (allowRemediationCommits.individual: true). Author of this commit, author of the remediated commits and the remediator named below are all giles-bot, which is what the DCO app requires of the individual form. I, giles-bot <319052204+giles-bot@users.noreply.github.com>, hereby add my Signed-off-by to this commit: d70b09c I, giles-bot <319052204+giles-bot@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 6527889 Signed-off-by: giles-bot <319052204+giles-bot@users.noreply.github.com>
Overview
Follow-up to #1217 (Least Authority audit A2 Suggestion 3, PM-22038). That PR hardened the traits on
WalletSeed— removedCopy, addedZeroize/ZeroizeOnDrop, redactedDebug, droppedClonefromKeypair, replaced the indexing panic inadd_addresseswith azip. It left the decode paths themselves untouched, and theziptraded a panic for silence. This closes those four gaps.WalletSeed::try_from_hex_strhex::decodefirst and checked the byte count afterwards, so a hostile string sized an allocation before anything rejected it.InvalidHex(OddLength); a well-formed but wrong-sized seed reportsInvalidLength(bytes).Optional
0xprefixtry_from_hex_strandtry_from_lazy_hexaccept one.Keypair::from_stralready did, so a0x-prefixed seed used to parse as a key but not as a seed.No allocation on either path
hex::decode_to_slicewrites straight into the fixed-size array behind the variant. Seed bytes no longer pass through an intermediateVec<u8>that outlivesZeroize— which was the point of addingZeroizeOnDropin the first place.try_from_lazy_hexalso drops itsVecof split parts forsplit_once, so the oversize check now genuinely happens before any allocation, as its test name already claimed.MaintenanceUpdateBuilder::add_addressesreturnsResultMaintenanceUpdateError::LengthMismatch { addresses, counters }. Thezipremoved the unchecked-indexing panic but silently dropped the tail of the longer slice, so a caller that miscounted got a partial update and no signal. Nothing is applied on mismatch.The identical change lands in both
ledger/helpers/src/ledger_8/types.rsandledger_9/types.rs— the two copies created by #2074 are byte-identical apart from their module paths.Deliberately out of scope
Removing
ClonefromWalletSeed. #1217's own description defers this: it needsBuildInput/BuildOutput/UtxoOutputInfo/wallet_from_seedto borrow&WalletSeed, threading lifetimes throughBox<dyn BuildInput<DefaultDB>>. That is 189 by-value signature sites and 238 clone sites across both ledger trees — an unreviewable diff if bolted onto this one. Worth its own PR.🗹 TODO before merging
📌 Submission Checklist
git commit -s) for the DCO🧪 Testing Evidence
cargo test -p midnight-node-ledger-helpers --lib— 52 passed, 0 failed.New tests, mirrored in both ledger trees:
hex_str_accepts_optional_0x_prefix0xprefix, direct and viaFromStrlazy_hex_accepts_optional_0x_prefix0xprefix on the lazy pathhex_str_rejects_wrong_length_before_decodinghex_str_rejects_oversized_inputhex_str_reports_odd_length_as_a_hex_errorhex_str_round_trips_every_seed_sizelazy_hex_rejects_more_than_one_ellipsis00..11..22lazy_hex_places_head_and_tail_at_the_seed_edgesadd_addresses_rejects_length_mismatchadd_addresses_applies_matched_slicesadd_addresses_with_zip_truncates_on_mismatchis replaced byadd_addresses_rejects_length_mismatch. It asserted the silent truncation as intended behaviour; that is the behaviour this PR changes.cargo clippy --all-targetsclean onmidnight-node-ledger-helpers,midnight-ledger-unsafe-helpersandmidnight-node-toolkit.🔱 Fork Strategy
Links
🤖 Generated with Claude Code
https://claude.ai/code/session_019K95xJqbnxsbyyRY7pESNJ