sec: listed CW20 can migrate off whitelist after CreatePair #582
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#582
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Gate
This issue blocks #581. Do not close #581 with a go to factory
AddWhitelistedCodeId8266, and do notCreatePair/ seed SpaceUSD/UST1, until this issue has a written severity + mitigation decision.Parent ops: #558. Extracted from #581 item (4) — the hole is not 8266-specific.
Policy:
docs/runbooks/cw20-whitelist-policy.md/ #377 H-01 / P2. Already named in SEC-I01 H05 (docs/audits/sec-i-prompt-pack.md): whitelist gatescode_idonly at pair creation; a later upgrade that adds a fee still passes. That line was treated as an audit-doc gap. It is also a live execute path.What the contracts actually do
Factory
CreatePairqueries each asset’sContractInfo.code_idand requires it inWHITELISTED_CODE_IDS(smartcontracts/contracts/factory/src/contract.rs). Pair swap / provide / withdraw / limit escrow never re-querycode_idand never consult the factory whitelist again.Terra Classic
MsgMigrateContract(wasm admin of that instance) can change the instance’scode_idto any stored wasm. The pair keeps transferring the same CW20 address. If the new wasm is fee-on-transfer, rebase, or otherwise not 1:1, reserves andPENDING_ESCROWdesync from the banked balance — same class as #448 / ALPHA 8654.BlacklistToken/ pair pause are after-the-fact (detection window).Factory today:
GetWhitelistedCodeIds=[6036, 10184]. Anyone who pays the CreatePair fee can list any instance currently on those templates, then the instance admin (often not us) can migrate it off the whitelist.Why this is not only #581 / SpaceUSD
MsgMigrateContractafter listing. Pair will keep trading.Live factory (2026-08-20): 14 pairs, 13 unique assets. None of the currently listed assets have a third-party wasm admin — all are protocol keys — so existing TVL is our-key / our-upgrade risk, not an outside issuer. That does not close the permissionless 6036 CreatePair → migrate path, and it does not make SpaceUSD safe to list.
cl8y2_adminterra1xsecn4…(wrap-stack CMM gov)terra1zlmv2…terra1zlmv2…terra133n0pv8…Exploit sketch (H-01 / P2)
CreatePairwith a currently-whitelisted CW20 that still has wasm admin.MsgMigrateContractonto a FoT / rebase / hook wasm not onWHITELISTED_CODE_IDS.Same sequence works for SpaceUSD after a #581 go, with the issuer key we do not control.
Mitigations to decide (not all required)
Record a go/no-go per option. Combining (A)+(D) is the usual fail-closed shape; (B) is stricter; (C) is listing policy only.
code_idon pair write paths — swap / provide / withdraw / limit place+fill queryContractInfo.code_idand abort unless it is still on the factory whitelist (or equals a snapshot stored at CreatePair). Stops migrate-to-unlisted-wasm. Gas + LCD query on every trade.code_idat CreatePair — store the listing-time ids on the pair; any instance migrate (even to another whitelisted template) fails closed until governance updates the pin. Honest 10184 upgrades would freeze those pairs until a pair migrate / admin msg.ContractInfo.adminis set.clear-contract-adminis irreversible. Blocks SpaceUSD unless the issuer clears admin. Does not fix tokens already listed. Does not stop a 6036 CreatePair unless we also enforce it in the factory.(pair, asset, code_id)at listing; alert +SetPairPaused/BlacklistTokenon drift. Detection window remains. Needed even with (A) for forensics.Do not add pair balance-delta / FoT swap math to “make migrated tokens work” (H-01).
Audit / decision checklist
code_idcode_id+ wasmadmin(refresh the table above)CreatePairhonest token → migrate to FoT → swap must fail), docs insecurity-model.md+cw20-whitelist-policy.mdAcceptance
Labels / owner
security + contracts (factory/pair) / ops (watch + incident) / governance (2-of-3 pause/blacklist, wrap-stack admins)
Priority
P1 / High — protocol whitelist invariant, not an 8266-only audit note. Not a reason to pause existing protocol-admin pairs by itself; is a reason to stop expanding the whitelist (8266) and to treat permissionless 6036 listing as in-scope.
marked as related to #581
marked as related to #558
marked as related to #448
marked as related to #377
mentioned in issue #581
mentioned in issue #558
Opened from #581 item (4). GitLab
blockslink type is not available on this project’s license (Blocked issues not available for current license); gate is process: #581 must not go on 8266 while this issue is open. Related: #558, #448, #377.Suggested direction:
(B) so that behavior of the token is guaranteed to remain the same post listing
(A) so that if a codeid is discovered that has a bug can be removed from whitelist blocking all trades until tokens are migrated
Do not proceed with (D) as (A) provides the same functionality with better coverage.
(E) Rejected as many quality tokens require admin or may need to be upgraded.
mentioned in merge request !1107
On-chain (A)+(B) shipped in !1107 (
fix/582-cw20-code-id-pin).make verify-issue-582passed twice (pin + FoT migrate + whitelist freeze + refresh refuses unlisted live id + pair migrate backfill). Playbook:skills/AGENTS_CW20_CODE_ID_PIN.md. Invariant F6.Audit / decision checklist
code_id+ factory whitelist —pair/src/asset_code_id_guard.rs(assert_asset_code_idson swap/provide/withdraw/limit place+fill/cancel/claim). Tests:asset_code_id_pin_tests::*.docs/runbooks/cw20-whitelist-policy.md(not a live LCD re-query).security-model.md,cw20-whitelist-policy.md,contracts-terraclassic.md, SEC-D06, H05).Acceptance
Not in this MR (follow-ups)
On-chain mitigation is in !1107 (
fix/582-cw20-code-id-pin). Chosen path is (B) pin + (A) whitelist re-check. Factory 1.9.0 + pair 1.15.0. Verify:make verify-issue-582.Audit / decision checklist
code_idbefore this MR; they now fail closed viaassert_asset_code_idson swap / provide / withdraw / limit place+fillcode_id+ wasmadmin(table in this issue is still 2026-08-20; not re-queried in this MR)RefreshPairAssetCodeIds)CreatePairhonest token → migrate to FoT → swap fails; refresh refuses unlisted live id), docs insecurity-model.md+cw20-whitelist-policy.md+ invariant F6 +skills/AGENTS_CW20_CODE_ID_PIN.mdAcceptance
Do not
AddWhitelistedCodeId 8266until factory then pair wasm is migrated.mentioned in commit
5402859e3dmentioned in issue #584
marked as related to #584
mentioned in merge request !1108
mentioned in commit
711ff9cad5Merge of !1107 (2026-08-21)
!1107 is merged to
main(711ff9ca). Localmake verify-issue-582passed (6 pin tests ×2 + docs greps). GitLab CI on the MR failed withci_quota_exceededontest-contracts/cargo-audit-smartcontracts/gitleaks— not a code failure; CI was not waited on.Acceptance vs this merge
main(factory 1.9.0, pair 1.15.0, invariant F6).AddWhitelistedCodeId 8266.docs/runbooks/cw20-whitelist-policy.mdis still the 2026-08-20 snapshot. Re-query LCDContractInfo(code_id + admin) before any #581 go — not done in this merge.CancelLimitOrder/ClaimExpiredLimitOrder/WithdrawLiquiditydrift cases.UpdateLimitOrderPrice/CleanLimitBookstay ungated (no CW20 movement).Follow-ups: execution on #584. Frontend/indexer freeze visibility is a separate P1 (filed after merge if missing).
marked as related to #585
mentioned in issue #585
P1 freeze UX follow-up: #585. Live migrate tracker remains #584.
Live columbus-5 status (execution on #584, this contract issue stays closed): factory 1.9.0 / code 11602 is live. 11/14 pairs are 1.15.0 with pins; 3 pairs still 1.14.0 / 11586 (no pin). Listed assets remain 10184 + 6036. #581 / 8266 still no-go until #584 14/14 + smoke.
Live F6 is on columbus-5: factory 1.9.0 / 11602, all 14 pairs 1.15.0 / 11601 with pins,
pair_code_id11601. Tracker #584 closing. #581 / 8266 still no-go (source review). Freeze UX: #585.mentioned in merge request !1110
mentioned in merge request !1111
UX / routing visibility for live F6 freeze (F389-01 / F389-02) is in !1111 / #585. Indexer
code_id_frozen+ hop exclude is not a substitute for on-chain pin + write-path re-check (this issue). Option (D) stays rejected as a security control.mentioned in issue #589
mentioned in issue #592
mentioned in issue #603
marked as related to #603
mentioned in issue #626
marked as related to #626
mentioned in issue #627
marked as related to #627
mentioned in issue #634
mentioned in issue #690
PlasticDigits referenced this issue2026-09-24 09:36:00 +00:00