Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Tor MVP
Browse files Browse the repository at this point in the history
Support isolated_storage and tor_proxy
requires brave/muon#473

Use Session::IsOffTheRecord to detect private tab
requires
brave/muon@edad1b2

Set Tor new identify
fix #12997
requires
brave/muon@7e052dd

Session::IsOffTheRecord for app/filtering

Refresh page after getting new identity
requires
brave/muon@c3d6769

implement switch Tor Private Tabs in about:newtab
set isTor depending on tor private tab setting and tor availability
close/re-open private tab when tor switch is toggled
Recreate tor private tab at the same index it was previously at
always enable new identity button for now

Disable search suggestions in private mode for Tor
Fix #13064

set ddg as default search engine in tor private tabs
fix #13212

Disable webtorrent in tor private mode
fix #13063

load favicons in Tor tabs as data: URLs
fix #13065

Omit unlocked icon for HTTP onion sites
Fix #12990
also fix unittests
Test Plan:
1. go to http://3expgpdnrrzezf7r.onion/ in a private tab
2. no icon should be in the urlbar
3. go to https://3g2upl4pq6kufc4m.onion/
4. you should see a lock icon

disable webrtc in tor mode
fix #13397
add note to shields panel about breakage in tor mode

Tor binary path will be passed from browser-laptop (#13455)

Disable widevine notification on Tor tabs
needed for #13396
Test Plan:
1. go to https://shaka-player-demo.appspot.com in a tor private tab
2. you should not see a widevine notification

disable plugins in Tor private tabs

Workaround browser-context-created not being emitted for tor tabs

Disable flash click to play on tor tabs
TODO: figure out a way to disable widevine on a per-tab basis
Test Plan:
1. ensure flash is installed and enabled in preferences
2. go to https://get.adobe.com/flashplayer/about/
3. make sure there is no popup asking you to run flash
4. right click on the flash click-to-play element (looks like a puzzle
   piece) on the page. no context menu should appear.

deny geolocation in tor mode
fix #13447

implement new tor tabs mockup
fix #12922

implement new Tor New Identity UX
fix #13658
Test Plan:
1. open new private tab
2. go to check.torproject.org and open shields
3. click 'new circuit' button in shields
4. it should reload the page and show a new IP
5. open shields again, click the info circle next to 'new circuit'
5. it should open a FAQ page

Bundling tor with Brave

Set the tor socks port and data directory. (#13641)
Pass the port and data directory down for tor to use depending on channel

bind tor new circuit to hard refresh in tor tabs
fix #13925

upgrade to muon 6.1.0 for tor API support
  • Loading branch information
riastradh-brave authored and bsclifton committed Jun 26, 2018
1 parent 996533d commit c61d699
Show file tree
Hide file tree
Showing 48 changed files with 689 additions and 73 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ app/extensions/brave/content/scripts/sync.js

# script used for signing for widevine
signature_generator.py

# binaries
app/extensions/bin
6 changes: 3 additions & 3 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
runtime = electron
target_arch = x64
brave_electron_version = 7.0.6
chromedriver_version = 2.37
target = v7.0.6
brave_electron_version = 6.1.0
chromedriver_version = 2.36
target = v6.1.0
disturl=https://brave-laptop-binaries.s3.amazonaws.com/atom-shell/dist/
build_from_source = true
5 changes: 4 additions & 1 deletion app/browser/api/ledger.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const format = require('date-fns/format')
const Immutable = require('immutable')
const electron = require('electron')
const ipc = electron.ipcMain
const session = electron.session
const path = require('path')
const os = require('os')
const qr = require('qr-image')
Expand Down Expand Up @@ -863,7 +864,9 @@ const shouldTrackTab = (state, tabId) => {
if (tabFromState == null) {
tabFromState = pageDataState.getLastClosedTab(state, tabId)
}
const isPrivate = !tabFromState.get('partition', '').startsWith('persist:') || tabFromState.get('incognito')
const partition = tabFromState.get('partition', '')
const ses = session.fromPartition(partition)
const isPrivate = (ses && ses.isOffTheRecord()) || tabFromState.get('incognito')
return !isPrivate && !tabFromState.isEmpty() && ledgerUtil.shouldTrackView(tabFromState)
}

Expand Down
10 changes: 9 additions & 1 deletion app/browser/contentSettings/hostContentSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const { makeImmutable } = require('../../common/state/immutableUtil')
const appConfig = require('../../../js/constants/appConfig')

let registeredCallbacks = []
let registeredSessions = {}
let registeredPrivateSessions = {}
const blockContentSetting = { setting: 'block', primaryPattern: '*' }

module.exports.setContentSettings = (contentSettings, incognito) => {
contentSettings = makeImmutable(contentSettings)

const partitions = incognito ? registeredPrivateSessions : registeredSessions
for (let partition in partitions) {
let newContentSettings = contentSettings
if (partition === appConfig.tor.partition) {
// Do not allow plugins to be enabled in Tor contexts
newContentSettings = contentSettings.set('plugins', makeImmutable([blockContentSetting]))
}

const ses = partitions[partition]

contentSettings.forEach((settings, contentType) => {
newContentSettings.forEach((settings, contentType) => {
ses.contentSettings.clearForOneType(contentType)
settings.forEach((setting) => {
module.exports.setContentSetting(ses, setting.get('primaryPattern'), setting.get('secondaryPattern'),
Expand Down
18 changes: 17 additions & 1 deletion app/browser/reducers/aboutNewTabReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ const aboutNewTabReducer = (state, action) => {
switch (action.actionType) {
case appConstants.APP_SET_STATE:
const useAlternativePrivateSearchEngine = getSetting(settings.USE_ALTERNATIVE_PRIVATE_SEARCH_ENGINE, state.get('settings'))
const torEnabled = getSetting(settings.USE_TOR_PRIVATE_TABS)
state = aboutNewTabState.mergeDetails(state, {
newTabPageDetail: {
useAlternativePrivateSearchEngine
useAlternativePrivateSearchEngine,
torEnabled
}
})
break
Expand All @@ -36,6 +38,20 @@ const aboutNewTabReducer = (state, action) => {
useAlternativePrivateSearchEngine: action.value
}
})
} else if (action.key === settings.USE_TOR_PRIVATE_TABS) {
state = aboutNewTabState.mergeDetails(state, {
newTabPageDetail: {
torEnabled: action.value
}
})
if (action.value === true) {
// Also enable DDG
state = aboutNewTabState.mergeDetails(state, {
newTabPageDetail: {
useAlternativePrivateSearchEngine: action.value
}
})
}
}
}
return state
Expand Down
21 changes: 20 additions & 1 deletion app/browser/reducers/tabsReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const tabState = require('../../common/state/tabState')
const windowState = require('../../common/state/windowState')
const siteSettings = require('../../../js/state/siteSettings')
const siteSettingsState = require('../../common/state/siteSettingsState')
const {frameOptsFromFrame} = require('../../../js/state/frameStateUtil')
const {frameOptsFromFrame, isTor} = require('../../../js/state/frameStateUtil')
const updateState = require('../../common/state/updateState')

// Constants
Expand Down Expand Up @@ -51,6 +51,10 @@ const getWebRTCPolicy = (state, tabId) => {
return webrtcConstants.default
}

if (isTor(tabValue)) {
return webrtcConstants.disableNonProxiedUdp
}

const allSiteSettings = siteSettingsState.getAllSiteSettings(state, tabValue.get('incognito') === true)
const tabSiteSettings =
siteSettings.getSiteSettingsForURL(allSiteSettings, tabValue.get('url'))
Expand Down Expand Up @@ -254,6 +258,21 @@ const tabsReducer = (state, action, immutableAction) => {
}
})
break
case appConstants.APP_RECREATE_TOR_TAB:
{
const tabId = action.get('tabId')
tabs.create({
url: 'about:newtab',
isPrivate: true,
windowId: tabState.getWindowId(state, tabId),
index: action.get('index'),
active: true,
isTor: action.get('torEnabled')
}, (tab) => {
appActions.tabCloseRequested(tabId)
})
break
}
case appConstants.APP_TAB_UPDATED:
state = tabState.maybeCreateTab(state, action)
// tabs.debugTabs(state)
Expand Down
32 changes: 30 additions & 2 deletions app/browser/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { shouldDebugTabEvents } = require('../cmdLine')
const tabState = require('../common/state/tabState')
const {app, extensions, session, ipcMain} = require('electron')
const {makeImmutable, makeJS} = require('../common/state/immutableUtil')
const {getTargetAboutUrl, getSourceAboutUrl, isSourceAboutUrl, newFrameUrl, isTargetAboutUrl, isIntermediateAboutPage, isTargetMagnetUrl, getSourceMagnetUrl} = require('../../js/lib/appUrlUtil')
const {getExtensionsPath, getTargetAboutUrl, getSourceAboutUrl, isSourceAboutUrl, newFrameUrl, isTargetAboutUrl, isIntermediateAboutPage, isTargetMagnetUrl, getSourceMagnetUrl} = require('../../js/lib/appUrlUtil')
const {isURL, getUrlFromInput, toPDFJSLocation, getDefaultFaviconUrl, isHttpOrHttps, getLocationIfPDF} = require('../../js/lib/urlutil')
const {isSessionPartition} = require('../../js/state/frameStateUtil')
const {getOrigin} = require('../../js/lib/urlutil')
Expand All @@ -28,7 +28,7 @@ const {newTabMode} = require('../common/constants/settingsEnums')
const {tabCloseAction} = require('../common/constants/settingsEnums')
const webContentsCache = require('./webContentsCache')
const {FilterOptions} = require('ad-block')
const {isResourceEnabled} = require('../filtering')
const {isResourceEnabled, initPartition} = require('../filtering')
const autofill = require('../autofill')
const bookmarksState = require('../common/state/bookmarksState')
const bookmarkFoldersState = require('../common/state/bookmarkFoldersState')
Expand All @@ -38,6 +38,8 @@ const bookmarkOrderCache = require('../common/cache/bookmarkOrderCache')
const ledgerState = require('../common/state/ledgerState')
const {getWindow, notifyWindowWebContentsAdded} = require('./windows')
const activeTabHistory = require('./activeTabHistory')
const path = require('path')
const {getTorSocksProxy} = require('../channel')

let adBlockRegions
let currentPartitionNumber = 0
Expand Down Expand Up @@ -101,6 +103,8 @@ const getPartition = (createProperties) => {
let partition = session.defaultSession.partition
if (createProperties.partition) {
partition = createProperties.partition
} else if (createProperties.isTor) {
partition = appConfig.tor.partition
} else if (createProperties.isPrivate) {
partition = 'default'
} else if (createProperties.isPartitioned) {
Expand All @@ -114,6 +118,7 @@ const getPartition = (createProperties) => {

const needsPartitionAssigned = (createProperties) => {
return !createProperties.openerTabId ||
createProperties.isTor ||
createProperties.isPrivate ||
createProperties.isPartitioned ||
createProperties.partitionNumber ||
Expand Down Expand Up @@ -511,6 +516,12 @@ const api = {
index = newTabValue.get('index')
}

const ses = session.fromPartition(newTab.session.partition)
let isPrivate
if (ses) {
isPrivate = ses.isOffTheRecord()
}

const frameOpts = {
location,
displayURL,
Expand All @@ -519,6 +530,7 @@ const api = {
active: !!newTabValue.get('active'),
guestInstanceId: newTab.guestInstanceId,
isPinned: !!newTabValue.get('pinned'),
isPrivate,
openerTabId,
disposition,
index,
Expand Down Expand Up @@ -1048,6 +1060,17 @@ const api = {
if (isSessionPartition(createProperties.partition)) {
createProperties.parent_partition = ''
}
if (createProperties.isTor) {
// TODO(riastradh): Duplicate logic in app/filtering.js.
createProperties.isolated_storage = true
createProperties.parent_partition = ''
createProperties.tor_proxy = getTorSocksProxy()
if (process.platform === 'win32') {
createProperties.tor_path = path.join(getExtensionsPath('bin'), 'tor.exe')
} else {
createProperties.tor_path = path.join(getExtensionsPath('bin'), 'tor')
}
}
}

// Tabs are allowed to be initially discarded (unloaded) if they are regular tabs
Expand All @@ -1069,6 +1092,11 @@ const api = {
}
extensions.createTab(createProperties, (tab) => {
cb && cb(tab)
// XXX: Workaround for 'browser-context-created' not emitted for Tor
// browsing context
if (createProperties.isTor) {
initPartition(appConfig.tor.partition)
}
})
}

Expand Down
31 changes: 31 additions & 0 deletions app/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,34 @@ exports.getLinuxDesktopName = () => {
}
return desktopName
}

// getTorSocksProxy()
//
// Return the socks5:// `URL' for the Tor socks proxy we will
// configure the tor daemon to listen on and muon to connect to,
// depending on which channel we're using. This is provisional
// until we let the OS choose the port number as in
// <https://github.com/brave/browser-laptop/issues/12936>, or
// until we add support for local sockets for SOCKS proxies as in
// <https://github.com/brave/muon/issues/469>.
//
exports.getTorSocksProxy = () => {
let portno
switch (channel) {
case 'dev':
case '':
default:
portno = 9250
break
case 'beta':
portno = 9260
break
case 'nightly':
portno = 9270
break
case 'developer':
portno = 9280
break
}
return `socks5://127.0.0.1:${portno}`
}
20 changes: 15 additions & 5 deletions app/common/commonMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const messages = require('../../js/constants/messages')
const locale = require('../../js/l10n')
const settings = require('../../js/constants/settings')
const {tabs} = require('../../js/constants/config')
const getSetting = require('../../js/settings').getSetting
const {getSetting} = require('../../js/settings')
const communityURL = 'https://community.brave.com/'
const isDarwin = process.platform === 'darwin'
const electron = require('electron')
Expand Down Expand Up @@ -77,10 +77,20 @@ module.exports.newPrivateTabMenuItem = () => {
label: locale.translation('newPrivateTab'),
accelerator: 'Shift+CmdOrCtrl+P',
click: function (item, focusedWindow) {
ensureAtLeastOneWindow({
url: 'about:newtab',
isPrivate: true
})
// Check if Tor is available
const useTor = getSetting(settings.USE_TOR_PRIVATE_TABS)
if (useTor) {
ensureAtLeastOneWindow({
url: 'about:newtab',
isPrivate: true,
isTor: true
})
} else {
ensureAtLeastOneWindow({
url: 'about:newtab',
isPrivate: true
})
}
}
}
}
Expand Down
12 changes: 10 additions & 2 deletions app/common/lib/suggestion.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ const sigmoid = (t) => {

const ONE_DAY = 1000 * 60 * 60 * 24

const searchSuggestionsEnabled = (state, tabId) => {
const frame = getFrameByTabId(state, tabId)
if (!frame || frame.get('isPrivate')) {
return false
}
return getSetting(settings.OFFER_SEARCH_SUGGESTIONS)
}

/*
* Calculate the sorting priority for a history item based on number of
* accesses and time since last access
Expand Down Expand Up @@ -564,7 +572,7 @@ const getSearchSuggestions = (state, tabId, urlLocationLower) => {
return new Promise((resolve, reject) => {
const mapListToElements = getMapListToElements(urlLocationLower)
let suggestionsList = Immutable.List()
if (getSetting(settings.OFFER_SEARCH_SUGGESTIONS)) {
if (searchSuggestionsEnabled(state, tabId)) {
const searchResults = state.get('searchResults')
const sortHandler = getSortForSearchSuggestions(urlLocationLower)
if (searchResults) {
Expand Down Expand Up @@ -630,7 +638,7 @@ const generateNewSearchXHRResults = debounce((state, windowId, tabId, input) =>
? frameSearchDetail.get('autocomplete')
: searchDetail.get('autocompleteURL')

const shouldDoSearchSuggestions = getSetting(settings.OFFER_SEARCH_SUGGESTIONS) &&
const shouldDoSearchSuggestions = searchSuggestionsEnabled(state, tabId) &&
autocompleteURL &&
!isUrl(input) &&
input.length !== 0
Expand Down
18 changes: 17 additions & 1 deletion app/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ let generateBraveManifest = () => {
'content/scripts/themeColor.js'
]
},
{
run_at: 'document_end',
all_frames: false,
matches: ['<all_urls>'],
include_globs: [
'http://*/*', 'https://*/*'
],
exclude_globs: [
indexHTML,
getBraveExtUrl('about-*.html'),
getBraveExtUrl('about-*.html') + '#*'
],
js: [
'content/scripts/favicon.js'
]
},
{
run_at: 'document_start',
js: [
Expand Down Expand Up @@ -273,7 +289,7 @@ let generateTorrentManifest = () => {
48: 'img/webtorrent-48.png',
16: 'img/webtorrent-16.png'
},
incognito: 'split',
incognito: 'not_allowed',
key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyWl+wMvL0wZX3JUs7GeZAvxMP+LWEh2bwMV1HyuBra/lGZIq3Fmh0+AFnvFPXz1NpQkbLS3QWyqhdIn/lepGwuc2ma0glPzzmieqwctUurMGSGManApGO1MkcbSPhb+R1mx8tMam5+wbme4WoW37PI3oATgOs2NvHYuP60qol3U7b/zB3IWuqtwtqKe2Q1xY17btvPuz148ygWWIHneedt0jwfr6Zp+CSLARB9Heq/jqGXV4dPSVZ5ebBHLQ452iZkHxS6fm4Z+IxjKdYs3HNj/s8xbfEZ2ydnArGdJ0lpSK9jkDGYyUBugq5Qp3FH6zV89WqBvoV1dqUmL9gxbHsQIDAQAB'
}
}
Expand Down
27 changes: 27 additions & 0 deletions app/extensions/brave/content/scripts/favicon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const getBase64FromImageUrl = (url) => {
return new Promise((resolve, reject) => {
const img = new window.Image()
img.onerror = function () {
reject(new Error('unable to load image'))
}
img.onload = function () {
const canvas = document.createElement('canvas')
canvas.width = this.naturalWidth
canvas.height = this.naturalHeight
canvas.getContext('2d')
.drawImage(this, 0, 0)
resolve(canvas.toDataURL('image/png'))
}
img.src = url
})
}

let faviconUrl = window.location.origin + '/favicon.ico'
const faviconNode = document.head.querySelector("link[rel='icon']") || document.head.querySelector("link[rel='shortcut icon']")
if (faviconNode) {
faviconUrl = faviconNode.getAttribute('href') || faviconUrl
}

getBase64FromImageUrl(faviconUrl).then((data) => {
chrome.ipcRenderer.sendToHost('got-page-favicon', data)
})
Loading

0 comments on commit c61d699

Please sign in to comment.