LAUNCH-BLOCKER: Terra recipient address accepted without bech32 checksum validation #117
Labels
No labels
agent:implement
agent:ready
backend
bug
cannot-reproduce
confirmed
desktop
docs
documentation
duplicate
enhancement
feature
frontend
good first issue
help wanted
high-risk
in-review
invalid
mobile
needs-triage
P0-critical
P1-high
P2-medium
P3-low
qa
QA
question
ready
report
responsive
security
security-escalate
smart-contract
solana
tablet
test-pass
ux
wallet-issue
wallet:keplr
wallet:metamask
wallet:station
wallet:walletconnect
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-bridge-monorepo#117
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?
Found during Bridge #116 Section C verification.
Repro
Why launch-blocker
Terra bech32 addresses checksum-encode the last 6-8 chars. Typos silently accepted = funds bridge to wrong address, permanently lost.
Fix
bech32 decode + checksum validation on Terra recipient field.
cc @PlasticDigits
@PlasticDigits extending scope — same silent-typo bug confirmed for EVM recipient field.
Repro 2 (EVM side)
0xc46b15f4B56489a16F561c22D5F0BA8bdCa806500xc46b15f4B56489a16F561c22D5F0BA8bdCa80651EVM uses EIP-55 mixed-case checksum. Same class of validation gap — format-level regex accepts the string but no checksum verification on the address. Fix should cover Terra (bech32) AND EVM (EIP-55) AND Solana (base58 checksum test needed still) in one recipient-validation pass.
Screenshot attached on web UI.
mentioned in commit
b12879afecGL-117 fix merged to
main(commitb12879a)@brouie please verify after v0.1.83+ is deployed (or any build that includes this commit).
What changed (original + extended scope)
terra1…): BIP173 bech32 checksum is verified inhashVerificationbefore decode;isValidTerraAddress/terraAddressToBytes32reject checksum typos (e.g. last char edit in the GL repro).0x…):isValidEvmAddressnow uses viemisAddress(addr, { strict: true })so EIP-55 is enforced when the user enters mixed case (catches the…50→…51repro).isValidSolanaAddress(PublicKey/ curve check); Solana→Terra deposits now encodeterra1…viaterraAddressToBytes32(checksum-valid addresses only).TransferFormdisables submit when the recipient fails the route’s validator (not only inline field styling).Docs / agents
docs/FRONTEND_BRIDGE_INVARIANTS.md(INV-RCP1)skills/agent-bridge-recipient-validation.mdChecklist for your verification
terra1…still works.Leaving issue open until you sign off on the deployed build.
mentioned in issue #119
mentioned in issue #116
@PlasticDigits verification on the live fix — Terra and EVM both working beautifully:
Terra bech32 — PASS
terra17ks3ncgx9q4q9d2rpfv0uafs732derhxvx0wntacceptedterra17ks3ncgx9q4q9d2rpfv0uafs732derhxvx0wny(last char swapped) → 'Invalid address' firesEVM EIP-55 — PASS
Solana — PARTIAL
Cu6Q7uU5qHsFuzAkrcxAz1xqrpUNB7vtEFNuSuQ1aDB0(last chary→0) → 'Invalid Solana recipient address' fires ✅Cu6Q7uU5qHsFuzAkrcxAz1xqrpUNB7vtEFNuSuQ1aDBo(last chary→o) → silently accepted, no error, CTA activeThe first case is caught because
0is not a valid base58 character (base58 excludes 0/O/I/l). The second case substitutes a valid base58 character but one that breaks the ed25519 public-key checksum — which doesn't appear to be validated.Solana addresses are 32-byte ed25519 public keys base58-encoded. Format check alone is insufficient; need to verify the decoded bytes are a valid ed25519 point, or at minimum verify the base58-decoded length is exactly 32 bytes.
Screenshots
All 5 tests attached via web UI — Terra valid, Terra corrupted, EVM mixed-case corrupted, Solana with
0swap (caught), Solana withoswap (not caught).Closing scope: Solana validation still has the same class of typo gap as the original bug. Terra + EVM good.
mentioned in commit
9ec963582b@brouie follow-up to your Solana — PARTIAL verification (y→
ooff-curve typo still accepted after GL-117 first pass).What we changed
@solana/web3.jsnew PublicKey(string)only checks base58 decode to 32 bytes — it does not verify the point is on the ed25519 curve, so a last-character change can stay in the base58 alphabet and still decode (your…DBy→…DBocase).isValidSolanaAddressnow decodes the key and requiresPublicKey.isOnCurve(same check as the Solana public API), so off-curve 32-byte strings are rejected.solanaAddressToBytes32/bytes32ToSolanaAddressuse the same on-curve rule so encoding paths cannot smuggle an invalid recipient.encodeDestAccountBase64Solana branch now usessolanaAddressToBytes32+viemhexToBytes(single validation path).isValidSolanaAddressbeforenew PublicKey(defense in depth if submit logic ever drifts).docs/FRONTEND_BRIDGE_INVARIANTS.md(INV-RCP1) andskills/agent-bridge-recipient-validation.mdupdated; unit tests inpackages/frontend/src/services/solana/address.test.ts(includes your Brouie repro) +useTerraDeposit.test.ts.Pushed to
main:9ec9635Checklist to verify
Cu6Q7uU5qHsFuzAkrcxAz1xqrpUNB7vtEFNuSuQ1aDBy→ field valid, CTA as expected.…DB0) → still rejected (non-base58).…DBo) → Invalid Solana recipient address, CTA disabled (ed25519 off-curve).…DBoin the recipient field, native/CW20 deposit should not build a 32-byte dest from that string (form should block;encodeDestAccountBase64throws the same class of error if invoked).11111111111111111111111111111111(system default) and normal wallets still accepted.Leaving the issue open per process until you sign off on a build that includes this commit.
mentioned in commit
3d51a91958Follow-up:
docs/SOLANA_BRIDGE_INVARIANTS.mdintro now cross-links to INV-RCP1 andskills/agent-bridge-recipient-validation.md(commit3d51a91) so program-side docs point at the same GL-117 UI rule.@PlasticDigits closing on
cb7f871. Solana follow-up shipped.Live mainnet from Friday already covered Terra bech32 + EVM EIP-55 (PASS for both with chain-specific helper text). Solana piece is what was still open from my Friday note (y to o silently accepted because o stays in base58 alphabet but decodes off-curve).
Fix at services/solana/address.ts: parseOnCurveUserPubkeyBase58 wraps PublicKey + PublicKey.isOnCurve. Off-curve points throw, so isValidSolanaAddress returns false on the y to o repro.
Test at services/solana/address.test.ts (5/5 passing): includes my exact Cu6Q...aDBy to aDBo case with explicit assertion that PublicKey.isOnCurve(badO) is false. Non-base58 case (aDB0) also covered.
Validator wired into:
Both my Friday repros now reject:
Closing.