diff options
Diffstat (limited to 'src/ng/compile.js')
| -rw-r--r-- | src/ng/compile.js | 60 |
1 files changed, 53 insertions, 7 deletions
diff --git a/src/ng/compile.js b/src/ng/compile.js index 788231ab..11a86f3f 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -155,7 +155,8 @@ function $CompileProvider($provide) { Suffix = 'Directive', COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, - MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: '; + MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ', + urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/; /** @@ -209,11 +210,41 @@ function $CompileProvider($provide) { }; + /** + * @ngdoc function + * @name ng.$compileProvider#urlSanitizationWhitelist + * @methodOf ng.$compileProvider + * @function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an + * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular + * expression. If a match is found the original url is written into the dom. Otherwise the + * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.urlSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + urlSanitizationWhitelist = regexp; + return this; + } + return urlSanitizationWhitelist; + }; + + this.$get = [ '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', - '$controller', '$rootScope', + '$controller', '$rootScope', '$document', function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, - $controller, $rootScope) { + $controller, $rootScope, $document) { var Attributes = function(element, attr) { this.$$element = element; @@ -235,7 +266,8 @@ function $CompileProvider($provide) { */ $set: function(key, value, writeAttr, attrName) { var booleanKey = getBooleanAttrName(this.$$element[0], key), - $$observers = this.$$observers; + $$observers = this.$$observers, + normalizedVal; if (booleanKey) { this.$$element.prop(key, value); @@ -254,6 +286,19 @@ function $CompileProvider($provide) { } } + + // sanitize a[href] values + if (nodeName_(this.$$element[0]) === 'A' && key === 'href') { + urlSanitizationNode.setAttribute('href', value); + + // href property always returns normalized absolute url, so we can match against that + normalizedVal = urlSanitizationNode.href; + if (!normalizedVal.match(urlSanitizationWhitelist)) { + this[key] = value = 'unsafe:' + normalizedVal; + } + } + + if (writeAttr !== false) { if (value === null || value === undefined) { this.$$element.removeAttr(attrName); @@ -297,7 +342,8 @@ function $CompileProvider($provide) { } }; - var startSymbol = $interpolate.startSymbol(), + var urlSanitizationNode = $document[0].createElement('a'), + startSymbol = $interpolate.startSymbol(), endSymbol = $interpolate.endSymbol(), denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') ? identity @@ -748,7 +794,7 @@ function $CompileProvider($provide) { } break; } - + case '=': { parentGet = $parse(attrs[attrName]); parentSet = parentGet.assign || function() { @@ -1029,10 +1075,10 @@ function $CompileProvider($provide) { function addAttrInterpolateDirective(node, directives, value, name) { var interpolateFn = $interpolate(value, true); - // no interpolation found -> ignore if (!interpolateFn) return; + directives.push({ priority: 100, compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { |
