29

Is there an angular JS command that will do HTML escaping on text? I am processing a custom directive and have need to escape some of the output which it generates.

Internally the AngularJS sanitzer uses a encodeEntities function, but does not expose it. I know I could duplicate the function, but it seems like there should be a standard way of doing this.


Use-Case: I have a custom directive which does language localization. This directive uses a key lookup from a data file to find language text. In some cases this text is allowed to contain HTML, and/or the directive produces HTML to improve the resulting visual formatting. In addition this directive takes Angular expressions as parameters and uses them as replacements for tokens in the strings. I need to encode these parameters as they may not be HTML safe.

The directive is called as an attribute, for example i18n-html='welcome_text_html,1+1,user.name'. The directive then formats the string as described and uses element.html to update the DOM node.

3
  • 1
    By default, when the data is bound it will be escaped automatically. Can you provide some more info on your use case? Commented Jan 22, 2013 at 20:28
  • Did this answer your question or did you need more information? Commented Feb 17, 2013 at 7:29
  • Josh, do you know where in the Documentation it states that the bound data is escaped Commented Aug 26, 2013 at 17:44

9 Answers 9

32

This answer is about escaping, not sanitizing HTML. There are two approaches:

  1. As mentioned by @maniekq, if you can work on the DOM, do:

    element.text( scope.myValue );
    
  2. From this answer, you can use this code from mustache.js and e.g. create an angular filter:

    angular.module('myModule').filter('escapeHtml', function () {
    
        var entityMap = {
            "&": "&",
            "<": "&lt;",
            ">": "&gt;",
            '"': '&quot;',
            "'": '&#39;',
            "/": '&#x2F;'
        };
    
        return function(str) {
            return String(str).replace(/[&<>"'\/]/g, function (s) {
                return entityMap[s];
            });
        }
    });
    
Sign up to request clarification or add additional context in comments.

3 Comments

Hmm. Values that you escape for use in HTML are either going to be used text content or attribute values, right? And neither of those circumstances will ever require that forward slashes be escaped? So why is the forward slash getting escaped here?
However, if you want to use this filter inside ng-bind-html, you must wrap the returned value in $sce.trustAsHtml(). Otherwise, Error: $sce:unsafe will be raised.
2

Sanitizing is one thing, but to display all characters and not "execute" HTML code I have used "text" function to set value.

In your directive, to set value, instead of writing:

element.html( scope.myValue );

write:

element.text( scope.myValue );

1 Comment

No, that will escape all of the text. My directive is assembling varous bits and doing HTML formatting on its own. I must use the html command to set the output. I was looking for a way to do escaping of the bits on their own.
2

This answer is derived from @mb21's. The only thing that is changed is utilizing $sce. So you can use this filter in ng-bind-html, without triggering Error: $sce:unsafe.

angular
  .module('yourModule', [
    'ngSanitize'
  ])
  .filter('escapeHtml', function ($sce) {
    // Modified by Rockallite: Add $sce.trustAsHtml() to mute "Error: $sce:unsafe"
    // http://stackoverflow.com/a/32835368/2293304
    // http://stackoverflow.com/a/28537958/2293304
    // https://github.com/janl/mustache.js/blob/master/mustache.js#L82
    var entityMap = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': '&quot;',
        "'": '&#39;',
        "/": '&#x2F;'
    };

    return function(str) {
      return $sce.trustAsHtml(String(str).replace(/[&<>"'\/]/g, function (s) {
          return entityMap[s];
      }));
    }
  });

Comments

1

There are two separate issues with escaping HTML. The first issue is that entities need to be encoded, and the second issue is that the result needs to be trusted so the data can be used as html bindings. Adding the following code to your controller(s) provides a solution for both issues using the $sce service.

CoffeeScript Solution:

