@hot-updater/react-native 0.31.3 → 0.32.0

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.
Files changed (29) hide show
  1. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +12 -4
  2. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +24 -38
  3. package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +62 -0
  4. package/android/src/main/java/com/hotupdater/RelativePathResolver.kt +41 -0
  5. package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +11 -12
  6. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +2 -2
  7. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +2 -2
  8. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +9 -14
  9. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +47 -0
  10. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +20 -0
  11. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +11 -11
  12. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +20 -3
  13. package/lib/commonjs/DefaultResolver.spec.js +18 -10
  14. package/lib/commonjs/DefaultResolver.spec.js.map +1 -1
  15. package/lib/commonjs/index.spec.js +29 -24
  16. package/lib/commonjs/index.spec.js.map +1 -1
  17. package/lib/commonjs/sdkVersion.js +1 -1
  18. package/lib/module/DefaultResolver.spec.js +18 -9
  19. package/lib/module/DefaultResolver.spec.js.map +1 -1
  20. package/lib/module/index.spec.js +29 -24
  21. package/lib/module/index.spec.js.map +1 -1
  22. package/lib/module/sdkVersion.js +1 -1
  23. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  24. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  25. package/package.json +7 -7
  26. package/plugin/build/withHotUpdater.js +52 -40
  27. package/src/DefaultResolver.spec.ts +25 -10
  28. package/src/index.spec.ts +32 -24
  29. package/src/sdkVersion.ts +1 -1
@@ -391,7 +391,7 @@ class BundleFileStorageService(
391
391
  }
392
392
 
393
393
  val sourceDir = activeBundleDir ?: return false
394
- val sourceFile = File(sourceDir, assetPath)
394
+ val sourceFile = RelativePathResolver.resolveInside(sourceDir, assetPath) ?: return false
395
395
  if (!sourceFile.exists() || !HashUtils.verifyHash(sourceFile, patch.baseFileHash)) {
396
396
  return false
397
397
  }
