mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 00:40:56 -05:00
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:
parent
f29f4cb82b
commit
bdabee865c
5 changed files with 43 additions and 9 deletions
|
@ -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) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
iframe(src="/web-dev-iframe.html")
|
||||
iframe(src="http://" + fullUnsafeContentHostname + "/web-dev-iframe.html")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue