Skip to content

Commit 84d809d

Browse files
committed
fix: add path containment check in View.prototype.lookup()
View.prototype.lookup() used path.resolve(root, name) without verifying the resolved path stayed within the configured views directory. This inconsistency with res.sendFile() (which uses the send library root containment check) could allow path traversal when user input is passed to res.render() unsanitized. Added a containment check that skips any resolved path not starting with resolve(root) + sep. Absolute paths are intentionally exempted since Express supports passing absolute paths directly to res.render(). Fixes #7140
1 parent e509919 commit 84d809d

1 file changed

Lines changed: 10 additions & 1 deletion

File tree

lib/view.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ var basename = path.basename;
2727
var extname = path.extname;
2828
var join = path.join;
2929
var resolve = path.resolve;
30+
var sep = path.sep;
31+
var isAbsolute = path.isAbsolute;
3032

3133
/**
3234
* Module exports.
@@ -112,6 +114,13 @@ View.prototype.lookup = function lookup(name) {
112114

113115
// resolve the path
114116
var loc = resolve(root, name);
117+
118+
// containment check: only for relative paths — absolute paths are intentional
119+
if (!isAbsolute(name) && loc.indexOf(resolve(root) + sep) !== 0) {
120+
debug('path traversal attempt blocked: "%s"', loc);
121+
continue;
122+
}
123+
115124
var dir = dirname(loc);
116125
var file = basename(loc);
117126

@@ -202,4 +211,4 @@ function tryStat(path) {
202211
} catch (e) {
203212
return undefined;
204213
}
205-
}
214+
}

0 commit comments

Comments
 (0)