From 98aea639b235a23f97b435d4803b25bb90cd443e Mon Sep 17 00:00:00 2001 From: Shayne Sweeney Date: Mon, 21 Mar 2016 21:57:56 -0700 Subject: [PATCH] Fix asset path-traversal outside of roots Summary:`/assets/...` requests previously supported path-traversal potentially exposing and serving (private) files outside roots. **Test plan** Prior to patching perform the a path-traversal request to the server: ``` GET /assets/../../../../etc/hosts HTTP/1.1 Cache-Control: no-store Host: 127.0.0.1:8081 Connection: close Accept-Encoding: gzip User-Agent: okhttp/2.5.0 ``` Apply patch and verify a `404` response with body: `Asset not found` Test normal asset requests work. Closes https://github.com/facebook/react-native/pull/6398 Differential Revision: D3034857 Pulled By: shayne fb-gh-sync-id: f0e6714e4e3c5a63a3a402634a1eb5f3186d3561 shipit-source-id: f0e6714e4e3c5a63a3a402634a1eb5f3186d3561 --- .../react-packager/src/AssetServer/index.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packager/react-packager/src/AssetServer/index.js b/packager/react-packager/src/AssetServer/index.js index 71356acf38f6ab..57d1aea2c1f188 100644 --- a/packager/react-packager/src/AssetServer/index.js +++ b/packager/react-packager/src/AssetServer/index.js @@ -129,16 +129,23 @@ class AssetServer { _findRoot(roots, dir) { return Promise.all( roots.map(root => { - const absPath = path.join(root, dir); + // important: we want to resolve root + dir + // to ensure the requested path doesn't traverse beyond root + const absPath = path.resolve(root, dir); return stat(absPath).then(fstat => { - return {path: absPath, isDirectory: fstat.isDirectory()}; - }, err => { - return {path: absPath, isDirectory: false}; + // keep asset requests from traversing files + // up from the root (e.g. ../../../etc/hosts) + if (!absPath.startsWith(root)) { + return {path: absPath, isValid: false}; + } + return {path: absPath, isValid: fstat.isDirectory()}; + }, _ => { + return {path: absPath, isValid: false}; }); }) ).then(stats => { for (let i = 0; i < stats.length; i++) { - if (stats[i].isDirectory) { + if (stats[i].isValid) { return stats[i].path; } }