Skip to content

fix(keychain): reject dirty recipient word in is_call_allowed - #91

Merged
onbjerg merged 1 commit into
tempoxyz:mainfrom
devorun:fix/is-call-allowed-recipient-word
Sep 14, 2026
Merged

onbjerg merged 1 commit into
tempoxyz:mainfrom
devorun:fix/is-call-allowed-recipient-word

Conversation

@devorun

@devorun devorun commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

KeyRestrictions.is_call_allowed documents that its resolution order "matches the Rust implementation", but for a recipient-constrained selector it extracts the recipient from the low 20 bytes of the first ABI word and ignores the upper 12 bytes:

word = input_data[4:36]
recipient = as_address(word[12:])
return recipient in rule.recipients

The Rust it mirrors does not. Both the client-side reference tempo_alloy::call_scopes_allow and the on-chain precompile (AccountKeychain::validate_call_scope_for_transaction) require the upper 12 bytes of that word to be zero before comparing the recipient, and deny the call otherwise:

let word: [u8; 32] = input.get(4..36)?.try_into().ok()?;
if word[..12].iter().any(|byte| *byte != 0) {
    return Some(false);
}
Some(rule.recipients.contains(&Address::from_slice(&word[12..])))

So for calldata whose first word has non-zero upper bytes but whose low 20 bytes happen to match an allowed recipient, is_call_allowed returns True while the chain rejects the call. A caller that trusts the helper to pre-check an access key would build and sign a transaction that then reverts on-chain.

Fix

Add the same zero-upper-bytes guard before extracting the recipient, so the client agrees with call_scopes_allow and the precompile.

Test

Adds test_recipient_word_dirty_upper_bytes_denied for the dirty-upper-bytes case; the existing tests only exercised well-formed, zero-padded recipient words.

is_call_allowed accepted a recipient-constrained call whose first ABI word had non-zero upper bytes, taking the recipient from the low 20 bytes regardless. tempo_alloy's call_scopes_allow and the on-chain precompile both reject such a word before comparing the recipient, so the client could report a call as allowed that the chain rejects. Require the upper 12 bytes to be zero, and add a regression test.
@onbjerg
onbjerg merged commit f3f16fe into tempoxyz:main Sep 14, 2026
13 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.

2 participants