npm @zip.js/zip.js 2.16.1

latest releases: 2.24.0, 2.23.0, 2.22.1...
one month ago

What's Changed in v2.16.1

New features

  • The filesystem API tries several passwords on an entry imported from a zip file when its data is read. The core ZipReader API keeps one password per reader or per call. The new passwords option lists the candidates, and the new requestPassword option is a function asked for a password when every candidate has failed, or when there is none: it is called with the entry being read and the error raised by the last candidate, it can return a promise, e.g. to prompt the user, and it gives up by returning undefined or null, in which case the entry fails with ERR_INVALID_PASSWORD, or with ERR_ENCRYPTED when nothing was tried. Both options are accepted by the import*() methods, by the readerOptions of the export*() methods and by the get*() methods of a file entry. The candidates are tried in order: the password or rawPassword option, then the passwords that already decrypted another entry of the same zip file, most recent first, then the passwords list, so the wrong candidates ahead of the right one are tried once for the archive, not once per entry. When several entries are read concurrently, e.g. by export*() with bufferedWrite, one requestPassword call is pending at a time, and the other entries try its answer before asking. With candidates in play, a ZipCrypto entry, whose one-byte check accepts one wrong password in 256, is checked on its header first without reading the content, the CRC-32 of the content is then verified whatever checkCrc32 says, and any failure of that read counts as a wrong password. An AES entry reports a failure of the read as-is. ERR_INVALID_PASSWORDS and ERR_INVALID_REQUEST_PASSWORD are thrown when the options have the wrong type, and the latter also when the function returns a value of another type
  • An error raised while exporting because the data of an entry cannot be read now carries the entry as entry and its name relative to the exported directory as entryName, next to the entryId it already had, and keeps its own cause, e.g. the codec error behind ERR_INVALID_COMPRESSED_DATA for an entry imported from a corrupted zip file. The export used to replace the cause with an object holding the entry, a property that was not documented

Bug fixes

  • A read or write of the WebAssembly codec torn down before the end of the entry, by a cancelled readable, an aborted signal, a failing source, a corrupted body or a wrong password, kept its two 64 KB buffers and its zlib state in the 16 MB heap of the module in every browser, because the codec freed them in the cancel() hook of a TransformStream transformer, which Node.js and Deno call and no browser does. That codec runs for deflate when useCompressionStream is false or the host has no native "deflate-raw", and its AES engine runs for the encrypted entries, whose contexts live in the same heap. About 120 aborted inflates or 40 aborted deflates exhausted the heap of the worker, or of the page without workers, and every later entry failed with allocation failed. The bundled zlib-streams 1.2.3 frees them on any teardown, the AES streams release their engine context the same way, and the wrappers of the entry stream cancel the codec when their own read fails. On a page that loads web-streams-polyfill, which Firefox below 102 needs, such a read used to error without cancelling the codec, so the pair stayed allocated for the life of the page
  • An empty rawPassword, i.e. an empty Uint8Array, means no raw password, like an empty password string. Reading an encrypted entry with one used to wait forever for a key that was never derived, and writing an entry with one used to encrypt it with an empty key, so it could not be read back with the password string passed next to it

Documentation

  • EntryError documents its entry property, and ERR_DUPLICATE_IMPORTED_ENTRY documents that its cause.entry holds the EntryMetaData of the entry that could not be imported

Tests and continuous integration

  • New tests cover the password candidates, with the ZipCrypto false accept found by brute force on the encryption header, the entry and the cause carried by an export error, the WebAssembly buffers and the AES contexts released by aborted reads and writes on the browser matrix, the heap capacity of the module after those teardowns, and the empty raw password on both sides
  • npm run test-deno-polyfill, part of npm run test-ci, runs the suite on Deno with web-streams-polyfill in place of the native streams, pipeTo() and pipeThrough() removed from the platform readables and CompressionStream and DecompressionStream removed, which is what the Firefox 79 job runs and the only way to reproduce a failure of that job on a developer machine
  • The mangling audit of the test suite lists the internal fields whose name collides with a host property, e.g. a DOM one, and therefore ships unmangled. Four were renamed, with no API change
  • The browser runner prints the message of a failing test with its stack

Full Changelog: v2.16.0...v2.16.1

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

Don't miss a new zip.js release

NewReleases is sending notifications on new releases.