pug-plugin-trusted-types (original) (raw)

Sisyphus Logo

Safe Pug Templates (Pug Trusted Types Plugin)

Build Status Dependencies Status npm Coverage Status Install Size Known Vulnerabilities

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 nonce attribute will not.

Content-Security-Policy: default-src 'nonce-7QgTXZjEaat5wrC8JAn0FsBq'

Plugin Configuration

Pug doesn't provide a way to directly configure plugins, but this plugin takes into account

({

  filtersOptions: {

    trustedTypes: {

      report() {

      }

    }

  }

})

csrfInputName

A value for an <input name> attribute that is automatically added to<form> elements to protect against Cross-Site Request Forgery (CSRF).

Defaults to csrfToken.

See also CSRF (Cross-Site Request Forgery) Protection.

csrfInputValueExpression

A string containing a JavaScript expression for the value corresponding to the csrfInputName.

Defaults to null. If null, then <form>s have no hidden input added.

See also CSRF (Cross-Site Request Forgery) Protection.

nonceValueExpression

A string containing a JavaScript expression for the value of nonceattribute automatically added to <script> and <style> elements.

Defaults to null. If null, then nonce attributes are not added.

See also Content-Security-Policy.

report(message)

Called if the plugin finds a problem with the template.

By default, this is console.warn.