Support WalletConnect Bitcoin proof of ownership - #6193
Conversation
b735b50 to
9662a95
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Stale comment
Security verification (icon URI / debug logging)
{ "is_real_finding": false, "severity": "none", "false_positive_risk": "low", "exploitability": "None at medium+. Icon URI is loaded client-side via FastImage for display only; same pre-existing WC pattern as WcSmartContractModal. No SSRF, no RN XSS via javascript:, no secrets logged.", "affected_lines": "WalletConnectService.tsx:80-83 (new bip122 path); mirrors existing :140-143 → WcSmartContractModal; WcSignMessageModal EdgeCard icon={dAppIcon}; useWalletConnect console.log typing-only", "rationale": "PR adds bip122 signMessage UI and reuses the established peer.metadata.icons → SVG fallback → EdgeCard/FastImage path already used for smart-contract WC requests. No new console logging of signatures, keys, seeds, or WC URIs; no TLS bypass or CSP changes. A dapp-controlled icon URL can at most show a misleading logo (pre-existing WC trust model), which is not a medium+ exploit in React Native FastImage." }Sent by Cursor Security Agent: Security Reviewer
There was a problem hiding this comment.
Stale comment
WalletConnect bip122 binding review (A–F)
Read-only verification of address/message binding for Bitcoin WC PoO. No MEDIUM+ exploitable findings in the six hypothesized issues.
A) Optional
addressignored → always sessionaccounts[0]Exploitable: no · Discard (compat gap, not a bind break)
asBip122SignMessageParamsonly cleansmessage+ optionalaccount. Signing and the modal both usesession.namespaces.bip122.accounts[0]→split(':')[2]. The optional specaddress(andprotocol) are ignored, so Edge cannot be steered onto a change/ordinals/other key. Shown address = signed address = responseaddress. Worst case is incomplete spec support, not UI/signature mismatch.B)
requestedAccount !== publicAddressCAIP-10 vs rawExploitable: no · Discard (fail-closed)
publicAddressis the raw CAIP-10 account suffix. A full CAIP-10accountfails inequality and is rejected. Omittedaccountproceeds with the session address (still bound). No fail-open wrong bind.C)
getWalletIdFromSessionNamespace+ multi-namespaceExploitable: no · Discard
approveSession→getSupportedNamespacesreturns one namespace for the selected wallet only, so Edge-created sessions are single-namespace. First-match wallet binding is fine for bip122-only sessions. Hypothetical multi-namespace confusion is not reachable via this approve path.D)
getAccountAddressesauto-approveExploitable: no · Discard
Returns only
{ address: publicAddress }already advertised at session approval. No extra addresses/keys.E)
sendTransfer/signPsbtif only PoO methods advertisedExploitable: no · Discard
Advertised methods are
getAccountAddresses+signMessageonly. Required unsupported methods throw at approve. Unknown bip122 methods hitdefault→rejectRequest. bip122 is handled before any EVM/Cosmos payload parser.F)
chainIdreference (mainnet genesis vs other bip122)Exploitable: no · Informational / Low at most — discard as MEDIUM+
Request routing checks namespace
bip122only, not genesis reference vs session chains. Onlybitcoinregisters bip122 (mainnet genesis). Signature still uses the session mainnet account; wrong-reference requests do not change the key or enable spends. Defense-in-depth chain equality would be nice, not a MEDIUM exploit.
Summary: Binding is session-account-centric and fail-closed on account mismatch. Optional
address/protocolincompleteness and missing chain-reference check are non-exploitable relative to PoO goals.Sent by Cursor Security Agent: Security Reviewer
9662a95 to
584cbb8
Compare
There was a problem hiding this comment.
Stale comment
Security verification (platform patterns)
Read-only check of CHANGED code in
WalletConnectService.tsx,WcSignMessageModal.tsx, anduseWalletConnect.tsxfor TLS bypass, sensitive logging, unsafe HTML/icon sinks, and unauthenticated identity.[ { "exploitable": false, "category": "TLS validation bypass", "evidence": "No rejectUnauthorized, insecure SSL/TLS agents, or certificate-validation bypass in the changed WC files. Network setup remains Web3Wallet/Core init only." }, { "exploitable": false, "category": "Sensitive debug/diagnostic logging", "evidence": "No new logs of WC messages, signatures, addresses, or credentials. Pre-existing WC error paths still use console.log(..., String(e)) / console.log(e) with type-only edits. The unrecognized-session console.log remains a static string (pre-existing). Message/signature stay in UI or approveRequest payloads only." }, { "exploitable": false, "category": "CSP / Trusted Types / unsafe HTML sinks", "evidence": "React Native path: dAppIcon from session.peer.metadata.icons uses the same svg→fallback then EdgeCard/FastImage uri pattern as existing EVM WcSmartContractModal in WalletConnectService.tsx. No WebView HTML, dangerouslySetInnerHTML, or Trusted Types sinks introduced." }, { "exploitable": false, "category": "Unauthenticated routes / identity from headers/JWT", "evidence": "No HTTP routes, header-based identity, or JWT handling in these files. Wallet binding uses WC session topic store / session namespace accounts only." } ]Verdict: No NEW production security/privacy risks in these platform-pattern categories from this PR.
Sent by Cursor Security Agent: Security Reviewer
There was a problem hiding this comment.
Stale comment
Security verification: remembered walletId vs session address
Verdict: discard — not a medium+ exploitable issue.
Checked
resolveSessionWalletId/lookupSessionWallet/rememberSessionWallet/getWalletIdFromSessionNamespace/handleBip122Requestand the UTXOsignMessagepath.
Missing GUI ownership check is real, but not exploitable for bad signatures.
resolveSessionWalletIddoes return a remembered topic→walletId without comparing it tosession.namespaces.*.accounts[0]. Signing still passespublicAddressfrom the session intowallet.signMessage.edge-currency-plugins refuses foreign addresses.
UtxoEngine.signMessageresolvespublicAddress→ scriptPubkey, loads it from the wallet data layer, and throwsAddressNotOwnedErrorwhenpathis missing (or the string is not this chain’s address). A mismatched remembered wallet cannot produce a valid proof for the session address; the modal’s approve path rejects the WC request on that error.walletId reuse / cross-wallet mixup lacks a concrete path. Edge wallet ids are
hmacSha256-derived from the wallet key material, not recycled counters. A deleted wallet makescurrencyWallets[walletId]undefined and the request is rejected. A stale mapping to a different live wallet still hitsAddressNotOwnedErrorunless that wallet actually owns the address (same keys).dataStore is not an external write surface.
account.dataStoreis the unlocked account’s encrypted storage-wallet disklet. Writing it already implies account access, which already implies the ability to sign with owned wallets. A WC dapp cannot plant topic→walletId entries.No medium+ security/privacy finding filed from this hypothesis. Defense-in-depth (verify remembered wallet still matches the session CAIP-10 account before opening the modal) would be optional hardening only, not a required fix for exploitability.
Sent by Cursor Security Agent: Security Reviewer
There was a problem hiding this comment.
Stale comment
Security verification: WcSignMessageModal message truncation (re-check)
Read-only check of current code (not prior review state).
{ "still_truncates": false, "severity_if_true": null, "evidence": "Message EdgeRow now uses maximumHeight=\"large\" → EdgeRow textHeights.large=0 → EdgeText gets numberOfLines={0}; {...rest} keeps 0 (overrides newline bump). RN treats 0 as unlimited, so no ellipsis of unseen suffix/prefix; ScrollView can show full payload before approve.", "confidence": 0.92 }Path:
WcSignMessageModalmessage row →EdgeRow(large→0) →EdgeText(numberOfLines+{...rest}) → RNTextunlimited lines. Prior MEDIUM finding (defaultmedium/3-line ellipsis) is addressed.Sent by Cursor Security Agent: Security Reviewer
There was a problem hiding this comment.
Stale comment
Verification: message body truncation via
maximumHeight="large"Verdict: not truncated. A user cannot approve a WalletConnect
signMessagewhile an ellipsized/unseen suffix is hidden byEdgeRow/EdgeText/Textline limiting.{ "verified_truncation": false, "reason": "EdgeRow maximumHeight=\"large\" sets numberOfLines=0. EdgeText's {...rest} after the explicit numberOfLines prop overwrites any newline-bump back to 0, so RN <Text> receives numberOfLines={0}. Per RN docs and Android native (ReactBaseTextShadowNode / ReactTextView), 0 unsets the line cap (unlimited). ellipsizeMode=\"tail\" is inert without a positive line limit; Android updateView even nulls TruncateAt when unlimited. The modal ScrollView can reveal the full wrapped body.", "actual_props_to_Text": { "numberOfLines": 0, "ellipsizeMode": "tail", "adjustsFontSizeToFit": true, "allowFontScaling": false, "minimumFontScale": 0.65 }, "exploit_with_newlines": false, "exploit_with_long_single_line": false }Trace (concrete)
WcSignMessageModalmessage row:maximumHeight="large"+body={message}EdgeRow:textHeights.large = 0→ passesnumberOfLines={0}andellipsizeMode="tail"toEdgeTextEdgeText: default is 1, then optionalbump, but{...rest}is spread afternumberOfLines={numberOfLines}, andreststill contains the originalnumberOfLines: 0→ final value on<Text>is 0 (bump never sticks when caller passed 0)- RN 0.79 semantics for
numberOfLines={0}: unlimited (docs: “unsetting this value… no lines restriction”). Android:0 → ReactConstants.UNSET/ViewDefaults.NUMBER_OF_LINES;updateViewsetssetEllipsize(null)when unlimitedPrior MEDIUM finding (default
medium→ 3-line ellipsis) is addressed by the currentmaximumHeight="large"on the message row.Sent by Cursor Security Agent: Security Reviewer
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 584cbb8. Configure here.
584cbb8 to
0ca67bd
Compare