MyApp.controller('MyController', ['$scope','$sce',($scope,$sce) ->

  ###
  ...
  ###

  $scope.html5Entities = (value) ->
    value.replace(/[\u00A0-\u9999<>\&\'\"]/gim, (i) ->
      '&#' + i.charCodeAt(0) + ';'
    )

  $scope.trustAsHtml = (value) ->
    $sce.trustAsHtml(value)

  ###
  ...
  ###

])    


Javascript Solution:

MyApp.controller('MyController', ['$scope','$sce', function($scope,$sce) {

  /* ... */

  $scope.html5Entities = function(value) {
    return value.replace(/[\u00A0-\u9999<>\&\'\"]/gim, function(i) {
          return '&#' + i.charCodeAt(0) + ';'
        })
  };

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

  /* ... */

}]);


The first function html5Entities does the actual entity encoding, while the second function trustAsHtml marks a string as safe to use in Angular for HTML bindings. Both versions require that the $sce service be included in your controller.

Example usage:

<div ng-bind-html="trustAsHtml((html5Entities(product.title) | highlight: $select.search))"></div>

See related issues:

1 Comment

Thanks, the html5Entities filter code was exactly what I've searched.
1

You can implement filter like this:

app.filter('escape', escape);

 function escape() {
    return function (html) {
      return angular.element('<pre/>').text(html).html();
    };
  }

6 Comments

Works great using the browser's built in escaping.
...and breaks new lines :(
This is a security hazard - consider html being <img src="dummy" onerror="alert('xss')">. This will run JavaScript code even though the element is never inserted into a document.
Ok then why I don't see the alert when I put that into console? e.g. angular.element('<pre/>').text("<img src=\"dummy\" onerror=\"alert('xss')\">").html(); You know why? because method .text() does the job well
@ruX, tag <pre> intended to save new lines!
|
0

It's not the straight solution, but if you'd dive into angular-sanitize code, you could find function encodeEntities. It's nice but private. Looking for usages of it you'd go to htmlSanitizeWriter, and then to sanitizeText. It's still private but used in public filter linky.

Either you can explicitly use linky and hope that no links will be found, or reimplement sanitizeText or encodeEntities int your services.

Comments

-1

There are two ways to do HTML sanitization in AngularJS. The first is by using the ngBindHtml directive and the second by using the $sanitize service.

function MyCtrl ( $scope, $sanitize ) {
  $scope.rawHtml = "<div><script></script></div>";
  $scope.sanitizedHmtl = $sanitize( $scope.rawHtml );
}

Then these two are functionally equivalent:

<div ng-bind-html="rawHtml"></div>
<div ng-bind-html-unsafe="sanitizedHtml"></div>

If used in a directive, as in your question, you can simply insert the sanitized HTML:

element.html( scope.sanitizedHtml );

But in most cases when writing directives, you'd have this in a template and use ngBindHtml, as above. But it works for the corner cases.

5 Comments

I'm just looking for a standard function to do escaping of HTML. None of the standard auto-escaping mechanisms achieve my purpose.
$sanitize takes a string and returns a sanitized string. What does it not do that you need?
santizing is not the same as escaping. Escpaing is doing transformations like < to &lt;
@edA $sanitize for as far as i am aware does exactly that, take a look at the demo at AngularJS
@BasGoossen it is not the same because it completely removes things like <script>alert('pwned')</script> instead of encoding tokens like < and > to &lt; &gt;. The documentation actually states "The input is sanitized by parsing the HTML into tokens. All safe tokens (from a trusted URI list) are then serialized back to a properly escaped HTML string." docs.angularjs.org/api/ngSanitize/service/$sanitize
-2

Use [innerHtml] shorthand tag in a HTML template file you are using in your Angular app.

My example shown below to escape HTML generated by wordpress on post_content that is retrieved from my WP API and therefore the html tags will not display in the browser:

<div [innerHtml]="post.post_content" *ngIf="post && post.post_content"></div>

Hope this helps.

1 Comment

The question is about AngularJS, not Angular.
-3

You can use encodeEntities() function in ngSanitize to escape & < > etc.

3 Comments

yes it is public (exposed as a service): docs.angularjs.org/api/ngSanitize/service/$sanitize
the function itself is not exposed through any service as being public

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.