With ng-bind-html-unsafe removed, how do I inject HTML?
Asked Answered
B

10

274

I'm trying to use $sanitize provider and the ng-bind-htm-unsafe directive to allow my controller to inject HTML into a DIV.

However, I can't get it to work.

<div ng-bind-html-unsafe="{{preview_data.preview.embed.html}}"></div>

I discovered that it is because it was removed from AngularJS (thanks).

But without ng-bind-html-unsafe, I get this error:

http://errors.angularjs.org/undefined/$sce/unsafe

Blanche answered 16/10, 2013 at 22:57 Comment(2)
There is a simple solution for 1.2.23+, see postTestes
see https://mcmap.net/q/110458/-how-do-you-use-sce-trustashtml-string-to-replicate-ng-bind-html-unsafe-in-angular-1-2Menhir
B
124
  1. You need to make sure that sanitize.js is loaded. For example, load it from https://ajax.googleapis.com/ajax/libs/angularjs/[LAST_VERSION]/angular-sanitize.min.js
  2. you need to include ngSanitize module on your app eg: var app = angular.module('myApp', ['ngSanitize']);
  3. you just need to bind with ng-bind-html the original html content. No need to do anything else in your controller. The parsing and conversion is automatically done by the ngBindHtml directive. (Read the How does it work section on this: $sce). So, in your case <div ng-bind-html="preview_data.preview.embed.html"></div> would do the work.
Barneybarnhart answered 5/9, 2014 at 6:31 Comment(4)
It is the cleanest option to do that safely. It came with more dependencies but it's about safety so no hesitation !Prank
Using this with ionic 1.0.0-beta.13Persona
This doesn't work with some tags, like input. Of course there's no easy way to get around this. Really frustrating.Otorhinolaryngology
Most common and secure way. Prefer this if you will plan use bind-html in diferent views.Tumer
Q
357

Instead of declaring a function in your scope, as suggested by Alex, you can convert it to a simple filter :

angular.module('myApp')
    .filter('to_trusted', ['$sce', function($sce){
        return function(text) {
            return $sce.trustAsHtml(text);
        };
    }]);

Then you can use it like this :

<div ng-bind-html="preview_data.preview.embed.html | to_trusted"></div>

And here is a working example : http://jsfiddle.net/leeroy/6j4Lg/1/

Quickie answered 21/1, 2014 at 9:53 Comment(6)
I have a small collection of useful tools for angular on github, I will include this filter in those tools if you won't mind. This is IMHO the best solution when you trust the html.Livi
@Livi No problem, but if you add a link to this answer this would be greatly appreciated. :-) stackoverflow.com/a/21254635Quickie
Very nice. this works like a charm on nested repeats!Epiphora
This seems like a MUCH better solution than coding for each controller. Just a quick filter and done! I used it with repeating table rows, simple as pie.... <td ng-bind-html="representative.primary | to_trusted"></td>Rabia
angular.module('myApp').filter('trustAsHtml', ['$sce', function($sce) { return $sce.trustAsHtml }]);Minister
Every solution that involves blessing the HTML as trusted introduces a XSS vulnerability. Please see the answer suggesting ngSanitize below (https://mcmap.net/q/108232/-with-ng-bind-html-unsafe-removed-how-do-i-inject-html) for a safer fix.Cleisthenes
G
275

You indicated that you're using Angular 1.2.0... as one of the other comments indicated, ng-bind-html-unsafe has been deprecated.

Instead, you'll want to do something like this:

<div ng-bind-html="preview_data.preview.embed.htmlSafe"></div>

In your controller, inject the $sce service, and mark the HTML as "trusted":

myApp.controller('myCtrl', ['$scope', '$sce', function($scope, $sce) {
  // ...
  $scope.preview_data.preview.embed.htmlSafe = 
     $sce.trustAsHtml(preview_data.preview.embed.html);
}

Note that you'll want to be using 1.2.0-rc3 or newer. (They fixed a bug in rc3 that prevented "watchers" from working properly on trusted HTML.)

Guberniya answered 17/10, 2013 at 2:51 Comment(5)
I tried using the above but it breaks my code. Seems you need to prepend '$scope' before the function definition -- perhaps it was "understood" at one time, but no longer. The following should work: myApp.controller('myCtrl', ['$scope', '$sce', function($scope, $sce) {Blodgett
You can look more information about $sce here just to pursue curiosity! ;)Dehydrogenase
Note that this will likely cause an XSS security issue in your code. See the answer suggesting ngSanitize below (https://mcmap.net/q/108232/-with-ng-bind-html-unsafe-removed-how-do-i-inject-html) for an alternative, safer fix.Glassman
Why this is a bad idea: docs.google.com/presentation/d/…Cuss
trustAsHtml does what it says, it trusts any incoming html code, which can result in Cross-Site Scripting (XSS) attacksSailcloth
B
124
  1. You need to make sure that sanitize.js is loaded. For example, load it from https://ajax.googleapis.com/ajax/libs/angularjs/[LAST_VERSION]/angular-sanitize.min.js
  2. you need to include ngSanitize module on your app eg: var app = angular.module('myApp', ['ngSanitize']);
  3. you just need to bind with ng-bind-html the original html content. No need to do anything else in your controller. The parsing and conversion is automatically done by the ngBindHtml directive. (Read the How does it work section on this: $sce). So, in your case <div ng-bind-html="preview_data.preview.embed.html"></div> would do the work.
Barneybarnhart answered 5/9, 2014 at 6:31 Comment(4)
It is the cleanest option to do that safely. It came with more dependencies but it's about safety so no hesitation !Prank
Using this with ionic 1.0.0-beta.13Persona
This doesn't work with some tags, like input. Of course there's no easy way to get around this. Really frustrating.Otorhinolaryngology
Most common and secure way. Prefer this if you will plan use bind-html in diferent views.Tumer
B
114

For me, the simplest and most flexible solution is:

<div ng-bind-html="to_trusted(preview_data.preview.embed.html)"></div>

And add function to your controller:

$scope.to_trusted = function(html_code) {
    return $sce.trustAsHtml(html_code);
}

Don't forget add $sce to your controller's initialization.

Baba answered 20/12, 2013 at 19:19 Comment(3)
Seems more straightforward to have the controller returned the trusted html in $scopePixilated
This can throw infinite loop on $sce, do something like: $scope.trusted = {}; $scope.to_trusted = function(html_code) { return $scope.trusted[html_code] || ($scope.trusted[html_code] = $sce.trustAsHtml(html_code)); };Celiaceliac
Every solution that involves blessing the HTML as trusted introduces a XSS vulnerability. Please see the answer suggesting ngSanitize below (https://mcmap.net/q/108232/-with-ng-bind-html-unsafe-removed-how-do-i-inject-html) for a safer fix.Cleisthenes
R
69

The best solution to this in my opinion is this:

  1. Create a custom filter which can be in a common.module.js file for example - used through out your app:

    var app = angular.module('common.module', []);
    
    // html filter (render text as html)
    app.filter('html', ['$sce', function ($sce) { 
        return function (text) {
            return $sce.trustAsHtml(text);
        };    
    }])
    
  2. Usage:

    <span ng-bind-html="yourDataValue | html"></span>
    

Now - I don't see why the directive ng-bind-html does not trustAsHtml as part of its function - seems a bit daft to me that it doesn't

Anyway - that's the way I do it - 67% of the time, it works ever time.

Ration answered 6/2, 2015 at 10:33 Comment(2)
You can use the following regex to do a find and replace: regex: ng-bind-html-unsafe="((?:(?!").)*)" replacement: ng-bind-html="($1) | html" with the above filter.Gavrilla
Every solution that involves blessing the HTML as trusted introduces a XSS vulnerability. Please see the answer suggesting ngSanitize below (https://mcmap.net/q/108232/-with-ng-bind-html-unsafe-removed-how-do-i-inject-html) for a safer fix.Cleisthenes
B
7

You can create your own simple unsafe html binding, of course if you use user input it could be a security risk.

App.directive('simpleHtml', function() {
  return function(scope, element, attr) {
    scope.$watch(attr.simpleHtml, function (value) {
      element.html(scope.$eval(attr.simpleHtml));
    })
  };
})
Bounteous answered 17/10, 2013 at 5:22 Comment(1)
Couldn't this directive also use the $sce.trustAsHtml?Chisolm
P
5

You do not need to use {{ }} inside of ng-bind-html-unsafe:

<div ng-bind-html-unsafe="preview_data.preview.embed.html"></div>

Here's an example: http://plnkr.co/edit/R7JmGIo4xcJoBc1v4iki?p=preview

The {{ }} operator is essentially just a shorthand for ng-bind, so what you were trying amounts to a binding inside a binding, which doesn't work.

Psychology answered 16/10, 2013 at 23:1 Comment(5)
However, if I remove it, I get nothing injected. And the docs are highly confusing, using a single } docs-angularjs-org-dev.appspot.com/api/…Blanche
Very odd. I've just tested it to be sure and for me it worked as expected. I agree the single { } are a bit confusing in the docs, but they're meant as a representation of an expression, not as literals in the string. I've updated my answer with a working plunk.Psychology
Also, if you're using 1.2.0 already, see the comments here as ng-bind-html-unsafe has been removed: docs.angularjs.org/api/ng.directive:ngBindHtmlPsychology
I am using 1.2. :( Grrr! How can one inject unsafe HTML? I get this error without it: errors.angularjs.org/undefined/$sce/unsafeBlanche
The {{}} operator was causing my issue with binding failing, thanks for the hint!Devy
P
2

I've had a similar problem. Still couldn't get content from my markdown files hosted on github.

After setting up a whitelist (with added github domain) to the $sceDelegateProvider in app.js it worked like a charm.

Description: Using a whitelist instead of wrapping as trusted if you load content from a different urls.

Docs: $sceDelegateProvider and ngInclude (for fetching, compiling and including external HTML fragment)

Piscatorial answered 1/12, 2013 at 4:28 Comment(0)
R
2

Strict Contextual Escaping can be disabled entirely, allowing you to inject html using ng-html-bind. This is an unsafe option, but helpful when testing.

Example from the AngularJS documentation on $sce:

angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
  // Completely disable SCE.  For demonstration purposes only!
  // Do not use in new projects.
  $sceProvider.enabled(false);
});

Attaching the above config section to your app will allow you inject html into ng-html-bind, but as the doc remarks:

SCE gives you a lot of security benefits for little coding overhead. It will be much harder to take an SCE disabled application and either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE for cases where you have a lot of existing code that was written before SCE was introduced and you're migrating them a module at a time.

Ramses answered 25/6, 2015 at 20:49 Comment(1)
Good to know about, but definitely something that should be handled with care.Stegosaur
N
2

You can use filter like this

angular.module('app').filter('trustAs', ['$sce', 
    function($sce) {
        return function (input, type) {
            if (typeof input === "string") {
                return $sce.trustAs(type || 'html', input);
            }
            console.log("trustAs filter. Error. input isn't a string");
            return "";
        };
    }
]);

usage

<div ng-bind-html="myData | trustAs"></div>

it can be used for other resource types, for example source link for iframes and other types declared here

Nisa answered 14/1, 2016 at 7:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.