@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,345 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import UIKit
4
+
5
+ /**
6
+ Capacitor bridge for the Integrity plugin (iOS).
7
+
8
+ Responsibilities:
9
+ - Parse JavaScript input
10
+ - Invoke the native implementation
11
+ - Resolve or reject CAPPluginCall exactly once
12
+ - Map native IntegrityError to JS-facing error codes
13
+
14
+ Forbidden:
15
+ - Platform-specific business logic
16
+ - System API usage
17
+ - Throwing uncaught exceptions
18
+ */
19
+ @objc(IntegrityPlugin)
20
+ public final class IntegrityPlugin: CAPPlugin, CAPBridgedPlugin {
21
+
22
+ // MARK: - Plugin metadata
23
+
24
+ /// The unique identifier for the plugin.
25
+ ///
26
+ /// CONTRACT:
27
+ /// - MUST match the plugin registration name used by Capacitor
28
+ /// - MUST remain stable across releases (breaking change otherwise)
29
+ public let identifier = "IntegrityPlugin"
30
+
31
+ /// The name used to reference this plugin in JavaScript.
32
+ ///
33
+ /// CONTRACT:
34
+ /// - MUST match `registerPlugin({ name })` on the JS side
35
+ /// - Changing this value breaks JS ↔ native binding
36
+ public let jsName = "Integrity"
37
+
38
+ /**
39
+ A list of methods exposed by this plugin.
40
+ - `check`:
41
+ - `presentBlockPage`:
42
+ - `getPluginVersion`: A method that returns the version of the plugin.
43
+
44
+ CONTRACT:
45
+ - Every method listed here MUST:
46
+ - be implemented exactly once
47
+ - resolve or reject its CAPPluginCall exactly once
48
+ - return a Promise on the JS side
49
+
50
+ NOTE:
51
+ - No method in this list may perform platform logic directly.
52
+ - All business logic MUST be delegated to the Impl layer.
53
+ */
54
+ public let pluginMethods: [CAPPluginMethod] = [
55
+ CAPPluginMethod(name: "check", returnType: CAPPluginReturnPromise),
56
+ CAPPluginMethod(name: "presentBlockPage", returnType: CAPPluginReturnPromise),
57
+ CAPPluginMethod(name: "getPluginVersion", returnType: CAPPluginReturnPromise)
58
+ ]
59
+
60
+ // MARK: - Properties
61
+
62
+ /// Native implementation instance.
63
+ private let implementation = IntegrityImpl()
64
+
65
+ // Configuration instance
66
+ private var config: IntegrityConfig?
67
+
68
+ // MARK: - Event-related properties
69
+
70
+ /// Buffer for integrity signals captured before a JS listener is registered.
71
+ private var bufferedSignals: [[String: Any]] = []
72
+
73
+ /// Canonical event name emitted to the JavaScript layer.
74
+ private static let integritySignalEvent = "integritySignal"
75
+
76
+ // MARK: - Lifecycle
77
+
78
+ /**
79
+ Plugin lifecycle entry point.
80
+
81
+ CONTRACT:
82
+ - Called exactly once by Capacitor
83
+ - This is the ONLY valid place to:
84
+ - read plugin configuration
85
+ - create IntegrityConfig
86
+ - inject configuration into the Impl layer
87
+ - register system event observers (NotificationCenter)
88
+
89
+ WARNING:
90
+ - Calling applyConfig(_:) outside this method is FORBIDDEN
91
+ - Observers registered here MUST be detached in `deinit`
92
+ */
93
+ override public func load() {
94
+ // Initialize IntegrityConfig
95
+ let cfg = IntegrityConfig(plugin: self)
96
+ self.config = cfg
97
+ implementation.applyConfig(cfg)
98
+ IntegrityLogger.debug("Integrity plugin loaded")
99
+
100
+ // Register passive system observers
101
+ addEventObservers()
102
+ }
103
+
104
+ /**
105
+ Overridden to catch the moment JavaScript starts listening.
106
+ Ensures boot-time signals are delivered immediately.
107
+ */
108
+ override public func addEventListener(_ eventName: String, listener: CAPPluginCall) {
109
+ // Fix: Added explicit 'listener:' label required by the CAPPlugin base class
110
+ super.addEventListener(eventName, listener: listener)
111
+
112
+ // Ensure we match the canonical event name used in the plugin
113
+ if eventName == "onIntegritySignal" {
114
+ // Trigger a flush of early boot signals
115
+ let options = IntegrityCheckOptions(level: "standard", includeDebugInfo: false)
116
+ do {
117
+ // This will internally call mergeBootSignals to clear the volatile queue
118
+ let report = try implementation.performCheck(options: options)
119
+ if let signals = report["signals"] as? [[String: Any]], !signals.isEmpty {
120
+ emitOrBufferSignal(report)
121
+ }
122
+ } catch {
123
+ IntegrityLogger.error("Failed to flush boot signals on listener registration")
124
+ }
125
+ }
126
+ }
127
+
128
+ /// Plugin teardown.
129
+ ///
130
+ /// NOTE:
131
+ /// - Invoked when the plugin instance is deallocated
132
+ /// - Responsible for detaching all NotificationCenter observers
133
+ deinit {
134
+ removeEventObservers()
135
+ }
136
+
137
+ // MARK: - Event emission and buffering
138
+
139
+ /**
140
+ Emits an integrity signal to JavaScript or buffers it if no listeners exist.
141
+ */
142
+ private func emitOrBufferSignal(_ signal: [String: Any]) {
143
+ if hasListeners(IntegrityPlugin.integritySignalEvent) {
144
+ notifyListeners(IntegrityPlugin.integritySignalEvent, data: signal, retainUntilConsumed: true)
145
+ } else {
146
+ bufferedSignals.append(signal)
147
+ }
148
+ }
149
+
150
+ /**
151
+ Flushes all buffered integrity signals to JavaScript listeners.
152
+ */
153
+ @objc private func flushBufferedSignals() {
154
+ if hasListeners(IntegrityPlugin.integritySignalEvent) && !bufferedSignals.isEmpty {
155
+ for signal in bufferedSignals {
156
+ notifyListeners(IntegrityPlugin.integritySignalEvent, data: signal, retainUntilConsumed: true)
157
+ }
158
+ bufferedSignals.removeAll()
159
+ }
160
+ }
161
+
162
+ // MARK: - NotificationCenter registration
163
+
164
+ /**
165
+ Registers passive system observers required for real-time integrity signals.
166
+ */
167
+ private func addEventObservers() {
168
+ NotificationCenter.default.addObserver(
169
+ self,
170
+ selector: #selector(handleDidBecomeActiveNotification(_:)),
171
+ name: UIApplication.didBecomeActiveNotification,
172
+ object: nil
173
+ )
174
+ NotificationCenter.default.addObserver(
175
+ self,
176
+ selector: #selector(flushBufferedSignals),
177
+ name: UIApplication.willEnterForegroundNotification,
178
+ object: nil
179
+ )
180
+ }
181
+
182
+ /**
183
+ Detaches all NotificationCenter observers.
184
+
185
+ NOTE:
186
+ - This method is invoked exclusively from `deinit`.
187
+ - The SwiftLint rule is suppressed intentionally as the call is lifecycle-safe.
188
+ */
189
+ private func removeEventObservers() {
190
+ // swiftlint:disable notification_center_detachment
191
+ NotificationCenter.default.removeObserver(self)
192
+ // swiftlint:enable notification_center_detachment
193
+ }
194
+
195
+ // MARK: - Error mapping
196
+
197
+ /**
198
+ Maps native `IntegrityError` values to JS-facing error codes.
199
+
200
+ CONTRACT:
201
+ - Error codes MUST be stable and documented
202
+ - Error codes MUST match across platforms
203
+ - Platform-specific error codes are FORBIDDEN
204
+ */
205
+ private func reject(
206
+ _ call: CAPPluginCall,
207
+ error: IntegrityError
208
+ ) {
209
+ let code: String
210
+
211
+ switch error {
212
+ case .unavailable:
213
+ code = "UNAVAILABLE"
214
+ case .permissionDenied:
215
+ code = "PERMISSION_DENIED"
216
+ case .initFailed:
217
+ code = "INIT_FAILED"
218
+ case .unknownType:
219
+ code = "UNKNOWN_TYPE"
220
+ }
221
+
222
+ call.reject(error.message, code)
223
+ }
224
+
225
+ // MARK: - Integrity check
226
+
227
+ /**
228
+ Executes a baseline integrity check.
229
+ */
230
+ @objc func check(_ call: CAPPluginCall) {
231
+ do {
232
+ let options =
233
+ try call.decode(IntegrityCheckOptions.self)
234
+
235
+ let normalizedOptions = IntegrityCheckOptions(
236
+ level: options.level ?? "basic",
237
+ includeDebugInfo: options.includeDebugInfo ?? false
238
+ )
239
+
240
+ let result =
241
+ try implementation.performCheck(
242
+ options: normalizedOptions
243
+ )
244
+
245
+ call.resolve(result)
246
+
247
+ } catch let error as IntegrityError {
248
+ reject(call, error: error)
249
+ } catch {
250
+ call.reject(
251
+ "Unexpected native error during integrity check.",
252
+ "INIT_FAILED"
253
+ )
254
+ }
255
+ }
256
+
257
+ // MARK: - Present block page
258
+
259
+ /**
260
+ Presents the configured integrity block page, if enabled.
261
+ */
262
+ @objc func presentBlockPage(_ call: CAPPluginCall) {
263
+ guard
264
+ let blockPage = config?.blockPage,
265
+ blockPage.enabled,
266
+ let baseURL = blockPage.url
267
+ else {
268
+ call.resolve(["presented": false])
269
+ return
270
+ }
271
+
272
+ let reason = call.getString("reason")
273
+ let dismissible = call.getBool("dismissible") ?? false
274
+
275
+ let finalURL =
276
+ reason != nil
277
+ ? "\(baseURL)?reason=\(reason!)"
278
+ : baseURL
279
+
280
+ DispatchQueue.main.async {
281
+ // CONTRACT:
282
+ // All UIKit interactions MUST occur on the main thread.
283
+ guard let rootVC = self.bridge?.viewController else {
284
+ call.reject(
285
+ "View controller not available",
286
+ "UNAVAILABLE"
287
+ )
288
+ return
289
+ }
290
+
291
+ let blockVC = IntegrityBlockViewController(
292
+ url: finalURL,
293
+ dismissible: dismissible
294
+ )
295
+ let nav = UINavigationController(rootViewController: blockVC)
296
+ rootVC.present(nav, animated: true) {
297
+ call.resolve(["presented": true])
298
+ }
299
+ }
300
+ }
301
+
302
+ // MARK: - Version
303
+
304
+ /**
305
+ Returns the native plugin version.
306
+
307
+ - Used exclusively for diagnostics and compatibility checks.
308
+ - Must not be used for feature detection.
309
+ */
310
+ @objc func getPluginVersion(_ call: CAPPluginCall) {
311
+ // Standardized enum name across all CapKit plugins
312
+ call.resolve([
313
+ "version": PluginVersion.number
314
+ ])
315
+ }
316
+
317
+ // MARK: - Notification handlers
318
+
319
+ /**
320
+ Handles application foreground transitions.
321
+ */
322
+ @objc private func handleDidBecomeActiveNotification(_ notification: Notification) {
323
+ // Targeted options for real-time monitoring.
324
+ // Using explicit labels to satisfy Swift compiler and struct definition.
325
+ let options = IntegrityCheckOptions(
326
+ level: "standard",
327
+ includeDebugInfo: false
328
+ )
329
+
330
+ // Perform check and notify JS immediately if signals are found
331
+ do {
332
+ let report = try implementation.performCheck(options: options)
333
+
334
+ // Optimization: Only emit if the report contains signals (compromised or score > 0)
335
+ if let signals = report["signals"] as? [[String: Any]], !signals.isEmpty {
336
+ emitOrBufferSignal(report)
337
+
338
+ // Logging for internal audit
339
+ IntegrityLogger.debug("Real-time signal detected and emitted to JS")
340
+ }
341
+ } catch {
342
+ IntegrityLogger.error("Real-time monitor failed during app activation:", error.localizedDescription)
343
+ }
344
+ }
345
+ }
@@ -0,0 +1,184 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Helper responsible for assembling the final integrity report payload.
5
+
6
+ Responsibilities:
7
+ - Aggregate signals
8
+ - Compute integrity score
9
+ - Build environment metadata
10
+ - Produce a JS-bridge-safe dictionary
11
+
12
+ This type contains NO platform logic.
13
+ */
14
+ struct IntegrityReportBuilder {
15
+
16
+ /**
17
+ Builds the final integrity report returned to the JavaScript layer.
18
+
19
+ This method is responsible for:
20
+ - aggregating all collected integrity signals
21
+ - computing a numeric integrity score
22
+ - deriving a boolean compromise state
23
+ - attaching immutable environment metadata
24
+ - producing a JSON-bridge-safe payload
25
+
26
+ IMPORTANT:
27
+ - This builder MUST remain platform-agnostic.
28
+ - No platform-specific heuristics or weighting logic
29
+ may be introduced here.
30
+ */
31
+ static func buildReport(
32
+ signals: [[String: Any]],
33
+ isEmulator: Bool,
34
+ platform: String
35
+ ) -> [String: Any] {
36
+
37
+ // Compute the aggregate integrity score based on signal confidence.
38
+ let score = computeScore(from: signals)
39
+
40
+ // Build informational explanation metadata for the score.
41
+ let scoreExplanation = buildScoreExplanation(from: signals)
42
+
43
+ return [
44
+ // Ordered list of all detected integrity signals.
45
+ "signals": signals,
46
+
47
+ // Numeric integrity score derived from signal confidence.
48
+ "score": score,
49
+
50
+ // Convenience flag indicating whether the device
51
+ // should be considered compromised.
52
+ "compromised": score >= 30,
53
+
54
+ // Static environment metadata describing the runtime context.
55
+ "environment": buildEnvironment(
56
+ platform: platform,
57
+ isEmulator: isEmulator
58
+ ),
59
+
60
+ // Informational explanation describing how the score was derived.
61
+ // This metadata MUST NOT be treated as a security decision.
62
+ "scoreExplanation": scoreExplanation,
63
+
64
+ // Millisecond-precision UNIX timestamp of report generation.
65
+ "timestamp": currentTimestamp()
66
+ ]
67
+ }
68
+
69
+ // MARK: - Scoring
70
+
71
+ /**
72
+ Computes a numeric integrity score from a list of signals.
73
+
74
+ Scoring policy:
75
+ - high → +30 points
76
+ - medium → +15 points
77
+ - low → +5 points
78
+
79
+ NOTES:
80
+ - Signals without a valid `confidence` field are ignored.
81
+ - This scoring model is intentionally simple and heuristic-based.
82
+ - Platform-specific adjustments MUST NOT be implemented here.
83
+ */
84
+ private static func computeScore(
85
+ from signals: [[String: Any]]
86
+ ) -> Int {
87
+ signals.reduce(0) { acc, signal in
88
+ guard let confidence = signal["confidence"] as? String else {
89
+ return acc
90
+ }
91
+
92
+ switch confidence {
93
+ case "high": return acc + 30
94
+ case "medium": return acc + 15
95
+ case "low": return acc + 5
96
+ default: return acc
97
+ }
98
+ }
99
+ }
100
+
101
+ // MARK: - Score Explanation
102
+
103
+ /**
104
+ Builds an informational explanation describing how the integrity
105
+ score was derived from the detected signals.
106
+
107
+ IMPORTANT:
108
+ - This metadata is informational only.
109
+ - It MUST NOT influence scoring or enforcement decisions.
110
+ */
111
+ private static func buildScoreExplanation(
112
+ from signals: [[String: Any]]
113
+ ) -> [String: Any] {
114
+
115
+ var high = 0
116
+ var medium = 0
117
+ var low = 0
118
+
119
+ let contributors = signals.compactMap {
120
+ $0["id"] as? String
121
+ }
122
+
123
+ for signal in signals {
124
+ switch signal["confidence"] as? String {
125
+ case "high":
126
+ high += 1
127
+ case "medium":
128
+ medium += 1
129
+ case "low":
130
+ low += 1
131
+ default:
132
+ break
133
+ }
134
+ }
135
+
136
+ return [
137
+ "totalSignals": signals.count,
138
+ "byConfidence": [
139
+ "high": high,
140
+ "medium": medium,
141
+ "low": low
142
+ ],
143
+ "contributors": contributors
144
+ ]
145
+ }
146
+
147
+ // MARK: - Environment
148
+
149
+ /**
150
+ Builds a static environment descriptor attached to every report.
151
+
152
+ This metadata is informational only and MUST NOT be used
153
+ to infer compromise on its own.
154
+ */
155
+ private static func buildEnvironment(
156
+ platform: String,
157
+ isEmulator: Bool
158
+ ) -> [String: Any] {
159
+ [
160
+ // Platform identifier (e.g. "ios", "android").
161
+ "platform": platform,
162
+
163
+ // Whether the app is running inside an emulator or simulator.
164
+ "isEmulator": isEmulator,
165
+
166
+ // Debug build flag reserved for future use.
167
+ // Currently always false on iOS.
168
+ "isDebugBuild": false
169
+ ]
170
+ }
171
+
172
+ // MARK: - Timestamp
173
+
174
+ /**
175
+ Returns the current UNIX timestamp in milliseconds.
176
+
177
+ This timestamp represents the moment the integrity
178
+ report was assembled, not when individual signals
179
+ were detected.
180
+ */
181
+ private static func currentTimestamp() -> Int {
182
+ Int(Date().timeIntervalSince1970 * 1000)
183
+ }
184
+ }
@@ -0,0 +1,69 @@
1
+ import Capacitor
2
+
3
+ /**
4
+ Centralized logger for the Integrity plugin.
5
+
6
+ Responsibilities:
7
+ - Provide a single logging entry point
8
+ - Support runtime-controlled verbose logging
9
+ - Keep logging behavior consistent across files
10
+
11
+ Forbidden:
12
+ - Controlling application logic
13
+ - Being queried for flow decisions
14
+ */
15
+ enum IntegrityLogger {
16
+
17
+ /**
18
+ Controls whether debug logs are printed.
19
+
20
+ This value MUST be set once during plugin initialization
21
+ based on static configuration.
22
+ */
23
+ static var verbose: Bool = false
24
+
25
+ /**
26
+ Prints a verbose / debug log message.
27
+
28
+ Debug logs are automatically silenced
29
+ when `verbose` is false.
30
+ */
31
+ static func debug(_ items: Any...) {
32
+ guard verbose else { return }
33
+ log(items)
34
+ }
35
+
36
+ /**
37
+ Prints an error log message.
38
+
39
+ Error logs are always printed regardless
40
+ of the verbose flag.
41
+ */
42
+ static func error(_ items: Any...) {
43
+ log(items)
44
+ }
45
+ }
46
+
47
+ // MARK: - Internal log printer
48
+
49
+ /**
50
+ Low-level log printer with a consistent prefix.
51
+
52
+ This function MUST NOT be used outside this file.
53
+ */
54
+ private func log(
55
+ _ items: [Any],
56
+ separator: String = " ",
57
+ terminator: String = "\n"
58
+ ) {
59
+ CAPLog.print("⚡️ Integrity -", terminator: separator)
60
+
61
+ for (index, item) in items.enumerated() {
62
+ CAPLog.print(
63
+ item,
64
+ terminator: index == items.count - 1
65
+ ? terminator
66
+ : separator
67
+ )
68
+ }
69
+ }