Skip to content

fix: keep zip progress monotonic and align missing-file errors - #397

Draft
plrthink wants to merge 2 commits into
masterfrom
cursor/progress-errors-abort-fef8
Draft

plrthink wants to merge 2 commits into
masterfrom
cursor/progress-errors-abort-fef8

Conversation

@plrthink

@plrthink plrthink commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Android zip progress counted work while it emitted, so a file followed by a folder could move progress backwards. unzipAssets added ZipEntry.getCompressedSize(), which ZipInputStream often reports as -1. A few other native paths disagreed with the documented error and extract behavior.

End-to-end tests now drive the same code the module calls (ZipProgress.events, ZipProgress.advanceAssetBytes, ZipExtractor.extractAll, ZipErrorCodes.mapException, ZipEncryptionChoice.parse, and JS zip()). They were run against the previous behavior first.

Red — same tests, previous behavior

Java, 6 failures:

  • zip progress: [0.0, 1.0, 0.6666666666666666, 1.0, 1.0] — first unit already 100%, then the value rewinds
  • unzipAssets progress: [0.0, 0.2, 0.199, 1.0] after a compressed size of -1
  • empty directory dropped; dest contained only note.txt
  • entry . rejected as Zip Slip
  • bare AES threw ArrayIndexOutOfBoundsException
  • missing archive mapped to ERR_UNZIP (zip file does not exist, cannot read comment)

JS zip() against the previous index.js: the signal that flips to aborted inside addEventListener resolved "/mock/path.zip" instead of rejecting ERR_CANCELLED.

Green — same tests, this branch

  • JUnit: 28 tests OK, including the 7 end-to-end cases
  • Jest: __tests__/zip-archive.e2e.test.js 2 tests passed; full suite 70 tests passed

Behavior

  • Android zip / zipWithPassword count work units before the first progress event.
  • Android unzipAssets uses bytes copied when the compressed size is unknown. Progress stays within 0–1.
  • Full Android unzip / unzipWithPassword create directory entries, including empty directories. An entry that resolves to the destination itself is accepted; a sibling prefix such as dest-evil is still rejected.
  • Missing archives reject with ERR_FILE_NOT_FOUND.
  • An invalid charset name rejects with ERR_UNSUPPORTED. A bare AES method is AES-128. An unknown method stays ZipCrypto.
  • Successful Android zips fsync before resolve.
  • An AbortSignal that aborts before the listener is attached rejects with ERR_CANCELLED and does not start native work.

JavaScript call sites are unchanged. A native rebuild is required.

Test plan

  • End-to-end tests fail on the previous zip/extract/abort behavior
  • npm test (70 tests)
  • npm run lint
  • npm run test:docs-sync
  • JUnit for progress, encryption, error codes, Zip Slip, and the zip round trip (28 tests)
  • Device extract still needs a native rebuild
Open in Web Open in Cursor 

cursoragent and others added 2 commits October 2, 2026 04:34
Android zip progress grew its total while emitting, so a file followed
by a folder could move progress backwards. unzipAssets added compressed
sizes that ZipInputStream often reports as -1. Full unzip skipped
directory entries, and several missing-archive paths rejected with a
generic code.

Count zip work up front, attribute unknown asset sizes to bytes copied,
create directory entries, fsync successful Android zips, and reject
missing archives with ERR_FILE_NOT_FOUND. An AbortSignal that flips
aborted before the listener is attached now rejects without starting
native work.

Co-authored-by: plrthink <plrthink@gmail.com>
The same tests fail on the previous behavior: zip progress hits 100%
and then rewinds, unzipAssets progress drops when a compressed size is
-1, empty directories are dropped, a bare AES method throws, a missing
archive maps to ERR_UNZIP, and an AbortSignal that flips during
addEventListener still resolves the zip.

Route native zip progress and extract through ZipProgress and
ZipExtractor so the tests execute the code the module calls.

Co-authored-by: plrthink <plrthink@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants