fix: remove CSP nonce from style-src to unblock reagraph canvas (#425)

Replace style-src nonce directive with unsafe-inline to support
reagraph's runtime <style> injection. Add style-src-elem and
style-src-attr directives for CSP Level 3 compliance. Extend
fitNodesInView retries from 2 to 4 for more reliable canvas sizing.

Closes #414
Supersedes #415
This commit is contained in:
nyk 2026-03-17 13:52:09 +07:00 committed by GitHub
parent 00a22a2e24
commit 4671946e97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 12 additions and 6 deletions

View File

@ -267,9 +267,11 @@ export function MemoryGraph() {
useEffect(() => {
if (!graphNodes.length) return
// reagraph force layout needs time to settle before fitNodesInView works
const t1 = setTimeout(() => graphRef.current?.fitNodesInView(), 800)
const t2 = setTimeout(() => graphRef.current?.fitNodesInView(), 2000)
return () => { clearTimeout(t1); clearTimeout(t2) }
const t1 = setTimeout(() => graphRef.current?.fitNodesInView(undefined, { animated: false }), 800)
const t2 = setTimeout(() => graphRef.current?.fitNodesInView(undefined, { animated: false }), 2500)
const t3 = setTimeout(() => graphRef.current?.fitNodesInView(undefined, { animated: false }), 5000)
const t4 = setTimeout(() => graphRef.current?.fitNodesInView(undefined, { animated: false }), 8000)
return () => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); clearTimeout(t4) }
}, [graphNodes.length, selectedAgent])
// Navigation helpers

View File

@ -6,7 +6,9 @@ describe('buildMissionControlCsp', () => {
const csp = buildMissionControlCsp({ nonce: 'nonce-123', googleEnabled: false })
expect(csp).toContain(`script-src 'self' 'nonce-nonce-123' 'strict-dynamic'`)
expect(csp).toContain(`style-src 'self' 'nonce-nonce-123'`)
expect(csp).toContain("style-src 'self' 'unsafe-inline'")
expect(csp).toContain("style-src-elem 'self' 'unsafe-inline'")
expect(csp).toContain("style-src-attr 'unsafe-inline'")
})
})
@ -19,6 +21,6 @@ describe('buildNonceRequestHeaders', () => {
})
expect(headers.get('x-nonce')).toBe('nonce-123')
expect(headers.get('Content-Security-Policy')).toContain(`'nonce-nonce-123'`)
expect(headers.get('Content-Security-Policy')).toContain("style-src 'self' 'unsafe-inline'")
})
})

View File

@ -7,7 +7,9 @@ export function buildMissionControlCsp(input: { nonce: string; googleEnabled: bo
`object-src 'none'`,
`frame-ancestors 'none'`,
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' blob:${googleEnabled ? ' https://accounts.google.com' : ''}`,
`style-src 'self' 'nonce-${nonce}'`,
`style-src 'self' 'unsafe-inline'`,
`style-src-elem 'self' 'unsafe-inline'`,
`style-src-attr 'unsafe-inline'`,
`connect-src 'self' ws: wss: http://127.0.0.1:* http://localhost:* https://cdn.jsdelivr.net`,
`img-src 'self' data: blob:${googleEnabled ? ' https://*.googleusercontent.com https://lh3.googleusercontent.com' : ''}`,
`font-src 'self' data:`,