diff --git a/plugins/c9.preview/lib/middleware/sanitize-path-param.js b/plugins/c9.preview/lib/middleware/sanitize-path-param.js new file mode 100644 index 00000000..f3cf8782 --- /dev/null +++ b/plugins/c9.preview/lib/middleware/sanitize-path-param.js @@ -0,0 +1,15 @@ +"use strict"; + +var Path = require("path"); + +module.exports = function sanitzePreviewPath(req, res, next) { + var normalized = Path.normalize(decodeURIComponent(req.params.path)); + + // N.B. Path.normalize does not strip away when the path starts with "../" + if (normalized) + normalized = normalized.replace(/[.]{2}\//g, "") || "/"; + + req.params.path = normalized; + + next(); +}; diff --git a/plugins/c9.preview/lib/middleware/sanitize-path-param_test.js b/plugins/c9.preview/lib/middleware/sanitize-path-param_test.js new file mode 100644 index 00000000..6120a020 --- /dev/null +++ b/plugins/c9.preview/lib/middleware/sanitize-path-param_test.js @@ -0,0 +1,60 @@ +"use struct"; +"use server"; + +require("c9/inline-mocha")(module); + +var sanitize = require("./sanitize-path-param"); +var async = require("async"); +var format = require("util").format; +var assert = require("assert"); + +describe(__filename, function() { + it("should sanitize params", function(done) { + + //https://preview.new-mvhenten.c9.io/mvhenten/demo-project/%2e%2e/foo.txt + + + var cases = [{ + path: "%2e%2e/foo.txt", + expect: "foo.txt" + }, { + path: "%2e%2e/%2e%2e/foo.txt", + expect: "foo.txt" + }, { + path: "%2e%2e/%2e%2e/%2e%2e/foo.txt", + expect: "foo.txt" + }, { + path: "foo/bar/%2e%2e/%2e%2e/xoo.txt", + expect: "xoo.txt" + }, { + path: "../foo.txt", + expect: "foo.txt" + }, { + path: "foo/../../foo.txt", + expect: "foo.txt" + }, { + path: "%7E/foo/../../foo.txt", + expect: "foo.txt" + }, { + path: "~/foo.txt", + expect: "~/foo.txt" + }, { + path: "%7E/../foo.txt", + expect: "foo.txt" + }]; + + + async.each(cases, function(testCase, next) { + var mockReq = { + params: { + path: testCase.path + } + }; + + sanitize(mockReq, null, function() { + assert.equal(mockReq.params.path, testCase.expect, format("Expect %s to become %s", testCase.path, testCase.expect)); + next(); + }); + }, done); + }); +}); diff --git a/plugins/c9.preview/preview.js b/plugins/c9.preview/preview.js index d084ec21..0f2c1a98 100644 --- a/plugins/c9.preview/preview.js +++ b/plugins/c9.preview/preview.js @@ -21,7 +21,6 @@ define(function(require, exports, module) { var userContent = imports["user-content.redirect"]; var getVfsServers = imports["vfs.serverlist"].getServers; - var Path = require("path"); var frontdoor = require("frontdoor"); var error = require("http-error"); @@ -52,10 +51,7 @@ define(function(require, exports, module) { } }, [ requestTimeout(15*60*1000), - function sanitzePreviewPath(req,res,next){ - req.params.path = Path.normalize(decodeURIComponent(req.params.path)); - next(); - }, + require("./lib/middleware/sanitize-path-param"), handler.getProjectSession(), handler.getRole(db), handler.getProxyUrl(function() {