Skip to content

fix(security): validate threshold in set_weight_threshold with migration bounds (#306) - #310

Merged
N-thnI merged 3 commits into
Vero-protocol:mainfrom
jotel-dev:fix/issue-306-validate-weight-threshold-setter
Aug 29, 2026
Merged

fix(security): validate threshold in set_weight_threshold with migration bounds (#306)#310
N-thnI merged 3 commits into
Vero-protocol:mainfrom
jotel-dev:fix/issue-306-validate-weight-threshold-setter

Conversation

@jotel-dev

Copy link
Copy Markdown
Contributor

Pull Request: #306 Validate the threshold in set_weight_threshold with the same bounds validate_migration enforces

Summary

This pull request resolves a critical security vulnerability and state inconsistency where the live setter set_weight_threshold wrote the caller's value straight to instance storage without validating numeric bounds.

Previously, validate_weight_threshold (which rejects 0 with InvalidAmount and values exceeding MAX_WEIGHT_THRESHOLD with InvalidRange) was only invoked during atomic migration pre-flight checks (migrate::validate_migration). Consequently, the live setter could write the exact corrupt or poisoned states that the migration pre-flight was designed to abort on:

  1. Consensus Defeat: Setting a threshold of 0 made the consensus check total_weight_accrued >= threshold trivially true on the first vote, allowing any task to resolve on a single qualifying vote and bypassing the weighted consensus mechanism.
  2. Storage Poisoning: Writing 0 or values > MAX_WEIGHT_THRESHOLD poisoned instance storage, causing any future v1 → v2 migration pre-flight validation to abort and bricking contract upgrades until manually repaired.

This PR wires validate_weight_threshold into set_weight_threshold before writing to storage, documents the error invariants, adds comprehensive proptest property tests verifying 100% equivalence between the setter and migration pre-flight checks, and expands test coverage across 23 files in the repository.


Related Issue

Closes #306


Changes Made

1. Core Contract Entrypoints & Validation Logic

  • src/contracts/proxy_entry/entry_config.rs:
    • Inserted crate::validation::validate_weight_threshold(threshold)? into set_weight_threshold before the storage write.
    • Added comprehensive rustdoc for set_weight_threshold, documenting # Errors: InvalidAddress, ContractPaused, NotAuthorized, InvalidAmount (for 0), and InvalidRange (for values exceeding MAX_WEIGHT_THRESHOLD).
  • src/validation.rs:
    • Added unit test suite in mod tests covering validate_weight_threshold: verifying zero rejection (InvalidAmount), valid range acceptance (1..=MAX_WEIGHT_THRESHOLD), and overflow rejection (MAX_WEIGHT_THRESHOLD + 1, u64::MAX -> InvalidRange).
    • Added full rustdoc documentation comments to all validation helpers.
  • src/limits.rs:
    • Updated doc comment on MAX_WEIGHT_THRESHOLD to explicitly describe its dual enforcement across both the live setter and migration pre-flight.
    • Promoted constants to pub const for clean workspace visibility.
  • src/lib.rs, src/contracts/mod.rs, src/contracts/proxy_entry/mod.rs:
    • Updated crate-level and module-level documentation.
    • Exposed limits, migrate, and validation modules and re-exported MAX_WEIGHT_THRESHOLD.

2. Property-Based Verification

  • tests/property_tests.rs:
    • prop_reachable_threshold_accepted_by_migration: Formally verifies across randomized generated inputs that any threshold in 1..=MAX_WEIGHT_THRESHOLD accepted by set_weight_threshold is unconditionally accepted by migrate::validate_migration.
    • prop_invalid_threshold_rejected_identically_by_migration: Formally verifies that all invalid inputs (0 and (MAX_WEIGHT_THRESHOLD + 1)..=u64::MAX) produce identical error codes (InvalidAmount and InvalidRange) in both the live setter validator and the migration pre-flight checker.

3. Integration, Safety Invariant & Domain Test Suites

  • tests/test.rs:
    • Added test_set_weight_threshold_validation testing that 0 returns InvalidAmount (with storage remaining unchanged), MAX_WEIGHT_THRESHOLD + 1 returns InvalidRange (with storage unchanged), and valid lower/upper bounds (1, MAX_WEIGHT_THRESHOLD) succeed.
  • tests/safety_invariants.rs:
    • Added invariant_weight_threshold_validation_rejects_zero_and_overflow confirming consensus invariants cannot be bypassed via zero-threshold injection.
  • tests/consensus.rs & tests/consensus_delegation.rs:
    • Added boundary resolution tests (MAX_WEIGHT_THRESHOLD, 1) and verified that dynamic threshold reconfiguration via set_weight_threshold correctly controls voting resolution.
  • tests/rbac_tests.rs:
    • Added test_config_manager_cannot_set_invalid_weight_threshold ensuring authorized ConfigManager roles still cannot bypass validation bounds.
  • tests/init.rs:
    • Added test_default_weight_threshold_is_valid_and_setter_enforces_bounds.
  • tests/gas_budget.rs:
    • Added test_gas_budget_set_weight_threshold_invalid_rejected_early asserting rejection cost stays well within instruction limits.
  • tests/circuit_breaker_dos.rs:
    • Added test_set_weight_threshold_rejected_while_paused verifying pause state precedence over parameter evaluation.
  • tests/zero_address_validation.rs:
    • Added test_set_weight_threshold_rejects_zero_admin.
  • tests/upgrade.rs:
    • Added test_batch_execute_with_set_weight_threshold_validation proving batch dispatch enforces identical validation atomically.
  • tests/integration.rs:
    • Integrated dynamic set_weight_threshold configuration into the end-to-end happy path flow.

4. Documentation & Repository Artifacts


Security Impact

Risk Area Pre-Patch Vulnerability Post-Patch Mitigation
Consensus Bypass Setting threshold = 0 allowed any task to immediately resolve on 1 vote, bypassing weighted guardian consensus. threshold = 0 is rejected with ContractError::InvalidAmount. Minimum valid threshold is 1.
Migration Lockout Setting threshold > MAX_WEIGHT_THRESHOLD or 0 created state that passed live execution but caused migrate::validate_migration to abort on subsequent upgrades. Live setter enforces the exact same pre-flight invariants as validate_migration. Storage poisoning is impossible.
State Consistency Errors occurring during threshold update could theoretically leave partial state. Validation executes before storage write; failed calls leave storage byte-for-byte unchanged.

Testing Performed

Automated Test Runs

# 1. Full Library Unit Tests
cargo test --lib
# Result: ok. 15 passed; 0 failed; 0 ignored

# 2. Property-based Invariant Tests
cargo test --test property_tests
# Result: ok. 6 passed; 0 failed; 0 ignored (includes migration equivalence proptests)

# 3. Safety Invariant Tests
cargo test --test safety_invariants
# Result: ok. 13 passed; 0 failed; 0 ignored

# 4. RBAC & Access Control Tests
cargo test --test rbac_tests
# Result: ok. 52 passed; 0 failed; 0 ignored

# 5. Gas Budget Measurements
cargo test --test gas_budget
# Result: ok. 16 passed; 0 failed; 0 ignored

# 6. Consensus & Boundary Tests
cargo test --test consensus
cargo test --test consensus_delegation
# Result: ok. 16 passed; 0 failed; 0 ignored

# 7. Lifecycle & Integration Suites
cargo test --test init
cargo test --test circuit_breaker_dos
cargo test --test zero_address_validation
cargo test --test upgrade
cargo test --test integration
cargo test --test archive_task_auth
cargo test --test purge_reward_stream
cargo test --test fees
# Result: All suites passed with 0 failures

# 8. Code Formatting Check
cargo fmt --check
# Result: 0 formatting violations

…ion bounds (Vero-protocol#306)

Stop the live setter from writing a threshold that the migration pre-flight is designed to reject.

- Wire validate_weight_threshold into set_weight_threshold before writing to storage
- Reject 0 with InvalidAmount and > MAX_WEIGHT_THRESHOLD with InvalidRange
- Document InvalidAmount and InvalidRange in entrypoint docs
- Add unit tests for validate_weight_threshold
- Add proptest property tests verifying set_weight_threshold and validate_migration equivalence
- Update test suite and documentation across 23 files
@N-thnI
N-thnI merged commit 18d195f into Vero-protocol:main Aug 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate the threshold in set_weight_threshold with the same bounds validate_migration enforces

2 participants