Skip to content

Commit 8946d3c

Browse files
committed
feat(perf): enhance CSP handling and nonce management for improved security and performance
1 parent 157f228 commit 8946d3c

File tree

3 files changed

+35
-11
lines changed

3 files changed

+35
-11
lines changed

frontend/scripts/advanced-post-build-optimize.cjs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -245,17 +245,20 @@ try {
245245
<link rel="preload" href="/assets/logos/thinkRED-np.svg" as="image" fetchpriority="high">
246246
<link rel="preload" href="/assets/avatars/assistant-red.webp" as="image">`;
247247

248-
// 4. ENHANCED CSP WITH NONCE
249-
const cspMeta = `
250-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-${nonce}' https://script.google.com https://script.googleusercontent.com; style-src 'self' 'nonce-${nonce}' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://script.google.com https://script.googleusercontent.com; frame-src 'self' https://script.google.com; object-src 'none'; base-uri 'self'; form-action 'self';">`;
248+
// 4. ENHANCED CSP WITH NONCE - Replace existing CSP if present
249+
const cspMeta = `<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-${nonce}' https://script.google.com https://script.googleusercontent.com; style-src 'self' 'nonce-${nonce}' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://script.google.com https://script.googleusercontent.com; frame-src 'self' https://script.google.com; object-src 'none'; base-uri 'self'; form-action 'self';">`;
250+
251+
// Remove any existing CSP headers to prevent conflicts
252+
html = html.replace(/<meta[^>]*Content-Security-Policy[^>]*>/gi, '');
251253

252254
// 5. ADDITIONAL SECURITY AND PERFORMANCE HEADERS
253255
const securityMeta = `
254256
<meta http-equiv="X-Content-Type-Options" content="nosniff">
255257
<meta http-equiv="X-Frame-Options" content="SAMEORIGIN">
256258
<meta http-equiv="X-XSS-Protection" content="1; mode=block">
257259
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
258-
<meta http-equiv="Permissions-Policy" content="camera=(), microphone=(), geolocation=(), payment=()">`;
260+
<meta http-equiv="Permissions-Policy" content="camera=(), microphone=(), geolocation=(), payment=()">
261+
`;
259262

260263
// 6. SERVICE WORKER REGISTRATION
261264
const serviceWorkerScript = `
@@ -291,9 +294,17 @@ try {
291294
'<script$1 fetchpriority="high">'
292295
);
293296

294-
// Add nonce to inline scripts and styles
295-
html = html.replace(/<script(?![^>]*src)([^>]*)>/g, `<script$1 nonce="${nonce}">`);
296-
html = html.replace(/<style([^>]*)>/g, `<style$1 nonce="${nonce}">`);
297+
// Add nonce to inline scripts and styles that don't already have one
298+
// More robust regex to prevent duplicate nonces
299+
html = html.replace(/<script(?![^>]*nonce=)(?![^>]*src)([^>]*)>/g, `<script$1 nonce="${nonce}">`);
300+
html = html.replace(/<style(?![^>]*nonce=)([^>]*)>/g, `<style$1 nonce="${nonce}">`);
301+
302+
// Add nonce to stylesheet links that don't already have one
303+
html = html.replace(/<link([^>]*rel="stylesheet"[^>]*?)(?![^>]*nonce=)>/g, `<link$1 nonce="${nonce}">`);
304+
305+
// Remove any duplicate nonce attributes that might have been created
306+
html = html.replace(/nonce="[^"]*"\s+nonce="[^"]*"/g, `nonce="${nonce}"`);
307+
html = html.replace(/nonce="[^"]*"\s+([^>]*)\s+nonce="[^"]*"/g, `nonce="${nonce}" $1`);
297308

298309
// 9. LAZY LOADING OPTIMIZATION
299310
// Add loading="lazy" to images that are not critical

frontend/scripts/optimize-accessibility.cjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,14 @@ function addReducedMotionSupport(html) {
279279
}
280280

281281
function enhanceColorContrast(html) {
282+
// Extract existing nonce from any style tag if present
283+
const nonceMatch = html.match(/nonce="([^"]+)"/);
284+
const nonce = nonceMatch ? nonceMatch[1] : '';
285+
const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
286+
282287
// Enhanced contrast styles
283288
const contrastCSS = `
284-
<style>
289+
<style${nonceAttr}>
285290
/* Enhanced contrast for accessibility */
286291
.text-gray-600 { color: #374151 !important; } /* 7.02:1 contrast ratio */
287292
.text-gray-700 { color: #1f2937 !important; } /* 12.63:1 contrast ratio */
@@ -350,8 +355,13 @@ function addSemanticImprovements(html) {
350355
}
351356

352357
function addKeyboardNavigation(html) {
358+
// Extract existing nonce from any script tag if present
359+
const nonceMatch = html.match(/nonce="([^"]+)"/);
360+
const nonce = nonceMatch ? nonceMatch[1] : '';
361+
const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
362+
353363
const keyboardScript = `
354-
<script>
364+
<script${nonceAttr}>
355365
// Enhanced keyboard navigation
356366
(function() {
357367
// Add keyboard support for custom interactive elements

frontend/scripts/optimize-css.cjs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,13 @@ img{max-width:100%;height:auto}
228228
let html = fs.readFileSync(indexPath, 'utf8');
229229

230230
// Find and replace or insert critical CSS
231-
const criticalCSSRegex = /<!-- Critical above-the-fold CSS[^>]*-->\s*<style[^>]*>[\s\S]*?<\/style>/;
231+
const criticalCSSRegex = /<!-- Critical above-the-fold CSS[^>]*-->\s*<style([^>]*)>[\s\S]*?<\/style>/;
232232

233233
if (criticalCSSRegex.test(html)) {
234-
html = html.replace(criticalCSSRegex, `<!-- Critical above-the-fold CSS --><style>${criticalCSS}</style>`);
234+
// Preserve the style tag attributes (including nonce)
235+
html = html.replace(criticalCSSRegex, (match, styleAttrs) => {
236+
return `<!-- Critical above-the-fold CSS --><style${styleAttrs}>${criticalCSS}</style>`;
237+
});
235238
} else {
236239
// Insert before closing head tag
237240
html = html.replace('</head>', `<style>${criticalCSS}</style>\n</head>`);

0 commit comments

Comments
 (0)