T09 · Insecure Skill Coding Practices
- Location
SKILL.md:209- Finding
FA2 Operator Authorization Is Not Scoped by Token ID
- Content
View full analysis
sender then let key = (xfer.from_, sender) in if not Big_map.mem key storage.operators then failwith "FA2_NOT_OPERATOR" else () else () in ``` ```ligo type operator_update = | Add_operator of address * address * nat | Remove_operator of address * address * nat [@entry] let update_operators (updates : operator_update list) (storage : storage) : operation list * storage = let sender = Tezos.get_sender() in let process_update (storage, update : storage * operator_update) : storage = match update with | Add_operator (owner, operator, token_id) -> let () = if sender <> owner then failwith "FA2_NOT_OWNER" else () in {storage with operators = Big_map.add (owner, operator) () storage.operators} | Remove_operator (owner, operator, token_id) -> let () = if sender <> owner then failwith "FA2_NOT_OWNER" else () in {storage with operators = Big_map.remove (owner, operator) storage.operators} in let storage = List.fold_left process_update storage updates in [], storage ``` ### Technical Analysis The operator-update interface accepts a `token_id`, but that value is discarded when the authorization record is created or removed. The transfer authorization check likewise constructs its key from only `(owner, operator)`. For an FA2 implementation with token-specific operators, the authorization key must include all three values: ```text (owner, operator, token_id) ``` The documented implementation instead grants an operator authority at the owner level. Consequently, approval associated with one token ID can satisfy authorization checks for every token held by the same owner. Because this Skill presents the code as an FA2 implementation pattern, developers may copy the flaw into production co ...[truncated 1048 chars]- Remediation
View remediation
