@cap-kit/tls-fingerprint 8.0.0

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 (51) hide show
  1. package/CapKitTlsFingerprint.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +25 -0
  4. package/README.md +427 -0
  5. package/android/build.gradle +103 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/java/io/capkit/settings/TLSFingerprintImpl.kt +333 -0
  8. package/android/src/main/java/io/capkit/settings/TLSFingerprintPlugin.kt +342 -0
  9. package/android/src/main/java/io/capkit/settings/config/TLSFingerprintConfig.kt +102 -0
  10. package/android/src/main/java/io/capkit/settings/error/TLSFingerprintError.kt +114 -0
  11. package/android/src/main/java/io/capkit/settings/error/TLSFingerprintErrorMessages.kt +27 -0
  12. package/android/src/main/java/io/capkit/settings/logger/TLSFingerprintLogger.kt +85 -0
  13. package/android/src/main/java/io/capkit/settings/model/TLSFingerprintResultModel.kt +32 -0
  14. package/android/src/main/java/io/capkit/settings/utils/TLSFingerprintUtils.kt +91 -0
  15. package/android/src/main/res/.gitkeep +0 -0
  16. package/dist/cli/fingerprint.js +163 -0
  17. package/dist/cli/fingerprint.js.map +1 -0
  18. package/dist/docs.json +386 -0
  19. package/dist/esm/cli/fingerprint.d.ts +1 -0
  20. package/dist/esm/cli/fingerprint.js +161 -0
  21. package/dist/esm/cli/fingerprint.js.map +1 -0
  22. package/dist/esm/definitions.d.ts +244 -0
  23. package/dist/esm/definitions.js +42 -0
  24. package/dist/esm/definitions.js.map +1 -0
  25. package/dist/esm/index.d.ts +13 -0
  26. package/dist/esm/index.js +11 -0
  27. package/dist/esm/index.js.map +1 -0
  28. package/dist/esm/version.d.ts +1 -0
  29. package/dist/esm/version.js +3 -0
  30. package/dist/esm/version.js.map +1 -0
  31. package/dist/esm/web.d.ts +33 -0
  32. package/dist/esm/web.js +47 -0
  33. package/dist/esm/web.js.map +1 -0
  34. package/dist/plugin.cjs +107 -0
  35. package/dist/plugin.cjs.map +1 -0
  36. package/dist/plugin.js +110 -0
  37. package/dist/plugin.js.map +1 -0
  38. package/ios/Sources/TLSFingerprintPlugin/TLSFingerprintDelegate.swift +365 -0
  39. package/ios/Sources/TLSFingerprintPlugin/TLSFingerprintImpl.swift +275 -0
  40. package/ios/Sources/TLSFingerprintPlugin/TLSFingerprintPlugin.swift +219 -0
  41. package/ios/Sources/TLSFingerprintPlugin/Version.swift +16 -0
  42. package/ios/Sources/TLSFingerprintPlugin/config/TLSFingerprintConfig.swift +114 -0
  43. package/ios/Sources/TLSFingerprintPlugin/error/TLSFingerprintError.swift +107 -0
  44. package/ios/Sources/TLSFingerprintPlugin/error/TLSFingerprintErrorMessages.swift +30 -0
  45. package/ios/Sources/TLSFingerprintPlugin/logger/TLSFingerprintLogger.swift +69 -0
  46. package/ios/Sources/TLSFingerprintPlugin/model/TLSFingerprintResult.swift +76 -0
  47. package/ios/Sources/TLSFingerprintPlugin/utils/TLSFingerprintUtils.swift +79 -0
  48. package/ios/Tests/TLSFingerprintPluginTests/TLSFingerprintPluginTests.swift +15 -0
  49. package/package.json +131 -0
  50. package/scripts/chmod.mjs +34 -0
  51. package/scripts/sync-version.mjs +68 -0
