diff --git a/craft/src/components/basic/HtmlBlock.security.test.ts b/craft/src/components/basic/HtmlBlock.security.test.ts new file mode 100644 index 0000000..ddfcca7 --- /dev/null +++ b/craft/src/components/basic/HtmlBlock.security.test.ts @@ -0,0 +1,260 @@ +import { describe, test, expect } from 'vitest'; +import { purifyHtml } from './HtmlBlock'; +// Vite/Vitest `?raw` import -- ships the exact bytes of the file as a +// string, declared by node_modules/vite/client.d.ts. This is a checked-in +// copy of the reference acceptance fixture used for Task 24 (widening the +// Custom HTML block's sanitiser allow-list); keep it byte-identical to the +// external fixture used to drive this task so these tests cannot silently +// drift from the thing they are supposed to be testing against. +import fixtureHtml from './__fixtures__/html-block-test-body.html?raw'; + +/** + * Task 24: the site owner tested a broad HTML fixture against the shipped + * sanitiser config and found 38% of it silently deleted -- merged table + * cells collapsing (colspan/rowspan/scope stripped),
//
/ + * inline /
    flattened. The fix widens + * ALLOWED_TAGS/ALLOWED_ATTR in HtmlBlock.tsx. These tests run the *actual* + * reference fixture through the *actual* purifyHtml() and assert the + * previously-broken constructs now survive with their meaningful + * attributes intact, while re-confirming (with attack payloads spliced + * into the newly-widened surface -- forms, media, inline svg) that the + * four non-negotiable security properties still hold. + */ + +describe('purifyHtml -- Task 24 fixture regression (formerly-dropped constructs survive)', () => { + const out = purifyHtml(fixtureHtml); + + test('table merged cells keep colspan/rowspan/scope', () => { + expect(out).toContain(''); + expect(out).toContain(''); + expect(out).toContain(''); + expect(out).toContain(''); + }); + + test('definition list keeps its dl/dt/dd structure (was flattened to "TermDef")', () => { + expect(out).toMatch(/
    [\s\S]*
    Term one<\/dt>[\s\S]*
    Definition of the first term\.<\/dd>[\s\S]*<\/dl>/); + }); + + test('menu list survives with nested buttons', () => { + expect(out).toMatch(/[\s\S]* + + + + + +

    + + + + + +
    + + +
    +

    Media and embeds

    + +

    Inline SVG

    + + + + svg + + +

    Figure with data-URI image

    +
    + Small red square +
    Figure caption describing the image above.
    +
    + +

    Broken image (alt-text fallback test)

    + This alt text should render because the source is missing + +

    Picture element

    + + + + Responsive image fallback + + +

    Video and audio (sources intentionally missing)

    + + + +

    Canvas and iframe

    + Canvas fallback text + +
    + +
    + + +
    +

    Interactive and semantic containers

    + +
    + Collapsed disclosure +

    Hidden content revealed on toggle.

    +
    +
    + Open disclosure +
    • With a list inside
    • Second item
    +
    + + +

    Non-modal dialog content.

    + +
    + + +
    +

    Article header

    +

    Article body content.

    + +

    Article footer.

    +
    + +

    Editable inline region

    + + + +
    + +
    + + +
    +

    Edge cases

    + +

    Very long unbroken token (overflow test):

    +

    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

    +

    Long URL: https://example.com/a/very/long/path/segment/that/keeps/going/and/going?query=1&another=2&third=3#fragment-identifier

    + +

    هذا نص عربي لاختبار الاتجاه من اليمين إلى اليسار.

    +

    זהו טקסט עברי לבדיקה.

    +

    日本語のテキストです。改行と折り返しの確認用。

    +

    Straßenverkehrsordnung — Grüße aus München

    +

    Emoji & combining: 👋🏽 👨‍👩‍👧‍👦 🇺🇸 é vs é (precomposed vs combining)

    +

    Zero-width chars between letters: a​b​c

    + +

    Escaped tag text: <script>alert(1)</script>

    +

    Attribute with quotes: hover me

    + +

    Empty elements follow:

    +
    +

    +
      +
      + +

      Deep nesting:

      +

      Seven levels deep.

      + +

      Inline element stress: + all five at once +

      + +

      Inline style attribute (teal).

      +

      Element with classes and data attributes.

      +
      + + + +