Partial HTML string escaping in Angular.js

angular-translate, angularjs, javascript, security

Solution

Let's start by restructuring your `logEntry` to separate out the interpolateParams

var translationId = 'Log.' + msg.context.entity_type) + '.' + msg.context.action;
var interpolateParams = {
        'object_name': msg.context.object_name,
        'user': msg.context.user_name
};
var translated = $translate(translationId, interpolateParams);
return $sce.trustAsHtml(translated);

You want to escape all HTML from `interpolateParams` but leave any HTML in your translation templates. Use this code to copy the object, iterate over its values and replace with escaped HTML.

var safeParams = angular.copy(interpolateParams);    
angular.forEach(safeParams, function(value, key, obj) {     
  obj[key] = encodeEntities(value)
  // if you want safe/sanitized HTML, use this instead
  // obj[key] = $sanitize(value);
});
var translated = $translate(translationId, safeParams);

Lastly, the `encodeEntities` functionality of angular isn't exposed, so we had to borrow the source from angular-sanitize.js

var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
    // Match everything outside of normal chars and " (quote character)
    NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
function encodeEntities(value) {
  return value.
    replace(/&/g, '&').
    replace(SURROGATE_PAIR_REGEXP, function(value) {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

Update: After updating to angular-translate 2.7.0 this message appeared:

pascalprecht.translate.$translateSanitization: No sanitization strategy has been configured. This can have serious security implications. See http://angular-translate.github.io/docs/#/guide/19_security for details.

Sp instead of the `trustlate` answer above, angular-translate can accomplish the same result with:

$translateProvider.useSanitizeValueStrategy('escapeParameters');

See the docs for more Sanitize Value Strategies

Problem

I have read about angular's way of escaping everything by default and `$sce`, so I white-list data with `$sce.trustAsHtml()` through filter (since `$sce` is not working in service), like this: ``` <sup class="ng-binding" ng-bind-html="row|logEntry"></sup> ``` But the problem is, that I don't trust some parts of HTML. To dive into details - I have translations that have HTML in it, but they have replaceable tokens/variables in them. So translations support HTML, but I don't want provided tokens to include HTML. My filter `logEntry` internally looks like this: ``` var translated = $translate('Log.' + msg.context.entity_type) + '.' + msg.context.action, { 'object_name': msg.context.object_name, 'user': msg.context.user_name }); return $sce.trustAsHtml(translated); ``` For example I can have translation about userX changing article, but I don't want result text to trigger alert() if user's name includes `<script>alert('evilname')</script>` `$translate` by itself is not relevant, it can be any HTML string where I want some parts to be replaced with regular JS `.replace()` with content staying "as text". So my question is - how can I escape parts of HTML? Do I have to resort to slicing it in parts inside of a view? Or do I have to resort to custom escaping ( Fastest method to escape HTML tags as HTML entities? )? Is there a preferred practice for such things?

Original source

Related problems