2

In our Angular application, we have a link that is filled with user content on server side. So we need to tell Angular not to interpret that link content. Otherwise, if the user, or an attacker puts Angular binding expressions in there (say {{User.Password}}) then Angular would evaluate it, opening a security hole - a kind of XSS attack.

ng-non-bindable mostly does that. However, we also want the link itself to be manipulated by Angular.

<a href="" class="side-bar-element" ng-class="{ 'side-bar-element-selected': isSiteSelected(@site.value.ID) }">@site.value.Name</a>

@site.value.Name is server side code to insert the content.

If we put the ng-non-bindable on the a element, then the ng-class won't work. The only solution I can see is to insert another span/div inside it and apply the ng-non-bindable to that:

<a href="" class="side-bar-element" ng-class="{ 'side-bar-element-selected': isSiteSelected(@site.value.ID) }"><span ng-non-bindable>@site.value.Name</span></a>

This just seems clunky, having to modify the HTML structure just to stop Angular interfering with server-side data.

Is there any cleaner solution?

Ideally I would like ng-non-bindable (or a variant) to mean "don't bind the content, but treat this element as normal otherwise".

Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67
O'Rooney
  • 2,878
  • 2
  • 27
  • 41
  • Can't you get away with ng-class? As I see, you're not really evaluating any angular model in there. – Narain Mittal Feb 03 '16 at 00:02
  • Hm you are right, I didn't really explain the problem :) If the user, or an attacker puts Angular binding expressions into the user content (say {{User.Password}}) then Angular would evaluate it, opening a security hole - a kind of XSS attack. – O'Rooney Feb 03 '16 at 02:03
  • Hm I see something similar is discussed here:https://github.com/angular/angular.js/issues/5601 – O'Rooney Feb 03 '16 at 02:16
  • Yes, that's what the recommendation is, we should not mix up the client and server code. If you know that @site.vaue.id is a server content and your class really depend on that, just write a plain JS function and put it in the class attribute. Don't use the ng-class. Something like class="isSiteSelected(@site.value.ID). Hope that helps – Narain Mittal Feb 03 '16 at 18:45
  • For starters, it is wreckless to transmit user passwords to the front end in any form. But hopefully that is only a contrived example and not what is actually going on in your app. – maurice Dec 14 '16 at 22:08

2 Answers2

0

If the user, or an attacker puts Angular binding expressions into the user content (say {{User.Password}}) then Angular would evaluate it, opening a security hole - a kind of XSS attack.

Use the $delegate service to lower the ng-non-bindable directive priority:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="nonBindableExample">  
  <script>
    function delegator($delegate)
     {
     /* Override the directive definition object (DDO) */ 
     $delegate[0].priority = 0;
     
     return $delegate;
     }


    function provider($provide)
     {
     /* Decorate ng-non-bindable with a proxy function */
     $provide.decorator('ngNonBindableDirective', ["$delegate", delegator]);
     }

    /* Inject the provider and the delegator methods with services */
    provider['$inject'] = ['$provide'];
    delegator['$inject'] = ['$delegate'];

    /* Inject the module with the new provider */
    angular.module('nonBindableExample', []);
    angular.module("nonBindableExample").config(["$provide",provider]);
  </script>
  
<div>{{$id}}</div><div ng-non-bindable class="{{$id}}">{{$id}}</div></div>
</body>

When there are multiple directives defined on a single DOM element, sometimes it is necessary to specify the order in which the directives are applied. The priority is used to sort the directives before their compile functions get called. Priority is defined as a number. Directives with greater numerical priority are compiled first. Pre-link functions are also run in priority order, but post-link functions are run in reverse order. The order of directives with the same priority is undefined. The default priority is 0.

Or simply separate user content via <script type="text/ng-template">:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  <!--Declare module-->
  <script>
  angular.module('foo', []);
  </script>

<!--Auto Bootstrapping-->
<div ng-app="foo">  
  <!--Inline Template-->
  <script type="text/ng-template" id="baz">
    <span ng-non-bindable>Hi {{bar}}</span>
  </script>

  <!--data binding-->
  <a href="" ng-init="bar=1" ng-class="{{bar}}" ng-include="'baz'"></a>
</div>

Use a ng-include directive with the a element and use a span element with a ng-non-bindable directive to decouple the text from the element.

References

Community
  • 1
  • 1
Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265
0

@farahslieme

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  <!--Declare module-->
  <script>
  angular.module('foo', []);
  </script>

<!--Auto Bootstrapping-->
<div ng-app="foo">  
  <!--Inline Template-->
  <script type="text/ng-template" id="baz">
    <span ng-non-bindable>Hi {{bar}}</span>
  </script>

  <!--data binding-->
  <a href="" ng-init="bar=1" ng-class="{{bar}}" ng-include="'baz'"></a>
</div>