Skip to content

Binary patching: replace copy + delete with a filtered copy - #67

Open
ofalvai wants to merge 1 commit into
masterfrom
push-trysqrlsvyss
Open

ofalvai wants to merge 1 commit into
masterfrom
push-trysqrlsvyss

Conversation

@ofalvai

@ofalvai ofalvai commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Why

At the moment, the install flow for a binary diff update is:

  1. Copy all of the unzipped update over the files of the current package. This includes the reserved __hcp_patches/ folder with the patch files.
  2. Apply the patches.
  3. Delete __hcp_patches/ from the new package.

The copy in step 1 is unnecessary I/O, because the patcher reads the patches from the unzipped folder. Step 3 is also a failure point. If the delete fails or the process stops before it, the patches folder stays in the installed package. It is not part of the release contents, so it changes the folder hash. The result is an integrity-check failure that does not point to the real cause.

What

  • The copy helper on each platform gets an overload that skips one top-level entry. For binary diff updates, the install flow skips __hcp_patches/. Full updates and non-binary diff updates copy the same files as before.
  • The delete step after patching is removed.
  • New validation for patchedFiles keys in the diff manifest. The delete step was also a guard against a manifest that creates or patches a file inside __hcp_patches/, so the parser now does that check:
    • A key with a .. component is rejected. Without this, a key such as a/../__hcp_patches/x could get past the top-level check.
    • A key whose first real component is __hcp_patches is rejected. This includes ./__hcp_patches/x and /__hcp_patches/x.
  • Small refactor: version == 2 checks become an isBinaryDiff property on the manifest, on both platforms.

I do not think a real server ever sends a manifest like this, but after the delete step is removed, nothing else stops it, so the parser must reject it.

Copilot AI balanced review requested due to automatic review settings September 25, 2026 08:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Filtering only the downloaded source can leave a reserved patch directory copied from the current installed package on both platforms.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds filtered directory copying so binary patch payloads are excluded from installed CodePush packages.

Changes:

  • Adds top-level exclusion support to Android and iOS copy utilities.
  • Identifies binary manifests and rejects patched targets inside the reserved patch directory.
  • Adds Android and iOS coverage for filtering and manifest validation.
File Description
ios/​CodePushTests/​CodePushDiffManifestTests.swift Tests reserved patch-directory validation.
ios/​CodePush/​CodePushUpdateUtils.m Adds filtered copy support.
ios/​CodePush/​CodePushPackage.m Excludes patches during package merging.
ios/​CodePush/​CodePushDiffManifest.m Adds binary-diff detection and reserved-path validation.
ios/​CodePush/​CodePushDiffManifest.h Exposes binary-diff status.
ios/​CodePush/​CodePush.h Declares the filtered copy overload.
android/​app/​src/​test/​java/​com/​microsoft/​codepush/​react/​FileUtilsTest.kt Tests top-level exclusion behavior.
android/​app/​src/​test/​java/​com/​microsoft/​codepush/​react/​diffpatch/​DiffManifestTest.kt Tests reserved-path validation.
android/​app/​src/​test/​java/​com/​microsoft/​codepush/​react/​CodePushUpdateManagerTest.kt Tests package hashing without patch payloads.
android/​app/​src/​main/​java/​com/​microsoft/​codepush/​react/​FileUtils.java Adds filtered directory copying.
android/​app/​src/​main/​java/​com/​microsoft/​codepush/​react/​diffpatch/​DiffManifest.kt Adds binary-diff status and path validation.
android/​app/​src/​main/​java/​com/​microsoft/​codepush/​react/​CodePushUpdateManager.java Filters patch payloads during installation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +279 to +281
boolean isBinaryDiffUpdate = isDiffUpdate && diffManifest.isBinaryDiff();
FileUtils.copyDirectoryContents(unzippedFolderPath, newUpdateFolderPath,
isBinaryDiffUpdate ? CodePushConstants.DIFF_PATCHES_FOLDER_NAME : null);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's acceptable, and a bit more correct than the old behavior. I don't think any app would ship a __hcp_patches folder on purpose, but this new behavior preserves that dir.

Comment on lines 287 to 290
[CodePushUpdateUtils copyEntriesInFolder:unzippedFolderPath
destFolder:newUpdateFolderPath
excludingEntry:(diffManifest.isBinaryDiff ? CodePushDiffPatchesFolderName : nil)
error:&error];

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the above comment about the Android impl, see my response there. I think it's acceptable.

@ofalvai
ofalvai marked this pull request as ready for review September 25, 2026 13:54
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