Something went wrong. Try again.
streaming 7-Zip archive extractor
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149import { assertRejects } from '@std/assert';
import { ChecksumMismatchError, InvalidPasswordError, InvalidSignatureError, MissingPasswordError, TruncatedArchiveError, UnsupportedFeatureError,} from '../lib/errors.ts';import { unsevenzip } from '../lib/extract.ts';import { fromUint8Array } from '../lib/reader/common.ts';
const COMMONS_COMPRESS = new URL('./fixtures/commons-compress/', import.meta.url);const LIBARCHIVE = new URL('./fixtures/libarchive/', import.meta.url);const PY7ZR = new URL('./fixtures/py7zr/', import.meta.url);
const load = (base: URL, name: string): Uint8Array<ArrayBuffer> => Deno.readFileSync(new URL(name, base));
const drain = async (reader: ReturnType<typeof fromUint8Array>, password?: string): Promise<void> => { for await (const entry of unsevenzip(reader, password !== undefined ? { password } : {})) { await entry.bytes(); }};
// #region unsupported codecs
Deno.test('unsevenzip: bzip2 throws UnsupportedFeatureError at decode time', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_bzip2.7z')); await assertRejects(() => drain(reader), UnsupportedFeatureError);});
Deno.test('unsevenzip: bcj2+bzip2 throws UnsupportedFeatureError at decode time', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_bcj2_bzip2.7z')); await assertRejects(() => drain(reader), UnsupportedFeatureError);});
Deno.test('unsevenzip: lz4 throws UnsupportedFeatureError at decode time', async () => { const reader = fromUint8Array(load(PY7ZR, 'lz4.7z')); await assertRejects(() => drain(reader), UnsupportedFeatureError);});
Deno.test('unsevenzip: zstd+brotli throws UnsupportedFeatureError at decode time', async () => { const reader = fromUint8Array(load(PY7ZR, 'zstdmt-brotli.7z')); await assertRejects(() => drain(reader), UnsupportedFeatureError);});
// #endregion
// #region missing password
Deno.test('unsevenzip: encrypted entry without password throws MissingPasswordError', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption.7z')); await assertRejects(() => drain(reader), MissingPasswordError);});
Deno.test('unsevenzip: encrypted header without password throws MissingPasswordError', async () => { // bla.encrypted has an encrypted EncodedHeader — failure surfaces during // container parsing, before any entry is yielded. const reader = fromUint8Array(load(COMMONS_COMPRESS, 'bla.encrypted.7z')); await assertRejects(() => drain(reader), MissingPasswordError);});
Deno.test('unsevenzip: filename_encryption without password throws MissingPasswordError', async () => { const reader = fromUint8Array(load(PY7ZR, 'filename_encryption.7z')); await assertRejects(() => drain(reader), MissingPasswordError);});
// #endregion
// #region wrong password
Deno.test('unsevenzip: wrong password on encrypted entry throws InvalidPasswordError', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption.7z')); await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError);});
Deno.test('unsevenzip: wrong password on encrypted header throws InvalidPasswordError', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption_header.7z')); await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError);});
Deno.test('unsevenzip: wrong password on partially-encrypted archive throws InvalidPasswordError', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_encryption_partially.7z')); await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError);});
Deno.test('unsevenzip: wrong password on bla.encrypted throws InvalidPasswordError', async () => { const reader = fromUint8Array(load(COMMONS_COMPRESS, 'bla.encrypted.7z')); await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError);});
Deno.test('unsevenzip: wrong password on filename_encryption throws InvalidPasswordError', async () => { const reader = fromUint8Array(load(PY7ZR, 'filename_encryption.7z')); await assertRejects(() => drain(reader, 'wrong'), InvalidPasswordError);});
// #endregion
// #region malformed archives
Deno.test('unsevenzip: libarchive malformed.7z truncated NextHeader', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_malformed.7z')); await assertRejects(() => drain(reader), TruncatedArchiveError);});
Deno.test('unsevenzip: libarchive malformed2.7z truncated NextHeader', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_malformed2.7z')); await assertRejects(() => drain(reader), TruncatedArchiveError);});
Deno.test('unsevenzip: libarchive malformed3.7z rejected as non-7z input', async () => { const reader = fromUint8Array(load(LIBARCHIVE, 'test_read_format_7zip_malformed3.7z')); await assertRejects(() => drain(reader), InvalidSignatureError);});
Deno.test('unsevenzip: COMPRESS-542-endheadercorrupted fails StartHeader CRC', async () => { const reader = fromUint8Array(load(COMMONS_COMPRESS, 'COMPRESS-542-endheadercorrupted.7z')); await assertRejects(() => drain(reader), ChecksumMismatchError);});
Deno.test('unsevenzip: COMPRESS-542-endheadercorrupted2 fails StartHeader CRC', async () => { const reader = fromUint8Array(load(COMMONS_COMPRESS, 'COMPRESS-542-endheadercorrupted2.7z')); await assertRejects(() => drain(reader), ChecksumMismatchError);});
Deno.test('unsevenzip: bla.noendheaderoffset fails StartHeader CRC', async () => { const reader = fromUint8Array(load(COMMONS_COMPRESS, 'bla.noendheaderoffset.7z')); await assertRejects(() => drain(reader), ChecksumMismatchError);});
// #endregion
// #region CRC mismatch on stored content
Deno.test('unsevenzip: mangled pack byte triggers ChecksumMismatchError on extraction', async () => { // 7z-hello-mhc-off-copy.7z stores raw "Hello, world!\n" at offset 32 with // an entry CRC. flipping one byte changes the computed CRC over the // reconstructed plaintext, so one of the CRC checks (folder- or entry- // level) must fire. const data = load(COMMONS_COMPRESS, '7z-hello-mhc-off-copy.7z'); const mangled = new Uint8Array(data); mangled[32] ^= 0xff; const reader = fromUint8Array(mangled); await assertRejects(() => drain(reader), ChecksumMismatchError);});
// #endregion