XSS prevention for Pug templates with typesafe exceptions
npm install pug-plugin-trusted-types






Hooks into Pug to add
Trusted Types checks to key attributes
to reduce the risk of XSS.
* Usage
* Webpack integration via pug-loader
* Requiring Templates
* Inline Templates
* Pre-compiled or manually compiled Templates
* Before
* After
* Double checking expressions
* Automagic
* CSRF (Cross-Site Request Forgery) Protection
* Configuring with csrf-crypto
* Content-Security-Policy
* Plugin Configuration
* csrfInputName
* csrfInputValueExpression
* nonceValueExpression
* report(message)
This plugin focuses on checking URLs, to prevent, e.g. arbitrary strings from reaching orjavascript: URLs from reaching .
Without this plugin, the below can lead to XSS.
``js
// Attacker controls x
const x = 'javascript:alert(document.domain)';
// Declare a template
const pug = require('pug');
const template = pug.compile('a(href=x) Link', {});
// Use the template
const html = template({ x });
console.log(html);
//! Link
`
This plugin cannot, by itself, prevent XSS due to intentionally
[unsafe][] features but when it finds a use of unsafe features, it
warns on them and refuses to output [TrustedHTML][].
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 by
[calling new Function()][pug-compile-code-snippet] so does not
load in a module context.
[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:
`js`
({
rules: [
// When loading Pug, run pug-loader.
{
test: /\.pug$/,
use: [
{
loader: path.resolve('node_modules/pug-loader/index.js'),
options: {
plugins: [
// Any other plugins you use should ideally go first.
require('pug-plugin-trusted-types'),
],
},
// Optionally, configure the plugin. You probably won't need to do this.
filterOptions: {
trustedTypes: {
// See "Plugin Configuration" below.
},
},
},
],
},
// This runs the module-keys babel processor on all JavaScript sources.
{
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)/,
},
],
})
First you need a dependency:
`sh`
npm install --save pug-require
Then you can load Pug templates by calling require.
`js
// Adds hooks so that requiring a .pug file loads it as a template.
// Even if you use the default config, you still need to require
// this module before you require the first .pug file.
const { configurePug } = require('pug-require');
configurePug({ / pug options / });
// Load a simple template a(href=x) Link.
const myTemplate = require('./templates/link.pug');
console.log(myTemplate({ x: 'https://example.com/' }));
//! Link
console.log(myTemplate({ x: 'javascript:evil()' }));
//! Link
`
See pug-require for
more details.
First you need a dependency:
`sh`
npm install --save pug-template-tag
Then you can declare Pug templates inline in JS or TS code.
`js
const pug = require('pug-template-tag');
const myTemplate = puga(href=x) Link;
console.log(myTemplate({ x: 'https://example.com/' }));
//! Link
console.log(myTemplate({ x: 'javascript:evil()' }));
//! Link
`
See pug-template-tag for
more details including how to configure templates.
First you need to install Pug and the Trusted Types Plugin.
`sh`
npm install --save pug pug-plugin-trusted-types
Then add the plugin to the plugins field of your Pug options object.
`js
const pug = require('pug');
const myTemplate = pug.compile(
templateCode,
{
// Options
});
`
`js
const pug = require('pug');
const pugPluginTT = require('pug-plugin-trusted-types/plugin');
const myTemplate = pug.compile(
templateCode,
{
plugins: [ pugPluginTT ],
// Options
});
`
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.
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 | |a(href=x)
| | Any value | No change |
| | External URL attribute | [TrustedURL.sanitize][] |http:
| | Constant expression | No change |
| | ... | No change |https:
| | ... | No change |mailto:
| | ... | No change |about:invalid
| | [TrustedURL][] | No change |
| | [TrustedResourceURL][] | No change |
| | Other | Replaced with |script(src=x)
| | URL loaded into same origin | |about:invalid
| | Constant expression | No change |
| | [TrustedResourceURL][] | No change |
| | Other | Replaced with |p =x
| | Text in a normal element | |!=
| | Constant expression | Auto-escaped unless |script =x
| | [TrustedHTML][] | No change |
| | Other | Auto-escaped |
| | Text in
`
If your HTTP response has a header like the below then those CSS and
JavaScript will load, but ones lacking the nonce attribute will not.
``
Content-Security-Policy: default-src 'nonce-7QgTXZjEaat5wrC8JAn0FsBq'
Pug doesn't provide a way to directly configure plugins, but this plugin takes into account
`js`
({
filtersOptions: {
trustedTypes: {
report() {
// ...
}
}
}
})
A value for an attribute that is automatically added to