react-native-security-suite 0.9.21 → 1.0.0-rc.1
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/README.md +233 -65
- package/android/build.gradle +11 -0
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/securitysuite/CryptoConfig.java +158 -0
- package/android/src/main/java/com/securitysuite/CryptoUtils.java +152 -0
- package/android/src/main/java/com/securitysuite/EcdhKeyStore.java +60 -0
- package/android/src/main/java/com/securitysuite/HeaderSanitizer.java +75 -0
- package/android/src/main/java/com/securitysuite/JWSGenerator.java +237 -32
- package/android/src/main/java/com/securitysuite/JwsFetchPayload.java +81 -0
- package/android/src/main/java/com/securitysuite/Obfuscation.java +57 -0
- package/android/src/main/java/com/securitysuite/SecureStorageNative.java +211 -0
- package/android/src/main/java/com/securitysuite/SecureView.java +2 -10
- package/android/src/main/java/com/securitysuite/SecureWindowHelper.java +30 -0
- package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +310 -102
- package/android/src/main/java/com/securitysuite/Sslpinning.java +219 -106
- package/android/src/main/java/com/securitysuite/security/AppIntegrityChecker.java +133 -0
- package/android/src/main/java/com/securitysuite/security/EmulatorDetector.java +145 -0
- package/android/src/main/java/com/securitysuite/security/RuntimeDetector.java +234 -0
- package/android/src/test/java/com/securitysuite/JWSGeneratorTest.java +153 -0
- package/android/src/test/java/com/securitysuite/SecureStorageNativeTest.java +37 -0
- package/ios/CryptoConfig.swift +124 -0
- package/ios/JWSGenerator.swift +288 -0
- package/ios/JWSGeneratorTests.swift +168 -0
- package/ios/KeychainHelper.swift +104 -0
- package/ios/Obfuscation.swift +42 -0
- package/ios/SecureStorageNative.swift +84 -0
- package/ios/Security/AppIntegrityChecker.swift +85 -0
- package/ios/Security/EmulatorDetector.swift +45 -0
- package/ios/Security/RuntimeDetector.swift +107 -0
- package/ios/SecuritySuite.mm +28 -4
- package/ios/SecuritySuite.swift +407 -131
- package/ios/SslPinning.swift +242 -263
- package/lib/commonjs/clipboard/index.js +3 -0
- package/lib/commonjs/clipboard/index.js.map +1 -0
- package/lib/commonjs/crypto/index.js +39 -0
- package/lib/commonjs/crypto/index.js.map +1 -0
- package/lib/commonjs/device/index.js +40 -0
- package/lib/commonjs/device/index.js.map +1 -0
- package/lib/commonjs/errors.js +62 -0
- package/lib/commonjs/errors.js.map +1 -0
- package/lib/commonjs/index.js +220 -151
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/integrity/index.js +40 -0
- package/lib/commonjs/integrity/index.js.map +1 -0
- package/lib/commonjs/jws.js +141 -0
- package/lib/commonjs/jws.js.map +1 -0
- package/lib/commonjs/legacy/cryptoOptions.js +20 -0
- package/lib/commonjs/legacy/cryptoOptions.js.map +1 -0
- package/lib/commonjs/native/bridge.js +23 -0
- package/lib/commonjs/native/bridge.js.map +1 -0
- package/lib/commonjs/network/index.js +3 -0
- package/lib/commonjs/network/index.js.map +1 -0
- package/lib/commonjs/risk/score.js +36 -0
- package/lib/commonjs/risk/score.js.map +1 -0
- package/lib/commonjs/runtime/index.js +31 -0
- package/lib/commonjs/runtime/index.js.map +1 -0
- package/lib/commonjs/screen/index.js +13 -0
- package/lib/commonjs/screen/index.js.map +1 -0
- package/lib/commonjs/securitySuite/index.js +42 -0
- package/lib/commonjs/securitySuite/index.js.map +1 -0
- package/lib/commonjs/storage/index.js +3 -0
- package/lib/commonjs/storage/index.js.map +1 -0
- package/lib/commonjs/types/detection.js +2 -0
- package/lib/commonjs/types/detection.js.map +1 -0
- package/lib/module/clipboard/index.js +3 -0
- package/lib/module/clipboard/index.js.map +1 -0
- package/lib/module/crypto/index.js +35 -0
- package/lib/module/crypto/index.js.map +1 -0
- package/lib/module/device/index.js +36 -0
- package/lib/module/device/index.js.map +1 -0
- package/lib/module/errors.js +55 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/index.js +147 -148
- package/lib/module/index.js.map +1 -1
- package/lib/module/integrity/index.js +36 -0
- package/lib/module/integrity/index.js.map +1 -0
- package/lib/module/jws.js +127 -0
- package/lib/module/jws.js.map +1 -0
- package/lib/module/legacy/cryptoOptions.js +16 -0
- package/lib/module/legacy/cryptoOptions.js.map +1 -0
- package/lib/module/native/bridge.js +19 -0
- package/lib/module/native/bridge.js.map +1 -0
- package/lib/module/network/index.js +3 -0
- package/lib/module/network/index.js.map +1 -0
- package/lib/module/risk/score.js +32 -0
- package/lib/module/risk/score.js.map +1 -0
- package/lib/module/runtime/index.js +27 -0
- package/lib/module/runtime/index.js.map +1 -0
- package/lib/module/screen/index.js +5 -0
- package/lib/module/screen/index.js.map +1 -0
- package/lib/module/securitySuite/index.js +38 -0
- package/lib/module/securitySuite/index.js.map +1 -0
- package/lib/module/storage/index.js +3 -0
- package/lib/module/storage/index.js.map +1 -0
- package/lib/module/types/detection.js +2 -0
- package/lib/module/types/detection.js.map +1 -0
- package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts +215 -0
- package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/SecureView.d.ts +1 -1
- package/lib/typescript/commonjs/src/SecureView.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/clipboard/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/clipboard/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/crypto/index.d.ts +15 -0
- package/lib/typescript/commonjs/src/crypto/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/device/index.d.ts +11 -0
- package/lib/typescript/commonjs/src/device/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/errors.d.ts +17 -0
- package/lib/typescript/commonjs/src/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +77 -24
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/integrity/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/integrity/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/jws.d.ts +44 -0
- package/lib/typescript/commonjs/src/jws.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts +35 -0
- package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/native/bridge.d.ts +12 -0
- package/lib/typescript/commonjs/src/native/bridge.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/network/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/network/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/risk/score.d.ts +12 -0
- package/lib/typescript/commonjs/src/risk/score.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/runtime/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/screen/index.d.ts +3 -0
- package/lib/typescript/commonjs/src/screen/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/securitySuite/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/securitySuite/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/storage/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/types/detection.d.ts +41 -0
- package/lib/typescript/commonjs/src/types/detection.d.ts.map +1 -0
- package/lib/typescript/module/docs/api-v1-proposal.d.ts +215 -0
- package/lib/typescript/module/docs/api-v1-proposal.d.ts.map +1 -0
- package/lib/typescript/module/src/SecureView.d.ts +1 -1
- package/lib/typescript/module/src/SecureView.d.ts.map +1 -1
- package/lib/typescript/module/src/clipboard/index.d.ts +2 -0
- package/lib/typescript/module/src/clipboard/index.d.ts.map +1 -0
- package/lib/typescript/module/src/crypto/index.d.ts +15 -0
- package/lib/typescript/module/src/crypto/index.d.ts.map +1 -0
- package/lib/typescript/module/src/device/index.d.ts +11 -0
- package/lib/typescript/module/src/device/index.d.ts.map +1 -0
- package/lib/typescript/module/src/errors.d.ts +17 -0
- package/lib/typescript/module/src/errors.d.ts.map +1 -0
- package/lib/typescript/module/src/helpers.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +77 -24
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/integrity/index.d.ts +6 -0
- package/lib/typescript/module/src/integrity/index.d.ts.map +1 -0
- package/lib/typescript/module/src/jws.d.ts +44 -0
- package/lib/typescript/module/src/jws.d.ts.map +1 -0
- package/lib/typescript/module/src/legacy/cryptoOptions.d.ts +35 -0
- package/lib/typescript/module/src/legacy/cryptoOptions.d.ts.map +1 -0
- package/lib/typescript/module/src/native/bridge.d.ts +12 -0
- package/lib/typescript/module/src/native/bridge.d.ts.map +1 -0
- package/lib/typescript/module/src/network/index.d.ts +2 -0
- package/lib/typescript/module/src/network/index.d.ts.map +1 -0
- package/lib/typescript/module/src/risk/score.d.ts +12 -0
- package/lib/typescript/module/src/risk/score.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/index.d.ts +6 -0
- package/lib/typescript/module/src/runtime/index.d.ts.map +1 -0
- package/lib/typescript/module/src/screen/index.d.ts +3 -0
- package/lib/typescript/module/src/screen/index.d.ts.map +1 -0
- package/lib/typescript/module/src/securitySuite/index.d.ts +6 -0
- package/lib/typescript/module/src/securitySuite/index.d.ts.map +1 -0
- package/lib/typescript/module/src/storage/index.d.ts +2 -0
- package/lib/typescript/module/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/module/src/types/detection.d.ts +41 -0
- package/lib/typescript/module/src/types/detection.d.ts.map +1 -0
- package/package.json +2 -4
- package/src/clipboard/index.ts +1 -0
- package/src/crypto/index.ts +49 -0
- package/src/device/index.ts +47 -0
- package/src/errors.ts +84 -0
- package/src/index.tsx +293 -195
- package/src/integrity/index.ts +46 -0
- package/src/jws.ts +213 -0
- package/src/legacy/cryptoOptions.ts +49 -0
- package/src/native/bridge.ts +37 -0
- package/src/network/index.ts +1 -0
- package/src/risk/score.ts +49 -0
- package/src/runtime/index.ts +43 -0
- package/src/screen/index.ts +2 -0
- package/src/securitySuite/index.ts +45 -0
- package/src/storage/index.ts +1 -0
- package/src/types/detection.ts +46 -0
- package/android/src/main/java/com/securitysuite/StorageEncryption.java +0 -52
- package/ios/StorageEncryption.swift +0 -89
package/ios/SslPinning.swift
CHANGED
|
@@ -1,303 +1,282 @@
|
|
|
1
1
|
import Pulse
|
|
2
|
+
import Foundation
|
|
3
|
+
import Security
|
|
4
|
+
import CommonCrypto
|
|
2
5
|
|
|
3
6
|
@available(iOS 13.0, *)
|
|
4
|
-
class
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
7
|
+
final class PinningConfiguration {
|
|
8
|
+
let enabled: Bool
|
|
9
|
+
let error: String?
|
|
10
|
+
let validDomains: [String]
|
|
11
|
+
let pinHashes: Set<Data>
|
|
12
|
+
|
|
13
|
+
init(data: NSDictionary) {
|
|
14
|
+
let hasCertificates = data["certificates"] != nil
|
|
15
|
+
let hasValidDomains = data["validDomains"] != nil
|
|
16
|
+
|
|
17
|
+
if hasCertificates != hasValidDomains {
|
|
18
|
+
self.enabled = false
|
|
19
|
+
self.error = "SSL pinning requires both 'certificates' (SPKI SHA-256 hashes) and 'validDomains'"
|
|
20
|
+
self.validDomains = []
|
|
21
|
+
self.pinHashes = []
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
guard hasCertificates, let certs = data["certificates"] as? [String],
|
|
26
|
+
let domains = data["validDomains"] as? [String] else {
|
|
27
|
+
self.enabled = false
|
|
28
|
+
self.error = nil
|
|
29
|
+
self.validDomains = []
|
|
30
|
+
self.pinHashes = []
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
guard !certs.isEmpty else {
|
|
35
|
+
self.enabled = false
|
|
36
|
+
self.error = "At least one certificate/public key pin is required"
|
|
37
|
+
self.validDomains = []
|
|
38
|
+
self.pinHashes = []
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
guard !domains.isEmpty else {
|
|
43
|
+
self.enabled = false
|
|
44
|
+
self.error = "At least one valid domain is required"
|
|
45
|
+
self.validDomains = []
|
|
46
|
+
self.pinHashes = []
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
self.validDomains = domains
|
|
51
|
+
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() }
|
|
52
|
+
.filter { !$0.isEmpty }
|
|
53
|
+
self.pinHashes = Set(certs.compactMap { cert in
|
|
54
|
+
let filtered = cert
|
|
55
|
+
.replacingOccurrences(of: "sha256/", with: "", options: .caseInsensitive)
|
|
56
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
57
|
+
return Data(base64Encoded: filtered)
|
|
58
|
+
}.filter { !$0.isEmpty })
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
63
|
-
return
|
|
60
|
+
if self.pinHashes.isEmpty {
|
|
61
|
+
self.enabled = false
|
|
62
|
+
self.error = "No valid SPKI SHA-256 pins found in certificates"
|
|
63
|
+
} else {
|
|
64
|
+
self.enabled = true
|
|
65
|
+
self.error = nil
|
|
66
|
+
}
|
|
64
67
|
}
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@available(iOS 13.0, *)
|
|
71
|
+
class SSLPinning: NSObject, URLSessionDataDelegate {
|
|
72
|
+
var url: NSString!
|
|
73
|
+
var data: NSDictionary!
|
|
74
|
+
var callback: RCTResponseSenderBlock!
|
|
75
|
+
let config: PinningConfiguration
|
|
76
|
+
var loggerEnabled = false
|
|
77
|
+
var networkLogger: NetworkLogger = .init()
|
|
78
|
+
var responseData: Data = Data()
|
|
79
|
+
let pulseNotification = PulseUINotification()
|
|
80
|
+
|
|
81
|
+
init(url: NSString, data: NSDictionary, callback: @escaping RCTResponseSenderBlock) {
|
|
82
|
+
self.url = url
|
|
83
|
+
self.data = data
|
|
84
|
+
self.callback = callback
|
|
85
|
+
self.config = PinningConfiguration(data: data)
|
|
86
|
+
super.init()
|
|
87
|
+
|
|
88
|
+
#if DEBUG
|
|
89
|
+
loggerEnabled = (data["loggerIsEnabled"] as? Bool) == true
|
|
90
|
+
#else
|
|
91
|
+
loggerEnabled = false
|
|
92
|
+
#endif
|
|
67
93
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
leafKeyHashes.isEmpty || leafCertificateValid
|
|
77
|
-
),
|
|
78
|
-
let commonNameRegex = try? NSRegularExpression(pattern: pattern) else {
|
|
79
|
-
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
80
|
-
return
|
|
94
|
+
|
|
95
|
+
static func isHttpsURL(_ urlString: String) -> Bool {
|
|
96
|
+
guard let url = URL(string: urlString.trimmingCharacters(in: .whitespacesAndNewlines)),
|
|
97
|
+
let scheme = url.scheme?.lowercased(),
|
|
98
|
+
let host = url.host, !host.isEmpty else {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
return scheme == "https"
|
|
81
102
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
|
|
104
|
+
static func hostnameMatchesValidDomains(_ host: String, validDomains: [String]) -> Bool {
|
|
105
|
+
let normalizedHost = host.lowercased()
|
|
106
|
+
return validDomains.contains { domain in
|
|
107
|
+
normalizedHost == domain || normalizedHost.hasSuffix("." + domain)
|
|
108
|
+
}
|
|
86
109
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
completionHandler(.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
|
|
111
|
+
func urlSession(
|
|
112
|
+
_ session: URLSession,
|
|
113
|
+
didReceive challenge: URLAuthenticationChallenge,
|
|
114
|
+
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
|
|
115
|
+
) {
|
|
116
|
+
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else {
|
|
117
|
+
completionHandler(.performDefaultHandling, nil)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
guard config.enabled else {
|
|
122
|
+
// No pinning configured: standard TLS validation only.
|
|
123
|
+
completionHandler(.performDefaultHandling, nil)
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
guard let trust = challenge.protectionSpace.serverTrust else {
|
|
128
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let host = challenge.protectionSpace.host.lowercased()
|
|
133
|
+
let port = challenge.protectionSpace.port
|
|
134
|
+
|
|
135
|
+
guard port == 443 else {
|
|
136
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
guard Self.hostnameMatchesValidDomains(host, validDomains: config.validDomains) else {
|
|
141
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Enforce hostname policy before trust evaluation.
|
|
146
|
+
SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, host as CFString))
|
|
147
|
+
|
|
148
|
+
var pinMatched = false
|
|
149
|
+
let certificateCount = SecTrustGetCertificateCount(trust)
|
|
150
|
+
for index in 0..<certificateCount {
|
|
151
|
+
guard let certificate = SecTrustGetCertificateAtIndex(trust, index) else { continue }
|
|
152
|
+
|
|
153
|
+
if let spkiHash = certificate.publicKeyPinHash, config.pinHashes.contains(spkiHash) {
|
|
154
|
+
pinMatched = true
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let certData = SecCertificateCopyData(certificate) as Data
|
|
159
|
+
let certHash = certData._hash()
|
|
160
|
+
if config.pinHashes.contains(certHash) {
|
|
161
|
+
pinMatched = true
|
|
162
|
+
break
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
guard pinMatched else {
|
|
167
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
var error: CFError?
|
|
172
|
+
guard SecTrustEvaluateWithError(trust, &error) else {
|
|
173
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
completionHandler(.useCredential, URLCredential(trust: trust))
|
|
96
178
|
}
|
|
97
|
-
|
|
98
|
-
let credential = URLCredential(trust: trust)
|
|
99
|
-
completionHandler(.useCredential, credential)
|
|
100
|
-
}
|
|
101
|
-
|
|
179
|
+
|
|
102
180
|
func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) {
|
|
103
|
-
|
|
181
|
+
guard loggerEnabled else { return }
|
|
182
|
+
networkLogger.logTaskCreated(task)
|
|
104
183
|
}
|
|
105
|
-
|
|
184
|
+
|
|
106
185
|
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
|
107
|
-
|
|
108
|
-
|
|
186
|
+
if loggerEnabled {
|
|
187
|
+
networkLogger.logDataTask(dataTask, didReceive: data)
|
|
188
|
+
}
|
|
189
|
+
responseData.append(data)
|
|
109
190
|
}
|
|
110
|
-
|
|
191
|
+
|
|
111
192
|
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
193
|
+
if loggerEnabled {
|
|
194
|
+
networkLogger.logTask(task, didCompleteWithError: error)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if let error = error {
|
|
198
|
+
callback([NSNull(), error.localizedDescription])
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
guard let httpResponse = task.response as? HTTPURLResponse else {
|
|
203
|
+
callback([NSNull(), "Unknown error occurred"])
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let responseUrl = httpResponse.url?.absoluteString ?? (url as String)
|
|
208
|
+
let status = httpResponse.statusCode
|
|
209
|
+
let headers = Self.sanitizedHeaders(httpResponse.allHeaderFields)
|
|
210
|
+
|
|
211
|
+
if status > 299 {
|
|
212
|
+
callback([NSNull(), [
|
|
213
|
+
"url": responseUrl,
|
|
214
|
+
"status": status,
|
|
215
|
+
"headers": headers,
|
|
216
|
+
"error": String(data: responseData, encoding: .utf8) ?? "",
|
|
128
217
|
]])
|
|
129
|
-
|
|
218
|
+
} else {
|
|
130
219
|
callback([[
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
220
|
+
"url": responseUrl,
|
|
221
|
+
"status": status,
|
|
222
|
+
"headers": headers,
|
|
223
|
+
"response": String(decoding: responseData, as: UTF8.self),
|
|
224
|
+
"responseJson": try? JSONSerialization.jsonObject(with: responseData, options: []),
|
|
136
225
|
], NSNull()])
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
callback([NSNull(), "Unknown error occurred"])
|
|
140
226
|
}
|
|
141
|
-
}
|
|
142
227
|
}
|
|
143
228
|
|
|
144
229
|
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
|
230
|
+
guard loggerEnabled else { return }
|
|
145
231
|
networkLogger.logTask(task, didFinishCollecting: metrics)
|
|
146
|
-
if
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
232
|
+
if let httpResponse = task.response as? HTTPURLResponse,
|
|
233
|
+
let responseURL = URL(string: httpResponse.url?.absoluteString ?? (url as String)) {
|
|
234
|
+
pulseNotification.showNotification(body: "\(httpResponse.statusCode) \(responseURL.relativePath)")
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private static let sensitiveHeaders: Set<String> = [
|
|
239
|
+
"authorization", "proxy-authorization", "cookie", "set-cookie",
|
|
240
|
+
"x-api-key", "x-auth-token", "x-access-token", "x-jws-signature", "x-request-signature", "x-csrf-token",
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
static func sanitizedHeaders(_ headers: [AnyHashable: Any]) -> [String: String] {
|
|
244
|
+
var result: [String: String] = [:]
|
|
245
|
+
for (key, value) in headers {
|
|
246
|
+
guard let name = key as? String else { continue }
|
|
247
|
+
let stringValue = String(describing: value)
|
|
248
|
+
if sensitiveHeaders.contains(name.lowercased()) {
|
|
249
|
+
result[name] = maskValue(stringValue)
|
|
250
|
+
} else {
|
|
251
|
+
result[name] = stringValue
|
|
150
252
|
}
|
|
151
|
-
}
|
|
152
253
|
}
|
|
254
|
+
return result
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
static func maskValue(_ value: String) -> String {
|
|
258
|
+
guard value.count > 8 else { return "***" }
|
|
259
|
+
return String(value.prefix(4)) + "***" + String(value.suffix(2))
|
|
153
260
|
}
|
|
154
261
|
}
|
|
155
262
|
|
|
156
263
|
@available(iOS 13.0, *)
|
|
157
264
|
extension Data {
|
|
158
|
-
/**
|
|
159
|
-
Calculates hash digest of data.
|
|
160
|
-
|
|
161
|
-
- Parameter digest: digest type. Currently only SHA is supported.
|
|
162
|
-
- Returns: A data object with length equal to digest length.
|
|
163
|
-
*/
|
|
164
265
|
public func _hash() -> Data {
|
|
165
266
|
guard !isEmpty else { return Data() }
|
|
166
|
-
var result = [UInt8](repeating: 0, count: 256/8)
|
|
167
|
-
self.withUnsafeBytes { (buf: UnsafeRawBufferPointer)
|
|
168
|
-
let ptr = buf.baseAddress
|
|
169
|
-
|
|
170
|
-
CC_SHA256(ptr, dataLen, &result)
|
|
267
|
+
var result = [UInt8](repeating: 0, count: 256 / 8)
|
|
268
|
+
self.withUnsafeBytes { (buf: UnsafeRawBufferPointer) in
|
|
269
|
+
guard let ptr = buf.baseAddress else { return }
|
|
270
|
+
_ = CC_SHA256(ptr, CC_LONG(buf.count), &result)
|
|
171
271
|
}
|
|
172
|
-
|
|
173
272
|
return Data(result)
|
|
174
273
|
}
|
|
175
274
|
}
|
|
176
275
|
|
|
177
|
-
extension Dictionary {
|
|
178
|
-
func toString() -> String? {
|
|
179
|
-
return (self.compactMap({ (key, value) -> String in
|
|
180
|
-
return "\(key)=\(value)"
|
|
181
|
-
}) as Array).joined(separator: "&")
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
@available(iOS 13.0, *)
|
|
186
|
-
extension SecTrust {
|
|
187
|
-
// Returns certificates of the certificate chain used to evaluate trust.
|
|
188
|
-
fileprivate var certificates: [SecCertificate] {
|
|
189
|
-
(0..<SecTrustGetCertificateCount(self))
|
|
190
|
-
.map { SecTrustGetCertificateAtIndex(self, $0 as CFIndex)! }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Retrieves the policies used by a given trust management object.
|
|
194
|
-
fileprivate var policies: [SecPolicy]? {
|
|
195
|
-
get {
|
|
196
|
-
var result: CFArray?
|
|
197
|
-
SecTrustCopyPolicies(self, &result)
|
|
198
|
-
return result as? [SecPolicy]
|
|
199
|
-
}
|
|
200
|
-
set {
|
|
201
|
-
if let newValue = newValue {
|
|
202
|
-
SecTrustSetPolicies(self, newValue as CFArray)
|
|
203
|
-
} else {
|
|
204
|
-
SecTrustSetPolicies(self, [] as CFArray)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
fileprivate func evaluate() throws -> Bool {
|
|
210
|
-
var error: CFError?
|
|
211
|
-
let success = SecTrustEvaluateWithError(self, &error)
|
|
212
|
-
if let error = error {
|
|
213
|
-
throw error
|
|
214
|
-
}
|
|
215
|
-
return success
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
276
|
@available(iOS 13.0, *)
|
|
220
277
|
extension SecCertificate {
|
|
221
|
-
fileprivate var
|
|
222
|
-
SecCertificateCopyKey(self)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
fileprivate var commonName: String? {
|
|
226
|
-
var result: CFString?
|
|
227
|
-
SecCertificateCopyCommonName(self, &result)
|
|
228
|
-
return result as String?
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
fileprivate var pin: Data? {
|
|
232
|
-
try? key?.bytes().hash(digest: .sha256)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
extension SecPolicy {
|
|
237
|
-
static func ssl(server: Bool, hostname: String) -> SecPolicy {
|
|
238
|
-
SecPolicyCreateSSL(server, hostname as CFString)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
static func basicX509() -> SecPolicy {
|
|
242
|
-
SecPolicyCreateBasicX509()
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
extension NSRegularExpression {
|
|
247
|
-
public func matches(
|
|
248
|
-
in string: String,
|
|
249
|
-
options: NSRegularExpression.MatchingOptions = []
|
|
250
|
-
) -> [NSTextCheckingResult] {
|
|
251
|
-
matches(in: string, options: options, range: NSRange(string.startIndex..., in: string))
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
public func textMatches(
|
|
255
|
-
in string: String,
|
|
256
|
-
options: NSRegularExpression.MatchingOptions = []
|
|
257
|
-
) -> [String] {
|
|
258
|
-
textMatches(in: string, options: options, range: string.startIndex...)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
public func matches<R: RangeExpression>(
|
|
262
|
-
in string: String,
|
|
263
|
-
options: NSRegularExpression.MatchingOptions = [],
|
|
264
|
-
range: R
|
|
265
|
-
) -> [NSTextCheckingResult] where R.Bound == String.Index {
|
|
266
|
-
matches(in: string, options: options, range: NSRange(range, in: string))
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
public func textMatches<R: RangeExpression>(
|
|
270
|
-
in string: String,
|
|
271
|
-
options: NSRegularExpression.MatchingOptions = [],
|
|
272
|
-
range: R
|
|
273
|
-
) -> [String] where R.Bound == String.Index {
|
|
274
|
-
matches(in: string, options: options, range: NSRange(range, in: string))
|
|
275
|
-
.map {
|
|
276
|
-
guard let range = Range($0.range, in: string) else {
|
|
277
|
-
return ""
|
|
278
|
-
}
|
|
279
|
-
return String(string[range])
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
struct JoseHeader: Codable {
|
|
285
|
-
internal init(alg: String = "HS256", kid: String, b64: Bool = false, crit: [String] = ["b64"], requestId: String) {
|
|
286
|
-
self.alg = alg
|
|
287
|
-
self.kid = kid
|
|
288
|
-
self.b64 = b64
|
|
289
|
-
self.crit = crit
|
|
290
|
-
self.requestId = requestId
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
private enum CodingKeys: String, CodingKey {
|
|
294
|
-
case alg, kid, b64, crit
|
|
295
|
-
case requestId = "request_id"
|
|
278
|
+
fileprivate var publicKeyPinHash: Data? {
|
|
279
|
+
guard let key = SecCertificateCopyKey(self) else { return nil }
|
|
280
|
+
return try? key.bytes().hash(digest: .sha256)
|
|
296
281
|
}
|
|
297
|
-
|
|
298
|
-
let alg: String
|
|
299
|
-
let kid: String
|
|
300
|
-
let b64: Bool
|
|
301
|
-
let crit: [String]
|
|
302
|
-
let requestId: String
|
|
303
282
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../../src","sources":["clipboard/index.ts"],"mappings":"AAAA;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Crypto = void 0;
|
|
7
|
+
var _bridge = require("../native/bridge.js");
|
|
8
|
+
function toNativeCryptoOptions(options) {
|
|
9
|
+
return {
|
|
10
|
+
keyAgreementAlgorithm: options?.keyAgreementAlgorithm ?? 'X25519',
|
|
11
|
+
keyFactoryAlgorithm: options?.keyType ?? options?.keyFactoryAlgorithm ?? 'OKP',
|
|
12
|
+
encryptionKeyAlgorithm: options?.encryptionKeyAlgorithm ?? 'AES-256',
|
|
13
|
+
hmacKeyAlgorithm: options?.hmacAlgorithm ?? options?.hmacKeyAlgorithm ?? 'HMAC-SHA-512',
|
|
14
|
+
cipherTransformation: options?.cipher ?? options?.cipherTransformation ?? 'AES-GCM',
|
|
15
|
+
gcmTagLength: options?.tagLength ?? options?.gcmTagLength ?? 128,
|
|
16
|
+
gcmIvLength: options?.ivLength ?? options?.gcmIvLength ?? 12
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const Crypto = exports.Crypto = {
|
|
20
|
+
getPublicKey() {
|
|
21
|
+
return (0, _bridge.getNativeModule)().getPublicKey();
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* Derives a shared encryption key natively without returning it to JavaScript.
|
|
25
|
+
* Call `encryptBySharedKey` / `decryptBySharedKey` afterward (legacy bridge methods).
|
|
26
|
+
*/
|
|
27
|
+
establishSharedKey(serverPublicKey, options) {
|
|
28
|
+
const native = (0, _bridge.getNativeModule)();
|
|
29
|
+
const nativeOptions = toNativeCryptoOptions(options);
|
|
30
|
+
if (options?.returnSharedKey) {
|
|
31
|
+
return native.getSharedKey(serverPublicKey, nativeOptions);
|
|
32
|
+
}
|
|
33
|
+
if (typeof native.establishSharedKey === 'function') {
|
|
34
|
+
return native.establishSharedKey(serverPublicKey, nativeOptions);
|
|
35
|
+
}
|
|
36
|
+
return native.getSharedKey(serverPublicKey, nativeOptions).then(() => undefined);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_bridge","require","toNativeCryptoOptions","options","keyAgreementAlgorithm","keyFactoryAlgorithm","keyType","encryptionKeyAlgorithm","hmacKeyAlgorithm","hmacAlgorithm","cipherTransformation","cipher","gcmTagLength","tagLength","gcmIvLength","ivLength","Crypto","exports","getPublicKey","getNativeModule","establishSharedKey","serverPublicKey","native","nativeOptions","returnSharedKey","getSharedKey","then","undefined"],"sourceRoot":"../../../src","sources":["crypto/index.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGA,SAASC,qBAAqBA,CAACC,OAA8B,EAAE;EAC7D,OAAO;IACLC,qBAAqB,EAAED,OAAO,EAAEC,qBAAqB,IAAI,QAAQ;IACjEC,mBAAmB,EAAEF,OAAO,EAAEG,OAAO,IAAIH,OAAO,EAAEE,mBAAmB,IAAI,KAAK;IAC9EE,sBAAsB,EAAEJ,OAAO,EAAEI,sBAAsB,IAAI,SAAS;IACpEC,gBAAgB,EAAEL,OAAO,EAAEM,aAAa,IAAIN,OAAO,EAAEK,gBAAgB,IAAI,cAAc;IACvFE,oBAAoB,EAAEP,OAAO,EAAEQ,MAAM,IAAIR,OAAO,EAAEO,oBAAoB,IAAI,SAAS;IACnFE,YAAY,EAAET,OAAO,EAAEU,SAAS,IAAIV,OAAO,EAAES,YAAY,IAAI,GAAG;IAChEE,WAAW,EAAEX,OAAO,EAAEY,QAAQ,IAAIZ,OAAO,EAAEW,WAAW,IAAI;EAC5D,CAAC;AACH;AAOO,MAAME,MAAM,GAAAC,OAAA,CAAAD,MAAA,GAAG;EACpBE,YAAYA,CAAA,EAAoB;IAC9B,OAAO,IAAAC,uBAAe,EAAC,CAAC,CAACD,YAAY,CAAC,CAAC;EACzC,CAAC;EAED;AACF;AACA;AACA;EACEE,kBAAkBA,CAChBC,eAAuB,EACvBlB,OAAmC,EACX;IACxB,MAAMmB,MAAM,GAAG,IAAAH,uBAAe,EAAC,CAAC;IAChC,MAAMI,aAAa,GAAGrB,qBAAqB,CAACC,OAAO,CAAC;IAEpD,IAAIA,OAAO,EAAEqB,eAAe,EAAE;MAC5B,OAAOF,MAAM,CAACG,YAAY,CAACJ,eAAe,EAAEE,aAAa,CAAC;IAC5D;IAEA,IAAI,OAAOD,MAAM,CAACF,kBAAkB,KAAK,UAAU,EAAE;MACnD,OAAOE,MAAM,CAACF,kBAAkB,CAACC,eAAe,EAAEE,aAAa,CAAC;IAClE;IAEA,OAAOD,MAAM,CAACG,YAAY,CAACJ,eAAe,EAAEE,aAAa,CAAC,CAACG,IAAI,CAAC,MAAMC,SAAS,CAAC;EAClF;AACF,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DeviceSecurity = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
var _bridge = require("../native/bridge.js");
|
|
9
|
+
function parseEnvironment(raw) {
|
|
10
|
+
return {
|
|
11
|
+
isEmulator: Boolean(raw.isEmulator),
|
|
12
|
+
isSimulator: Boolean(raw.isSimulator),
|
|
13
|
+
indicators: Array.isArray(raw.indicators) ? raw.indicators.filter(item => typeof item === 'string') : []
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const DeviceSecurity = exports.DeviceSecurity = {
|
|
17
|
+
/** @deprecated Use `isCompromised()` or `SecuritySuite.getSecurityReport()`. */
|
|
18
|
+
hasSecurityRisk() {
|
|
19
|
+
return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
|
|
20
|
+
},
|
|
21
|
+
isCompromised() {
|
|
22
|
+
return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
|
|
23
|
+
},
|
|
24
|
+
isRooted() {
|
|
25
|
+
if (_reactNative.Platform.OS !== 'android') {
|
|
26
|
+
return Promise.resolve(false);
|
|
27
|
+
}
|
|
28
|
+
return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
|
|
29
|
+
},
|
|
30
|
+
isJailbroken() {
|
|
31
|
+
if (_reactNative.Platform.OS !== 'ios') {
|
|
32
|
+
return Promise.resolve(false);
|
|
33
|
+
}
|
|
34
|
+
return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
|
|
35
|
+
},
|
|
36
|
+
getEnvironment() {
|
|
37
|
+
return (0, _bridge.getNativeModule)().deviceGetEnvironment().then(result => parseEnvironment(result));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_bridge","parseEnvironment","raw","isEmulator","Boolean","isSimulator","indicators","Array","isArray","filter","item","DeviceSecurity","exports","hasSecurityRisk","getNativeModule","deviceHasSecurityRisk","isCompromised","isRooted","Platform","OS","Promise","resolve","isJailbroken","getEnvironment","deviceGetEnvironment","then","result"],"sourceRoot":"../../../src","sources":["device/index.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,IAAAC,OAAA,GAAAD,OAAA;AAGA,SAASE,gBAAgBA,CAACC,GAA4B,EAAqB;EACzE,OAAO;IACLC,UAAU,EAAEC,OAAO,CAACF,GAAG,CAACC,UAAU,CAAC;IACnCE,WAAW,EAAED,OAAO,CAACF,GAAG,CAACG,WAAW,CAAC;IACrCC,UAAU,EAAEC,KAAK,CAACC,OAAO,CAACN,GAAG,CAACI,UAAU,CAAC,GACrCJ,GAAG,CAACI,UAAU,CAACG,MAAM,CAAEC,IAAI,IAAqB,OAAOA,IAAI,KAAK,QAAQ,CAAC,GACzE;EACN,CAAC;AACH;AAEO,MAAMC,cAAc,GAAAC,OAAA,CAAAD,cAAA,GAAG;EAC5B;EACAE,eAAeA,CAAA,EAAqB;IAClC,OAAO,IAAAC,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDC,aAAaA,CAAA,EAAqB;IAChC,OAAO,IAAAF,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDE,QAAQA,CAAA,EAAqB;IAC3B,IAAIC,qBAAQ,CAACC,EAAE,KAAK,SAAS,EAAE;MAC7B,OAAOC,OAAO,CAACC,OAAO,CAAC,KAAK,CAAC;IAC/B;IACA,OAAO,IAAAP,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDO,YAAYA,CAAA,EAAqB;IAC/B,IAAIJ,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;MACzB,OAAOC,OAAO,CAACC,OAAO,CAAC,KAAK,CAAC;IAC/B;IACA,OAAO,IAAAP,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDQ,cAAcA,CAAA,EAA+B;IAC3C,OAAO,IAAAT,uBAAe,EAAC,CAAC,CACrBU,oBAAoB,CAAC,CAAC,CACtBC,IAAI,CAAEC,MAAM,IAAKzB,gBAAgB,CAACyB,MAAM,CAAC,CAAC;EAC/C;AACF,CAAC","ignoreList":[]}
|