From a8bd19220bf714b228ce2de5ed81354e29568c9e Mon Sep 17 00:00:00 2001 From: ryanml Date: Sat, 18 Apr 2020 14:47:16 -0700 Subject: [PATCH] Fixes brave/brave-browser#9317 - Uses noopener for Binance URLs, client url validation --- components/brave_new_tab_ui/binance-utils.ts | 37 +++++++++++++++++++ .../components/default/binance/index.tsx | 2 +- .../containers/newTab/index.tsx | 8 ++-- .../reducers/new_tab_reducer.ts | 10 ++++- .../helpers/binance-utils-test.ts | 36 ++++++++++++++++++ 5 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 components/test/brave_new_tab_ui/helpers/binance-utils-test.ts diff --git a/components/brave_new_tab_ui/binance-utils.ts b/components/brave_new_tab_ui/binance-utils.ts index 243f2850d36d..9f3481e382fe 100644 --- a/components/brave_new_tab_ui/binance-utils.ts +++ b/components/brave_new_tab_ui/binance-utils.ts @@ -31,3 +31,40 @@ export const getUSDPrice = (accountBTCBalance: string, btcUSDPrice: string) => { return (btcUSDPriceNumber * btcBalanceNumber).toFixed(2) } + +export const isValidClientURL = (url: string) => { + if (!url) { + return false + } + + let urlObj + try { + urlObj = new URL(url) + } catch (err) { + return false + } + + if (urlObj.protocol !== 'https:') { + return false + } + + if (urlObj.host !== 'accounts.binance.com') { + return false + } + + const { pathname } = urlObj + const pathSplit = pathname.split('/') + + // The first level of path is the locale, so the second and + // third levels should be checked for equality to 'oauth' and + // 'authorize' respectively. ex: '/en/oauth/authorize' + if (pathSplit.length !== 4) { + return false + } + + if (pathSplit[2] !== 'oauth' || pathSplit[3] !== 'authorize') { + return false + } + + return true +} diff --git a/components/brave_new_tab_ui/components/default/binance/index.tsx b/components/brave_new_tab_ui/components/default/binance/index.tsx index 3455c4cb26ec..d14dba2fc39a 100644 --- a/components/brave_new_tab_ui/components/default/binance/index.tsx +++ b/components/brave_new_tab_ui/components/default/binance/index.tsx @@ -283,7 +283,7 @@ class Binance extends React.PureComponent { connectBinance = () => { const { binanceClientUrl } = this.props - window.open(binanceClientUrl, '_self') + window.open(binanceClientUrl, '_self', 'noopener') this.props.onConnectBinance() } diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index 34bd2f3d5150..5007ca93f2b1 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -271,9 +271,9 @@ class NewTabPage extends React.Component { const refParams = `ref=${refCode}&utm_source=brave` if (userTLD === 'us') { - window.open(`https://www.binance.us/en/buy-sell-crypto?crypto=${coin}&amount=${amount}&${refParams}`, '_blank') + window.open(`https://www.binance.us/en/buy-sell-crypto?crypto=${coin}&amount=${amount}&${refParams}`, '_blank', 'noopener') } else { - window.open(`https://www.binance.com/en/buy-sell-crypto?fiat=${fiat}&crypto=${coin}&amount=${amount}&${refParams}`, '_blank') + window.open(`https://www.binance.com/en/buy-sell-crypto?fiat=${fiat}&crypto=${coin}&amount=${amount}&${refParams}`, '_blank', 'noopener') } } @@ -356,11 +356,11 @@ class NewTabPage extends React.Component { } learnMoreRewards = () => { - window.open('https://brave.com/brave-rewards/', '_blank') + window.open('https://brave.com/brave-rewards/', '_blank', 'noopener') } learnMoreBinance = () => [ - window.open('https://brave.com/binance/', '_blank') + window.open('https://brave.com/binance/', '_blank', 'noopener') ] setAssetDepositQRCodeSrc = (asset: string, src: string) => { diff --git a/components/brave_new_tab_ui/reducers/new_tab_reducer.ts b/components/brave_new_tab_ui/reducers/new_tab_reducer.ts index 5ef2ca702b7a..cfacf85c6502 100644 --- a/components/brave_new_tab_ui/reducers/new_tab_reducer.ts +++ b/components/brave_new_tab_ui/reducers/new_tab_reducer.ts @@ -17,7 +17,7 @@ import { registerViewCount } from '../api/brandedWallpaper' import * as preferencesAPI from '../api/preferences' import * as storage from '../storage/new_tab_storage' import { getTotalContributions } from '../rewards-utils' -import { getUSDPrice } from '../binance-utils' +import { getUSDPrice, isValidClientURL } from '../binance-utils' const initialState = storage.load() @@ -456,8 +456,14 @@ export const newTabReducer: Reducer = (state: NewTab.S break case types.ON_BINANCE_CLIENT_URL: + const { clientUrl } = payload + + if (!isValidClientURL(clientUrl)) { + break + } + state = { ...state } - state.binanceState.binanceClientUrl = payload.clientUrl + state.binanceState.binanceClientUrl = clientUrl break case types.ON_BTC_USD_PRICE: diff --git a/components/test/brave_new_tab_ui/helpers/binance-utils-test.ts b/components/test/brave_new_tab_ui/helpers/binance-utils-test.ts new file mode 100644 index 000000000000..c0c408cac1c7 --- /dev/null +++ b/components/test/brave_new_tab_ui/helpers/binance-utils-test.ts @@ -0,0 +1,36 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +import * as binanceUtils from '../../../brave_new_tab_ui/binance-utils' + +describe('binance utilities tests', () => { + describe('isValidClientURL', () => { + it('handles falsey urls', () => { + expect(binanceUtils.isValidClientURL('')).toBe(false) + expect(binanceUtils.isValidClientURL(null)).toBe(false) + }) + + it('handles non-url strings', () => { + expect(binanceUtils.isValidClientURL('httpsurl')).toBe(false) + expect(binanceUtils.isValidClientURL('abcdefgh')).toBe(false) + }) + + it('handles non https protocols', () => { + expect(binanceUtils.isValidClientURL('http://accounts.binance.com/en/oauth/authorize')).toBe(false) + }) + + it('handles unexpected hosts', () => { + expect(binanceUtils.isValidClientURL('https://accounts.fake-binance.com/en/oauth/authorize')).toBe(false) + }) + + it('handles unexpected paths', () => { + expect(binanceUtils.isValidClientURL('https://accounts.binance.com/en/oauth/not-authorize')).toBe(false) + }) + + it('accepts an expected url', () => { + expect(binanceUtils.isValidClientURL('https://accounts.binance.com/en/oauth/authorize?response_type=code&client_id=some_id&redirect_uri=com.brave.binance%3A%2F%2Fauthorization&scope=user%3Aemail%2Cuser%3Aaddress%2Casset%3Abalance%2Casset%3Aocbs&code_challenge=code&code_challenge_method=S256')).toBe(true) + }) + }) +})