Update CSP to allow external fonts, scripts, and add corresponding test coverage
CI / check (22) (push) Failing after 15m50s
CI / check (24) (push) Failing after 15m49s

This commit is contained in:
2026-10-07 23:51:32 +02:00
parent 5ca611c113
commit 5624eead82
4 changed files with 13 additions and 3 deletions
+1 -1
View File
@@ -439,7 +439,7 @@
integrity="sha512-rCQgmUulW6f6QegOvTntKKb5IAoxTpGVCdWqYjkXEpzAns6XUFs8NKVqWe+KQpctp/EoRSFSuykVputqknLYMg=="
crossorigin="anonymous" defer></script>
<script src="/vendor/dompurify.js" defer></script>
<script type="module" src="/js/script.js?v=20260930-chat-usability"></script>
<script type="module" src="/js/script.js?v=20261007-chat-csp-fix"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
+1 -1
View File
@@ -1017,7 +1017,7 @@ if (headerModelButton) headerModelButton.addEventListener("click", (event) => {
});
saveGenerationSettingsBtn.addEventListener("click", () => { void saveGenerationSettings(); });
roleplayNewPersonaBtn.addEventListener("click", closeRoleplayStarter);
clearUserPersonaBtn.addEventListener("click", () => {
if (clearUserPersonaBtn) clearUserPersonaBtn.addEventListener("click", () => {
closeChatDrawer();
void clearChatUserPersona();
});
+1 -1
View File
@@ -221,7 +221,7 @@ function sameOriginProtection(config) {
function browserSecurityHeaders(config) {
return (_req, res, next) => {
res.setHeader("Content-Security-Policy", "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: blob:; media-src 'self' blob:; connect-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self' data:; worker-src 'self' blob:");
res.setHeader("Content-Security-Policy", "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: blob:; media-src 'self' blob:; connect-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' data: https://fonts.gstatic.com; worker-src 'self' blob:");
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
res.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=(), payment=(), usb=()");
res.setHeader("X-Content-Type-Options", "nosniff");
+10
View File
@@ -127,6 +127,16 @@ test("public health checks reach the backend without exposing diagnostics or req
assert.equal((await request(server.baseUrl, "/admin/diagnostics")).status, 403);
});
test("CSP permits the pinned chat markdown script and the app's hosted fonts", async t => {
const server = await startTestServer();
t.after(() => server.close());
const response = await fetch(`${server.baseUrl}/`);
const policy = response.headers.get("content-security-policy");
assert.match(policy, /style-src[^;]*https:\/\/fonts\.googleapis\.com/);
assert.match(policy, /script-src[^;]*https:\/\/cdnjs\.cloudflare\.com/);
assert.match(policy, /font-src[^;]*https:\/\/fonts\.gstatic\.com/);
});
test("personality API keeps prompts private, validates IDs, and enforces chat ownership", async t => {
const server = await startTestServer();
t.after(() => server.close());