Skip to content

Commit

Permalink
Add whitelistHosts option.
Browse files Browse the repository at this point in the history
  • Loading branch information
dracos committed Mar 13, 2018
1 parent 1251fce commit b864f04
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 14 deletions.
8 changes: 8 additions & 0 deletions cli/schema/cypress.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@
"default": null,
"description": "A String or Array of hosts that you wish to block traffic for. Please read the notes for examples on using this https://on.cypress.io/configuration#blacklistHosts"
},
"whitelistHosts": {
"type": ["string", "array"],
"items": {
"type": "string"
},
"default": null,
"description": "A String or Array of hosts that you wish to allow traffic for. Please read the notes for examples on using this https://on.cypress.io/configuration#whitelistHosts"
},
"modifyObstructiveCode": {
"type": "boolean",
"default": true,
Expand Down
35 changes: 35 additions & 0 deletions packages/server/__snapshots__/whitelist_hosts_spec.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
exports['e2e whitelist passes 1'] = `
Started video recording: /foo/bar/.projects/e2e/cypress/videos/abc123.mp4

(Tests Starting)


whitelist
forces non-whitelisted hosts to return 503


1 passing


(Tests Finished)

- Tests: 1
- Passes: 1
- Failures: 0
- Pending: 0
- Duration: 10 seconds
- Screenshots: 0
- Video Recorded: true
- Cypress Version: 1.2.3


(Video)

- Started processing: Compressing to 32 CRF
- Finished processing: /foo/bar/.projects/e2e/cypress/videos/abc123.mp4 (0 seconds)


(All Done)

`

8 changes: 7 additions & 1 deletion packages/server/lib/config.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ configKeys = toWords """
testFiles execTimeout
trashAssetsBeforeHeadlessRuns pageLoadTimeout
blacklistHosts requestTimeout
userAgent responseTimeout
whitelistHosts responseTimeout
userAgent
viewportWidth
viewportHeight
videoRecording
Expand Down Expand Up @@ -87,6 +88,7 @@ defaults = {
reporter: "spec"
reporterOptions: null
blacklistHosts: null
whitelistHosts: null
clientRoute: "/__/"
xhrRoute: "/xhrs/"
socketIoRoute: "/__socket.io"
Expand Down Expand Up @@ -130,6 +132,7 @@ validationRules = {
animationDistanceThreshold: v.isNumber
baseUrl: v.isFullyQualifiedUrl
blacklistHosts: v.isStringOrArrayOfStrings
whitelistHosts: v.isStringOrArrayOfStrings
modifyObstructiveCode: v.isBoolean
chromeWebSecurity: v.isBoolean
defaultCommandTimeout: v.isNumber
Expand Down Expand Up @@ -245,6 +248,9 @@ module.exports = {
if blacklistHosts = config.blacklistHosts
config.blacklistHosts = toArrayFromPipes(blacklistHosts)

if whitelistHosts = config.whitelistHosts
config.whitelistHosts = toArrayFromPipes(whitelistHosts)

## when headless
if config.isTextTerminal
## dont ever watch for file changes
Expand Down
18 changes: 16 additions & 2 deletions packages/server/lib/controllers/proxy.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ cwd = require("../cwd")
cors = require("../util/cors")
buffers = require("../util/buffers")
rewriter = require("../util/rewriter")
blacklist = require("../util/blacklist")
hostlist = require("../util/hostlist")
conditional = require("../util/conditional_stream")
networkFailures = require("../util/network_failures")

Expand Down Expand Up @@ -63,7 +63,7 @@ module.exports = {
## if we have black listed hosts
if blh = config.blacklistHosts
## and url matches any of our blacklisted hosts
if matched = blacklist.matches(req.proxiedUrl, blh)
if matched = hostlist.matches(req.proxiedUrl, blh)
## then bail and return with 503
## and set a custom header
res.set("x-cypress-matched-blacklisted-host", matched)
Expand All @@ -75,6 +75,20 @@ module.exports = {

return res.status(503).end()

## if we have white listed hosts
if wlh = config.whitelistHosts
## and url does not match any of our whitelisted hosts
if not matched = hostlist.matches(req.proxiedUrl, wlh)
## then bail and return with 503
## and set a custom header
res.set("x-cypress-not-matched-whitelisted-host", '1')

debug("blacklisting request %o as not on whitelist", {
url: req.proxiedUrl
})

return res.status(503).end()

thr = through (d) -> @queue(d)

@getHttpContent(thr, req, res, remoteState, config, request)
Expand Down
11 changes: 8 additions & 3 deletions packages/server/lib/server.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ origin = require("./util/origin")
connect = require("./util/connect")
appData = require("./util/app_data")
buffers = require("./util/buffers")
blacklist = require("./util/blacklist")
hostlist = require("./util/hostlist")
statusCode = require("./util/status_code")
headersUtil = require("./util/headers")
allowDestroy = require("./util/server_destroy")
Expand Down Expand Up @@ -134,7 +134,7 @@ class Server

createServer: (app, config, request) ->
new Promise (resolve, reject) =>
{port, fileServerFolder, socketIoRoute, baseUrl, blacklistHosts} = config
{port, fileServerFolder, socketIoRoute, baseUrl, blacklistHosts, whitelistHosts} = config

@_server = http.createServer(app)
@_wsProxy = httpProxy.createProxyServer()
Expand Down Expand Up @@ -186,10 +186,15 @@ class Server
## we cannot allow it to make a direct
## connection
if blacklistHosts and not isMatching
isMatching = blacklist.matches(urlToCheck, blacklistHosts)
isMatching = hostlist.matches(urlToCheck, blacklistHosts)

debug("HTTPS request #{urlToCheck} matches blacklist?", isMatching)

if whitelistHosts and not isMatching
isMatching = not hostlist.matches(urlToCheck, whitelistHosts)

debug("HTTPS request #{urlToCheck} does not match whitelist?", isMatching)

## make a direct connection only if
## our req url does not match the origin policy
## which is the superDomain + port
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ stripProtocolAndDefaultPorts = (urlToCheck) ->
## else return the host
return host

matches = (urlToCheck, blacklistHosts) ->
matches = (urlToCheck, listHosts) ->
## normalize into flat array
blacklistHosts = [].concat(blacklistHosts)
listHosts = [].concat(listHosts)

urlToCheck = stripProtocolAndDefaultPorts(urlToCheck)

Expand All @@ -27,7 +27,7 @@ matches = (urlToCheck, blacklistHosts) ->
## to see if any match
minimatch(urlToCheck, hostMatcher)

_.find(blacklistHosts, matchUrl)
_.find(listHosts, matchUrl)


module.exports = {
Expand Down
33 changes: 33 additions & 0 deletions packages/server/test/e2e/whitelist_hosts_spec.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
e2e = require("../support/helpers/e2e")

onServer = (app) ->
app.get "/", (req, res) ->
res.send("<html>hi there</html>")

app.get "/req", (req, res) ->
res.sendStatus(200)

app.get "/status", (req, res) ->
res.sendStatus(503)

describe "e2e whitelist", ->
e2e.setup({
servers: [{
port: 3131
onServer
}, {
port: 3232
onServer
}]
settings: {
baseUrl: "http://localhost:3232"
whitelistHosts: "localhost:3232"
}
})

it "passes", ->
e2e.exec(@, {
spec: "whitelist_hosts_spec.coffee"
snapshot: true
expectedExitCode: 0
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
describe "whitelist", ->
it "forces non-whitelisted hosts to return 503", ->
cy
.visit("http://localhost:3232")

.window().then (win) ->
new Promise (resolve) ->
xhr = new win.XMLHttpRequest
xhr.open("GET", "http://localhost:3232/req")
xhr.setRequestHeader('Content-Type', 'text/plain')
xhr.send()
xhr.onload = ->
resolve(xhr)
.its("status").should("eq", 200)

.window().then (win) ->
new Promise (resolve) ->
xhr = new win.XMLHttpRequest
xhr.open("GET", "http://localhost:3131/req")
xhr.setRequestHeader('Content-Type', 'text/plain')
xhr.send()
xhr.onerror = ->
## cross origin requests which return 503
## result in a zero status code
resolve(xhr)
.its("status").should("eq", 0)
36 changes: 36 additions & 0 deletions packages/server/test/unit/config_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,24 @@ describe "lib/config", ->
@setup({watchForFileChanges: 42})
@expectValidationFails("be a boolean")

context "whitelistHosts", ->
it "passes if a string", ->
@setup({whitelistHosts: "google.com"})
@expectValidationPasses()

it "passes if an array of strings", ->
@setup({whitelistHosts: ["google.com"]})
@expectValidationPasses()

it "fails if not a string or array", ->
@setup({whitelistHosts: 5})
@expectValidationFails("be a string or an array of string")

it "fails if not an array of strings", ->
@setup({whitelistHosts: [5]})
@expectValidationFails("be a string or an array of string")
@expectValidationFails("the value was: [5]")

context "blacklistHosts", ->
it "passes if a string", ->
@setup({blacklistHosts: "google.com"})
Expand All @@ -427,6 +445,9 @@ describe "lib/config", ->
it "includes blacklistHosts", ->
@includes("blacklistHosts")

it "includes whitelistHosts", ->
@includes("whitelistHosts")

context ".resolveConfigValues", ->
beforeEach ->
@expected = (obj) ->
Expand Down Expand Up @@ -589,6 +610,19 @@ describe "lib/config", ->
it "supportFile=false", ->
@defaults "supportFile", false, {supportFile: false}

it "whitelistHosts=null", ->
@defaults("whitelistHosts", null)

it "whitelistHosts=[a,b]", ->
@defaults("whitelistHosts", ["a", "b"], {
whitelistHosts: ["a", "b"]
})

it "whitelistHosts=a|b", ->
@defaults("whitelistHosts", ["a", "b"], {
whitelistHosts: "a|b"
})

it "blacklistHosts=null", ->
@defaults("blacklistHosts", null)

Expand Down Expand Up @@ -716,6 +750,7 @@ describe "lib/config", ->
port: { value: 1234, from: "cli" },
hosts: { value: null, from: "default" }
blacklistHosts: { value: null, from: "default" }
whitelistHosts: { value: null, from: "default" }
userAgent: { value: null, from: "default" }
reporter: { value: "json", from: "cli" },
reporterOptions: { value: null, from: "default" },
Expand Down Expand Up @@ -775,6 +810,7 @@ describe "lib/config", ->
port: { value: 2020, from: "config" },
hosts: { value: null, from: "default" }
blacklistHosts: { value: null, from: "default" }
whitelistHosts: { value: null, from: "default" }
userAgent: { value: null, from: "default" }
reporter: { value: "spec", from: "default" },
reporterOptions: { value: null, from: "default" },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require("../spec_helper")

blacklist = require("#{root}lib/util/blacklist")
hostlist = require("#{root}lib/util/hostlist")

hosts = [
"*.google.com"
Expand All @@ -11,17 +11,17 @@ hosts = [
]

matchesStr = (url, host, val) ->
m = blacklist.matches(url, host)
m = hostlist.matches(url, host)
expect(!!m).to.eq(val, "url: '#{url}' did not pass")

matchesArray = (url, val) ->
m = blacklist.matches(url, hosts)
m = hostlist.matches(url, hosts)
expect(!!m).to.eq(val, "url: '#{url}' did not pass")

matchesHost = (url, host) ->
expect(blacklist.matches(url, hosts)).to.eq(host)
expect(hostlist.matches(url, hosts)).to.eq(host)

describe "lib/util/blacklist", ->
describe "lib/util/hostlist", ->
it "handles hosts, ports, wildcards", ->
matchesArray("https://mail.google.com/foo", true)
matchesArray("https://shop.apple.com/bar", true)
Expand Down

0 comments on commit b864f04

Please sign in to comment.