mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(routing): allow hash path redirect under CSP
This commit is contained in:
+1
-1
@@ -65,8 +65,8 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!-- Normalize path-based URLs before HashRouter starts. -->
|
||||
<script>
|
||||
// Redirect non-hash URLs before the app bundle is requested.
|
||||
if (
|
||||
window.location.protocol !== 'file:' &&
|
||||
window.location.pathname &&
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@
|
||||
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" },
|
||||
{
|
||||
"key": "Content-Security-Policy",
|
||||
"value": "default-src 'self'; base-uri 'none'; object-src 'none'; form-action 'self'; frame-ancestors 'none'; script-src 'self' 'wasm-unsafe-eval' 'sha256-kF+BgNpp0e7WM8m8a9eRaLHmLrSQGQVmaFYjvPYU1/8=' 'sha256-kiz84y15tsP8n+Qo/YHYAPLBR6nQyVSqHO4BWcVVOik=' https://platform.twitter.com https://embed.reddit.com https://www.tiktok.com https://www.instagram.com; style-src 'self' 'unsafe-inline'; img-src 'self' https: http: data: blob:; media-src 'self' https: http: blob:; connect-src 'self' https: http: wss: ws: blob:; frame-src https: http://localhost:* http://*.localhost:* http://127.0.0.1:* http://[::1]:*; child-src https: http://localhost:* http://*.localhost:* http://127.0.0.1:* http://[::1]:*; worker-src 'self' blob:; manifest-src 'self'"
|
||||
"value": "default-src 'self'; base-uri 'none'; object-src 'none'; form-action 'self'; frame-ancestors 'none'; script-src 'self' 'wasm-unsafe-eval' 'sha256-kF+BgNpp0e7WM8m8a9eRaLHmLrSQGQVmaFYjvPYU1/8=' 'sha256-uIRmdbN2zhK9eOgX0W2eTHZJSiH88uiZBeEZTwCIUyI=' https://platform.twitter.com https://embed.reddit.com https://www.tiktok.com https://www.instagram.com; style-src 'self' 'unsafe-inline'; img-src 'self' https: http: data: blob:; media-src 'self' https: http: blob:; connect-src 'self' https: http: wss: ws: blob:; frame-src https: http://localhost:* http://*.localhost:* http://127.0.0.1:* http://[::1]:*; child-src https: http://localhost:* http://*.localhost:* http://127.0.0.1:* http://[::1]:*; worker-src 'self' blob:; manifest-src 'self'"
|
||||
},
|
||||
{ "key": "X-DNS-Prefetch-Control", "value": "off" }
|
||||
]
|
||||
|
||||
@@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { resolve } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import { createHash } from 'crypto';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
const { version: packageVersion } = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
||||
@@ -97,6 +98,55 @@ function appVersionMetadataPlugin() {
|
||||
};
|
||||
}
|
||||
|
||||
function getVercelContentSecurityPolicy() {
|
||||
const vercelConfig = JSON.parse(readFileSync(new URL('./vercel.json', import.meta.url), 'utf8'));
|
||||
const cspHeader = vercelConfig.headers
|
||||
?.flatMap((entry) => entry.headers || [])
|
||||
.find((header) => typeof header.key === 'string' && header.key.toLowerCase() === 'content-security-policy');
|
||||
|
||||
if (typeof cspHeader?.value !== 'string') {
|
||||
throw new Error('vercel.json is missing a Content-Security-Policy header.');
|
||||
}
|
||||
|
||||
return cspHeader.value;
|
||||
}
|
||||
|
||||
function getInlineScriptHashes(indexHtml) {
|
||||
const scriptPattern = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
||||
const hashes = [];
|
||||
|
||||
for (const [, attributes, source] of indexHtml.matchAll(scriptPattern)) {
|
||||
if (/\bsrc\s*=/i.test(attributes) || source.trim().length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hashes.push(`sha256-${createHash('sha256').update(source).digest('base64')}`);
|
||||
}
|
||||
|
||||
return [...new Set(hashes)];
|
||||
}
|
||||
|
||||
function verifyVercelCspHashesPlugin() {
|
||||
return {
|
||||
name: 'fivechan-verify-vercel-csp-hashes',
|
||||
apply: 'build',
|
||||
enforce: 'post',
|
||||
closeBundle() {
|
||||
const indexHtml = readFileSync(new URL(`./${buildOutDir}/index.html`, import.meta.url), 'utf8');
|
||||
const contentSecurityPolicy = getVercelContentSecurityPolicy();
|
||||
const missingHashes = getInlineScriptHashes(indexHtml).filter(
|
||||
(hash) => !contentSecurityPolicy.includes(`'${hash}'`) && !contentSecurityPolicy.includes(hash),
|
||||
);
|
||||
|
||||
if (missingHashes.length > 0) {
|
||||
const plural = missingHashes.length === 1 ? '' : 'es';
|
||||
|
||||
throw new Error(`vercel.json Content-Security-Policy is missing inline script hash${plural}: ${missingHashes.join(', ')}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function adaptReactPluginForRolldown(plugin) {
|
||||
if (!plugin?.config || plugin.name !== 'vite:react-babel') {
|
||||
return plugin;
|
||||
@@ -252,6 +302,7 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
}),
|
||||
verifyVercelCspHashesPlugin(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Reference in New Issue
Block a user