@@ -735,11 +735,15 @@ class BundleFileStorageService(
735
735
  if (assetPath !is String) {
736
736
  return null
737
737
  }
738
+ val normalizedAssetPath = RelativePathResolver.normalizeRelativePath(assetPath)
739
+ if (normalizedAssetPath == null || normalizedAssetPath != assetPath) {
740
+ return null
741
+ }
738
742
 
739
743
  val assetMap = assetValue as? Map<*, *> ?: return null
740
744
  val fileHash = assetMap["fileHash"] as? String ?: return null
741
745
  val signature = assetMap["signature"] as? String
742
- assets[assetPath] =
746
+ assets[normalizedAssetPath] =
743
747
  ParsedManifestAsset(
744
748
  fileHash = fileHash,
745
749
  signature = signature?.takeIf { it.isNotBlank() },
@@ -1523,7 +1527,9 @@ class BundleFileStorageService(
1523
1527
  val targetEntries = targetManifest.assets.entries.toList()
1524
1528
  targetEntries.forEachIndexed { index, (assetPath, expectedAsset) ->
1525
1529
  val expectedHash = expectedAsset.fileHash
1526
- val targetFile = File(tmpDir, assetPath)
1530
+ val targetFile =
1531
+ RelativePathResolver.resolveInside(tmpDir, assetPath)
1532
+ ?: throw HotUpdaterException.invalidBundle()
1527
1533
  val currentAsset = currentManifest?.assets?.get(assetPath)
1528
1534
 
1529
1535
  if (currentAsset?.fileHash == expectedHash) {
@@ -1532,7 +1538,9 @@ class BundleFileStorageService(
1532
1538
  ?: throw HotUpdaterException.downloadFailed(
1533
1539
  IllegalStateException("Current bundle directory unavailable for reused asset: $assetPath"),
1534
1540
  )
1535
- val sourceFile = File(sourceDir, assetPath)
1541
+ val sourceFile =
1542
+ RelativePathResolver.resolveInside(sourceDir, assetPath)
1543
+ ?: throw HotUpdaterException.invalidBundle()
1536
1544
  if (!sourceFile.exists() || !HashUtils.verifyHash(sourceFile, expectedHash)) {
1537
1545
  throw HotUpdaterException.downloadFailed(
1538
1546
  IllegalStateException("Reusable asset missing or corrupted: $assetPath"),
@@ -105,14 +105,7 @@ class HotUpdaterImpl {
105
105
  * @return The isolation key in format: HotUpdaterPrefs_{fingerprintOrVersion}_{channel}
106
106
  */
107
107
  private fun getIsolationKey(context: Context): String {
108
- // Get fingerprint hash directly from resources
109
- val fingerprintId = StringResourceUtils.getIdentifier(context, "hot_updater_fingerprint_hash")
110
- val fingerprintHash =
111
- if (fingerprintId != 0) {
112
- context.getString(fingerprintId).takeIf { it.isNotEmpty() }
113
- } else {
114
- null
115
- }
108
+ val fingerprintHash = getFingerprintHash(context)
116
109
 
117
110
  // Get app version and channel
118
111
  val appVersion = getAppVersion(context) ?: "unknown"
@@ -147,14 +140,12 @@ class HotUpdaterImpl {
147
140
  null
148
141
  }
149
142
 
150
- fun getChannel(context: Context): String {
151
- val id = StringResourceUtils.getIdentifier(context, "hot_updater_channel")
152
- return if (id != 0) {
153
- context.getString(id).takeIf { it.isNotEmpty() } ?: DEFAULT_CHANNEL
154
- } else {
155
- DEFAULT_CHANNEL
156
- }
157
- }
143
+ fun getChannel(context: Context): String =
144
+ NativeConfigUtils.getString(
145
+ context,
146
+ NativeConfigUtils.CHANNEL_META_DATA_KEY,
147
+ "hot_updater_channel",
148
+ ) ?: DEFAULT_CHANNEL
158
149
 
159
150
  /**
160
151
  * Get minimum bundle ID string
@@ -221,28 +212,24 @@ class HotUpdaterImpl {
221
212
  * @param context Application context
222
213
  * @return The fingerprint hash or null if not set
223
214
  */
224
- fun getFingerprintHash(context: Context): String? {
225
- val id = StringResourceUtils.getIdentifier(context, "hot_updater_fingerprint_hash")
226
- return if (id != 0) {
227
- context.getString(id).takeIf { it.isNotEmpty() }
228
- } else {
229
- null
230
- }
231
- }
215
+ fun getFingerprintHash(context: Context): String? =
216
+ NativeConfigUtils.getString(
217
+ context,
218
+ NativeConfigUtils.FINGERPRINT_HASH_META_DATA_KEY,
219
+ "hot_updater_fingerprint_hash",
220
+ )
232
221
  }
233
222
 
234
223
  /**
235
224
  * Gets the current fingerprint hash
236
225
  * @return The fingerprint hash or null if not set
237
226
  */
238
- fun getFingerprintHash(): String? {
239
- val id = StringResourceUtils.getIdentifier(context, "hot_updater_fingerprint_hash")
240
- return if (id != 0) {
241
- context.getString(id).takeIf { it.isNotEmpty() }
242
- } else {
243
- null
244
- }
245
- }
227
+ fun getFingerprintHash(): String? =
228
+ NativeConfigUtils.getString(
229
+ context,
230
+ NativeConfigUtils.FINGERPRINT_HASH_META_DATA_KEY,
231
+ "hot_updater_fingerprint_hash",
232
+ )
246
233
 
247
234
  /**
248
235
  * Gets the current update channel
@@ -254,12 +241,11 @@ class HotUpdaterImpl {
254
241
  return overriddenChannel
255
242
  }
256
243
 
257
- val id = StringResourceUtils.getIdentifier(context, "hot_updater_channel")
258
- return if (id != 0) {
259
- context.getString(id).takeIf { it.isNotEmpty() } ?: DEFAULT_CHANNEL
260
- } else {
261
- DEFAULT_CHANNEL
262
- }
244
+ return NativeConfigUtils.getString(
245
+ context,
246
+ NativeConfigUtils.CHANNEL_META_DATA_KEY,
247
+ "hot_updater_channel",
248
+ ) ?: DEFAULT_CHANNEL
263
249
  }
264
250
 
265
251
  fun getDefaultChannel(): String = getChannel(context)
@@ -0,0 +1,62 @@
1
+ package com.hotupdater
2
+
3
+ import android.content.Context
4
+ import android.content.pm.PackageManager
5
+ import android.os.Build
6
+
7
+ object NativeConfigUtils {
8
+ const val CHANNEL_META_DATA_KEY = "com.hotupdater.CHANNEL"
9
+ const val FINGERPRINT_HASH_META_DATA_KEY = "com.hotupdater.FINGERPRINT_HASH"
10
+ const val PUBLIC_KEY_META_DATA_KEY = "com.hotupdater.PUBLIC_KEY"
11
+
12
+ fun getString(
13
+ context: Context,
14
+ metaDataKey: String,
15
+ stringResourceName: String,
16
+ ): String? =
17
+ getManifestMetaDataString(context, metaDataKey)
18
+ ?: getStringResource(context, stringResourceName)
19
+
20
+ private fun getManifestMetaDataString(
21
+ context: Context,
22
+ key: String,
23
+ ): String? =
24
+ try {
25
+ val packageManager = context.packageManager
26
+ val applicationInfo =
27
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
28
+ packageManager.getApplicationInfo(
29
+ context.packageName,
30
+ PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong()),
31
+ )
32
+ } else {
33
+ @Suppress("DEPRECATION")
34
+ packageManager.getApplicationInfo(
35
+ context.packageName,
36
+ PackageManager.GET_META_DATA,
37
+ )
38
+ }
39
+
40
+ @Suppress("DEPRECATION")
41
+ coerceManifestMetaDataString(applicationInfo.metaData?.get(key))
42
+ } catch (e: Exception) {
43
+ null
44
+ }
45
+
46
+ internal fun coerceManifestMetaDataString(value: Any?): String? =
47
+ value
48
+ ?.toString()
49
+ ?.takeIf { it.isNotEmpty() }
50
+
51
+ private fun getStringResource(
52
+ context: Context,
53
+ name: String,
54
+ ): String? {
55
+ val id = StringResourceUtils.getIdentifier(context, name)
56
+ return if (id != 0) {
57
+ context.getString(id).takeIf { it.isNotEmpty() }
58
+ } else {
59
+ null
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,41 @@
1
+ package com.hotupdater
2
+
3
+ import java.io.File
4
+
5
+ internal object RelativePathResolver {
6
+ fun normalizeRelativePath(path: String): String? {
7
+ if (path.isBlank() || path.indexOf('\u0000') >= 0 || path.contains('\\')) {
8
+ return null
9
+ }
10
+
11
+ if (File(path).isAbsolute || Regex("^[A-Za-z]:").containsMatchIn(path)) {
12
+ return null
13
+ }
14
+
15
+ val components = path.split("/")
16
+ if (components.any { it.isEmpty() || it == "." || it == ".." }) {
17
+ return null
18
+ }
19
+
20
+ return components.joinToString("/")
21
+ }
22
+
23
+ fun resolveInside(
24
+ root: File,
25
+ relativePath: String,
26
+ ): File? {
27
+ val normalizedPath = normalizeRelativePath(relativePath) ?: return null
28
+ val file = File(root, normalizedPath)
29
+ return if (isInside(root, file)) file else null
30
+ }
31
+
32
+ fun isInside(
33
+ root: File,
34
+ file: File,
35
+ ): Boolean {
36
+ val rootPath = root.canonicalFile.path
37
+ val filePath = file.canonicalFile.path
38
+ return filePath == rootPath ||
39
+ filePath.startsWith("$rootPath${File.separator}")
40
+ }
41
+ }
@@ -27,7 +27,7 @@ sealed class SignatureVerificationException(
27
27
  class PublicKeyNotConfigured :
28
28
  SignatureVerificationException(
29
29
  "Public key not configured for signature verification. " +
30
- "Add 'hot_updater_public_key' to res/values/strings.xml",
30
+ "Add 'com.hotupdater.PUBLIC_KEY' to AndroidManifest.xml",
31
31
  )
32
32
 
33
33
  class InvalidPublicKeyFormat :
@@ -97,16 +97,15 @@ object SignatureVerifier {
97
97
  * @return Public key PEM string or null if not configured
98
98
  */
99
99
  private fun getPublicKeyFromConfig(context: Context): String? {
100
- val resourceId = StringResourceUtils.getIdentifier(context, "hot_updater_public_key")
101
-
102
- if (resourceId == 0) {
103
- Log.d(TAG, "hot_updater_public_key not found in strings.xml")
104
- return null
105
- }
100
+ val publicKeyPEM =
101
+ NativeConfigUtils.getString(
102
+ context,
103
+ NativeConfigUtils.PUBLIC_KEY_META_DATA_KEY,
104
+ "hot_updater_public_key",
105
+ )
106
106
 
107
- val publicKeyPEM = context.getString(resourceId)
108
- if (publicKeyPEM.isEmpty()) {
109
- Log.d(TAG, "hot_updater_public_key is empty")
107
+ if (publicKeyPEM.isNullOrEmpty()) {
108
+ Log.d(TAG, "com.hotupdater.PUBLIC_KEY is not configured")
110
109
  return null
111
110
  }
112
111
 
@@ -229,7 +228,7 @@ object SignatureVerifier {
229
228
  val publicKeyPEM =
230
229
  getPublicKeyFromConfig(context)
231
230
  ?: run {
232
- Log.e(TAG, "Cannot verify signature: public key not configured in strings.xml")
231
+ Log.e(TAG, "Cannot verify signature: public key not configured")
233
232
  throw SignatureVerificationException.PublicKeyNotConfigured()
234
233
  }
235
234
 
@@ -267,7 +266,7 @@ object SignatureVerifier {
267
266
  val publicKeyPEM =
268
267
  getPublicKeyFromConfig(context)
269
268
  ?: run {
270
- Log.e(TAG, "Cannot verify signature: public key not configured in strings.xml")
269
+ Log.e(TAG, "Cannot verify signature: public key not configured")
271
270
  throw SignatureVerificationException.PublicKeyNotConfigured()
272
271
  }
273
272
 
@@ -72,9 +72,9 @@ class TarBrDecompressionStrategy : DecompressionStrategy {
72
72
  var entry = tarInputStream.getNextEntry()
73
73
 
74
74
  while (entry != null) {
75
- val file = File(destinationPath, entry.name)
75
+ val file = RelativePathResolver.resolveInside(destinationDir, entry.name)
76
76
 
77
- if (!file.canonicalPath.startsWith(destinationDir.canonicalPath)) {
77
+ if (file == null) {
78
78
  Log.w(TAG, "Skipping potentially malicious tar entry: ${entry.name}")
79
79
  entry = tarInputStream.getNextEntry()
80
80
  continue
@@ -73,9 +73,9 @@ class TarGzDecompressionStrategy : DecompressionStrategy {
73
73
  var entry = tarInputStream.getNextEntry()
74
74
 
75
75
  while (entry != null) {
76
- val file = File(destinationPath, entry.name)
76
+ val file = RelativePathResolver.resolveInside(destinationDir, entry.name)
77
77
 
78
- if (!file.canonicalPath.startsWith(destinationDir.canonicalPath)) {
78
+ if (file == null) {
79
79
  Log.w(TAG, "Skipping potentially malicious tar entry: ${entry.name}")
80
80
  entry = tarInputStream.getNextEntry()
81
81
  continue
@@ -117,21 +117,16 @@ class ZipDecompressionStrategy : DecompressionStrategy {
117
117
  ZipInputStream(bufferedInputStream).use { zipInputStream ->
118
118
  var entry: ZipEntry? = zipInputStream.nextEntry
119
119
  while (entry != null) {
120
- val file = File(destinationPath, entry.name)
121
-
122
- // Zip Slip vulnerability check - verify entry path is within destination
123
- try {
124
- val canonicalDestPath = destinationDir.canonicalPath
125
- val canonicalFilePath = file.canonicalPath
126
-
127
- if (!canonicalFilePath.startsWith(canonicalDestPath)) {
128
- Log.w(TAG, "Skipping potentially malicious zip entry: ${entry.name}")
129
- entry = zipInputStream.nextEntry
130
- continue
120
+ val file =
121
+ try {
122
+ RelativePathResolver.resolveInside(destinationDir, entry.name)
123
+ } catch (e: IOException) {
124
+ Log.w(TAG, "Failed to resolve canonical path for zip entry: ${entry.name}", e)
125
+ null
131
126
  }
132
- } catch (e: IOException) {
133
- // If we can't resolve canonical paths, treat as potentially malicious
134
- Log.w(TAG, "Failed to resolve canonical path for zip entry: ${entry.name}", e)
127
+
128
+ if (file == null) {
129
+ Log.w(TAG, "Skipping potentially malicious zip entry: ${entry.name}")
135
130
  entry = zipInputStream.nextEntry
136
131
  continue
137
132
  }
@@ -12,6 +12,8 @@ import org.junit.Test
12
12
  import org.junit.rules.TemporaryFolder
13
13
  import java.io.File
14
14
  import java.net.URL
15
+ import java.util.zip.ZipEntry
16
+ import java.util.zip.ZipOutputStream
15
17
 
16
18
  class BundleFileStorageServiceTest {
17
19
  @get:Rule
@@ -253,6 +255,41 @@ class BundleFileStorageServiceTest {
253
255
  assertTrue(invokeCanUseManifestDrivenInstall(service))
254
256
  }
255
257
 
258
+ @Test
259
+ fun `manifest driven install rejects unsafe asset paths`() {
260
+ val rootDir = temporaryFolder.newFolder("active-ota-unsafe-manifest")
261
+ val preferences = InMemoryPreferencesService()
262
+ val service = createService(rootDir, preferences)
263
+ val activeDir = createBundleDir(rootDir, "active-bundle")
264
+ val activeBundleFile = writeFile(activeDir, "index.android.bundle")
265
+ writeManifest(activeDir, listOf("../active-bundle_evil/index.android.bundle"))
266
+
267
+ preferences.setItem("HotUpdaterBundleURL", activeBundleFile.absolutePath)
268
+
269
+ assertFalse(invokeCanUseManifestDrivenInstall(service))
270
+ }
271
+
272
+ @Test
273
+ fun `zip decompression does not write sibling prefix traversal entries`() {
274
+ val rootDir = temporaryFolder.newFolder("zip-sibling-prefix")
275
+ val zipFile = File(rootDir, "bundle.zip")
276
+ ZipOutputStream(zipFile.outputStream()).use { zip ->
277
+ writeZipEntry(zip, "../bundle-temp_evil/escape.txt", "blocked")
278
+ writeZipEntry(zip, "safe/kept.txt", "kept")
279
+ }
280
+
281
+ val destinationDir = File(rootDir, "bundle-temp")
282
+ val extracted =
283
+ ZipDecompressionStrategy().decompress(
284
+ zipFile.absolutePath,
285
+ destinationDir.absolutePath,
286
+ ) {}
287
+
288
+ assertTrue(extracted)
289
+ assertTrue(File(destinationDir, "safe/kept.txt").isFile)
290
+ assertFalse(File(rootDir, "bundle-temp_evil/escape.txt").exists())
291
+ }
292
+
256
293
  private fun createService(
257
294
  rootDir: File,
258
295
  preferences: InMemoryPreferencesService = InMemoryPreferencesService(),
@@ -313,6 +350,16 @@ class BundleFileStorageServiceTest {
313
350
  writeText(content)
314
351
  }
315
352
 
353
+ private fun writeZipEntry(
354
+ zip: ZipOutputStream,
355
+ path: String,
356
+ content: String,
357
+ ) {
358
+ zip.putNextEntry(ZipEntry(path))
359
+ zip.write(content.toByteArray())
360
+ zip.closeEntry()
361
+ }
362
+
316
363
  private fun bundleStoreDir(rootDir: File): File = File(rootDir, "bundle-store").apply { mkdirs() }
317
364
 
318
365
  private fun invokeResolveBundleFile(
@@ -0,0 +1,20 @@
1
+ package com.hotupdater
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertNull
5
+ import org.junit.Test
6
+
7
+ class NativeConfigUtilsTest {
8
+ @Test
9
+ fun `coerces typed manifest metadata to strings`() {
10
+ assertEquals("123", NativeConfigUtils.coerceManifestMetaDataString(123))
11
+ assertEquals("true", NativeConfigUtils.coerceManifestMetaDataString(true))
12
+ assertEquals("-16711936", NativeConfigUtils.coerceManifestMetaDataString(-16711936))
13
+ }
14
+
15
+ @Test
16
+ fun `ignores missing and empty manifest metadata`() {
17
+ assertNull(NativeConfigUtils.coerceManifestMetaDataString(null))
18
+ assertNull(NativeConfigUtils.coerceManifestMetaDataString(""))
19
+ }
20
+ }
@@ -84,16 +84,14 @@ enum ArchiveExtractionUtilities {
84
84
  }
85
85
 
86
86
  static func normalizedRelativePath(from rawPath: String) -> String? {
87
- var candidate = rawPath
88
- .replacingOccurrences(of: "\\", with: "/")
89
- .trimmingCharacters(in: .whitespacesAndNewlines)
90
-
91
- while candidate.hasPrefix("/") {
92
- candidate.removeFirst()
93
- }
94
-
95
- while candidate.hasPrefix("./") {
96
- candidate.removeFirst(2)
87
+ let candidate = rawPath.trimmingCharacters(in: .whitespacesAndNewlines)
88
+ guard !candidate.isEmpty,
89
+ !candidate.contains("\0"),
90
+ !candidate.contains("\\"),
91
+ !candidate.hasPrefix("/"),
92
+ candidate.range(of: #"^[A-Za-z]:"#, options: .regularExpression) == nil
93
+ else {
94
+ return nil
97
95
  }
98
96
 
99
97
  let components = candidate
@@ -104,7 +102,9 @@ enum ArchiveExtractionUtilities {
104
102
  return nil
105
103
  }
106
104
 
107
- guard !components.contains(".."), !components.contains(".") else {
105
+ guard components.count == candidate.split(separator: "/", omittingEmptySubsequences: false).count,
106
+ !components.contains(".."),
107
+ !components.contains(".") else {
108
108
  return nil
109
109
  }
110
110
 
@@ -556,7 +556,15 @@ class BundleFileStorageService: BundleStorageService {
556
556
  return false
557
557
  }
558
558
 
559
- let sourcePath = (currentBundleDir as NSString).appendingPathComponent(assetPath)
559
+ let sourcePath: String
560
+ do {
561
+ sourcePath = try ArchiveExtractionUtilities.extractionURL(
562
+ for: assetPath,
563
+ destinationRoot: currentBundleDir
564
+ ).path
565
+ } catch {
566
+ return false
567
+ }
560
568
  guard self.fileSystem.fileExists(atPath: sourcePath),
561
569
  HashUtils.verifyHash(
562
570
  fileURL: URL(fileURLWithPath: sourcePath),
@@ -969,6 +977,9 @@ class BundleFileStorageService: BundleStorageService {
969
977
 
970
978
  var assets: [String: ParsedManifestAsset] = [:]
971
979
  for (assetPath, assetValue) in rawAssets {
980
+ guard ArchiveExtractionUtilities.normalizedRelativePath(from: assetPath) == assetPath else {
981
+ return nil
982
+ }
972
983
  guard let asset = assetValue as? [String: Any],
973
984
  let fileHash = asset["fileHash"] as? String,
974
985
  !fileHash.isEmpty
@@ -1738,7 +1749,10 @@ class BundleFileStorageService: BundleStorageService {
1738
1749
  let assetPath = asset.key
1739
1750
  let expectedAsset = asset.value
1740
1751
  let expectedHash = expectedAsset.fileHash
1741
- let destinationPath = (tmpDir as NSString).appendingPathComponent(assetPath)
1752
+ let destinationPath = try ArchiveExtractionUtilities.extractionURL(
1753
+ for: assetPath,
1754
+ destinationRoot: tmpDir
1755
+ ).path
1742
1756
  let destinationDir = (destinationPath as NSString).deletingLastPathComponent
1743
1757
  guard self.fileSystem.createDirectory(atPath: destinationDir) else {
1744
1758
  throw BundleStorageError.directoryCreationFailed
@@ -1755,7 +1769,10 @@ class BundleFileStorageService: BundleStorageService {
1755
1769
  )
1756
1770
  }
1757
1771
 
1758
- let sourcePath = (currentBundleDir as NSString).appendingPathComponent(assetPath)
1772
+ let sourcePath = try ArchiveExtractionUtilities.extractionURL(
1773
+ for: assetPath,
1774
+ destinationRoot: currentBundleDir
1775
+ ).path
1759
1776
  guard self.fileSystem.fileExists(atPath: sourcePath),
1760
1777
  HashUtils.verifyHash(fileURL: URL(fileURLWithPath: sourcePath), expectedHash: expectedHash)
1761
1778
  else {
@@ -1,14 +1,19 @@
1
1
  "use strict";
2
2
 
3
+ var _nodeChild_process = require("node:child_process");
3
4
  var _promises = require("node:fs/promises");
4
5
  var _nodePath = require("node:path");
5
6
  var _vitest = require("vitest");
6
7
  var _DefaultResolver = require("./DefaultResolver.js");
7
8
  var _sdkVersion = require("./sdkVersion.js");
8
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
9
- const mocks = _vitest.vi.hoisted(() => ({
10
- fetchUpdateInfo: _vitest.vi.fn()
11
- }));
9
+ const mocks = _vitest.vi.hoisted(() => {
10
+ globalThis.HotUpdater = {
11
+ SDK_VERSION: "test-sdk-version"
12
+ };
13
+ return {
14
+ fetchUpdateInfo: _vitest.vi.fn()
15
+ };
16
+ });
12
17
  _vitest.vi.mock("./fetchUpdateInfo", () => ({
13
18
  fetchUpdateInfo: mocks.fetchUpdateInfo
14
19
  }));
@@ -71,12 +76,15 @@ const createParams = params => ({
71
76
  url: "http://localhost:3007/hot-updater/fingerprint/ios/fingerprint-hash/beta/min-bundle/current-bundle/qa"
72
77
  });
73
78
  });
74
- (0, _vitest.it)("keeps the SDK version header aligned with package.json", async () => {
75
- const [{
76
- HOT_UPDATER_SDK_VERSION
77
- }, packageJson] = await Promise.all([Promise.resolve().then(() => _interopRequireWildcard(require("./sdkVersion"))), (0, _promises.readFile)((0, _nodePath.join)(__dirname, "../package.json"), "utf-8")]);
78
- const pkg = JSON.parse(packageJson);
79
- (0, _vitest.expect)(HOT_UPDATER_SDK_VERSION).toBe(pkg.version);
79
+ (0, _vitest.it)("keeps SDK version sync from leaving source changes behind", async () => {
80
+ const sdkVersionPath = (0, _nodePath.join)(__dirname, "sdkVersion.ts");
81
+ const before = await (0, _promises.readFile)(sdkVersionPath, "utf-8");
82
+ const result = (0, _nodeChild_process.spawnSync)(process.execPath, [(0, _nodePath.join)(__dirname, "../scripts/sync-sdk-version.mjs")], {
83
+ cwd: (0, _nodePath.join)(__dirname, ".."),
84
+ encoding: "utf-8"
85
+ });
86
+ (0, _vitest.expect)(result.status, result.stderr).toBe(0);
87
+ await (0, _vitest.expect)((0, _promises.readFile)(sdkVersionPath, "utf-8")).resolves.toBe(before);
80
88
  });
81
89
  (0, _vitest.it)("propagates fetchUpdateInfo errors", async () => {
82
90
  mocks.fetchUpdateInfo.mockRejectedValueOnce(new Error("Network failed"));
@@ -1 +1 @@
1
- {"version":3,"names":["_promises","require","_nodePath","_vitest","_DefaultResolver","_sdkVersion","_interopRequireWildcard","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","mocks","vi","hoisted","fetchUpdateInfo","fn","mock","createParams","params","appVersion","bundleId","channel","cohort","fingerprintHash","minBundleId","platform","updateStrategy","describe","beforeEach","mockReset","mockResolvedValue","it","resolver","createDefaultResolver","checkUpdate","expect","toHaveBeenCalledWith","requestHeaders","HOT_UPDATER_SDK_VERSION","requestTimeout","undefined","url","authorization","packageJson","Promise","all","resolve","then","readFile","join","__dirname","pkg","JSON","parse","toBe","version","mockRejectedValueOnce","Error","rejects","toThrow","resolveBaseURL","mockResolvedValueOnce","toHaveBeenCalledTimes","toHaveBeenNthCalledWith"],"sourceRoot":"../../src","sources":["DefaultResolver.spec.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_nodeChild_process","require","_promises","_nodePath","_vitest","_DefaultResolver","_sdkVersion","mocks","vi","hoisted","globalThis","HotUpdater","SDK_VERSION","fetchUpdateInfo","fn","mock","createParams","params","appVersion","bundleId","channel","cohort","fingerprintHash","minBundleId","platform","updateStrategy","describe","beforeEach","mockReset","mockResolvedValue","it","resolver","createDefaultResolver","checkUpdate","expect","toHaveBeenCalledWith","requestHeaders","HOT_UPDATER_SDK_VERSION","requestTimeout","undefined","url","authorization","sdkVersionPath","join","__dirname","before","readFile","result","spawnSync","process","execPath","cwd","encoding","status","stderr","toBe","resolves","mockRejectedValueOnce","Error","rejects","toThrow","resolveBaseURL","mockResolvedValueOnce","toHaveBeenCalledTimes","toHaveBeenNthCalledWith"],"sourceRoot":"../../src","sources":["DefaultResolver.spec.ts"],"mappings":"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","ignoreList":[]}