Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSP-safe ES6 template literals

Is there a template engine that will parse templates in the style of ES6 template literals (e.g. "string ${var}") without violating a Content-Security-Policy (CSP) restriction on script evaluation?

The CSP restrictions on script evaluation prevent eval, new Function, setTimeout(string) and setInterval(string).

There are a number of template engines that can provide or be modified to provide something like ES6-style template literals, such as John Resig's MicroTemplates, lodash _.template and DoT.js. However all seem to violate the CSP by using new Function.

It would in some ways be convenient if var could be unrestricted Javascript, but for apparent reasons this may not be possible. However I would need to be able to modify the engine to format the output as desired.

In the circumstances performance is not a concern, and pre-compiling the templates is not an option. Others have discussed pre-compilation.

As an additional restriction, the content is text - not HTML. I do not think DOM-oriented templating engines such as Knockout or PURE would not work effectively, as a result.

My first thought is to start with mustache.js and modify it from there (i.e. change mustache.tags = ['${', '}'] or a DIY solution, but I would be grateful for any thoughts on the topic in general as there seems to be quite a dearth of discussion on CSP and templates.

like image 544
Brian M. Hunt Avatar asked Nov 27 '13 02:11

Brian M. Hunt


2 Answers

If all you need is key value substitution, you can make do with a simple function like the templateReplace that I provide below. No eval involved, only regular expressions.

If you need to include 'unrestriced javascript', have content like ${[1,2,3].join(', ')} then, as you acknowledged, you will obviously need a solution that will violate your CSP policy.

var templateReplace = function(html, data, keyTemplate) {
  if (!keyTemplate || typeof keyTemplate !== 'string' || keyTemplate.indexOf('key') === -1) {
    keyTemplate = '{{key}}';
  }
  return (Object.keys(data) || []).reduce(function(html, key) {
    var val = (data[key] !== undefined) ? data[key] : '';
    return html.replace(new RegExp(keyTemplate.replace('key', key), 'gi'), val);
  }, html);
};

// demo 1, using {{key}} syntax
(function() {
  var li = [{ text: 'one' }, { text: 'two' }, { text: 'three' }].map(function(d) {
    return templateReplace( '<li>Item: {{text}}</li>', d);
  });
  document.querySelector('#result1').innerHTML = li.join('\n')
}())

// demo 2, using ${key} syntax
(function() {
  var helloWorld = templateReplace('${hello} ${world}', { hello: 'Hello', world: 'World!' }, '\\${key}');
  document.querySelector('#result2').innerHTML = helloWorld;
}())
demo 1 - {{key}} syntax
<div id="result1"></div>

demo 2 - ${key} syntax
<div id="result2"></div>
like image 88
widged Avatar answered Sep 21 '22 21:09

widged


Micromustache library does exactly that. version 6 supports setting custom open and close markers (${ and } instead of {{ and }}) which makes it more of a drop-in replacement for template literals.

like image 44
AlexStack Avatar answered Sep 20 '22 21:09

AlexStack