field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | LICENSE

FieldTelemetryUITestProbe.swift (4474B)


      1 import Foundation
      2 import RadrootsKit
      3 
      4 enum FieldTelemetryUITestProbe {
      5     private static let enabledKey = "RADROOTS_FIELD_IOS_UI_TEST_TELEMETRY_PROBE"
      6     private static let redactionPolicy = RadrootsTelemetryRedactionPolicy.default
      7 
      8     static var isRequested: Bool {
      9         ProcessInfo.processInfo.environment[enabledKey] == "true"
     10     }
     11 
     12     static func value(recordedBy telemetry: FieldTelemetry) async -> String? {
     13         guard isRequested else {
     14             return nil
     15         }
     16         let events = await telemetry.recordedEventsForUITest()
     17         return value(events: events)
     18     }
     19 
     20     private static func value(events: [RadrootsTelemetryEvent]) -> String {
     21         let eventNames = Set(events.map(\.name))
     22         let fieldKeys = Set(events.flatMap { event in
     23             event.fields.map(\.key)
     24         })
     25         let values = events.flatMap(stringValues)
     26         return [
     27             "event_count=\(events.count)",
     28             "event_names=\(eventNames.sorted().joined(separator: ","))",
     29             "field_keys=\(fieldKeys.sorted().joined(separator: ","))",
     30             "runtime_logging_seen=\(eventNames.contains("field_ios.runtime.logging_initialized"))",
     31             "startup_success_seen=\(eventNames.contains("field_ios.startup.success"))",
     32             "relay_status_seen=\(eventNames.contains("field_ios.relay.status_changed"))",
     33             "identity_create_seen=\(eventNames.contains("field_ios.identity_custody.create"))",
     34             "user_presence_save_identity_seen=\(eventNames.contains("field_ios.user_presence.save_identity"))",
     35             "document_diagnostics_export_seen=\(eventNames.contains("field_ios.document_interchange.diagnostics_export"))",
     36             "document_relay_config_export_seen=\(eventNames.contains("field_ios.document_interchange.relay_config_export"))",
     37             "document_relay_config_import_seen=\(eventNames.contains("field_ios.document_interchange.relay_config_import"))",
     38             "document_public_share_prepare_seen=\(eventNames.contains("field_ios.document_interchange.public_share_prepare"))",
     39             "capture_support_refreshed_seen=\(eventNames.contains("field_ios.capture.support_refreshed"))",
     40             "capture_import_photo_seen=\(eventNames.contains("field_ios.capture.import_photo"))",
     41             "capture_scan_document_seen=\(eventNames.contains("field_ios.capture.scan_document"))",
     42             "external_action_open_seen=\(eventNames.contains("field_ios.external_action.open"))",
     43             "unsafe_values_present=\(values.contains(where: containsUnsafeValue))",
     44             "relay_url_values_present=\(values.contains(where: containsRelayURL))",
     45             "secret_like_values_present=\(values.contains(where: containsSecretLikeValue))",
     46             "path_like_values_present=\(values.contains(where: containsPathLikeValue))",
     47             "npub_values_present=\(values.contains(where: containsNpubValue))"
     48         ].joined(separator: ";")
     49     }
     50 
     51     private static func stringValues(from event: RadrootsTelemetryEvent) -> [String] {
     52         var values = event.message.map { [$0] } ?? []
     53         values.append(contentsOf: event.fields.map { field in
     54             field.value.renderedValue
     55         })
     56         return values
     57     }
     58 
     59     private static func containsUnsafeValue(_ value: String) -> Bool {
     60         redactionPolicy.containsUnsafeValue(value)
     61             || containsRelayURL(value)
     62             || containsSecretLikeValue(value)
     63             || containsPathLikeValue(value)
     64             || containsNpubValue(value)
     65     }
     66 
     67     private static func containsRelayURL(_ value: String) -> Bool {
     68         let normalized = value.lowercased()
     69         return normalized.contains("ws://") || normalized.contains("wss://")
     70     }
     71 
     72     private static func containsSecretLikeValue(_ value: String) -> Bool {
     73         let normalized = value.lowercased()
     74         return normalized.contains("nsec")
     75             || normalized.range(of: "[a-f0-9]{64}", options: .regularExpression) != nil
     76     }
     77 
     78     private static func containsPathLikeValue(_ value: String) -> Bool {
     79         let normalized = value.lowercased()
     80         return normalized.contains("/users/")
     81             || normalized.contains("/private/var/")
     82             || normalized.contains("/var/mobile/containers/")
     83             || normalized.contains("/var/folders/")
     84             || normalized.contains("file:///")
     85     }
     86 
     87     private static func containsNpubValue(_ value: String) -> Bool {
     88         value.lowercased().contains("npub")
     89     }
     90 }