Description
Bitcoin was never registered for WalletConnect in Edge, so a bip122 session
proposal (Bringin's Proof of Ownership flow) matched no plugin. Because Bringin
lists its chains as OPTIONAL namespaces, the existing "no wallets meet dapp
requirements" guard never fired, and
WcConnectionsScenehanded the walletpicker an empty
allowedAssetsarray. That filtered out every wallet the userowns while leaving the create-wallet rows visible, which is why the reporter saw
Edge steer them into Create Wallet instead of listing their BTC wallets.
What this branch does:
bitcoinwithwalletConnectV2ChainIdbip122:000000000019d6689c085ae165831e93,and widens
WalletConnectChainId.namespaceto acceptbip122.signMessageandgetAccountAddresses, plus thebip122_addressesChangedevent.sendTransferand
signPsbtare deliberately absent: the UTXO plugin has no WalletConnectpayload parser, so advertising them would accept spend requests Edge cannot
fulfill, and a dapp requiring them is now rejected cleanly instead.
session_requests to their own handler.signMessageopens anew
WcSignMessageModal(dApp, wallet, address, message, Sign / Reject) andsigns with
signatureFormat: 'bip137', the encoding SegWit verifiers such asBringin require;
getAccountAddressesreturns the address the session alreadydisclosed. Signing always uses the address the session advertised, since that
is the one the dapp verifies against.
NO_WALLETS_DAPP_REQUIREMENTSwhen the proposal matches no wallet atall, so an unsupported dapp gets a toast rather than a create-only picker.
from the absence of a
walletConnectV2ChainId.Review-round changes on top of that:
one, matching the receive address Edge shows the user, rather than the wrapped or
legacy address
getReceiveAddressreturns. A wallet that reports no address isskipped instead of throwing, which would have taken down session listing for every
other wallet.
account.dataStore,so an incoming request still resolves after a UTXO receive address rotates. The
address on the session stays the fallback.
unsupported method, an account the session does not hold, or a payload the cleaners
reject all send a WalletConnect rejection instead of leaving the dapp waiting.
three lines, since the message is what the user is authorizing, and it ignores a
dismissal while a signature is in flight so the request cannot be answered twice.
No dependency change is needed: BIP137 message signing already shipped in
edge-currency-plugins v3.12.0 (#454), and
signMessageis a coreEdgeCurrencyWalletmethod.Asana: https://app.asana.com/0/1215088146871429/1216274035296616
CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Requirements
If you have made any visual changes to the GUI. Make sure you have: