Restrict Asset Hub agent from executing contract calls in V2#1788
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1788 +/- ##
==========================================
+ Coverage 76.90% 78.17% +1.26%
==========================================
Files 24 24
Lines 983 985 +2
Branches 186 187 +1
==========================================
+ Hits 756 770 +14
+ Misses 203 192 -11
+ Partials 24 23 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| vm.expectEmit(true, false, false, true); | ||
| emit IGatewayV2.InboundMessageDispatched(1, topic, true, relayerRewardAddress); | ||
|
|
||
| address bridgeHubAgent = IGatewayV2(address(gateway)).agentOf(Constants.BRIDGE_HUB_AGENT_ID); |
There was a problem hiding this comment.
Kind of a weird test to allow BH agent to make arbitrary contract calls, since this is privileged as well, right? Even if the BH agent doesn't hold funds.
There was a problem hiding this comment.
Allowing BH is expected. Yes, it’s not privilege-based — it only disables the AH agent that holds the funds.
claravanstaden
left a comment
There was a problem hiding this comment.
I think this okay in principle. AH is the only agent that contains token transfer funds, right?
|
Added some more tests here: #1793 |
* Add tests for ensureNotAssetHubAgent Strengthens coverage around the AssetHub-agent callContract block introduced in #1788: - positive: a v2_createAgent'd user agent can still callContract - direct: ensureNotAssetHubAgent revert reason and allow/deny paths pinned via an exposed wrapper on MockGateway, including a fuzz invariant that only ASSET_HUB_AGENT_ID returns UnauthorizedPrivilegedAgent - no-state-leak: AssetHub failure path emits no SaidHello, leaves agent balance and registration untouched - dual-invariant: ASSET_HUB_AGENT_ID is rejected as a callContract origin AND still accepted as an unlockNativeToken recipient - multi-command: a poisoned AssetHub-origin callContract bundled with a legit one fails every command; same payload from a user agent succeeds - skipped: testAgentCallContractFailsForBridgeHub documents the latent AliasOrigin(Here) -> BRIDGE_HUB_AGENT_ID bypass that the current single-entry deny list does not cover Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop BridgeHub-block skip test BridgeHub is intentionally non-privileged in the V2 deny list; only ASSET_HUB_AGENT_ID is reserved. Remove the speculative testAgentCallContractFailsForBridgeHub skip and clarify the existing testEnsureNotAssetHubAgent_AllowsBridgeHub with a comment so future readers know the allow is by design. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop ticket id from section comment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop PR reference from section comment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I'd like us to go a step further and remove support for CallContract entirely. |
| function callContract(bytes32 origin, address executor, bytes calldata data) external { | ||
| CallContractParams memory params = abi.decode(data, (CallContractParams)); | ||
| address agent = Functions.ensureAgent(origin); | ||
| address agent = Functions.ensureNotAssetHubAgent(origin); |
There was a problem hiding this comment.
I prefer this, its easier to audit.
| address agent = Functions.ensureNotAssetHubAgent(origin); | |
| if (agentID == Constants.ASSET_HUB_AGENT_ID) { | |
| revert IGatewayBase.Unauthorized(); | |
| } | |
| address agent = Functions.ensureAgent(origin); |
Per review feedback, inline the AssetHub deny check at the callContract call site instead of the single-use ensureNotAssetHubAgent helper, and revert with the existing Unauthorized error rather than a new UnauthorizedPrivilegedAgent type. The V2 dispatcher swallows revert reasons in a bare catch, so a distinct error gives no observable benefit.
No description provided.