pug-plugin-trusted-types (original) (raw)
Safe Pug Templates (Pug Trusted Types Plugin)
Hooks into Pug to addTrusted Types checks to key attributes to reduce the risk of XSS.
This plugin focuses on checking URLs, to prevent, e.g. arbitrary strings from reaching <script src>
orjavascript:
URLs from reaching <a href>
.
Without this plugin, the below can lead to XSS.
const x = 'javascript:alert(document.domain)';
const pug = require('pug');
const template = pug.compile('a(href=x) Link', {});
const html = template({ x });
console.log(html);
This plugin cannot, by itself, prevent XSS due to intentionallyunsafe features but when it finds a use of unsafe features, it warns on them and refuses to output TrustedHTML.
Usage
There are several ways to use safe Pug templates.
The Trusted Types plugin adds require
calls which only work with code loaded in a CommonJS module context.
Pug compiles templates to JavaScript which it loads bycalling new Function() so does not load in a module context.
Webpack integration via pug-loader
Pug-loader makes it easy to compile templates when webpacking.
You do need to configure pug-loader to use this plugin though.
If you're using pug-loader
, your webpack.config.js should probably have something like:
({
rules: [
{
test: /.pug$/,
use: [
{
loader: path.resolve('node_modules/pug-loader/index.js'),
options: {
plugins: [
require('pug-plugin-trusted-types'),
],
},
filterOptions: {
trustedTypes: {
},
},
},
],
},
{
test: /.js$/,
use: [
{
loader: path.resolve('node_modules/babel-loader/lib/index.js'),
options: {
plugins: ['module-keys/babel'],
},
},
],
exclude: /node_modules/(webpack/buildin|module-keys|path-browserify|process)/,
},
],
})
Requiring Templates
First you need a dependency:
npm install --save pug-require
Then you can load Pug templates by calling require
.
const { configurePug } = require('pug-require');
configurePug({ });
const myTemplate = require('./templates/link.pug');
console.log(myTemplate({ x: 'https://example.com/' }));
console.log(myTemplate({ x: 'javascript:evil()' }));
See pug-require for more details.
Inline Templates
First you need a dependency:
npm install --save pug-template-tag
Then you can declare Pug templates inline in JS or TS code.
const pug = require('pug-template-tag');
const myTemplate = puga(href=x) Link
;
console.log(myTemplate({ x: 'https://example.com/' }));
console.log(myTemplate({ x: 'javascript:evil()' }));
See pug-template-tag for more details including how to configure templates.
Pre-compiled or manually compiled Templates
First you need to install Pug and the Trusted Types Plugin.
npm install --save pug pug-plugin-trusted-types
Then add the plugin to the plugins
field of your Pug options object.
Before
const pug = require('pug');
const myTemplate = pug.compile(
templateCode,
{
});
After
const pug = require('pug');
const pugPluginTT = require('pug-plugin-trusted-types/plugin');
const myTemplate = pug.compile(
templateCode,
{
plugins: [ pugPluginTT ],
});
Since the Trusted Types Plugin provides security checks, it should ideally run after plugins that do not aim to provide security guarantees. Putting it at the end of any existing plugins
array should suffice.
postCodeGen stage plugins could undo security guarantees even if the trusted types plugin runs late.
Double checking expressions
Expressions in Pug templates, whether for attribute values or for text nodes, are double-checked as described below.
Pug Example | Value of X | Policy |
---|---|---|
div(title=x) | Ordinary attribute value | |
Any value | No change | |
a(href=x) | External URL attribute | TrustedURL.sanitize |
Constant expression | No change | |
http: ... | No change | |
https: ... | No change | |
mailto: ... | No change | |
TrustedURL | No change | |
TrustedResourceURL | No change | |
Other | Replaced with about:invalid | |
script(src=x) | URL loaded into same origin | |
Constant expression | No change | |
TrustedResourceURL | No change | |
Other | Replaced with about:invalid | |
p =x | Text in a normal element | |
Constant expression | Auto-escaped unless != | |
TrustedHTML | No change | |
Other | Auto-escaped | |
script =x | Text in
main() If your HTTP response has a header like the below then those CSS and JavaScript will load, but ones lacking the
Plugin ConfigurationPug doesn't provide a way to directly configure plugins, but this plugin takes into account ({ filtersOptions: { trustedTypes: { report() { } } } }) csrfInputNameA value for an Defaults to See also CSRF (Cross-Site Request Forgery) Protection. csrfInputValueExpressionA string containing a JavaScript expression for the value corresponding to the Defaults to See also CSRF (Cross-Site Request Forgery) Protection. nonceValueExpressionA string containing a JavaScript expression for the value of Defaults to See also Content-Security-Policy. report(message)Called if the plugin finds a problem with the template. By default, this is |