mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
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.
950 lines
39 KiB
JavaScript
950 lines
39 KiB
JavaScript
'use strict';
|
||
|
||
var $sceMinErr = minErr('$sce');
|
||
|
||
var SCE_CONTEXTS = {
|
||
HTML: 'html',
|
||
CSS: 'css',
|
||
URL: 'url',
|
||
// RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
|
||
// url. (e.g. ng-include, script src, templateUrl)
|
||
RESOURCE_URL: 'resourceUrl',
|
||
JS: 'js'
|
||
};
|
||
|
||
|
||
/**
|
||
* @ngdoc service
|
||
* @name ng.$sceDelegate
|
||
* @function
|
||
*
|
||
* @description
|
||
*
|
||
* `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
|
||
* Contextual Escaping (SCE)} services to AngularJS.
|
||
*
|
||
* Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
|
||
* the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
|
||
* because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
|
||
* override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
|
||
* work because `$sce` delegates to `$sceDelegate` for these operations.
|
||
*
|
||
* Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
|
||
*
|
||
* The default instance of `$sceDelegate` should work out of the box with little pain. While you
|
||
* can override it completely to change the behavior of `$sce`, the common case would
|
||
* involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
|
||
* your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
|
||
* templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
|
||
* $sceDelegateProvider.resourceUrlWhitelist} and {@link
|
||
* ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc object
|
||
* @name ng.$sceDelegateProvider
|
||
* @description
|
||
*
|
||
* The $sceDelegateProvider provider allows developers to configure the {@link ng.$sceDelegate
|
||
* $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
|
||
* that URLs used for sourcing Angular templates are safe. Refer {@link
|
||
* ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
|
||
* {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
|
||
*
|
||
* Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
|
||
*/
|
||
|
||
function $SceDelegateProvider() {
|
||
this.SCE_CONTEXTS = SCE_CONTEXTS;
|
||
|
||
// Resource URLs can also be trusted by policy.
|
||
var resourceUrlWhitelist = ['self'],
|
||
resourceUrlBlacklist = [];
|
||
|
||
/**
|
||
* @ngdoc function
|
||
* @name ng.sceDelegateProvider#resourceUrlWhitelist
|
||
* @methodOf ng.$sceDelegateProvider
|
||
* @function
|
||
*
|
||
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
|
||
* provided. This must be an array.
|
||
*
|
||
* Each element of this array must either be a regex or the special string `'self'`.
|
||
*
|
||
* When a regex is used, it is matched against the normalized / absolute URL of the resource
|
||
* being tested.
|
||
*
|
||
* The **special string** `'self'` can be used to match against all URLs of the same domain as the
|
||
* application document with the same protocol (allows sourcing https resources from http documents.)
|
||
*
|
||
* Please note that **an empty whitelist array will block all URLs**!
|
||
*
|
||
* @return {Array} the currently set whitelist array.
|
||
*
|
||
* The **default value** when no whitelist has been explicitly set is `['self']`.
|
||
*
|
||
* @description
|
||
* Sets/Gets the whitelist of trusted resource URLs.
|
||
*/
|
||
this.resourceUrlWhitelist = function (value) {
|
||
if (arguments.length) {
|
||
resourceUrlWhitelist = value;
|
||
}
|
||
return resourceUrlWhitelist;
|
||
};
|
||
|
||
/**
|
||
* @ngdoc function
|
||
* @name ng.sceDelegateProvider#resourceUrlBlacklist
|
||
* @methodOf ng.$sceDelegateProvider
|
||
* @function
|
||
*
|
||
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
|
||
* provided. This must be an array.
|
||
*
|
||
* Each element of this array must either be a regex or the special string `'self'` (see
|
||
* `resourceUrlWhitelist` for meaning - it's only really useful there.)
|
||
*
|
||
* When a regex is used, it is matched against the normalized / absolute URL of the resource
|
||
* being tested.
|
||
*
|
||
* The typical usage for the blacklist is to **block [open redirects](http://cwe.mitre.org/data/definitions/601.html)**
|
||
* served by your domain as these would otherwise be trusted but actually return content from the redirected
|
||
* domain.
|
||
*
|
||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||
*
|
||
* @return {Array} the currently set blacklist array.
|
||
*
|
||
* The **default value** when no whitelist has been explicitly set is the empty array (i.e. there is
|
||
* no blacklist.)
|
||
*
|
||
* @description
|
||
* Sets/Gets the blacklist of trusted resource URLs.
|
||
*/
|
||
|
||
this.resourceUrlBlacklist = function (value) {
|
||
if (arguments.length) {
|
||
resourceUrlBlacklist = value;
|
||
}
|
||
return resourceUrlBlacklist;
|
||
};
|
||
|
||
// Helper functions for matching resource urls by policy.
|
||
function isCompatibleProtocol(documentProtocol, resourceProtocol) {
|
||
return ((documentProtocol === resourceProtocol) ||
|
||
(documentProtocol === "http:" && resourceProtocol === "https:"));
|
||
}
|
||
|
||
this.$get = ['$log', '$document', '$injector', '$$urlUtils', function(
|
||
$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) {
|
||
if (matcher === 'self') {
|
||
return $$urlUtils.isSameOrigin(parsedUrl);
|
||
} else {
|
||
return !!parsedUrl.href.match(matcher);
|
||
}
|
||
}
|
||
|
||
function isResourceUrlAllowedByPolicy(url) {
|
||
var parsedUrl = $$urlUtils.resolve(url.toString(), true);
|
||
var i, n, allowed = false;
|
||
// Ensure that at least one item from the whitelist allows this url.
|
||
for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
|
||
if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
|
||
allowed = true;
|
||
break;
|
||
}
|
||
}
|
||
if (allowed) {
|
||
// Ensure that no item from the blacklist blocked this url.
|
||
for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
|
||
if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
|
||
allowed = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return allowed;
|
||
}
|
||
|
||
function generateHolderType(base) {
|
||
var holderType = function TrustedValueHolderType(trustedValue) {
|
||
this.$$unwrapTrustedValue = function() {
|
||
return trustedValue;
|
||
};
|
||
};
|
||
if (base) {
|
||
holderType.prototype = new base();
|
||
}
|
||
holderType.prototype.valueOf = function sceValueOf() {
|
||
return this.$$unwrapTrustedValue();
|
||
}
|
||
holderType.prototype.toString = function sceToString() {
|
||
return this.$$unwrapTrustedValue().toString();
|
||
}
|
||
return holderType;
|
||
}
|
||
|
||
var trustedValueHolderBase = generateHolderType(),
|
||
byType = {};
|
||
|
||
byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
|
||
byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
|
||
byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
|
||
byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
|
||
byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sceDelegate#trustAs
|
||
* @methodOf ng.$sceDelegate
|
||
*
|
||
* @description
|
||
* Returns an object that is trusted by angular for use in specified strict
|
||
* contextual escaping contexts (such as ng-html-bind-unsafe, ng-include, any src
|
||
* attribute interpolation, any dom event binding attribute interpolation
|
||
* such as for onclick, etc.) that uses the provided value.
|
||
* See {@link ng.$sce $sce} for enabling strict contextual escaping.
|
||
*
|
||
* @param {string} type The kind of context in which this value is safe for use. e.g. url,
|
||
* resourceUrl, html, js and css.
|
||
* @param {*} value The value that that should be considered trusted/safe.
|
||
* @returns {*} A value that can be used to stand in for the provided `value` in places
|
||
* where Angular expects a $sce.trustAs() return value.
|
||
*/
|
||
function trustAs(type, trustedValue) {
|
||
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
|
||
if (!constructor) {
|
||
throw $sceMinErr('icontext', 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
|
||
type, trustedValue);
|
||
}
|
||
if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
|
||
return trustedValue;
|
||
}
|
||
// All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
|
||
// mutable objects, we ensure here that the value passed in is actually a string.
|
||
if (typeof trustedValue !== 'string') {
|
||
throw $sceMinErr('itype',
|
||
'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
|
||
type);
|
||
}
|
||
return new constructor(trustedValue);
|
||
}
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sceDelegate#valueOf
|
||
* @methodOf ng.$sceDelegate
|
||
*
|
||
* @description
|
||
* If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
|
||
* `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
|
||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
|
||
*
|
||
* If the passed parameter is not a value that had been returned by {@link
|
||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
|
||
*
|
||
* @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
|
||
* call or anything else.
|
||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
|
||
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns `value`
|
||
* unchanged.
|
||
*/
|
||
function valueOf(maybeTrusted) {
|
||
if (maybeTrusted instanceof trustedValueHolderBase) {
|
||
return maybeTrusted.$$unwrapTrustedValue();
|
||
} else {
|
||
return maybeTrusted;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sceDelegate#getTrusted
|
||
* @methodOf ng.$sceDelegate
|
||
*
|
||
* @description
|
||
* Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and returns the
|
||
* originally supplied value if the queried context type is a supertype of the created type. If
|
||
* this condition isn't satisfied, throws an exception.
|
||
*
|
||
* @param {string} type The kind of context in which this value is to be used.
|
||
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
|
||
* `$sceDelegate.trustAs`} call.
|
||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
|
||
* `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
|
||
*/
|
||
function getTrusted(type, maybeTrusted) {
|
||
if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
|
||
return maybeTrusted;
|
||
}
|
||
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
|
||
if (constructor && maybeTrusted instanceof constructor) {
|
||
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 (isResourceUrlAllowedByPolicy(maybeTrusted)) {
|
||
return maybeTrusted;
|
||
} else {
|
||
throw $sceMinErr('isecrurl',
|
||
'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', maybeTrusted.toString());
|
||
return;
|
||
}
|
||
} else if (type === SCE_CONTEXTS.HTML) {
|
||
return htmlSanitizer(maybeTrusted);
|
||
}
|
||
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
||
}
|
||
|
||
return { trustAs: trustAs,
|
||
getTrusted: getTrusted,
|
||
valueOf: valueOf };
|
||
}];
|
||
}
|
||
|
||
|
||
/**
|
||
* @ngdoc object
|
||
* @name ng.$sceProvider
|
||
* @description
|
||
*
|
||
* The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
|
||
* - enable/disable Strict Contextual Escaping (SCE) in a module
|
||
* - override the default implementation with a custom delegate
|
||
*
|
||
* Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc service
|
||
* @name ng.$sce
|
||
* @function
|
||
*
|
||
* @description
|
||
*
|
||
* `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
|
||
*
|
||
* # Strict Contextual Escaping
|
||
*
|
||
* 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
|
||
* a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer to these
|
||
* contexts as privileged or SCE contexts.
|
||
*
|
||
* As of version 1.2, Angular ships with SCE enabled by default.
|
||
*
|
||
* Note: When enabled (the default), IE8 in quirks mode is not supported. In this mode, IE8 allows
|
||
* one to execute arbitrary javascript by the use of the expression() syntax. Refer
|
||
* <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
|
||
* You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
|
||
* to the top of your HTML document.
|
||
*
|
||
* SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
|
||
* security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
|
||
*
|
||
* Here's an example of a binding in a privileged context:
|
||
*
|
||
* <pre class="prettyprint">
|
||
* <input ng-model="userHtml">
|
||
* <div ng-bind-html="{{userHtml}}">
|
||
* </pre>
|
||
*
|
||
* 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.
|
||
* 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
|
||
* security vulnerabilities.)
|
||
*
|
||
* For the case of HTML, you might use a library, either on the client side, or on the server side,
|
||
* to sanitize unsafe HTML before binding to the value and rendering it in the document.
|
||
*
|
||
* How would you ensure that every place that used these types of bindings was bound to a value that
|
||
* was sanitized by your library (or returned as safe for rendering by your server?) How can you
|
||
* ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
|
||
* properties/fields and forgot to update the binding to the sanitized value?
|
||
*
|
||
* To be secure by default, you want to ensure that any such bindings are disallowed unless you can
|
||
* determine that something explicitly says it's safe to use a value for binding in that
|
||
* context. You can then audit your code (a simple grep would do) to ensure that this is only done
|
||
* for those values that you can easily tell are safe - because they were received from your server,
|
||
* sanitized by your library, etc. You can organize your codebase to help with this - perhaps
|
||
* allowing only the files in a specific directory to do this. Ensuring that the internal API
|
||
* exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
|
||
*
|
||
* In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} (and shorthand
|
||
* methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to obtain values that will be
|
||
* accepted by SCE / privileged contexts.
|
||
*
|
||
*
|
||
* ## How does it work?
|
||
*
|
||
* In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
|
||
* $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
|
||
* 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.
|
||
*
|
||
* As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
|
||
* ng.$sce#parseHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
|
||
* simplified):
|
||
*
|
||
* <pre class="prettyprint">
|
||
* var ngBindHtmlDirective = ['$sce', function($sce) {
|
||
* return function(scope, element, attr) {
|
||
* scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
|
||
* element.html(value || '');
|
||
* });
|
||
* };
|
||
* }];
|
||
* </pre>
|
||
*
|
||
* ## Impact on loading templates
|
||
*
|
||
* This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
|
||
* `templateUrl`'s specified by {@link guide/directive directives}.
|
||
*
|
||
* By default, Angular only loads templates from the same domain and protocol as the application
|
||
* document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
|
||
* $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
|
||
* protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
|
||
* them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
|
||
*
|
||
* *Please note*:
|
||
* The browser's
|
||
* {@link https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest
|
||
* Same Origin Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing (CORS)}
|
||
* policy apply in addition to this and may further restrict whether the template is successfully
|
||
* loaded. This means that without the right CORS policy, loading templates from a different domain
|
||
* won't work on all browsers. Also, loading templates from `file://` URL does not work on some
|
||
* browsers.
|
||
*
|
||
* ## This feels like too much overhead for the developer?
|
||
*
|
||
* It's important to remember that SCE only applies to interpolation expressions.
|
||
*
|
||
* If your expressions are constant literals, they're automatically trusted and you don't need to
|
||
* call `$sce.trustAs` on them. (e.g.
|
||
* `<div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div>`) just works.
|
||
*
|
||
* Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
||
* through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
|
||
*
|
||
* The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
|
||
* templates in `ng-include` from your application's domain without having to even know about SCE.
|
||
* It blocks loading templates from other domains or loading templates over http from an https
|
||
* served document. You can change these by setting your own custom {@link
|
||
* ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
|
||
* ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
|
||
*
|
||
* This significantly reduces the overhead. It is far easier to pay the small overhead and have an
|
||
* application that's secure and can be audited to verify that with much more ease than bolting
|
||
* security onto an application later.
|
||
*
|
||
* ## What trusted context types are supported?<a name="contexts"></a>
|
||
*
|
||
* | Context | Notes |
|
||
* |=====================|================|
|
||
* | `$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.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.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
|
||
*
|
||
* ## Show me an example.
|
||
*
|
||
*
|
||
*
|
||
* @example
|
||
<example module="mySceApp">
|
||
<file name="index.html">
|
||
<div ng-controller="myAppController as myCtrl">
|
||
<i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
|
||
<b>User comments</b><br>
|
||
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.
|
||
<div class="well">
|
||
<div ng-repeat="userComment in myCtrl.userComments">
|
||
<b>{{userComment.name}}</b>:
|
||
<span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
|
||
<br>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</file>
|
||
|
||
<file name="script.js">
|
||
var mySceApp = angular.module('mySceApp', ['ngSanitize']);
|
||
|
||
mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
|
||
var self = this;
|
||
$http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
|
||
self.userComments = userComments;
|
||
});
|
||
self.explicitlyTrustedHtml = $sce.trustAsHtml(
|
||
'<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
|
||
'sanitization."">Hover over this text.</span>');
|
||
});
|
||
</file>
|
||
|
||
<file name="test_data.json">
|
||
[
|
||
{ "name": "Alice",
|
||
"htmlComment": "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
|
||
},
|
||
{ "name": "Bob",
|
||
"htmlComment": "<i>Yes!</i> Am I the only other one?"
|
||
}
|
||
]
|
||
</file>
|
||
|
||
<file name="scenario.js">
|
||
describe('SCE doc demo', function() {
|
||
it('should sanitize untrusted values', function() {
|
||
expect(element('.htmlComment').html()).toBe('<span>Is <i>anyone</i> reading this?</span>');
|
||
});
|
||
it('should NOT sanitize explicitly trusted values', function() {
|
||
expect(element('#explicitlyTrustedHtml').html()).toBe(
|
||
'<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
|
||
'sanitization."">Hover over this text.</span>');
|
||
});
|
||
});
|
||
</file>
|
||
</example>
|
||
*
|
||
*
|
||
*
|
||
* ## Can I disable SCE completely?
|
||
*
|
||
* Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
|
||
* for little coding overhead. It will be much harder to take an SCE disabled application and
|
||
* either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
|
||
* for cases where you have a lot of existing code that was written before SCE was introduced and
|
||
* you're migrating them a module at a time.
|
||
*
|
||
* That said, here's how you can completely disable SCE:
|
||
*
|
||
* <pre class="prettyprint">
|
||
* angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
|
||
* // Completely disable SCE. For demonstration purposes only!
|
||
* // Do not use in new projects.
|
||
* $sceProvider.enabled(false);
|
||
* });
|
||
* </pre>
|
||
*
|
||
*/
|
||
|
||
function $SceProvider() {
|
||
var enabled = true;
|
||
|
||
/**
|
||
* @ngdoc function
|
||
* @name ng.sceProvider#enabled
|
||
* @methodOf ng.$sceProvider
|
||
* @function
|
||
*
|
||
* @param {boolean=} value If provided, then enables/disables SCE.
|
||
* @return {boolean} true if SCE is enabled, false otherwise.
|
||
*
|
||
* @description
|
||
* Enables/disables SCE and returns the current value.
|
||
*/
|
||
this.enabled = function (value) {
|
||
if (arguments.length) {
|
||
enabled = !!value;
|
||
}
|
||
return enabled;
|
||
};
|
||
|
||
|
||
/* Design notes on the default implementation for SCE.
|
||
*
|
||
* The API contract for the SCE delegate
|
||
* -------------------------------------
|
||
* The SCE delegate object must provide the following 3 methods:
|
||
*
|
||
* - trustAs(contextEnum, value)
|
||
* This method is used to tell the SCE service that the provided value is OK to use in the
|
||
* contexts specified by contextEnum. It must return an object that will be accepted by
|
||
* getTrusted() for a compatible contextEnum and return this value.
|
||
*
|
||
* - valueOf(value)
|
||
* For values that were not produced by trustAs(), return them as is. For values that were
|
||
* produced by trustAs(), return the corresponding input value to trustAs. Basically, if
|
||
* trustAs is wrapping the given values into some type, this operation unwraps it when given
|
||
* such a value.
|
||
*
|
||
* - getTrusted(contextEnum, value)
|
||
* This function should return the a value that is safe to use in the context specified by
|
||
* contextEnum or throw and exception otherwise.
|
||
*
|
||
* NOTE: This contract deliberately does NOT state that values returned by trustAs() must be opaque
|
||
* or wrapped in some holder object. That happens to be an implementation detail. For instance,
|
||
* an implementation could maintain a registry of all trusted objects by context. In such a case,
|
||
* trustAs() would return the same object that was passed in. getTrusted() would return the same
|
||
* object passed in if it was found in the registry under a compatible context or throw an
|
||
* exception otherwise. An implementation might only wrap values some of the time based on
|
||
* some criteria. getTrusted() might return a value and not throw an exception for special
|
||
* constants or objects even if not wrapped. All such implementations fulfill this contract.
|
||
*
|
||
*
|
||
* A note on the inheritance model for SCE contexts
|
||
* ------------------------------------------------
|
||
* I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
|
||
* is purely an implementation details.
|
||
*
|
||
* The contract is simply this:
|
||
*
|
||
* getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
|
||
* will also succeed.
|
||
*
|
||
* Inheritance happens to capture this in a natural way. In some future, we
|
||
* may not use inheritance anymore. That is OK because no code outside of
|
||
* sce.js and sceSpecs.js would need to be aware of this detail.
|
||
*/
|
||
|
||
this.$get = ['$parse', '$document', '$sceDelegate', function(
|
||
$parse, $document, $sceDelegate) {
|
||
// Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows
|
||
// the "expression(javascript expression)" syntax which is insecure.
|
||
if (enabled && msie) {
|
||
var documentMode = $document[0].documentMode;
|
||
if (documentMode !== undefined && documentMode < 8) {
|
||
throw $sceMinErr('iequirks',
|
||
'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
|
||
'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
|
||
'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
|
||
}
|
||
}
|
||
|
||
var sce = copy(SCE_CONTEXTS);
|
||
|
||
/**
|
||
* @ngdoc function
|
||
* @name ng.sce#isEnabled
|
||
* @methodOf ng.$sce
|
||
* @function
|
||
*
|
||
* @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
|
||
* have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
|
||
*
|
||
* @description
|
||
* Returns a boolean indicating if SCE is enabled.
|
||
*/
|
||
sce.isEnabled = function () {
|
||
return enabled;
|
||
};
|
||
sce.trustAs = $sceDelegate.trustAs;
|
||
sce.getTrusted = $sceDelegate.getTrusted;
|
||
sce.valueOf = $sceDelegate.valueOf;
|
||
|
||
if (!enabled) {
|
||
sce.trustAs = sce.getTrusted = function(type, value) { return value; },
|
||
sce.valueOf = identity
|
||
}
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#parse
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Converts Angular {@link guide/expression expression} into a function. This is like {@link
|
||
* ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
|
||
* wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
|
||
* *result*)}
|
||
*
|
||
* @param {string} type The kind of SCE context in which this result will be used.
|
||
* @param {string} expression String expression to compile.
|
||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||
*
|
||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||
* are evaluated against (typically a scope object).
|
||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||
* `context`.
|
||
*/
|
||
sce.parseAs = function sceParseAs(type, expr) {
|
||
var parsed = $parse(expr);
|
||
if (parsed.literal && parsed.constant) {
|
||
return parsed;
|
||
} else {
|
||
return function sceParseAsTrusted(self, locals) {
|
||
return sce.getTrusted(type, parsed(self, locals));
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#trustAs
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns an object
|
||
* that is trusted by angular for use in specified strict contextual escaping contexts (such as
|
||
* ng-html-bind-unsafe, ng-include, any src attribute interpolation, any dom event binding
|
||
* attribute interpolation such as for onclick, etc.) that uses the provided value. See *
|
||
* {@link ng.$sce $sce} for enabling strict contextual escaping.
|
||
*
|
||
* @param {string} type The kind of context in which this value is safe for use. e.g. url,
|
||
* resource_url, html, js and css.
|
||
* @param {*} value The value that that should be considered trusted/safe.
|
||
* @returns {*} A value that can be used to stand in for the provided `value` in places
|
||
* where Angular expects a $sce.trustAs() return value.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#trustAsHtml
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.trustAsHtml(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
|
||
*
|
||
* @param {*} value The value to trustAs.
|
||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
|
||
* $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
|
||
* only accept expressions that are either literal constants or are the
|
||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#trustAsUrl
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.trustAsUrl(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
|
||
*
|
||
* @param {*} value The value to trustAs.
|
||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
|
||
* $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
|
||
* only accept expressions that are either literal constants or are the
|
||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#trustAsResourceUrl
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.trustAsResourceUrl(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
|
||
*
|
||
* @param {*} value The value to trustAs.
|
||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
|
||
* $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
|
||
* only accept expressions that are either literal constants or are the return
|
||
* value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#trustAsJs
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.trustAsJs(value)` → {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
|
||
*
|
||
* @param {*} value The value to trustAs.
|
||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
|
||
* $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
|
||
* only accept expressions that are either literal constants or are the
|
||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#getTrusted
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, takes
|
||
* the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the originally supplied
|
||
* value if the queried context type is a supertype of the created type. If this condition
|
||
* isn't satisfied, throws an exception.
|
||
*
|
||
* @param {string} type The kind of context in which this value is to be used.
|
||
* @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} call.
|
||
* @returns {*} The value the was originally provided to {@link ng.$sce#trustAs `$sce.trustAs`} if
|
||
* valid in this context. Otherwise, throws an exception.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#getTrustedHtml
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.getTrustedHtml(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
|
||
*
|
||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||
* @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#getTrustedCss
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.getTrustedCss(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
|
||
*
|
||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||
* @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#getTrustedUrl
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.getTrustedUrl(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
|
||
*
|
||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||
* @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#getTrustedResourceUrl
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.getTrustedResourceUrl(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
|
||
*
|
||
* @param {*} value The value to pass to `$sceDelegate.getTrusted`.
|
||
* @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#getTrustedJs
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.getTrustedJs(value)` → {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
|
||
*
|
||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||
* @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#parseAsHtml
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.parseAsHtml(expression string)` → {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`}
|
||
*
|
||
* @param {string} expression String expression to compile.
|
||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||
*
|
||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||
* are evaluated against (typically a scope object).
|
||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||
* `context`.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#parseAsCss
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.parseAsCss(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`}
|
||
*
|
||
* @param {string} expression String expression to compile.
|
||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||
*
|
||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||
* are evaluated against (typically a scope object).
|
||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||
* `context`.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#parseAsUrl
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.parseAsUrl(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`}
|
||
*
|
||
* @param {string} expression String expression to compile.
|
||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||
*
|
||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||
* are evaluated against (typically a scope object).
|
||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||
* `context`.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#parseAsResourceUrl
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.parseAsResourceUrl(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`}
|
||
*
|
||
* @param {string} expression String expression to compile.
|
||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||
*
|
||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||
* are evaluated against (typically a scope object).
|
||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||
* `context`.
|
||
*/
|
||
|
||
/**
|
||
* @ngdoc method
|
||
* @name ng.$sce#parseAsJs
|
||
* @methodOf ng.$sce
|
||
*
|
||
* @description
|
||
* Shorthand method. `$sce.parseAsJs(value)` → {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`}
|
||
*
|
||
* @param {string} expression String expression to compile.
|
||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||
*
|
||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||
* are evaluated against (typically a scope object).
|
||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||
* `context`.
|
||
*/
|
||
|
||
// Shorthand delegations.
|
||
var parse = sce.parseAs,
|
||
getTrusted = sce.getTrusted,
|
||
trustAs = sce.trustAs;
|
||
|
||
angular.forEach(SCE_CONTEXTS, function (enumValue, name) {
|
||
var lName = lowercase(name);
|
||
sce[camelCase("parse_as_" + lName)] = function (expr) {
|
||
return parse(enumValue, expr);
|
||
}
|
||
sce[camelCase("get_trusted_" + lName)] = function (value) {
|
||
return getTrusted(enumValue, value);
|
||
}
|
||
sce[camelCase("trust_as_" + lName)] = function (value) {
|
||
return trustAs(enumValue, value);
|
||
}
|
||
});
|
||
|
||
return sce;
|
||
}];
|
||
}
|