Reputation: 3080
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".
Upvotes: 2
Views: 4100
Reputation: 1
@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>
Upvotes: 0
Reputation: 24617
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
Upvotes: 0