npm package: @melloware/csp-webpack-plugin
Description: A plugin which, when combined with HTMLWebpackPlugin, adds CSP tags to the HTML output
Author: Slack
Latest version: 6.0.4

# CSP Webpack Plugin

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![npm version](https://badge.fury.io/js/@melloware%2Fcsp-webpack-plugin.svg)](https://badge.fury.io/js/@melloware%2Fcsp-webpack-plugin)
[![Code Style](https://img.shields.io/badge/code%20style-prettier-brightgreen.svg)](https://github.com/prettier/prettier)
[![Build Status](https://github.com/melloware/csp-webpack-plugin/actions/workflows/build.yml/badge.svg)](https://github.com/melloware/csp-webpack-plugin/actions/workflows/build.yml)

## About

This plugin was forked from the wonderful work done by [Slack](https://github.com/slackhq/csp-html-webpack-plugin) but adds some key features:

- [Subresource Integrity](http://www.w3.org/TR/SRI/) (SRI) is a security feature that enables browsers to verify that files they fetch are delivered without unexpected manipulation. Thanks to [webpack-subresource-integrity](https://www.npmjs.com/package/webpack-subresource-integrity) plugin.
- [Trusted Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types) support and use of [DOMPurify](https://www.npmjs.com/package/dompurify) to sanitize any `innerHTML` calls to prevent XSS
- [PrimeReact](https://www.primefaces.org/primereact/) special handling for inline CSS styles. See [Issue #2423](https://github.com/primefaces/primereact/issues/2423)
- Configure NONCE for pre-loaded scripts
- Typescript definition
- Default to SHA384 instead of SHA256
- GitHub Actions Build and Dependabot to keep dependencies up to date

## Description

This plugin will generate meta content for your [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
tag and input the correct data into your HTML template, generated by [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin/).

All inline JS and CSS will be hashed and inserted into the policy.

## Installation

Install the plugin with npm:

```shell
$ npm i --save-dev @melloware/csp-webpack-plugin
```

## Basic Usage

Include the following in your webpack config:

```javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CspHtmlWebpackPlugin = require('@melloware/csp-webpack-plugin');

module.exports = {
  // rest of webpack config

  plugins: [
    new HtmlWebpackPlugin()
    new CspHtmlWebpackPlugin({
      // config here, see below
    })
  ]
}
```

## Recommended Configuration

By default, the `@melloware/csp-webpack-plugin` has a very lax policy. You should configure it for your needs.

A good starting policy would be the following:

```javascript
new CspHtmlWebpackPlugin({
  'script-src': '',
  'style-src': ''
});
```

Although we're configuring `script-src` and `style-src` to be blank, the CSP plugin will scan your HTML
generated in `html-webpack-plugin` for external/inline script and style tags, and will add the appropriate
hashes and nonces to your CSP policy. This configuration will also add a `base-uri` and `object-src` entry
that exist in the default policy:

```xml
<meta http-equiv="Content-Security-Policy" content="
  base-uri 'self';
  object-src 'none';
  script-src 'sha256-0Tumwf1AbPDHZO4kdvXUd4c5PiHwt55hre+RDxj9O3Q='
             'nonce-hOlyTAhW5QI5p+rv9VUPZg==';
  style-src 'sha256-zfLUTOi9wwJktpDIoBZQecK4DNIVxW8Tl0cadROvQgo='
">
```

This configuration should work for most use cases, and will provide a strong layer of extra security.

## All Configuration Options

### `CspHtmlWebpackPlugin`

This `CspHtmlWebpackPlugin` accepts 2 params with the following structure:

- `{object}` Policy (optional) - a flat object which defines your CSP policy. Valid keys and values can be found on the [MDN CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) page. Values can either be a string, or an array of strings.
- `{object}` Additional Options (optional) - a flat object with the optional configuration options:
  - `{boolean|Function}` enabled - if false, or the function returns false, the empty CSP tag will be stripped from the html output.
    - The `htmlPluginData` is passed into the function as it's first param.
    - If `enabled` is set the false, it will disable generating a CSP for all instances of `HtmlWebpackPlugin` in your webpack config.
  - `{boolean}` integrityEnabled - Enable or disable SHA384  [Subresource Integrity](http://www.w3.org/TR/SRI/)
  - `{boolean}` primeReactEnabled - Enable or disable custom [PrimeReact](https://www.primefaces.org/primereact/) NONCE value added to the environment for inline styles.
  - `{boolean}` trustedTypesEnabled - Enable or disable [Trusted Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types) handling which automatically adds DOMPurify to sanitize `innerHTML` calls to prevent XSS
  - `{string}` hashingMethod - accepts 'sha256', 'sha384', 'sha512' - your node version must also accept this hashing method.
  - `{object}` hashEnabled - a `<string, boolean>` entry for which policy rules are allowed to include hashes
  - `{object}` nonceEnabled - a `<string, boolean>` entry for which policy rules are allowed to include nonces
  - `{Function}` processFn - allows the developer to overwrite the default method of what happens to the CSP after it has been created
    - Parameters are:
      - `builtPolicy`: a `string` containing the completed policy;
      - `htmlPluginData`: the `HtmlWebpackPlugin` `object`;
      - `$`: the `cheerio` object of the html file currently being processed
      - `compilation`: Internal webpack object to manipulate the build

## Trusted Types

[Trusted Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types) is a newer CSP directive which adds XSS protection by preventing `innerHTML` without being trusted.

To add Trusted Type support automatically to your application you would add the `require-trusted-types-for 'script'` CSP directive.

```javascript
{
  'base-uri': "'self'",
  'object-src': "'none'",
  'script-src': ["'strict-dynamic'"],
  'style-src': ["'self'"],
  'require-trusted-types-for': ["'script'"]
};
```

If `trustedTypesEnabled=true` this plugin will automatically add a special script which executes before any other script to enable a default policy that sanitizes HTML using DOMPurify.

```javascript
import DOMPurify from 'dompurify';

if (window.trustedTypes && window.trustedTypes.createPolicy) { // Feature testing
    window.trustedTypes.createPolicy('default', {
        createHTML: (string) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true}),
        createScriptURL: string => sanitizeUrl(string),
        createScript: string => string // allow scripts
    });
};
```

You will need to include DOMPurify and Trusted Types Polyfill using `npm install dompurify trusted-types` to your `package.json`.


## Appendix

#### Default Policy:

```javascript
{
  'base-uri': "'self'",
  'object-src': "'none'",
  'script-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"],
  'style-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"]
};
```

#### Default Additional Options:

```javascript
{
  enabled: true,
  integrityEnabled: true,
  primeReactEnabled: true,
  trustedTypesEnabled: true,
  hashingMethod: 'sha384',
  hashEnabled: {
    'script-src': true,
    'style-src': true
  },
  nonceEnabled: {
    'script-src': true,
    'style-src': true
  },
  processFn: defaultProcessFn
}
```

#### Full Default Configuration:

```javascript
new CspHtmlWebpackPlugin({
  'base-uri': "'self'",
  'object-src': "'none'",
  'script-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"],
  'style-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"]
}, {
  enabled: true,
  integrityEnabled: true,
  primeReactEnabled: true,
  trustedTypesEnabled: true,
  hashingMethod: 'sha384',
  hashEnabled: {
    'script-src': true,
    'style-src': true
  },
  nonceEnabled: {
    'script-src': true,
    'style-src': true
  },
  processFn: defaultProcessFn  // defined in the plugin itself
})
```
## Advanced Usage
### Generating a file containing the CSP directives

Some specific directives require the CSP to be sent to the client via a response header (e.g. `report-uri` and `report-to`)
You can set your own `processFn` callback to make this happen.

#### nginx

In your webpack config:

```javascript
const RawSource = require('webpack-sources').RawSource;

function generateNginxHeaderFile(
  builtPolicy,
  _htmlPluginData,
  _obj,
  compilation
) {
  const header =
    'add_header Content-Security-Policy "' +
    builtPolicy +
    '; report-uri /csp-report/ ";';
  compilation.emitAsset('nginx-csp-header.conf', new RawSource(header));
}

module.exports = {
  {...},
  plugins: [
    new CspHtmlWebpackPlugin(
      {...}, {
      processFn: generateNginxHeaderFile
    })
  ]
};
```
In your nginx config:
```nginx
location / {
  ...
  include /path/to/webpack/output/nginx-csp-header.conf
}
```

## Publishing

Adjust the version in the `package.json` if necessary, then

```shell
npm login
# This will run npm run build automatically
npm publish --access public
```

Then upload code to github, create tag & release.

## Contribution

Contributions are most welcome! Please see the included contributing file for more information.

## License

This project is licensed under MIT. Please see the included license file for more information.