@elliemae/pui-cli 8.40.3 → 8.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,7 +37,6 @@ var import_cors = __toESM(require("cors"), 1);
37
37
  var import_compression = __toESM(require("compression"), 1);
38
38
  var import_express_static_gzip = __toESM(require("express-static-gzip"), 1);
39
39
  var import_pino_http = __toESM(require("pino-http"), 1);
40
- var import_csp = require("./csp.js");
41
40
  var import_helpers = require("../webpack/helpers.js");
42
41
  const paths = (0, import_helpers.getPaths)();
43
42
  const setupDefaultMiddlewares = (app) => {
@@ -53,7 +52,6 @@ const setupDefaultMiddlewares = (app) => {
53
52
  app.use(pino);
54
53
  app.use((0, import_cors.default)());
55
54
  app.options("*", (0, import_cors.default)());
56
- (0, import_csp.csp)(app);
57
55
  app.use(import_express.default.urlencoded({ extended: false }));
58
56
  app.use(import_express.default.text({ type: "text/plain" }));
59
57
  app.use(import_express.default.json({ type: "application/json" }));
@@ -61,9 +59,6 @@ const setupDefaultMiddlewares = (app) => {
61
59
  const setupAdditionalMiddlewars = (app, options = {}) => {
62
60
  const { buildPath = paths.buildPath, basePath = paths.basePath } = options;
63
61
  app.use((0, import_compression.default)());
64
- app.get(basePath, (req, res) => {
65
- (0, import_csp.sendFileWithCSPNonce)({ buildPath, res });
66
- });
67
62
  app.use(
68
63
  basePath,
69
64
  (0, import_express_static_gzip.default)(buildPath, {
@@ -73,9 +68,5 @@ const setupAdditionalMiddlewars = (app, options = {}) => {
73
68
  })
74
69
  );
75
70
  app.use((0, import_express_static_gzip.default)("cdn", {}));
76
- app.get(
77
- "*",
78
- (req, res) => (0, import_csp.sendFileWithCSPNonce)({ buildPath, res })
79
- );
80
71
  return app;
81
72
  };
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var csp_plugin_exports = {};
30
+ __export(csp_plugin_exports, {
31
+ CspPlugin: () => CspPlugin
32
+ });
33
+ module.exports = __toCommonJS(csp_plugin_exports);
34
+ var import_html_webpack_plugin = __toESM(require("html-webpack-plugin"), 1);
35
+ var import_csp = require("./csp.js");
36
+ const defaultOptions = {
37
+ enableUnsafeEval: false
38
+ };
39
+ class CspPlugin {
40
+ #options = defaultOptions;
41
+ #htmlWebpackPlugin;
42
+ /**
43
+ *
44
+ * @param htmlWebpackPlugin
45
+ * @param {object} options Additional options for this module.
46
+ */
47
+ constructor(htmlWebpackPlugin, options) {
48
+ this.#htmlWebpackPlugin = htmlWebpackPlugin;
49
+ this.#options = { ...this.#options, ...options ?? {} };
50
+ }
51
+ /**
52
+ * Processes HtmlWebpackPlugin's html data by adding the CSP
53
+ * @param compilation
54
+ * @param htmlPluginData
55
+ * @param htmlPluginData.html
56
+ * @param compileCb
57
+ * @returns {*}
58
+ */
59
+ processCsp(compilation, htmlPluginData, compileCb) {
60
+ const cspModule = new import_csp.CSP(htmlPluginData.html);
61
+ cspModule.refactorSourcedScriptsForHashBasedCsp();
62
+ const scriptHashes = cspModule.hashAllInlineScripts();
63
+ const { enableUnsafeEval } = this.#options;
64
+ const strictCsp = import_csp.CSP.getStrictCsp(scriptHashes, {
65
+ enableUnsafeEval
66
+ });
67
+ cspModule.addMetaTag(strictCsp);
68
+ htmlPluginData.html = cspModule.serializeDom();
69
+ return compileCb(null, htmlPluginData);
70
+ }
71
+ /**
72
+ * Hooks into webpack to collect assets and hash them, build the policy, and add it into our HTML template
73
+ * @param compiler
74
+ */
75
+ apply(compiler) {
76
+ compiler.hooks.compilation.tap("CspPlugin", (compilation) => {
77
+ import_html_webpack_plugin.default.getCompilationHooks(compilation).beforeEmit.tapAsync(
78
+ "CspPlugin",
79
+ this.processCsp.bind(this, compilation)
80
+ );
81
+ });
82
+ }
83
+ }
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var csp_exports = {};
30
+ __export(csp_exports, {
31
+ CSP: () => CSP
32
+ });
33
+ module.exports = __toCommonJS(csp_exports);
34
+ var crypto = __toESM(require("node:crypto"), 1);
35
+ var cheerio = __toESM(require("cheerio"), 1);
36
+ class CSP {
37
+ static HASH_FUNCTION = "sha256";
38
+ static INLINE_SCRIPT_SELECTOR = "script:not([src])";
39
+ static SOURCED_SCRIPT_SELECTOR = "script[src]";
40
+ $;
41
+ constructor(html) {
42
+ this.$ = cheerio.load(html);
43
+ }
44
+ serializeDom() {
45
+ return this.$.root().html() ?? "";
46
+ }
47
+ /**
48
+ * Returns a strict Content Security Policy for mittigating XSS.
49
+ * For more details read csp.withgoogle.com.
50
+ * If you modify this CSP, make sure it has not become trivially bypassable by
51
+ * checking the policy using csp-evaluator.withgoogle.com.
52
+ * @param hashes A list of sha-256 hashes of trusted inline scripts.
53
+ * @param cspOptions
54
+ * @param enableTrustedTypes If Trusted Types should be enabled for scripts.
55
+ * @param enableBrowserFallbacks If fallbacks for older browsers should be
56
+ * added. This is will not weaken the policy as modern browsers will ignore
57
+ * the fallbacks.
58
+ * @param enableUnsafeEval If you cannot remove all uses of eval(), you can
59
+ * still set a strict CSP, but you will have to use the 'unsafe-eval'
60
+ * keyword which will make your policy slightly less secure.
61
+ * @param cspOptions.enableBrowserFallbacks
62
+ * @param cspOptions.enableTrustedTypes
63
+ * @param cspOptions.enableUnsafeEval
64
+ * @returns A strict Content Security Policy string.
65
+ */
66
+ static getStrictCsp(hashes, cspOptions = {
67
+ enableUnsafeEval: false
68
+ }) {
69
+ const strictCspTemplate = {
70
+ // 'strict-dynamic' allows hashed scripts to create new scripts.
71
+ "script-src": [`'strict-dynamic'`, ...hashes ?? []],
72
+ // Restricts `object-src` to disable dangerous plugins like Flash.
73
+ "object-src": [`'none'`],
74
+ // Restricts `base-uri` to block the injection of `<base>` tags. This
75
+ // prevents attackers from changing the locations of scripts loaded from
76
+ // relative URLs.
77
+ "base-uri": [`'self'`]
78
+ };
79
+ if (cspOptions.enableUnsafeEval) {
80
+ strictCspTemplate["script-src"].push(`'unsafe-eval'`);
81
+ }
82
+ return Object.entries(strictCspTemplate).map(([directive, values]) => `${directive} ${values.join(" ")};`).join("");
83
+ }
84
+ /**
85
+ * Enables a CSP via a meta tag at the beginning of the document.
86
+ * Warning: It's recommended to set CSP as HTTP response header instead of
87
+ * using a meta tag. Injections before the meta tag will not be covered by CSP
88
+ * and meta tags don't support CSP in report-only mode.
89
+ * @param csp A Content Security Policy string.
90
+ */
91
+ addMetaTag(csp) {
92
+ let metaTag = this.$('meta[http-equiv="Content-Security-Policy"]');
93
+ if (!metaTag.length) {
94
+ metaTag = cheerio.load('<meta http-equiv="Content-Security-Policy">')(
95
+ "meta"
96
+ );
97
+ metaTag.prependTo(this.$("head"));
98
+ }
99
+ metaTag.attr("content", csp);
100
+ }
101
+ /**
102
+ * Replaces all sourced scripts with a single inline script that can be hashed
103
+ */
104
+ refactorSourcedScriptsForHashBasedCsp() {
105
+ const scriptInfoList = this.$(CSP.SOURCED_SCRIPT_SELECTOR).map((i, script) => {
106
+ const src = this.$(script).attr("src") ?? "";
107
+ const type = this.$(script).attr("type") ?? "";
108
+ this.$(script).remove();
109
+ return { src, type };
110
+ }).toArray().filter((info) => info.src);
111
+ const loaderScript = CSP.createLoaderScript(scriptInfoList);
112
+ if (!loaderScript) {
113
+ return;
114
+ }
115
+ const newScript = cheerio.load("<script>")("script");
116
+ newScript.text(loaderScript);
117
+ newScript.appendTo(this.$("body"));
118
+ }
119
+ /**
120
+ * Returns a list of hashes of all inline scripts found in the HTML document.
121
+ * @returns A list of sha-256 hashes of inline scripts.
122
+ */
123
+ hashAllInlineScripts() {
124
+ return this.$(CSP.INLINE_SCRIPT_SELECTOR).map((i, elem) => CSP.hashInlineScript(this.$(elem).html() ?? "")).get();
125
+ }
126
+ /**
127
+ * Returns JS code for dynamically loading sourced (external) scripts.
128
+ * @param scriptInfoList A list of objects containing src and type for scripts that should be loaded
129
+ * @returns JS code for loading scripts.
130
+ */
131
+ static createLoaderScript(scriptInfoList) {
132
+ if (!scriptInfoList.length) {
133
+ return void 0;
134
+ }
135
+ return `
136
+ var scripts = ${JSON.stringify(scriptInfoList)};
137
+ scripts.forEach(function(scriptInfo) {
138
+ var s = document.createElement('script');
139
+ s.src = scriptInfo.src;
140
+ if (scriptInfo.type) {
141
+ s.type = scriptInfo.type;
142
+ }
143
+ s.async = false; // preserve execution order.
144
+ document.body.appendChild(s);
145
+ });
146
+ `;
147
+ }
148
+ /**
149
+ * Calculates a CSP compatible hash of an inline script.
150
+ * @param scriptText Text between opening and closing script tag. Has to
151
+ * include whitespaces and newlines!
152
+ * @returns A sha-256 hash of the script.
153
+ */
154
+ static hashInlineScript(scriptText) {
155
+ const hash = crypto.createHash(CSP.HASH_FUNCTION).update(scriptText, "utf-8").digest("base64");
156
+ return `'${CSP.HASH_FUNCTION}-${hash}'`;
157
+ }
158
+ }
@@ -114,6 +114,9 @@ const devConfig = {
114
114
  googleTagManager: (0, import_helpers.isGoogleTagManagerEnabled)()
115
115
  }
116
116
  }),
117
+ // new CspPlugin(HtmlWebpackPlugin, {
118
+ // enableUnsafeEval: true,
119
+ // }),
117
120
  new import_circular_dependency_plugin.default({
118
121
  exclude: /a\.(js|ts|jsx|tsx)|node_modules/,
119
122
  // exclude node_modules
@@ -57,6 +57,7 @@ const getProdConfig = ({ latestVersion = true } = {}) => {
57
57
  },
58
58
  optimization: {
59
59
  moduleIds: "deterministic",
60
+ realContentHash: false,
60
61
  minimizer: [
61
62
  new import_esbuild_loader.EsbuildPlugin({
62
63
  target: (0, import_browserslist_to_esbuild.default)(),
@@ -140,5 +141,7 @@ const htmlWebpackPlugin = new import_html_webpack_plugin.default({
140
141
  }
141
142
  });
142
143
  const config = (0, import_webpack_base_babel.baseConfig)(getProdConfig());
143
- if (config.plugins) config.plugins.push(htmlWebpackPlugin);
144
+ if (config.plugins) {
145
+ config.plugins.push(htmlWebpackPlugin);
146
+ }
144
147
  var webpack_prod_babel_default = config;
@@ -3,7 +3,6 @@ import cors from "cors";
3
3
  import compression from "compression";
4
4
  import expressStaticGzip from "express-static-gzip";
5
5
  import pinoLogger from "pino-http";
6
- import { csp, sendFileWithCSPNonce } from "./csp.js";
7
6
  import { getPaths } from "../webpack/helpers.js";
8
7
  const paths = getPaths();
9
8
  const setupDefaultMiddlewares = (app) => {
@@ -19,7 +18,6 @@ const setupDefaultMiddlewares = (app) => {
19
18
  app.use(pino);
20
19
  app.use(cors());
21
20
  app.options("*", cors());
22
- csp(app);
23
21
  app.use(express.urlencoded({ extended: false }));
24
22
  app.use(express.text({ type: "text/plain" }));
25
23
  app.use(express.json({ type: "application/json" }));
@@ -27,9 +25,6 @@ const setupDefaultMiddlewares = (app) => {
27
25
  const setupAdditionalMiddlewars = (app, options = {}) => {
28
26
  const { buildPath = paths.buildPath, basePath = paths.basePath } = options;
29
27
  app.use(compression());
30
- app.get(basePath, (req, res) => {
31
- sendFileWithCSPNonce({ buildPath, res });
32
- });
33
28
  app.use(
34
29
  basePath,
35
30
  expressStaticGzip(buildPath, {
@@ -39,10 +34,6 @@ const setupAdditionalMiddlewars = (app, options = {}) => {
39
34
  })
40
35
  );
41
36
  app.use(expressStaticGzip("cdn", {}));
42
- app.get(
43
- "*",
44
- (req, res) => sendFileWithCSPNonce({ buildPath, res })
45
- );
46
37
  return app;
47
38
  };
48
39
  export {
@@ -0,0 +1,53 @@
1
+ import HtmlWebpackPlugin from "html-webpack-plugin";
2
+ import { CSP } from "./csp.js";
3
+ const defaultOptions = {
4
+ enableUnsafeEval: false
5
+ };
6
+ class CspPlugin {
7
+ #options = defaultOptions;
8
+ #htmlWebpackPlugin;
9
+ /**
10
+ *
11
+ * @param htmlWebpackPlugin
12
+ * @param {object} options Additional options for this module.
13
+ */
14
+ constructor(htmlWebpackPlugin, options) {
15
+ this.#htmlWebpackPlugin = htmlWebpackPlugin;
16
+ this.#options = { ...this.#options, ...options ?? {} };
17
+ }
18
+ /**
19
+ * Processes HtmlWebpackPlugin's html data by adding the CSP
20
+ * @param compilation
21
+ * @param htmlPluginData
22
+ * @param htmlPluginData.html
23
+ * @param compileCb
24
+ * @returns {*}
25
+ */
26
+ processCsp(compilation, htmlPluginData, compileCb) {
27
+ const cspModule = new CSP(htmlPluginData.html);
28
+ cspModule.refactorSourcedScriptsForHashBasedCsp();
29
+ const scriptHashes = cspModule.hashAllInlineScripts();
30
+ const { enableUnsafeEval } = this.#options;
31
+ const strictCsp = CSP.getStrictCsp(scriptHashes, {
32
+ enableUnsafeEval
33
+ });
34
+ cspModule.addMetaTag(strictCsp);
35
+ htmlPluginData.html = cspModule.serializeDom();
36
+ return compileCb(null, htmlPluginData);
37
+ }
38
+ /**
39
+ * Hooks into webpack to collect assets and hash them, build the policy, and add it into our HTML template
40
+ * @param compiler
41
+ */
42
+ apply(compiler) {
43
+ compiler.hooks.compilation.tap("CspPlugin", (compilation) => {
44
+ HtmlWebpackPlugin.getCompilationHooks(compilation).beforeEmit.tapAsync(
45
+ "CspPlugin",
46
+ this.processCsp.bind(this, compilation)
47
+ );
48
+ });
49
+ }
50
+ }
51
+ export {
52
+ CspPlugin
53
+ };
@@ -0,0 +1,128 @@
1
+ import * as crypto from "node:crypto";
2
+ import * as cheerio from "cheerio";
3
+ class CSP {
4
+ static HASH_FUNCTION = "sha256";
5
+ static INLINE_SCRIPT_SELECTOR = "script:not([src])";
6
+ static SOURCED_SCRIPT_SELECTOR = "script[src]";
7
+ $;
8
+ constructor(html) {
9
+ this.$ = cheerio.load(html);
10
+ }
11
+ serializeDom() {
12
+ return this.$.root().html() ?? "";
13
+ }
14
+ /**
15
+ * Returns a strict Content Security Policy for mittigating XSS.
16
+ * For more details read csp.withgoogle.com.
17
+ * If you modify this CSP, make sure it has not become trivially bypassable by
18
+ * checking the policy using csp-evaluator.withgoogle.com.
19
+ * @param hashes A list of sha-256 hashes of trusted inline scripts.
20
+ * @param cspOptions
21
+ * @param enableTrustedTypes If Trusted Types should be enabled for scripts.
22
+ * @param enableBrowserFallbacks If fallbacks for older browsers should be
23
+ * added. This is will not weaken the policy as modern browsers will ignore
24
+ * the fallbacks.
25
+ * @param enableUnsafeEval If you cannot remove all uses of eval(), you can
26
+ * still set a strict CSP, but you will have to use the 'unsafe-eval'
27
+ * keyword which will make your policy slightly less secure.
28
+ * @param cspOptions.enableBrowserFallbacks
29
+ * @param cspOptions.enableTrustedTypes
30
+ * @param cspOptions.enableUnsafeEval
31
+ * @returns A strict Content Security Policy string.
32
+ */
33
+ static getStrictCsp(hashes, cspOptions = {
34
+ enableUnsafeEval: false
35
+ }) {
36
+ const strictCspTemplate = {
37
+ // 'strict-dynamic' allows hashed scripts to create new scripts.
38
+ "script-src": [`'strict-dynamic'`, ...hashes ?? []],
39
+ // Restricts `object-src` to disable dangerous plugins like Flash.
40
+ "object-src": [`'none'`],
41
+ // Restricts `base-uri` to block the injection of `<base>` tags. This
42
+ // prevents attackers from changing the locations of scripts loaded from
43
+ // relative URLs.
44
+ "base-uri": [`'self'`]
45
+ };
46
+ if (cspOptions.enableUnsafeEval) {
47
+ strictCspTemplate["script-src"].push(`'unsafe-eval'`);
48
+ }
49
+ return Object.entries(strictCspTemplate).map(([directive, values]) => `${directive} ${values.join(" ")};`).join("");
50
+ }
51
+ /**
52
+ * Enables a CSP via a meta tag at the beginning of the document.
53
+ * Warning: It's recommended to set CSP as HTTP response header instead of
54
+ * using a meta tag. Injections before the meta tag will not be covered by CSP
55
+ * and meta tags don't support CSP in report-only mode.
56
+ * @param csp A Content Security Policy string.
57
+ */
58
+ addMetaTag(csp) {
59
+ let metaTag = this.$('meta[http-equiv="Content-Security-Policy"]');
60
+ if (!metaTag.length) {
61
+ metaTag = cheerio.load('<meta http-equiv="Content-Security-Policy">')(
62
+ "meta"
63
+ );
64
+ metaTag.prependTo(this.$("head"));
65
+ }
66
+ metaTag.attr("content", csp);
67
+ }
68
+ /**
69
+ * Replaces all sourced scripts with a single inline script that can be hashed
70
+ */
71
+ refactorSourcedScriptsForHashBasedCsp() {
72
+ const scriptInfoList = this.$(CSP.SOURCED_SCRIPT_SELECTOR).map((i, script) => {
73
+ const src = this.$(script).attr("src") ?? "";
74
+ const type = this.$(script).attr("type") ?? "";
75
+ this.$(script).remove();
76
+ return { src, type };
77
+ }).toArray().filter((info) => info.src);
78
+ const loaderScript = CSP.createLoaderScript(scriptInfoList);
79
+ if (!loaderScript) {
80
+ return;
81
+ }
82
+ const newScript = cheerio.load("<script>")("script");
83
+ newScript.text(loaderScript);
84
+ newScript.appendTo(this.$("body"));
85
+ }
86
+ /**
87
+ * Returns a list of hashes of all inline scripts found in the HTML document.
88
+ * @returns A list of sha-256 hashes of inline scripts.
89
+ */
90
+ hashAllInlineScripts() {
91
+ return this.$(CSP.INLINE_SCRIPT_SELECTOR).map((i, elem) => CSP.hashInlineScript(this.$(elem).html() ?? "")).get();
92
+ }
93
+ /**
94
+ * Returns JS code for dynamically loading sourced (external) scripts.
95
+ * @param scriptInfoList A list of objects containing src and type for scripts that should be loaded
96
+ * @returns JS code for loading scripts.
97
+ */
98
+ static createLoaderScript(scriptInfoList) {
99
+ if (!scriptInfoList.length) {
100
+ return void 0;
101
+ }
102
+ return `
103
+ var scripts = ${JSON.stringify(scriptInfoList)};
104
+ scripts.forEach(function(scriptInfo) {
105
+ var s = document.createElement('script');
106
+ s.src = scriptInfo.src;
107
+ if (scriptInfo.type) {
108
+ s.type = scriptInfo.type;
109
+ }
110
+ s.async = false; // preserve execution order.
111
+ document.body.appendChild(s);
112
+ });
113
+ `;
114
+ }
115
+ /**
116
+ * Calculates a CSP compatible hash of an inline script.
117
+ * @param scriptText Text between opening and closing script tag. Has to
118
+ * include whitespaces and newlines!
119
+ * @returns A sha-256 hash of the script.
120
+ */
121
+ static hashInlineScript(scriptText) {
122
+ const hash = crypto.createHash(CSP.HASH_FUNCTION).update(scriptText, "utf-8").digest("base64");
123
+ return `'${CSP.HASH_FUNCTION}-${hash}'`;
124
+ }
125
+ }
126
+ export {
127
+ CSP
128
+ };
@@ -84,6 +84,9 @@ const devConfig = {
84
84
  googleTagManager: isGoogleTagManagerEnabled()
85
85
  }
86
86
  }),
87
+ // new CspPlugin(HtmlWebpackPlugin, {
88
+ // enableUnsafeEval: true,
89
+ // }),
87
90
  new CircularDependencyPlugin({
88
91
  exclude: /a\.(js|ts|jsx|tsx)|node_modules/,
89
92
  // exclude node_modules
@@ -29,6 +29,7 @@ const getProdConfig = ({ latestVersion = true } = {}) => {
29
29
  },
30
30
  optimization: {
31
31
  moduleIds: "deterministic",
32
+ realContentHash: false,
32
33
  minimizer: [
33
34
  new EsbuildPlugin({
34
35
  target: browserslistToEsbuild(),
@@ -112,7 +113,9 @@ const htmlWebpackPlugin = new HtmlWebpackPlugin({
112
113
  }
113
114
  });
114
115
  const config = baseConfig(getProdConfig());
115
- if (config.plugins) config.plugins.push(htmlWebpackPlugin);
116
+ if (config.plugins) {
117
+ config.plugins.push(htmlWebpackPlugin);
118
+ }
116
119
  var webpack_prod_babel_default = config;
117
120
  export {
118
121
  webpack_prod_babel_default as default
@@ -0,0 +1,34 @@
1
+ import webpack from 'webpack';
2
+ import HtmlWebpackPlugin from 'html-webpack-plugin';
3
+ type HTMLWebpackPluginData = {
4
+ html: string;
5
+ outputName: string;
6
+ plugin: HtmlWebpackPlugin;
7
+ };
8
+ declare const defaultOptions: {
9
+ enableUnsafeEval: boolean;
10
+ };
11
+ declare class CspPlugin {
12
+ #private;
13
+ /**
14
+ *
15
+ * @param htmlWebpackPlugin
16
+ * @param {object} options Additional options for this module.
17
+ */
18
+ constructor(htmlWebpackPlugin: typeof HtmlWebpackPlugin, options?: typeof defaultOptions);
19
+ /**
20
+ * Processes HtmlWebpackPlugin's html data by adding the CSP
21
+ * @param compilation
22
+ * @param htmlPluginData
23
+ * @param htmlPluginData.html
24
+ * @param compileCb
25
+ * @returns {*}
26
+ */
27
+ processCsp(compilation: webpack.Compilation, htmlPluginData: HTMLWebpackPluginData, compileCb: (err: Error | null, htmlPluginData: HTMLWebpackPluginData) => void): void;
28
+ /**
29
+ * Hooks into webpack to collect assets and hash them, build the policy, and add it into our HTML template
30
+ * @param compiler
31
+ */
32
+ apply(compiler: webpack.Compiler): void;
33
+ }
34
+ export { CspPlugin };
@@ -0,0 +1,65 @@
1
+ /** Module for enabling a hash-based strict Content Security Policy. */
2
+ declare class CSP {
3
+ private static readonly HASH_FUNCTION;
4
+ private static readonly INLINE_SCRIPT_SELECTOR;
5
+ private static readonly SOURCED_SCRIPT_SELECTOR;
6
+ private $;
7
+ constructor(html: string);
8
+ serializeDom(): string;
9
+ /**
10
+ * Returns a strict Content Security Policy for mittigating XSS.
11
+ * For more details read csp.withgoogle.com.
12
+ * If you modify this CSP, make sure it has not become trivially bypassable by
13
+ * checking the policy using csp-evaluator.withgoogle.com.
14
+ * @param hashes A list of sha-256 hashes of trusted inline scripts.
15
+ * @param cspOptions
16
+ * @param enableTrustedTypes If Trusted Types should be enabled for scripts.
17
+ * @param enableBrowserFallbacks If fallbacks for older browsers should be
18
+ * added. This is will not weaken the policy as modern browsers will ignore
19
+ * the fallbacks.
20
+ * @param enableUnsafeEval If you cannot remove all uses of eval(), you can
21
+ * still set a strict CSP, but you will have to use the 'unsafe-eval'
22
+ * keyword which will make your policy slightly less secure.
23
+ * @param cspOptions.enableBrowserFallbacks
24
+ * @param cspOptions.enableTrustedTypes
25
+ * @param cspOptions.enableUnsafeEval
26
+ * @returns A strict Content Security Policy string.
27
+ */
28
+ static getStrictCsp(hashes?: string[], cspOptions?: {
29
+ enableUnsafeEval?: boolean;
30
+ }): string;
31
+ /**
32
+ * Enables a CSP via a meta tag at the beginning of the document.
33
+ * Warning: It's recommended to set CSP as HTTP response header instead of
34
+ * using a meta tag. Injections before the meta tag will not be covered by CSP
35
+ * and meta tags don't support CSP in report-only mode.
36
+ * @param csp A Content Security Policy string.
37
+ */
38
+ addMetaTag(csp: string): void;
39
+ /**
40
+ * Replaces all sourced scripts with a single inline script that can be hashed
41
+ */
42
+ refactorSourcedScriptsForHashBasedCsp(): void;
43
+ /**
44
+ * Returns a list of hashes of all inline scripts found in the HTML document.
45
+ * @returns A list of sha-256 hashes of inline scripts.
46
+ */
47
+ hashAllInlineScripts(): string[];
48
+ /**
49
+ * Returns JS code for dynamically loading sourced (external) scripts.
50
+ * @param scriptInfoList A list of objects containing src and type for scripts that should be loaded
51
+ * @returns JS code for loading scripts.
52
+ */
53
+ static createLoaderScript(scriptInfoList: {
54
+ src: string;
55
+ type?: string;
56
+ }[]): string | undefined;
57
+ /**
58
+ * Calculates a CSP compatible hash of an inline script.
59
+ * @param scriptText Text between opening and closing script tag. Has to
60
+ * include whitespaces and newlines!
61
+ * @returns A sha-256 hash of the script.
62
+ */
63
+ static hashInlineScript(scriptText: string): string;
64
+ }
65
+ export { CSP };