Skip to content

Commit 7124387

Browse files
committed
http: don't escape request path, reject bad chars
Commit 38149bb changes http.get() and http.request() to escape unsafe characters. However, that creates an incompatibility with v0.10 that is difficult to work around: if you escape the path manually, then in v0.11 it gets escaped twice. Change lib/http.js so it no longer tries to fix up bad request paths, simply reject them with an exception. The actual check is rather basic right now. The full check for illegal characters is difficult to implement efficiently because it requires a few characters of lookahead. That's why it currently only checks for spaces because those are guaranteed to create an invalid request. Fixes #5474.
1 parent b3d1e50 commit 7124387

File tree

3 files changed

+15
-56
lines changed

3 files changed

+15
-56
lines changed

doc/api/http.markdown

+3-1
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,9 @@ Options:
413413
- `socketPath`: Unix Domain Socket (use one of host:port or socketPath)
414414
- `method`: A string specifying the HTTP request method. Defaults to `'GET'`.
415415
- `path`: Request path. Defaults to `'/'`. Should include query string if any.
416-
E.G. `'/index.html?page=12'`
416+
E.G. `'/index.html?page=12'`. An exception is thrown when the request path
417+
contains illegal characters. Currently, only spaces are rejected but that
418+
may change in the future.
417419
- `headers`: An object containing request headers.
418420
- `auth`: Basic authentication i.e. `'user:password'` to compute an
419421
Authorization header.

lib/http.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,14 @@ var ClientRequest = exports.ClientRequest = client.ClientRequest;
5252
exports.request = function(options, cb) {
5353
if (typeof options === 'string') {
5454
options = url.parse(options);
55-
} else if (options && options.path) {
56-
options = util._extend({}, options);
57-
options.path = encodeURI(options.path);
58-
// encodeURI() doesn't escape quotes while url.parse() does. Fix up.
59-
options.path = options.path.replace(/'/g, '%27');
55+
} else if (options && options.path && / /.test(options.path)) {
56+
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
57+
// with an additional rule for ignoring percentage-escaped characters
58+
// but that's a) hard to capture in a regular expression that performs
59+
// well, and b) possibly too restrictive for real-world usage. That's
60+
// why it only scans for spaces because those are guaranteed to create
61+
// an invalid request.
62+
throw new TypeError('Request path contains unescaped characters.');
6063
}
6164

6265
if (options.protocol && options.protocol !== 'http:') {

test/simple/test-http-client-escape-path.js test/simple/test-http-client-unescaped-path.js

+4-50
Original file line numberDiff line numberDiff line change
@@ -22,54 +22,8 @@
2222
var common = require('../common');
2323
var assert = require('assert');
2424
var http = require('http');
25-
var util = require('util');
2625

27-
first();
28-
29-
function first() {
30-
test('/~username/', '/~username/', second);
31-
}
32-
function second() {
33-
test('/\'foo bar\'', '/%27foo%20bar%27', third);
34-
}
35-
function third() {
36-
var expected = '/%3C%3E%22%60%20%0D%0A%09%7B%7D%7C%5C%5E~%60%27';
37-
test('/<>"` \r\n\t{}|\\^~`\'', expected);
38-
}
39-
40-
function test(path, expected, next) {
41-
function helper(arg, next) {
42-
var server = http.createServer(function(req, res) {
43-
assert.equal(req.url, expected);
44-
res.end('OK');
45-
server.close(next);
46-
});
47-
server.on('clientError', function(err) {
48-
throw err;
49-
});
50-
server.listen(common.PORT, '127.0.0.1', function() {
51-
http.get(arg);
52-
});
53-
}
54-
55-
// Go the extra mile to ensure that the behavior of
56-
// http.get("http://example.com/...") matches http.get({ path: ... }).
57-
test1();
58-
59-
function test1() {
60-
console.log('as url: ' + util.inspect(path));
61-
helper('http://127.0.0.1:' + common.PORT + path, test2);
62-
}
63-
function test2() {
64-
var options = {
65-
host: '127.0.0.1',
66-
port: common.PORT,
67-
path: path
68-
};
69-
console.log('as options: ' + util.inspect(options));
70-
helper(options, done);
71-
}
72-
function done() {
73-
if (next) next();
74-
}
75-
}
26+
assert.throws(function() {
27+
// Path with spaces in it should throw.
28+
http.get({ path: 'bad path' }, assert.fail);
29+
}, /contains unescaped characters/);

0 commit comments

Comments
 (0)