File size: 1,810 Bytes
0ed8124
 
 
 
 
 
 
 
897170b
 
0ed8124
 
897170b
 
 
0ed8124
 
 
 
 
 
 
 
897170b
 
 
 
 
 
 
 
 
0ed8124
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { describe, expect, it } from 'vitest';
import { prepareHtmlArtifact } from './agent-tools';

describe('prepareHtmlArtifact', () => {
  it('injects a deny-by-default CSP while preserving source for the code view', () => {
    const source = '<html><head><title>x</title></head><body><script>document.body.dataset.ok="1"</script></body></html>';
    const artifact = prepareHtmlArtifact(source, 'Demo', 'artifact-1');
    expect(artifact.source).toBe(source);
    expect(artifact.sandboxedSource).toMatch(/^<meta http-equiv="Content-Security-Policy"/);
    expect(artifact.sandboxedSource.indexOf('Content-Security-Policy')).toBeLessThan(artifact.sandboxedSource.indexOf('document.body.dataset.ok'));
    expect(artifact.sandboxedSource).toContain("default-src 'none'");
    expect(artifact.sandboxedSource).toContain("connect-src 'none'");
    expect(artifact.sandboxedSource).toContain("script-src 'unsafe-inline'");
    expect(artifact.sandboxedSource).toContain('data-bonsai-artifact-runtime');
    expect(artifact.runtimeToken).toBeTruthy();
    expect(artifact.title).toBe('Demo');
  });

  it('rejects artifacts larger than the client-side safety bound', () => {
    expect(() => prepareHtmlArtifact('x'.repeat(256 * 1024 + 1), 'large', 'artifact-2')).toThrow(
      'exceeds 256 KiB',
    );
  });

  it('rejects direct document navigation while keeping ordinary inline scripts available', () => {
    expect(() => prepareHtmlArtifact('<script>location.href="https://example.com"</script>'))
      .toThrow('cannot navigate');
    expect(() => prepareHtmlArtifact('<meta http-equiv="refresh" content="0;url=https://example.com">'))
      .toThrow('navigation or refresh');
    expect(() => prepareHtmlArtifact('<script>document.body.textContent = "ok"</script>'))
      .not.toThrow();
  });
});