Summary
ATA should only forward token operations to the canonical token program, and idempotent Create should only succeed when the existing ATA PDA occupant is a valid canonical token holding for the requested owner and token definition.
The current ATA implementation derives downstream dispatch from account metadata:
Create uses token_definition.account.program_owner as the token program for the chained Token::InitializeAccount.
Transfer uses sender_ata.account.program_owner as the token program for the chained Token::Transfer.
Burn uses holder_ata.account.program_owner as the token program for the chained Token::Burn.
Create also returns success for any non-default account at the derived ATA PDA without checking that the occupant is owned by the canonical token program, decodes as a TokenHolding, and points at the requested token definition.
This is the ATA-program subissue for the trust-boundary hardening tracked in #70. It is adjacent to #69, which raises the same broader question for AMM user deposit accounts: token accounts should not be accepted only because their account data looks token-like.
Affected Area
ata/src/create.rs derives token_program_id from token_definition.account.program_owner.
ata/src/create.rs treats any non-default ATA PDA occupant as idempotent success.
ata/src/transfer.rs derives token_program_id from sender_ata.account.program_owner.
ata/src/burn.rs derives token_program_id from holder_ata.account.program_owner.
ata/core/src/lib.rs documents that dispatch is derived from these account owners.
- Existing ATA tests cover happy-path create, idempotent create, transfer, burn, and wrong ATA address, but do not cover non-canonical token-program dispatch or malformed existing ATA occupants.
Why This Matters
ATA owns the PDA authorization used for downstream token calls. If an attacker can make ATA call a token-like program chosen from account metadata, the ATA PDA authorization can be forwarded to code that is not the canonical token program.
For Create, a malicious or malformed account can also occupy the correct ATA PDA and make future creates return success, even though the account is not a valid associated token account for that owner and definition.
Proposed Scope
- Stop selecting downstream token programs from
token_definition.account.program_owner, sender_ata.account.program_owner, or holder_ata.account.program_owner unless those owners are first checked against a trusted token program identity.
- Validate
Create inputs before emitting Token::InitializeAccount: the token definition must belong to the trusted token program.
- Validate idempotent
Create: an existing ATA PDA occupant must be owned by the trusted token program, decode as TokenHolding, and have definition_id == token_definition.account_id.
- Validate
Transfer and Burn before emitting chained calls: the ATA occupant must be owned by the trusted token program and decode as a holding for the PDA seed being verified.
- For
Burn, also require the supplied token definition account to be owned by the trusted token program and to match the holder ATA's definition_id.
- Keep the fix focused on ATA ownership and dispatch validation. AMM-specific downstream effect checks and token
InitializeAccount ownership checks should remain separate subissues under #70.
Acceptance Criteria
ata::create rejects token definition accounts owned by a non-canonical token program.
ata::create rejects an existing ATA PDA occupant that is not a canonical token holding for the requested definition.
ata::transfer rejects sender ATA accounts whose owner is not the canonical token program.
ata::burn rejects holder ATA accounts or token definition accounts whose owner is not the canonical token program.
ata::burn rejects a holder ATA whose definition_id does not match the supplied token definition account.
- Tests cover malicious token-like program owners, malformed idempotent ATA occupants, and definition mismatches across unit and integration paths.
- Existing valid create, idempotent create, transfer, burn, and private-owner create behavior remains intact.
Notes
- The trusted token program identity should come from program-side configuration or guest wiring, not from caller-controlled account metadata.
- If that identity cannot be represented without a small ATA guest or IDL change, split the wiring explicitly instead of preserving account-owner-derived dispatch.
Summary
ATA should only forward token operations to the canonical token program, and idempotent
Createshould only succeed when the existing ATA PDA occupant is a valid canonical token holding for the requested owner and token definition.The current ATA implementation derives downstream dispatch from account metadata:
Createusestoken_definition.account.program_owneras the token program for the chainedToken::InitializeAccount.Transferusessender_ata.account.program_owneras the token program for the chainedToken::Transfer.Burnusesholder_ata.account.program_owneras the token program for the chainedToken::Burn.Createalso returns success for any non-default account at the derived ATA PDA without checking that the occupant is owned by the canonical token program, decodes as aTokenHolding, and points at the requested token definition.This is the ATA-program subissue for the trust-boundary hardening tracked in #70. It is adjacent to #69, which raises the same broader question for AMM user deposit accounts: token accounts should not be accepted only because their account data looks token-like.
Affected Area
ata/src/create.rsderivestoken_program_idfromtoken_definition.account.program_owner.ata/src/create.rstreats any non-default ATA PDA occupant as idempotent success.ata/src/transfer.rsderivestoken_program_idfromsender_ata.account.program_owner.ata/src/burn.rsderivestoken_program_idfromholder_ata.account.program_owner.ata/core/src/lib.rsdocuments that dispatch is derived from these account owners.Why This Matters
ATA owns the PDA authorization used for downstream token calls. If an attacker can make ATA call a token-like program chosen from account metadata, the ATA PDA authorization can be forwarded to code that is not the canonical token program.
For
Create, a malicious or malformed account can also occupy the correct ATA PDA and make future creates return success, even though the account is not a valid associated token account for that owner and definition.Proposed Scope
token_definition.account.program_owner,sender_ata.account.program_owner, orholder_ata.account.program_ownerunless those owners are first checked against a trusted token program identity.Createinputs before emittingToken::InitializeAccount: the token definition must belong to the trusted token program.Create: an existing ATA PDA occupant must be owned by the trusted token program, decode asTokenHolding, and havedefinition_id == token_definition.account_id.TransferandBurnbefore emitting chained calls: the ATA occupant must be owned by the trusted token program and decode as a holding for the PDA seed being verified.Burn, also require the supplied token definition account to be owned by the trusted token program and to match the holder ATA'sdefinition_id.InitializeAccountownership checks should remain separate subissues under #70.Acceptance Criteria
ata::createrejects token definition accounts owned by a non-canonical token program.ata::createrejects an existing ATA PDA occupant that is not a canonical token holding for the requested definition.ata::transferrejects sender ATA accounts whose owner is not the canonical token program.ata::burnrejects holder ATA accounts or token definition accounts whose owner is not the canonical token program.ata::burnrejects a holder ATA whosedefinition_iddoes not match the supplied token definition account.Notes