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

Commit 406a4c9

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 facfec9 commit 406a4c9

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
@@ -1046,6 +1046,16 @@ describe('$http', function() {
10461046
});
10471047

10481048

1049+
it('should ignore leading/trailing whitespace', function() {
1050+
$httpBackend.expect('GET', '/url').respond(' \n {"foo":"bar","baz":23} \r\n \n ');
1051+
$http({method: 'GET', url: '/url'}).success(callback);
1052+
$httpBackend.flush();
1053+
1054+
expect(callback).toHaveBeenCalledOnce();
1055+
expect(callback.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23});
1056+
});
1057+
1058+
10491059
it('should deserialize json numbers when response header contains application/json',
10501060
function() {
10511061
$httpBackend.expect('GET', '/url').respond('123', {'Content-Type': 'application/json'});
@@ -1173,6 +1183,20 @@ describe('$http', function() {
11731183
expect(callback).toHaveBeenCalledOnce();
11741184
expect(callback.mostRecentCall.args[0]).toEqual('{{some}}');
11751185
});
1186+
1187+
it('should not deserialize json when the opening and closing brackets do not match',
1188+
function() {
1189+
$httpBackend.expect('GET', '/url1').respond('[Code](url): function() {}');
1190+
$httpBackend.expect('GET', '/url2').respond('{"is": "not"} ["json"]');
1191+
$http.get('/url1').success(callback);
1192+
$http.get('/url2').success(callback);
1193+
$httpBackend.flush();
1194+
1195+
expect(callback.calls.length).toBe(2);
1196+
expect(callback.calls[0].args[0]).toEqual('[Code](url): function() {}');
1197+
expect(callback.calls[1].args[0]).toEqual('{"is": "not"} ["json"]');
1198+
}
1199+
);
11761200
});
11771201

11781202

0 commit comments

Comments
 (0)