File
Blob: tests/worker/protocol/xml/parser.test.ts
| 1 | import { describe, expect, it } from "vitest"; |
| 2 | |
| 3 | import { parseICalendarBytes } from "@/worker/caldav/ical/parse"; |
| 4 | import { parseVCardBytes } from "@/worker/carddav/vcard"; |
| 5 | import { utf8ArrayBuffer } from "@tests/worker/helpers/bytes"; |
| 6 | import { extractDeadPropertyFragment } from "@/worker/xml/dead-props"; |
| 7 | import { parseSafeXml } from "@/worker/xml/parser"; |
| 8 | |
| 9 | describe("parser compatibility checks", () => { |
| 10 | it("preserves namespace URI and local name for dead property fragments", () => { |
| 11 | const fragment = extractDeadPropertyFragment('<x:favorite xmlns:x="urn:example:test">blue</x:favorite>'); |
| 12 | expect(fragment).toEqual({ |
| 13 | nsUri: "urn:example:test", |
| 14 | localName: "favorite", |
| 15 | xmlValue: '<x:favorite xmlns:x="urn:example:test">blue</x:favorite>', |
| 16 | }); |
| 17 | }); |
| 18 | |
| 19 | it("rejects unsafe entity declarations", () => { |
| 20 | expect(() => parseSafeXml("<!DOCTYPE x [<!ENTITY y 'z'>]><x>&y;</x>")).toThrow( |
| 21 | "Unsafe XML entity declarations are not accepted", |
| 22 | ); |
| 23 | }); |
| 24 | |
| 25 | it("rejects structurally hostile XML below the byte limit", () => { |
| 26 | const deep = `${"<x>".repeat(129)}${"</x>".repeat(129)}`; |
| 27 | expect(() => parseSafeXml(deep)).toThrow(); |
| 28 | |
| 29 | const many = `<root>${"<x/>".repeat(50_001)}</root>`; |
| 30 | expect(() => parseSafeXml(many, 1_000_000)).toThrow("element count limit"); |
| 31 | }); |
| 32 | |
| 33 | it("imports bounded calendar and contact parsers in the Worker runtime", () => { |
| 34 | const calendar = [ |
| 35 | "BEGIN:VCALENDAR", |
| 36 | "VERSION:2.0", |
| 37 | "PRODID:-//dab//parser-check//EN", |
| 38 | "BEGIN:VEVENT", |
| 39 | "UID:calendar-parser-check", |
| 40 | "DTSTAMP:20260521T000000Z", |
| 41 | "DTSTART:20260521T000000Z", |
| 42 | "END:VEVENT", |
| 43 | "END:VCALENDAR", |
| 44 | ].join("\r\n"); |
| 45 | const contact = ["BEGIN:VCARD", "VERSION:4.0", "UID:contact-parser-check", "FN:Parser Check", "END:VCARD"].join( |
| 46 | "\r\n", |
| 47 | ); |
| 48 | |
| 49 | expect(parseICalendarBytes(utf8ArrayBuffer(calendar), 4096, { maxYears: 1, maxInstances: 10 }).uid).toBe( |
| 50 | "calendar-parser-check", |
| 51 | ); |
| 52 | expect(parseVCardBytes(utf8ArrayBuffer(contact), 4096).uid).toBe("contact-parser-check"); |
| 53 | }); |
| 54 | }); |