fix(genesis-tool): close 4 medium audit findings (#533–#536)#102
Merged
Conversation
Address four medium-severity findings against `genesis-tool` from the Galxe/gravity-audit batch. Each fix converts a silent / heuristic failure mode into a loud, fail-closed invariant so a misconfigured input cannot ship a broken genesis through CI. #533 — convert_config_to_sol (genesis.rs): votingPower and stakeAmount were two independent attacker- (or operator-) controlled fields in GenesisConfig, written verbatim into SolInitialValidator. ValidatorManagement.initialize deliberately bypasses _validateRegistration (no PoP / minimumBond check), so this tool was the only line of defense against a validator booted with consensus weight decoupled from staked bond. Assert votingPower == stake_amount per validator and abort with a clear diagnostic naming the offending operator/moniker. #534 — extract_runtime_bytecode (execute.rs): Misleading name and useless heuristic: the function "extracted" nothing — it hex-decoded the input and, if the first byte was 0x60/0x61 and len > 100, emitted a WARN and returned bytes unchanged (both arms returned `bytes`). All Solidity runtime bytecode begins with `60 80 60 40` (free-memory-pointer prologue), so the WARN fired on every legitimate run and could not distinguish constructor from runtime bytecode. Rename to decode_runtime_bytecode, drop the dead branch, and document that callers must supply `deployedBytecode.object` (per scripts/helpers/extract_bytecode.py). #535 — print_active_validators_result + verify_result (genesis.rs, post_genesis.rs, main.rs): Validator-count mismatch only logged `error!` and returned `()`; verify_result `.expect`-panicked but print_active_validators_result itself swallowed the failure inside `let _ = handle_execution_result`. Make the leaf return Result<(), String>, lift handle_execution_result to take a fallible success_handler, and propagate through verify_result → run_generate via `?` so a mismatch becomes a non-zero exit instead of a buried log line in CI. #536 — parse_u256_hex (verify.rs): Returned U256::ZERO on parse failure, masking malformed balances and storage values in the alloc the `verify` subcommand is supposed to independently police. Return Result, propagate at the three callsites with context naming the offending address/key, so a decimal-or-typo'd balance fails verification rather than silently zeroing the account. cargo check / cargo test: clean (pre-existing warnings unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Closes four medium-severity findings from the Galxe/gravity-audit batch against the
genesis-toolcrate. Each fix converts a silent / heuristic failure mode into a loud, fail-closed invariant so a misconfigured input cannot ship a broken genesis through CI.voting_power == stake_amountper validator inconvert_config_to_sol(genesis.rs) —ValidatorManagement.initializebypasses_validateRegistration, so this tool is the only line of defense.extract_runtime_bytecodeno-op heuristicdecode_runtime_bytecode, drop the dead0x60/0x61branch + noisy WARN (both arms returned the samebytes; all Solidity runtime starts with60 80 60 40, so the heuristic could not distinguish constructor from runtime). Document the caller contract: must supplydeployedBytecode.object(already done byscripts/helpers/extract_bytecode.py).run_generateexitsOk(())on verify failureResult<(), String>throughprint_active_validators_result→handle_execution_result(now takes a fallible success_handler) →verify_result→run_generate, so a validator-count mismatch becomes a non-zero exit instead of a buried error log.parse_u256_hexsilently returns zero on parse failureResult<U256>and propagate at all three callsites with context naming the offending address/key. A malformed balance/storage value now fails verification instead of silently zeroing the account.Notes
extract_runtime_bytecodeheuristic in #534 fired on every legitimate run (all.hexartifacts start with60 80 60 40), but both branches of theif/elsereturned the same bytes, so this was log noise rather than a behavior bug. Current pipeline (viascripts/helpers/extract_bytecode.pyreadingdeployedBytecode.object) was never affected; the rename + comment makes the caller contract explicit so a future pipeline change cannot resurrect the trap.handle_execution_result(success_handler now returnsResult<(), String>) is internal —print_active_validators_resultis its only external caller.Test plan
cargo check -p genesis-tool— clean (warnings are pre-existing).cargo build -p genesis-tool— clean.cargo test -p genesis-tool— 0 tests, all pass (no existing tests in the crate).genesis-tool generateagainst the currentnew_genesis_config.jsonand confirm exit 0 (happy path unchanged).voting_power != stake_amountfor one validator → expect panic from #533 assertion.verifyagenesis.jsonwith a decimal balance string → expect non-zero exit citing the offending address (was: silent success).🤖 Generated with Claude Code