-
Notifications
You must be signed in to change notification settings - Fork 27.4k
sec(http): JSONP should require trusted resource URLs #11623
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,14 +21,20 @@ function createXhr() { | |
* $httpBackend} which can be trained with responses. | ||
*/ | ||
function $HttpBackendProvider() { | ||
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { | ||
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); | ||
this.$get = ['$sce', '$browser', '$window', '$document', function($sce, $browser, $window, $document) { | ||
return createHttpBackend($sce, $browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); | ||
}]; | ||
} | ||
|
||
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { | ||
function createHttpBackend($sce, $browser, createXhr, $browserDefer, callbacks, rawDocument) { | ||
// TODO(vojta): fix the signature | ||
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { | ||
if (lowercase(method) == 'jsonp') { | ||
// This is a pretty sensitive operation where we're allowing a script to have full access to | ||
// our DOM and JS space. So we require that the URL satisfies SCE.RESOURCE_URL. | ||
url = $sce.getTrustedResourceUrl(url); | ||
} | ||
|
||
$browser.$$incOutstandingRequestCount(); | ||
url = url || $browser.url(); | ||
|
||
|
@@ -142,8 +148,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc | |
// - adds and immediately removes script elements from the document | ||
var script = rawDocument.createElement('script'), callback = null; | ||
script.type = "text/javascript"; | ||
script.src = url; | ||
script.async = true; | ||
script.src = url; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. phantom edit? |
||
|
||
callback = function(event) { | ||
removeEventListenerFn(script, "load", callback); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1095,7 +1095,7 @@ angular.mock.dump = function(object) { | |
``` | ||
*/ | ||
angular.mock.$HttpBackendProvider = function() { | ||
this.$get = ['$rootScope', '$timeout', createHttpBackendMock]; | ||
this.$get = ['$sce', '$rootScope', '$timeout', createHttpBackendMock]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see why the changes in this file are needed... |
||
}; | ||
|
||
/** | ||
|
@@ -1112,7 +1112,7 @@ angular.mock.$HttpBackendProvider = function() { | |
* @param {Object=} $browser Auto-flushing enabled if specified | ||
* @return {Object} Instance of $httpBackend mock | ||
*/ | ||
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { | ||
function createHttpBackendMock($sce, $rootScope, $timeout, $delegate, $browser) { | ||
var definitions = [], | ||
expectations = [], | ||
responses = [], | ||
|
@@ -2092,7 +2092,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { | |
*/ | ||
angular.mock.e2e = {}; | ||
angular.mock.e2e.$httpBackendDecorator = | ||
['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock]; | ||
['$sce', '$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock]; | ||
|
||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,13 @@ | |
|
||
describe('$httpBackend', function() { | ||
|
||
var $backend, $browser, callbacks, | ||
var $sce, $backend, $browser, callbacks, | ||
xhr, fakeDocument, callback; | ||
|
||
|
||
beforeEach(inject(function($injector) { | ||
callbacks = {counter: 0}; | ||
$sce = $injector.get('$sce'); | ||
$browser = $injector.get('$browser'); | ||
fakeDocument = { | ||
$$scripts: [], | ||
|
@@ -28,7 +29,7 @@ describe('$httpBackend', function() { | |
}) | ||
} | ||
}; | ||
$backend = createHttpBackend($browser, createMockXhr, $browser.defer, callbacks, fakeDocument); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr, $browser.defer, callbacks, fakeDocument); | ||
callback = jasmine.createSpy('done'); | ||
})); | ||
|
||
|
@@ -255,7 +256,22 @@ describe('$httpBackend', function() { | |
}); | ||
|
||
|
||
describe('JSONP', function() { | ||
[true, false].forEach(function(trustAsResourceUrl) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like a good place for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or maybe not (since it's on the |
||
describe('JSONP: trustAsResourceUrl='+trustAsResourceUrl, function() { | ||
|
||
|
||
function $backendCall() { | ||
var args = Array.prototype.slice.call(arguments); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is also the |
||
var url = args[1]; | ||
if (trustAsResourceUrl || url == null) { | ||
args[1] = $sce.trustAsResourceUrl(url); | ||
return $backend.apply(null, args); | ||
} else { | ||
var badUrlPrefix = url ? url.replace('JSON_CALLBACK', '') : ''; | ||
expect(function() { $backend.apply(null, args); }).toThrowMinErr( | ||
'$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: ' + badUrlPrefix); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is too much work going on here for a test. We should just duplicate what is needed so that we can easily read the tests. |
||
|
||
var SCRIPT_URL = /([^\?]*)\?cb=angular\.callbacks\.(.*)/; | ||
|
||
|
@@ -266,7 +282,9 @@ describe('$httpBackend', function() { | |
expect(response).toBe('some-data'); | ||
}); | ||
|
||
$backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); | ||
$backendCall('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); | ||
if (!trustAsResourceUrl) return; | ||
|
||
expect(fakeDocument.$$scripts.length).toBe(1); | ||
|
||
var script = fakeDocument.$$scripts.shift(), | ||
|
@@ -281,7 +299,9 @@ describe('$httpBackend', function() { | |
|
||
|
||
it('should clean up the callback and remove the script', function() { | ||
$backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); | ||
$backendCall('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); | ||
if (!trustAsResourceUrl) return; | ||
|
||
expect(fakeDocument.$$scripts.length).toBe(1); | ||
|
||
|
||
|
@@ -297,11 +317,13 @@ describe('$httpBackend', function() { | |
|
||
|
||
it('should set url to current location if not specified or empty string', function() { | ||
$backend('JSONP', undefined, null, callback); | ||
$backendCall('JSONP', undefined, null, callback); | ||
if (!trustAsResourceUrl) return; | ||
|
||
expect(fakeDocument.$$scripts[0].src).toBe($browser.url()); | ||
fakeDocument.$$scripts.shift(); | ||
|
||
$backend('JSONP', '', null, callback); | ||
$backendCall('JSONP', '', null, callback); | ||
expect(fakeDocument.$$scripts[0].src).toBe($browser.url()); | ||
}); | ||
|
||
|
@@ -311,7 +333,9 @@ describe('$httpBackend', function() { | |
expect(status).toBe(-1); | ||
}); | ||
|
||
$backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback, null, 2000); | ||
$backendCall('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback, null, 2000); | ||
if (!trustAsResourceUrl) return; | ||
|
||
expect(fakeDocument.$$scripts.length).toBe(1); | ||
expect($browser.deferredFns[0].time).toBe(2000); | ||
|
||
|
@@ -328,7 +352,7 @@ describe('$httpBackend', function() { | |
|
||
// TODO(vojta): test whether it fires "async-start" | ||
// TODO(vojta): test whether it fires "async-end" on both success and error | ||
}); | ||
})}); | ||
|
||
describe('protocols that return 0 status code', function() { | ||
|
||
|
@@ -341,7 +365,7 @@ describe('$httpBackend', function() { | |
|
||
|
||
it('should convert 0 to 200 if content and file protocol', function() { | ||
$backend = createHttpBackend($browser, createMockXhr); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr); | ||
|
||
$backend('GET', 'file:///whatever/index.html', null, callback); | ||
respond(0, 'SOME CONTENT'); | ||
|
@@ -351,7 +375,7 @@ describe('$httpBackend', function() { | |
}); | ||
|
||
it('should convert 0 to 200 if content for protocols other than file', function() { | ||
$backend = createHttpBackend($browser, createMockXhr); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr); | ||
|
||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback); | ||
respond(0, 'SOME CONTENT'); | ||
|
@@ -361,7 +385,7 @@ describe('$httpBackend', function() { | |
}); | ||
|
||
it('should convert 0 to 404 if no content and file protocol', function() { | ||
$backend = createHttpBackend($browser, createMockXhr); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr); | ||
|
||
$backend('GET', 'file:///whatever/index.html', null, callback); | ||
respond(0, ''); | ||
|
@@ -371,7 +395,7 @@ describe('$httpBackend', function() { | |
}); | ||
|
||
it('should not convert 0 to 404 if no content for protocols other than file', function() { | ||
$backend = createHttpBackend($browser, createMockXhr); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr); | ||
|
||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback); | ||
respond(0, ''); | ||
|
@@ -399,7 +423,7 @@ describe('$httpBackend', function() { | |
|
||
try { | ||
|
||
$backend = createHttpBackend($browser, createMockXhr); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr); | ||
|
||
$backend('GET', '/whatever/index.html', null, callback); | ||
respond(0, ''); | ||
|
@@ -414,7 +438,7 @@ describe('$httpBackend', function() { | |
|
||
|
||
it('should return original backend status code if different from 0', function() { | ||
$backend = createHttpBackend($browser, createMockXhr); | ||
$backend = createHttpBackend($sce, $browser, createMockXhr); | ||
|
||
// request to http:// | ||
$backend('POST', 'http://rest_api/create_whatever', null, callback); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this necessary? why not just
url = $sce.valueOf(origUrl);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is related to #11328