From 84d809d4de1583b13da5016e85a27dbc6e453102 Mon Sep 17 00:00:00 2001 From: som14062005 Date: Tue, 31 Mar 2026 14:13:38 +0530 Subject: [PATCH] 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 --- lib/view.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/view.js b/lib/view.js index d66b4a2d89c..3540451114b 100644 --- a/lib/view.js +++ b/lib/view.js @@ -27,6 +27,8 @@ var basename = path.basename; var extname = path.extname; var join = path.join; var resolve = path.resolve; +var sep = path.sep; +var isAbsolute = path.isAbsolute; /** * Module exports. @@ -112,6 +114,13 @@ View.prototype.lookup = function lookup(name) { // resolve the path var loc = resolve(root, name); + + // containment check: only for relative paths — absolute paths are intentional + if (!isAbsolute(name) && loc.indexOf(resolve(root) + sep) !== 0) { + debug('path traversal attempt blocked: "%s"', loc); + continue; + } + var dir = dirname(loc); var file = basename(loc); @@ -202,4 +211,4 @@ function tryStat(path) { } catch (e) { return undefined; } -} +} \ No newline at end of file