fix(android): skip symlink entries during extraction (#357)#361
Conversation
zip4j defaults UnzipParameters.extractSymbolicLinks to true and does not validate that a symlink's resolved target stays inside the destination directory, so a crafted archive could plant a symlink escaping the extraction root. unzip/unzipWithPassword now extract with symlinks disabled (zip4j skips such entries entirely); unzipAssets is unaffected by construction. Adds a regression test with an embedded symlink-entry fixture plus a control test proving the fixture exercises the vulnerable default path.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
|
||
| if (!fileHeader.isDirectory()) { | ||
| zipFile.extractFile(fileHeader, destDirectory); | ||
| zipFile.extractFile(fileHeader, destDirectory, ZipSecurity.createExtractParameters()); |
There was a problem hiding this comment.
🔍 iOS symlink-target handling not addressed by this Android-only fix
REVIEW.md flags path-traversal / Zip Slip changes for extra scrutiny across both platforms. This PR only hardens the Android extraction path against symlink escape; ios/RNZipArchive.mm is untouched. Since the PR does not modify the codegen spec, this is not a spec-parity violation, and the symlink-target escape may or may not apply to the iOS unzip backend. Worth confirming whether the iOS implementation has an equivalent exposure so the fix isn't left half-applied.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Re: Devin's review comment on iOS symlink-target handling — checked this, and iOS is already protected: our |
Fixes #357.
Problem
zip4j 2.11.5 defaults
UnzipParameters.extractSymbolicLinkstotrueand does not validate that a symlink's resolved target stays inside the destination directory.unzip/unzipWithPasswordcalledextractFilewithout parameters, so a crafted archive entry likelink_to_outside -> ../outside_target.txtwould materialize a symlink escaping the extraction root. (The v9ZipSecurityZip Slip guard covers entry names, not symlink targets.)Fix
ZipSecurity.createExtractParameters()returnsUnzipParameterswithsetExtractSymbolicLinks(false); both extraction call sites use it. With symlinks disabled, zip4j skips symlink entries entirely (verified against the 2.11.5 bytecode), so nothing can escape the destination.unzipAssetsis unaffected by construction (manual stream copy, never creates symlinks).Tests
extractWithZipSecurityParams_doesNotCreateSymlink: regression test using an embedded base64 fixture zip containinglink_to_outside -> ../outside_target.txt; asserts nothing is materialized and nothing appears outside the destination.extractWithDefaultParams_createsEscapingSymlink: control test proving the fixture exercises the vulnerable zip4j default path.createExtractParameters_disablesSymlinkExtraction: sanity check on the params themselves../gradlew :react-native-zip-archive:testDebugUnitTest— 11/11 pass.npx jest— 27/27 pass.Thanks @kimdu0 for the thorough report and repro.