|
| 1 | +/** |
| 2 | + * @vitest-environment node |
| 3 | + */ |
| 4 | +import JSZip from 'jszip' |
| 5 | +import { describe, expect, it } from 'vitest' |
| 6 | +import { ZipBombError } from '@/lib/file-parsers/ooxml-limits' |
| 7 | +import { assertOoxmlPreviewWithinLimits } from '@/lib/file-parsers/ooxml-preview-guard' |
| 8 | + |
| 9 | +async function buildZip(entries: Record<string, string>): Promise<ArrayBuffer> { |
| 10 | + const zip = new JSZip() |
| 11 | + for (const [name, content] of Object.entries(entries)) { |
| 12 | + zip.file(name, content) |
| 13 | + } |
| 14 | + const buffer = await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' }) |
| 15 | + return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) |
| 16 | +} |
| 17 | + |
| 18 | +const TINY_LIMITS = { |
| 19 | + maxTotalUncompressedBytes: 1024 * 1024, |
| 20 | + maxEntryUncompressedBytes: 100_000, |
| 21 | +} |
| 22 | + |
| 23 | +describe('assertOoxmlPreviewWithinLimits', () => { |
| 24 | + it('accepts an archive within the limits', async () => { |
| 25 | + const data = await buildZip({ 'word/document.xml': '<w:document/>' }) |
| 26 | + await expect(assertOoxmlPreviewWithinLimits(data, TINY_LIMITS)).resolves.toBeUndefined() |
| 27 | + }) |
| 28 | + |
| 29 | + it('rejects an archive whose single part exceeds the per-entry limit', async () => { |
| 30 | + const data = await buildZip({ 'word/document.xml': 'A'.repeat(200_000) }) |
| 31 | + await expect(assertOoxmlPreviewWithinLimits(data, TINY_LIMITS)).rejects.toBeInstanceOf( |
| 32 | + ZipBombError |
| 33 | + ) |
| 34 | + }) |
| 35 | + |
| 36 | + it('rejects an archive whose summed parts exceed the total limit', async () => { |
| 37 | + const data = await buildZip({ |
| 38 | + 'a.xml': 'A'.repeat(60_000), |
| 39 | + 'b.xml': 'B'.repeat(60_000), |
| 40 | + 'c.xml': 'C'.repeat(60_000), |
| 41 | + }) |
| 42 | + await expect( |
| 43 | + assertOoxmlPreviewWithinLimits(data, { |
| 44 | + maxTotalUncompressedBytes: 100_000, |
| 45 | + maxEntryUncompressedBytes: 1024 * 1024, |
| 46 | + }) |
| 47 | + ).rejects.toBeInstanceOf(ZipBombError) |
| 48 | + }) |
| 49 | + |
| 50 | + it('accepts an ordinary document under the shared default limits', async () => { |
| 51 | + const data = await buildZip({ |
| 52 | + '[Content_Types].xml': '<?xml version="1.0"?><Types/>', |
| 53 | + 'word/document.xml': `<w:document>${'text '.repeat(5000)}</w:document>`, |
| 54 | + }) |
| 55 | + await expect(assertOoxmlPreviewWithinLimits(data)).resolves.toBeUndefined() |
| 56 | + }) |
| 57 | +}) |
0 commit comments