Skip to content

Commit db8016c

Browse files
committed
Remove forge.util.parseUrl.
- Switch URL parsing to the WHATWG URL Standard `URL` API. - Older browser or Node.js usage of related code might now require a URL polyfill.
1 parent e1a740d commit db8016c

File tree

7 files changed

+45
-74
lines changed

7 files changed

+45
-74
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,22 @@ Forge ChangeLog
99
maintainers for internal project debug purposes and was never intended to be
1010
used with untrusted user inputs. This API was not documented or advertised
1111
and is being removed rather than fixed.
12+
- **SECURITY**, **BREAKING**: Remove `forge.util.parseUrl()` (and
13+
`forge.http.parseUrl` alias) and use the [WHATWG URL
14+
Standard](https://url.spec.whatwg.org/). `URL` is supported by modern browers
15+
and modern Node.js. This change is needed to address URL parsing security
16+
issues. If `forge.util.parseUrl()` is used directly or through `forge.xhr` or
17+
`forge.http` APIs, and support is needed for environments without `URL`
18+
support, then a polyfill must be used.
1219
- **BREAKING**: Remove `forge.task` API. This API was never used, documented,
1320
or advertised by the maintainers. If anyone was using this API and wishes to
1421
continue development it in other project, please let the maintainers know.
1522
Due to use in the test suite, a modified version is located in
1623
`tests/support/`.
1724

25+
### Changed
26+
- **BREAKING**: Increase supported Node.js version to 6.13.0 for URL support.
27+
1828
### Added
1929
- OIDs for `surname`, `title`, and `givenName`.
2030

@@ -23,6 +33,12 @@ Forge ChangeLog
2333
Depending on how applications used this id to name association it could cause
2434
compatibility issues.
2535

36+
### Notes
37+
- The URL related changes may expose bugs in some of the networking related
38+
code (unrelated to the much wider used cryptography code). The automated and
39+
manual test coverage for this code is weak at best. Issues or patches to
40+
update the code or tests would be appriciated.
41+
2642
## 0.10.0 - 2020-09-01
2743

2844
### Changed

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,10 +1968,6 @@ var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary');
19681968
// make sure you specify the encoding as 'binary'
19691969
var nodeBuffer = Buffer.from('CAFE', 'hex');
19701970
var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary'));
1971-
1972-
// parse a URL
1973-
var parsed = forge.util.parseUrl('http://example.com/foo?bar=baz');
1974-
// parsed.scheme, parsed.host, parsed.port, parsed.path, parsed.fullHost
19751971
```
19761972

19771973
<a name="log" />

lib/http.js

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ var _getStorageId = function(client) {
3333
// browsers (if this is undesirable)
3434
// navigator.userAgent
3535
return 'forge.http.' +
36-
client.url.scheme + '.' +
37-
client.url.host + '.' +
36+
client.url.protocol.slice(0, -1) + '.' +
37+
client.url.hostname + '.' +
3838
client.url.port;
3939
};
4040

@@ -121,7 +121,7 @@ var _doRequest = function(client, socket) {
121121
// connect
122122
socket.options.request.connectTime = +new Date();
123123
socket.connect({
124-
host: client.url.host,
124+
host: client.url.hostname,
125125
port: client.url.port,
126126
policyPort: client.policyPort,
127127
policyUrl: client.policyUrl
@@ -310,7 +310,7 @@ var _initSocket = function(client, socket, tlsOptions) {
310310
// prime socket by connecting and caching TLS session, will do
311311
// next request from there
312312
socket.connect({
313-
host: client.url.host,
313+
host: client.url.hostname,
314314
port: client.url.port,
315315
policyPort: client.policyPort,
316316
policyUrl: client.policyUrl
@@ -405,7 +405,7 @@ var _readCookies = function(client, response) {
405405
*
406406
* @param options:
407407
* url: the url to connect to (scheme://host:port).
408-
* socketPool: the flash socket pool to use.
408+
* socketPool: the flash socket pool to use.
409409
* policyPort: the flash policy port to use (if other than the
410410
* socket pool default), use 0 for flash default.
411411
* policyUrl: the flash policy file URL to use (if provided will
@@ -441,8 +441,10 @@ http.createClient = function(options) {
441441
// get scheme, host, and port from url
442442
options.url = (options.url ||
443443
window.location.protocol + '//' + window.location.host);
444-
var url = http.parseUrl(options.url);
445-
if(!url) {
444+
var url;
445+
try {
446+
url = new URL(options.url);
447+
} catch(e) {
446448
var error = new Error('Invalid url.');
447449
error.details = {url: options.url};
448450
throw error;
@@ -469,7 +471,7 @@ http.createClient = function(options) {
469471
// idle sockets
470472
idle: [],
471473
// whether or not the connections are secure
472-
secure: (url.scheme === 'https'),
474+
secure: (url.protocol === 'https:'),
473475
// cookie jar (key'd off of name and then path, there is only 1 domain
474476
// and one setting for secure per client so name+path is unique)
475477
cookies: {},
@@ -497,7 +499,7 @@ http.createClient = function(options) {
497499
if(depth === 0 && verified === true) {
498500
// compare common name to url host
499501
var cn = certs[depth].subject.getField('CN');
500-
if(cn === null || client.url.host !== cn.value) {
502+
if(cn === null || client.url.hostname !== cn.value) {
501503
verified = {
502504
message: 'Certificate common name does not match url host.'
503505
};
@@ -512,7 +514,7 @@ http.createClient = function(options) {
512514
tlsOptions = {
513515
caStore: caStore,
514516
cipherSuites: options.cipherSuites || null,
515-
virtualHost: options.virtualHost || url.host,
517+
virtualHost: options.virtualHost || url.hostname,
516518
verify: options.verify || _defaultCertificateVerify,
517519
getCertificate: options.getCertificate || null,
518520
getPrivateKey: options.getPrivateKey || null,
@@ -552,7 +554,7 @@ http.createClient = function(options) {
552554
client.send = function(options) {
553555
// add host header if not set
554556
if(options.request.getField('Host') === null) {
555-
options.request.setField('Host', client.url.fullHost);
557+
options.request.setField('Host', client.url.origin);
556558
}
557559

558560
// set default dummy handlers
@@ -1307,15 +1309,6 @@ http.createResponse = function() {
13071309
return response;
13081310
};
13091311

1310-
/**
1311-
* Parses the scheme, host, and port from an http(s) url.
1312-
*
1313-
* @param str the url string.
1314-
*
1315-
* @return the parsed url object or null if the url is invalid.
1316-
*/
1317-
http.parseUrl = forge.util.parseUrl;
1318-
13191312
/**
13201313
* Returns true if the given url is within the given cookie's domain.
13211314
*
@@ -1336,11 +1329,11 @@ http.withinCookieDomain = function(url, cookie) {
13361329
// ensure domain starts with a '.'
13371330
// parse URL as necessary
13381331
if(typeof url === 'string') {
1339-
url = http.parseUrl(url);
1332+
url = new URL(url);
13401333
}
13411334

1342-
// add '.' to front of URL host to match against domain
1343-
var host = '.' + url.host;
1335+
// add '.' to front of URL hostname to match against domain
1336+
var host = '.' + url.hostname;
13441337

13451338
// if the host ends with domain then it falls within it
13461339
var idx = host.lastIndexOf(domain);

lib/util.js

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,43 +2258,6 @@ util.clearItems = function(api, id, location) {
22582258
_callStorageFunction(_clearItems, arguments, location);
22592259
};
22602260

2261-
/**
2262-
* Parses the scheme, host, and port from an http(s) url.
2263-
*
2264-
* @param str the url string.
2265-
*
2266-
* @return the parsed url object or null if the url is invalid.
2267-
*/
2268-
util.parseUrl = function(str) {
2269-
// FIXME: this regex looks a bit broken
2270-
var regex = /^(https?):\/\/([^:&^\/]*):?(\d*)(.*)$/g;
2271-
regex.lastIndex = 0;
2272-
var m = regex.exec(str);
2273-
var url = (m === null) ? null : {
2274-
full: str,
2275-
scheme: m[1],
2276-
host: m[2],
2277-
port: m[3],
2278-
path: m[4]
2279-
};
2280-
if(url) {
2281-
url.fullHost = url.host;
2282-
if(url.port) {
2283-
if(url.port !== 80 && url.scheme === 'http') {
2284-
url.fullHost += ':' + url.port;
2285-
} else if(url.port !== 443 && url.scheme === 'https') {
2286-
url.fullHost += ':' + url.port;
2287-
}
2288-
} else if(url.scheme === 'http') {
2289-
url.port = 80;
2290-
} else if(url.scheme === 'https') {
2291-
url.port = 443;
2292-
}
2293-
url.full = url.scheme + '://' + url.fullHost;
2294-
}
2295-
return url;
2296-
};
2297-
22982261
/* Storage for query variables */
22992262
var _queryVariables = null;
23002263

lib/xhr.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ xhrApi.init = function(options) {
151151
getPrivateKey: options.getPrivateKey,
152152
getSignature: options.getSignature
153153
});
154-
_clients[_client.url.full] = _client;
154+
_clients[_client.url.origin] = _client;
155155

156156
forge.log.debug(cat, 'ready');
157157
};
@@ -380,18 +380,20 @@ xhrApi.create = function(options) {
380380
// use default
381381
_state.client = _client;
382382
} else {
383-
var url = http.parseUrl(options.url);
384-
if(!url) {
383+
var url;
384+
try {
385+
url = new URL(options.url);
386+
} catch(e) {
385387
var error = new Error('Invalid url.');
386388
error.details = {
387389
url: options.url
388390
};
389391
}
390392

391393
// find client
392-
if(url.full in _clients) {
394+
if(url.origin in _clients) {
393395
// client found
394-
_state.client = _clients[url.full];
396+
_state.client = _clients[url.origin];
395397
} else {
396398
// create client
397399
_state.client = http.createClient({
@@ -409,7 +411,7 @@ xhrApi.create = function(options) {
409411
getPrivateKey: options.getPrivateKey,
410412
getSignature: options.getSignature
411413
});
412-
_clients[url.full] = _state.client;
414+
_clients[url.origin] = _state.client;
413415
}
414416
}
415417

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"dist/*.min.js.map"
6161
],
6262
"engines": {
63-
"node": ">= 6.0.0"
63+
"node": ">= 6.13.0"
6464
},
6565
"keywords": [
6666
"aes",

tests/websockets/server-webid.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,10 @@ var fetchUrl = function(url, callback, redirects) {
174174
console.log('Fetching URL: \"' + url + '\"');
175175

176176
// parse URL
177-
url = forge.util.parseUrl(url);
178-
var client = http.createClient(
179-
url.port, url.fullHost, url.scheme === 'https');
177+
url = new URL(url);
178+
var client = http.createClient({
179+
url: url
180+
});
180181
var request = client.request('GET', url.path, {
181182
Host: url.host,
182183
Accept: 'application/rdf+xml'

0 commit comments

Comments
 (0)