streaming 7-Zip archive extractor
0

Configure Feed

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

pkg-7zip / tests / errors.test.ts
6.4 kB 148 lines
1import { assertRejects } from '@std/assert'; 2 3import { 4 ChecksumMismatchError, 5 InvalidPasswordError, 6 InvalidSignatureError, 7 MissingPasswordError, 8 TruncatedArchiveError, 9 UnsupportedFeatureError, 10} from '../lib/errors.ts'; 11import { unsevenzip } from '../lib/extract.ts'; 12import { fromUint8Array } from '../lib/reader/common.ts'; 13 14const COMMONS_COMPRESS = new URL('./fixtures/commons-compress/', import.meta.url); 15const LIBARCHIVE = new URL('./fixtures/libarchive/', import.meta.url); 16const PY7ZR = new URL('./fixtures/py7zr/', import.meta.url); 17 18const load = (base: URL, name: string): Uint8Array<ArrayBuffer> => Deno.readFileSync(new URL(name, base)); 19 20const drain = async (reader: ReturnType<typeof fromUint8Array>, password?: string): Promise<void> => { 21 for await (const entry of unsevenzip(reader, password !== undefined ? { password } : {})) { 22 await entry.bytes(); 23 } 24}; 25 26// #region unsupported codecs 27 28Deno.test('unsevenzip: bzip2 throws UnsupportedFeatureError at decode time', async () => { 29 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_bzip2.7z')); 30 await assertRejects(() => drain(reader), UnsupportedFeatureError); 31}); 32 33Deno.test('unsevenzip: bcj2+bzip2 throws UnsupportedFeatureError at decode time', async () => { 34 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_bcj2_bzip2.7z')); 35 await assertRejects(() => drain(reader), UnsupportedFeatureError); 36}); 37 38Deno.test('unsevenzip: lz4 throws UnsupportedFeatureError at decode time', async () => { 39 const reader = fromUint8Array(load(PY7ZR, 'lz4.7z')); 40 await assertRejects(() => drain(reader), UnsupportedFeatureError); 41}); 42 43Deno.test('unsevenzip: zstd+brotli throws UnsupportedFeatureError at decode time', async () => { 44 const reader = fromUint8Array(load(PY7ZR, 'zstdmt-brotli.7z')); 45 await assertRejects(() => drain(reader), UnsupportedFeatureError); 46}); 47 48// #endregion 49 50// #region missing password 51 52Deno.test('unsevenzip: encrypted entry without password throws MissingPasswordError', async () => { 53 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption.7z')); 54 await assertRejects(() => drain(reader), MissingPasswordError); 55}); 56 57Deno.test('unsevenzip: encrypted header without password throws MissingPasswordError', async () => { 58 // bla.encrypted has an encrypted EncodedHeader — failure surfaces during 59 // container parsing, before any entry is yielded. 60 const reader = fromUint8Array(load(COMMONS_COMPRESS, 'bla.encrypted.7z')); 61 await assertRejects(() => drain(reader), MissingPasswordError); 62}); 63 64Deno.test('unsevenzip: filename_encryption without password throws MissingPasswordError', async () => { 65 const reader = fromUint8Array(load(PY7ZR, 'filename_encryption.7z')); 66 await assertRejects(() => drain(reader), MissingPasswordError); 67}); 68 69// #endregion 70 71// #region wrong password 72 73Deno.test('unsevenzip: wrong password on encrypted entry throws InvalidPasswordError', async () => { 74 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption.7z')); 75 await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError); 76}); 77 78Deno.test('unsevenzip: wrong password on encrypted header throws InvalidPasswordError', async () => { 79 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption_header.7z')); 80 await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError); 81}); 82 83Deno.test('unsevenzip: wrong password on partially-encrypted archive throws InvalidPasswordError', async () => { 84 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption_partially.7z')); 85 await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError); 86}); 87 88Deno.test('unsevenzip: wrong password on bla.encrypted throws InvalidPasswordError', async () => { 89 const reader = fromUint8Array(load(COMMONS_COMPRESS, 'bla.encrypted.7z')); 90 await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError); 91}); 92 93Deno.test('unsevenzip: wrong password on filename_encryption throws InvalidPasswordError', async () => { 94 const reader = fromUint8Array(load(PY7ZR, 'filename_encryption.7z')); 95 await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError); 96}); 97 98// #endregion 99 100// #region malformed archives 101 102Deno.test('unsevenzip: libarchive malformed.7z truncated NextHeader', async () => { 103 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_malformed.7z')); 104 await assertRejects(() => drain(reader), TruncatedArchiveError); 105}); 106 107Deno.test('unsevenzip: libarchive malformed2.7z truncated NextHeader', async () => { 108 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_malformed2.7z')); 109 await assertRejects(() => drain(reader), TruncatedArchiveError); 110}); 111 112Deno.test('unsevenzip: libarchive malformed3.7z rejected as non-7z input', async () => { 113 const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_malformed3.7z')); 114 await assertRejects(() => drain(reader), InvalidSignatureError); 115}); 116 117Deno.test('unsevenzip: COMPRESS-542-endheadercorrupted fails StartHeader CRC', async () => { 118 const reader = fromUint8Array(load(COMMONS_COMPRESS, 'COMPRESS-542-endheadercorrupted.7z')); 119 await assertRejects(() => drain(reader), ChecksumMismatchError); 120}); 121 122Deno.test('unsevenzip: COMPRESS-542-endheadercorrupted2 fails StartHeader CRC', async () => { 123 const reader = fromUint8Array(load(COMMONS_COMPRESS, 'COMPRESS-542-endheadercorrupted2.7z')); 124 await assertRejects(() => drain(reader), ChecksumMismatchError); 125}); 126 127Deno.test('unsevenzip: bla.noendheaderoffset fails StartHeader CRC', async () => { 128 const reader = fromUint8Array(load(COMMONS_COMPRESS, 'bla.noendheaderoffset.7z')); 129 await assertRejects(() => drain(reader), ChecksumMismatchError); 130}); 131 132// #endregion 133 134// #region CRC mismatch on stored content 135 136Deno.test('unsevenzip: mangled pack byte triggers ChecksumMismatchError on extraction', async () => { 137 // 7z-hello-mhc-off-copy.7z stores raw "Hello, world!\n" at offset 32 with 138 // an entry CRC. flipping one byte changes the computed CRC over the 139 // reconstructed plaintext, so one of the CRC checks (folder- or entry- 140 // level) must fire. 141 const data = load(COMMONS_COMPRESS, '7z-hello-mhc-off-copy.7z'); 142 const mangled = new Uint8Array(data); 143 mangled[32] ^= 0xff; 144 const reader = fromUint8Array(mangled); 145 await assertRejects(() => drain(reader), ChecksumMismatchError); 146}); 147 148// #endregion