# Browser Archive Conversion

## Engine and Licenses

- Official npm `libarchive.js@2.0.2`: https://registry.npmjs.org/libarchive.js/-/libarchive.js-2.0.2.tgz
- Official repository revision: `0989c1a6db20d030d793b1763e20d880068091bd`, https://github.com/nika-begiashvili/libarchivejs/tree/0989c1a6db20d030d793b1763e20d880068091bd
- npm integrity: `sha512-JHb+P4suNSjvz/dMdRgOe7JAxluXJeialzSFkKHU5y0ZK+m175drPOaIYW6I9WXSDcPcQ13eCUgMnpgY0ggmoQ==`.
- npm files remain intact, including MIT `LICENSE`, source, dist worker, and original WASM. Runtime uses the source-generated ES loader plus the locally adapted WASM, not the upstream high-level archive object API.
- Upstream build Dockerfile pins libarchive 3.7.2, zlib 1.3, bzip2 1.0.8, xz/liblzma 5.2.11, OpenSSL 1.0.2s. Their notices are under `licenses/`. `licenses/LICENSE` is bzip2's original license. Emscripten's license is included. The full official libarchive v3.7.2 source tarball is retained there to preserve all individual source copyright notices and fixture provenance.
- ZIP output uses the site's existing `../jszip.min.js` (JSZip 3.10.1), under its MIT license option. No replacement or modification of the shared vendor file.

## Why a Local WASM Adaptation?

Upstream `lib/wrapper/main.c` hides `archive_read_next_header` failures as EOF and discards the byte count returned by `archive_read_data`. Trusting that API can turn damaged archives into partial ZIPs. `build-safe.cjs` mechanically adapts only those wrappers in the exact hash-pinned official binary, preserving the decompression implementation:

1. Distinguish successful header, clean EOF, and every warning/error.
2. Expose the actual data read count through the existing i32 temporary-return global. The same global supplies the high 32 bits of entry sizes; nonzero high bits are rejected before allocation.
3. Reduce the WASM memory maximum from 2GiB to 256MiB.

The worker checks exact byte counts, an additional EOF read, native errors and encryption flags. No dynamic archive path is used as a JavaScript object key. ZIP results are published only after all entries pass. WABT is a build-only dependency, not loaded by the browser.

Rebuild (WABT 1.0.39):

```sh
npm install --prefix /tmp/p2-archive-build wabt@1.0.39 --ignore-scripts
NODE_PATH=/tmp/p2-archive-build/node_modules node vendor/libarchive-2.0.2/build-safe.cjs
```

Original WASM SHA-256: `8a66066ed3ea1e6db0469ec752d8fca888a6bf737c6cff21fdbcb62241a72424`.
Adapted WASM SHA-256: `c45791539662a09f585054d09bfb8dcb5ef0bd0e0041feee8f9bd5afb6216dee`.

## Fixture Provenance

Positive fixtures and expected file contents are copied without modification from the pinned official libarchivejs repository's `test/files/archives/{7z,rar}` and `test/archive_content/test`. They are distributed with its MIT license. These are public repository test assets, not user files. Tests compare extracted output to expected contents by SHA-256 and independently load the downloaded ZIP using JSZip with CRC verification. Python/XML sample contents are treated only as bytes and never executed.

| Fixture | SHA-256 |
| --- | --- |
| 7z/bzip2.7z | 312091623b6f56b93f0a949bbea54866a4f52959f7c62fdce9389705b442643a |
| 7z/lzma.7z | c70c6cb460e1d193308ea8625fa5f438a8cb16d1e22129b6a4af76959d0cd9a2 |
| 7z/lzma2.7z | e1096e068cbcdf8aac238ab9e71d2b6e948ad9ffb026a76f7419cc18d9c2ec82 |
| rar/test-v4.rar | 198080e63eb83f0c58cee8c53777332c4cbe09d5f9cbd35138a5e46b94add530 |
| rar/test-v5.rar | c6442ed8caff535e9c1dcfd38e0aeed96ac51121335df83b5b68676b6ddbd4ed |

Negative `.uu` fixtures under `fixtures/rejection/` are unchanged from https://github.com/libarchive/libarchive/tree/v3.7.2/libarchive/test. Corresponding encryption test sources retain author-specific BSD notices; the complete release is in `licenses/libarchive-3.7.2-source.tar.gz`. Tests decode uuencoding only in memory and deliberately rename split volumes to verify content-based rejection. Coverage includes 7z and RAR data/header/partial encryption, RAR4 and RAR5 multivolume archives, truncated/wrong signatures, and policy boundaries.

## Support and Bounds

Supported targets: single unencrypted RAR v4/v5 and 7z, as decoded by this engine. Not all historical methods, malformed-but-tolerated archives, or service metadata are supported. Conservative RAR container validation rejects unrecognized block types. ALZ and EGG are NOT supported: neither appears in libarchive 3.7.2's `archive_read_support_format_all.c` registration list, and libarchive.js does not add a decoder. An actual licensed ALZ/EGG engine with browser-compatible extraction and safety APIs is required to unblock them. Renaming or JSZip cannot add support. This is not an ALTools replacement.

- Input 32MiB; individual uncompressed file 32MiB; total 64MiB.
- At most 1,000 entries including directories; maximum total/input expansion ratio 100.
- Maximum path 512 characters and 20 components; reject absolute, backslash, control, parent/current, Windows reserved, prototype-sensitive, duplicate/case-normalized collision paths and file/directory conflicts.
- Reject symlinks and special entries; preserve ordinary file bytes/directories only. No recursive unpacking, code execution, preview, permissions, ownership, comments, or timestamp preservation.
- Main-thread 30-second watchdog and cancel/reset terminate the dedicated worker, including extraction and ZIP generation. WASM memory is capped at 256MiB; JS buffers and ZIP generation add memory beyond that cap. No claim of an overall browser-memory cap or antivirus guarantee.
- Empty containers and directory-only archives produce no ZIP. Valid zero-byte files may be retained.
- Container signatures, RAR header CRCs, 7z start/next-header CRCs, bounds and volume/encryption flags are checked before decoding; native decoder errors and CRC errors abort the whole conversion.
- Bundled engines are version-pinned, not claimed to be the latest native libarchive security release. Limits reduce resource exposure but are not a universal security guarantee. Review upstream security updates before changing the pin; wrapper adaptation fails closed on a different binary hash.

## Loading, Privacy and Verification

`file://` is intentionally disabled with a visible HTTP(S) instruction. Use a static HTTP(S) server; Worker, ES imports, WASM and JSZip are same-origin relative assets. Serve `.js` as JavaScript and `.wasm` as `application/wasm`. No conversion request uploads input or output bytes. Advertising and icon styles may request third-party resources, but are not needed by extraction. Sharing only attaches output after explicit user action.

Run `tests/archive-tools-checks.cjs` with Playwright available via NODE_PATH. It starts and closes its own loopback server, blocks external requests, performs genuine extraction and downloaded-ZIP verification, tests rejection/state transitions and captures desktop/mobile screenshots under `/tmp/archive-tools-*.png`. No deployment server is required or left running. Page registration belongs to the parent task; these changes do not modify index/app/catalogue.
