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

implement flash CtP and show flash permissions in about:preferences#security #2327

Merged
merged 6 commits into from
Jun 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -83,6 +83,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