28

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.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267

9 Answers9

31

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];
            });
        }
    });
    
Community
  • 1
  • 1
mb21
  • 34,845
  • 8
  • 116
  • 142
  • 1
    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? – Mark Amery Mar 29 '15 at 11:54
  • 2
    the slash is [recommended by OWASP](https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet#RULE_.234_-_CSS_Escape_And_Strictly_Validate_Before_Inserting_Untrusted_Data_into_HTML_Style_Property_Values) – mb21 Mar 29 '15 at 13:30
  • 2
    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. – Rockallite Sep 29 '15 at 03:42
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 );
jozala
  • 4,625
  • 2
  • 18
  • 15
  • 3
    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. – edA-qa mort-ora-y Jul 29 '14 at 03:04
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];
      }));
    }
  });
Rockallite
  • 16,437
  • 7
  • 54
  • 48
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:

Community
  • 1
  • 1
Ralph Ritoch
  • 3,260
  • 27
  • 37
1

You can implement filter like this:

app.filter('escape', escape);

 function escape() {
    return function (html) {
      return angular.element('<pre/>').text(html).html();
    };
  }
Pavel Blagodov
  • 572
  • 5
  • 6
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.

AlpenDitrix
  • 322
  • 2
  • 8
-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.

Josh David Miller
  • 120,525
  • 16
  • 127
  • 95
  • I'm just looking for a standard function to do escaping of HTML. None of the standard auto-escaping mechanisms achieve my purpose. – edA-qa mort-ora-y Feb 17 '13 at 17:12
  • `$sanitize` takes a string and returns a sanitized string. What does it not do that you need? – Josh David Miller Feb 17 '13 at 19:28
  • 37
    santizing is not the same as escaping. Escpaing is doing transformations like `<` to `<` – edA-qa mort-ora-y Feb 17 '13 at 20:09
  • @edA $sanitize for as far as i am aware does exactly that, take a look at the demo at AngularJS – Bas Goossen Jul 08 '21 at 11:10
  • 1
    @BasGoossen it is not the same because it completely removes things like `` instead of encoding tokens like `<` and `>` to `<` `>`. 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." https://docs.angularjs.org/api/ngSanitize/service/$sanitize – bzzWomp Nov 30 '21 at 13:16
-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.

Allen
  • 1
-3

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

JRulz
  • 497
  • 3
  • 5
  • 16