// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= import { describe, expect, it } from 'vitest'; import { buildPreviewContentSecurityPolicy, collectPreviewRemoteOrigins, injectPreviewContentSecurityPolicy, isStaticImageSrc, PREVIEW_CONTENT_SECURITY_POLICY, repairGeneratedReportBraces, stripScriptBlocks, } from '@/lib/htmlSanitization'; describe('isStaticImageSrc', () => { it('accepts static relative paths', () => { expect(isStaticImageSrc('assets/home.png')).toBe(true); }); it('rejects JS template literal expressions', () => { expect(isStaticImageSrc('${escapeHtml(node.image)}')).toBe(false); expect(isStaticImageSrc('assets/${node.id}.png')).toBe(false); }); }); describe('stripScriptBlocks', () => { it('removes script blocks so img scans skip JS template strings', () => { const html = ` home `; expect(stripScriptBlocks(html)).not.toContain('escapeHtml'); expect(stripScriptBlocks(html)).toContain('assets/home.png'); }); }); describe('HTML preview CSP', () => { it('replaces an agent-authored policy with the application policy', () => { const html = injectPreviewContentSecurityPolicy(` `); const doc = new DOMParser().parseFromString(html, 'text/html'); const policies = doc.querySelectorAll( 'meta[http-equiv="Content-Security-Policy" i]' ); expect(policies).toHaveLength(1); expect(policies[0].getAttribute('content')).toBe( PREVIEW_CONTENT_SECURITY_POLICY ); expect(PREVIEW_CONTENT_SECURITY_POLICY).toContain("default-src 'none'"); expect(PREVIEW_CONTENT_SECURITY_POLICY).toContain("connect-src 'none'"); expect(PREVIEW_CONTENT_SECURITY_POLICY).not.toContain('https:'); expect(PREVIEW_CONTENT_SECURITY_POLICY).not.toContain('navigate-to'); }); it('collects exact resource origins including import maps and font bytes', () => { const origins = collectPreviewRemoteOrigins(` link `); expect(origins).toEqual([ 'https://api.example', 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://images.example', 'https://unpkg.com', ]); }); it('grants only normalized HTTPS origins for an authorized preview', () => { const policy = buildPreviewContentSecurityPolicy([ 'https://unpkg.com/path/file.js', 'https://unpkg.com/another.js', 'http://insecure.example/script.js', 'not a url', ]); expect(policy).toContain( "script-src 'unsafe-inline' data: blob: localfile: https://unpkg.com" ); expect(policy).toContain('connect-src https://unpkg.com'); expect(policy).not.toContain('insecure.example'); expect(policy).not.toContain('navigate-to'); }); it('injects the authorized policy without preserving an authored wildcard', () => { const html = injectPreviewContentSecurityPolicy( '', ['https://unpkg.com/module.js'] ); const doc = new DOMParser().parseFromString(html, 'text/html'); const policy = doc .querySelector('meta[http-equiv="Content-Security-Policy" i]') ?.getAttribute('content'); expect(policy).toContain('https://unpkg.com'); expect(policy).not.toContain('https:;'); }); }); describe('generated report brace repair', () => { it('preserves valid nested CSS closing braces and all following HUD rules', () => { const html = `
online
`; expect(repairGeneratedReportBraces(html)).toBe(html); }); it('repairs template-escaped CSS only when doubled opening braces exist', () => { const repaired = repairGeneratedReportBraces( '' ); expect(repaired).toContain('.hud { display:block; }'); expect(repaired).toContain('#title { z-index:10; }'); expect(repaired).not.toContain('{{'); }); });