feat(rust-fuzzer): implement truncate and pad length mutators + fix broken test data - #300
Conversation
Add length-mutation helpers for the rust-address-fuzzer: - truncate(addr, rng): removes 1 to len/2 trailing characters - pad(addr, rng): appends 1-16 random base32 characters Both produce strings guaranteed to fail parsing with no panics and no partial-parse Ok results. Also fixes pre-existing test data: 3 tests in prism-core and 1 test in parse.rs used 53-char phantom addresses that could never pass the LEN_G=56 check. Replaced with valid 56-char addresses from spec/vectors.json. Closes Boxkit-Labs#291
|
@Yinklekay Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds truncation and padding mutators for Stellar addresses, integrates them into the fuzzer module tree, validates malformed inputs, and updates lowercase/uppercase parser test vectors. ChangesAddress length mutators
Parser test vectors
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/rust-address-fuzzer/src/main.rs`:
- Line 1: Update the fuzz loop in run_random to add a length-mutation campaign
that uses valid G/M seeds, invokes both mutators::truncate and mutators::pad,
and passes each mutated result to fuzz_one. Keep the existing random_string
campaign intact and remove any unused-code suppression in mutators/mod.rs that
is no longer needed once these helpers are exercised.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 81a17a06-4f4a-4524-afc3-c74cbba308e2
📒 Files selected for processing (5)
examples/prism-core/src/address.rsexamples/rust-address-fuzzer/src/main.rsexamples/rust-address-fuzzer/src/mutators/length.rsexamples/rust-address-fuzzer/src/mutators/mod.rsexamples/rust-address-fuzzer/src/parse.rs
| @@ -1,3 +1,4 @@ | |||
| mod mutators; | |||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Actually feed length mutations into the fuzz loop.
Declaring the module does not register either helper: run_random still fuzzes only random_string, while mutators/mod.rs suppresses their unused-code warning. Add a campaign that calls both truncate and pad on valid G/M seeds and passes each result to fuzz_one; otherwise this binary never exercises the new mutators.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/rust-address-fuzzer/src/main.rs` at line 1, Update the fuzz loop in
run_random to add a length-mutation campaign that uses valid G/M seeds, invokes
both mutators::truncate and mutators::pad, and passes each mutated result to
fuzz_one. Keep the existing random_string campaign intact and remove any
unused-code suppression in mutators/mod.rs that is no longer needed once these
helpers are exercised.
|
@Yinklekay fix conflicts |
Summary
Implements length-mutation fuzzer helpers (
truncate/pad) for therust-address-fuzzer and fixes pre-existing broken test data in both
Rust crates. Closes #291.
Problem
Off-by-one and truncated inputs are classic parser killers. Feeding
progressively shorter and padded strings probes the length-handling
logic that base32 decoders often get wrong. The rust-address-fuzzer
had no dedicated mutators for length manipulation, leaving the
parser's edge-case behaviour around over-length and under-length
inputs unexplored.
Additionally, several tests across both Rust crates used a phantom
G address (
GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV3PR5T4Q)that was only 53 characters long, while the parser declares
LEN_G = 56. These tests silently failed to validate anythinguseful because the parse step always rejected the input before
reaching the logic being tested.
Changes
New: length mutators (
src/mutators/length.rs)truncate(addr, rng)— Randomly removes 1 tomax(1, len/2)trailing characters. Uses
saturating_subto guard against underflowon empty / 1-char inputs. The shortened string is always too short
for any valid Stellar address.
pad(addr, rng)— Appends 1–16 random RFC 4648 base32 characters(A–Z, 2–7). The extended string is always too long for any valid
Stellar address.
Module wiring in
src/mutators/mod.rswith#![allow(dead_code)].Bugfixes in existing test data
prism-core/src/address.rsvalid_g_address_parsesprism-core/src/address.rslowercase_normalised_correctlyprism-core/src/address.rsinvalid_base32_character1that reaches the base32 checkrust-address-fuzzer/src/parse.rsparses_valid_g_addressrust-address-fuzzer/src/main.rssaturating_add_signedexpectsisize, changed fromi64range toisizeAll fixes use the valid G address
GAYCUYT553C5LHVE2XPW5GMEJT4BXGM7AHMJWLAPZP53KJO7EIQADRSIfrom the normative
spec/vectors.json(modulemuxed_encode).Test coverage
12 rust-address-fuzzer tests — all pass
base_addresses_are_validVALID_GandVALID_Mfrom spec vectors are parseabletruncate_produces_shorter_stringtruncate_preserves_prefixtruncate_g_always_err_no_panictruncate_m_always_err_no_panicpad_produces_longer_stringpad_preserves_prefixpad_g_always_err_no_panicpad_m_always_err_no_panicparses_valid_g_addressrejects_garbagerejects_empty_stringEach parse call in the four "always Err" tests is wrapped in
std::panic::catch_unwindto catch both partial-parseOkresultsand genuine panics. 200 seeds × 4 test cases = 800 distinct
parse attempts, all producing errors with zero panics.
7 prism-core tests — all pass
All original tests now pass with the corrected addresses.
CI validation
cargo test(rust-address-fuzzer)cargo test(prism-core)cargo clippy --all-targets(rust-address-fuzzer)cargo clippy --all-targets(prism-core)Design decisions
strings rejected at the prefix check; keeping the range ensures
the parser consistently reaches length-validation logic.
Even +1 guarantees InvalidLength. The 1–16 range adds variety.
StdRng::seed_from_u64makes everytest run reproducible for debugging.
addresses pass TypeScript, Go, Dart, and now Rust.
Summary by CodeRabbit