From cba68a8cc758edd14672c5e60a0a65cd29c9bae9 Mon Sep 17 00:00:00 2001 From: WuYongJie Date: Mon, 19 Oct 2026 17:07:09 +0800 Subject: [PATCH] fix(print): allow C-Lodop embedded preview frames --- furniture-3d-system/next.config.js | 1 + .../tests/next-config-security.test.js | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/furniture-3d-system/next.config.js b/furniture-3d-system/next.config.js index 2bd4ad88..4a2510f1 100644 --- a/furniture-3d-system/next.config.js +++ b/furniture-3d-system/next.config.js @@ -74,6 +74,7 @@ const contentSecurityPolicy = [ "base-uri 'self'", "object-src 'none'", "frame-ancestors 'none'", + `frame-src 'self' ${lodopScriptSources.join(' ')}`, "form-action 'self'", "img-src 'self' data: blob: http: https:", "font-src 'self' data:", diff --git a/furniture-3d-system/tests/next-config-security.test.js b/furniture-3d-system/tests/next-config-security.test.js index 32cc14c0..5b118e8f 100644 --- a/furniture-3d-system/tests/next-config-security.test.js +++ b/furniture-3d-system/tests/next-config-security.test.js @@ -25,6 +25,36 @@ describe('next.config security headers', () => { return config.images?.remotePatterns ?? [] } + it.each(['production', 'development'])( + 'allows C-Lodop preview frames with a fixed origin list in %s', + async (nodeEnv) => { + const config = loadConfigWithEnv({ NODE_ENV: nodeEnv }) + const csp = await readContentSecurityPolicy(config) + const directives = new Map(csp.split(';').map((directive) => { + const [name, ...sources] = directive.trim().split(/\s+/) + return [name, sources] + })) + + expect(directives.get('frame-src')).toEqual([ + "'self'", + 'http://127.0.0.1:8000', + 'http://127.0.0.1:18000', + 'http://localhost:8000', + 'http://localhost:18000', + 'https://localhost.lodop.net:8443', + ]) + expect(directives.get('default-src')).toEqual(["'self'"]) + expect(directives.get('frame-ancestors')).toEqual(["'none'"]) + expect(directives.get('object-src')).toEqual(["'none'"]) + + const headers = await config.headers() + expect(headers.find((entry) => entry.source === '/(.*)').headers).toContainEqual({ + key: 'X-Frame-Options', + value: 'DENY', + }) + }, + ) + it('allows configured object storage origins for 3D model fetches', async () => { const config = loadConfigWithEnv({ NODE_ENV: 'production',