Filter domains for webdev iFrame

This serves the web-dev surface iFrame from another domain, such that user-created levels can't sniff cookies from a visitor to their page. It forces a redirect if a path is accesses through the wrong domain.

Use ENV variables for hostnames

Allow messages from all relevant domains

Use the right iFrame URL for different domains

Let the load balancer check /healthcheck

Add special handling for china server

Generalize subdomain handling
This commit is contained in:
Phoenix Eliot 2016-08-19 13:22:14 -07:00
parent f29f4cb82b
commit bdabee865c
5 changed files with 43 additions and 9 deletions

View file

@ -17,13 +17,9 @@ var virtualScripts;
var goalStates;
var allowedOrigins = [
/https:\/\/codecombat\.com/,
/https?:\/\/cn\.codecombat\.com/,
/http:\/\/localhost:3000/,
/http:\/\/direct\.codecombat\.com/,
/http:\/\/staging\.codecombat\.com/,
/http:\/\/next\.codecombat\.com/,
/http:\/\/.*codecombat-staging-codecombat\.runnableapp\.com/,
/^https?:\/\/(.*\.)?codecombat\.com$/,
/^https?:\/\/localhost:3000$/,
/^https?:\/\/.*codecombat-staging-codecombat\.runnableapp\.com$/,
];
function receiveMessage(event) {

View file

@ -1 +1 @@
iframe(src="/web-dev-iframe.html")
iframe(src="http://" + fullUnsafeContentHostname + "/web-dev-iframe.html")

View file

@ -13,6 +13,9 @@ module.exports = class WebSurfaceView extends CocoView
# Consider https://www.npmjs.com/package/css-select to do this on virtualDom instead of in iframe on concreteDOM
super(options)
getRenderData: ->
_.merge super(), { fullUnsafeContentHostname: serverConfig.fullUnsafeContentHostname }
afterRender: ->
super()
@iframe = @$('iframe')[0]
@ -81,7 +84,7 @@ module.exports = class WebSurfaceView extends CocoView
onIframeMessage: (event) =>
origin = event.origin or event.originalEvent.origin
unless origin is window.location.origin
unless new RegExp("^https?:\/\/#{serverConfig.fullUnsafeContentHostname}$").test origin
return console.log 'Ignoring message from bad origin:', origin
unless event.source is @iframe.contentWindow
return console.log 'Ignoring message from somewhere other than our iframe:', event.source

View file

@ -91,6 +91,11 @@ config.cookie_secret = process.env.COCO_COOKIE_SECRET or 'chips ahoy'
config.isProduction = config.mongo.host isnt 'localhost'
# Domains (without subdomain prefix, with port number) for main hostname (usually codecombat.com)
# and unsafe web-dev iFrame content (usually codecombatprojects.com).
config.mainHostname = process.env.COCO_MAIN_HOSTNAME or 'localhost:3000'
config.unsafeContentHostname = process.env.COCO_UNSAFE_CONTENT_HOSTNAME or 'localhost:3000'
if process.env.COCO_PICOCTF
config.picoCTF = true
config.picoCTF_api_url = 'http://staging.picoctf.com/api'

View file

@ -52,6 +52,32 @@ developmentLogging = (tokens, req, res) ->
s += ' (proxied)' if req.proxied
return s
setupDomainFilterMiddleware = (app) ->
if config.isProduction
unsafePaths = [
/^\/web-dev-iframe\.html$/
/^\/javascripts\/web-dev-listener\.js$/
]
serveFromBoth = [
/^\/healthcheck$/ # Allow the load balancer to check if we're up yet
/^\/javascripts\/workers\/aether_worker\.js$/
/^\/javascripts\/app\/vendor\/aether-html\.js$/
/^\/file\/db\/thang.type\/[a-f0-9]+\/.*$/
/^\/images\/.*$/
]
app.use (req, res, next) ->
domainRegex = new RegExp("(.*\.)?(#{config.mainHostname}|#{config.unsafeContentHostname})")
domainPrefix = req.host.match(domainRegex)?[1] or ''
if _.any(serveFromBoth, (pathRegex) -> pathRegex.test(req.path))
next()
else if _.any(unsafePaths, (pathRegex) -> pathRegex.test(req.path))
if req.host isnt domainPrefix + config.unsafeContentHostname
res.redirect('http://' + domainPrefix + config.unsafeContentHostname + req.path)
else
next()
else
next()
setupErrorMiddleware = (app) ->
app.use (err, req, res, next) ->
if err
@ -177,6 +203,7 @@ exports.setupMiddleware = (app) ->
setupPerfMonMiddleware app
setupCountryRedirectMiddleware app, "china", "CN", "zh", config.chinaDomain
setupCountryRedirectMiddleware app, "brazil", "BR", "pt-BR", config.brazilDomain
setupDomainFilterMiddleware app
setupMiddlewareToSendOldBrowserWarningWhenPlayersViewLevelDirectly app
setupExpressMiddleware app
setupPassportMiddleware app
@ -206,6 +233,9 @@ setupFallbackRouteToIndex = (app) ->
configData = _.omit mandate?.toObject() or {}, '_id'
configData.picoCTF = config.picoCTF
configData.production = config.isProduction
domainRegex = new RegExp("(.*\.)?(#{config.mainHostname}|#{config.unsafeContentHostname})")
domainPrefix = req.host.match(domainRegex)?[1] or ''
configData.fullUnsafeContentHostname = domainPrefix + config.unsafeContentHostname
data = data.replace '"serverConfigTag"', JSON.stringify configData
data = data.replace('"userObjectTag"', user)
data = data.replace('"amActuallyTag"', JSON.stringify(req.session.amActually))