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, '<').
replace(/>/g, '>');
}
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?