@onekeyfe/react-native-bundle-update 3.0.53 → 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.
package/android/src/main/java/com/margelo/nitro/reactnativebundleupdate/ReactNativeBundleUpdate.kt
CHANGED
|
@@ -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.
|
|
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()
|
|
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
|
|
535
|
-
val relativePath = file.absolutePath.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|