@onekeyfe/react-native-bundle-update 3.0.52 → 3.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,18 +137,26 @@ object BundleUpdateStoreAndroid {
137
137
  fun setCurrentBundleVersionAndSignature(context: Context, version: String, signature: String?) {
138
138
  val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
139
139
  val currentVersion = prefs.getString(CURRENT_BUNDLE_VERSION_KEY, "")
140
+
141
+ // Write the signature file BEFORE flipping the current-version pointer,
142
+ // and commit the pref synchronously (commit() not apply()). This matches
143
+ // the atomic intent of the commit() pre-launch path and guarantees that
144
+ // a crash can never leave the version pointing at a bundle whose
145
+ // signature is missing — which would make a perfectly valid bundle fail
146
+ // verification and get discarded on next launch. Either the pointer flip
147
+ // hasn't happened (old bundle stays active, new bundle untouched on disk)
148
+ // or both signature + version are durably persisted together.
149
+ if (!signature.isNullOrEmpty()) {
150
+ writeSignatureFile(context, version, signature)
151
+ }
152
+
140
153
  val editor = prefs.edit()
141
154
  editor.putString(CURRENT_BUNDLE_VERSION_KEY, version)
142
155
  // Remove old signature key from prefs (legacy cleanup)
143
156
  if (!currentVersion.isNullOrEmpty()) {
144
157
  editor.remove(currentVersion)
145
158
  }
146
- editor.apply()
147
-
148
- // Store signature to file
149
- if (!signature.isNullOrEmpty()) {
150
- writeSignatureFile(context, version, signature)
151
- }
159
+ editor.commit()
152
160
  }
153
161
 
154
162
  fun clearUpdateBundleData(context: Context) {
@@ -526,13 +534,24 @@ object BundleUpdateStoreAndroid {
526
534
  }
527
535
 
528
536
  private fun validateFilesRecursive(dir: File, metadata: Map<String, String>, jsBundleDir: String): Boolean {
529
- val files = dir.listFiles() ?: return true
537
+ val files = dir.listFiles()
538
+ if (files == null) {
539
+ // listFiles() returns null on I/O error or unreadable directory.
540
+ // Fail closed instead of treating an unlistable subtree as
541
+ // "nothing to verify" — that would silently allow a tampered
542
+ // bundle asset whose containing dir was made unreadable.
543
+ OneKeyLog.error(
544
+ "BundleUpdate",
545
+ "[bundle-verify] failed to list directory: ${dir.absolutePath}",
546
+ )
547
+ return false
548
+ }
530
549
  for (file in files) {
531
550
  if (file.isDirectory) {
532
551
  if (!validateFilesRecursive(file, metadata, jsBundleDir)) return false
533
552
  } else {
534
- if (file.name.contains("metadata.json") || file.name.contains(".DS_Store")) continue
535
- val relativePath = file.absolutePath.replace(jsBundleDir, "")
553
+ if (file.name == "metadata.json" || file.name == ".DS_Store") continue
554
+ val relativePath = file.absolutePath.removePrefix(jsBundleDir)
536
555
  val expectedSHA256 = metadata[relativePath]
537
556
  if (expectedSHA256 == null) {
538
557
  OneKeyLog.error("BundleUpdate", "[bundle-verify] File on disk not found in metadata: $relativePath")
@@ -1208,8 +1227,12 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
1208
1227
  // partial/manifest left by an earlier interrupted attempt.
1209
1228
  if (partialFile.exists()) partialFile.delete()
1210
1229
  File("$partialFilePath.progress").delete()
1211
- isDownloading.set(false)
1230
+ // Keep isDownloading held across the skip delay below. Clearing
1231
+ // it before the sleep opens a ~1s window where a second
1232
+ // downloadBundle could pass the getAndSet guard and run
1233
+ // concurrently. Reset only after the delay completes.
1212
1234
  Thread.sleep(1000)
1235
+ isDownloading.set(false)
1213
1236
  sendEvent("update/complete")
1214
1237
  return@async result
1215
1238
  } else {
@@ -2051,7 +2074,11 @@ n2DMz6gqk326W6SFynYtvuiXo7wG4Cmn3SuIU8xfv9rJqunpZGYchMd7nZektmEJ
2051
2074
  val resolvedPath = File(filePath).canonicalPath
2052
2075
  val bundleDir = File(BundleUpdateStoreAndroid.getBundleDir(context)).canonicalPath
2053
2076
  val downloadDir = File(BundleUpdateStoreAndroid.getDownloadBundleDir(context)).canonicalPath
2054
- if (!resolvedPath.startsWith(bundleDir) && !resolvedPath.startsWith(downloadDir)) {
2077
+ // Enforce a separator boundary so e.g. "onekey-bundle-evil/x" is not
2078
+ // accepted as confined under "onekey-bundle". The base dir itself is allowed.
2079
+ fun isConfinedTo(base: String): Boolean =
2080
+ resolvedPath == base || resolvedPath.startsWith(base + File.separator)
2081
+ if (!isConfinedTo(bundleDir) && !isConfinedTo(downloadDir)) {
2055
2082
  OneKeyLog.error("BundleUpdate", "getSha256FromFilePath: path outside allowed directories: $resolvedPath")
2056
2083
  throw Exception("File path outside allowed bundle directories")
2057
2084
  }
@@ -2048,7 +2048,12 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
2048
2048
  let resolvedPath = (filePath as NSString).resolvingSymlinksInPath
2049
2049
  let bundleDir = (BundleUpdateStore.bundleDir() as NSString).resolvingSymlinksInPath
2050
2050
  let downloadDir = (BundleUpdateStore.downloadBundleDir() as NSString).resolvingSymlinksInPath
2051
- guard resolvedPath.hasPrefix(bundleDir) || resolvedPath.hasPrefix(downloadDir) else {
2051
+ // Enforce a separator boundary so e.g. "onekey-bundle-evil/x" is not
2052
+ // accepted as confined under "onekey-bundle". The base dir itself is allowed.
2053
+ func isConfined(to base: String) -> Bool {
2054
+ return resolvedPath == base || resolvedPath.hasPrefix(base + "/")
2055
+ }
2056
+ guard isConfined(to: bundleDir) || isConfined(to: downloadDir) else {
2052
2057
  OneKeyLog.error("BundleUpdate", "getSha256FromFilePath: path outside allowed directories: \(resolvedPath)")
2053
2058
  throw NSError(domain: "BundleUpdate", code: -1, userInfo: [NSLocalizedDescriptionKey: "File path outside allowed bundle directories"])
2054
2059
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-bundle-update",
3
- "version": "3.0.52",
3
+ "version": "3.0.54",
4
4
  "description": "react-native-bundle-update",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",