@cap-kit/integrity 8.0.0-next.6

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 (59) hide show
  1. package/CapKitIntegrity.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +26 -0
  4. package/README.md +1104 -0
  5. package/android/build.gradle +104 -0
  6. package/android/src/main/AndroidManifest.xml +21 -0
  7. package/android/src/main/java/io/capkit/integrity/IntegrityCheckOptions.kt +37 -0
  8. package/android/src/main/java/io/capkit/integrity/IntegrityConfig.kt +59 -0
  9. package/android/src/main/java/io/capkit/integrity/IntegrityError.kt +40 -0
  10. package/android/src/main/java/io/capkit/integrity/IntegrityImpl.kt +319 -0
  11. package/android/src/main/java/io/capkit/integrity/IntegrityPlugin.kt +475 -0
  12. package/android/src/main/java/io/capkit/integrity/IntegrityReportBuilder.kt +130 -0
  13. package/android/src/main/java/io/capkit/integrity/IntegritySignalBuilder.kt +72 -0
  14. package/android/src/main/java/io/capkit/integrity/emulator/IntegrityEmulatorChecks.kt +38 -0
  15. package/android/src/main/java/io/capkit/integrity/filesystem/IntegrityFilesystemChecks.kt +51 -0
  16. package/android/src/main/java/io/capkit/integrity/hook/IntegrityHookChecks.kt +61 -0
  17. package/android/src/main/java/io/capkit/integrity/remote/IntegrityRemoteAttestor.kt +49 -0
  18. package/android/src/main/java/io/capkit/integrity/root/IntegrityRootDetector.kt +136 -0
  19. package/android/src/main/java/io/capkit/integrity/runtime/IntegrityRuntimeChecks.kt +87 -0
  20. package/android/src/main/java/io/capkit/integrity/ui/IntegrityBlockActivity.kt +173 -0
  21. package/android/src/main/java/io/capkit/integrity/ui/IntegrityUISignals.kt +57 -0
  22. package/android/src/main/java/io/capkit/integrity/utils/IntegrityLogger.kt +85 -0
  23. package/android/src/main/java/io/capkit/integrity/utils/IntegrityUtils.kt +105 -0
  24. package/android/src/main/res/.gitkeep +0 -0
  25. package/android/src/main/res/values/styles.xml +5 -0
  26. package/dist/docs.json +598 -0
  27. package/dist/esm/definitions.d.ts +554 -0
  28. package/dist/esm/definitions.js +56 -0
  29. package/dist/esm/definitions.js.map +1 -0
  30. package/dist/esm/index.d.ts +15 -0
  31. package/dist/esm/index.js +16 -0
  32. package/dist/esm/index.js.map +1 -0
  33. package/dist/esm/web.d.ts +32 -0
  34. package/dist/esm/web.js +51 -0
  35. package/dist/esm/web.js.map +1 -0
  36. package/dist/plugin.cjs.js +130 -0
  37. package/dist/plugin.cjs.js.map +1 -0
  38. package/dist/plugin.js +133 -0
  39. package/dist/plugin.js.map +1 -0
  40. package/ios/Sources/IntegrityPlugin/IntegrityCheckOptions.swift +41 -0
  41. package/ios/Sources/IntegrityPlugin/IntegrityConfig.swift +135 -0
  42. package/ios/Sources/IntegrityPlugin/IntegrityEntitlementChecks.swift +58 -0
  43. package/ios/Sources/IntegrityPlugin/IntegrityError.swift +49 -0
  44. package/ios/Sources/IntegrityPlugin/IntegrityImpl.swift +397 -0
  45. package/ios/Sources/IntegrityPlugin/IntegrityPlugin.swift +345 -0
  46. package/ios/Sources/IntegrityPlugin/IntegrityReportBuilder.swift +184 -0
  47. package/ios/Sources/IntegrityPlugin/Utils/IntegrityLogger.swift +69 -0
  48. package/ios/Sources/IntegrityPlugin/Utils/IntegrityUtils.swift +144 -0
  49. package/ios/Sources/IntegrityPlugin/Version.swift +16 -0
  50. package/ios/Sources/IntegrityPlugin/filesystem/IntegrityFilesystemChecks.swift +86 -0
  51. package/ios/Sources/IntegrityPlugin/hook/IntegrityHookChecks.swift +85 -0
  52. package/ios/Sources/IntegrityPlugin/jailbreak/IntegrityJailbreakDetector.swift +74 -0
  53. package/ios/Sources/IntegrityPlugin/jailbreak/IntegrityJailbreakUrlSchemeDetector.swift +42 -0
  54. package/ios/Sources/IntegrityPlugin/remote/IntegrityRemoteAttestor.swift +40 -0
  55. package/ios/Sources/IntegrityPlugin/runtime/IntegrityRuntimeChecks.swift +63 -0
  56. package/ios/Sources/IntegrityPlugin/simulator/IntegritySimulatorChecks.swift +20 -0
  57. package/ios/Sources/IntegrityPlugin/ui/IntegrityBlockViewController.swift +143 -0
  58. package/ios/Tests/IntegrityPluginTests/IntegrityPluginTests.swift +10 -0
  59. package/package.json +106 -0
@@ -0,0 +1,397 @@
1
+ import Foundation
2
+ import MachO
3
+
4
+ /**
5
+ Native iOS implementation for the Integrity plugin.
6
+ */
7
+ @objc
8
+ public final class IntegrityImpl: NSObject {
9
+
10
+ // MARK: - Configuration
11
+
12
+ /// Immutable plugin configuration injected by the Plugin layer.
13
+ private var config: IntegrityConfig?
14
+
15
+ /// Buffer for integrity signals captured during early app boot.
16
+ /// Flushed on the first explicit integrity check.
17
+ private static var bootSignals: [[String: Any]] = []
18
+
19
+ // Negative cache for expensive integrity checks.
20
+ // Caches only "no-signal" results for a short time window.
21
+ private struct NegativeCacheEntry {
22
+ let timestampMs: Int
23
+ }
24
+
25
+ private var negativeCache: [String: NegativeCacheEntry] = [:]
26
+
27
+ private let negativeCacheTTLms = 30_000
28
+
29
+ // MARK: - Early Boot Hooks
30
+
31
+ /// Entry point invoked from AppDelegate during application launch.
32
+ @objc public static func onAppLaunch() {
33
+ // Capture jailbreak-related filesystem signals immediately at boot.
34
+ let signals = IntegrityJailbreakDetector.detect(includeDebug: false)
35
+ self.bootSignals.append(contentsOf: signals)
36
+ }
37
+
38
+ // MARK: - Configuration Injection
39
+
40
+ /// Applies static plugin configuration.
41
+ func applyConfig(_ config: IntegrityConfig) {
42
+ precondition(
43
+ self.config == nil,
44
+ "IntegrityImpl.applyConfig(_:) must be called exactly once"
45
+ )
46
+
47
+ self.config = config
48
+ IntegrityLogger.verbose = config.verboseLogging
49
+
50
+ IntegrityLogger.debug(
51
+ "Integrity configuration applied. Verbose logging:",
52
+ config.verboseLogging
53
+ )
54
+ }
55
+
56
+ // MARK: - Remote Attestation (Stub)
57
+
58
+ /// Placeholder for future Apple App Attest integration.
59
+ func getAppAttestSignal(options: IntegrityCheckOptions) -> [String: Any]? {
60
+ return IntegrityRemoteAttestor.getAppAttestSignal(options: options)
61
+ }
62
+
63
+ // MARK: - Integrity Check Orchestration
64
+
65
+ /**
66
+ Executes integrity checks according to the requested options
67
+ and returns a fully assembled integrity report.
68
+ */
69
+ func performCheck(
70
+ options: IntegrityCheckOptions
71
+ ) throws -> [String: Any] {
72
+
73
+ // Apply negative cache only for standard / strict levels.
74
+ // Cached results represent a recent "no-signal" execution.
75
+ if let level = options.level,
76
+ level != "basic",
77
+ isNegativeCacheValid(level: level) {
78
+
79
+ IntegrityLogger.debug(
80
+ "Negative cache hit for integrity check:",
81
+ level
82
+ )
83
+
84
+ return IntegrityReportBuilder.buildReport(
85
+ signals: [],
86
+ isEmulator: false,
87
+ platform: "ios"
88
+ )
89
+ }
90
+
91
+ var signals: [[String: Any]] = []
92
+ let includeDebug = options.includeDebugInfo ?? false
93
+
94
+ mergeBootSignals(into: &signals)
95
+
96
+ let isSimulator = runChecks(
97
+ options: options,
98
+ signals: &signals,
99
+ includeDebug: includeDebug
100
+ )
101
+
102
+ appendCorrelations(
103
+ signals: &signals,
104
+ includeDebug: includeDebug
105
+ )
106
+
107
+ // Update negative cache only when no integrity signals are detected.
108
+ // Any detected signal invalidates the cached clean state.
109
+ if let level = options.level, level != "basic" {
110
+ if signals.isEmpty {
111
+ updateNegativeCache(level: level)
112
+ } else {
113
+ clearNegativeCache(level: level)
114
+ }
115
+ }
116
+
117
+ return IntegrityReportBuilder.buildReport(
118
+ signals: signals,
119
+ isEmulator: isSimulator,
120
+ platform: "ios"
121
+ )
122
+ }
123
+
124
+ // MARK: - BASIC Checks
125
+
126
+ /// Executes BASIC integrity checks.
127
+ private func performBasicChecks(
128
+ signals: inout [[String: Any]],
129
+ includeDebug: Bool
130
+ ) -> Bool {
131
+
132
+ appendFilesystemJailbreakSignals(
133
+ to: &signals,
134
+ includeDebug: includeDebug
135
+ )
136
+
137
+ let isSimulator = appendSimulatorSignalIfNeeded(
138
+ to: &signals,
139
+ includeDebug: includeDebug
140
+ )
141
+
142
+ appendUrlSchemeJailbreakSignalIfNeeded(
143
+ to: &signals,
144
+ includeDebug: includeDebug
145
+ )
146
+
147
+ return isSimulator
148
+ }
149
+
150
+ // MARK: - STANDARD / STRICT Checks
151
+
152
+ /**
153
+ Executes STANDARD and STRICT integrity checks,
154
+ including runtime and instrumentation heuristics.
155
+ */
156
+ private func performStandardChecks(
157
+ signals: inout [[String: Any]],
158
+ includeDebug: Bool
159
+ ) {
160
+
161
+ signals.append(
162
+ contentsOf: IntegrityRuntimeChecks.debugSignals(
163
+ includeDebug: includeDebug
164
+ )
165
+ )
166
+
167
+ let hookingDetected =
168
+ IntegrityHookChecks.hookingSignal(
169
+ includeDebug: includeDebug
170
+ )
171
+
172
+ if let hookingDetected {
173
+ signals.append(hookingDetected)
174
+ }
175
+
176
+ let portDetected =
177
+ IntegrityHookChecks.isFridaPortOpen()
178
+
179
+ if portDetected {
180
+ signals.append(
181
+ IntegrityUtils.buildSignal(
182
+ id: "ios_frida_port_detected",
183
+ category: "hook",
184
+ confidence: "medium",
185
+ description: "Known instrumentation service port is reachable on localhost",
186
+ metadata: ["port": 27042],
187
+ includeDebug: includeDebug
188
+ )
189
+ )
190
+ }
191
+
192
+ if hookingDetected != nil && portDetected {
193
+ signals.append(
194
+ IntegrityUtils.buildSignal(
195
+ id: "ios_frida_correlation_confirmed",
196
+ category: "hook",
197
+ confidence: "high",
198
+ description: "Multiple instrumentation indicators detected simultaneously",
199
+ metadata: ["source": "library+port"],
200
+ includeDebug: includeDebug
201
+ )
202
+ )
203
+ }
204
+ }
205
+
206
+ // MARK: - Orchestration Helpers
207
+
208
+ /// Merges early boot signals into the current execution context.
209
+ private func mergeBootSignals(
210
+ into signals: inout [[String: Any]]
211
+ ) {
212
+ signals.append(contentsOf: IntegrityImpl.bootSignals)
213
+ IntegrityImpl.bootSignals.removeAll()
214
+ }
215
+
216
+ /// Runs integrity checks according to the selected strictness level.
217
+ private func runChecks(
218
+ options: IntegrityCheckOptions,
219
+ signals: inout [[String: Any]],
220
+ includeDebug: Bool
221
+ ) -> Bool {
222
+
223
+ let isSimulator = performBasicChecks(
224
+ signals: &signals,
225
+ includeDebug: includeDebug
226
+ )
227
+
228
+ if (options.level ?? "basic") != "basic" {
229
+ performStandardChecks(
230
+ signals: &signals,
231
+ includeDebug: includeDebug
232
+ )
233
+
234
+ // Entitlement & Provisioning Verification (RASP)
235
+ // Added check to verify if the production app allows debugging via entitlements
236
+ if let entData = IntegrityEntitlementChecks.checkEntitlements() {
237
+ if let isDebuggable = entData["debuggable"] as? Bool, isDebuggable, options.level == "strict" {
238
+ signals.append(
239
+ IntegrityUtils.buildSignal(
240
+ id: "ios_entitlement_debuggable",
241
+ category: "tamper",
242
+ confidence: "high",
243
+ description: "Production app has 'get-task-allow' enabled in provisioning profile",
244
+ metadata: entData,
245
+ includeDebug: includeDebug
246
+ )
247
+ )
248
+ }
249
+
250
+ if let hasKeychain = entData["has_keychain_access"] as? Bool, !hasKeychain, options.level == "strict" {
251
+ signals.append(
252
+ IntegrityUtils.buildSignal(
253
+ id: "ios_keychain_entitlement_missing",
254
+ category: "tamper",
255
+ confidence: "medium",
256
+ description: "Expected keychain-access-groups are missing from provisioning profile",
257
+ metadata: entData,
258
+ includeDebug: includeDebug
259
+ )
260
+ )
261
+ }
262
+ }
263
+ }
264
+
265
+ if options.level == "strict",
266
+ let attest = getAppAttestSignal(options: options) {
267
+ signals.append(attest)
268
+ }
269
+
270
+ return isSimulator
271
+ }
272
+
273
+ /// Appends derived correlation signals based on collected indicators.
274
+ private func appendCorrelations(
275
+ signals: inout [[String: Any]],
276
+ includeDebug: Bool
277
+ ) {
278
+ if let jailbreakCorrelation =
279
+ IntegrityCorrelationUtils.jailbreakCorrelation(
280
+ from: signals,
281
+ includeDebug: includeDebug
282
+ ) {
283
+ signals.append(jailbreakCorrelation)
284
+ }
285
+
286
+ if let jailbreakAndHookCorrelation =
287
+ IntegrityCorrelationUtils.jailbreakAndHookCorrelation(
288
+ from: signals,
289
+ includeDebug: includeDebug
290
+ ) {
291
+ signals.append(jailbreakAndHookCorrelation)
292
+ }
293
+ }
294
+
295
+ // MARK: - BASIC Check Helpers
296
+
297
+ /// Appends filesystem-based jailbreak indicators.
298
+ private func appendFilesystemJailbreakSignals(
299
+ to signals: inout [[String: Any]],
300
+ includeDebug: Bool
301
+ ) {
302
+ signals.append(
303
+ contentsOf: IntegrityJailbreakDetector.detect(
304
+ includeDebug: includeDebug
305
+ )
306
+ )
307
+
308
+ if IntegrityFilesystemChecks.canEscapeSandbox() {
309
+ signals.append(
310
+ IntegrityUtils.buildSignal(
311
+ id: "ios_sandbox_escaped",
312
+ category: "tamper",
313
+ confidence: "high",
314
+ description: "Successfully wrote to a protected system directory (Sandbox violation)",
315
+ metadata: ["path": "/private/integrity_test.txt"],
316
+ includeDebug: includeDebug
317
+ )
318
+ )
319
+ }
320
+
321
+ if IntegrityFilesystemChecks.hasSuspiciousSymlinks() {
322
+ signals.append(
323
+ IntegrityUtils.buildSignal(
324
+ id: "ios_suspicious_symlink",
325
+ category: "jailbreak",
326
+ confidence: "high",
327
+ description: "System directories are redirected via symbolic links",
328
+ includeDebug: includeDebug
329
+ )
330
+ )
331
+ }
332
+ }
333
+
334
+ /// Appends simulator signal if the app is running in a simulator.
335
+ private func appendSimulatorSignalIfNeeded(
336
+ to signals: inout [[String: Any]],
337
+ includeDebug: Bool
338
+ ) -> Bool {
339
+
340
+ let isSimulator = IntegritySimulatorChecks.isSimulator()
341
+
342
+ if isSimulator {
343
+ signals.append(
344
+ IntegrityUtils.buildSignal(
345
+ id: "ios_simulator",
346
+ category: "emulator",
347
+ confidence: "high",
348
+ description: "Application is running in an iOS simulator environment",
349
+ metadata: ["type": "apple_simulator"],
350
+ includeDebug: includeDebug
351
+ )
352
+ )
353
+ }
354
+
355
+ return isSimulator
356
+ }
357
+
358
+ /// Appends optional jailbreak URL scheme detection signal.
359
+ private func appendUrlSchemeJailbreakSignalIfNeeded(
360
+ to signals: inout [[String: Any]],
361
+ includeDebug: Bool
362
+ ) {
363
+ guard
364
+ let schemeConfig = config?.jailbreakUrlSchemes,
365
+ schemeConfig.enabled,
366
+ let schemeSignal =
367
+ IntegrityJailbreakUrlSchemeDetector.detect(
368
+ schemes: schemeConfig.schemes,
369
+ includeDebug: includeDebug
370
+ )
371
+ else { return }
372
+
373
+ signals.append(schemeSignal)
374
+ }
375
+
376
+ private func cacheKey(level: String) -> String {
377
+ return "ios:\(level)"
378
+ }
379
+
380
+ private func isNegativeCacheValid(level: String) -> Bool {
381
+ guard let entry = negativeCache[cacheKey(level: level)] else {
382
+ return false
383
+ }
384
+
385
+ let now = Int(Date().timeIntervalSince1970 * 1000)
386
+ return now - entry.timestampMs <= negativeCacheTTLms
387
+ }
388
+
389
+ private func updateNegativeCache(level: String) {
390
+ let now = Int(Date().timeIntervalSince1970 * 1000)
391
+ negativeCache[cacheKey(level: level)] = NegativeCacheEntry(timestampMs: now)
392
+ }
393
+
394
+ private func clearNegativeCache(level: String) {
395
+ negativeCache.removeValue(forKey: cacheKey(level: level))
396
+ }
397
+ }