diff --git a/app/extensions/brave/locales/en-US/app.properties b/app/extensions/brave/locales/en-US/app.properties index 2c9ebd61d77..257c623036f 100644 --- a/app/extensions/brave/locales/en-US/app.properties +++ b/app/extensions/brave/locales/en-US/app.properties @@ -190,6 +190,9 @@ phone=Phone email=Email editAddress=Edit Address editCreditCard=Edit Credit Card -denyRunInsecureContent=Stay Secure +dismissAllowRunInsecureContent=Stay Secure allowRunInsecureContent=Load Unsafe Scripts +dismissDenyRunInsecureContent=Stay Insecure +denyRunInsecureContent=Stop Loading Unsafe Scripts runInsecureContentWarning=This page is trying to load scripts from insecure sources. If you allow this content to run it will not be encrypted and it may transmit unencrypted data to other sites. +denyRunInsecureContentWarning=This page is currently loading scripts from insecure sources. diff --git a/docs/appActions.md b/docs/appActions.md index be99670f993..26889e446cd 100644 --- a/docs/appActions.md +++ b/docs/appActions.md @@ -240,7 +240,7 @@ Change a hostPattern's config -### removeSiteSetting(hostPattern, key) +### removeSiteSetting(hostPattern, key, temp) Removes a site setting @@ -250,6 +250,9 @@ Removes a site setting **key**: `string`, The config key to update +**temp**: `boolean`, Whether to change temporary or persistent + settings. defaults to false (persistent). + ### updateLedgerInfo(ledgerInfo) diff --git a/docs/state.md b/docs/state.md index 7572a0d3533..12502143acc 100644 --- a/docs/state.md +++ b/docs/state.md @@ -52,7 +52,8 @@ AppStore httpsEverywhere: boolean, fingerprintingProtection: boolean, flash: (number|boolean), // approval expiration time if allowed, false if never allow - ledgerPayments: boolean // False if site should not be paid by the ledger. Defaults to true. + ledgerPayments: boolean, // False if site should not be paid by the ledger. Defaults to true. + runInsecureContent: boolean // Allow active mixed content } }, temporarySiteSettings: { diff --git a/js/actions/appActions.js b/js/actions/appActions.js index 5b23338be2b..e4b01a21b6f 100644 --- a/js/actions/appActions.js +++ b/js/actions/appActions.js @@ -283,12 +283,15 @@ const appActions = { * Removes a site setting * @param {string} hostPattern - The host pattern to update the config for * @param {string} key - The config key to update + * @param {boolean} temp - Whether to change temporary or persistent + * settings. defaults to false (persistent). */ - removeSiteSetting: function (hostPattern, key) { + removeSiteSetting: function (hostPattern, key, temp) { AppDispatcher.dispatch({ actionType: AppConstants.APP_REMOVE_SITE_SETTING, hostPattern, - key + key, + temporary: temp || false }) }, diff --git a/js/components/frame.js b/js/components/frame.js index 6a68a6c7963..1540dcb7ec3 100644 --- a/js/components/frame.js +++ b/js/components/frame.js @@ -887,6 +887,9 @@ class Frame extends ImmutableComponent { }) this.webview.addEventListener('did-finish-load', () => { loadEnd(true) + if (this.runInsecureContent()) { + appActions.removeSiteSetting(this.origin, 'runInsecureContent', this.props.isPrivate) + } }) this.webview.addEventListener('did-navigate-in-page', (e) => { windowActions.setNavigated(e.url, this.props.frameKey, true) diff --git a/js/components/siteInfo.js b/js/components/siteInfo.js index e89b8491d62..e5492274b38 100644 --- a/js/components/siteInfo.js +++ b/js/components/siteInfo.js @@ -16,18 +16,29 @@ class SiteInfo extends ImmutableComponent { constructor () { super() this.onAllowRunInsecureContent = this.onAllowRunInsecureContent.bind(this) + this.onDenyRunInsecureContent = this.onDenyRunInsecureContent.bind(this) } onAllowRunInsecureContent () { - appActions.changeSiteSetting(siteUtil.getOrigin(this.isBlockedRunInsecureContent), 'runInsecureContent', true) + appActions.changeSiteSetting(siteUtil.getOrigin(this.isBlockedRunInsecureContent), + 'runInsecureContent', true, this.isPrivate) ipc.emit(messages.SHORTCUT_ACTIVE_FRAME_LOAD_URL, {}, this.isBlockedRunInsecureContent) this.props.onHide() } + onDenyRunInsecureContent () { + appActions.removeSiteSetting(siteUtil.getOrigin(this.location), + 'runInsecureContent', this.isPrivate) + ipc.emit(messages.SHORTCUT_ACTIVE_FRAME_LOAD_URL, {}, this.location) + this.props.onHide() + } get isExtendedValidation () { return this.props.frameProps.getIn(['security', 'isExtendedValidation']) } get isSecure () { return this.props.frameProps.getIn(['security', 'isSecure']) } + get isPrivate () { + return this.props.frameProps.getIn(['isPrivate']) + } get runInsecureContent () { return this.props.frameProps.getIn(['security', 'runInsecureContent']) } @@ -37,6 +48,9 @@ class SiteInfo extends ImmutableComponent { get partitionNumber () { return this.props.frameProps.getIn(['partitionNumber']) } + get location () { + return this.props.frameProps.getIn(['location']) + } render () { let secureIcon if (this.isSecure && !this.runInsecureContent) { @@ -63,19 +77,34 @@ class SiteInfo extends ImmutableComponent { } - let runInsecureContentWarning = null + let runInsecureContentInfo = null if (this.isBlockedRunInsecureContent) { - runInsecureContentWarning = + runInsecureContentInfo =
  • +
  • + } else if (this.runInsecureContent) { + runInsecureContentInfo = +
  • +
  • } + // Disable in private mode for now + if (this.isPrivate) { + runInsecureContentInfo = null + } return diff --git a/js/state/contentSettings.js b/js/state/contentSettings.js index e3e97f59581..014233ac2d6 100644 --- a/js/state/contentSettings.js +++ b/js/state/contentSettings.js @@ -172,6 +172,7 @@ const getContentSettingsFromSiteSettings = (appState) => { addContentSettings(contentSettings.referer, hostPattern, '*', 'allow') } } + return { content_settings: contentSettings } } diff --git a/js/stores/appStore.js b/js/stores/appStore.js index f1ccd7f71d1..517e936c3eb 100644 --- a/js/stores/appStore.js +++ b/js/stores/appStore.js @@ -487,15 +487,20 @@ const handleAppAction = (action) => { handleChangeSettingAction(action.key, action.value) break case AppConstants.APP_CHANGE_SITE_SETTING: - let propertyName = action.temporary ? 'temporarySiteSettings' : 'siteSettings' - appState = appState.set(propertyName, - siteSettings.mergeSiteSetting(appState.get(propertyName), action.hostPattern, action.key, action.value)) - break + { + let propertyName = action.temporary ? 'temporarySiteSettings' : 'siteSettings' + appState = appState.set(propertyName, + siteSettings.mergeSiteSetting(appState.get(propertyName), action.hostPattern, action.key, action.value)) + break + } case AppConstants.APP_REMOVE_SITE_SETTING: - let newSiteSettings = siteSettings.removeSiteSetting(appState.get('siteSettings'), - action.hostPattern, action.key) - appState = appState.set('siteSettings', newSiteSettings) - break + { + let propertyName = action.temporary ? 'temporarySiteSettings' : 'siteSettings' + let newSiteSettings = siteSettings.removeSiteSetting(appState.get(propertyName), + action.hostPattern, action.key) + appState = appState.set(propertyName, newSiteSettings) + break + } case AppConstants.APP_UPDATE_LEDGER_INFO: appState = appState.set('ledgerInfo', Immutable.fromJS(action.ledgerInfo)) break diff --git a/test/components/navigationBarTest.js b/test/components/navigationBarTest.js index 89d3ce77092..6e03cc04211 100644 --- a/test/components/navigationBarTest.js +++ b/test/components/navigationBarTest.js @@ -4,11 +4,13 @@ const Brave = require('../lib/brave') const config = require('../../js/constants/config') const {urlBarSuggestions, urlInput, activeWebview, activeTabFavicon, activeTab, navigatorLoadTime, navigator, titleBar, urlbarIcon, bookmarksToolbar, navigatorNotBookmarked, navigatorBookmarked, - saveButton, allowRunInsecureContentButton, denyRunInsecureContentButton} = require('../lib/selectors') + saveButton, allowRunInsecureContentButton, dismissAllowRunInsecureContentButton, + denyRunInsecureContentButton, dismissDenyRunInsecureContentButton} = require('../lib/selectors') const urlParse = require('url').parse const assert = require('assert') const settings = require('../../js/constants/settings') const searchProviders = require('../../js/data/searchProviders') +const messages = require('../../js/constants/messages') describe('navigationBar', function () { function * setup (client) { @@ -283,10 +285,10 @@ describe('navigationBar', function () { .waitForExist(urlbarIcon + '.fa-lock') .click(urlbarIcon) .waitForVisible('.runInsecureContentWarning') - .waitForVisible(denyRunInsecureContentButton) + .waitForVisible(dismissAllowRunInsecureContentButton) .waitForVisible(allowRunInsecureContentButton) .waitForVisible('[data-l10n-id="secureConnection"]') - .click(denyRunInsecureContentButton) + .click(dismissAllowRunInsecureContentButton) // TODO(bridiver) there is a race condition here because we are waiting for a non-change // and we need some way to verify that the page does not reload and allow insecure content .tabByUrl(page1Url).waitUntil(() => { @@ -298,7 +300,7 @@ describe('navigationBar', function () { .click(urlbarIcon) .waitForExist(urlbarIcon + '.fa-lock') }) - it('Temporarily allow running insecure content', function * () { + it('Temporarily allow/deny running insecure content', function * () { const page1Url = 'https://mixed-script.badssl.com/' yield this.app.client.tabByUrl(Brave.newTabUrl) .loadUrl(page1Url) @@ -311,8 +313,9 @@ describe('navigationBar', function () { .windowByUrl(Brave.browserWindowUrl) .waitForExist(urlbarIcon + '.fa-lock') .click(urlbarIcon) + .waitForVisible('[data-l10n-id="secureConnection"]') .waitForVisible('.runInsecureContentWarning') - .waitForVisible(denyRunInsecureContentButton) + .waitForVisible(dismissAllowRunInsecureContentButton) .waitForVisible(allowRunInsecureContentButton) .click(allowRunInsecureContentButton) .tabByUrl(this.page1Url) @@ -322,10 +325,163 @@ describe('navigationBar', function () { ) }) .windowByUrl(Brave.browserWindowUrl) + .waitForExist(urlbarIcon) .click(urlbarIcon) - .waitForExist(urlbarIcon + '.fa-unlock') .waitForVisible('[data-l10n-id="mixedConnection"]') - .keys('\uE00C') + .waitForVisible('.denyRunInsecureContentWarning') + .waitForVisible(dismissDenyRunInsecureContentButton) + .waitForVisible(denyRunInsecureContentButton) + .click(denyRunInsecureContentButton) + .tabByUrl(this.page1Url) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .click(urlbarIcon) + .waitForExist(urlbarIcon + '.fa-lock') + }) + it('Limit effect of running insecure content in frame', function * () { + const page1Url = 'https://mixed-script.badssl.com/' + yield this.app.client.tabByUrl(Brave.newTabUrl) + .loadUrl(page1Url) + // background color changes when insecure content runs + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .waitForExist(urlbarIcon + '.fa-lock') + .click(urlbarIcon) + .waitForVisible('[data-l10n-id="secureConnection"]') + .waitForVisible('.runInsecureContentWarning') + .waitForVisible(dismissAllowRunInsecureContentButton) + .waitForVisible(allowRunInsecureContentButton) + .click(allowRunInsecureContentButton) + .tabByIndex(0) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(255,0,0,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 2 + }) + }) + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(1) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url, { isPrivate: true }) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 3 + }) + }) + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(2) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url, { partitionNumber: 1 }) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 4 + }) + }) + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(3) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + }) + it.skip('Limit effect of running insecure content in private frame', function * () { + const page1Url = 'https://mixed-script.badssl.com/' + yield this.app.client + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url, { isPrivate: true }) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 2 + }) + }) + // background color changes when insecure content runs + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(1) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .waitForExist(urlbarIcon + '.fa-lock') + .click(urlbarIcon) + .waitForVisible('[data-l10n-id="secureConnection"]') + .waitForVisible('.runInsecureContentWarning') + .waitForVisible(dismissAllowRunInsecureContentButton) + .waitForVisible(allowRunInsecureContentButton) + .click(allowRunInsecureContentButton) + .tabByUrl(this.page1Url) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(255,0,0,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 3 + }) + }) + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(2) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url, { isPrivate: true }) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 4 + }) + }) + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(3) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) + .windowByUrl(Brave.browserWindowUrl) + .ipcSend(messages.SHORTCUT_NEW_FRAME, page1Url, { partitionNumber: 1 }) + .waitUntil(function () { + return this.getWindowState().then((val) => { + return val.value.frames.length === 5 + }) + }) + .windowByUrl(Brave.browserWindowUrl) + .tabByIndex(4) + .waitUntil(() => { + return this.app.client.getCssProperty('body', 'background-color').then((color) => + color.value === 'rgba(128,128,128,1)' + ) + }) }) }) diff --git a/test/lib/selectors.js b/test/lib/selectors.js index eb8c71de6eb..4ff78271d1f 100644 --- a/test/lib/selectors.js +++ b/test/lib/selectors.js @@ -62,5 +62,7 @@ module.exports = { autofillAddressPanel: '.autofillAddressPanel', autofillCreditCardPanel: '.autofillCreditCardPanel', allowRunInsecureContentButton: '.allowRunInsecureContentButton', - denyRunInsecureContentButton: '.denyRunInsecureContentButton' + dismissAllowRunInsecureContentButton: '.dismissAllowRunInsecureContentButton', + denyRunInsecureContentButton: '.denyRunInsecureContentButton', + dismissDenyRunInsecureContentButton: '.dismissDenyRunInsecureContentButton' }