mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
feat(ngBindHtml, sce): combine ng-bind-html and ng-bind-html-unsafe
Changes: - remove ng-bind-html-unsafe - ng-bind-html is now in core - ng-bind-html is secure - supports SCE - so you can bind to an arbitrary trusted string - automatic sanitization if $sanitize is available BREAKING CHANGE: ng-html-bind-unsafe has been removed and replaced by ng-html-bind (which has been removed from ngSanitize.) ng-bind-html provides ng-html-bind-unsafe like behavior (innerHTML's the result without sanitization) when bound to the result of $sce.trustAsHtml(string). When bound to a plain string, the string is sanitized via $sanitize before being innerHTML'd. If $sanitize isn't available, it's logs an exception.
This commit is contained in:
parent
bea9422ebf
commit
dae694739b
9 changed files with 135 additions and 150 deletions
|
|
@ -153,7 +153,6 @@ module.exports = function(grunt) {
|
||||||
dest: 'build/angular-sanitize.js',
|
dest: 'build/angular-sanitize.js',
|
||||||
src: util.wrap([
|
src: util.wrap([
|
||||||
'src/ngSanitize/sanitize.js',
|
'src/ngSanitize/sanitize.js',
|
||||||
'src/ngSanitize/directive/ngBindHtml.js',
|
|
||||||
'src/ngSanitize/filter/linky.js'
|
'src/ngSanitize/filter/linky.js'
|
||||||
], 'module')
|
], 'module')
|
||||||
},
|
},
|
||||||
|
|
|
||||||
1
angularFiles.js
vendored
1
angularFiles.js
vendored
|
|
@ -73,7 +73,6 @@ angularFiles = {
|
||||||
'src/ngRoute/routeParams.js',
|
'src/ngRoute/routeParams.js',
|
||||||
'src/ngRoute/directive/ngView.js',
|
'src/ngRoute/directive/ngView.js',
|
||||||
'src/ngSanitize/sanitize.js',
|
'src/ngSanitize/sanitize.js',
|
||||||
'src/ngSanitize/directive/ngBindHtml.js',
|
|
||||||
'src/ngSanitize/filter/linky.js',
|
'src/ngSanitize/filter/linky.js',
|
||||||
'src/ngMock/angular-mocks.js',
|
'src/ngMock/angular-mocks.js',
|
||||||
'src/ngMobile/mobile.js',
|
'src/ngMobile/mobile.js',
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ function publishExternalAPI(angular){
|
||||||
style: styleDirective,
|
style: styleDirective,
|
||||||
option: optionDirective,
|
option: optionDirective,
|
||||||
ngBind: ngBindDirective,
|
ngBind: ngBindDirective,
|
||||||
ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective,
|
ngBindHtml: ngBindHtmlDirective,
|
||||||
ngBindTemplate: ngBindTemplateDirective,
|
ngBindTemplate: ngBindTemplateDirective,
|
||||||
ngClass: ngClassDirective,
|
ngClass: ngClassDirective,
|
||||||
ngClassEven: ngClassEvenDirective,
|
ngClassEven: ngClassEvenDirective,
|
||||||
|
|
|
||||||
|
|
@ -116,23 +116,27 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc directive
|
* @ngdoc directive
|
||||||
* @name ng.directive:ngBindHtmlUnsafe
|
* @name ng.directive:ngBindHtml
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Creates a binding that will innerHTML the result of evaluating the `expression` into the current
|
* Creates a binding that will innerHTML the result of evaluating the `expression` into the current
|
||||||
* element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if
|
* element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
|
||||||
* {@link ngSanitize.directive:ngBindHtml ngBindHtml} directive is too
|
* ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
|
||||||
* restrictive and when you absolutely trust the source of the content you are binding to.
|
* is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
|
||||||
|
* core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to
|
||||||
|
* an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
|
||||||
|
* under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
|
||||||
*
|
*
|
||||||
* See {@link ngSanitize.$sanitize $sanitize} docs for examples.
|
* Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
|
||||||
|
* will have an exception (instead of an exploit.)
|
||||||
*
|
*
|
||||||
* @element ANY
|
* @element ANY
|
||||||
* @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate.
|
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
|
||||||
*/
|
*/
|
||||||
var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
|
var ngBindHtmlDirective = ['$sce', function($sce) {
|
||||||
return function(scope, element, attr) {
|
return function(scope, element, attr) {
|
||||||
element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
|
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
|
||||||
scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function ngBindHtmlUnsafeWatchAction(value) {
|
scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function ngBindHtmlWatchAction(value) {
|
||||||
element.html(value || '');
|
element.html(value || '');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
111
src/ng/sce.js
111
src/ng/sce.js
|
|
@ -137,8 +137,17 @@ function $SceDelegateProvider() {
|
||||||
(documentProtocol === "http:" && resourceProtocol === "https:"));
|
(documentProtocol === "http:" && resourceProtocol === "https:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$get = ['$log', '$document', '$$urlUtils', function(
|
this.$get = ['$log', '$document', '$injector', '$$urlUtils', function(
|
||||||
$log, $document, $$urlUtils) {
|
$log, $document, $injector, $$urlUtils) {
|
||||||
|
|
||||||
|
var htmlSanitizer = function htmlSanitizer(html) {
|
||||||
|
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($injector.has('$sanitize')) {
|
||||||
|
htmlSanitizer = $injector.get('$sanitize');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function matchUrl(matcher, parsedUrl) {
|
function matchUrl(matcher, parsedUrl) {
|
||||||
if (matcher === 'self') {
|
if (matcher === 'self') {
|
||||||
|
|
@ -285,6 +294,9 @@ function $SceDelegateProvider() {
|
||||||
if (constructor && maybeTrusted instanceof constructor) {
|
if (constructor && maybeTrusted instanceof constructor) {
|
||||||
return maybeTrusted.$$unwrapTrustedValue();
|
return maybeTrusted.$$unwrapTrustedValue();
|
||||||
}
|
}
|
||||||
|
// If we get here, then we may only take one of two actions.
|
||||||
|
// 1. sanitize the value for the requested type, or
|
||||||
|
// 2. throw an exception.
|
||||||
if (type === SCE_CONTEXTS.RESOURCE_URL) {
|
if (type === SCE_CONTEXTS.RESOURCE_URL) {
|
||||||
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
|
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
|
||||||
return maybeTrusted;
|
return maybeTrusted;
|
||||||
|
|
@ -293,6 +305,8 @@ function $SceDelegateProvider() {
|
||||||
'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', maybeTrusted.toString());
|
'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', maybeTrusted.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (type === SCE_CONTEXTS.HTML) {
|
||||||
|
return htmlSanitizer(maybeTrusted);
|
||||||
}
|
}
|
||||||
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
||||||
}
|
}
|
||||||
|
|
@ -329,8 +343,8 @@ function $SceDelegateProvider() {
|
||||||
*
|
*
|
||||||
* Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
|
* Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
|
||||||
* contexts to result in a value that is marked as safe to use for that context One example of such
|
* contexts to result in a value that is marked as safe to use for that context One example of such
|
||||||
* a context is binding arbitrary html controlled by the user via `ng-bind-html-unsafe`. We refer
|
* a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer to these
|
||||||
* to these contexts as privileged or SCE contexts.
|
* contexts as privileged or SCE contexts.
|
||||||
*
|
*
|
||||||
* As of version 1.2, Angular ships with SCE enabled by default.
|
* As of version 1.2, Angular ships with SCE enabled by default.
|
||||||
*
|
*
|
||||||
|
|
@ -347,10 +361,10 @@ function $SceDelegateProvider() {
|
||||||
*
|
*
|
||||||
* <pre class="prettyprint">
|
* <pre class="prettyprint">
|
||||||
* <input ng-model="userHtml">
|
* <input ng-model="userHtml">
|
||||||
* <div ng-bind-html-unsafe="{{userHtml}}">
|
* <div ng-bind-html="{{userHtml}}">
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Notice that `ng-bind-html-unsafe` is bound to `{{userHtml}}` controlled by the user. With SCE
|
* Notice that `ng-bind-html` is bound to `{{userHtml}}` controlled by the user. With SCE
|
||||||
* disabled, this application allows the user to render arbitrary HTML into the DIV.
|
* disabled, this application allows the user to render arbitrary HTML into the DIV.
|
||||||
* In a more realistic example, one may be rendering user comments, blog articles, etc. via
|
* In a more realistic example, one may be rendering user comments, blog articles, etc. via
|
||||||
* bindings. (HTML is just one example of a context where rendering user controlled input creates
|
* bindings. (HTML is just one example of a context where rendering user controlled input creates
|
||||||
|
|
@ -384,14 +398,14 @@ function $SceDelegateProvider() {
|
||||||
* ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
|
* ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
|
||||||
* {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
|
* {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
|
||||||
*
|
*
|
||||||
* As an example, {@link ng.directive:ngBindHtmlUnsafe ngBindHtmlUnsafe} uses {@link
|
* As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
|
||||||
* ng.$sce#parseHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
|
* ng.$sce#parseHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
|
||||||
* simplified):
|
* simplified):
|
||||||
*
|
*
|
||||||
* <pre class="prettyprint">
|
* <pre class="prettyprint">
|
||||||
* var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
|
* var ngBindHtmlDirective = ['$sce', function($sce) {
|
||||||
* return function(scope, element, attr) {
|
* return function(scope, element, attr) {
|
||||||
* scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function(value) {
|
* scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
|
||||||
* element.html(value || '');
|
* element.html(value || '');
|
||||||
* });
|
* });
|
||||||
* };
|
* };
|
||||||
|
|
@ -444,7 +458,7 @@ function $SceDelegateProvider() {
|
||||||
*
|
*
|
||||||
* | Context | Notes |
|
* | Context | Notes |
|
||||||
* |=====================|================|
|
* |=====================|================|
|
||||||
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtmlUnsafe ngBindHtmlUnsafe} directive uses this context for bindings. |
|
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. |
|
||||||
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
|
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
|
||||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't consititute an SCE context. |
|
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't consititute an SCE context. |
|
||||||
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
||||||
|
|
@ -458,61 +472,37 @@ function $SceDelegateProvider() {
|
||||||
<example module="mySceApp">
|
<example module="mySceApp">
|
||||||
<file name="index.html">
|
<file name="index.html">
|
||||||
<div ng-controller="myAppController as myCtrl">
|
<div ng-controller="myAppController as myCtrl">
|
||||||
<button ng-click="myCtrl.fetchUserComments()" id="fetchBtn">Fetch Comments</button>
|
<i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
|
||||||
<div ng-show="myCtrl.errorMsg">Error: {{myCtrl.errorMsg}}</div>
|
<b>User comments</b><br>
|
||||||
<div ng-repeat="userComment in myCtrl.userComments">
|
By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when $sanitize is available. If $sanitize isn't available, this results in an error instead of an exploit.
|
||||||
<hr>
|
<div class="well">
|
||||||
<b>{{userComment.name}}</b>:
|
<div ng-repeat="userComment in myCtrl.userComments">
|
||||||
<span ng-bind-html-unsafe="userComment.htmlComment" class="htmlComment"></span>
|
<b>{{userComment.name}}</b>:
|
||||||
|
<span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-bind-html-unsafe="myCtrl.someHtml" id="someHtml"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</file>
|
</file>
|
||||||
|
|
||||||
<file name="script.js">
|
<file name="script.js">
|
||||||
// These types of functions would be in the data access layer of your application code.
|
var mySceApp = angular.module('mySceApp', ['ngSanitize']);
|
||||||
function fetchUserCommentsFromServer($http, $q, $templateCache, $sce) {
|
|
||||||
var deferred = $q.defer();
|
|
||||||
$http({method: "GET", url: "test_data.json", cache: $templateCache}).
|
|
||||||
success(function(userComments, status) {
|
|
||||||
// The comments coming from the server have been sanitized by the server and can be
|
|
||||||
// trusted.
|
|
||||||
angular.forEach(userComments, function(userComment) {
|
|
||||||
userComment.htmlComment = $sce.trustAsHtml(userComment.htmlComment);
|
|
||||||
});
|
|
||||||
deferred.resolve(userComments);
|
|
||||||
}).
|
|
||||||
error(function (data, status) {
|
|
||||||
deferred.reject("HTTP status code " + status + ": " + data);
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
var mySceApp = angular.module('mySceApp', []);
|
mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
|
||||||
|
|
||||||
mySceApp.controller("myAppController", function myAppController($injector) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
$http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
|
||||||
self.someHtml = "This might have been any binding including an input element " +
|
self.userComments = userComments;
|
||||||
"controlled by the user.";
|
});
|
||||||
|
self.explicitlyTrustedHtml = $sce.trustAsHtml(
|
||||||
self.fetchUserComments = function() {
|
'<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
|
||||||
$injector.invoke(fetchUserCommentsFromServer).then(
|
'sanitization."">Hover over this text.</span>');
|
||||||
function onSuccess(userComments) {
|
|
||||||
self.errorMsg = null;
|
|
||||||
self.userComments = userComments;
|
|
||||||
},
|
|
||||||
function onFailure(errorMsg) {
|
|
||||||
self.errorMsg = errorMsg;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</file>
|
</file>
|
||||||
|
|
||||||
<file name="test_data.json">
|
<file name="test_data.json">
|
||||||
[
|
[
|
||||||
{ "name": "Alice",
|
{ "name": "Alice",
|
||||||
"htmlComment": "Is <i>anyone</i> reading this?"
|
"htmlComment": "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
|
||||||
},
|
},
|
||||||
{ "name": "Bob",
|
{ "name": "Bob",
|
||||||
"htmlComment": "<i>Yes!</i> Am I the only other one?"
|
"htmlComment": "<i>Yes!</i> Am I the only other one?"
|
||||||
|
|
@ -521,14 +511,15 @@ function $SceDelegateProvider() {
|
||||||
</file>
|
</file>
|
||||||
|
|
||||||
<file name="scenario.js">
|
<file name="scenario.js">
|
||||||
describe('SCE doc demo', function() {
|
describe('SCE doc demo', function() {
|
||||||
it('should bind trusted values', function() {
|
it('should sanitize untrusted values', function() {
|
||||||
element('#fetchBtn').click();
|
expect(element('.htmlComment').html()).toBe('<span>Is <i>anyone</i> reading this?</span>');
|
||||||
expect(element('.htmlComment').html()).toBe('Is <i>anyone</i> reading this?');
|
});
|
||||||
});
|
it('should NOT sanitize explicitly trusted values', function() {
|
||||||
it('should NOT bind arbitrary values', function() {
|
expect(element('#explicitlyTrustedHtml').html()).toBe(
|
||||||
expect(element('#someHtml').html()).toBe('');
|
'<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
|
||||||
});
|
'sanitization."">Hover over this text.</span>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc directive
|
|
||||||
* @name ngSanitize.directive:ngBindHtml
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Creates a binding that will sanitize the result of evaluating the `expression` with the
|
|
||||||
* {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
|
|
||||||
*
|
|
||||||
* See {@link ngSanitize.$sanitize $sanitize} docs for examples.
|
|
||||||
*
|
|
||||||
* @element ANY
|
|
||||||
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
|
|
||||||
*/
|
|
||||||
angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
|
|
||||||
return function(scope, element, attr) {
|
|
||||||
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
|
|
||||||
scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
|
|
||||||
value = $sanitize(value);
|
|
||||||
element.html(value || '');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
|
|
@ -68,8 +68,7 @@ var ngSanitizeMinErr = angular.$$minErr('ngSanitize');
|
||||||
'<p style="color:blue">an html\n' +
|
'<p style="color:blue">an html\n' +
|
||||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||||
'snippet</p>';
|
'snippet</p>';
|
||||||
// ng-bind-html-unsafe requires a $sce trusted value of type $sce.HTML.
|
$scope.deliberatelyTrustDangerousSnippet = function() {
|
||||||
$scope.getSceSnippet = function() {
|
|
||||||
return $sce.trustAsHtml($scope.snippet);
|
return $sce.trustAsHtml($scope.snippet);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -78,57 +77,57 @@ var ngSanitizeMinErr = angular.$$minErr('ngSanitize');
|
||||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Filter</td>
|
<td>Directive</td>
|
||||||
|
<td>How</td>
|
||||||
<td>Source</td>
|
<td>Source</td>
|
||||||
<td>Rendered</td>
|
<td>Rendered</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="html-filter">
|
<tr id="bind-html-with-sanitize">
|
||||||
<td>html filter</td>
|
<td>ng-bind-html</td>
|
||||||
<td>
|
<td>Automatically uses $sanitize</td>
|
||||||
<pre><div ng-bind-html="snippet"><br/></div></pre>
|
<td><pre><div ng-bind-html="snippet"><br/></div></pre></td>
|
||||||
</td>
|
<td><div ng-bind-html="snippet"></div></td>
|
||||||
<td>
|
|
||||||
<div ng-bind-html="snippet"></div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="escaped-html">
|
<tr id="bind-html-with-trust">
|
||||||
<td>no filter</td>
|
<td>ng-bind-html</td>
|
||||||
|
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
|
||||||
|
<td><pre><div ng-bind-html="deliberatelyTrustDangerousSnippet()"><br/></div></pre></td>
|
||||||
|
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
|
||||||
|
</tr>
|
||||||
|
<tr id="bind-default">
|
||||||
|
<td>ng-bind</td>
|
||||||
|
<td>Automatically escapes</td>
|
||||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||||
<td><div ng-bind="snippet"></div></td>
|
<td><div ng-bind="snippet"></div></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="html-unsafe-filter">
|
|
||||||
<td>unsafe html filter</td>
|
|
||||||
<td><pre><div ng-bind-html-unsafe="getSceSnippet()"><br/></div></pre></td>
|
|
||||||
<td><div ng-bind-html-unsafe="getSceSnippet()"></div></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</doc:source>
|
</doc:source>
|
||||||
<doc:scenario>
|
<doc:scenario>
|
||||||
it('should sanitize the html snippet ', function() {
|
it('should sanitize the html snippet by default', function() {
|
||||||
expect(using('#html-filter').element('div').html()).
|
expect(using('#bind-html-with-sanitize').element('div').html()).
|
||||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should escape snippet without any filter', function() {
|
it('should inline raw snippet if bound to a trusted value', function() {
|
||||||
expect(using('#escaped-html').element('div').html()).
|
expect(using('#bind-html-with-trust').element("div").html()).
|
||||||
toBe("<p style=\"color:blue\">an html\n" +
|
|
||||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
|
||||||
"snippet</p>");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inline raw snippet if filtered as unsafe', function() {
|
|
||||||
expect(using('#html-unsafe-filter').element("div").html()).
|
|
||||||
toBe("<p style=\"color:blue\">an html\n" +
|
toBe("<p style=\"color:blue\">an html\n" +
|
||||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||||
"snippet</p>");
|
"snippet</p>");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update', function($sce) {
|
it('should escape snippet without any filter', function() {
|
||||||
input('snippet').enter('new <b>text</b>');
|
expect(using('#bind-default').element('div').html()).
|
||||||
expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
|
toBe("<p style=\"color:blue\">an html\n" +
|
||||||
expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>");
|
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||||
expect(using('#html-unsafe-filter').element('div').html()).toBe('new <b>text</b>');
|
"snippet</p>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update', function() {
|
||||||
|
input('snippet').enter('new <b onclick="alert(1)">text</b>');
|
||||||
|
expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>');
|
||||||
|
expect(using('#bind-html-with-trust').element('div').html()).toBe('new <b onclick="alert(1)">text</b>');
|
||||||
|
expect(using('#bind-default').element('div').html()).toBe("new <b onclick=\"alert(1)\">text</b>");
|
||||||
});
|
});
|
||||||
</doc:scenario>
|
</doc:scenario>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
|
|
|
||||||
|
|
@ -67,19 +67,14 @@ describe('ngBind*', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('ngBindHtmlUnsafe', function() {
|
describe('ngBindHtml', function() {
|
||||||
|
|
||||||
function configureSce(enabled) {
|
|
||||||
module(function($provide, $sceProvider) {
|
|
||||||
$sceProvider.enabled(enabled);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('SCE disabled', function() {
|
describe('SCE disabled', function() {
|
||||||
beforeEach(function() {configureSce(false)});
|
beforeEach(function() {
|
||||||
|
module(function($sceProvider) { $sceProvider.enabled(false); });
|
||||||
|
});
|
||||||
|
|
||||||
it('should set unsafe html', inject(function($rootScope, $compile) {
|
it('should set html', inject(function($rootScope, $compile) {
|
||||||
element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope);
|
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
|
||||||
$rootScope.html = '<div onclick="">hello</div>';
|
$rootScope.html = '<div onclick="">hello</div>';
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>');
|
expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>');
|
||||||
|
|
@ -88,27 +83,35 @@ describe('ngBind*', function() {
|
||||||
|
|
||||||
|
|
||||||
describe('SCE enabled', function() {
|
describe('SCE enabled', function() {
|
||||||
beforeEach(function() {configureSce(true)});
|
it('should NOT set html for untrusted values', inject(function($rootScope, $compile) {
|
||||||
|
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
|
||||||
it('should NOT set unsafe html for untrusted values', inject(function($rootScope, $compile) {
|
|
||||||
element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope);
|
|
||||||
$rootScope.html = '<div onclick="">hello</div>';
|
$rootScope.html = '<div onclick="">hello</div>';
|
||||||
expect($rootScope.$digest).toThrow();
|
expect($rootScope.$digest).toThrow();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should NOT set unsafe html for wrongly typed values', inject(function($rootScope, $compile, $sce) {
|
it('should NOT set html for wrongly typed values', inject(function($rootScope, $compile, $sce) {
|
||||||
element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope);
|
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
|
||||||
$rootScope.html = $sce.trustAsCss('<div onclick="">hello</div>');
|
$rootScope.html = $sce.trustAsCss('<div onclick="">hello</div>');
|
||||||
expect($rootScope.$digest).toThrow();
|
expect($rootScope.$digest).toThrow();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should set unsafe html for trusted values', inject(function($rootScope, $compile, $sce) {
|
it('should set html for trusted values', inject(function($rootScope, $compile, $sce) {
|
||||||
element = $compile('<div ng-bind-html-unsafe="html"></div>')($rootScope);
|
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
|
||||||
$rootScope.html = $sce.trustAsHtml('<div onclick="">hello</div>');
|
$rootScope.html = $sce.trustAsHtml('<div onclick="">hello</div>');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>');
|
expect(angular.lowercase(element.html())).toEqual('<div onclick="">hello</div>');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('when $sanitize is available', function() {
|
||||||
|
beforeEach(function() { module('ngSanitize'); });
|
||||||
|
|
||||||
|
it('should sanitize untrusted html', inject(function($rootScope, $compile) {
|
||||||
|
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
|
||||||
|
$rootScope.html = '<div onclick="">hello</div>';
|
||||||
|
$rootScope.$digest();
|
||||||
|
expect(angular.lowercase(element.html())).toEqual('<div>hello</div>');
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,22 @@ describe('SCE', function() {
|
||||||
expect(function() { $sce.getTrustedResourceUrl('open_redirect'); }).toThrow(
|
expect(function() { $sce.getTrustedResourceUrl('open_redirect'); }).toThrow(
|
||||||
'[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy. URL: open_redirect');
|
'[$sce:isecrurl] Blocked loading resource from url not allowed by $sceDelegate policy. URL: open_redirect');
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sanitizing html', function() {
|
||||||
|
describe('when $sanitize is NOT available', function() {
|
||||||
|
it('should throw an exception for getTrusted(string) values', inject(function($sce) {
|
||||||
|
expect(function() { $sce.getTrustedHtml('<b></b>'); }).toThrow(
|
||||||
|
'[$sce:unsafe] Attempting to use an unsafe value in a safe context.');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when $sanitize is available', function() {
|
||||||
|
beforeEach(function() { module('ngSanitize'); });
|
||||||
|
it('should sanitize html using $sanitize', inject(function($sce) {
|
||||||
|
expect($sce.getTrustedHtml('a<xxx><B>b</B></xxx>c')).toBe('a<b>b</b>c');
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue