Fix: origin-rewriting wrappers bypass high-security restrictions#610
Fix: origin-rewriting wrappers bypass high-security restrictions#610n13 wants to merge 3 commits into
Conversation
for all origin-altering calls
Bound `HighSecurityConfig::call_allowed_for` to a fixed nesting depth (MAX_CALL_DEPTH = 16) and fail closed beyond it, so deeply nested origin-rewriting/combinator calls cannot exhaust transaction-validation work. Adds a regression test.
There was a problem hiding this comment.
Verdict: Approve — no remaining blockers from this review.
Update: the only blocker from my earlier review (the failing Format check) is resolved. Commit e196524a ("format") makes the Fast Checks (Format) job pass, and git show -w confirms it is whitespace-only (rustfmt line-reflow in runtime/src/transaction_extensions.rs — weight(), validate_with(), and two test constructors), so the logic and tests validated earlier are unaffected. (Build & Test / Clippy & Doc were still running at review time — merge once they go green.)
Effective-origin high-security enforcement (reconfirmed):
- The recursive resolver re-checks the whitelist at the effective dispatched origin, covering the origin-rewriting wrappers (
Recovery::as_recovered,Utility::as_derivative) and the origin-preserving combinators (batch/batch_all/force_batch/if_else). - The remaining
pallet-utilityorigin-altering calls —dispatch_as,with_weight,dispatch_as_fallible— are allensure_root, so they are not an unprivileged bypass and correctly are not traversed. The scheduler's extrinsics are disabled. ReversibleTransactionExtensionandpallet-multisigshare the singleHighSecurityInspector::is_call_allowedimplementation (no duplicated logic); multisig re-checks at execute time against the multisig address.- Recursion is bounded (
MAX_CALL_DEPTH = 16) and fails closed, well under theMAX_EXTRINSIC_DEPTH = 256decode limit;weight()scales with the number of traversed nodes. - Recovery address handling is safe: this runtime sets
type Lookup = AccountIdLookup, which only resolvesMultiAddress::Id, so the non-Idarm cannot dispatch and is not an alternate-encoding bypass.
No security or logic blocker found.
Local tests (still valid — the format commit is whitespace-only):
cargo test -p quantus-runtime --lib transaction_extensions::testspassedcargo test -p pallet-multisigpassedcargo test -p pallet-reversible-transferspassed
n13
left a comment
There was a problem hiding this comment.
Updated verdict after the format commit: previous blocker cleared.
The new format commit fixed the issue I flagged; GitHub's Fast Checks (Format) job is now passing. My code-review verdict is approve/merge-ready from the effective-origin high-security enforcement perspective.
Remaining note: the build/test matrix and clippy/doc jobs are still running on the latest commit, so I would wait for those checks to finish green before merging.
Summary
High-security (HS) accounts are meant to be limited to whitelisted calls (e.g. reversible
schedule_transfer/cancel). They could be bypassed:Recovery::as_recoveredandUtility::as_derivativesynthesize a freshSignedorigin after top-level transaction validation, so a non-HS outer signer could dispatch a non-whitelisted call as an HS account — including when nested underbatch/batch_all/force_batch/if_else.This PR enforces the HS whitelist at the effective dispatched origin, entirely at the runtime/extension layer:
HighSecurityConfig::call_allowed_forrecursively resolves origin-rewriting wrappers (as_recovered/as_derivative) and origin-preserving combinators (batch*/if_else), re-checking the whitelist at each effective signer. Root-onlydispatch_as/with_weightand the scheduler are intentionally not traversed.HighSecurityInspector::is_call_allowedprovided method, soReversibleTransactionExtensionandpallet-multisiguse one implementation (no duplicated logic).MAX_CALL_DEPTH = 16, well belowMAX_EXTRINSIC_DEPTH = 256): over-nested calls are rejected rather than allowed to escape the whitelist, so validation work can't be exhausted.weight()scales with the number of nodes traversed (oneis_high_securityread per node).pallet-recoveryandpallet-utilityare left unchanged from upstream Substrate.Test plan
cargo test -p quantus-runtime --lib transaction_extensions::tests— origin-rewriter bypass blocked, whitelisted inner call allowed, nested-in-batch bypass blocked, non-HS origin unaffected, over-nested call rejected fail-closedcargo test -p pallet-multisigcargo test -p pallet-reversible-transfers