Skip to content

fix: validateDelegation should allow redelegation signing (non-root authority) #7917

@osobot-ai

Description

@osobot-ai

Description

The validateDelegation function in packages/signature-controller/src/utils/validation.ts currently blocks all delegation signing requests from external origins when the delegator is an internal account. This is overly restrictive — it should only block signing when the delegation's authority field equals the ROOT_AUTHORITY, meaning the EOA is the root delegator.

Current Behavior

When an external dapp requests a delegation signature where the delegator is an internal account, validateDelegation unconditionally throws:

External signature requests cannot sign delegations for internal accounts.

This happens regardless of the authority field value in the delegation message.

Relevant code (validation.ts, lines ~230-255):

function validateDelegation({ data, internalAccounts, origin, decodedPermission }) {
  if (!isDelegationRequest(data)) return;

  const hasDecodedPermission = decodedPermission !== undefined;
  if (!hasDecodedPermission) {
    const isOriginExternal = origin && origin !== ORIGIN_METAMASK;
    const delegatorAddressLowercase = (data.message?.delegator)?.toLowerCase();
    const isSignerInternal = internalAccounts.some(
      (internalAccount) => internalAccount.toLowerCase() === delegatorAddressLowercase,
    );

    if (isOriginExternal && isSignerInternal) {
      throw new Error(
        `External signature requests cannot sign delegations for internal accounts.`,
      );
    }
  }
}

Why This Is Wrong

In the ERC-7710 delegation framework, there are two types of delegation signing:

  1. Root delegation — The EOA creates a new delegation where it is the root delegator. The authority field is set to ROOT_AUTHORITY (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff). This grants access to the EOA's own funds and should indeed be restricted.

  2. Redelegation — The EOA has been granted a delegation and wants to redelegate some or all of those permissions to another party. The authority field is set to the hash of the parent delegation (not ROOT_AUTHORITY). This is safe because:

    • The signer is not risking their own funds
    • They can only redelegate permissions that were already granted to them
    • The root delegator's funds are already at risk via the original delegation
    • The redelegation is bounded by the caveats of the parent delegation

Currently, MetaMask blocks both cases, preventing users from redelegating delegations that were granted to them by external dapps.

Proposed Fix

Add a check for the authority field in the delegation message. Only block the signing request when authority === ROOT_AUTHORITY:

const ROOT_AUTHORITY = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
const AUTHORITY_FIELD = 'authority';

function validateDelegation({ data, internalAccounts, origin, decodedPermission }) {
  if (!isDelegationRequest(data)) return;

  const hasDecodedPermission = decodedPermission !== undefined;
  if (!hasDecodedPermission) {
    const isOriginExternal = origin && origin !== ORIGIN_METAMASK;
    const delegatorAddressLowercase = (data.message?.delegator)?.toLowerCase();
    const isSignerInternal = internalAccounts.some(
      (internalAccount) => internalAccount.toLowerCase() === delegatorAddressLowercase,
    );
    
    const authority = (data.message?.authority)?.toLowerCase();
    const isRootAuthority = authority === ROOT_AUTHORITY;

    if (isOriginExternal && isSignerInternal && isRootAuthority) {
      throw new Error(
        `External signature requests cannot sign root delegations for internal accounts.`,
      );
    }
  }
}

Security Analysis

Scenario Authority Blocked? Rationale
Root delegation (EOA creates new delegation) 0xfff...fff ✅ Yes EOA is granting access to its own funds
Redelegation (EOA passes on granted delegation) <parent hash> ❌ No EOA is only passing on already-granted permissions; no additional risk to signer's own funds

Allowing redelegation signing is safe because the redelegation can never exceed the permissions of the parent delegation. The root delegator already accepted the risk when creating the original delegation.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions