mozilla / nunjucks

A powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired)
https://mozilla.github.io/nunjucks/
BSD 2-Clause "Simplified" License
8.48k stars 635 forks source link

Use Environment.renderString with a Content Security Policy that prohibits unsafe-eval? #1383

Open ataylormays-duetto opened 2 years ago

ataylormays-duetto commented 2 years ago

When using Environment.renderString in an app that prohibits unsafe-eval, an error is thrown from this line. An old nunjucks issue suggests a workaround of pre-compiling templates, but the app doesn't use templates and only calls renderString. How can renderString be used in conjunction with a CSP that prohibits unsafe-eval?

devoidfury commented 2 years ago

It can't, renderString has to invoke the compiler, and the compiler works by creating a string containing javascript that's evaled into a function (with new Function(source)). Precompiling the templates means, invoking the compiler, generating that javascript string, and then storing it as code that will be loaded and run just like any other javascript without the need for eval.

Or to put it a different way, using a CSP to disable unsafe-eval is saying to the user agent, "don't allow code to be created from strings", which is exactly what using not-precompiled templates is.

ataylormays-duetto commented 2 years ago

Is it possible to bypass the compiler? The use case I have is fairly simple:

import moment from 'moment';
import nunjucks from 'nunjucks';

const NunjucksEnvironment = new nunjucks.Environment();

// Add custom filters
NunjucksEnvironment.addFilter('datetimeformat', (str, format) => {
    if (!str) {
        throw new TypeError('Invalid string input');
    }
    if (!format) {
        throw new TypeError('Invalid format input');
    }
    return moment(str).format(format);
});

export default NunjucksEnvironment;
devoidfury commented 2 years ago

No, the compiler is a core part of the library. The nunjucks templates are converted by the compiler into plain javascript code through stringbuilding. The whole thing is built on that.

devoidfury commented 2 years ago

If you precompile the templates, then you won't need to eval the template strings in production in the browser as I already explained.

ataylormays-duetto commented 2 years ago

@devoidfury my understanding though is that it's not possible to use renderString in conjunction with templates, is that correct?

My goal is to handle arbitrary strings that such as Today is {{ day | datetimeformat('MMM-D')}} and allow the day to be formatted according to the datetimeformat passed in the string. There's no defined list of such strings however, which means there's nothing to precompile.

devoidfury commented 2 years ago

I think you'd need to use render instead. And if your use-case is to treat user input strings as templates, that's a huge security problem and will allow them to XSS your site. (see the warning in the docs for reference https://mozilla.github.io/nunjucks/api.html#user-defined-templates-warning )

The way the library works is, it takes in a template as a string (either directly as a string or from a file), it compiles it by transforming that template into a string of javascript code, converting it to a function with eval, and then calling that function with whatever input you're rendering it with.

You either need to find a way to precompile your templates, allow unsafe-eval on your site, do server-side rendering of those, or use a library that doesn't rely on eval to compile template strings.