diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift index 39d9a99..2c5ee7d 100644 --- a/Sources/Packages/Package.swift +++ b/Sources/Packages/Package.swift @@ -1,12 +1,14 @@ -// swift-tools-version:5.9 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription +let secretiveDefaults: [PackageDescription.SwiftSetting]? = [.swiftLanguageMode(.v6), .unsafeFlags(["-warnings-as-errors"])] + let package = Package( name: "SecretivePackages", platforms: [ - .macOS(.v12) + .macOS(.v13) ], products: [ .library( @@ -34,27 +36,27 @@ let package = Package( .target( name: "SecretKit", dependencies: [], - swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] + swiftSettings: secretiveDefaults ), .testTarget( name: "SecretKitTests", dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"], - swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] + swiftSettings: secretiveDefaults ), .target( name: "SecureEnclaveSecretKit", dependencies: ["SecretKit"], - swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] + swiftSettings: secretiveDefaults ), .target( name: "SmartCardSecretKit", dependencies: ["SecretKit"], - swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] + swiftSettings: secretiveDefaults ), .target( name: "SecretAgentKit", dependencies: ["SecretKit", "SecretAgentKitHeaders"], - swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])] + swiftSettings: secretiveDefaults ), .systemLibrary( name: "SecretAgentKitHeaders" @@ -73,3 +75,4 @@ let package = Package( ), ] ) + diff --git a/Sources/Packages/Sources/Brief/Release.swift b/Sources/Packages/Sources/Brief/Release.swift index 847dffe..ffc3293 100644 --- a/Sources/Packages/Sources/Brief/Release.swift +++ b/Sources/Packages/Sources/Brief/Release.swift @@ -1,7 +1,7 @@ import Foundation /// A release is a representation of a downloadable update. -public struct Release: Codable { +public struct Release: Codable, Sendable { /// The user-facing name of the release. Typically "Secretive 1.2.3" public let name: String diff --git a/Sources/Packages/Sources/Brief/Updater.swift b/Sources/Packages/Sources/Brief/Updater.swift index 6c88d82..8a0ffd3 100644 --- a/Sources/Packages/Sources/Brief/Updater.swift +++ b/Sources/Packages/Sources/Brief/Updater.swift @@ -2,7 +2,7 @@ import Foundation import Combine /// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version. -public final class Updater: ObservableObject, UpdaterProtocol { +@MainActor public final class Updater: ObservableObject, UpdaterProtocol, Sendable { @Published public var update: Release? public let testBuild: Bool @@ -18,27 +18,27 @@ public final class Updater: ObservableObject, UpdaterProtocol { /// - checkFrequency: The interval at which the Updater should check for updates. Subject to a tolerance of 1 hour. /// - osVersion: The current OS version. /// - currentVersion: The current version of the app that is running. - public init(checkOnLaunch: Bool, checkFrequency: TimeInterval = Measurement(value: 24, unit: UnitDuration.hours).converted(to: .seconds).value, osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion), currentVersion: SemVer = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")) { + public init(checkOnLaunch: Bool, checkFrequency: Duration = .seconds(24*60*60), osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion), currentVersion: SemVer = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")) { self.osVersion = osVersion self.currentVersion = currentVersion testBuild = currentVersion == SemVer("0.0.0") - if checkOnLaunch { - // Don't do a launch check if the user hasn't seen the setup prompt explaining updater yet. - checkForUpdates() + Task { + if checkOnLaunch { + // Don't do a launch check if the user hasn't seen the setup prompt explaining updater yet. + await checkForUpdates() + } + while true { + try await Task.sleep(for: checkFrequency, tolerance: .seconds(60*60)) + await checkForUpdates() + } } - let timer = Timer.scheduledTimer(withTimeInterval: checkFrequency, repeats: true) { _ in - self.checkForUpdates() - } - timer.tolerance = 60*60 } /// Manually trigger an update check. - public func checkForUpdates() { - URLSession.shared.dataTask(with: Constants.updateURL) { data, _, _ in - guard let data = data else { return } - guard let releases = try? JSONDecoder().decode([Release].self, from: data) else { return } - self.evaluate(releases: releases) - }.resume() + public func checkForUpdates() async { + guard let (data, _) = try? await URLSession.shared.data(from: Constants.updateURL) else { return } + guard let releases = try? JSONDecoder().decode([Release].self, from: data) else { return } + evaluate(releases: releases) } /// Ignores a specified release. `update` will be nil if the user has ignored the latest available release. diff --git a/Sources/Packages/Sources/Brief/UpdaterProtocol.swift b/Sources/Packages/Sources/Brief/UpdaterProtocol.swift index a5c5edc..5942845 100644 --- a/Sources/Packages/Sources/Brief/UpdaterProtocol.swift +++ b/Sources/Packages/Sources/Brief/UpdaterProtocol.swift @@ -5,7 +5,7 @@ import Combine public protocol UpdaterProtocol: ObservableObject { /// The latest update - var update: Release? { get } + @MainActor var update: Release? { get } /// A boolean describing whether or not the current build of the app is a "test" build (ie, a debug build or otherwise special build) var testBuild: Bool { get } diff --git a/Sources/Packages/Sources/SecretAgentKit/Agent.swift b/Sources/Packages/Sources/SecretAgentKit/Agent.swift index ed1654b..c27c94b 100644 --- a/Sources/Packages/Sources/SecretAgentKit/Agent.swift +++ b/Sources/Packages/Sources/SecretAgentKit/Agent.swift @@ -5,7 +5,7 @@ import SecretKit import AppKit /// The `Agent` is an implementation of an SSH agent. It manages coordination and access between a socket, traces requests, notifies witnesses and passes requests to stores. -public final class Agent { +public actor Agent { private let storeList: SecretStoreList private let witness: SigningWitness? @@ -35,7 +35,7 @@ extension Agent { /// - writer: A ``FileHandleWriter`` to write the response to. /// - Return value: /// - Boolean if data could be read - @discardableResult @Sendable public func handle(reader: FileHandleReader, writer: FileHandleWriter) async -> Bool { + @discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) async -> Bool { logger.debug("Agent handling new data") let data = Data(reader.availableData) guard data.count > 4 else { return false} diff --git a/Sources/Packages/Sources/SecretKit/Erasers/AnySecret.swift b/Sources/Packages/Sources/SecretKit/Erasers/AnySecret.swift index f6e8bef..88991dc 100644 --- a/Sources/Packages/Sources/SecretKit/Erasers/AnySecret.swift +++ b/Sources/Packages/Sources/SecretKit/Erasers/AnySecret.swift @@ -1,7 +1,7 @@ import Foundation /// Type eraser for Secret. -public struct AnySecret: Secret { +public struct AnySecret: Secret, @unchecked Sendable { let base: Any private let hashable: AnyHashable diff --git a/Sources/Packages/Sources/SecretKit/Types/Secret.swift b/Sources/Packages/Sources/SecretKit/Types/Secret.swift index 8f9656c..e4cdb22 100644 --- a/Sources/Packages/Sources/SecretKit/Types/Secret.swift +++ b/Sources/Packages/Sources/SecretKit/Types/Secret.swift @@ -1,7 +1,7 @@ import Foundation /// The base protocol for describing a Secret -public protocol Secret: Identifiable, Hashable { +public protocol Secret: Identifiable, Hashable, Sendable { /// A user-facing string identifying the Secret. var name: String { get } @@ -17,7 +17,7 @@ public protocol Secret: Identifiable, Hashable { } /// The type of algorithm the Secret uses. Currently, only elliptic curve algorithms are supported. -public enum Algorithm: Hashable { +public enum Algorithm: Hashable, Sendable { case ellipticCurve case rsa diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift index d079bf3..b5f8fa3 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift @@ -8,7 +8,7 @@ import SecretKit extension SecureEnclave { /// An implementation of Store backed by the Secure Enclave. - public final class Store: SecretStoreModifiable { + public final class Store: SecretStoreModifiable, Sendable { public var isAvailable: Bool { // For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false @@ -18,14 +18,18 @@ extension SecureEnclave { } public let id = UUID() public let name = String(localized: "secure_enclave") - @Published public private(set) var secrets: [Secret] = [] + @MainActor public private(set) var secrets: [Secret] = [] { + willSet { + self.objectWillChange.send() + } + } - private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:] + @MainActor private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:] /// Initializes a Store. public init() { - DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { [reload = reloadSecretsInternal(notifyAgent:)] _ in - reload(false) + DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in + self.reloadSecretsInternal(notifyAgent: false) } loadSecrets() } @@ -211,7 +215,7 @@ extension SecureEnclave.Store { /// Reloads all secrets from the store. /// - Parameter notifyAgent: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well. - @Sendable private func reloadSecretsInternal(notifyAgent: Bool = true) { + private func reloadSecretsInternal(notifyAgent: Bool = true) { let before = secrets secrets.removeAll() loadSecrets() @@ -304,8 +308,8 @@ extension SecureEnclave.Store { extension SecureEnclave { enum Constants { - static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! as CFData - static let keyType = kSecAttrKeyTypeECSECPrimeRandom + static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! + static let keyType = kSecAttrKeyTypeECSECPrimeRandom as String static let unauthenticatedThreshold: TimeInterval = 0.05 } diff --git a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift index f6f83c8..a90426c 100644 --- a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift +++ b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift @@ -20,13 +20,13 @@ extension SmartCard { /// Initializes a Store. public init() { tokenID = watcher.nonSecureEnclaveTokens.first - watcher.setInsertionHandler { [reload = reloadSecretsInternal] string in + watcher.setInsertionHandler { string in guard self.tokenID == nil else { return } guard !string.contains("setoken") else { return } self.tokenID = string DispatchQueue.main.async { - reload() +// reload() } self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string) } @@ -117,7 +117,7 @@ extension SmartCard { extension SmartCard.Store { - @Sendable private func reloadSecretsInternal() { + private func reloadSecretsInternal() { self.isAvailable = self.tokenID != nil let before = self.secrets self.secrets.removeAll() diff --git a/Sources/SecretAgent/Notifier.swift b/Sources/SecretAgent/Notifier.swift index 69b29bb..d442623 100644 --- a/Sources/SecretAgent/Notifier.swift +++ b/Sources/SecretAgent/Notifier.swift @@ -5,7 +5,7 @@ import SecretKit import SecretAgentKit import Brief -class Notifier { +final class Notifier: Sendable { private let notificationDelegate = NotificationDelegate() @@ -129,7 +129,8 @@ extension Notifier { } -class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { +// FIXME: UNCHECKED SENDABLE +@MainActor final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate, @unchecked Sendable { fileprivate var release: Release? fileprivate var ignore: ((Release) -> Void)? @@ -138,7 +139,7 @@ class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { fileprivate var pendingPersistableStores: [String: AnySecretStore] = [:] fileprivate var pendingPersistableSecrets: [String: AnySecret] = [:] - func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) { + nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) { } diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj index 6a67328..5501209 100644 --- a/Sources/Secretive.xcodeproj/project.pbxproj +++ b/Sources/Secretive.xcodeproj/project.pbxproj @@ -619,6 +619,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -678,6 +679,7 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -700,13 +702,12 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -729,13 +730,12 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; }; name = Release; }; @@ -755,7 +755,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretiveTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive"; }; name = Debug; @@ -776,7 +775,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretiveTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive"; }; name = Release; @@ -844,6 +842,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; }; name = Test; }; @@ -863,12 +862,11 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; }; name = Test; }; @@ -889,7 +887,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretiveTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Secretive.app/Contents/MacOS/Secretive"; }; name = Test; @@ -908,12 +905,11 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; }; name = Test; }; @@ -933,12 +929,11 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -959,13 +954,12 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Sources/Secretive/Localizable.xcstrings b/Sources/Secretive/Localizable.xcstrings index 2f06bcd..5e744f1 100644 --- a/Sources/Secretive/Localizable.xcstrings +++ b/Sources/Secretive/Localizable.xcstrings @@ -2,6 +2,7 @@ "sourceLanguage" : "en", "strings" : { "agent_not_running_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -48,6 +49,7 @@ } }, "agent_running_notice_detail_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -94,6 +96,7 @@ } }, "agent_running_notice_detail_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -140,6 +143,7 @@ } }, "agent_running_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -186,6 +190,7 @@ } }, "agent_setup_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -232,6 +237,7 @@ } }, "app_menu_help_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -278,6 +284,7 @@ } }, "app_menu_new_secret_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -324,6 +331,7 @@ } }, "app_menu_setup_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -370,6 +378,7 @@ } }, "app_not_in_applications_notice_detail_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -416,6 +425,7 @@ } }, "app_not_in_applications_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -798,6 +808,7 @@ } }, "copyable_click_to_copy_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -844,6 +855,7 @@ } }, "copyable_copied" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -890,6 +902,7 @@ } }, "create_secret_cancel_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -936,6 +949,7 @@ } }, "create_secret_create_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -982,6 +996,7 @@ } }, "create_secret_name_label" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1028,6 +1043,7 @@ } }, "create_secret_name_placeholder" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1074,6 +1090,7 @@ } }, "create_secret_notify_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1120,6 +1137,7 @@ } }, "create_secret_notify_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1166,6 +1184,7 @@ } }, "create_secret_require_authentication_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1212,6 +1231,7 @@ } }, "create_secret_require_authentication_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1258,6 +1278,7 @@ } }, "create_secret_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1304,6 +1325,7 @@ } }, "delete_confirmation_cancel_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1350,6 +1372,7 @@ } }, "delete_confirmation_confirm_name_label" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1396,6 +1419,7 @@ } }, "delete_confirmation_delete_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1442,6 +1466,7 @@ } }, "delete_confirmation_description_%@_%@" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1488,6 +1513,7 @@ } }, "delete_confirmation_title_%@" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1534,6 +1560,7 @@ } }, "empty_store_modifiable_click_here_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1580,6 +1607,7 @@ } }, "empty_store_modifiable_click_here_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1626,6 +1654,7 @@ } }, "empty_store_modifiable_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1672,6 +1701,7 @@ } }, "empty_store_nonmodifiable_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1718,6 +1748,7 @@ } }, "empty_store_nonmodifiable_supported_key_types" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1764,6 +1795,7 @@ } }, "empty_store_nonmodifiable_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1810,6 +1842,7 @@ } }, "no_secure_storage_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1856,6 +1889,7 @@ } }, "no_secure_storage_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1902,6 +1936,7 @@ } }, "no_secure_storage_yubico_link" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2044,6 +2079,7 @@ } }, "rename_cancel_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2084,6 +2120,7 @@ } }, "rename_rename_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2124,6 +2161,7 @@ } }, "rename_title_%@" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2164,6 +2202,7 @@ } }, "secret_detail_md5_fingerprint_label" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2204,6 +2243,7 @@ } }, "secret_detail_public_key_label" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2244,6 +2284,7 @@ } }, "secret_detail_public_key_path_label" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2284,6 +2325,7 @@ } }, "secret_detail_sha256_fingerprint_label" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2324,6 +2366,7 @@ } }, "secret_list_delete_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2364,6 +2407,7 @@ } }, "secret_list_rename_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2445,6 +2489,7 @@ } }, "setup_agent_activity_monitor_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2485,6 +2530,7 @@ } }, "setup_agent_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2525,6 +2571,7 @@ } }, "setup_agent_install_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2565,6 +2612,7 @@ } }, "setup_agent_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2605,6 +2653,7 @@ } }, "setup_ssh_add_for_me_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2645,6 +2694,7 @@ } }, "setup_ssh_add_to_config_button_%@" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2685,6 +2735,7 @@ } }, "setup_ssh_added_manually_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2725,6 +2776,7 @@ } }, "setup_ssh_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2765,6 +2817,7 @@ } }, "setup_ssh_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2805,6 +2858,7 @@ } }, "setup_step_complete_symbol" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2845,6 +2899,7 @@ } }, "setup_third_party_faq_link" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2885,6 +2940,7 @@ } }, "setup_updates_description" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2925,6 +2981,7 @@ } }, "setup_updates_ok" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2965,6 +3022,7 @@ } }, "setup_updates_readmore" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3005,6 +3063,7 @@ } }, "setup_updates_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3211,6 +3270,7 @@ } }, "update_critical_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3251,6 +3311,7 @@ } }, "update_ignore_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3291,6 +3352,7 @@ } }, "update_normal_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3541,6 +3603,7 @@ } }, "update_release_notes_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3581,6 +3644,7 @@ } }, "update_test_notice_title" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3621,6 +3685,7 @@ } }, "update_update_button" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -3661,6 +3726,7 @@ } }, "update_version_name_%@" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : {