@hot-updater/react-native 0.29.2 → 0.29.3
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/HotUpdater.podspec +0 -4
- package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +178 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +82 -47
- package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +359 -0
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +386 -0
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +7 -213
- package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +8 -126
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -2
- package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +462 -0
- package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +4 -113
- package/package.json +6 -6
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
-
import SWCompression
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Strategy for handling ZIP compressed files
|
|
@@ -34,7 +33,8 @@ class ZipDecompressionStrategy: DecompressionStrategy {
|
|
|
34
33
|
fileHandle.closeFile()
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
guard let header = try?
|
|
36
|
+
guard let header = try? ArchiveExtractionUtilities.readUpToCount(from: fileHandle, count: 4),
|
|
37
|
+
header.count == 4 else {
|
|
38
38
|
NSLog("[ZipStrategy] Invalid ZIP: cannot read header")
|
|
39
39
|
return false
|
|
40
40
|
}
|
|
@@ -45,121 +45,12 @@ class ZipDecompressionStrategy: DecompressionStrategy {
|
|
|
45
45
|
return false
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
NSLog("[ZipStrategy] Invalid ZIP: cannot read file data")
|
|
50
|
-
return false
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
do {
|
|
54
|
-
_ = try ZipContainer.open(container: zipData)
|
|
55
|
-
return true
|
|
56
|
-
} catch {
|
|
57
|
-
NSLog("[ZipStrategy] Invalid ZIP: structure validation failed - \(error.localizedDescription)")
|
|
58
|
-
return false
|
|
59
|
-
}
|
|
48
|
+
return true
|
|
60
49
|
}
|
|
61
50
|
|
|
62
51
|
func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
|
|
63
52
|
NSLog("[ZipStrategy] Starting extraction of \(file) to \(destination)")
|
|
64
|
-
|
|
65
|
-
guard let zipData = try? Data(contentsOf: URL(fileURLWithPath: file)) else {
|
|
66
|
-
throw NSError(
|
|
67
|
-
domain: "ZipDecompressionStrategy",
|
|
68
|
-
code: 1,
|
|
69
|
-
userInfo: [NSLocalizedDescriptionKey: "Failed to read ZIP file at: \(file)"]
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
progressHandler(0.1)
|
|
74
|
-
|
|
75
|
-
let zipEntries: [ZipEntry]
|
|
76
|
-
do {
|
|
77
|
-
zipEntries = try ZipContainer.open(container: zipData)
|
|
78
|
-
NSLog("[ZipStrategy] ZIP extraction successful, found \(zipEntries.count) entries")
|
|
79
|
-
} catch {
|
|
80
|
-
throw NSError(
|
|
81
|
-
domain: "ZipDecompressionStrategy",
|
|
82
|
-
code: 2,
|
|
83
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP extraction failed: \(error.localizedDescription)"]
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
progressHandler(0.2)
|
|
88
|
-
|
|
89
|
-
let destinationURL = URL(fileURLWithPath: destination)
|
|
90
|
-
let canonicalDestination = destinationURL.standardized.path
|
|
91
|
-
|
|
92
|
-
let fileManager = FileManager.default
|
|
93
|
-
if !fileManager.fileExists(atPath: canonicalDestination) {
|
|
94
|
-
try fileManager.createDirectory(
|
|
95
|
-
atPath: canonicalDestination,
|
|
96
|
-
withIntermediateDirectories: true,
|
|
97
|
-
attributes: nil
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
let totalEntries = Double(zipEntries.count)
|
|
102
|
-
for (index, entry) in zipEntries.enumerated() {
|
|
103
|
-
try extractZipEntry(entry, to: canonicalDestination)
|
|
104
|
-
progressHandler(0.2 + (Double(index + 1) / totalEntries * 0.8))
|
|
105
|
-
}
|
|
106
|
-
|
|
53
|
+
try ZipArchiveExtractor.extract(file: file, to: destination, progressHandler: progressHandler)
|
|
107
54
|
NSLog("[ZipStrategy] Successfully extracted all entries")
|
|
108
55
|
}
|
|
109
|
-
|
|
110
|
-
private func extractZipEntry(_ entry: ZipEntry, to destination: String) throws {
|
|
111
|
-
let fileManager = FileManager.default
|
|
112
|
-
let entryPath = entry.info.name.trimmingCharacters(in: .init(charactersIn: "/"))
|
|
113
|
-
|
|
114
|
-
guard !entryPath.isEmpty,
|
|
115
|
-
!entryPath.contains(".."),
|
|
116
|
-
!entryPath.hasPrefix("/") else {
|
|
117
|
-
NSLog("[ZipStrategy] Skipping suspicious path: \(entry.info.name)")
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let fullPath = (destination as NSString).appendingPathComponent(entryPath)
|
|
122
|
-
let fullURL = URL(fileURLWithPath: fullPath)
|
|
123
|
-
let canonicalFullPath = fullURL.standardized.path
|
|
124
|
-
let canonicalDestination = URL(fileURLWithPath: destination).standardized.path
|
|
125
|
-
|
|
126
|
-
guard canonicalFullPath.hasPrefix(canonicalDestination + "/") ||
|
|
127
|
-
canonicalFullPath == canonicalDestination else {
|
|
128
|
-
throw NSError(
|
|
129
|
-
domain: "ZipDecompressionStrategy",
|
|
130
|
-
code: 3,
|
|
131
|
-
userInfo: [NSLocalizedDescriptionKey: "Path traversal attempt detected: \(entry.info.name)"]
|
|
132
|
-
)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if entry.info.type == .directory {
|
|
136
|
-
if !fileManager.fileExists(atPath: canonicalFullPath) {
|
|
137
|
-
try fileManager.createDirectory(
|
|
138
|
-
atPath: canonicalFullPath,
|
|
139
|
-
withIntermediateDirectories: true,
|
|
140
|
-
attributes: nil
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if entry.info.type == .regular {
|
|
147
|
-
let parentPath = (canonicalFullPath as NSString).deletingLastPathComponent
|
|
148
|
-
if !fileManager.fileExists(atPath: parentPath) {
|
|
149
|
-
try fileManager.createDirectory(
|
|
150
|
-
atPath: parentPath,
|
|
151
|
-
withIntermediateDirectories: true,
|
|
152
|
-
attributes: nil
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
guard let data = entry.data else {
|
|
157
|
-
NSLog("[ZipStrategy] Skipping file with no data: \(entry.info.name)")
|
|
158
|
-
return
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
try data.write(to: URL(fileURLWithPath: canonicalFullPath))
|
|
162
|
-
NSLog("[ZipStrategy] Extracted: \(entryPath)")
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
56
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/react-native",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.3",
|
|
4
4
|
"description": "React Native OTA solution for self-hosted",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -120,14 +120,14 @@
|
|
|
120
120
|
"react-native": "0.79.1",
|
|
121
121
|
"react-native-builder-bob": "^0.40.10",
|
|
122
122
|
"typescript": "^5.8.3",
|
|
123
|
-
"hot-updater": "0.29.
|
|
123
|
+
"hot-updater": "0.29.3"
|
|
124
124
|
},
|
|
125
125
|
"dependencies": {
|
|
126
126
|
"use-sync-external-store": "1.5.0",
|
|
127
|
-
"@hot-updater/cli-tools": "0.29.
|
|
128
|
-
"@hot-updater/core": "0.29.
|
|
129
|
-
"@hot-updater/js": "0.29.
|
|
130
|
-
"@hot-updater/plugin-core": "0.29.
|
|
127
|
+
"@hot-updater/cli-tools": "0.29.3",
|
|
128
|
+
"@hot-updater/core": "0.29.3",
|
|
129
|
+
"@hot-updater/js": "0.29.3",
|
|
130
|
+
"@hot-updater/plugin-core": "0.29.3"
|
|
131
131
|
},
|
|
132
132
|
"scripts": {
|
|
133
133
|
"build": "bob build && tsc -p plugin/tsconfig.build.json",
|