@rimelight/security 0.0.7 → 0.0.8
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/dist/config/index.d.mts +2 -2
- package/dist/{csp-DM1NVenC.d.mts → csp-ESHHTg-n.d.mts} +1 -1
- package/dist/index.d.mts +10 -11
- package/dist/index.mjs +5 -6
- package/dist/middleware/index.d.mts +6 -7
- package/dist/middleware/index.mjs +62 -12
- package/dist/{types-CY5GDm3Z.d.mts → types-Bg8w94Fn.d.mts} +4 -4
- package/package.json +3 -3
- package/src/components/SecurityHead.astro +43 -0
package/dist/config/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as SecurityOptions } from "../types-
|
|
2
|
-
import { t as buildCspHeader } from "../csp-
|
|
1
|
+
import { t as SecurityOptions } from "../types-Bg8w94Fn.mjs";
|
|
2
|
+
import { t as buildCspHeader } from "../csp-ESHHTg-n.mjs";
|
|
3
3
|
export { SecurityOptions, buildCspHeader };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as SecurityOptions } from "./types-
|
|
2
|
-
import { t as buildCspHeader } from "./csp-
|
|
1
|
+
import { t as SecurityOptions } from "./types-Bg8w94Fn.mjs";
|
|
2
|
+
import { t as buildCspHeader } from "./csp-ESHHTg-n.mjs";
|
|
3
3
|
//#region src/vite.d.ts
|
|
4
|
-
interface RimelightSecurityPlugin {
|
|
4
|
+
export interface RimelightSecurityPlugin {
|
|
5
5
|
name: string;
|
|
6
6
|
enforce?: "pre" | "post";
|
|
7
7
|
resolveId?: (id: string) => string | null | undefined;
|
|
@@ -10,19 +10,18 @@ interface RimelightSecurityPlugin {
|
|
|
10
10
|
generateBundle?: (options: any, bundle: Record<string, any>) => Promise<void> | void;
|
|
11
11
|
[key: string]: any;
|
|
12
12
|
}
|
|
13
|
-
interface SecurityPluginOptions extends SecurityOptions {
|
|
13
|
+
export interface SecurityPluginOptions extends SecurityOptions {
|
|
14
14
|
/**
|
|
15
15
|
* Additional external script/style URLs to fetch and hash for SRI at build time.
|
|
16
16
|
*/
|
|
17
17
|
resources?: string[];
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Pure Vite Security & SRI Plugin.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* 4. Exposes virtual:sri-manifest with a unified URL -> hash mapping.
|
|
20
|
+
* Pure Vite Security & SRI Plugin. 1. Accepts framework-agnostic security options (domain, imgSrc,
|
|
21
|
+
* etc.) and exposes virtual:rimelight-security-config. 2. Fetches and hashes external 3rd-party
|
|
22
|
+
* scripts/styles at build time. 3. Emits SRI hashes for locally bundled JS/CSS chunks
|
|
23
|
+
* (Nuxt-security style). 4. Exposes virtual:sri-manifest with a unified URL -> hash mapping.
|
|
25
24
|
*/
|
|
26
|
-
declare function security(options?: SecurityPluginOptions): RimelightSecurityPlugin;
|
|
25
|
+
export declare function security(options?: SecurityPluginOptions): RimelightSecurityPlugin;
|
|
27
26
|
//#endregion
|
|
28
|
-
export {
|
|
27
|
+
export { SecurityOptions, buildCspHeader, security as sri };
|
package/dist/index.mjs
CHANGED
|
@@ -6,11 +6,10 @@ function hashContent(buffer) {
|
|
|
6
6
|
return `sha384-${createHash("sha384").update(buffer).digest("base64")}`;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* Pure Vite Security & SRI Plugin.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* 4. Exposes virtual:sri-manifest with a unified URL -> hash mapping.
|
|
9
|
+
* Pure Vite Security & SRI Plugin. 1. Accepts framework-agnostic security options (domain, imgSrc,
|
|
10
|
+
* etc.) and exposes virtual:rimelight-security-config. 2. Fetches and hashes external 3rd-party
|
|
11
|
+
* scripts/styles at build time. 3. Emits SRI hashes for locally bundled JS/CSS chunks
|
|
12
|
+
* (Nuxt-security style). 4. Exposes virtual:sri-manifest with a unified URL -> hash mapping.
|
|
14
13
|
*/
|
|
15
14
|
function security(options = {}) {
|
|
16
15
|
const sriManifest = {};
|
|
@@ -23,7 +22,7 @@ function security(options = {}) {
|
|
|
23
22
|
name: "vite-plugin-rimelight-security",
|
|
24
23
|
enforce: "post",
|
|
25
24
|
async buildStart() {
|
|
26
|
-
if (process.env
|
|
25
|
+
if (process.env["NODE_ENV"] === "development") return;
|
|
27
26
|
await Promise.all(externalUrls.map(async (url) => {
|
|
28
27
|
if (!url.startsWith("https://")) return;
|
|
29
28
|
try {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as SecurityOptions } from "../types-
|
|
1
|
+
import { t as SecurityOptions } from "../types-Bg8w94Fn.mjs";
|
|
2
2
|
//#region src/middleware/security.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* Universal Security Middleware for Hono and Web Standards (Fetch API).
|
|
5
|
-
*
|
|
4
|
+
* Universal Security Middleware for Hono and Web Standards (Fetch API). Injects security headers,
|
|
5
|
+
* CSP with per-request nonce, and SRI attributes.
|
|
6
6
|
*/
|
|
7
|
-
declare function security(options?: SecurityOptions): (c: any, next: any) => Promise<Response | void>;
|
|
7
|
+
export declare function security(options?: SecurityOptions): (c: any, next: any) => Promise<Response | void>;
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region src/middleware/dev-only.d.ts
|
|
10
10
|
/**
|
|
@@ -17,6 +17,5 @@ declare function security(options?: SecurityOptions): (c: any, next: any) => Pro
|
|
|
17
17
|
* import { devOnly } from "@rimelight/security/middleware"
|
|
18
18
|
* app.use(devOnly)
|
|
19
19
|
*/
|
|
20
|
-
declare const devOnly: (c: any, next: any) => Promise<Response>;
|
|
21
|
-
//#endregion
|
|
22
|
-
export { devOnly, security };
|
|
20
|
+
export declare const devOnly: (c: any, next: any) => Promise<Response>;
|
|
21
|
+
//#endregion
|
|
@@ -10,8 +10,15 @@ function isConfigRecord(obj) {
|
|
|
10
10
|
}
|
|
11
11
|
const manifest$1 = isManifestRecord(manifest) ? manifest : {};
|
|
12
12
|
const defaultPluginConfig = isConfigRecord(config) ? config : {};
|
|
13
|
-
function applySecurityHeaders(response, options, origin, nonce) {
|
|
13
|
+
function applySecurityHeaders(response, options, origin, nonce, reportOnlyOptions) {
|
|
14
14
|
const cspHeader = buildCspHeader(options, nonce);
|
|
15
|
+
const reportOnlyHeader = reportOnlyOptions ? buildCspHeader({
|
|
16
|
+
...reportOnlyOptions,
|
|
17
|
+
directives: {
|
|
18
|
+
...reportOnlyOptions.directives,
|
|
19
|
+
"upgrade-insecure-requests": false
|
|
20
|
+
}
|
|
21
|
+
}, nonce) : void 0;
|
|
15
22
|
const headers = new Headers(response.headers);
|
|
16
23
|
headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
17
24
|
headers.set("X-Content-Type-Options", "nosniff");
|
|
@@ -21,6 +28,7 @@ function applySecurityHeaders(response, options, origin, nonce) {
|
|
|
21
28
|
headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), payment=(), usb=()");
|
|
22
29
|
headers.set("No-Vary-Search", "except=(\"q\" \"search\" \"locale\"), params=?1");
|
|
23
30
|
if (cspHeader) headers.set("Content-Security-Policy", cspHeader);
|
|
31
|
+
if (reportOnlyHeader) headers.set("Content-Security-Policy-Report-Only", reportOnlyHeader);
|
|
24
32
|
if (origin) headers.set("Link", [
|
|
25
33
|
`<${origin}/sitemap-index.xml>; rel="sitemap"`,
|
|
26
34
|
`<${origin}/llms.txt>; rel="llms"`,
|
|
@@ -35,8 +43,8 @@ function applySecurityHeaders(response, options, origin, nonce) {
|
|
|
35
43
|
});
|
|
36
44
|
}
|
|
37
45
|
/**
|
|
38
|
-
* Universal Security Middleware for Hono and Web Standards (Fetch API).
|
|
39
|
-
*
|
|
46
|
+
* Universal Security Middleware for Hono and Web Standards (Fetch API). Injects security headers,
|
|
47
|
+
* CSP with per-request nonce, and SRI attributes.
|
|
40
48
|
*/
|
|
41
49
|
function security(options = {}) {
|
|
42
50
|
const mergedOptions = {
|
|
@@ -49,17 +57,17 @@ function security(options = {}) {
|
|
|
49
57
|
scriptResources: [...defaultPluginConfig.scriptResources || [], ...options.scriptResources || []],
|
|
50
58
|
styleResources: [...defaultPluginConfig.styleResources || [], ...options.styleResources || []],
|
|
51
59
|
directives: {
|
|
52
|
-
...defaultPluginConfig.directives
|
|
53
|
-
...options.directives
|
|
60
|
+
...defaultPluginConfig.directives,
|
|
61
|
+
...options.directives
|
|
54
62
|
},
|
|
55
63
|
headers: {
|
|
56
|
-
...defaultPluginConfig.headers
|
|
57
|
-
...options.headers
|
|
64
|
+
...defaultPluginConfig.headers,
|
|
65
|
+
...options.headers
|
|
58
66
|
}
|
|
59
67
|
};
|
|
60
68
|
return async (c, next) => {
|
|
61
|
-
const isProd = import.meta.env?.PROD ?? process.env
|
|
62
|
-
const isDev = import.meta.env?.DEV ?? process.env
|
|
69
|
+
const isProd = import.meta.env?.PROD ?? process.env["NODE_ENV"] === "production";
|
|
70
|
+
const isDev = import.meta.env?.DEV ?? process.env["NODE_ENV"] === "development";
|
|
63
71
|
const req = c.req?.raw || c.request || c.req;
|
|
64
72
|
const proto = req?.headers?.get("x-forwarded-proto");
|
|
65
73
|
if (isProd && proto === "http" && req?.url) {
|
|
@@ -76,13 +84,55 @@ function security(options = {}) {
|
|
|
76
84
|
return c.res;
|
|
77
85
|
}
|
|
78
86
|
if (isDev) {
|
|
79
|
-
|
|
87
|
+
const devDirectives = {
|
|
88
|
+
...mergedOptions.directives,
|
|
89
|
+
"default-src": false,
|
|
90
|
+
"script-src": [
|
|
91
|
+
"'self'",
|
|
92
|
+
"'unsafe-inline'",
|
|
93
|
+
"'unsafe-eval'",
|
|
94
|
+
"https:",
|
|
95
|
+
"http:",
|
|
96
|
+
"ws:",
|
|
97
|
+
"wss:"
|
|
98
|
+
],
|
|
99
|
+
"style-src": [
|
|
100
|
+
"'self'",
|
|
101
|
+
"'unsafe-inline'",
|
|
102
|
+
"https:",
|
|
103
|
+
"http:"
|
|
104
|
+
],
|
|
105
|
+
"connect-src": [
|
|
106
|
+
"'self'",
|
|
107
|
+
"ws:",
|
|
108
|
+
"wss:",
|
|
109
|
+
"http:",
|
|
110
|
+
"https:"
|
|
111
|
+
],
|
|
112
|
+
"img-src": [
|
|
113
|
+
"'self'",
|
|
114
|
+
"data:",
|
|
115
|
+
"blob:",
|
|
116
|
+
"https:",
|
|
117
|
+
"http:"
|
|
118
|
+
]
|
|
119
|
+
};
|
|
120
|
+
const reportOnlySimulation = {
|
|
80
121
|
...mergedOptions,
|
|
81
122
|
directives: {
|
|
82
123
|
...mergedOptions.directives,
|
|
83
|
-
"
|
|
124
|
+
"style-src": ["'self'", "'unsafe-inline'"],
|
|
125
|
+
"script-src": [
|
|
126
|
+
"'self'",
|
|
127
|
+
"'unsafe-inline'",
|
|
128
|
+
"'unsafe-eval'"
|
|
129
|
+
]
|
|
84
130
|
}
|
|
85
|
-
}
|
|
131
|
+
};
|
|
132
|
+
c.res = applySecurityHeaders(response, {
|
|
133
|
+
...mergedOptions,
|
|
134
|
+
directives: devDirectives
|
|
135
|
+
}, origin, void 0, reportOnlySimulation);
|
|
86
136
|
return c.res;
|
|
87
137
|
}
|
|
88
138
|
const nonce = crypto.randomUUID().replace(/-/g, "");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
2
|
interface SecurityOptions {
|
|
3
3
|
/**
|
|
4
|
-
* The base domain name for this project (e.g. "idantity.me") used to autoconfigure
|
|
5
|
-
*
|
|
4
|
+
* The base domain name for this project (e.g. "idantity.me") used to autoconfigure standard
|
|
5
|
+
* allowed domains and img-src CDN targets.
|
|
6
6
|
*/
|
|
7
7
|
domain?: string;
|
|
8
8
|
/**
|
|
@@ -34,8 +34,8 @@ interface SecurityOptions {
|
|
|
34
34
|
*/
|
|
35
35
|
trustedTypes?: boolean;
|
|
36
36
|
/**
|
|
37
|
-
* Granular CSP directive overrides. Values are arrays of directive tokens, e.g.:
|
|
38
|
-
*
|
|
37
|
+
* Granular CSP directive overrides. Values are arrays of directive tokens, e.g.: {
|
|
38
|
+
* "frame-ancestors": ["'self'"] }
|
|
39
39
|
*/
|
|
40
40
|
directives?: Record<string, string[] | string | boolean>;
|
|
41
41
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimelight/security",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Rimelight Entertainment's Security Package",
|
|
6
6
|
"homepage": "https://rimelight.com/docs",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@astrojs/check": "0.9.10",
|
|
43
|
-
"@rimelight/config": "0.0.
|
|
44
|
-
"astro": "7.3.
|
|
43
|
+
"@rimelight/config": "0.0.5",
|
|
44
|
+
"astro": "7.3.2",
|
|
45
45
|
"typescript": "6.0.3"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
@@ -121,3 +121,46 @@
|
|
|
121
121
|
});
|
|
122
122
|
})();
|
|
123
123
|
</script>
|
|
124
|
+
|
|
125
|
+
<!-- Development CSP Violation Reporter -->
|
|
126
|
+
{import.meta.env.DEV && (
|
|
127
|
+
<script>
|
|
128
|
+
(function() {
|
|
129
|
+
// Ignore known internal dev-only tooling noise
|
|
130
|
+
function isDevInternal(e: SecurityPolicyViolationEvent): boolean {
|
|
131
|
+
const file = e.sourceFile || "";
|
|
132
|
+
const sample = e.sample || "";
|
|
133
|
+
const blockedUri = e.blockedURI || "";
|
|
134
|
+
|
|
135
|
+
if (file.includes("dev-toolbar") || file.includes("toolbar-") || file.includes("astro_runtime_client")) return true;
|
|
136
|
+
if (file.includes("vite/client") || file.includes("@vite/client") || file.includes("/@fs/") || file.includes("/@id/")) return true;
|
|
137
|
+
if (file.includes("node_modules/.vite") || file.includes("client:")) return true;
|
|
138
|
+
if (blockedUri.startsWith("ws://") || blockedUri.startsWith("wss://")) return true;
|
|
139
|
+
if (sample.includes("--astro-dev-toolbar") || sample.includes("astro-dev-toolbar")) return true;
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const reported = new Set<string>();
|
|
144
|
+
|
|
145
|
+
window.addEventListener("securitypolicyviolation", function(e) {
|
|
146
|
+
if (e.disposition !== "report") return; // Only notify on report-only (prod violation simulation)
|
|
147
|
+
if (isDevInternal(e)) return;
|
|
148
|
+
|
|
149
|
+
const key = `${e.effectiveDirective}:${e.blockedURI || e.sample || ""}`;
|
|
150
|
+
if (reported.has(key)) return;
|
|
151
|
+
reported.add(key);
|
|
152
|
+
|
|
153
|
+
console.warn(
|
|
154
|
+
`%c[Rimelight Security]%c Allowed in DEV, but will fail in PRODUCTION:\n` +
|
|
155
|
+
`Directive: ${e.effectiveDirective}\n` +
|
|
156
|
+
(e.blockedURI ? `Blocked URI: ${e.blockedURI}\n` : "") +
|
|
157
|
+
(e.sourceFile ? `Source: ${e.sourceFile}:${e.lineNumber || 0}\n` : "") +
|
|
158
|
+
(e.sample ? `Sample: ${e.sample.slice(0, 100)}\n` : "") +
|
|
159
|
+
`Fix: Add to imgSrc, scriptResources, styleResources or use nonces in astro.config.ts`,
|
|
160
|
+
"background: #e11d48; color: white; padding: 2px 6px; border-radius: 4px; font-weight: bold;",
|
|
161
|
+
"color: inherit; font-weight: normal;"
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
})();
|
|
165
|
+
</script>
|
|
166
|
+
)}
|