@@ -0,0 +1,219 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ Capacitor bridge for the TLSFingerprint plugin.
6
+
7
+ Responsibilities:
8
+ - Parse JS input
9
+ - Delegate to TLSFingerprintImpl
10
+ - Resolve or reject CAPPluginCall
11
+ */
12
+ @objc(TLSFingerprintPlugin)
13
+ public final class TLSFingerprintPlugin: CAPPlugin, CAPBridgedPlugin {
14
+
15
+ // MARK: - Plugin metadata
16
+
17
+ /// The unique identifier for the plugin.
18
+ public let identifier = "TLSFingerprintPlugin"
19
+
20
+ /// The name used to reference this plugin in JavaScript.
21
+ public let jsName = "TLSFingerprint"
22
+
23
+ /**
24
+ * A list of methods exposed by this plugin. These methods can be called from the JavaScript side.
25
+ * - `checkCertificate`: Validates an SSL certificate for a given URL.
26
+ * - `checkCertificates`: Validates multiple SSL certificates for given URLs.
27
+ * - `getPluginVersion`: Retrieves the current version of the plugin.
28
+ */
29
+ public let pluginMethods: [CAPPluginMethod] = [
30
+ CAPPluginMethod(name: "checkCertificate", returnType: CAPPluginReturnPromise),
31
+ CAPPluginMethod(name: "checkCertificates", returnType: CAPPluginReturnPromise),
32
+ CAPPluginMethod(name: "getPluginVersion", returnType: CAPPluginReturnPromise)
33
+ ]
34
+
35
+ // MARK: - Properties
36
+
37
+ /// Native implementation containing platform-specific logic.
38
+ private let implementation = TLSFingerprintImpl()
39
+
40
+ /// Configuration instance
41
+ private var config: TLSFingerprintConfig?
42
+
43
+ // MARK: - Lifecycle
44
+
45
+ /**
46
+ Plugin lifecycle entry point.
47
+
48
+ Called once when the plugin is loaded by the Capacitor bridge.
49
+ This is the correct place to:
50
+ - read configuration values
51
+ - initialize native resources
52
+ - configure the implementation instance
53
+ */
54
+ override public func load() {
55
+ // Initialize TLSFingerprintConfig with the correct type
56
+ let cfg = TLSFingerprintConfig(plugin: self)
57
+ self.config = cfg
58
+ implementation.applyConfig(cfg)
59
+
60
+ // Log if verbose logging is enabled
61
+ TLSFingerprintLogger.debug("Plugin loaded. Version: ", PluginVersion.number)
62
+ }
63
+
64
+ // MARK: - Error Mapping
65
+
66
+ /**
67
+ * Rejects the call using standardized error codes from the native TLSFingerprintError enum.
68
+ */
69
+ private func reject(
70
+ _ call: CAPPluginCall,
71
+ error: TLSFingerprintError
72
+ ) {
73
+ // Use the centralized errorCode and message defined in TLSFingerprintError.swift
74
+ call.reject(error.message, error.errorCode)
75
+ }
76
+
77
+ private func handleError(_ call: CAPPluginCall, _ error: Error) {
78
+ if let settingsError = error as? TLSFingerprintError {
79
+ call.reject(settingsError.message, settingsError.errorCode)
80
+ } else {
81
+ reject(call, error: .initFailed(error.localizedDescription))
82
+ }
83
+ }
84
+
85
+ // MARK: - SSL Pinning (single fingerprint)
86
+
87
+ /**
88
+ Validates the SSL certificate of a HTTPS endpoint
89
+ using a single fingerprint.
90
+ */
91
+ @objc func checkCertificate(_ call: CAPPluginCall) {
92
+ let url = call.getString("url", "")
93
+ let fingerprintValue = call.getString("fingerprint")
94
+ let fingerprint =
95
+ fingerprintValue?.isEmpty == false
96
+ ? fingerprintValue
97
+ : nil
98
+
99
+ guard !url.isEmpty else {
100
+ call.reject(
101
+ TLSFingerprintErrorMessages.urlRequired,
102
+ "INVALID_INPUT"
103
+ )
104
+ return
105
+ }
106
+
107
+ guard let urlObj = URL(string: url), let host = urlObj.host, !host.isEmpty else {
108
+ call.reject(
109
+ TLSFingerprintErrorMessages.noHostFoundInUrl,
110
+ "INVALID_INPUT"
111
+ )
112
+ return
113
+ }
114
+
115
+ if let fp = fingerprint, !TLSFingerprintUtils.isValidFingerprintFormat(fp) {
116
+ call.reject(
117
+ TLSFingerprintErrorMessages.invalidFingerprintFormat,
118
+ "INVALID_INPUT"
119
+ )
120
+ return
121
+ }
122
+
123
+ Task {
124
+ do {
125
+ let result =
126
+ try await implementation.checkCertificate(
127
+ urlString: url,
128
+ fingerprintFromArgs: fingerprint
129
+ )
130
+
131
+ // Converting Swift TLSFingerprintResult to JSObject
132
+ call.resolve(result.toDictionary())
133
+ } catch let error as TLSFingerprintError {
134
+ self.reject(call, error: error)
135
+ } catch {
136
+ call.reject(
137
+ error.localizedDescription,
138
+ "INIT_FAILED"
139
+ )
140
+ }
141
+ }
142
+ }
143
+
144
+ // MARK: - SSL Pinning (multiple fingerprints)
145
+
146
+ /**
147
+ Validates the SSL certificate of a HTTPS endpoint
148
+ using multiple allowed fingerprints.
149
+ */
150
+ @objc func checkCertificates(_ call: CAPPluginCall) {
151
+ let url = call.getString("url", "")
152
+
153
+ // Extraction and filtering of optional fingerprints
154
+ let fingerprints =
155
+ call.getArray("fingerprints")?
156
+ .compactMap { $0 as? String }
157
+ .filter { !$0.isEmpty }
158
+
159
+ guard !url.isEmpty else {
160
+ call.reject(
161
+ TLSFingerprintErrorMessages.urlRequired,
162
+ "INVALID_INPUT"
163
+ )
164
+ return
165
+ }
166
+
167
+ guard let urlObj = URL(string: url), let host = urlObj.host, !host.isEmpty else {
168
+ call.reject(
169
+ TLSFingerprintErrorMessages.noHostFoundInUrl,
170
+ "INVALID_INPUT"
171
+ )
172
+ return
173
+ }
174
+
175
+ if let fps = fingerprints {
176
+ for fp in fps {
177
+ if !TLSFingerprintUtils.isValidFingerprintFormat(fp) {
178
+ call.reject(
179
+ TLSFingerprintErrorMessages.invalidFingerprintFormat,
180
+ "INVALID_INPUT"
181
+ )
182
+ return
183
+ }
184
+ }
185
+ }
186
+
187
+ Task {
188
+ do {
189
+ let result =
190
+ try await implementation.checkCertificates(
191
+ urlString: url,
192
+ fingerprintsFromArgs:
193
+ fingerprints?.isEmpty == false
194
+ ? fingerprints
195
+ : nil
196
+ )
197
+
198
+ call.resolve(result.toDictionary())
199
+ } catch let error as TLSFingerprintError {
200
+ self.reject(call, error: error)
201
+ } catch {
202
+ call.reject(
203
+ error.localizedDescription,
204
+ "INIT_FAILED"
205
+ )
206
+ }
207
+ }
208
+ }
209
+
210
+ // MARK: - Version
211
+
212
+ /// Retrieves the plugin version synchronized from package.json.
213
+ @objc func getPluginVersion(_ call: CAPPluginCall) {
214
+ // Standardized enum name across all CapKit plugins
215
+ call.resolve([
216
+ "version": PluginVersion.number
217
+ ])
218
+ }
219
+ }
@@ -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"
14
+ */
15
+ public static let number = "8.0.0"
16
+ }
@@ -0,0 +1,114 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ Plugin configuration container.
6
+
7
+ This struct is responsible for reading and exposing
8
+ static configuration values defined under the
9
+ `TLSFingerprint` key in capacitor.config.ts.
10
+
11
+ Configuration rules:
12
+ - Read once during plugin initialization
13
+ - Treated as immutable runtime input
14
+ - Accessible only from native code
15
+ */
16
+ public struct TLSFingerprintConfig {
17
+
18
+ // MARK: - Configuration Keys
19
+
20
+ /**
21
+ Centralized definition of configuration keys.
22
+ Avoids string duplication and typos.
23
+ */
24
+ private enum Keys {
25
+ static let verboseLogging = "verboseLogging"
26
+ static let fingerprint = "fingerprint"
27
+ static let fingerprints = "fingerprints"
28
+ static let excludedDomains = "excludedDomains"
29
+ }
30
+
31
+ // MARK: - Public Configuration Values
32
+
33
+ /**
34
+ Enables verbose native logging.
35
+
36
+ When enabled, the plugin prints additional
37
+ debug information to the Xcode console.
38
+
39
+ Default: false
40
+ */
41
+ public let verboseLogging: Bool
42
+
43
+ /**
44
+ Default SHA-256 fingerprint used by `checkCertificate()`
45
+ when no fingerprint is provided at runtime.
46
+ */
47
+ public let fingerprint: String?
48
+
49
+ /**
50
+ Default SHA-256 fingerprints used by `checkCertificates()`
51
+ when no fingerprints are provided at runtime.
52
+ */
53
+ public let fingerprints: [String]
54
+
55
+ /**
56
+ Domains or URL prefixes excluded from SSL pinning.
57
+
58
+ Any request whose host matches one of these values
59
+ MUST bypass SSL pinning checks.
60
+ */
61
+ public let excludedDomains: [String]
62
+
63
+ // MARK: - Defaults
64
+
65
+ private static let defaultVerboseLogging: Bool = false
66
+ private static let defaultFingerprint: String? = nil
67
+ private static let defaultFingerprints: [String] = []
68
+ private static let defaultExcludedDomains: [String] = []
69
+
70
+ // MARK: - Initialization
71
+
72
+ /**
73
+ Initializes the configuration by reading values
74
+ from the Capacitor PluginConfig.
75
+
76
+ - Parameter plugin: The CAPPlugin instance used
77
+ to access typed configuration values.
78
+ */
79
+ init(plugin: CAPPlugin) {
80
+ let config = plugin.getConfig()
81
+
82
+ // Verbose logging flag
83
+ self.verboseLogging =
84
+ config.getBoolean(
85
+ Keys.verboseLogging,
86
+ Self.defaultVerboseLogging
87
+ )
88
+
89
+ // Synchronize logger state
90
+ TLSFingerprintLogger.verbose = self.verboseLogging
91
+
92
+ // Single fingerprint (optional)
93
+ if let value = config.getString(Keys.fingerprint),
94
+ !value.isEmpty {
95
+ self.fingerprint = value
96
+ } else {
97
+ self.fingerprint = Self.defaultFingerprint
98
+ }
99
+
100
+ // Multiple fingerprints (optional)
101
+ self.fingerprints =
102
+ config.getArray(Keys.fingerprints)?
103
+ .compactMap { $0 as? String }
104
+ .filter { !$0.isEmpty }
105
+ ?? Self.defaultFingerprints
106
+
107
+ // Excluded domains (optional)
108
+ self.excludedDomains =
109
+ config.getArray(Keys.excludedDomains)?
110
+ .compactMap { $0 as? String }
111
+ .filter { !$0.isEmpty }
112
+ ?? Self.defaultExcludedDomains
113
+ }
114
+ }
@@ -0,0 +1,107 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Native error model for the TLSFingerprint plugin (iOS).
5
+
6
+ This enum represents all error categories that can be
7
+ produced by the native implementation layer.
8
+
9
+ Architectural rules:
10
+ - Must NOT reference Capacitor
11
+ - Must NOT reference JavaScript
12
+ - Must be throwable from the Impl layer
13
+ - Mapping to JS-facing error codes happens ONLY in the Plugin layer
14
+ */
15
+ enum TLSFingerprintError: Error {
16
+
17
+ /// Feature or capability is not available on this device or configuration
18
+ case unavailable(String)
19
+
20
+ /// The user cancelled an interactive flow
21
+ case cancelled(String)
22
+
23
+ /// Required permission was denied or not granted
24
+ case permissionDenied(String)
25
+
26
+ /// Plugin failed to initialize or perform a required operation
27
+ case initFailed(String)
28
+
29
+ /// The input provided to the plugin method is invalid or malformed
30
+ case invalidInput(String)
31
+
32
+ /// Invalid or unsupported input was provided
33
+ case unknownType(String)
34
+
35
+ /// The requested resource does not exist
36
+ case notFound(String)
37
+
38
+ /// The operation conflicts with the current state
39
+ case conflict(String)
40
+
41
+ /// The operation did not complete within the expected time
42
+ case timeout(String)
43
+
44
+ /// The certificate fingerprint did not match any expected fingerprint
45
+ case pinningFailed(String)
46
+
47
+ /// SSL/TLS specific error (certificate expired, handshake failure, etc.)
48
+ case sslError(String)
49
+
50
+ /// Invalid or malformed configuration
51
+ case invalidConfig(String)
52
+
53
+ // MARK: - Human-readable message
54
+
55
+ /**
56
+ Human-readable error message.
57
+
58
+ This message is intended to be passed verbatim
59
+ to JavaScript via `call.reject(message, code)`.
60
+ */
61
+ var message: String {
62
+ switch self {
63
+ case .unavailable(let message):
64
+ return message
65
+ case .cancelled(let message):
66
+ return message
67
+ case .permissionDenied(let message):
68
+ return message
69
+ case .initFailed(let message):
70
+ return message
71
+ case .invalidInput(let message):
72
+ return message
73
+ case .unknownType(let message):
74
+ return message
75
+ case .notFound(let message):
76
+ return message
77
+ case .conflict(let message):
78
+ return message
79
+ case .timeout(let message):
80
+ return message
81
+ case .pinningFailed(let message):
82
+ return message
83
+ case .sslError(let message):
84
+ return message
85
+ case .invalidConfig(let message):
86
+ return message
87
+ }
88
+ }
89
+
90
+ /// Standardized error code string for JS rejection.
91
+ var errorCode: String {
92
+ switch self {
93
+ case .unavailable: return "UNAVAILABLE"
94
+ case .cancelled: return "CANCELLED"
95
+ case .permissionDenied: return "PERMISSION_DENIED"
96
+ case .initFailed: return "INIT_FAILED"
97
+ case .invalidInput: return "INVALID_INPUT"
98
+ case .unknownType: return "UNKNOWN_TYPE"
99
+ case .notFound: return "NOT_FOUND"
100
+ case .conflict: return "CONFLICT"
101
+ case .timeout: return "TIMEOUT"
102
+ case .pinningFailed: return "PINNING_FAILED"
103
+ case .sslError: return "SSL_ERROR"
104
+ case .invalidConfig: return "INVALID_INPUT"
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,30 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Canonical error messages shared across platforms.
5
+
6
+ These strings must remain byte-identical on iOS and Android.
7
+ */
8
+ enum TLSFingerprintErrorMessages {
9
+ static let urlRequired = "url is required"
10
+ static let invalidUrlMustBeHttps = "Invalid URL. Must be https."
11
+ static let invalidUrl = "Invalid URL."
12
+ static let noFingerprintsProvided = "No fingerprints provided"
13
+ static let noHostFoundInUrl = "No host found in URL"
14
+ static let invalidFingerprintFormat = "Invalid fingerprint format"
15
+ static let unsupportedHost = "Unsupported host: %@"
16
+ static let pinningFailed = "Pinning failed"
17
+ static let excludedDomain = "Excluded domain"
18
+ static let timeout = "Timeout"
19
+ static let networkError = "Network error"
20
+ static let internalError = "Internal error"
21
+ static let invalidConfig = "Invalid configuration: %@"
22
+
23
+ static func unsupportedHost(_ value: String) -> String {
24
+ return String(format: unsupportedHost, value)
25
+ }
26
+
27
+ static func invalidConfig(_ value: String) -> String {
28
+ return String(format: invalidConfig, value)
29
+ }
30
+ }
@@ -0,0 +1,69 @@
1
+ import Capacitor
2
+
3
+ /**
4
+ Centralized logger for the TLSFingerprint 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 TLSFingerprintLogger {
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("⚡️ TLSFingerprint -", 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
+ }
@@ -0,0 +1,76 @@
1
+ import Foundation
2
+
3
+ /**
4
+ Canonical result model for TLS fingerprint validation operations on iOS.
5
+
6
+ This struct is exchanged between the native implementation (TLSFingerprintImpl)
7
+ and the bridge (TLSFingerprintPlugin) and serialized to JavaScript via a JSObject.
8
+
9
+ Fields mirror the public JS payload:
10
+ - actualFingerprint: server fingerprint used for matching
11
+ - fingerprintMatched: whether the fingerprint check succeeded (true) or not (false)
12
+ - matchedFingerprint: the fingerprint that matched (only present for fingerprint mode)
13
+ - excludedDomain: indicates an excluded-domain bypass (true when applicable)
14
+ - mode: active mode: "fingerprint" | "excluded"
15
+ - error: human-readable error (empty on success/match)
16
+ - errorCode: canonical error code string (empty on success)
17
+ */
18
+ struct TLSFingerprintResult {
19
+ let actualFingerprint: String?
20
+ let fingerprintMatched: Bool
21
+ let matchedFingerprint: String?
22
+ let excludedDomain: Bool?
23
+ let mode: String?
24
+ let error: String?
25
+ let errorCode: String?
26
+
27
+ init(
28
+ actualFingerprint: String? = nil,
29
+ fingerprintMatched: Bool,
30
+ matchedFingerprint: String? = nil,
31
+ excludedDomain: Bool? = nil,
32
+ mode: String? = nil,
33
+ error: String? = nil,
34
+ errorCode: String? = nil
35
+ ) {
36
+ self.actualFingerprint = actualFingerprint
37
+ self.fingerprintMatched = fingerprintMatched
38
+ self.matchedFingerprint = matchedFingerprint
39
+ self.excludedDomain = excludedDomain
40
+ self.mode = mode
41
+ self.error = error
42
+ self.errorCode = errorCode
43
+ }
44
+
45
+ func toDictionary() -> [String: Any] {
46
+ var dict: [String: Any] = [
47
+ "fingerprintMatched": fingerprintMatched
48
+ ]
49
+
50
+ if let actualFingerprint = actualFingerprint {
51
+ dict["actualFingerprint"] = actualFingerprint
52
+ }
53
+
54
+ if let matchedFingerprint = matchedFingerprint {
55
+ dict["matchedFingerprint"] = matchedFingerprint
56
+ }
57
+
58
+ if let excludedDomain = excludedDomain {
59
+ dict["excludedDomain"] = excludedDomain
60
+ }
61
+
62
+ if let mode = mode {
63
+ dict["mode"] = mode
64
+ }
65
+
66
+ if let error = error {
67
+ dict["error"] = error
68
+ }
69
+
70
+ if let errorCode = errorCode {
71
+ dict["errorCode"] = errorCode
72
+ }
73
+
74
+ return dict
75
+ }
76
+ }