Skip to content

Commit 579960c

Browse files
committedOct 13, 2017
improved traversal detection & location redirect
1 parent ae7b676 commit 579960c

File tree

3 files changed

+31
-17
lines changed

3 files changed

+31
-17
lines changed
 

‎st.js

+19-16
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ Mount.prototype.getCacheOptions = function (opt) {
164164
return c
165165
}
166166

167-
// get a path from a url
168-
Mount.prototype.getPath = function (u) {
167+
// get the path component from a URI
168+
Mount.prototype.getUriPath = function (u) {
169169
var p = url.parse(u).pathname
170170

171171
// Encoded dots are dots
@@ -179,8 +179,7 @@ Mount.prototype.getPath = function (u) {
179179

180180
// Make sure it starts with a slash
181181
p = p.replace(/^\//, '/')
182-
183-
if (p.match(/[\/\\]\.\.[\/\\]/)) {
182+
if ((/[\/\\]\.\.([\/\\]|$)/).test(p)) {
184183
// traversal urls not ever even slightly allowed. clearly shenanigans
185184
// send a 403 on that noise, do not pass go, do not collect $200
186185
return 403
@@ -202,8 +201,12 @@ Mount.prototype.getPath = function (u) {
202201
u = u.substr(this.url.length)
203202
if (u.charAt(0) !== '/') u = '/' + u
204203

205-
p = path.join(this.path, u)
206-
return p
204+
return u
205+
}
206+
207+
// get a path from a url
208+
Mount.prototype.getPath = function (u) {
209+
return path.join(this.path, u)
207210
}
208211

209212
// get a url from a path
@@ -223,25 +226,25 @@ Mount.prototype.serve = function (req, res, next) {
223226

224227
// querystrings are of no concern to us
225228
if (!req.sturl)
226-
req.sturl = url.parse(req.url).pathname
229+
req.sturl = this.getUriPath(req.url)
227230

228-
var p = this.getPath(req.sturl)
231+
// don't allow dot-urls by default, unless explicitly allowed.
232+
// If we got a 403, then it's explicitly forbidden.
233+
if (req.sturl === 403 || (!this.opt.dot && (/(^|\/)\./).test(req.sturl))) {
234+
res.statusCode = 403
235+
res.end('Forbidden')
236+
return true
237+
}
229238

230239
// Falsey here means we got some kind of invalid path.
231240
// Probably urlencoding we couldn't understand, or some
232241
// other "not compatible with st, but maybe ok" thing.
233-
if (!p) {
242+
if (typeof req.sturl !== 'string' || req.sturl == '') {
234243
if (typeof next === 'function') next()
235244
return false
236245
}
237246

238-
// don't allow dot-urls by default, unless explicitly allowed.
239-
// If we got a 403, then it's explicitly forbidden.
240-
if (p === 403 || !this.opt.dot && req.sturl.match(/(^|\/)\./)) {
241-
res.statusCode = 403
242-
res.end('Forbidden')
243-
return true
244-
}
247+
var p = this.getPath(req.sturl)
245248

246249
// now we have a path. check for the fd.
247250
this.cache.fd.get(p, function (er, fd) {

‎test/basic.js

+10
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,13 @@ test('shenanigans', function(t) {
123123
t.end()
124124
})
125125
})
126+
127+
128+
test('shenanigans2', function(t) {
129+
req('/test//foo/%2e%2E', function(er, res) {
130+
if (er)
131+
throw er
132+
t.equal(res.statusCode, 403)
133+
t.end()
134+
})
135+
})

‎test/common.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ function req (url, headers, cb) {
2626
if (typeof headers === 'function') cb = headers, headers = {}
2727
request({ encoding: null,
2828
url: 'http://localhost:' + port + url,
29-
headers: headers }, cb)
29+
headers: headers,
30+
followRedirect: false }, cb)
3031
}
3132

3233

0 commit comments

Comments
 (0)
Please sign in to comment.