From 1bb37dcbce806764b47c6a54d98b3f74b606f5eb Mon Sep 17 00:00:00 2001 From: Antonio Bello <1085903+jeden@users.noreply.github.com> Date: Tue, 5 Nov 2024 05:29:07 +0100 Subject: [PATCH 1/2] [Bug] Fix in app browser security issue https://github.com/Outblock/FRW-iOS/issues/485 --- FRW/Modules/TrustProvider/TrustJSMessageHandler.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift b/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift index 14c2895e..18fba9bc 100644 --- a/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift +++ b/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift @@ -90,7 +90,7 @@ extension TrustJSMessageHandler { } extension TrustJSMessageHandler: WKScriptMessageHandler { - func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let json = message.json guard let method = extractMethod(json: json), let id = json["id"] as? Int64, @@ -103,7 +103,8 @@ extension TrustJSMessageHandler: WKScriptMessageHandler { switch method { case .requestAccounts: log.info("[Trust] requestAccounts") - handleRequestAccounts(network: network, id: id) + let url = message.frameInfo.request.url ?? webVC?.webView.url + handleRequestAccounts(url: url, network: network, id: id) case .signRawTransaction: log.info("[Trust] signRawTransaction") case .signTransaction: @@ -166,7 +167,7 @@ extension TrustJSMessageHandler: WKScriptMessageHandler { } extension TrustJSMessageHandler { - private func handleRequestAccounts(network: ProviderNetwork, id: Int64) { + private func handleRequestAccounts(url: URL?, network: ProviderNetwork, id: Int64) { let callback = { [weak self] in guard let self = self else { return @@ -176,7 +177,6 @@ extension TrustJSMessageHandler { let title = webVC?.webView.title ?? "unknown" let chainID = LocalUserDefaults.shared.flowNetwork.toFlowType() - let url = webVC?.webView.url let vm = BrowserAuthnViewModel(title: title, url: url?.host ?? "unknown", logo: url?.absoluteString.toFavIcon()?.absoluteString, From 663df23181823ad9342bff69593577dca32536fd Mon Sep 17 00:00:00 2001 From: Antonio Bello <1085903+jeden@users.noreply.github.com> Date: Tue, 5 Nov 2024 23:08:18 +0100 Subject: [PATCH 2/2] - Added more cases --- .../Browser/Handler/JSMessageHandler.swift | 63 ++++++++++--------- .../TrustProvider/TrustJSMessageHandler.swift | 20 +++--- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/FRW/Modules/Browser/Handler/JSMessageHandler.swift b/FRW/Modules/Browser/Handler/JSMessageHandler.swift index 6403f245..fc253cf9 100644 --- a/FRW/Modules/Browser/Handler/JSMessageHandler.swift +++ b/FRW/Modules/Browser/Handler/JSMessageHandler.swift @@ -29,26 +29,28 @@ class JSMessageHandler: NSObject { extension JSMessageHandler: WKScriptMessageHandler { func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) { + let url = message.frameInfo.request.url ?? webVC?.webView.url + log.debug("did receive message") - + if message.name == TrustWeb3Provider.scriptHandlerName { return } - + switch JSListenerType(rawValue: message.name) { case .message: guard let msgString = message.body as? String else { log.error("JSListenerType.message body invalid") return } - - handleMessage(msgString) + + handleMessage(msgString, url: url) case .flowTransaction: guard let msgString = message.body as? String else { log.error("JSListenerType.flowTransaction body invalid") return } - + handleTransaction(msgString) default: log.error("can't handle message", context: message.body) @@ -93,7 +95,7 @@ extension JSMessageHandler { } extension JSMessageHandler { - private func handleMessage(_ message: String) { + private func handleMessage(_ message: String, url: URL?) { if message.isEmpty || processingMessage == message { return } @@ -115,7 +117,7 @@ extension JSMessageHandler { handleService(message) } else if jsonDict["type"] as? String == JSMessageType.response.rawValue { log.debug("will handle view ready response") - handleViewReadyResponse(message) + handleViewReadyResponse(message, url: url) } else { log.warning("unknown message", context: message) } @@ -179,7 +181,7 @@ extension JSMessageHandler { // MARK: - Response extension JSMessageHandler { - private func handleViewReadyResponse(_ message: String) { + private func handleViewReadyResponse(_ message: String, url: URL?) { do { guard let data = message.data(using: .utf8) else { log.error("decode message failed") @@ -210,13 +212,13 @@ extension JSMessageHandler { switch fcl.serviceType { case .authn: log.debug("will handle authn") - handleAuthn(message) + handleAuthn(message, url: url) case .authz: log.debug("will handle authz") - handleAuthz(message) + handleAuthz(message, url: url) case .userSignature: log.debug("will handle user signature") - handleUserSignature(message) + handleUserSignature(message, url: url) default: log.error("unsupport service type", context: fcl.serviceType) } @@ -225,7 +227,7 @@ extension JSMessageHandler { } } - private func handleAuthn(_ message: String) { + private func handleAuthn(_ message: String, url: URL?) { do { guard let data = message.data(using: .utf8) else { log.error("decode message failed") @@ -247,7 +249,7 @@ extension JSMessageHandler { let network = authnResponse.config?.client?.network ?? "" let chainID = Flow.ChainID(name: network) let vm = BrowserAuthnViewModel(title: title, - url: webVC?.webView.url?.host ?? "unknown", + url: url?.host ?? "unknown", logo: authnResponse.config?.app?.icon, walletAddress: WalletManager.shared.getPrimaryWalletAddress(), network: chainID) { [weak self] result in @@ -282,7 +284,7 @@ extension JSMessageHandler { } } - private func handleAuthz(_ message: String) { + private func handleAuthz(_ message: String, url: URL?) { do { guard let data = message.data(using: .utf8) else { log.error("decode message failed") @@ -301,13 +303,13 @@ extension JSMessageHandler { if readyToSignEnvelope, authzResponse.isSignEnvelope { log.debug("will sign envelope") - signEnvelope(authzResponse) + signEnvelope(authzResponse, url: url) return } if authzResponse.isLinkAccount { log.debug("will link account") - linkAccount(authzResponse) + linkAccount(authzResponse, url: url) return } @@ -317,13 +319,13 @@ extension JSMessageHandler { if authzResponse.isSignAuthz { log.debug("will sign authz") - signAuthz(authzResponse) + signAuthz(authzResponse, url: url) return } if authzResponse.isSignPayload { log.debug("will sign payload") - signPayload(authzResponse) + signPayload(authzResponse, url: url) return } @@ -333,7 +335,7 @@ extension JSMessageHandler { } } - private func handleUserSignature(_ message: String) { + private func handleUserSignature(_ message: String, url: URL?) { do { guard let data = message.data(using: .utf8) else { log.error("decode message failed") @@ -351,7 +353,7 @@ extension JSMessageHandler { log.debug("handle user signature, uid: \(response.uniqueId())") let title = response.config?.app?.title ?? webVC?.webView.title ?? "unknown" - let url = webVC?.webView.url?.host ?? "unknown" + let url = url?.host ?? "unknown" let vm = BrowserSignMessageViewModel(title: title, url: url, logo: response.config?.app?.icon, cadence: response.body?.message ?? "") { [weak self] result in guard let self = self else { return @@ -372,10 +374,10 @@ extension JSMessageHandler { } extension JSMessageHandler { - private func signAuthz(_ authzResponse: FCLAuthzResponse) { + private func signAuthz(_ authzResponse: FCLAuthzResponse, url: URL?) { let title = authzResponse.config?.app?.title ?? webVC?.webView.title ?? "unknown" - let url = webVC?.webView.url?.host ?? "unknown" - let vm = BrowserAuthzViewModel(title: title, url: url, logo: authzResponse.config?.app?.icon, + let urlHost = url?.host ?? "unknown" + let vm = BrowserAuthzViewModel(title: title, url: urlHost, logo: authzResponse.config?.app?.icon, cadence: authzResponse.body.cadence, arguments: authzResponse.body.voucher.arguments) { [weak self] result in guard let self = self else { @@ -384,7 +386,7 @@ extension JSMessageHandler { DispatchQueue.main.async { if result { - self.processingAuthzTransaction = AuthzTransaction(url: self.webVC?.webView.url?.absoluteString, title: self.webVC?.webView.title, voucher: authzResponse.body.voucher) + self.processingAuthzTransaction = AuthzTransaction(url: url?.absoluteString, title: self.webVC?.webView.title, voucher: authzResponse.body.voucher) self.didConfirmSignPayload(authzResponse) } } @@ -394,9 +396,9 @@ extension JSMessageHandler { Router.route(to: RouteMap.Explore.authz(vm)) } - private func linkAccount(_ authzResponse: FCLAuthzResponse) { + private func linkAccount(_ authzResponse: FCLAuthzResponse, url: URL?) { let title = authzResponse.config?.app?.title ?? webVC?.webView.title ?? "unknown" - let url = webVC?.webView.url?.host ?? "unknown" + let url = url?.host ?? "unknown" let logo = authzResponse.config?.app?.icon ?? "" let vm = ChildAccountLinkViewModel(fromTitle: title, url: url, logo: logo) { [weak self] result in @@ -416,9 +418,9 @@ extension JSMessageHandler { Router.route(to: RouteMap.Explore.linkChildAccount(vm)) } - private func signPayload(_ authzResponse: FCLAuthzResponse) { + private func signPayload(_ authzResponse: FCLAuthzResponse, url: URL?) { let title = authzResponse.config?.app?.title ?? webVC?.webView.title ?? "unknown" - let url = webVC?.webView.url?.host ?? "unknown" + let url = url?.host ?? "unknown" let vm = BrowserAuthzViewModel(title: title, url: url, logo: authzResponse.config?.app?.icon, cadence: authzResponse.body.cadence, arguments: authzResponse.body.voucher.arguments) { [weak self] result in @@ -449,8 +451,7 @@ extension JSMessageHandler { } } - private func signEnvelope(_ authzResponse: FCLAuthzResponse) { - let url = webVC?.webView.url?.absoluteString + private func signEnvelope(_ authzResponse: FCLAuthzResponse, url: URL?) { let title = webVC?.webView.title Task { @@ -461,7 +462,7 @@ extension JSMessageHandler { DispatchQueue.main.async { self.webVC?.postAuthzEnvelopeSignResponse(sign: sign) - let authzTransaction = AuthzTransaction(url: url, title: title, voucher: authzResponse.body.voucher) + let authzTransaction = AuthzTransaction(url: url?.absoluteString, title: title, voucher: authzResponse.body.voucher) self.processingAuthzTransaction = authzTransaction self.readyToSignEnvelope = false diff --git a/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift b/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift index 18fba9bc..9d21404f 100644 --- a/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift +++ b/FRW/Modules/TrustProvider/TrustJSMessageHandler.swift @@ -92,6 +92,8 @@ extension TrustJSMessageHandler { extension TrustJSMessageHandler: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let json = message.json + let url = message.frameInfo.request.url ?? webVC?.webView.url + guard let method = extractMethod(json: json), let id = json["id"] as? Int64, let network = extractNetwork(json: json) @@ -103,7 +105,6 @@ extension TrustJSMessageHandler: WKScriptMessageHandler { switch method { case .requestAccounts: log.info("[Trust] requestAccounts") - let url = message.frameInfo.request.url ?? webVC?.webView.url handleRequestAccounts(url: url, network: network, id: id) case .signRawTransaction: log.info("[Trust] signRawTransaction") @@ -114,7 +115,7 @@ extension TrustJSMessageHandler: WKScriptMessageHandler { log.info("[Trust] data is missing") return } - handleSendTransaction(network: network, id: id, info: obj) + handleSendTransaction(url: url, network: network, id: id, info: obj) case .signMessage: log.info("[Trust] signMessage") @@ -124,13 +125,13 @@ extension TrustJSMessageHandler: WKScriptMessageHandler { print("data is missing") return } - handleSignTypedMessage(id: id, data: data, raw: raw) + handleSignTypedMessage(url: url, id: id, data: data, raw: raw) case .signPersonalMessage: guard let data = extractMessage(json: json) else { log.info("[Trust] data is missing") return } - handleSignPersonal(network: network, id: id, data: data, addPrefix: true) + handleSignPersonal(url: url, network: network, id: id, data: data, addPrefix: true) case .sendTransaction: log.info("[Trust] sendTransaction") @@ -206,7 +207,7 @@ extension TrustJSMessageHandler { MoveAssetsAction.shared.startBrowserWithMoveAssets(appName: webVC?.webView.title, callback: callback) } - private func handleSignPersonal(network: ProviderNetwork, id: Int64, data: Data, addPrefix _: Bool) { + private func handleSignPersonal(url: URL?, network: ProviderNetwork, id: Int64, data: Data, addPrefix _: Bool) { Task { await TrustJSMessageHandler.checkCoa() } @@ -214,7 +215,7 @@ extension TrustJSMessageHandler { if title.isEmpty { title = "unknown" } - let url = webVC?.webView.url + let vm = BrowserSignMessageViewModel(title: title, url: url?.absoluteString ?? "unknown", logo: url?.absoluteString.toFavIcon()?.absoluteString, @@ -250,7 +251,7 @@ extension TrustJSMessageHandler { Router.route(to: RouteMap.Explore.signMessage(vm)) } - func handleSignTypedMessage(id: Int64, data: Data, raw: String) { + func handleSignTypedMessage(url: URL?, id: Int64, data: Data, raw: String) { Task { await TrustJSMessageHandler.checkCoa() } @@ -258,7 +259,7 @@ extension TrustJSMessageHandler { if title.isEmpty { title = "unknown" } - let url = webVC?.webView.url + let vm = BrowserSignTypedMessageViewModel(title: title, urlString: url?.absoluteString ?? "unknown", logo: url?.absoluteString.toFavIcon()?.absoluteString, rawString: raw) { [weak self] result in guard let self = self else { return @@ -289,12 +290,11 @@ extension TrustJSMessageHandler { Router.route(to: RouteMap.Explore.signTypedMessage(vm)) } - private func handleSendTransaction(network _: ProviderNetwork, id: Int64, info: [String: Any]) { + private func handleSendTransaction(url: URL?, network _: ProviderNetwork, id: Int64, info: [String: Any]) { var title = webVC?.webView.title ?? "unknown" if title.isEmpty { title = "unknown" } - let url = webVC?.webView.url let originCadence = CadenceManager.shared.current.evm?.callContract?.toFunc() ?? ""