@rimelight/security 0.0.21 → 0.0.24
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/middleware/construction.mjs +6 -0
- package/dist/middleware/security.mjs +1 -10
- package/package.json +6 -6
- package/src/components/SecurityHead.astro +166 -166
- package/src/layouts/ConstructionLayout.astro +1 -1
- package/src/middleware/construction.ts +6 -0
- package/src/middleware/security.ts +3 -5
|
@@ -8,8 +8,14 @@ const DEFAULT_WHITELISTED_PATTERNS = [
|
|
|
8
8
|
"/_astro/",
|
|
9
9
|
"/_image",
|
|
10
10
|
"/favicon.",
|
|
11
|
+
"/apple-touch-icon",
|
|
12
|
+
"/site.webmanifest",
|
|
13
|
+
"manifest.json",
|
|
14
|
+
".webmanifest",
|
|
11
15
|
"/robots.txt",
|
|
12
16
|
"/sitemap",
|
|
17
|
+
"/rss.xml",
|
|
18
|
+
"/llms.txt",
|
|
13
19
|
"/.well-known/"
|
|
14
20
|
];
|
|
15
21
|
const getPassphrase = (c, options) => {
|
|
@@ -122,16 +122,7 @@ function security(options = {}) {
|
|
|
122
122
|
};
|
|
123
123
|
const reportOnlySimulation = {
|
|
124
124
|
...mergedOptions,
|
|
125
|
-
directives: {
|
|
126
|
-
...mergedOptions.directives,
|
|
127
|
-
"script-src-attr": ["'none'"],
|
|
128
|
-
"style-src": ["'self'", "'unsafe-inline'"],
|
|
129
|
-
"script-src": [
|
|
130
|
-
"'self'",
|
|
131
|
-
"'unsafe-inline'",
|
|
132
|
-
"'unsafe-eval'"
|
|
133
|
-
]
|
|
134
|
-
}
|
|
125
|
+
directives: { ...mergedOptions.directives }
|
|
135
126
|
};
|
|
136
127
|
c.res = applySecurityHeaders(response, {
|
|
137
128
|
...mergedOptions,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimelight/security",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Rimelight Entertainment's Security Package",
|
|
6
6
|
"homepage": "https://rimelight.com/docs",
|
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@rimelight/i18n": "0.0.
|
|
54
|
-
"@rimelight/seo": "0.0.
|
|
55
|
-
"@rimelight/ui": "0.0.
|
|
53
|
+
"@rimelight/i18n": "0.0.20",
|
|
54
|
+
"@rimelight/seo": "0.0.17",
|
|
55
|
+
"@rimelight/ui": "0.0.60"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@astrojs/check": "0.9.10",
|
|
59
|
-
"@rimelight/config": "0.0.
|
|
59
|
+
"@rimelight/config": "0.0.20",
|
|
60
60
|
"astro": "7.3.3",
|
|
61
61
|
"hono": "4.13.8",
|
|
62
62
|
"typescript": "6.0.3"
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
74
|
-
"node": ">=26.
|
|
74
|
+
"node": ">=26.9.0"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
77
77
|
"build": "vp pack",
|
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* SecurityHead — Security infrastructure scripts for CSP Nonce patching & Trusted Types.
|
|
4
|
-
* Provided by @rimelight/security.
|
|
5
|
-
*/
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
<!-- Trusted Types Policy -->
|
|
9
|
-
<script>
|
|
10
|
-
(function() {
|
|
11
|
-
const w = window as any;
|
|
12
|
-
if (w.trustedTypes && w.trustedTypes.createPolicy) {
|
|
13
|
-
try {
|
|
14
|
-
if (!w.trustedTypes.defaultPolicy) {
|
|
15
|
-
w.trustedTypes.createPolicy('default', {
|
|
16
|
-
createHTML: function(input: string) { return input; },
|
|
17
|
-
createScript: function(input: string) { return input; },
|
|
18
|
-
createScriptURL: function(input: string) { return input; }
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
} catch {
|
|
22
|
-
// Policy already exists or cannot be created
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
})();
|
|
26
|
-
</script>
|
|
27
|
-
|
|
28
|
-
<!-- CSP Nonce Patching -->
|
|
29
|
-
<script>
|
|
30
|
-
(function() {
|
|
31
|
-
const w = window as any;
|
|
32
|
-
const doc = document as any;
|
|
33
|
-
const elemProto = Element.prototype as any;
|
|
34
|
-
|
|
35
|
-
if (!w.initialCspNonce) {
|
|
36
|
-
w.initialCspNonce = document.querySelector('meta[name="csp-nonce"]')?.getAttribute('content') ||
|
|
37
|
-
Array.from(document.querySelectorAll('script')).find(function(s: any) { return s.nonce; })?.nonce ||
|
|
38
|
-
document.querySelector('script[nonce]')?.getAttribute('nonce') ||
|
|
39
|
-
document.querySelector('style[nonce]')?.getAttribute('nonce');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var orig = doc.createElement;
|
|
43
|
-
doc.createElement = function(tag: string, opts?: ElementCreationOptions) {
|
|
44
|
-
var el = orig.call(document, tag, opts);
|
|
45
|
-
var lowTag = tag.toLowerCase();
|
|
46
|
-
if (lowTag === 'script' || lowTag === 'style') {
|
|
47
|
-
var nonce = w.initialCspNonce;
|
|
48
|
-
if (nonce) {
|
|
49
|
-
el.setAttribute('nonce', nonce);
|
|
50
|
-
el.nonce = nonce;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return el;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
var origAppend = elemProto.appendChild;
|
|
57
|
-
elemProto.appendChild = function(child: any) {
|
|
58
|
-
if (child && (child.tagName === 'SCRIPT' || child.tagName === 'STYLE')) {
|
|
59
|
-
var nonce = w.initialCspNonce;
|
|
60
|
-
if (nonce && !child.getAttribute('nonce')) {
|
|
61
|
-
child.setAttribute('nonce', nonce);
|
|
62
|
-
child.nonce = nonce;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return origAppend.apply(this, [child]);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
var origInsert = elemProto.insertBefore;
|
|
69
|
-
elemProto.insertBefore = function(newChild: any, refChild: any) {
|
|
70
|
-
if (newChild && (newChild.tagName === 'SCRIPT' || newChild.tagName === 'STYLE')) {
|
|
71
|
-
var nonce = w.initialCspNonce;
|
|
72
|
-
if (nonce && !newChild.getAttribute('nonce')) {
|
|
73
|
-
newChild.setAttribute('nonce', nonce);
|
|
74
|
-
newChild.nonce = nonce;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return origInsert.apply(this, [newChild, refChild]);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
var origAppendMethod = elemProto.append;
|
|
81
|
-
if (origAppendMethod) {
|
|
82
|
-
elemProto.append = function(...nodes: (string | Node)[]) {
|
|
83
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
84
|
-
var arg = nodes[i] as any;
|
|
85
|
-
if (arg && (arg.tagName === 'SCRIPT' || arg.tagName === 'STYLE')) {
|
|
86
|
-
var nonce = w.initialCspNonce;
|
|
87
|
-
if (nonce && !arg.getAttribute('nonce')) {
|
|
88
|
-
arg.setAttribute('nonce', nonce);
|
|
89
|
-
arg.nonce = nonce;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return origAppendMethod.apply(this, nodes);
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
var origPrependMethod = elemProto.prepend;
|
|
98
|
-
if (origPrependMethod) {
|
|
99
|
-
elemProto.prepend = function(...nodes: (string | Node)[]) {
|
|
100
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
101
|
-
var arg = nodes[i] as any;
|
|
102
|
-
if (arg && (arg.tagName === 'SCRIPT' || arg.tagName === 'STYLE')) {
|
|
103
|
-
var nonce = w.initialCspNonce;
|
|
104
|
-
if (nonce && !arg.getAttribute('nonce')) {
|
|
105
|
-
arg.setAttribute('nonce', nonce);
|
|
106
|
-
arg.nonce = nonce;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return origPrependMethod.apply(this, nodes);
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
document.addEventListener('astro:before-swap', function(e: any) {
|
|
115
|
-
var incomingNonce =
|
|
116
|
-
e.newDocument?.querySelector('meta[name="csp-nonce"]')?.getAttribute('content') ||
|
|
117
|
-
Array.from((e.newDocument?.querySelectorAll('script[nonce]') || []) as any[]).find(function(s) { return s.getAttribute('nonce'); })?.getAttribute('nonce');
|
|
118
|
-
if (incomingNonce) {
|
|
119
|
-
w.initialCspNonce = incomingNonce;
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
})();
|
|
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
|
-
)}
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* SecurityHead — Security infrastructure scripts for CSP Nonce patching & Trusted Types.
|
|
4
|
+
* Provided by @rimelight/security.
|
|
5
|
+
*/
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- Trusted Types Policy -->
|
|
9
|
+
<script>
|
|
10
|
+
(function() {
|
|
11
|
+
const w = window as any;
|
|
12
|
+
if (w.trustedTypes && w.trustedTypes.createPolicy) {
|
|
13
|
+
try {
|
|
14
|
+
if (!w.trustedTypes.defaultPolicy) {
|
|
15
|
+
w.trustedTypes.createPolicy('default', {
|
|
16
|
+
createHTML: function(input: string) { return input; },
|
|
17
|
+
createScript: function(input: string) { return input; },
|
|
18
|
+
createScriptURL: function(input: string) { return input; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
// Policy already exists or cannot be created
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<!-- CSP Nonce Patching -->
|
|
29
|
+
<script>
|
|
30
|
+
(function() {
|
|
31
|
+
const w = window as any;
|
|
32
|
+
const doc = document as any;
|
|
33
|
+
const elemProto = Element.prototype as any;
|
|
34
|
+
|
|
35
|
+
if (!w.initialCspNonce) {
|
|
36
|
+
w.initialCspNonce = document.querySelector('meta[name="csp-nonce"]')?.getAttribute('content') ||
|
|
37
|
+
Array.from(document.querySelectorAll('script')).find(function(s: any) { return s.nonce; })?.nonce ||
|
|
38
|
+
document.querySelector('script[nonce]')?.getAttribute('nonce') ||
|
|
39
|
+
document.querySelector('style[nonce]')?.getAttribute('nonce');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
var orig = doc.createElement;
|
|
43
|
+
doc.createElement = function(tag: string, opts?: ElementCreationOptions) {
|
|
44
|
+
var el = orig.call(document, tag, opts);
|
|
45
|
+
var lowTag = tag.toLowerCase();
|
|
46
|
+
if (lowTag === 'script' || lowTag === 'style') {
|
|
47
|
+
var nonce = w.initialCspNonce;
|
|
48
|
+
if (nonce) {
|
|
49
|
+
el.setAttribute('nonce', nonce);
|
|
50
|
+
el.nonce = nonce;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return el;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
var origAppend = elemProto.appendChild;
|
|
57
|
+
elemProto.appendChild = function(child: any) {
|
|
58
|
+
if (child && (child.tagName === 'SCRIPT' || child.tagName === 'STYLE')) {
|
|
59
|
+
var nonce = w.initialCspNonce;
|
|
60
|
+
if (nonce && !child.getAttribute('nonce')) {
|
|
61
|
+
child.setAttribute('nonce', nonce);
|
|
62
|
+
child.nonce = nonce;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return origAppend.apply(this, [child]);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
var origInsert = elemProto.insertBefore;
|
|
69
|
+
elemProto.insertBefore = function(newChild: any, refChild: any) {
|
|
70
|
+
if (newChild && (newChild.tagName === 'SCRIPT' || newChild.tagName === 'STYLE')) {
|
|
71
|
+
var nonce = w.initialCspNonce;
|
|
72
|
+
if (nonce && !newChild.getAttribute('nonce')) {
|
|
73
|
+
newChild.setAttribute('nonce', nonce);
|
|
74
|
+
newChild.nonce = nonce;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return origInsert.apply(this, [newChild, refChild]);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
var origAppendMethod = elemProto.append;
|
|
81
|
+
if (origAppendMethod) {
|
|
82
|
+
elemProto.append = function(...nodes: (string | Node)[]) {
|
|
83
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
84
|
+
var arg = nodes[i] as any;
|
|
85
|
+
if (arg && (arg.tagName === 'SCRIPT' || arg.tagName === 'STYLE')) {
|
|
86
|
+
var nonce = w.initialCspNonce;
|
|
87
|
+
if (nonce && !arg.getAttribute('nonce')) {
|
|
88
|
+
arg.setAttribute('nonce', nonce);
|
|
89
|
+
arg.nonce = nonce;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return origAppendMethod.apply(this, nodes);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var origPrependMethod = elemProto.prepend;
|
|
98
|
+
if (origPrependMethod) {
|
|
99
|
+
elemProto.prepend = function(...nodes: (string | Node)[]) {
|
|
100
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
101
|
+
var arg = nodes[i] as any;
|
|
102
|
+
if (arg && (arg.tagName === 'SCRIPT' || arg.tagName === 'STYLE')) {
|
|
103
|
+
var nonce = w.initialCspNonce;
|
|
104
|
+
if (nonce && !arg.getAttribute('nonce')) {
|
|
105
|
+
arg.setAttribute('nonce', nonce);
|
|
106
|
+
arg.nonce = nonce;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return origPrependMethod.apply(this, nodes);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
document.addEventListener('astro:before-swap', function(e: any) {
|
|
115
|
+
var incomingNonce =
|
|
116
|
+
e.newDocument?.querySelector('meta[name="csp-nonce"]')?.getAttribute('content') ||
|
|
117
|
+
Array.from((e.newDocument?.querySelectorAll('script[nonce]') || []) as any[]).find(function(s) { return s.getAttribute('nonce'); })?.getAttribute('nonce');
|
|
118
|
+
if (incomingNonce) {
|
|
119
|
+
w.initialCspNonce = incomingNonce;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
})();
|
|
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
|
+
)}
|
|
@@ -28,7 +28,6 @@ const {
|
|
|
28
28
|
<!doctype html>
|
|
29
29
|
<html lang="en">
|
|
30
30
|
<head>
|
|
31
|
-
<SecurityHead />
|
|
32
31
|
<SEOHead
|
|
33
32
|
title={title}
|
|
34
33
|
description={description}
|
|
@@ -36,6 +35,7 @@ const {
|
|
|
36
35
|
favicon={favicon}
|
|
37
36
|
noindex={noindex}
|
|
38
37
|
/>
|
|
38
|
+
<SecurityHead />
|
|
39
39
|
<UIHead />
|
|
40
40
|
<Font cssVariable="--font-sans" preload={true} />
|
|
41
41
|
<Font cssVariable="--font-serif" preload={true} />
|
|
@@ -10,8 +10,14 @@ const DEFAULT_WHITELISTED_PATTERNS = [
|
|
|
10
10
|
"/_astro/",
|
|
11
11
|
"/_image",
|
|
12
12
|
"/favicon.",
|
|
13
|
+
"/apple-touch-icon",
|
|
14
|
+
"/site.webmanifest",
|
|
15
|
+
"manifest.json",
|
|
16
|
+
".webmanifest",
|
|
13
17
|
"/robots.txt",
|
|
14
18
|
"/sitemap",
|
|
19
|
+
"/rss.xml",
|
|
20
|
+
"/llms.txt",
|
|
15
21
|
"/.well-known/"
|
|
16
22
|
]
|
|
17
23
|
|
|
@@ -184,14 +184,12 @@ export function security(options: SecurityOptions = {}) {
|
|
|
184
184
|
"img-src": ["'self'", "data:", "blob:", "https:", "http:"]
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
// Prod simulation: check strict
|
|
187
|
+
// Prod simulation: check strict production directives in Report-Only mode
|
|
188
|
+
// so any violation that will break in production emits a warning in dev
|
|
188
189
|
const reportOnlySimulation: SecurityOptions = {
|
|
189
190
|
...mergedOptions,
|
|
190
191
|
directives: {
|
|
191
|
-
...mergedOptions.directives
|
|
192
|
-
"script-src-attr": ["'none'"],
|
|
193
|
-
"style-src": ["'self'", "'unsafe-inline'"],
|
|
194
|
-
"script-src": ["'self'", "'unsafe-inline'", "'unsafe-eval'"]
|
|
192
|
+
...mergedOptions.directives
|
|
195
193
|
}
|
|
196
194
|
}
|
|
197
195
|
|