Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 33f769b

Browse files
sp00mgkalpak
authored andcommitted
fix($$cookieReader): correctly handle forbidden access to document.cookie
In certain cases (e.g. on LG webOS using the `file:` protocol), access to `document.cookie` may not be allowed and throw an error. This could break `$http` which relies on `$$cookieReader()` for retrieving the XSRF token. This commit fixes it by treating `document.cookie` as empty, when access to it is fordibben. Fixes #15523 Closes #15532
1 parent c8abf20 commit 33f769b

File tree

2 files changed

+108
-68
lines changed

2 files changed

+108
-68
lines changed

Diff for: src/ng/cookieReader.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ function $$CookieReader($document) {
1414
var lastCookies = {};
1515
var lastCookieString = '';
1616

17+
function safeGetCookie(rawDocument) {
18+
try {
19+
return rawDocument.cookie || '';
20+
} catch (e) {
21+
return '';
22+
}
23+
}
24+
1725
function safeDecodeURIComponent(str) {
1826
try {
1927
return decodeURIComponent(str);
@@ -24,7 +32,7 @@ function $$CookieReader($document) {
2432

2533
return function() {
2634
var cookieArray, cookie, i, index, name;
27-
var currentCookieString = rawDocument.cookie || '';
35+
var currentCookieString = safeGetCookie(rawDocument);
2836

2937
if (currentCookieString !== lastCookieString) {
3038
lastCookieString = currentCookieString;

Diff for: test/ng/cookieReaderSpec.js

+99-67
Original file line numberDiff line numberDiff line change
@@ -3,103 +3,135 @@
33
describe('$$cookieReader', function() {
44
var $$cookieReader, document;
55

6-
function deleteAllCookies() {
7-
var cookies = document.cookie.split(';');
8-
var path = window.location.pathname;
9-
10-
for (var i = 0; i < cookies.length; i++) {
11-
var cookie = cookies[i];
12-
var eqPos = cookie.indexOf('=');
13-
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
14-
var parts = path.split('/');
15-
while (parts.length) {
16-
document.cookie = name + '=;path=' + (parts.join('/') || '/') + ';expires=Thu, 01 Jan 1970 00:00:00 GMT';
17-
parts.pop();
6+
7+
describe('with access to `document.cookie`', function() {
8+
9+
function deleteAllCookies() {
10+
var cookies = document.cookie.split(';');
11+
var path = window.location.pathname;
12+
13+
for (var i = 0; i < cookies.length; i++) {
14+
var cookie = cookies[i];
15+
var eqPos = cookie.indexOf('=');
16+
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
17+
var parts = path.split('/');
18+
while (parts.length) {
19+
document.cookie = name + '=;path=' + (parts.join('/') || '/') + ';expires=Thu, 01 Jan 1970 00:00:00 GMT';
20+
parts.pop();
21+
}
1822
}
1923
}
20-
}
2124

22-
beforeEach(function() {
23-
document = window.document;
24-
deleteAllCookies();
25-
expect(document.cookie).toEqual('');
25+
beforeEach(function() {
26+
document = window.document;
27+
deleteAllCookies();
28+
expect(document.cookie).toEqual('');
2629

27-
inject(function(_$$cookieReader_) {
28-
$$cookieReader = _$$cookieReader_;
30+
inject(function(_$$cookieReader_) {
31+
$$cookieReader = _$$cookieReader_;
32+
});
2933
});
30-
});
3134

35+
afterEach(function() {
36+
deleteAllCookies();
37+
expect(document.cookie).toEqual('');
38+
});
3239

33-
afterEach(function() {
34-
deleteAllCookies();
35-
expect(document.cookie).toEqual('');
36-
});
3740

41+
describe('get via $$cookieReader()[cookieName]', function() {
3842

39-
describe('get via $$cookieReader()[cookieName]', function() {
43+
it('should return undefined for nonexistent cookie', function() {
44+
expect($$cookieReader().nonexistent).not.toBeDefined();
45+
});
4046

41-
it('should return undefined for nonexistent cookie', function() {
42-
expect($$cookieReader().nonexistent).not.toBeDefined();
43-
});
4447

48+
it('should return a value for an existing cookie', function() {
49+
document.cookie = 'foo=bar=baz;path=/';
50+
expect($$cookieReader().foo).toEqual('bar=baz');
51+
});
4552

46-
it('should return a value for an existing cookie', function() {
47-
document.cookie = 'foo=bar=baz;path=/';
48-
expect($$cookieReader().foo).toEqual('bar=baz');
49-
});
5053

51-
it('should return the the first value provided for a cookie', function() {
52-
// For a cookie that has different values that differ by path, the
53-
// value for the most specific path appears first. $$cookieReader()
54-
// should provide that value for the cookie.
55-
document.cookie = 'foo="first"; foo="second"';
56-
expect($$cookieReader()['foo']).toBe('"first"');
57-
});
54+
it('should return the the first value provided for a cookie', function() {
55+
// For a cookie that has different values that differ by path, the
56+
// value for the most specific path appears first. $$cookieReader()
57+
// should provide that value for the cookie.
58+
document.cookie = 'foo="first"; foo="second"';
59+
expect($$cookieReader()['foo']).toBe('"first"');
60+
});
61+
62+
63+
it('should decode cookie values that were encoded by puts', function() {
64+
document.cookie = 'cookie2%3Dbar%3Bbaz=val%3Due;path=/';
65+
expect($$cookieReader()['cookie2=bar;baz']).toEqual('val=ue');
66+
});
67+
68+
69+
it('should preserve leading & trailing spaces in names and values', function() {
70+
document.cookie = '%20cookie%20name%20=%20cookie%20value%20';
71+
expect($$cookieReader()[' cookie name ']).toEqual(' cookie value ');
72+
expect($$cookieReader()['cookie name']).not.toBeDefined();
73+
});
74+
75+
76+
it('should decode special characters in cookie values', function() {
77+
document.cookie = 'cookie_name=cookie_value_%E2%82%AC';
78+
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_€');
79+
});
5880

59-
it('should decode cookie values that were encoded by puts', function() {
60-
document.cookie = 'cookie2%3Dbar%3Bbaz=val%3Due;path=/';
61-
expect($$cookieReader()['cookie2=bar;baz']).toEqual('val=ue');
62-
});
6381

82+
it('should not decode cookie values that do not appear to be encoded', function() {
83+
// see #9211 - sometimes cookies contain a value that causes decodeURIComponent to throw
84+
document.cookie = 'cookie_name=cookie_value_%XX';
85+
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_%XX');
86+
});
6487

65-
it('should preserve leading & trailing spaces in names and values', function() {
66-
document.cookie = '%20cookie%20name%20=%20cookie%20value%20';
67-
expect($$cookieReader()[' cookie name ']).toEqual(' cookie value ');
68-
expect($$cookieReader()['cookie name']).not.toBeDefined();
6988
});
7089

71-
it('should decode special characters in cookie values', function() {
72-
document.cookie = 'cookie_name=cookie_value_%E2%82%AC';
73-
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_€');
90+
91+
describe('getAll via $$cookieReader()', function() {
92+
93+
it('should return cookies as hash', function() {
94+
document.cookie = 'foo1=bar1;path=/';
95+
document.cookie = 'foo2=bar2;path=/';
96+
expect($$cookieReader()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
97+
});
98+
99+
100+
it('should return empty hash if no cookies exist', function() {
101+
expect($$cookieReader()).toEqual({});
102+
});
103+
74104
});
75105

76-
it('should not decode cookie values that do not appear to be encoded', function() {
77-
// see #9211 - sometimes cookies contain a value that causes decodeURIComponent to throw
78-
document.cookie = 'cookie_name=cookie_value_%XX';
79-
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_%XX');
106+
107+
it('should initialize cookie cache with existing cookies', function() {
108+
document.cookie = 'existingCookie=existingValue;path=/';
109+
expect($$cookieReader()).toEqual({'existingCookie':'existingValue'});
80110
});
111+
81112
});
82113

83114

84-
describe('getAll via $$cookieReader()', function() {
115+
describe('without access to `document.cookie`', function() {
116+
var cookieSpy;
85117

86-
it('should return cookies as hash', function() {
87-
document.cookie = 'foo1=bar1;path=/';
88-
document.cookie = 'foo2=bar2;path=/';
89-
expect($$cookieReader()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
90-
});
118+
beforeEach(module(function($provide) {
119+
cookieSpy = jasmine.createSpy('cookie').and.throwError('Can\'t touch this!');
120+
document = Object.create({}, {'cookie': {get: cookieSpy}});
121+
122+
$provide.value('$document', [document]);
123+
}));
124+
125+
beforeEach(inject(function(_$$cookieReader_) {
126+
$$cookieReader = _$$cookieReader_;
127+
}));
91128

92129

93-
it('should return empty hash if no cookies exist', function() {
130+
it('should return an empty object', function() {
94131
expect($$cookieReader()).toEqual({});
132+
expect(cookieSpy).toHaveBeenCalled();
95133
});
96-
});
97-
98134

99-
it('should initialize cookie cache with existing cookies', function() {
100-
document.cookie = 'existingCookie=existingValue;path=/';
101-
expect($$cookieReader()).toEqual({'existingCookie':'existingValue'});
102135
});
103136

104137
});
105-

0 commit comments

Comments
 (0)