From 0cbc50512126fa22546dbe9b79a14939d9dc4459 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 2 Nov 2017 12:58:49 +0000 Subject: [PATCH] fix($location): do not decode forward slashes in the path in HTML5 mode Closes #16312 --- src/ng/location.js | 30 +++++++++++++++++++++++------- test/ng/locationSpec.js | 11 +++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/ng/location.js b/src/ng/location.js index bf1858079c3f..184428883090 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -16,7 +16,23 @@ function encodePath(path) { i = segments.length; while (i--) { - segments[i] = encodeUriSegment(segments[i]); + // decode forward slashes to prevent them from being double encoded + segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, '/')); + } + + return segments.join('/'); +} + +function decodePath(path, html5Mode) { + var segments = path.split('/'), + i = segments.length; + + while (i--) { + segments[i] = decodeURIComponent(segments[i]); + if (html5Mode) { + // encode forward slashes to prevent them from being mistaken for path separators + segments[i] = segments[i].replace(/\//g, '%2F'); + } } return segments.join('/'); @@ -31,7 +47,7 @@ function parseAbsoluteUrl(absoluteUrl, locationObj) { } var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/; -function parseAppUrl(url, locationObj) { +function parseAppUrl(url, locationObj, html5Mode) { if (DOUBLE_SLASH_REGEX.test(url)) { throw $locationMinErr('badpath', 'Invalid url "{0}".', url); @@ -42,8 +58,8 @@ function parseAppUrl(url, locationObj) { url = '/' + url; } var match = urlResolve(url); - locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? - match.pathname.substring(1) : match.pathname); + var path = prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname; + locationObj.$$path = decodePath(path, html5Mode); locationObj.$$search = parseKeyValue(match.search); locationObj.$$hash = decodeURIComponent(match.hash); @@ -118,7 +134,7 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { appBaseNoFile); } - parseAppUrl(pathUrl, this); + parseAppUrl(pathUrl, this, true); if (!this.$$path) { this.$$path = '/'; @@ -221,7 +237,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { } } - parseAppUrl(withoutHashUrl, this); + parseAppUrl(withoutHashUrl, this, false); this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); @@ -406,7 +422,7 @@ var locationPrototype = { } var match = PATH_MATCH.exec(url); - if (match[1] || url === '') this.path(decodeURIComponent(match[1])); + if (match[1] || url === '') this.path(decodeURI(match[1])); if (match[2] || match[1] || url === '') this.search(match[3] || ''); this.hash(match[5] || ''); diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index 723e12740a30..dd48edbe4e52 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -477,6 +477,17 @@ describe('$location', function() { expect(locationUrl.hash()).toBe('x <>#'); }); + + it('should not decode encoded forward slashes in the path', function() { + var locationUrl = new LocationHtml5Url('http://host.com/base/', 'http://host.com/base/'); + locationUrl.$$parse('http://host.com/base/a/ng2;path=%2Fsome%2Fpath'); + expect(locationUrl.path()).toBe('/a/ng2;path=%2Fsome%2Fpath'); + expect(locationUrl.search()).toEqual({}); + expect(locationUrl.hash()).toBe(''); + expect(locationUrl.url()).toBe('/a/ng2;path=%2Fsome%2Fpath'); + expect(locationUrl.absUrl()).toBe('http://host.com/base/a/ng2;path=%2Fsome%2Fpath'); + }); + it('should decode pluses as spaces in urls', function() { var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/'); locationUrl.$$parse('http://host.com/?a+b=c+d');