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

Commit a99e97e

Browse files
committed
fix($http): only parse as JSON when opening/closing brackets match
Previously, due to weak JSON-detecting RegExp, string like `[...}` and `{...]` would be considered JSON (even if they obviously aren't) and an expection would be thrown while trying to parse them. This commit makes sure the opening and closing brackets match. This doesn't completely eliminate false positives (e.g. `[]{}[]`), but does help reduce them. Closes #10349
1 parent 6617b42 commit a99e97e

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

src/ng/http.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,35 @@
22

33
var APPLICATION_JSON = 'application/json';
44
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
5-
var JSON_START = /^\s*(\[|\{[^\{])/;
6-
var JSON_END = /[\}\]]\s*$/;
5+
var JSON_START = /^\[|^\{(?!\{)/;
6+
var JSON_ENDS = {
7+
'[': /]$/,
8+
'{': /}$/
9+
};
710
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
811

912
function defaultHttpResponseTransform(data, headers) {
1013
if (isString(data)) {
11-
// strip json vulnerability protection prefix
12-
data = data.replace(JSON_PROTECTION_PREFIX, '');
13-
var contentType = headers('Content-Type');
14-
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
15-
(JSON_START.test(data) && JSON_END.test(data))) {
16-
data = fromJson(data);
14+
// Strip json vulnerability protection prefix and trim whitespace
15+
var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
16+
17+
if (tempData) {
18+
var contentType = headers('Content-Type');
19+
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
20+
// Parse JSON
21+
data = fromJson(tempData);
22+
}
1723
}
1824
}
25+
1926
return data;
2027
}
2128

29+
function isJsonLike(str) {
30+
var jsonStart = str.match(JSON_START);
31+
return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
32+
}
33+
2234
/**
2335
* Parse headers into key value object
2436
*

test/ng/httpSpec.js

+24
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,16 @@ describe('$http', function() {
10551055
});
10561056

10571057

1058+
it('should ignore leading/trailing whitespace', function() {
1059+
$httpBackend.expect('GET', '/url').respond(' \n {"foo":"bar","baz":23} \r\n \n ');
1060+
$http({method: 'GET', url: '/url'}).success(callback);
1061+
$httpBackend.flush();
1062+
1063+
expect(callback).toHaveBeenCalledOnce();
1064+
expect(callback.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23});
1065+
});
1066+
1067+
10581068
it('should deserialize json numbers when response header contains application/json',
10591069
function() {
10601070
$httpBackend.expect('GET', '/url').respond('123', {'Content-Type': 'application/json'});
@@ -1182,6 +1192,20 @@ describe('$http', function() {
11821192
expect(callback).toHaveBeenCalledOnce();
11831193
expect(callback.mostRecentCall.args[0]).toEqual('{{some}}');
11841194
});
1195+
1196+
it('should not deserialize json when the opening and closing brackets do not match',
1197+
function() {
1198+
$httpBackend.expect('GET', '/url1').respond('[Code](url): function() {}');
1199+
$httpBackend.expect('GET', '/url2').respond('{"is": "not"} ["json"]');
1200+
$http.get('/url1').success(callback);
1201+
$http.get('/url2').success(callback);
1202+
$httpBackend.flush();
1203+
1204+
expect(callback.calls.length).toBe(2);
1205+
expect(callback.calls[0].args[0]).toEqual('[Code](url): function() {}');
1206+
expect(callback.calls[1].args[0]).toEqual('{"is": "not"} ["json"]');
1207+
}
1208+
);
11851209
});
11861210

11871211

0 commit comments

Comments
 (0)