diff options
| author | Chirayu Krishnappa | 2013-06-21 19:00:45 -0700 | 
|---|---|---|
| committer | Chirayu Krishnappa | 2013-06-24 14:17:18 -0700 | 
| commit | 38deedd6e3d806eb8262bb43f26d47245f6c2739 (patch) | |
| tree | 2307e8a84b040b32acf4fe8d325af0eb021469a1 /src/ng/interpolate.js | |
| parent | 39841f2ec9b17b3b2920fd1eb548d444251f4f56 (diff) | |
| download | angular.js-38deedd6e3d806eb8262bb43f26d47245f6c2739.tar.bz2 | |
fix($compile): reject multi-expression interpolations for src attribute
BREAKING CHANGE: Concatenating expressions makes it hard to reason about
    whether some combination of concatenated values are unsafe to use
    and could easily lead to XSS.  By requiring that a single expression
    be used for *[src/ng-src] such as iframe[src], object[src], etc.
    (but not img[src/ng-src] since that value is sanitized), we ensure that the value
    that's used is assigned or constructed by some JS code somewhere
    that is more testable or make it obvious that you bound the value to
    some user controlled value.  This helps reduce the load when
    auditing for XSS issues.
    To migrate your code, follow the example below:
        Before:
            JS:
                scope.baseUrl = 'page';
                scope.a = 1;
                scope.b = 2;
            HTML:
                <!-- Are a and b properly escaped here? Is baseUrl
                     controlled by user? -->
                <iframe src="{{baseUrl}}?a={{a}&b={{b}}">
        After:
            JS:
                var baseUrl = "page";
                scope.getIframeSrc = function() {
                  // There are obviously better ways to do this.  The
                  // key point is that one will think about this and do
                  // it the right way.
                  var qs = ["a", "b"].map(function(value, name) {
                      return encodeURIComponent(name) + "=" +
                             encodeURIComponent(value);
                    }).join("&");
                  // baseUrl isn't on scope so it isn't bound to a user
                  // controlled value.
                  return baseUrl + "?" + qs;
                }
            HTML: <iframe src="{{getIframeSrc()}}">
Diffstat (limited to 'src/ng/interpolate.js')
| -rw-r--r-- | src/ng/interpolate.js | 24 | 
1 files changed, 22 insertions, 2 deletions
| diff --git a/src/ng/interpolate.js b/src/ng/interpolate.js index 51f4630d..8e94fe24 100644 --- a/src/ng/interpolate.js +++ b/src/ng/interpolate.js @@ -1,5 +1,7 @@  'use strict'; +var $interpolateMinErr = minErr('$interpolate'); +  /**   * @ngdoc object   * @name ng.$interpolateProvider @@ -82,6 +84,12 @@ function $InterpolateProvider() {       * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have       *    embedded expression in order to return an interpolation function. Strings with no       *    embedded expression will return null for the interpolation function. +     * @param {boolean=} isTrustedContext when true, requires that the interpolation string does not +     *    contain any concatenations - i.e. the interpolation string is a single expression. +     *    Interpolations for *[src] and *[ng-src] (except IMG, since itwhich sanitizes its value) +     *    pass true for this parameter.  This helps avoid hunting through the template code to +     *    figure out of some iframe[src], object[src], etc. was interpolated with a concatenation +     *    that ended up introducing a XSS.       * @returns {function(context)} an interpolation function which is used to compute the interpolated       *    string. The function has these parameters:       * @@ -89,7 +97,7 @@ function $InterpolateProvider() {       *      against.       *       */ -    function $interpolate(text, mustHaveExpression) { +    function $interpolate(text, mustHaveExpression, isTrustedContext) {        var startIndex,            endIndex,            index = 0, @@ -121,6 +129,18 @@ function $InterpolateProvider() {          length = 1;        } +      // Concatenating expressions makes it hard to reason about whether some combination of concatenated +      // values are unsafe to use and could easily lead to XSS.  By requiring that a single +      // expression be used for iframe[src], object[src], etc., we ensure that the value that's used +      // is assigned or constructed by some JS code somewhere that is more testable or make it +      // obvious that you bound the value to some user controlled value.  This helps reduce the load +      // when auditing for XSS issues. +      if (isTrustedContext && parts.length > 1) { +          throw $interpolateMinErr('noconcat', +              "Error while interpolating: {0}\nYou may not use multiple expressions when " + +              "interpolating this expression.", text); +      } +        if (!mustHaveExpression  || hasInterpolation) {          concat.length = length;          fn = function(context) { @@ -139,7 +159,7 @@ function $InterpolateProvider() {              return concat.join('');            }            catch(err) { -            var newErr = minErr('$interpolate')('interr', "Can't interpolate: {0}\n{1}", text, err.toString()); +            var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());              $exceptionHandler(newErr);            }          }; | 
