- {warning &&
-
- }
+ {this._renderWarnings()}
{this._currentView()}
@@ -72,8 +68,16 @@ class Project extends Component {
}
}
- _removeWarning = () => {
- this.props.project.clearWarning()
+ _renderWarnings = () => {
+ const { warnings } = this.props.project
+
+ return warnings.map((warning, i) =>
+ ( this._removeWarning(warning)}/>)
+ )
+ }
+
+ _removeWarning = (warning) => {
+ this.props.project.clearWarning(warning)
}
_reopenProject = () => {
diff --git a/packages/desktop-gui/src/project/warning-message.jsx b/packages/desktop-gui/src/project/warning-message.jsx
index 1e3197878176..9acc559bb5ae 100644
--- a/packages/desktop-gui/src/project/warning-message.jsx
+++ b/packages/desktop-gui/src/project/warning-message.jsx
@@ -1,32 +1,9 @@
import React, { Component } from 'react'
import { observer } from 'mobx-react'
-import Markdown from 'markdown-it'
-
-import ipc from '../lib/ipc'
-
-const md = new Markdown({
- html: true,
- linkify: true,
-})
+import MarkdownRenderer from '../lib/markdown-renderer'
@observer
class WarningMessage extends Component {
- componentDidMount () {
- this.warningMessageNode.addEventListener('click', this._clickHandler)
- }
-
- componentWillUnmount () {
- this.warningMessageNode.removeEventListener('click', this._clickHandler)
- }
-
- _clickHandler (e) {
- if (e.target.href) {
- e.preventDefault()
-
- return ipc.externalOpen(e.target.href)
- }
- }
-
render () {
const warningText = this.props.warning.message.split('\n').join('
')
@@ -36,9 +13,9 @@ class WarningMessage extends Component {
{' '}
Warning
- this.warningMessageNode = node} dangerouslySetInnerHTML={{
- __html: md.render(warningText),
- }}>
+
+
+
diff --git a/packages/desktop-gui/src/projects/projects-api.js b/packages/desktop-gui/src/projects/projects-api.js
index 78a4d1773af5..32c1ba1bf392 100644
--- a/packages/desktop-gui/src/projects/projects-api.js
+++ b/packages/desktop-gui/src/projects/projects-api.js
@@ -168,7 +168,7 @@ const openProject = (project) => {
})
ipc.onProjectWarning((__, warning) => {
- project.setWarning(warning)
+ project.addWarning(warning)
})
return ipc.openProject(project.path)
diff --git a/packages/launcher/lib/types.ts b/packages/launcher/lib/types.ts
index 1ba6ca1f1cd4..1aca1ebec540 100644
--- a/packages/launcher/lib/types.ts
+++ b/packages/launcher/lib/types.ts
@@ -35,6 +35,8 @@ export type FoundBrowser = Browser & {
custom?: boolean
/** optional info that will be shown in the GUI */
info?: string
+ /** optional warning that will be shown in the GUI */
+ warning?: string
}
// all common type definition for this module
diff --git a/packages/server/lib/errors.coffee b/packages/server/lib/errors.coffee
index c1677c1387ae..377e0854d47b 100644
--- a/packages/server/lib/errors.coffee
+++ b/packages/server/lib/errors.coffee
@@ -788,6 +788,20 @@ getMsgByType = (type, arg1 = {}, arg2) ->
Provide a path to an existing fixture file.
"""
+ when "BAD_POLICY_WARNING"
+ """
+ Cypress detected policy settings on your computer that may cause issues.
+
+ The following policies were detected that may prevent Cypress from automating Chrome:
+
+ > #{arg1.join('\n > ')}
+
+ For more information, see https://on.cypress.io/bad-browser-policy
+ """
+ when "BAD_POLICY_WARNING_TOOLTIP"
+ """
+ Cypress detected policy settings on your computer that may cause issues with using this browser. For more information, see https://on.cypress.io/bad-browser-policy
+ """
get = (type, arg1, arg2) ->
msg = getMsgByType(type, arg1, arg2)
diff --git a/packages/server/lib/gui/events.coffee b/packages/server/lib/gui/events.coffee
index bb179e7a62ac..4d66aef92af0 100644
--- a/packages/server/lib/gui/events.coffee
+++ b/packages/server/lib/gui/events.coffee
@@ -14,6 +14,7 @@ Updater = require("../updater")
Project = require("../project")
openProject = require("../open_project")
ensureUrl = require("../util/ensure-url")
+chromePolicyCheck = require("../util/chrome_policy_check")
browsers = require("../browsers")
konfig = require("../konfig")
@@ -204,6 +205,11 @@ handleEvent = (options, bus, event, id, type, arg) ->
.then (browsers = []) ->
options.config = _.assign(options.config, { browsers })
.then ->
+ chromePolicyCheck.run (err) ->
+ options.config.browsers.forEach (browser) ->
+ if browser.family == 'chrome'
+ browser.warning = errors.getMsgByType('BAD_POLICY_WARNING_TOOLTIP')
+
openProject.create(arg, options, {
onFocusTests: onFocusTests
onSpecChanged: onSpecChanged
diff --git a/packages/server/lib/modes/run.coffee b/packages/server/lib/modes/run.coffee
index 89963e011a4c..cbf894dfc172 100644
--- a/packages/server/lib/modes/run.coffee
+++ b/packages/server/lib/modes/run.coffee
@@ -25,6 +25,7 @@ terminal = require("../util/terminal")
specsUtil = require("../util/specs")
humanTime = require("../util/human_time")
electronApp = require("../util/electron_app")
+chromePolicyCheck = require("../util/chrome_policy_check")
color = (val, c) ->
chalk[c](val)
@@ -341,6 +342,9 @@ writeOutput = (outputPath, results) ->
fs.outputJsonAsync(outputPath, results)
+onWarning = (err) ->
+ console.log(chalk.yellow(err.message))
+
openProjectCreate = (projectRoot, socketId, options) ->
## now open the project to boot the server
## putting our web client app in headless mode
@@ -351,8 +355,7 @@ openProjectCreate = (projectRoot, socketId, options) ->
morgan: false
report: true
isTextTerminal: options.isTextTerminal
- onWarning: (err) ->
- console.log(err.message)
+ onWarning
onError: (err) ->
console.log("")
if err.details
@@ -953,6 +956,9 @@ module.exports = {
if not specs.length
errors.throw('NO_SPECS_FOUND', config.integrationFolder, specPattern)
+ if browser.family == 'chrome'
+ chromePolicyCheck.run(onWarning)
+
runAllSpecs = ({ beforeSpecRun, afterSpecRun, runUrl }, parallelOverride = parallel) =>
@runSpecs({
beforeSpecRun
diff --git a/packages/server/lib/util/chrome_policy_check.js b/packages/server/lib/util/chrome_policy_check.js
new file mode 100644
index 000000000000..8e550f7aec6d
--- /dev/null
+++ b/packages/server/lib/util/chrome_policy_check.js
@@ -0,0 +1,111 @@
+const _ = require('lodash')
+const debug = require('debug')('cypress:server:chrome_policy_check')
+const errors = require('../errors')
+const os = require('os')
+
+// https://www.chromium.org/administrators/policy-list-3#Proxy
+// https://www.chromium.org/administrators/policy-list-3#ProxySettings
+const BAD_PROXY_POLICY_NAMES = [
+ 'ProxySettings',
+ 'ProxyMode',
+ 'ProxyServerMode',
+ 'ProxyServer',
+ 'ProxyPacUrl',
+ 'ProxyBypassList',
+]
+
+// https://www.chromium.org/administrators/policy-list-3#Extensions
+const BAD_EXTENSION_POLICY_NAMES = [
+ 'ExtensionInstallBlacklist',
+ 'ExtensionInstallWhitelist',
+ 'ExtensionInstallForcelist',
+ 'ExtensionInstallSources',
+ 'ExtensionAllowedTypes',
+ 'ExtensionAllowInsecureUpdates',
+ 'ExtensionSettings',
+ 'UninstallBlacklistedExtensions',
+]
+
+const POLICY_KEYS = [
+ 'Software\\Policies\\Google\\Chrome',
+ 'Software\\Policies\\Google\\Chromium',
+]
+
+const POLICY_HKEYS = [
+ 'HKEY_LOCAL_MACHINE',
+ 'HKEY_CURRENT_USER',
+]
+
+function warnIfPolicyMatches (policyNames, allPolicies, warningName, cb) {
+ const matchedPolicyPaths = _.chain(policyNames)
+ .map((policyName) => {
+ return _.chain(allPolicies)
+ .find({ name: policyName })
+ .get('fullPath')
+ .value()
+ })
+ .filter()
+ .value()
+
+ if (!matchedPolicyPaths.length) {
+ return
+ }
+
+ cb(errors.get(warningName, matchedPolicyPaths))
+}
+
+function getRunner ({ enumerateValues }) {
+ function getAllPolicies () {
+ return _.flattenDeep(
+ POLICY_KEYS.map((key) => {
+ return POLICY_HKEYS.map((hkey) => {
+ return enumerateValues(hkey, key)
+ .map((value) => {
+ value.fullPath = `${hkey}\\${key}\\${value.name}`
+
+ return value
+ })
+ })
+ })
+ )
+ }
+
+ return function run (cb) {
+ try {
+ debug('running chrome policy check')
+
+ const policies = getAllPolicies()
+ const badPolicyNames = _.concat(BAD_PROXY_POLICY_NAMES, BAD_EXTENSION_POLICY_NAMES)
+
+ debug('received policies %o', { policies, badPolicyNames })
+
+ warnIfPolicyMatches(badPolicyNames, policies, 'BAD_POLICY_WARNING', cb)
+ } catch (err) {
+ debug('error running policy check %o', { err })
+ }
+ }
+}
+
+module.exports = {
+ run: _.noop,
+ getRunner,
+}
+
+/**
+ * Only check on Windows. While it is possible for macOS/Linux to have preferences set that
+ * override Cypress's settings, it's never been reported as an issue and would require more
+ * native extensions to support checking.
+ * https://github.com/cypress-io/cypress/issues/4391
+ */
+if (os.platform() === 'win32') {
+ try {
+ const registryJs = require('@cypress/registry-js')
+
+ module.exports = {
+ run: getRunner(registryJs),
+ getRunner,
+ }
+ } catch (err) {
+ debug('error initializing chrome policy check %o', { err })
+ }
+}
diff --git a/packages/server/test/unit/chrome_policy_check.coffee b/packages/server/test/unit/chrome_policy_check.coffee
new file mode 100644
index 000000000000..ee0914517e13
--- /dev/null
+++ b/packages/server/test/unit/chrome_policy_check.coffee
@@ -0,0 +1,64 @@
+require("../spec_helper")
+
+_ = require("lodash")
+{ stripIndent } = require("common-tags")
+chromePolicyCheck = require("#{root}lib/util/chrome_policy_check")
+
+describe "lib/util/chrome_policy_check", ->
+ context ".getRunner returns a function", ->
+ it "calls callback with an error if policies are found", ->
+ run = chromePolicyCheck.getRunner({
+ enumerateValues: (hkey, key) ->
+ ## mock a registry with a couple of policies
+ _.get({
+ 'HKEY_LOCAL_MACHINE': {
+ 'Software\\Policies\\Google\\Chrome': [
+ { name: 'ProxyServer' }
+ ]
+ },
+ 'HKEY_CURRENT_USER': {
+ 'Software\\Policies\\Google\\Chromium': [
+ { name: 'ExtensionSettings' }
+ ]
+ },
+ }, "#{hkey}.#{key}", [])
+ })
+
+ cb = sinon.stub()
+
+ run(cb)
+
+ expect(cb).to.be.calledOnce
+ expect(cb.getCall(0).args[0].message).to.eq(stripIndent """
+ Cypress detected policy settings on your computer that may cause issues.
+
+ The following policies were detected that may prevent Cypress from automating Chrome:
+
+ > HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome\\ProxyServer
+ > HKEY_CURRENT_USER\\Software\\Policies\\Google\\Chromium\\ExtensionSettings
+
+ For more information, see https://on.cypress.io/bad-browser-policy
+ """
+ )
+
+ it "does not call callback if no policies are found", ->
+ run = chromePolicyCheck.getRunner({
+ enumerateValues: _.constant([])
+ })
+
+ cb = sinon.stub()
+
+ run(cb)
+
+ expect(cb).to.not.be.called
+
+ it "fails silently if enumerateValues throws", ->
+ run = chromePolicyCheck.getRunner({
+ enumerateValues: -> throw new Error('blah')
+ })
+
+ cb = sinon.stub()
+
+ run(cb)
+
+ expect(cb).to.not.be.called
diff --git a/packages/server/test/unit/gui/events_spec.coffee b/packages/server/test/unit/gui/events_spec.coffee
index eb9f1b70624b..872d4080684c 100644
--- a/packages/server/test/unit/gui/events_spec.coffee
+++ b/packages/server/test/unit/gui/events_spec.coffee
@@ -5,6 +5,7 @@ EE = require("events")
extension = require("@packages/extension")
electron = require("electron")
Promise = require("bluebird")
+chromePolicyCheck = require("#{root}../lib/util/chrome_policy_check")
cache = require("#{root}../lib/cache")
logger = require("#{root}../lib/logger")
Project = require("#{root}../lib/project")
@@ -493,6 +494,34 @@ describe "lib/gui/events", ->
}
)
+ it "attaches warning to Chrome browsers when Chrome policy check fails", ->
+ sinon.stub(openProject, "create").resolves()
+ @options.browser = "/foo"
+
+ browsers.getAllBrowsersWith.withArgs("/foo").resolves([{family: 'chrome'}, {family: 'some other'}])
+
+ sinon.stub(chromePolicyCheck, "run").callsArgWith(0, new Error)
+
+ @handleEvent("open:project", "/_test-output/path/to/project").then =>
+ expect(browsers.getAllBrowsersWith).to.be.calledWith(@options.browser)
+ expect(openProject.create).to.be.calledWithMatch(
+ "/_test-output/path/to/project",
+ {
+ browser: "/foo",
+ config: {
+ browsers: [
+ {
+ family: "chrome"
+ warning: "Cypress detected policy settings on your computer that may cause issues with using this browser. For more information, see https://on.cypress.io/bad-browser-policy"
+ },
+ {
+ family: "some other"
+ }
+ ]
+ }
+ }
+ )
+
describe "close:project", ->
beforeEach ->
sinon.stub(Project.prototype, "close").withArgs({sync: true}).resolves()
diff --git a/scripts/check-deps.js b/scripts/check-deps.js
index 77b89a2bc74c..2b4d3184e867 100644
--- a/scripts/check-deps.js
+++ b/scripts/check-deps.js
@@ -6,6 +6,10 @@ const fs = require('fs')
const path = require('path')
const stripAnsi = require('strip-ansi')
+if (process.env.NO_CHECK_DEPS) {
+ process.exit(0)
+}
+
const args = require('minimist')(process.argv.slice(2))
const cwd = args.cwd || process.cwd()