From e024fec82d3125ce758bdc13382a10fdaff38678 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 27 Feb 2015 13:57:57 +0000 Subject: [PATCH] provide a static server which forbids symlinks prevents mismatch between rootdir of server and rootdir of symlink checking middleware --- services/clsi/app.coffee | 27 ++++++------------- .../clsi/app/coffee/OutputCacheManager.coffee | 8 ++++-- .../coffee/StaticServerForbidSymlinks.coffee | 24 +++++++++++++++++ .../coffee/SymlinkCheckerMiddlewear.coffee | 17 ------------ 4 files changed, 38 insertions(+), 38 deletions(-) create mode 100644 services/clsi/app/coffee/StaticServerForbidSymlinks.coffee delete mode 100644 services/clsi/app/coffee/SymlinkCheckerMiddlewear.coffee diff --git a/services/clsi/app.coffee b/services/clsi/app.coffee index ee2e8387ec..9ac1d1dc18 100644 --- a/services/clsi/app.coffee +++ b/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/pdf", CompileController.syncFromPdf -url = require "url" +ForbidSymlinks = require "./app/js/StaticServerForbidSymlinks" -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) -> +# 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" res.set("Content-Type", "application/pdf") # 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. 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) - 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 req.url = "/#{req.params.project_id}/#{req.params[0]}" staticServer(req, res, next) diff --git a/services/clsi/app/coffee/OutputCacheManager.coffee b/services/clsi/app/coffee/OutputCacheManager.coffee index f77227a185..50c5837002 100644 --- a/services/clsi/app/coffee/OutputCacheManager.coffee +++ b/services/clsi/app/coffee/OutputCacheManager.coffee @@ -13,9 +13,13 @@ module.exports = OutputCacheManager = CACHE_LIMIT: 32 # maximum of 32 cache directories 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' - 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) ->) -> # make a compileDir/CACHE_SUBDIR/build_id directory and diff --git a/services/clsi/app/coffee/StaticServerForbidSymlinks.coffee b/services/clsi/app/coffee/StaticServerForbidSymlinks.coffee new file mode 100644 index 0000000000..2f48190835 --- /dev/null +++ b/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) diff --git a/services/clsi/app/coffee/SymlinkCheckerMiddlewear.coffee b/services/clsi/app/coffee/SymlinkCheckerMiddlewear.coffee deleted file mode 100644 index b7baf1fa77..0000000000 --- a/services/clsi/app/coffee/SymlinkCheckerMiddlewear.coffee +++ /dev/null @@ -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()