diff --git a/src/ngSanitize/sanitize.js b/src/ngSanitize/sanitize.js index 70797a1c9267..8cf7b9e2b474 100644 --- a/src/ngSanitize/sanitize.js +++ b/src/ngSanitize/sanitize.js @@ -34,13 +34,17 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize'); * @kind function * * @description + * Sanitizes an html string by stripping all potentially dangerous tokens. + * * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are * then serialized back to properly escaped html string. This means that no unsafe input can make - * it into the returned string, however, since our parser is more strict than a typical browser - * parser, it's possible that some obscure input, which would be recognized as valid HTML by a - * browser, won't make it through the sanitizer. The input may also contain SVG markup. - * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and - * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}. + * it into the returned string. + * + * The whitelist for URL sanitization of attribute values is configured using the functions + * `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider + * `$compileProvider`}. + * + * The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}. * * @param {string} html HTML input. * @returns {string} Sanitized HTML. @@ -126,8 +130,22 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize'); */ + + +/** + * @ngdoc provider + * @name $sanitizeProvider + * + * @description + * Creates and configures {@link $sanitize} instance. + */ function $SanitizeProvider() { + var svgEnabled = false; + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + if (svgEnabled) { + angular.extend(validElements, svgElements); + } return function(html) { var buf = []; htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { @@ -136,6 +154,46 @@ function $SanitizeProvider() { return buf.join(''); }; }]; + + + /** + * @ngdoc method + * @name $sanitizeProvider#enableSvg + * @kind function + * + * @description + * Enables a subset of svg to be supported by the sanitizer. + * + *
+ *

By enabling this setting without taking other precautions, you might expose your + * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned + * outside of the containing element and be rendered over other elements on the page (e.g. a login + * link). Such behavior can then result in phishing incidents.

+ * + *

To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg + * tags within the sanitized content:

+ * + *
+ * + *

+   *   .rootOfTheIncludedContent svg {
+   *     overflow: hidden !important;
+   *   }
+   *   
+ *
+ * + * @param {boolean=} regexp New regexp to whitelist urls with. + * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called + * without an argument or self for chaining otherwise. + */ + this.enableSvg = function(enableSvg) { + if (angular.isDefined(enableSvg)) { + svgEnabled = enableSvg; + return this; + } else { + return svgEnabled; + } + }; } function sanitizeText(chars) { @@ -193,8 +251,7 @@ var validElements = angular.extend({}, voidElements, blockElements, inlineElements, - optionalEndTagElements, - svgElements); + optionalEndTagElements); //Attributes that have href and hence need to be sanitized var uriAttrs = toMap("background,cite,href,longdesc,src,usemap,xlink:href"); diff --git a/test/ngSanitize/filter/linkySpec.js b/test/ngSanitize/filter/linkySpec.js index f0c4f3956e54..11f676a8769e 100644 --- a/test/ngSanitize/filter/linkySpec.js +++ b/test/ngSanitize/filter/linkySpec.js @@ -50,8 +50,10 @@ describe('linky', function() { it('should handle target:', function() { expect(linky("http://example.com", "_blank")). - toEqual('http://example.com'); + toBeOneOf('http://example.com', + 'http://example.com'); expect(linky("http://example.com", "someNamedIFrame")). - toEqual('http://example.com'); + toBeOneOf('http://example.com', + 'http://example.com'); }); }); diff --git a/test/ngSanitize/sanitizeSpec.js b/test/ngSanitize/sanitizeSpec.js index 0f25caa53229..e6bda530dddc 100644 --- a/test/ngSanitize/sanitizeSpec.js +++ b/test/ngSanitize/sanitizeSpec.js @@ -100,7 +100,8 @@ describe('HTML', function() { // THESE TESTS ARE EXECUTED WITH COMPILED ANGULAR it('should echo html', function() { expectHTML('helloworld.'). - toEqual('helloworld.'); + toBeOneOf('helloworld.', + 'helloworld.'); }); it('should remove script', function() { @@ -180,7 +181,8 @@ describe('HTML', function() { it('should ignore back slash as escape', function() { expectHTML('xxx\\'). - toEqual('xxx\\'); + toBeOneOf('xxx\\', + 'xxx\\'); }); it('should ignore object attributes', function() { @@ -214,42 +216,64 @@ describe('HTML', function() { expectHTML(false).toBe('false'); }); - it('should accept SVG tags', function() { - expectHTML('') - .toEqual(''); + it('should strip svg elements if not enabled via provider', function() { + expectHTML('') + .toEqual(''); }); - it('should not ignore white-listed svg camelCased attributes', function() { - expectHTML('') + + describe('SVG support', function() { + + beforeEach(module(function($sanitizeProvider) { + $sanitizeProvider.enableSvg(true); + })); + + + it('should accept SVG tags', function() { + expectHTML('') + .toBeOneOf('', + '', + ''); + }); + + it('should not ignore white-listed svg camelCased attributes', function() { + expectHTML('') .toEqual(''); - }); + }); - it('should sanitize SVG xlink:href attribute values', function() { - expectHTML('') - .toEqual(''); + it('should sanitize SVG xlink:href attribute values', function() { + expectHTML('') + .toBeOneOf('', + ''); - expectHTML('') - .toEqual(''); - }); + expectHTML('') + .toBeOneOf('', + ''); + }); - it('should sanitize unknown namespaced SVG attributes', function() { - expectHTML('') - .toEqual(''); + it('should sanitize unknown namespaced SVG attributes', function() { + expectHTML('') + .toBeOneOf('', + ''); - expectHTML('') - .toEqual(''); - }); + expectHTML('') + .toBeOneOf('', + ''); + }); - it('should not accept SVG animation tags', function() { - expectHTML('Click me') - .toEqual('Click me'); + it('should not accept SVG animation tags', function() { + expectHTML('Click me') + .toEqual('Click me'); - expectHTML('' + - '') - .toEqual(''); + expectHTML('' + + '') + .toBeOneOf('', + ''); + }); }); + describe('htmlSanitizerWriter', function() { /* global htmlSanitizeWriter: false */ if (angular.isUndefined(window.htmlSanitizeWriter)) return;