diff --git a/app/extensions/brave/brave-default.js b/app/extensions/brave/brave-default.js index f7a79102538..5db2c270103 100644 --- a/app/extensions/brave/brave-default.js +++ b/app/extensions/brave/brave-default.js @@ -909,13 +909,14 @@ if (typeof KeyEvent === 'undefined') { function reportBlock (item) { var script_url = getOriginatingScriptUrl() var msg = { + type: item.type, obj: item.objName, prop: item.propName, + url: window.location.href, scriptUrl: stripLineAndColumnNumbers(script_url) } // Block the read from occuring; send info to background page instead - console.log('blocking potential canvas fingerprint', msg) send(msg) } @@ -933,10 +934,12 @@ if (typeof KeyEvent === 'undefined') { */ function trapIFrameMethods (frame) { var items = [{ + type: 'Canvas', objName: 'contentDocument', propName: 'createElement', obj: frame.contentDocument }, { + type: 'Canvas', objName: 'contentDocument', propName: 'createElementNS', obj: frame.contentDocument @@ -962,6 +965,7 @@ if (typeof KeyEvent === 'undefined') { var canvasMethods = ['getImageData', 'getLineDash', 'measureText'] canvasMethods.forEach(function (method) { var item = { + type: 'Canvas', objName: 'CanvasRenderingContext2D.prototype', propName: method, obj: window.CanvasRenderingContext2D.prototype @@ -973,6 +977,7 @@ if (typeof KeyEvent === 'undefined') { var canvasElementMethods = ['toDataURL', 'toBlob'] canvasElementMethods.forEach(function (method) { var item = { + type: 'Canvas', objName: 'HTMLCanvasElement.prototype', propName: method, obj: window.HTMLCanvasElement.prototype @@ -984,6 +989,7 @@ if (typeof KeyEvent === 'undefined') { 'getShaderPrecisionFormat', 'getExtension'] webglMethods.forEach(function (method) { var item = { + type: 'WebGL', objName: 'WebGLRenderingContext.prototype', propName: method, obj: window.WebGLRenderingContext.prototype @@ -994,6 +1000,7 @@ if (typeof KeyEvent === 'undefined') { var audioBufferMethods = ['copyFromChannel', 'getChannelData'] audioBufferMethods.forEach(function (method) { var item = { + type: 'AudioContext', objName: 'AudioBuffer.prototype', propName: method, obj: window.AudioBuffer.prototype @@ -1005,6 +1012,7 @@ if (typeof KeyEvent === 'undefined') { 'getFloatTimeDomainData', 'getByteTimeDomainData'] analyserMethods.forEach(function (method) { var item = { + type: 'AudioContext', objName: 'AnalyserNode.prototype', propName: method, obj: window.AnalyserNode.prototype diff --git a/app/extensions/brave/locales/en-US/bravery.properties b/app/extensions/brave/locales/en-US/bravery.properties index e9eeeb17c25..e6ab37c6f62 100644 --- a/app/extensions/brave/locales/en-US/bravery.properties +++ b/app/extensions/brave/locales/en-US/bravery.properties @@ -5,6 +5,7 @@ httpsEverywhere=HTTPS Everywhere noScript=Block Scripts safeBrowsing=Block Phishing / Malware blockPopups=Block Popups +fingerprintingProtection=Fingerprinting Protection adControl=Ad Control cookieControl=Cookie Control allowAllCookies=Allow all cookies @@ -12,4 +13,5 @@ adBlock=Ad Block showBraveAds=Show Brave Ads adsBlocked=Ads and Trackers Blocked scriptsBlockedNumber=Scripts Blocked +fingerprintingBlocked=Fingerprinting Methods Blocked httpReroutes=HTTPS Upgrades diff --git a/app/index.js b/app/index.js index 247a7cd4525..b56e3219c5f 100644 --- a/app/index.js +++ b/app/index.js @@ -558,7 +558,9 @@ app.on('ready', () => { }) ipcMain.on(messages.GOT_CANVAS_FINGERPRINTING, (e, details) => { - console.log('got canvas fingerprint block', details) + BrowserWindow.getAllWindows().forEach((win) => { + win.webContents.send(messages.GOT_CANVAS_FINGERPRINTING, details) + }) }) // Setup the crash handling diff --git a/app/sessionStore.js b/app/sessionStore.js index a62e8bba017..84f19658280 100644 --- a/app/sessionStore.js +++ b/app/sessionStore.js @@ -125,6 +125,7 @@ module.exports.cleanSessionData = (sessionData) => { delete frame.httpsEverywhere delete frame.adblock delete frame.noScript + delete frame.trackingProtection // Guest instance ID's are not valid after restarting. // Electron won't know about them. diff --git a/docs/state.md b/docs/state.md index 292e659f2ee..cf692112411 100644 --- a/docs/state.md +++ b/docs/state.md @@ -46,7 +46,8 @@ AppStore cookieControl: string, // (block3rdPartyCookie | allowAllCookies) safeBrowsing: boolean, noScript: boolean, - httpsEverywhere: boolean + httpsEverywhere: boolean, + fingerprintingProtection: boolean } }, temporarySiteSettings: { @@ -188,6 +189,9 @@ WindowStore noScript: { blocked: Array }, + fingerprintingProtection: { + blocked: Array + } security: { isSecure: boolean, // is using https loginRequiredDetail: { @@ -269,8 +273,9 @@ WindowStore braveryPanelDetail: { advancedControls: boolean, // If specified, indicates if advanced controls should be shown expandAdblock: boolean, // If specified, indicates if the tracking protection and adblock section should be expanded - expandHttpse: boolean // If specified, indicates if the httpse section should be expanded - expandNoScript: boolean // Whether noscript section should be expanded + expandHttpse: boolean, // If specified, indicates if the httpse section should be expanded + expandNoScript: boolean, // Whether noscript section should be expanded + expandFp: boolean // Whether fingerprinting protection should be expanded }, contextMenuDetail: { left: number, // the left position of the context menu diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js index ba60680e7b9..1b4ac5fa95f 100644 --- a/js/actions/windowActions.js +++ b/js/actions/windowActions.js @@ -782,7 +782,7 @@ const windowActions = { * Dispatches a message to indicate the site info, such as # of blocked ads, should be shown * * @param {object} frameProps - The frame to set blocked info on - * @param {string} blockType - either 'adblock' or 'trackingProtection' + * @param {string} blockType - type of the block * @param {string} location - URL that was blocked */ setBlockedBy: function (frameProps, blockType, location) { diff --git a/js/components/braveryPanel.js b/js/components/braveryPanel.js index 099babd6a4d..7bcea622ee1 100644 --- a/js/components/braveryPanel.js +++ b/js/components/braveryPanel.js @@ -22,6 +22,7 @@ class BraveryPanel extends ImmutableComponent { this.onToggleAdsAndTracking = this.onToggleAdsAndTracking.bind(this) this.onToggleHttpseList = this.onToggleHttpseList.bind(this) this.onToggleNoScriptList = this.onToggleNoScriptList.bind(this) + this.onToggleFpList = this.onToggleFpList.bind(this) this.onToggleAdvanced = this.onToggleAdvanced.bind(this) this.onToggleShields = this.onToggleSiteSetting.bind(this, 'shieldsUp') this.onToggleAdControl = this.onToggleSiteSetting.bind(this, 'adControl') @@ -29,6 +30,7 @@ class BraveryPanel extends ImmutableComponent { this.onToggleNoScript = this.onToggleSiteSetting.bind(this, 'noScript') this.onToggleCookieControl = this.onToggleSiteSetting.bind(this, 'cookieControl') this.onToggleHTTPSE = this.onToggleSiteSetting.bind(this, 'httpsEverywhere') + this.onToggleFp = this.onToggleSiteSetting.bind(this, 'fingerprintingProtection') this.onReload = this.onReload.bind(this) } get isBlockingTrackedContent () { @@ -58,9 +60,18 @@ class BraveryPanel extends ImmutableComponent { get isBlockedScriptsShown () { return this.props.braveryPanelDetail.get('expandNoScript') } + get isBlockingFingerprinting () { + return this.blockedFingerprinting && this.blockedFingerprinting.size > 0 + } + get blockedFingerprinting () { + return this.props.frameProps.getIn(['fingerprintingProtection', 'blocked']) + } get isHttpseShown () { return this.props.braveryPanelDetail.get('expandHttpse') } + get isFpShown () { + return this.props.braveryPanelDetail.get('expandFp') + } get redirectedResources () { return this.props.frameProps.get('httpsEverywhere') } @@ -95,6 +106,12 @@ class BraveryPanel extends ImmutableComponent { }) e.stopPropagation() } + onToggleFpList (e) { + windowActions.setBraveryPanelDetail({ + expandFp: !this.isFpShown + }) + e.stopPropagation() + } onToggleNoScriptList (e) { windowActions.setBraveryPanelDetail({ expandNoScript: !this.isBlockedScriptsShown @@ -140,8 +157,10 @@ class BraveryPanel extends ImmutableComponent { const noScriptEnabled = this.getSiteSetting('noScript', this.props.braveryDefaults.noScript) const httpseEnabled = this.getSiteSetting('httpsEverywhere', this.props.braveryDefaults.httpsEverywhere) const adControl = this.getSiteSetting('adControl', this.props.braveryDefaults.adControl) + const fpEnabled = this.getSiteSetting('fingerprintingProtection', this.props.braveryDefaults.fingerprintingProtection) const adsBlockedStat = (this.blockedAds ? this.blockedAds.size : 0) + (this.blockedByTrackingList ? this.blockedByTrackingList.size : 0) const scriptsBlockedStat = this.blockedScripts ? this.blockedScripts.size : 0 + const fpBlockedStat = this.blockedFingerprinting ? this.blockedFingerprinting.size : 0 return
e.stopPropagation()}>
@@ -177,6 +196,13 @@ class BraveryPanel extends ImmutableComponent {
{scriptsBlockedStat}
+
+
{fpBlockedStat}
+
+
    @@ -216,6 +242,16 @@ class BraveryPanel extends ImmutableComponent {
: null } + { + this.isBlockingFingerprinting && this.isFpShown + ?
    • + { + this.blockedFingerprinting.map((site) => +
    • {site}
    • ) + } +
  • + : null + }
    - +
    : null diff --git a/js/components/frame.js b/js/components/frame.js index 7f636498eb7..b15edc907e5 100644 --- a/js/components/frame.js +++ b/js/components/frame.js @@ -407,7 +407,7 @@ class Frame extends ImmutableComponent { ipc.send(messages.CHECK_CERT_ERROR_ACCEPTED, parsedUrl.host, this.props.frame.get('key')) } } - if (getSetting(settings.BLOCK_CANVAS_FINGERPRINTING)) { + if (this.props.enableFingerprintingProtection) { this.webview.send(messages.BLOCK_CANVAS_FINGERPRINTING) } windowActions.updateBackForwardState( @@ -473,7 +473,7 @@ class Frame extends ImmutableComponent { }) this.webview.addEventListener('load-start', (e) => { loadStart(e) - if (getSetting(settings.BLOCK_CANVAS_FINGERPRINTING)) { + if (this.props.enableFingerprintingProtection) { this.webview.send(messages.BLOCK_CANVAS_FINGERPRINTING) } }) diff --git a/js/components/main.js b/js/components/main.js index 382502ed074..ed3604e92c6 100644 --- a/js/components/main.js +++ b/js/components/main.js @@ -248,6 +248,18 @@ class Main extends ImmutableComponent { windowActions.setRedirectedBy(frameProps, ruleset, details.url)) }) + ipc.on(messages.GOT_CANVAS_FINGERPRINTING, (e, details) => { + if (!details.length) { + return + } + details.forEach((detail) => { + const filteredFrameProps = this.props.windowState.get('frames').filter((frame) => frame.get('location') === detail.url) + const description = [detail.type, detail.scriptUrl || detail.url].join(': ') + filteredFrameProps.forEach((frameProps) => + windowActions.setBlockedBy(frameProps, 'fingerprintingProtection', description)) + }) + }) + ipc.on(messages.SHOW_NOTIFICATION, (e, text) => { void new window.Notification(text) }) @@ -440,6 +452,20 @@ class Main extends ImmutableComponent { return enabled } + get enableFingerprintingProtection () { + if (this.activeSiteSettings) { + if (this.activeSiteSettings.get('shieldsUp') === false) { + return false + } + + if (typeof this.activeSiteSettings.get('fingerprintingProtection') === 'boolean') { + return this.activeSiteSettings.get('fingerprintingProtection') + } + } + + return getSetting(settings.BLOCK_CANVAS_FINGERPRINTING) || false + } + onCloseFrame (activeFrameProps) { windowActions.closeFrame(this.props.windowState.get('frames'), activeFrameProps) } @@ -563,6 +589,7 @@ class Main extends ImmutableComponent { braveryDefaults.adControl = 'blockAds' } braveryDefaults.cookieControl = blockCookies ? 'block3rdPartyCookie' : 'allowAllCookies' + braveryDefaults.fingerprintingProtection = getSetting(settings.BLOCK_CANVAS_FINGERPRINTING) return braveryDefaults } @@ -770,6 +797,7 @@ class Main extends ImmutableComponent { activeSiteSettings={activeSiteSettings} enableAds={this.enableAds} enableNoScript={this.enableNoScript} + enableFingerprintingProtection={this.enableFingerprintingProtection} isPreview={frame.get('key') === this.props.windowState.get('previewFrameKey')} isActive={FrameStateUtil.isFrameKeyActive(this.props.windowState, frame.get('key'))} />) diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index e7eb66b0893..8d825c85e4a 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -197,7 +197,8 @@ const doAction = (action) => { noScript: {}, themeColor: undefined, title: '', - trackingProtection: {} + trackingProtection: {}, + fingerprintingProtection: {} }) } updateNavBarInput(action.location, frameStatePath(key)) @@ -523,7 +524,8 @@ const doAction = (action) => { advancedControls: action.braveryPanelDetail.advancedControls, expandAdblock: action.braveryPanelDetail.expandAdblock, expandHttpse: action.braveryPanelDetail.expandHttpse, - expandNoScript: action.braveryPanelDetail.expandNoScript + expandNoScript: action.braveryPanelDetail.expandNoScript, + expandFp: action.braveryPanelDetail.expandFp }) } break diff --git a/less/forms.less b/less/forms.less index 900537bdf0c..a8b688bdbec 100644 --- a/less/forms.less +++ b/less/forms.less @@ -122,6 +122,9 @@ &.redirectedResourcesStat { color: #0796fa; } + &.fpStat { + color: #faae5a; + } } .statDisabled { opacity: 0.3; @@ -129,6 +132,9 @@ .statClickable { cursor: pointer; } + &>div { + max-width: 75px; + } } .braveryPanelBody {