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

Commit

Permalink
Merge pull request #2327 from brave/feature/flash-ctp
Browse files Browse the repository at this point in the history
implement flash CtP and show flash permissions in about:preferences#security
  • Loading branch information
bbondy authored Jun 29, 2016
2 parents 74d8db7 + 8d0b9ea commit 5039414
Show file tree
Hide file tree
Showing 31 changed files with 527 additions and 80 deletions.
20 changes: 20 additions & 0 deletions app/extensions/brave/about-flash.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<!-- 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/. -->
<html>
<head>
<meta charset="utf-8">
<meta name="availableLanguages" content="">
<meta name="defaultLanguage" content="en-US">
<meta name='theme-color' content='#ff5000'>
<title data-l10n-id="flashTitle"></title>
<script src='js/about-flash.js'></script>
<script src='js/about.js'></script>
<script src="ext/l20n.min.js" async></script>
<link rel="localization" href="locales/{locale}/app.properties">
</head>
<body>
<div id="appContainer"/>
</body>
</html>
9 changes: 4 additions & 5 deletions app/extensions/brave/content/scripts/blockFlash.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@
* 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/. */


function blockFlashDetection () {
const handler = {
length: 0,
item: () => { return null },
namedItem: () => { return null },
refresh: () => {}
}
Navigator.prototype.__defineGetter__('plugins', () => { return handler })
Navigator.prototype.__defineGetter__('mimeTypes', () => { return handler })
window.Navigator.prototype.__defineGetter__('plugins', () => { return handler })
window.Navigator.prototype.__defineGetter__('mimeTypes', () => { return handler })
}

function getBlockFlashPageScript () {
return '(' + Function.prototype.toString.call(blockFlashDetection) + '());'
}

if (!window.location.search ||
!window.location.search.includes('brave_flash_allowed')) {
if (chrome.contentSettings.flashActive != 'allow' ||
chrome.contentSettings.flashEnabled != 'allow') {
executeScript(getBlockFlashPageScript())
}
130 changes: 129 additions & 1 deletion app/extensions/brave/content/scripts/flashListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
})
}
// Some pages insert the password form into the DOM after it's loaded
var observer = new MutationObserver(function (mutations) {
var observer = new window.MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length) {
replaceAdobeLinks()
Expand All @@ -29,3 +29,131 @@
})
}, 1000)
})()

const placeholderUrl = 'chrome-extension://mnojpmjdmbbfmejpflffifhffcmidifd/about-flash.html'

/**
* Whether a src is a .swf file.
* If so, returns the origin of the file. Otherwise returns false.
* @param {string} src
* @return {boolean|string}
*/
function isSWF (src) {
if (!src) {
return false
}
let a = document.createElement('a')
a.href = src
if (a.pathname && a.pathname.toLowerCase().endsWith('.swf')) {
return a.origin
} else {
return false
}
}

/**
* Gets all Flash object descendants of an element.
* Reference:
* https://helpx.adobe.com/flash/kb/flash-object-embed-tag-attributes.html
* @param {Element} elem - HTML element to search
* @return {Array.<Element>}
*/
function getFlashObjects (elem) {
let results = [] // Array.<{element: Element, origin: string}>
Array.from(elem.getElementsByTagName('embed')).forEach((el) => {
let origin = isSWF(el.getAttribute('src'))
if (origin) {
results.push({
element: el,
origin
})
}
})

Array.from(elem.getElementsByTagName('object')).forEach((el) => {
// Skip objects that are contained in other flash objects
/*
for (let i = 0; i < results.length; i++) {
if (results[i].element.contains(el)) {
return
}
}
*/
let origin = isSWF(el.getAttribute('data'))
if (origin) {
results.push({
element: el,
origin
})
} else {
// See example at
// https://helpx.adobe.com/animate/kb/object-tag-syntax.html
Array.from(el.getElementsByTagName('param')).forEach((param) => {
let name = param.getAttribute('name')
let origin = isSWF(param.getAttribute('value'))
if (name && ['movie', 'src'].includes(name.toLowerCase()) &&
origin) {
results.push({
element: el,
origin
})
}
})
}
})
return results
}

