security(community-tax): ratify InstantiateMsg.launcher so GetLauncherOrigin cannot spoof EnableFeature #1229
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#1229
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?
Summary
InstantiateMsg.launcheris an unauthenticated caller field.GetLauncherOriginreturns that stored address with no instantiate-sender check, so a token that was not created by the official launcher can claim it. After #606 (T606-3 / T606-4), the official launcher treatsGetLauncherOrigin.launcher == selfas proof it may forwardEnableFeature(and AutoLP bind) for that token.This is auth / confused-deputy today on live
community-tax-token+community-token-launcher. It is not a pair LP drain. Protocol invoice integrity and launcher-only privileges are in scope.Do not reopen #606 (Enable Feature path was Unauthorized; that is fixed). Do not fold into #592 (template + catalog-filter design). Rogue
--admininstantiate was assumed to yieldGetLauncherOrigin.launcher == null(O601-4 / #620). That assumption does not hold if the instantiator setslauncher.Impact (Immunefi-style)
Today (auth): Anyone who can instantiate the listed tax wasm (code_id is factory-whitelisted) can set
launcherto the official launcher. The official launcher then:EnableFeaturefor that instance (enable_featurequeriesGetLauncherOriginand compares toenv.contract.address).origin.launcheras anEnableFeaturepayer andTransfers the invoice to itscmm_treasury(also caller-chosen at instantiate). Protocol CMM need not receive the 50 UST1.AutoV2Lp, instantiates an AutoLP sister with CMM as wasm admin andBindAutolp(gated only by storedCONFIG.launcher).Catalog: If attestation is
GetLauncherOrigin == env launcherwithout a launcher instantiate tx and CMM wasm admin, a rogue instance can look official. #626 already asked for code_id + CMM admin + launcher-tx + origin; origin alone must not be enough.Not claimed: draining unrelated pair LP, stealing another manager’s UST1 in a different tx, or wasm-admin takeover of launcher-created tokens. Direct
EnableFeatureas manager (no launcher) is out of this ticket except where the same unratified field is the gate.Current codebase
Token instantiate copies
msg.launcherintoCONFIGafteraddr_validateonly.info.senderis not required to equal that address.QueryMsg::GetLauncherOriginreturnscfg.launcherwith no origin proof.Official create does stamp
launcher: Some(env.contract.address)from the launcher (community-token-launchercreate_token). Direct wasm instantiate can stamp the same string without going through that crate.Launcher
enable_feature(post-#606):payer == GetConfig.managerGetLauncherOrigin.launcher == this launcherSendhookEnableFeatureto the token (no second send to CMM)AutoV2Lp→ instantiate AutoLP (CMM admin) + laterBindAutolpToken
invoice::assert_invoice_payer:EnableFeatureallowspayer == managerorpayer == config.launcher.BindAutolpisinfo.sender == config.launcher.There is no launcher-side map of tokens created in
REPLY_TOKEN. Origin is entirely the token’s self-report.Why this is needed
#606’s long-term path made the official launcher a privileged
EnableFeaturedeputy because origin said so. Origin is not an attestation ofWasmMsg::Instantiatefrom that deputy. Retail Create Token, catalogattested_cmm/created_by_launcher, and AutoLP bind all inherit that lie.Invoice 50 UST1 is meant to land at protocol CMM (T592-4). A self-reported launcher plus a self-reported
cmm_treasurybreaks that for the official forward path.Constraints / guardrails
EnableFeature, orBindAutolpfrom the official launcher. Catalog must still refuse unverified admin (O601-4, F6).payer == managerfor arbitrary contracts. Do not accept a second launcher address after instantiate (no managerUpdateSettingsof origin).AdoptMigrateMsg.official_launcherstays CMM-only same-crate/adopt (#626). This ticket does not add a public adopt stamp.EnableFeatureinto a settings batch.Relevant files
smartcontracts/contracts/community-tax-token/src/contract.rslauncherstamp;GetLauncherOrigin;BindAutolpsmartcontracts/contracts/community-tax-token/src/msg.rsInstantiateMsg.launcher;LauncherOriginResponse; adoptofficial_launchersmartcontracts/contracts/community-tax-token/src/invoice.rsEnableFeaturepayer; invoicecmm_treasurysmartcontracts/contracts/community-token-launcher/src/contract.rscreate_tokenstamp;enable_featureorigin query; AutoLP replysmartcontracts/contracts/community-token-launcher/src/state.rssmartcontracts/contracts/community-tax-token/src/state.rsConfig.launcherGetLauncherOriginaloneskills/AGENTS_COMMUNITY_TAX_CW20.md/ Create Token skillaudit_pocor sibling)Recommended direction
Defense in depth; ship both on-chain (one migrate):
InstantiateMsg.launcherisSome(addr), requireinfo.sender == addr(after validate). Otherwise ignore / storeNone(prefer reject ifSomeand sender mismatch — fail closed). Honest launcher instantiate already hassender == launcher. Direct instantiate cannot claim the official launcher.REPLY_TOKEN, persist created token addresses.enable_feature/ AutoLP bind only if the token is in that set. Do not treatGetLauncherOriginas sufficient. Query mismatch →Unauthorized; UST1 not forwarded.code_id+ CMM admin + instantiate tx from env launcher (or ratified origin and those LCD checks). Document thatGetLauncherOriginis not a listing proof.Prefer (1)+(2) in one wasm pair so a future query bug cannot re-open the deputy.
Acceptance criteria
launcher = official launcherandinfo.sender ≠ that launcherdoes not persist that origin (GetLauncherOrigin.launcherisNoneor instantiate reverts).CreateTokenstill storesGetLauncherOrigin.launcher == launcherand CMM wasm admin.EnableFeaturefor a token it did not instantiate (including AC1 spoof attempt) →Unauthorized; SKU unchanged; launcher does not forward UST1 to that token; protocol CMM is not used as a pass-through into attackercmm_treasury.EnableFeaturevia launcher → SKU on, protocol CMM receives 50 UST1.BindAutolp/ AutoLP instantiate from official launcher does not run for a token missing from the launcher’s created set.EnableFeature(no launcher) unchanged for a token withlauncher == None.attested_cmm.make verify-issue-606stays green and new origin tests pass.Test plan (functional)
CreateToken(0 SKU)EnableFeaturevia launcherlauncherNone; launcherEnableFeatureUnauthorizedlauncher= official, sender = EOANoneEnableFeaturelauncher= self (a dummy contract)EnableFeatureTest plan (attack / abuse)
Non-exploitative. Assert reverts and balances; do not publish a mainnet recipe.
launcher= official launcher,cmm_treasury= attacker, then launcherEnableFeaturemanager= attacker (deputy check would pass if origin were trusted)enable_featurefor a launcher-created tokenEnableFeatureAutoV2Lp on spoofed originBindAutolpAdoptMigrateMsg.official_launcherfrom non-CMM adminVerification criteria
audit_poc(or sibling) for spoofedGetLauncherOrigin— the case must fail closed, not document a working deputy.cargo test -p cl8y-community-tax-tokenand-p cl8y-community-token-launcher(includingaudit_poc).make verify-issue-606/ catalog greps for O601-4 remain green.Out of scope
First-pass model recommendation
Recommendation: grok-high
Rationale: CosmWasm token + launcher auth (instantiate origin, invoice deputy, AutoLP bind). Founder-required surface (contracts / wasm / wallet 2-of-3 migrate). Crosses
community-tax-tokeninstantiate/query/invoice andcommunity-token-launcherenable/reply state, plus catalog attestation. Not a local three-file edit; Composer criteria fail on security, wasm, and cross-crate protocol. Verify with crate tests + inverted origin case + #606 regression, then 2-of-3 migrate — not UI-only.