diff options
| author | Igor Minar | 2013-02-19 09:55:05 -0800 | 
|---|---|---|
| committer | Igor Minar | 2013-02-20 00:40:51 -0800 | 
| commit | a8cc4497063118c766bdfa9464c9cbfc59413a81 (patch) | |
| tree | e2940be3b1ebc026a1b6a124bf53de8839fd6e60 /src/ng/compile.js | |
| parent | 2aa212b19c71df82287b4b074da3ab14cbf37348 (diff) | |
| download | angular.js-a8cc4497063118c766bdfa9464c9cbfc59413a81.tar.bz2 | |
fix($compile): sanitize values bound to a[href]
Diffstat (limited to 'src/ng/compile.js')
| -rw-r--r-- | src/ng/compile.js | 58 | 
1 files changed, 52 insertions, 6 deletions
| diff --git a/src/ng/compile.js b/src/ng/compile.js index 2ed34f73..84a9c3ca 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 @@ -1025,10 +1071,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) { | 
