Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
fix(all): Use $templateRequest instead of $http for security.
Browse files Browse the repository at this point in the history
For security purposes, we have switched from using `$http` for our
template and icon requests to using `$templateRequest` since it
provides some automatic caching, and more importantly, security
checks about the URL/data.

See https://docs.angularjs.org/api/ng/service/$templateRequest for
more information.

Fixes #8413.

Closes #8423
  • Loading branch information
topherfangio authored and ThomasBurleson committed May 12, 2016
1 parent b528ca2 commit 0397e29
Show file tree
Hide file tree
Showing 13 changed files with 52 additions and 50 deletions.
9 changes: 4 additions & 5 deletions docs/app/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,9 +722,8 @@ function($scope, doc, component, $rootScope) {
'$scope',
'component',
'demos',
'$http',
'$templateCache',
function($rootScope, $scope, component, demos, $http, $templateCache) {
'$templateRequest',
function($rootScope, $scope, component, demos, $templateRequest) {
$rootScope.currentComponent = component;
$rootScope.currentDoc = null;

Expand All @@ -737,9 +736,9 @@ function($rootScope, $scope, component, demos, $http, $templateCache) {
.concat(demo.css || [])
.concat(demo.html || []);
files.forEach(function(file) {
file.httpPromise =$http.get(file.outputPath, {cache: $templateCache})
file.httpPromise = $templateRequest(file.outputPath)
.then(function(response) {
file.contents = response.data
file.contents = response
.replace('<head/>', '');
return file.contents;
});
Expand Down
4 changes: 1 addition & 3 deletions docs/app/js/demoInclude.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
DocsApp.directive('demoInclude', [
'$q',
'$http',
'$compile',
'$templateCache',
'$timeout',
function($q, $http, $compile, $templateCache, $timeout) {
function($q, $compile, $timeout) {
return {
restrict: 'E',
link: postLink
Expand Down
2 changes: 1 addition & 1 deletion docs/config/template/index.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ <h2 class="md-toolbar-item md-breadcrumb md-headline">
<div layout="row" flex="noshrink" layout-align="center center">
<div id="license-footer" flex>
Powered by Google &copy;2014&#8211;{{thisYear}}.
Code licensed under the <a href="./license" class="md-accent">MIT License</a>.
Code licensed under the <a ng-href="./license" class="md-accent">MIT License</a>.
Documentation licensed under
<a href="http://creativecommons.org/licenses/by/4.0/" target="_blank" class="md-default-theme md-accent">CC BY 4.0</a>.
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/bottomSheet/demoBasicUsage/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ angular.module('bottomSheetDemo1', ['ngMaterial'])
$mdBottomSheet.hide(clickedItem);
};
})
.run(function($http, $templateCache) {
.run(function($templateResult) {

This comment has been minimized.

Copy link
@gkalpak

gkalpak May 13, 2016

Member

Currently the demo is broken, because there is no $templateResult service 😃
Submitted #8453. /cc @ThomasBurleson


var urls = [
'img/icons/share-arrow.svg',
Expand All @@ -87,7 +87,7 @@ angular.module('bottomSheetDemo1', ['ngMaterial'])
];

angular.forEach(urls, function(url) {
$http.get(url, {cache: $templateCache});
$templateResult(url);
});

});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div ng-controller="DemoCtrl" layout="column" layout-margin ng-cloak>

<p>
Pre-fetch with $http & cache SVG icons using $templateCache.<br/>
Pre-fetch and cache SVG icons using $templateRequest.<br/>
<span class="note"> NOTE: Show the Source views for details...</span>
</p>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ angular.module('appUsingTemplateCache', ['ngMaterial'])
.config(function($mdIconProvider) {

// Register icon IDs with sources. Future $mdIcon( <id> ) lookups
// will load by url and retrieve the data via the $http and $templateCache
// will load by url and retrieve the data via the $templateRequest

$mdIconProvider
.iconSet('core', 'img/icons/sets/core-icons.svg',24)
.icon('social:cake', 'img/icons/cake.svg',24);

})
.run(function($http, $templateCache) {
.run(function($templateRequest) {

var urls = [
'img/icons/sets/core-icons.svg',
Expand All @@ -20,10 +20,10 @@ angular.module('appUsingTemplateCache', ['ngMaterial'])
];

// Pre-fetch icons sources by URL and cache in the $templateCache...
// subsequent $http calls will look there first.
// subsequent $templateRequest calls will look there first.

angular.forEach(urls, function(url) {
$http.get(url, {cache: $templateCache});
$templateRequest(url);
});

})
Expand Down
17 changes: 6 additions & 11 deletions src/components/icon/icon.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
describe('mdIcon directive', function() {
describe('MdIcon directive', function() {
var el;
var $scope;
var $compile;
Expand Down Expand Up @@ -332,7 +332,7 @@ describe('mdIcon directive', function() {
});


describe('mdIcon service', function() {
describe('MdIcon service', function() {

var $mdIcon;
var $httpBackend;
Expand Down Expand Up @@ -467,7 +467,7 @@ describe('mdIcon service', function() {
var msg;
try {
$mdIcon('notconfigured')
.catch(function(error){
.catch(function(error) {
msg = error;
});

Expand Down Expand Up @@ -518,8 +518,8 @@ describe('mdIcon service', function() {
var msg;
try {
$mdIcon('notfound:someIcon')
.catch(function(error){
msg = error;
.catch(function(error) {
msg = error.data;
});

$httpBackend.flush();
Expand All @@ -529,18 +529,13 @@ describe('mdIcon service', function() {
});
});

/*
* Previous to Angular 1.6, requesting an icon that is not found would throw no errors. After
* 1.6, since we do not have a .catch() handler, it now throws a Possibly Unhandled Rejection
* error.
*/
describe('icon is not found', function() {
it('should not throw Error', function() {
expect(function(){
$mdIcon('notfound');

$httpBackend.flush();
}).not.toThrow('Cannot GET notfoundicon.svg');
}).not.toThrow();
});
});
});
Expand Down
50 changes: 30 additions & 20 deletions src/components/icon/js/iconService.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
* If using font-icons, the developer is responsible for loading the fonts.
*
* If using SVGs, loading of the actual svg files are deferred to on-demand requests and are loaded
* internally by the `$mdIcon` service using the `$http` service. When an SVG is requested by name/ID,
* the `$mdIcon` service searches its registry for the associated source URL;
* internally by the `$mdIcon` service using the `$templateRequest` service. When an SVG is
* requested by name/ID, the `$mdIcon` service searches its registry for the associated source URL;
* that URL is used to on-demand load and parse the SVG dynamically.
*
* **Notice:** Most font-icons libraries do not support ligatures (for example `fontawesome`).<br/>
Expand Down Expand Up @@ -60,15 +60,15 @@
* $mdIconProvider.defaultIconSet('my/app/icons.svg')
*
* })
* .run(function($http, $templateCache){
* .run(function($templateRequest){
*
* // Pre-fetch icons sources by URL and cache in the $templateCache...
* // subsequent $http calls will look there first.
* // subsequent $templateRequest calls will look there first.
*
* var urls = [ 'imy/app/icons.svg', 'img/icons/android.svg'];
*
* angular.forEach(urls, function(url) {
* $http.get(url, {cache: $templateCache});
* $templateRequest(url);
* });
*
* });
Expand All @@ -88,8 +88,9 @@
* These icons will later be retrieved from the cache using `$mdIcon( <icon name> )`
*
* @param {string} id Icon name/id used to register the icon
* @param {string} url specifies the external location for the data file. Used internally by `$http` to load the
* data or as part of the lookup in `$templateCache` if pre-loading was configured.
* @param {string} url specifies the external location for the data file. Used internally by
* `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
* was configured.
* @param {number=} viewBoxSize Sets the width and height the icon's viewBox.
* It is ignored for icons with an existing viewBox. Default size is 24.
*
Expand Down Expand Up @@ -118,8 +119,9 @@
* `$mdIcon(<icon set name>:<icon name>)`
*
* @param {string} id Icon name/id used to register the iconset
* @param {string} url specifies the external location for the data file. Used internally by `$http` to load the
* data or as part of the lookup in `$templateCache` if pre-loading was configured.
* @param {string} url specifies the external location for the data file. Used internally by
* `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
* was configured.
* @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set.
* It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size.
* Default value is 24.
Expand Down Expand Up @@ -148,8 +150,9 @@
* subsequent lookups of icons will failover to search this 'default' icon set.
* Icon can be retrieved from this cached, default set using `$mdIcon(<name>)`
*
* @param {string} url specifies the external location for the data file. Used internally by `$http` to load the
* data or as part of the lookup in `$templateCache` if pre-loading was configured.
* @param {string} url specifies the external location for the data file. Used internally by
* `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
* was configured.
* @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set.
* It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size.
* Default value is 24.
Expand Down Expand Up @@ -354,9 +357,9 @@

},

$get : ['$http', '$q', '$log', '$templateCache', '$mdUtil', function($http, $q, $log, $templateCache, $mdUtil) {
$get : ['$templateRequest', '$q', '$log', '$templateCache', '$mdUtil', function($templateRequest, $q, $log, $templateCache, $mdUtil) {
this.preloadIcons($templateCache);
return MdIconService(config, $http, $q, $log, $templateCache, $mdUtil);
return MdIconService(config, $templateRequest, $q, $log, $templateCache, $mdUtil);
}]
};

Expand Down Expand Up @@ -411,7 +414,7 @@
*/

/* @ngInject */
function MdIconService(config, $http, $q, $log, $templateCache, $mdUtil) {
function MdIconService(config, $templateRequest, $q, $log, $templateCache, $mdUtil) {

This comment has been minimized.

Copy link
@gkalpak

gkalpak May 13, 2016

Member

$templateCache is not needed any more here.
Submitted #8456. /cc @ThomasBurleson

var iconCache = {};
var urlRegex = /[-\w@:%\+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%\+.~#?&//=]*)?/i;
var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i;
Expand Down Expand Up @@ -513,7 +516,7 @@

function announceIdNotFound(id) {
var msg = 'icon ' + id + ' not found';
$log.warn(msg);
$log.warn(msg);

return $q.reject(msg || id);
}
Expand All @@ -534,11 +537,18 @@

/* Load the icon by URL using HTTP. */
function loadByHttpUrl(url) {
return $http
.get(url, { cache: $templateCache })
.then(function(response) {
return angular.element('<div>').append(response.data).find('svg')[0];
}).catch(announceNotFound);
return $q(function(resolve, reject) {
var extractSvg = function(response) {
var svg = angular.element('<div>').append(response).find('svg')[0];
resolve(svg);
};
var announceAndReject = function(e) {
announceNotFound(e);
reject(e);
};

$templateRequest(url, true).then(extractSvg, announceAndReject);
});
}

return dataUrlRegex.test(url)
Expand Down
6 changes: 3 additions & 3 deletions src/core/services/compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ angular
.module('material.core')
.service('$mdCompiler', mdCompilerService);

function mdCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
function mdCompilerService($q, $templateRequest, $injector, $compile, $controller) {
/* jshint validthis: true */

/*
Expand Down Expand Up @@ -89,9 +89,9 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
angular.extend(resolve, locals);

if (templateUrl) {
resolve.$template = $http.get(templateUrl, {cache: $templateCache})
resolve.$template = $templateRequest(templateUrl)
.then(function(response) {
return response.data;
return response;
});
} else {
resolve.$template = $q.when(template);
Expand Down

0 comments on commit 0397e29

Please sign in to comment.