Skip to main content
Sigvex

Cross-Account Relationship Remediation

How to fix missing relationship validation between related accounts.

Cross-Account Relationship Remediation

Overview

Related Detector: Cross-Account Relationship Validation

Cross-account relationship vulnerabilities occur when accounts that should be related (token account to mint, ATA to owner) are used together without verifying the relationship. The fix is to validate all account relationships before performing operations.

Before (Vulnerable)

pub fn withdraw(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let vault_token = &accounts[0]; // No relationship check
    let user_token = &accounts[1];
    let authority = &accounts[2];
    invoke(&transfer_ix, accounts)?;
    Ok(())
}

After (Fixed)

pub fn withdraw(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let vault_token = &accounts[0];
    let user_token = &accounts[1];
    let authority = &accounts[2];

    // Validate vault ownership
    let vault_data = spl_token::state::Account::unpack(&vault_token.data.borrow())?;
    if vault_data.owner != *authority.key {
        return Err(ProgramError::InvalidAccountData);
    }

    // Validate both accounts use same mint
    let user_data = spl_token::state::Account::unpack(&user_token.data.borrow())?;
    if vault_data.mint != user_data.mint {
        return Err(ProgramError::InvalidAccountData);
    }

    invoke(&transfer_ix, accounts)?;
    Ok(())
}

Alternative Mitigations

1. Anchor has_one constraint

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(mut, has_one = mint, has_one = authority)]
    pub vault: Account<'info, TokenAccount>,
    #[account(mut, constraint = user_token.mint == vault.mint)]
    pub user_token: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub mint: Account<'info, Mint>,
}

2. ATA derivation verification

let expected_ata = get_associated_token_address(owner.key, mint.key);
if user_token.key != &expected_ata {
    return Err(ProgramError::InvalidAccountData);
}

Common Mistakes

Mistake 1: Validating individual accounts but not their relationships

// INSUFFICIENT: both accounts are valid individually but may not be related
if !authority.is_signer { return Err(...); }
if vault.owner == &spl_token::id() { /* OK */ }
// Missing: does vault.owner match authority?

Mistake 2: Only checking one direction

// INCOMPLETE: checks source -> authority but not destination -> mint
let data = Account::unpack(&source.data.borrow())?;
if data.owner != *authority.key { return Err(...); }
// Missing: data.mint == expected_mint

References