feat: add recipient_allowlist declarative policy rule - #225
Conversation
…rule and clarify declarative rules limitations
|
@br-to is attempting to deploy a commit to the MoonPay Team on Vercel. A member of the Team first needs to authorize it. |
…engine and update related documentation and tests
|
Hi @br-to I'm the author of #169 which overlaps with this. I'd suggest we keep your RecipientAllowlist as is and I open a
Would that split work for you? |
|
Quick heads up on convergence with #169, which proposed an overlapping recipient-allowlist rule. We are aligning on this PR as the base, since it is the more focused version and updates the spec doc. @Sertug17 will add the value cap and the per-operation gating as a follow-up on top of this once it lands, rather than crowding this PR. One thing worth adding here while it is open: validate that the allowlist entries are well-formed addresses and normalize them (lowercase, or EIP-55) at load time, so a malformed or mis-cased entry cannot be silently accepted and then never match. Happy to help review. Thanks. |
|
Thanks @Sertug17, that split works great for me — happy to keep this focused on RecipientAllowlist and let you layer the value cap and operation gating on top. @0xultravioleta good call on address validation. I'll add normalization (lowercase) and reject malformed entries at load time before merging. Will ping when ready for review. |
Reject malformed Ethereum addresses and normalize to lowercase when saving or loading policies, preventing silent mismatches from mis-cased or invalid entries in recipient_allowlist / allowed_typed_data_contracts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ef45a83. Configure here.
… works in signing paths The API-key signing paths always built PolicyContext with to: None, so any policy with recipient_allowlist denied every real transaction. Extend the existing rlp module to extract the `to` field from unsigned EIP-1559/EIP-2930 transactions and populate it for EVM chains before policy evaluation. Legacy txs, malformed bytes, contract creation and non-EVM chains still yield None and remain fail-closed. Co-authored-by: Cursor <cursoragent@cursor.com>
…-policy-rule Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # ows/crates/ows-lib/src/key_ops.rs
|
Update: both items addressed and pushed!
For non-transaction operations (message signing, typed data), to remains None and recipient_allowlist passes through, matching the AllowedTypedDataContracts pattern. Ready for re-review! @0xultravioleta @Sertug17 |
|
Hi @br-to, thanks, this looks much better on the re-read. Two notes. First, the address handling is solid now. Validating entries as EVM addresses at save time, and only extracting Second, one thing to confirm so we're on the same page: your note says messages and typed-data "pass through," but the code and the doc actually deny them. Assuming fail-closed is intended, this is in good shape. Nice catch on the |
|
Yes, fail-closed is intentional for recipient_allowlist if you attach The per-operation gating in the #169 follow up is exactly how you relax |
|
Good catch on the wording — “passes through” was misleading on my part. You’re right that Thanks both for the review! |

What
Add
RecipientAllowlistvariant to thePolicyRuleserde enum — a declarative rule that restricts transaction recipients to an address allowlist (case-insensitive). Denies transactions with notofield (e.g. contract creation).Why
Recipient allowlists were previously only possible via custom executable policies. Adding a built-in declarative rule lets agents be scoped to specific destination addresses without subprocess overhead.
Testing
cargo test --workspacepasses (265 tests)cargo clippy --workspace -- -D warningsis cleannpm testpasses (if Node bindings changed) — N/A, no binding changesowsCLINotes
tofield, empty list, serde roundtriprecipient_allowlistsection todocs/03-policy-engine.md, removed recipient allowlists from the "not implemented" noteNote
Medium Risk
Changes the agent signing policy path before keys are decrypted; mis-parsed or legacy txs are denied when the rule is attached, and combined policies can block message/hash signing unintentionally.
Overview
Adds a built-in
recipient_allowlistdeclarative policy so API keys can restrict transaction signing to specific EVM recipient addresses without a custom executable.Policy evaluation compares
PolicyContext.transaction.toto the allowlist (case-insensitive) and denies whentois missing, the recipient is not listed, or the list is empty. Agent transaction signing now populatestoby parsing unsigned EIP-1559 / EIP-2930 bytes via newextract_to_addressRLP decoding; legacy, malformed, contract-creation, and non-EVM cases leavetounset and the rule fails closed. Message, hash, and typed-data paths still passto: None, so keys with this rule cannot use those operations unless policies are split later.Persistence: saving/loading policies validates EVM address shape and lowercases addresses on
recipient_allowlistandallowed_typed_data_contracts. CLIows policy showprints the new rule type. Docs describe behavior and remove recipient allowlists from the “not implemented” list.Reviewed by Cursor Bugbot for commit e243135. Bugbot is set up for automated code reviews on this repo. Configure here.