vite-plugin-csp-dev 1.0.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.
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025-present gatisr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # vite-plugin-csp-dev
2
+
3
+ A Vite plugin to add Content Security Policy (CSP) headers to your application in serve mode.
4
+
5
+ In order to set CSP headers in production builds, you must configure your web server accordingly, as this plugin only affects the development server.
6
+
7
+ ## Usage
8
+
9
+ In your `vite.config.mjs`:
10
+
11
+ ```js
12
+ import { secureHeaders } from 'vite-plugin-csp-dev';
13
+
14
+ export default {
15
+ plugins: [
16
+ secureHeaders({
17
+ reportOnly: false, // default: false - Report CSP violations instead of blocking them.
18
+ processI18n: true, // default: true - Process i18n.
19
+ defaultSrc: "'self'", // default: "'self'" - Value for default-src directive in CSP.
20
+ noncePlaceholder: 'NONCE_PLACEHOLDER', // default: 'NONCE_PLACEHOLDER' - Placeholder for nonce in HTML.
21
+ xssProtection: '1; mode=block', // default: '1; mode=block' - Value for X-XSS-Protection header.
22
+ frameOptions: 'DENY', // default: 'DENY' - Value for X-Frame-Options header.
23
+ contentTypeOptions: 'nosniff', // default: 'nosniff' - Value for X-Content-Type-Options header.
24
+ referrerPolicy: 'strict-origin-when-cross-origin', // default: 'strict-origin-when-cross-origin' - Value for Referrer-Policy header.
25
+ permissionsPolicy: 'camera=(), microphone=(), geolocation=()', // default: 'camera=(), microphone=(), geolocation()' - Value for Permissions-Policy header.
26
+ cacheControl: 'no-store, max-age=0', // default: 'no-store, max-age=0' - Value for Cache-Control header.
27
+ scriptSrc: (nonce) => `'self' 'nonce-${nonce}'`, // default: `'self' 'nonce-${nonce}'` - Function to generate script-src directive in CSP
28
+ styleSrc: (nonce) => `'self' 'nonce-${nonce}'`, // default: `'self' 'nonce-${nonce}'` - Function to generate style-src directive in CSP
29
+ workerSrc: "'self'", // default: "'self'" - Value for worker-src directive in CSP.
30
+ connectSrc: "'self'", // default: "'self'" - Value for connect-src directive in CSP.
31
+ imgSrc: "'self' data:", // default: "'self' data:" - Value for img-src directive in CSP.
32
+ fontSrc: "'self'", // default: "'self'" - Value for font-src directive in CSP.
33
+ objectSrc: "'none'", // default: "'none'" - Value for object-src directive in CSP.
34
+ frameSrc: "'self'", // default: "'self'" - Value for frame-src directive in CSP.
35
+ baseUri: "'self'", // default: "'self'" - Value for base-uri directive in CSP.
36
+ formAction: "'self'", // default: "'self'" - Value for form-action directive in CSP.
37
+ frameAncestors: "'none'", // default: "'none'" - Value for frame-ancestors directive in CSP.
38
+ }),
39
+ ],
40
+ };
41
+ ```
@@ -0,0 +1,97 @@
1
+ import { Plugin as Plugin_2 } from 'vite';
2
+
3
+ declare type Options = {
4
+ /**
5
+ * - Whether to use Content-Security-Policy-Report-Only header instead of Content-Security-Policy header. For development purposes.
6
+ */
7
+ reportOnly?: boolean;
8
+ /**
9
+ * - Whether to process i18n
10
+ */
11
+ processI18n?: boolean;
12
+ /**
13
+ * - Value for default-src directive in CSP
14
+ */
15
+ defaultSrc?: string;
16
+ /**
17
+ * - The placeholder for nonce in the HTML
18
+ */
19
+ noncePlaceholder?: string;
20
+ /**
21
+ * - Value for X-XSS-Protection header
22
+ */
23
+ xssProtection?: string;
24
+ /**
25
+ * - Value for X-Frame-Options header
26
+ */
27
+ frameOptions?: string;
28
+ /**
29
+ * - Value for X-Content-Type-Options header
30
+ */
31
+ contentTypeOptions?: string;
32
+ /**
33
+ * - Value for Referrer-Policy header
34
+ */
35
+ referrerPolicy?: string;
36
+ /**
37
+ * - Value for Permissions-Policy header
38
+ */
39
+ permissionsPolicy?: string;
40
+ /**
41
+ * - Value for Cache-Control header
42
+ */
43
+ cacheControl?: string;
44
+ /**
45
+ * - Function to generate script-src directive in CSP, nonce is the generated nonce value
46
+ */
47
+ scriptSrc?: (nonce: string) => string;
48
+ /**
49
+ * - Function to generate style-src directive in CSP, nonce is the generated nonce value
50
+ */
51
+ styleSrc?: (nonce: string) => string;
52
+ /**
53
+ * - Value for img-src directive in CSP
54
+ */
55
+ imgSrc?: string;
56
+ /**
57
+ * - Value for font-src directive in CSP
58
+ */
59
+ fontSrc?: string;
60
+ /**
61
+ * - Value for object-src directive in CSP
62
+ */
63
+ objectSrc?: string;
64
+ /**
65
+ * - Value for frame-src directive in CSP
66
+ */
67
+ frameSrc?: string;
68
+ /**
69
+ * - Value for base-uri directive in CSP
70
+ */
71
+ baseUri?: string;
72
+ /**
73
+ * - Value for form-action directive in CSP
74
+ */
75
+ formAction?: string;
76
+ /**
77
+ * - Value for frame-ancestors directive in CSP
78
+ */
79
+ frameAncestors?: string;
80
+ /**
81
+ * - Value for worker-src directive in CSP
82
+ */
83
+ workerSrc?: string;
84
+ /**
85
+ * - Value for connect-src directive in CSP
86
+ */
87
+ connectSrc?: string;
88
+ };
89
+
90
+ /**
91
+ * Creates a Vite plugin for handling Content Security Policy with nonce and i18n processing
92
+ * @param {Options} [options] - Configuration options for the plugin
93
+ * @returns {import('vite').Plugin} The Vite plugin instance
94
+ */
95
+ export declare function secureHeaders(options?: Options): Plugin_2;
96
+
97
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,138 @@
1
+ import { createHash as B } from "node:crypto";
2
+ const L = () => B("sha256").update(Date.now().toString()).digest("base64");
3
+ function I(w = {}) {
4
+ const {
5
+ reportOnly: N = !1,
6
+ processI18n: v = !0,
7
+ defaultSrc: S = "'self'",
8
+ noncePlaceholder: C = "NONCE_PLACEHOLDER",
9
+ xssProtection: p = "1; mode=block",
10
+ frameOptions: m = "DENY",
11
+ contentTypeOptions: u = "nosniff",
12
+ referrerPolicy: f = "strict-origin-when-cross-origin",
13
+ permissionsPolicy: y = "camera=(), microphone=(), geolocation=()",
14
+ cacheControl: $ = "no-store, max-age=0",
15
+ scriptSrc: g,
16
+ styleSrc: h,
17
+ imgSrc: A = "'self' data:",
18
+ fontSrc: H = "'self'",
19
+ objectSrc: P = "'none'",
20
+ frameSrc: E = "'self'",
21
+ baseUri: O = "'self'",
22
+ formAction: b = "'self'",
23
+ frameAncestors: k = "'none'",
24
+ workerSrc: j = "'self'",
25
+ connectSrc: x = "'self'"
26
+ } = w;
27
+ let i;
28
+ return {
29
+ name: "vite-plugin-csp-dev",
30
+ enforce: "post",
31
+ apply: "serve",
32
+ config(n, s) {
33
+ if (n.resolve = n.resolve || {}, n.resolve.alias = n.resolve.alias || {}, v) {
34
+ n.plugins = n.plugins || [];
35
+ const e = s.command === "serve";
36
+ let o = "vue-i18n/dist/vue-i18n.esm-browser.prod.js";
37
+ e && (o = "vue-i18n/dist/vue-i18n.esm-browser.js"), n.resolve.alias["vue-i18n"] = o;
38
+ }
39
+ return n;
40
+ },
41
+ configResolved(n) {
42
+ i = n.command === "serve" ? L() : C, n.html = n.html || {}, n.html.cspNonce = i;
43
+ },
44
+ /**
45
+ * @param {import('vite').ViteDevServer} server - The Vite development server
46
+ */
47
+ configureServer(n) {
48
+ n.middlewares.use((s, e, o) => {
49
+ const t = i, c = g ? g(t) : `'self' 'nonce-${t}'`, r = h ? h(t) : `'self' 'nonce-${t}'`, l = [
50
+ `default-src ${S}`,
51
+ `script-src ${c}`,
52
+ `style-src ${r}`,
53
+ `img-src ${A}`,
54
+ `font-src ${H}`,
55
+ `object-src ${P}`,
56
+ `base-uri ${O}`,
57
+ `frame-src ${E}`,
58
+ `form-action ${b}`,
59
+ `frame-ancestors ${k}`,
60
+ `worker-src ${j}`,
61
+ `connect-src ${x}`,
62
+ "upgrade-insecure-requests"
63
+ ].join("; "), a = N ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
64
+ e.setHeader(a, l), p && e.setHeader("X-XSS-Protection", p), m && e.setHeader("X-Frame-Options", m), u && e.setHeader("X-Content-Type-Options", u), f && e.setHeader("Referrer-Policy", f), y && e.setHeader("Permissions-Policy", y), $ && e.setHeader("Cache-Control", $), o();
65
+ });
66
+ },
67
+ /**
68
+ * @param {string} html - The original HTML
69
+ * @param {import('vite').IndexHtmlTransformContext} _ctx - The transform context
70
+ * @returns {string} The transformed HTML with nonces added
71
+ */
72
+ // eslint-disable-next-line no-unused-vars
73
+ transformIndexHtml(n, s) {
74
+ const e = i, o = `
75
+ <script nonce="${e}">
76
+ (function() {
77
+ const originalCreateElement = document.createElement;
78
+ document.createElement = function() {
79
+ const element = originalCreateElement.apply(document, arguments);
80
+ if (arguments[0].toLowerCase() === 'style' || arguments[0].toLowerCase() === 'script') {
81
+ element.nonce = "${e}";
82
+ }
83
+ return element;
84
+ };
85
+ // Override insertBefore to add nonce to dynamically inserted style tags
86
+ const originalInsertBefore = Element.prototype.insertBefore;
87
+ Element.prototype.insertBefore = function(newNode, referenceNode) {
88
+ if (newNode.tagName && newNode.tagName.toLowerCase() === 'style' && !newNode.nonce) {
89
+ newNode.nonce = "${e}";
90
+ }
91
+ return originalInsertBefore.call(this, newNode, referenceNode);
92
+ };
93
+ // Override appendChild to add nonce to dynamically inserted style tags
94
+ const originalAppendChild = Element.prototype.appendChild;
95
+ Element.prototype.appendChild = function(newNode) {
96
+ if (newNode.tagName && newNode.tagName.toLowerCase() === 'style' && !newNode.nonce) {
97
+ newNode.nonce = "${e}";
98
+ }
99
+ return originalAppendChild.call(this, newNode);
100
+ };
101
+ })();
102
+ <\/script>
103
+ `, t = /<link\s+([^>]*)>/g;
104
+ return n.replaceAll("<script", `<script nonce="${e}"`).replaceAll("<style", `<style nonce="${e}"`).replace(t, (c, r) => {
105
+ let l = r.replace(/\s*\/\s*$/, "");
106
+ return l.includes("nonce=") || (l += ` nonce="${e}"`), `<link ${l.trim()}>`;
107
+ }).replaceAll('style="', `style="nonce="${e}" `).replace("</head>", `${o}</head>`);
108
+ },
109
+ /**
110
+ //@param {import('rollup').OutputOptions} buildOptions - The build options
111
+ //@param {import('rollup').OutputBundle} bundle - The output bundle
112
+ //@returns {import('rollup').OutputBundle} The modified bundle with nonces added to HTML assets
113
+ */
114
+ // @ts-ignore
115
+ generateBundle(n, s) {
116
+ const e = i, o = /<link\s+([^>]*?)(\/?\s*>)/g;
117
+ return Object.keys(s).reduce((t, c) => {
118
+ const r = s[c];
119
+ return r.type === "asset" && r.fileName.endsWith(".html") ? {
120
+ ...t,
121
+ [c]: {
122
+ ...r,
123
+ source: r.source.replaceAll("<script", `<script nonce="${e}"`).replaceAll("<style", `<style nonce="${e}"`).replace(o, (l, a) => {
124
+ let d = a.replace(/\s*\/\s*$/, "");
125
+ return d.includes("nonce=") || (d += ` nonce="${e}"`), `<link ${d.trim()}>`;
126
+ }).replaceAll('style="', `style="nonce="${e}" `)
127
+ }
128
+ } : {
129
+ ...t,
130
+ [c]: r
131
+ };
132
+ }, {});
133
+ }
134
+ };
135
+ }
136
+ export {
137
+ I as secureHeaders
138
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "vite-plugin-csp-dev",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "A Vite plugin to add Content Security Policy (CSP) headers to your application in serve mode.",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "vite build",
20
+ "lint": "oxlint .",
21
+ "format": "prettier --write .",
22
+ "prepublishOnly": "pnpm run build"
23
+ },
24
+ "keywords": [
25
+ "vite",
26
+ "plugin"
27
+ ],
28
+ "author": "gatisr",
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@types/node": "^25.0.3",
32
+ "@zzdats/prettier-config": "^1.0.0",
33
+ "oxlint": "^1.36.0",
34
+ "prettier": "^3.7.4",
35
+ "vite": "^7.3.0",
36
+ "vite-plugin-dts": "^4.5.4"
37
+ },
38
+ "peerDependencies": {
39
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
40
+ }
41
+ }