A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

at trunk 3.5 kB View raw
1// src/lib/embeds/card.test.ts 2import { describe, expect, it } from 'vitest'; 3import { escapeHtml, renderEmbedCard } from './card'; 4 5describe( 'escapeHtml', () => { 6 it( 'escapes the HTML metacharacters', () => { 7 expect( escapeHtml( `<img src=x onerror="alert(1)"> & "q" 'a'` ) ).toBe( 8 '&lt;img src=x onerror=&quot;alert(1)&quot;&gt; &amp; &quot;q&quot; &#39;a&#39;' 9 ); 10 } ); 11} ); 12 13describe( 'renderEmbedCard — atproto', () => { 14 const data = { 15 kind: 'atproto' as const, 16 authorName: 'Jeremy', 17 handle: 'jeremy.herve.bzh', 18 avatar: 'https://cdn.example/av.jpg', 19 text: 'Hello <script>alert(1)</script> world', 20 images: [ { src: 'https://cdn.example/i.jpg', alt: 'a "nice" pic' } ], 21 createdAt: '2026-06-19T10:00:00.000Z', 22 viewUrl: 'https://mu.social/profile/did:plc:x/post/abc', 23 }; 24 25 it( 'renders a static card with escaped post text', () => { 26 const html = renderEmbedCard( data ); 27 expect( html ).toContain( 'class="wp-block-embed skypress-embed skypress-embed--atproto"' ); 28 expect( html ).toContain( 'Hello &lt;script&gt;alert(1)&lt;/script&gt; world' ); 29 expect( html ).not.toContain( '<script>' ); 30 expect( html ).toContain( 'href="https://mu.social/profile/did:plc:x/post/abc"' ); 31 expect( html ).toContain( '@jeremy.herve.bzh' ); 32 } ); 33 34 it( 'escapes hostile image alt text', () => { 35 expect( renderEmbedCard( data ) ).toContain( 'alt="a &quot;nice&quot; pic"' ); 36 } ); 37 38 it( 'contains no iframe', () => { 39 expect( renderEmbedCard( data ) ).not.toContain( '<iframe' ); 40 } ); 41} ); 42 43describe( 'renderEmbedCard — video facade', () => { 44 const data = { 45 kind: 'youtube' as const, 46 id: 'dQw4w9WgXcQ', 47 title: 'Cool <b>video</b>', 48 thumbnail: 'https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg', 49 url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', 50 }; 51 52 it( 'renders a facade with a play button and data attributes, no iframe', () => { 53 const html = renderEmbedCard( data ); 54 expect( html ).toContain( 'skypress-embed--video' ); 55 expect( html ).toContain( 'data-embed-provider="youtube"' ); 56 expect( html ).toContain( 'data-embed-id="dQw4w9WgXcQ"' ); 57 expect( html ).toContain( '<button' ); 58 expect( html ).toContain( 'Cool &lt;b&gt;video&lt;/b&gt;' ); 59 expect( html ).not.toContain( '<iframe' ); 60 } ); 61} ); 62 63describe( 'renderEmbedCard — URL scheme guard', () => { 64 it( 'neutralizes a javascript: viewUrl on an atproto card', () => { 65 const html = renderEmbedCard( { 66 kind: 'atproto', 67 authorName: 'Jeremy', 68 handle: 'jeremy.herve.bzh', 69 text: 'hi', 70 images: [], 71 viewUrl: 'javascript:alert(1)', 72 } ); 73 expect( html ).not.toContain( 'href="javascript:alert(1)"' ); 74 expect( html ).not.toContain( 'javascript:' ); 75 expect( html ).toContain( 'href=""' ); 76 } ); 77 78 it( 'neutralizes a javascript: url in the video fallback href', () => { 79 const html = renderEmbedCard( { 80 kind: 'youtube', 81 id: 'dQw4w9WgXcQ', 82 title: 'x', 83 thumbnail: 'https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg', 84 url: 'javascript:alert(1)', 85 } ); 86 expect( html ).not.toContain( 'href="javascript:alert(1)"' ); 87 expect( html ).not.toContain( 'javascript:' ); 88 expect( html ).toContain( 'href=""' ); 89 } ); 90 91 it( 'leaves a valid https url untouched in the href', () => { 92 const html = renderEmbedCard( { 93 kind: 'atproto', 94 authorName: 'Jeremy', 95 handle: 'jeremy.herve.bzh', 96 text: 'hi', 97 images: [], 98 viewUrl: 'https://mu.social/profile/did:plc:x/post/abc', 99 } ); 100 expect( html ).toContain( 'href="https://mu.social/profile/did:plc:x/post/abc"' ); 101 } ); 102} );