@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,144 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Utility helpers for the Integrity plugin (iOS).
5
+
6
+ PURPOSE:
7
+ - Host pure Swift utility functions that are:
8
+ - platform-agnostic
9
+ - side-effect free
10
+ - independent from Capacitor APIs
11
+
12
+ CURRENT STATE:
13
+ - This type is intentionally empty.
14
+ - iOS does not require explicit JSON serialization helpers
15
+ because Capacitor accepts native `[String: Any]` payloads.
16
+
17
+ FUTURE USE CASES:
18
+ - Cross-platform payload normalization
19
+ - Remote attestation artifact encoding
20
+ - Data redaction or canonicalization
21
+ - Shared, testable transformation logic
22
+
23
+ CONTRACT:
24
+ - Utilities defined here MUST NOT:
25
+ - access Capacitor APIs
26
+ - reference Plugin or Impl layers
27
+ - perform I/O or side effects
28
+ */
29
+ struct IntegrityUtils {
30
+
31
+ /**
32
+ Builds a JSON-compatible integrity signal dictionary.
33
+
34
+ - Parameters:
35
+ - id: Stable signal identifier.
36
+ - category: High-level signal category.
37
+ - confidence: Confidence level ("low", "medium", "high").
38
+ - description: Optional human-readable description.
39
+ - metadata: Optional diagnostic metadata.
40
+ - includeDebug: Whether debug fields should be included.
41
+
42
+ - Returns: A `[String: Any]` dictionary safe to cross the JS bridge.
43
+ */
44
+ static func buildSignal(
45
+ id: String,
46
+ category: String,
47
+ confidence: String,
48
+ description: String?,
49
+ metadata: [String: Any]? = nil,
50
+ includeDebug: Bool
51
+ ) -> [String: Any] {
52
+
53
+ var signal: [String: Any] = [
54
+ "id": id,
55
+ "category": category,
56
+ "confidence": confidence
57
+ ]
58
+
59
+ if includeDebug, let description {
60
+ signal["description"] = description
61
+ }
62
+
63
+ if let metadata {
64
+ signal["metadata"] = metadata
65
+ }
66
+
67
+ return signal
68
+ }
69
+ }
70
+
71
+ enum IntegrityCorrelationUtils {
72
+
73
+ static func jailbreakCorrelation(
74
+ from signals: [[String: Any]],
75
+ includeDebug: Bool
76
+ ) -> [String: Any]? {
77
+
78
+ let jailbreakSignals = signals.filter {
79
+ ($0["category"] as? String) == "jailbreak"
80
+ }
81
+
82
+ guard jailbreakSignals.count >= 2 else {
83
+ return nil
84
+ }
85
+
86
+ let sources: Set<String> = Set(
87
+ jailbreakSignals.compactMap {
88
+ ($0["metadata"] as? [String: Any])?["source"] as? String
89
+ }
90
+ )
91
+
92
+ guard sources.count >= 2 else {
93
+ return nil
94
+ }
95
+
96
+ return [
97
+ "id": "ios_jailbreak_multiple_indicators",
98
+ "category": "jailbreak",
99
+ "confidence": "high",
100
+ "description": includeDebug
101
+ ? "Multiple independent jailbreak indicators detected simultaneously"
102
+ : nil,
103
+ "metadata": [
104
+ "indicatorCount": jailbreakSignals.count,
105
+ "sources": Array(sources)
106
+ ]
107
+ ].compactMapValues { $0 }
108
+ }
109
+
110
+ static func jailbreakAndHookCorrelation(
111
+ from signals: [[String: Any]],
112
+ includeDebug: Bool
113
+ ) -> [String: Any]? {
114
+
115
+ let hasJailbreak = signals.contains {
116
+ ($0["category"] as? String) == "jailbreak"
117
+ }
118
+
119
+ let hasHook = signals.contains {
120
+ ($0["category"] as? String) == "hook"
121
+ }
122
+
123
+ guard hasJailbreak && hasHook else {
124
+ return nil
125
+ }
126
+
127
+ return [
128
+ "id": "ios_jailbreak_and_hook_detected",
129
+ "category": "tamper",
130
+ "confidence": "high",
131
+ "description": includeDebug
132
+ ? "Device appears to be jailbroken and actively instrumented at runtime"
133
+ : nil,
134
+ "metadata": [
135
+ "jailbreakIndicators": signals.filter {
136
+ ($0["category"] as? String) == "jailbreak"
137
+ }.count,
138
+ "hookIndicators": signals.filter {
139
+ ($0["category"] as? String) == "hook"
140
+ }.count
141
+ ]
142
+ ].compactMapValues { $0 }
143
+ }
144
+ }
@@ -0,0 +1,16 @@
1
+ // This file is automatically generated. Do not modify manually.
2
+ import Foundation
3
+
4
+ /**
5
+ Container for the plugin's version information.
6
+ This enum provides a centralized, single source of truth for the native
7
+ version string, synchronized directly from the project's 'package.json'.
8
+ It ensures parity across JavaScript, Android, and iOS platforms.
9
+ */
10
+ public enum PluginVersion {
11
+ /**
12
+ The semantic version string of the plugin.
13
+ Value synchronized from package.json: "8.0.0-next.6"
14
+ */
15
+ public static let number = "8.0.0-next.6"
16
+ }
@@ -0,0 +1,86 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Filesystem integrity checks related to sandbox escape
5
+ and suspicious redirections.
6
+ */
7
+ struct IntegrityFilesystemChecks {
8
+
9
+ /**
10
+ Attempts to detect sandbox escape by testing write access
11
+ to a restricted filesystem location.
12
+
13
+ Characteristics:
14
+ - Best-effort heuristic
15
+ - Synchronous
16
+ - No persistent side effects (temporary file is removed)
17
+
18
+ IMPORTANT:
19
+ - A successful write strongly suggests a sandbox escape.
20
+ - A failure does NOT guarantee a secure environment.
21
+ */
22
+ static func canEscapeSandbox() -> Bool {
23
+
24
+ // Target path intentionally chosen inside a protected directory.
25
+ let testPath = "/private/integrity_test.txt"
26
+ let testString = "integrity_test"
27
+
28
+ do {
29
+ // Attempt to write a temporary file outside the app sandbox.
30
+ try testString.write(
31
+ toFile: testPath,
32
+ atomically: true,
33
+ encoding: .utf8
34
+ )
35
+
36
+ // Cleanup if the write unexpectedly succeeds.
37
+ try? FileManager.default.removeItem(atPath: testPath)
38
+ return true
39
+ } catch {
40
+ // Write failed as expected on non-compromised devices.
41
+ return false
42
+ }
43
+ }
44
+
45
+ /**
46
+ Detects suspicious symbolic links that may indicate
47
+ filesystem redirection caused by a jailbreak.
48
+
49
+ Characteristics:
50
+ - Read-only inspection
51
+ - No recursion
52
+ - Limited to well-known system paths
53
+
54
+ NOTES:
55
+ - This check intentionally avoids deep filesystem traversal
56
+ to reduce overhead and false positives.
57
+ */
58
+ static func hasSuspiciousSymlinks() -> Bool {
59
+
60
+ // Well-known system directories that should never be symbolic links.
61
+ let pathsToCheck = [
62
+ "/Applications",
63
+ "/Library",
64
+ "/usr/libexec",
65
+ "/usr/share"
66
+ ]
67
+
68
+ for path in pathsToCheck {
69
+ do {
70
+ let attributes =
71
+ try FileManager.default.attributesOfItem(atPath: path)
72
+
73
+ // Any symbolic link at these locations is highly suspicious.
74
+ if attributes[.type] as? FileAttributeType == .typeSymbolicLink {
75
+ return true
76
+ }
77
+ } catch {
78
+ // Ignore inaccessible paths and continue checking others.
79
+ continue
80
+ }
81
+ }
82
+
83
+ // No suspicious filesystem redirections detected.
84
+ return false
85
+ }
86
+ }
@@ -0,0 +1,85 @@
1
+ import Foundation
2
+ import MachO
3
+
4
+ /**
5
+ Runtime integrity checks related to instrumentation frameworks (e.g., Frida).
6
+ */
7
+ struct IntegrityHookChecks {
8
+
9
+ /**
10
+ Scans loaded dynamic libraries for suspicious instrumentation frameworks.
11
+ */
12
+ static func hookingSignal(
13
+ includeDebug: Bool
14
+ ) -> [String: Any]? {
15
+
16
+ // Total number of images currently loaded by dyld.
17
+ let imageCount = _dyld_image_count()
18
+
19
+ // Substrings commonly found in instrumentation or hooking frameworks.
20
+ // Matching is intentionally heuristic-based.
21
+ let suspicious = ["frida", "gadget", "substrate", "cydia", "substitute"]
22
+
23
+ for index in 0..<imageCount {
24
+ guard let cName = _dyld_get_image_name(index) else { continue }
25
+ let imageName = String(cString: cName).lowercased()
26
+
27
+ // Optimization: skip standard system libraries to reduce
28
+ // both runtime cost and false positives.
29
+ if imageName.contains("/usr/lib/")
30
+ || imageName.contains("/system/library/") {
31
+
32
+ // Still inspect non-standard subpaths for suspicious fragments.
33
+ if !suspicious.contains(where: { imageName.contains($0) }) {
34
+ continue
35
+ }
36
+ }
37
+
38
+ // Emit a hooking signal as soon as a suspicious library is found.
39
+ // Only the first match is reported to limit noise.
40
+ if suspicious.contains(where: { imageName.contains($0) }) {
41
+ return [
42
+ "id": "ios_hooking_library_detected",
43
+ "category": "hook",
44
+ "confidence": "high",
45
+ "description": includeDebug
46
+ ? "Detected suspicious instrumentation library: \(imageName)"
47
+ : nil,
48
+ "metadata": ["library_path": imageName]
49
+ ].compactMapValues { $0 }
50
+ }
51
+ }
52
+
53
+ return nil
54
+ }
55
+
56
+ /**
57
+ Detects if a known instrumentation port (Frida) is open on localhost.
58
+ */
59
+ static func isFridaPortOpen() -> Bool {
60
+
61
+ // Known default Frida server port.
62
+ // This check is intentionally limited to a single, well-known port.
63
+ var serverAddress = sockaddr_in()
64
+ serverAddress.sin_family = sa_family_t(AF_INET)
65
+ serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1")
66
+ serverAddress.sin_port = UInt16(27042).bigEndian
67
+
68
+ // Create a TCP socket to test local connectivity.
69
+ let sock = socket(AF_INET, SOCK_STREAM, 0)
70
+ if sock < 0 { return false }
71
+ defer { close(sock) }
72
+
73
+ // Attempt to connect to the local Frida server port.
74
+ // A successful connection suggests an active instrumentation service.
75
+ return withUnsafePointer(to: &serverAddress) {
76
+ $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
77
+ connect(
78
+ sock,
79
+ $0,
80
+ socklen_t(MemoryLayout<sockaddr_in>.size)
81
+ ) == 0
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,74 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Performs filesystem-based jailbreak detection.
5
+ All checks are synchronous and deterministic.
6
+ */
7
+ struct IntegrityJailbreakDetector {
8
+
9
+ /**
10
+ Performs filesystem-based jailbreak detection.
11
+
12
+ This detector relies exclusively on the presence of well-known
13
+ filesystem paths commonly introduced by jailbreak tools.
14
+
15
+ Characteristics:
16
+ - Deterministic
17
+ - Synchronous
18
+ - No side effects
19
+ - Safe to execute at application boot time
20
+
21
+ IMPORTANT:
22
+ - This detector intentionally emits at most ONE signal.
23
+ - The first matching path short-circuits further checks
24
+ to minimize noise and overhead.
25
+ */
26
+ static func detect(
27
+ includeDebug: Bool
28
+ ) -> [[String: Any]] {
29
+
30
+ // Well-known filesystem paths associated with common jailbreaks.
31
+ // This list is intentionally conservative and may evolve over time.
32
+ let suspiciousPaths = [
33
+ "/Applications/Cydia.app",
34
+ "/Library/MobileSubstrate/MobileSubstrate.dylib",
35
+ "/bin/bash",
36
+ "/usr/sbin/sshd",
37
+ "/etc/apt",
38
+ "/usr/bin/ssh",
39
+ "/private/var/lib/apt",
40
+ "/private/var/lib/cydia",
41
+ "/usr/libexec/sftp-server",
42
+ "/Applications/Sileo.app",
43
+ "/Applications/Zebra.app"
44
+ ]
45
+
46
+ // Iterate through known paths and emit a signal
47
+ // as soon as a single match is found.
48
+ for path in suspiciousPaths
49
+ where FileManager.default.fileExists(atPath: path) {
50
+
51
+ return [[
52
+ "id": "ios_jailbreak_path",
53
+ "category": "jailbreak",
54
+ "confidence": "high",
55
+
56
+ // Description is included only when debug output is enabled.
57
+ "description": includeDebug
58
+ ? "Filesystem contains paths commonly associated with jailbroken devices"
59
+ : nil,
60
+
61
+ // Include the matched path for diagnostic purposes only.
62
+ "metadata": [
63
+ "path": path,
64
+ "source": "filesystem",
65
+ // Added explicit detection method to metadata
66
+ "detection_method": "file_exists"
67
+ ]
68
+ ].compactMapValues { $0 }]
69
+ }
70
+
71
+ // No jailbreak-related filesystem indicators detected.
72
+ return []
73
+ }
74
+ }
@@ -0,0 +1,42 @@
1
+ import UIKit
2
+
3
+ /**
4
+ Detects jailbreak-related applications by probing URL schemes.
5
+
6
+ IMPORTANT:
7
+ - This detection is opt-in via native configuration.
8
+ - Requires LSApplicationQueriesSchemes to be declared in Info.plist.
9
+ - This is a LOW confidence heuristic and must never be used alone.
10
+ */
11
+ struct IntegrityJailbreakUrlSchemeDetector {
12
+
13
+ static func detect(
14
+ schemes: [String],
15
+ includeDebug: Bool
16
+ ) -> [String: Any]? {
17
+
18
+ guard !schemes.isEmpty else { return nil }
19
+
20
+ for scheme in schemes {
21
+ let urlString = "\(scheme)://"
22
+ guard let url = URL(string: urlString) else { continue }
23
+
24
+ if UIApplication.shared.canOpenURL(url) {
25
+ return [
26
+ "id": "ios_jailbreak_url_scheme",
27
+ "category": "jailbreak",
28
+ "confidence": "low",
29
+ "description": includeDebug
30
+ ? "Detected jailbreak-related application via URL scheme probing"
31
+ : nil,
32
+ "metadata": [
33
+ "scheme": scheme,
34
+ "source": "url_scheme"
35
+ ]
36
+ ].compactMapValues { $0 }
37
+ }
38
+ }
39
+
40
+ return nil
41
+ }
42
+ }
@@ -0,0 +1,40 @@
1
+ import Foundation
2
+ import DeviceCheck
3
+
4
+ /**
5
+ Handles remote attestation signals using Apple's DeviceCheck (App Attest) framework.
6
+
7
+ IMPORTANT:
8
+ - App Attest is NOT implemented yet.
9
+ - This component explicitly reports unavailability instead of failing silently.
10
+ - The emitted signal is observational only and LOW confidence.
11
+ */
12
+ struct IntegrityRemoteAttestor {
13
+
14
+ /**
15
+ Returns a LOW confidence signal indicating that App Attest
16
+ is currently not available or not implemented.
17
+
18
+ This signal is emitted only when strict mode is requested.
19
+ */
20
+ static func getAppAttestSignal(options: IntegrityCheckOptions) -> [String: Any]? {
21
+
22
+ guard options.level == "strict" else {
23
+ return nil
24
+ }
25
+
26
+ return [
27
+ "id": "ios_app_attest_unavailable",
28
+ "category": "environment",
29
+ "confidence": "low",
30
+ "description": options.includeDebugInfo == true
31
+ ? "Apple App Attest is not implemented or not available on this device"
32
+ : nil,
33
+ "metadata": [
34
+ "attestation": "unsupported",
35
+ "framework": "DeviceCheck",
36
+ "reason": "not_implemented"
37
+ ]
38
+ ].compactMapValues { $0 }
39
+ }
40
+ }
@@ -0,0 +1,63 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Runtime integrity checks related to debugging
5
+ and instrumentation frameworks.
6
+ */
7
+ struct IntegrityRuntimeChecks {
8
+
9
+ static func debugSignals(
10
+ includeDebug: Bool
11
+ ) -> [[String: Any]] {
12
+
13
+ // Collected runtime debug-related signals.
14
+ var signals: [[String: Any]] = []
15
+
16
+ // 1. Check if a debugger is attached via sysctl
17
+ var info = kinfo_proc()
18
+ var size = MemoryLayout.size(ofValue: info)
19
+ var mib: [Int32] = [
20
+ CTL_KERN,
21
+ KERN_PROC,
22
+ KERN_PROC_PID,
23
+ getpid()
24
+ ]
25
+
26
+ if sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0) == 0 {
27
+ if (info.kp_proc.p_flag & P_TRACED) != 0 {
28
+ signals.append([
29
+ "id": "ios_debugger_attached",
30
+ "category": "debug",
31
+ "confidence": "high",
32
+ "description": includeDebug
33
+ ? "A debugger is currently attached to the running process"
34
+ : nil,
35
+ "metadata": ["method": "sysctl_p_traced"]
36
+ ].compactMapValues { $0 })
37
+ }
38
+ }
39
+
40
+ // 2. Check for development/debug provisioning profile
41
+ // This mirrors the Android FLAG_DEBUGGABLE check.
42
+ if let path = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision") {
43
+ do {
44
+ let profileContent = try String(contentsOfFile: path, encoding: .ascii)
45
+ if profileContent.contains("<key>get-task-allow</key>\n\t\t<true/>") {
46
+ signals.append([
47
+ "id": "ios_runtime_debuggable",
48
+ "category": "debug",
49
+ "confidence": "medium",
50
+ "description": includeDebug
51
+ ? "Application is signed with a profile that allows debugging"
52
+ : nil,
53
+ "metadata": ["entitlement": "get-task-allow"]
54
+ ].compactMapValues { $0 })
55
+ }
56
+ } catch {
57
+ // Ignore errors reading the profile
58
+ }
59
+ }
60
+
61
+ return signals
62
+ }
63
+ }
@@ -0,0 +1,20 @@
1
+ import Foundation
2
+
3
+ /**
4
+ * Detects if the application is running in an iOS simulator environment.
5
+ */
6
+ struct IntegritySimulatorChecks {
7
+
8
+ /**
9
+ * Determines whether the current process is running under the iOS Simulator.
10
+ *
11
+ * NOTE: This uses compile-time environment checks and is not spoofable at runtime.
12
+ */
13
+ static func isSimulator() -> Bool {
14
+ #if targetEnvironment(simulator)
15
+ return true
16
+ #else
17
+ return false
18
+ #endif
19
+ }
20
+ }