Просмотр исходного кода

provide a static server which forbids symlinks

prevents mismatch between rootdir of server and rootdir of symlink
checking middleware
Brian Gough 11 лет назад
Родитель
Сommit
e024fec82d

+ 9 - 20
services/clsi/app.coffee

@@ -37,24 +37,12 @@ app.delete "/project/:project_id", CompileController.clearCache
 app.get  "/project/:project_id/sync/code", CompileController.syncFromCode
 app.get  "/project/:project_id/sync/code", CompileController.syncFromCode
 app.get  "/project/:project_id/sync/pdf", CompileController.syncFromPdf
 app.get  "/project/:project_id/sync/pdf", CompileController.syncFromPdf
 
 
-url = require "url"
-
-staticForbidSymLinks = (root, options) ->
-	expressStatic = express.static root, options
-	basePath = Path.resolve(root)
-	return (req, res, next) ->
-		path = url.parse(req.url).pathname
-		requestedFsPath = Path.normalize("#{basePath}/#{path}")
-		fs.realpath requestedFsPath, (err, realFsPath)->
-			if err?
-				return res.send(500)
-			else if requestedFsPath != realFsPath
-				logger.warn requestedFsPath:requestedFsPath, realFsPath:realFsPath, path: req.params[0], project_id: req.params.project_id, "trying to access a different file (symlink), aborting"
-				return res.send(404)
-			else
-				expressStatic(req, res, next)
-
-staticServer = staticForbidSymLinks Settings.path.compilesDir, setHeaders: (res, path, stat) ->
+ForbidSymlinks = require "./app/js/StaticServerForbidSymlinks"
+
+# create a static server which does not allow access to any symlinks
+# avoids possible mismatch of root directory between middleware check
+# and serving the files
+staticServer = ForbidSymlinks express.static, Settings.path.compilesDir, setHeaders: (res, path, stat) ->
 	if Path.basename(path) == "output.pdf"
 	if Path.basename(path) == "output.pdf"
 		res.set("Content-Type", "application/pdf")
 		res.set("Content-Type", "application/pdf")
 		# Calculate an etag in the same way as nginx
 		# Calculate an etag in the same way as nginx
@@ -68,9 +56,10 @@ staticServer = staticForbidSymLinks Settings.path.compilesDir, setHeaders: (res,
 		# that could be used in same-origin/XSS attacks.
 		# that could be used in same-origin/XSS attacks.
 		res.set("Content-Type", "text/plain")
 		res.set("Content-Type", "text/plain")
 
 
-app.get "/project/:project_id/output/*", require("./app/js/SymlinkCheckerMiddlewear"), (req, res, next) ->
+app.get "/project/:project_id/output/*", (req, res, next) ->
 	if req.query?.build? && req.query.build.match(OutputCacheManager.BUILD_REGEX)
 	if req.query?.build? && req.query.build.match(OutputCacheManager.BUILD_REGEX)
-		req.url = "/#{req.params.project_id}/" + OutputCacheManager.path(req.query.build) + "/#{req.params[0]}"
+		# for specific build get the path from the OutputCacheManager (e.g. .clsi/buildId)
+		req.url = "/#{req.params.project_id}/" + OutputCacheManager.path(req.query.build, "/#{req.params[0]}")
 	else
 	else
 		req.url = "/#{req.params.project_id}/#{req.params[0]}"
 		req.url = "/#{req.params.project_id}/#{req.params[0]}"
 	staticServer(req, res, next)
 	staticServer(req, res, next)

+ 6 - 2
services/clsi/app/coffee/OutputCacheManager.coffee

@@ -13,9 +13,13 @@ module.exports = OutputCacheManager =
 	CACHE_LIMIT: 32  # maximum of 32 cache directories
 	CACHE_LIMIT: 32  # maximum of 32 cache directories
 	CACHE_AGE: 60*60*1000 # up to one hour old
 	CACHE_AGE: 60*60*1000 # up to one hour old
 
 
-	path: (buildId) ->
+	path: (buildId, file) ->
 		# used by static server, given build id return '.cache/clsi/buildId'
 		# used by static server, given build id return '.cache/clsi/buildId'
-		return Path.join(OutputCacheManager.CACHE_SUBDIR, buildId)
+		if buildId.match OutputCacheManager.BUILD_REGEX
+			return Path.join(OutputCacheManager.CACHE_SUBDIR, buildId, file)
+		else
+			# for invalid build id, return top level
+			return file
 
 
 	saveOutputFiles: (outputFiles, compileDir, callback = (error) ->) ->
 	saveOutputFiles: (outputFiles, compileDir, callback = (error) ->) ->
 		# make a compileDir/CACHE_SUBDIR/build_id directory and
 		# make a compileDir/CACHE_SUBDIR/build_id directory and

+ 24 - 0
services/clsi/app/coffee/StaticServerForbidSymlinks.coffee

@@ -0,0 +1,24 @@
+Path = require("path")
+fs = require("fs")
+Settings = require("settings-sharelatex")
+logger = require("logger-sharelatex")
+url = require "url"
+
+module.exports = ForbidSymlinks = (staticFn, root, options) ->
+	expressStatic = staticFn root, options
+	basePath = Path.resolve(root)
+	return (req, res, next) ->
+		path = url.parse(req.url)?.pathname
+		requestedFsPath = Path.normalize("#{basePath}/#{path}")
+		fs.realpath requestedFsPath, (err, realFsPath)->
+			if err?
+				logger.warn err:err, requestedFsPath:requestedFsPath, realFsPath:realFsPath, path: req.params[0], project_id: req.params.project_id, "error checking file access"
+				if err.code == 'ENOENT'
+					return res.sendStatus(404)
+				else
+					return res.sendStatus(500)
+			else if requestedFsPath != realFsPath
+				logger.warn requestedFsPath:requestedFsPath, realFsPath:realFsPath, path: req.params[0], project_id: req.params.project_id, "trying to access a different file (symlink), aborting"
+				return res.sendStatus(404)
+			else
+				expressStatic(req, res, next)

+ 0 - 17
services/clsi/app/coffee/SymlinkCheckerMiddlewear.coffee

@@ -1,17 +0,0 @@
-Path = require("path")
-fs = require("fs")
-Settings = require("settings-sharelatex")
-logger = require("logger-sharelatex")
-
-
-module.exports = (req, res, next)->
-	basePath = Path.resolve("#{Settings.path.compilesDir}/#{req.params.project_id}")
-	requestedFsPath = Path.normalize("#{basePath}/#{req.params[0]}")
-	fs.realpath requestedFsPath, (err, realFsPath)->
-		if err?
-			return res.send(500)
-		else if requestedFsPath != realFsPath
-			logger.warn requestedFsPath:requestedFsPath, realFsPath:realFsPath, path: req.params[0], project_id: req.params.project_id, "trying to access a different file (symlink), aborting"
-			return res.send(404)
-		else
-			return next()