/**
* Inserts Flash placeholders.
* @param {Element} elem - HTML element to search
*/
function insertFlashPlaceholders (elem) {
const minWidth = 200
const minHeight = 100
let flashObjects = getFlashObjects(elem)
flashObjects.forEach((obj) => {
let el = obj.element
let pluginRect = el.getBoundingClientRect()
let height = el.getAttribute('height') || pluginRect.height
let width = el.getAttribute('width') || pluginRect.width
if (height > minHeight && width > minWidth) {
let parent = el.parentNode
if (!parent) {
return
}
let iframe = document.createElement('iframe')
iframe.setAttribute('sandbox', 'allow-scripts')
let hash = window.location.origin
if (chrome.contentSettings.flashEnabled == 'allow') {
hash = hash + '#flashEnabled'
}
iframe.setAttribute('src', [placeholderUrl, hash].join('#'))
iframe.setAttribute('style', `width: ${width}px; height: ${height}px`)
parent.replaceChild(iframe, el)
} else {
// Note when elements are too small so we can improve the heuristic.
console.log('got too-small Flash element', obj, height, width)
}
})
}

var observer = new window.MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes) {
Array.from(mutation.addedNodes).forEach((node) => {
insertFlashPlaceholders(node)
})
}
})
})

if (chrome.contentSettings.flashActive != 'allow' ||
chrome.contentSettings.flashEnabled != 'allow') {
setTimeout(() => {
insertFlashPlaceholders(document.documentElement)
observer.observe(document.documentElement, {
childList: true
})
}, 1000)
}
Binary file added app/extensions/brave/img/bravePluginAlert.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions app/extensions/brave/js/about-flash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function initBraveryDefaultsListener (e) {
window.initBraveryDefaults = e.detail
window.removeEventListener('bravery-defaults-updated', initBraveryDefaultsListener)
}
window.addEventListener('bravery-defaults-updated', initBraveryDefaultsListener)
5 changes: 4 additions & 1 deletion app/extensions/brave/locales/en-US/app.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,12 @@ permissionWebMidi=use web MIDI
permissionDisableCursor=disable your mouse cursor
permissionFullscreen=use fullscreen mode
permissionExternal=open an external application

tabsSuggestionTitle=Tabs
bookmarksSuggestionTitle=Bookmarks
historySuggestionTitle=History
searchSuggestionTitle=Search
topSiteSuggestionTitle=Top Site
flashTitle=Flash Object Blocked
flashRightClick=Right-click to run Adobe Flash
flashSubtext=from {{source}} on {{site}}.
flashExpirationText=Approvals reset 7 days after last visit.
2 changes: 2 additions & 0 deletions app/extensions/brave/locales/en-US/menu.properties
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,5 @@ autoHideMenuBar=Menu Bar
updateChannel=Update Channel
licenseText=This software uses libraries from the FFmpeg project under the LGPLv2.1
lookupSelection=Look Up “{{selectedVariable}}”
allowFlashOnce=Allow once
allowFlashAlways=Allow for 1 week
3 changes: 3 additions & 0 deletions app/extensions/brave/locales/en-US/preferences.properties
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ midiSysexPermission=Use web MIDI
pointerLockPermission=Disable your mouse cursor
fullscreenPermission=Fullscreen access
openExternalPermission=Open external applications
flash=Run Adobe Flash Player
flashAllowOnce=Allow once
flashAllowAlways=Allow until {{time}}
alwaysAllow=Always allow
alwaysDeny=Always deny
appearanceSettings=Appearance settings:
Expand Down
14 changes: 9 additions & 5 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ let loadAppStatePromise = SessionStore.loadAppState().catch(() => {
return SessionStore.defaultAppState()
})

let flashEnabled = false
let flashInitialized = false

// Some settings must be set right away on startup, those settings should be handled here.
loadAppStatePromise.then((initialState) => {
Expand All @@ -191,7 +191,7 @@ loadAppStatePromise.then((initialState) => {
if (initialState.flash && initialState.flash.enabled === true) {
if (flash.init()) {
// Flash was initialized successfully
flashEnabled = true
flashInitialized = true
return
}
}
Expand Down Expand Up @@ -346,7 +346,7 @@ app.on('ready', () => {
// For tests we always want to load default app state
const loadedPerWindowState = initialState.perWindowState
delete initialState.perWindowState
initialState.flashEnabled = flashEnabled
initialState.flashInitialized = flashInitialized
appActions.setState(Immutable.fromJS(initialState))
return loadedPerWindowState
}).then((loadedPerWindowState) => {
Expand Down Expand Up @@ -392,8 +392,12 @@ app.on('ready', () => {
appActions.changeSetting(key, value)
})

ipcMain.on(messages.CHANGE_SITE_SETTING, (e, hostPattern, key, value) => {
appActions.changeSiteSetting(hostPattern, key, value)
ipcMain.on(messages.CHANGE_SITE_SETTING, (e, hostPattern, key, value, temp) => {
appActions.changeSiteSetting(hostPattern, key, value, temp)
})

ipcMain.on(messages.REMOVE_SITE_SETTING, (e, hostPattern, key) => {
appActions.removeSiteSetting(hostPattern, key)
})

ipcMain.on(messages.SET_CLIPBOARD, (e, text) => {
Expand Down
2 changes: 2 additions & 0 deletions app/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ var rendererIdentifiers = function () {
'copyLinkAddress',
'copyEmailAddress',
'saveLinkAs',
'allowFlashOnce',
'allowFlashAlways',
'openInNewWindow',
'openInNewSessionTab',
'openInNewPrivateTab',
Expand Down
10 changes: 9 additions & 1 deletion app/sessionStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,21 @@ module.exports.cleanAppData = (data) => {
// Delete temp site settings
data.temporarySiteSettings = {}
// Delete Flash state since this is checked on startup
delete data.flashEnabled
delete data.flashInitialized
// We used to store a huge list of IDs but we didn't use them.
// Get rid of them here.
delete data.windows
if (data.perWindowState) {
data.perWindowState.forEach(module.exports.cleanSessionData)
}
// Delete expired Flash approvals
let now = Date.now()
for (var host in data.siteSettings) {
let expireTime = data.siteSettings[host].flash
if (typeof expireTime === 'number' && expireTime < now) {
delete data.siteSettings[host].flash
}
}
}

/**
Expand Down
12 changes: 12 additions & 0 deletions docs/appActions.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ Change a hostPattern's config



### removeSiteSetting(hostPattern, key)

Removes a site setting

**Parameters**

**hostPattern**: `string`, The host pattern to update the config for

**key**: `string`, The config key to update



### showMessageBox(detail)

Shows a message box in the notification bar
Expand Down
5 changes: 3 additions & 2 deletions docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ AppStore
safeBrowsing: boolean,
noScript: boolean,
httpsEverywhere: boolean,
fingerprintingProtection: boolean
fingerprintingProtection: boolean,
flash: number, // approval expiration time
}
},
temporarySiteSettings: {
Expand Down Expand Up @@ -328,7 +329,7 @@ WindowStore
maxHeight: number, // the maximum height of the popup window
src: string, // the src for the popup window webview
},
flashEnabled: boolean, // Whether flash is installed and enabled. Cleared on shutdown.
flashInitialized: boolean, // Whether flash was initialized successfully. Cleared on shutdown.
cleanedOnShutdown: boolean, // whether app data was successfully cleared on shutdown
}
```
16 changes: 16 additions & 0 deletions js/about/aboutActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ const AboutActions = {
window.dispatchEvent(event)
},

/**
* Dispatches an event to the renderer process to remove a site setting
*
* @param {string} hostPattern - host pattern of site
* @param {string} key - The settings key to change the value on
*/
removeSiteSetting: function (hostPattern, key) {
const event = new window.CustomEvent(messages.CHANGE_SITE_SETTING, {
detail: {
hostPattern,
key
}
})
window.dispatchEvent(event)
},

/**
* Loads a URL in a new frame in a safe way.
* It is important that it is not a simple anchor because it should not
Expand Down
3 changes: 2 additions & 1 deletion js/about/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ switch (getBaseUrl(getSourceAboutUrl(window.location.href))) {
case 'about:error':
element = require('./errorPage')
break
case 'about:flash':
element = require('./flashPlaceholder')
}

if (element) {
Expand All @@ -41,4 +43,3 @@ if (element) {
component.setState(e.detail)
})
}

Loading

0 comments on commit 5039414

Please sign in to comment.