mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-08-20 08:09:01 +02:00
Adding host reader xpc service and connection info
This commit is contained in:
@@ -45,7 +45,6 @@ let package = Package(
|
||||
targets: ["SSHProtocolKit"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apple/swift-system", from: "1.6.1"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
||||
@@ -5548,6 +5548,378 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth_context_request_signature_for_connection_description" : {
|
||||
"comment" : "When the user performs a signature action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used, and where the request is coming from. The first placeholder is the name of the app requesting the operation. The second placeholder is the name of the secret.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"af" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "signa una petición de “%1$(appName)@” usant el secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "eine Anfrage von “%1$(appName)@” mit dem Secret “%2$(targetName)@” signieren"
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sign a request from “%1$(appName)@” to connect to “%2$(targetName)@” using secret “%3$(secretName)@”"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "allekirjoita pyyntö lähteestä “%1$(appName)@” käyttäen salaisuutta “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "signer une requête de “%1$(appName)@” en utilisant le secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "firma la richiesta di “%1$(appName)@” usando il Segreto “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "シークレット“%2$(targetName)@”を使って“%1$(appName)@”の要求に署名します"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "비밀 “%2$(targetName)@”를 사용해서 “%1$(appName)@”의 요청에 서명"
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "podpisz zapytanie od “%1$(appName)@” za pomocą sekretu “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "assinar requisição a partir do “%1$(appName)@” utilizando o segredo “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "подписать запрос от “%1$(appName)@” используя секрет “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(appName)@” using secret “%2$(targetName)@”"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "用密钥串“%2$(targetName)@”认证来自“%1$(appName)@”的请求"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "用Secret「%2$(targetName)@」認證来自「%1$(appName)@」的请求"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth_context_request_signature_for_signature_description" : {
|
||||
"comment" : "When the user performs a signature action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used, and where the request is coming from. The first placeholder is the name of the app requesting the operation. The second placeholder is the name of the secret.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"af" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "signa una petición de “%1$(namespace)@” usant el secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "eine Anfrage von “%1$(namespace)@” mit dem Secret “%2$(appName)@” signieren"
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sign a %1$(namespace)@“ request from “%2$(appName)@” using secret “%3$(secretName)@”"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "allekirjoita pyyntö lähteestä “%1$(namespace)@” käyttäen salaisuutta “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "signer une requête de “%1$(namespace)@” en utilisant le secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "firma la richiesta di “%1$(namespace)@” usando il Segreto “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "シークレット“%2$(appName)@”を使って“%1$(namespace)@”の要求に署名します"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "비밀 “%2$(appName)@”를 사용해서 “%1$(namespace)@”의 요청에 서명"
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "podpisz zapytanie od “%1$(namespace)@” za pomocą sekretu “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "assinar requisição a partir do “%1$(namespace)@” utilizando o segredo “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "подписать запрос от “%1$(namespace)@” используя секрет “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "sign a request from “%1$(namespace)@” using secret “%2$(appName)@”"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "用密钥串“%2$(appName)@”认证来自“%1$(namespace)@”的请求"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "用Secret「%2$(appName)@」認證来自「%1$(namespace)@」的请求"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"certificate_detail_critical_options_label" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -23673,6 +24045,192 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"signed_connection_notification_description" : {
|
||||
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the description, showing which secret was used. The placeholder is the name of the secret.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"af" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Usant secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Benutze Secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Using secret %1$(secretName)@ connecting to %2$(username)@@%3$(host)@"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Käytetään salaisuutta %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Utilisation du secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Usato il Segreto %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "%1$(secretName)@を使用中"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "비밀 %1$(secretName)@ 사용됨"
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Użyto sekretu %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Utilizando o segredo %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Используя секрет %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "使用密钥串 %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "使用Secret「%1$(secretName)@」"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"signed_notification_description" : {
|
||||
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the description, showing which secret was used. The placeholder is the name of the secret.",
|
||||
"extractionState" : "manual",
|
||||
@@ -24045,6 +24603,192 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"signed_signature_notification_description" : {
|
||||
"comment" : "When the user performs an action using a secret, they're shown a notification describing what happened. This is the description, showing which secret was used. The placeholder is the name of the secret.",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"af" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Usant secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Benutze Secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Using secret %1$(secretName)@ for %2$(namespace)@"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Käytetään salaisuutta %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Utilisation du secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Usato il Segreto %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "%1$(secretName)@を使用中"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "비밀 %1$(secretName)@ 사용됨"
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Użyto sekretu %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Utilizando o segredo %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "Используя секрет %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Using secret %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "使用密钥串 %1$(secretName)@"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "使用Secret「%1$(secretName)@」"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"smart_card" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -26826,5 +27570,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
"version" : "1.2"
|
||||
}
|
||||
@@ -34,9 +34,9 @@ import XPCWrappers
|
||||
) {
|
||||
self.osVersion = osVersion
|
||||
self.currentVersion = currentVersion
|
||||
Task {
|
||||
_ = Task {
|
||||
if checkOnLaunch {
|
||||
try await checkForUpdates()
|
||||
try? await checkForUpdates()
|
||||
}
|
||||
while !Task.isCancelled {
|
||||
try? await Task.sleep(for: .seconds(Int(checkFrequency)))
|
||||
|
||||
@@ -33,7 +33,11 @@ public final class Agent: Sendable {
|
||||
|
||||
extension Agent {
|
||||
|
||||
public func handle(request: SSHAgent.Request, provenance: SigningRequestProvenance) async -> Data {
|
||||
public func handle(
|
||||
request: SSHAgent.Request,
|
||||
provenance: SigningRequestProvenance,
|
||||
hosts: [Data: String]?
|
||||
) async -> Data {
|
||||
logger.debug("Agent received request of type \(request.debugDescription)")
|
||||
// Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting
|
||||
await reloadSecretsIfNeccessary()
|
||||
@@ -45,22 +49,38 @@ extension Agent {
|
||||
response.append(await identities())
|
||||
logger.debug("Agent returned \(SSHAgent.Response.agentIdentitiesAnswer.debugDescription)")
|
||||
case .signRequest(let context):
|
||||
if let boundSession = await sessionID {
|
||||
switch context.dataToSign.decoded {
|
||||
case .sshConnection(let payload):
|
||||
let target: SigningRequestTarget?
|
||||
switch context.dataToSign.decoded {
|
||||
case .sshConnection(let payload):
|
||||
target = .connection(
|
||||
.init(
|
||||
username: payload.username,
|
||||
hasSignature: payload.hasSignature,
|
||||
publicKeyAlgorithm: payload.publicKeyAlgorithm,
|
||||
publicKey: payload.publicKey,
|
||||
hostKey: payload.hostKey,
|
||||
host: hosts?[payload.hostKey]
|
||||
)
|
||||
)
|
||||
if let boundSession = await sessionID {
|
||||
guard payload.hostKey == boundSession.hostKey else {
|
||||
logger.error("Agent received bind request, but host key does not match signature reqeust host key.")
|
||||
logger.error("Agent received bind request, but host key does not match signature request host key.")
|
||||
throw BindingFailure()
|
||||
}
|
||||
case .sshSig:
|
||||
// SSHSIG does not have a host binding payload.
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .sshSig(let payload):
|
||||
target = .signature(
|
||||
.init(
|
||||
namespace: payload.namespace,
|
||||
hashAlgorithm: payload.hashAlgorithm,
|
||||
hash: payload.hash
|
||||
)
|
||||
)
|
||||
default:
|
||||
target = nil
|
||||
}
|
||||
response.append(SSHAgent.Response.agentSignResponse.data)
|
||||
response.append(try await sign(data: context.dataToSign.raw, keyBlob: context.keyBlob, provenance: provenance))
|
||||
response.append(try await sign(data: context.dataToSign.raw, keyBlob: context.keyBlob, provenance: provenance, target: target))
|
||||
logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
|
||||
case .protocolExtension(.openSSH(.sessionBind(let bind))):
|
||||
response = try await MainActor.run {
|
||||
@@ -120,19 +140,19 @@ extension Agent {
|
||||
/// - data: The data to sign.
|
||||
/// - provenance: A ``SecretKit.SigningRequestProvenance`` object describing the origin of the request.
|
||||
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
||||
func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance) async throws -> Data {
|
||||
func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws -> Data {
|
||||
guard let (secret, store) = await secret(matching: keyBlob) else {
|
||||
let keyBlobHex = keyBlob.formatted(.hex())
|
||||
logger.debug("Agent did not have a key matching \(keyBlobHex)")
|
||||
throw NoMatchingKeyError()
|
||||
}
|
||||
|
||||
try await witness?.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance)
|
||||
try await witness?.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance, target: target)
|
||||
|
||||
let rawRepresentation = try await store.sign(data: data, with: secret, for: provenance)
|
||||
let rawRepresentation = try await store.sign(data: data, with: secret, for: provenance, target: target)
|
||||
let signedData = signatureWriter.data(secret: secret, signature: rawRepresentation)
|
||||
|
||||
try await witness?.witness(accessTo: secret, from: store, by: provenance)
|
||||
try await witness?.witness(accessTo: secret, from: store, by: provenance, target: target)
|
||||
|
||||
logger.debug("Agent signed request")
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import AppKit
|
||||
import SSHProtocolKit
|
||||
|
||||
public actor ProxiedAgent: Sendable {
|
||||
|
||||
/// The active SocketPort. Must be retained to be kept valid.
|
||||
private let port: SocketPort
|
||||
|
||||
/// The FileHandle for the main socket.
|
||||
private let fileHandle: FileHandle
|
||||
|
||||
public init() {
|
||||
let path = "/var/run/com.apple.launchd.0ucqoa5x1y/Listeners"
|
||||
|
||||
port = SocketPort(path: path)
|
||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||
|
||||
}
|
||||
|
||||
public func test() {
|
||||
let requestID = SSHAgent.Request.requestIdentities
|
||||
let request = requestID.data.lengthAndData
|
||||
try! fileHandle.write(contentsOf: request)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SSHAgent.Request {
|
||||
|
||||
var data: Data {
|
||||
var raw = self.protocolID
|
||||
return unsafe Data(bytes: &raw, count: MemoryLayout<UInt8>.size)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension SocketPort {
|
||||
|
||||
convenience init(path: String) {
|
||||
var addr = sockaddr_un()
|
||||
|
||||
let length = unsafe withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
||||
unsafe path.withCString { cstring in
|
||||
let len = unsafe strlen(cstring)
|
||||
unsafe strncpy(pointer, cstring, len)
|
||||
return len
|
||||
}
|
||||
}
|
||||
// This doesn't seem to be _strictly_ neccessary with SocketPort.
|
||||
// but just for good form.
|
||||
addr.sun_family = sa_family_t(AF_UNIX)
|
||||
// This mirrors the SUN_LEN macro format.
|
||||
addr.sun_len = UInt8(MemoryLayout<sockaddr_un>.size - MemoryLayout.size(ofValue: addr.sun_path) + length)
|
||||
|
||||
let data = unsafe Data(bytes: &addr, count: MemoryLayout<sockaddr_un>.size)
|
||||
self.init(remoteWithProtocolFamily: AF_UNIX, socketType: SOCK_STREAM, protocol: 0, address: data)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
/// A protocol that allows conformers to be notified of access to secrets, and optionally prevent access.
|
||||
public protocol SigningWitness: Sendable {
|
||||
@@ -10,13 +11,13 @@ public protocol SigningWitness: Sendable {
|
||||
/// - store: The `Store` being asked to sign the request..
|
||||
/// - provenance: A `SigningRequestProvenance` object describing the origin of the request.
|
||||
/// - Note: This method being called does not imply that the requst has been authorized. If a secret requires authentication, authentication will still need to be performed by the user before the request will be performed. If the user declines or fails to authenticate, the request will fail.
|
||||
func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) async throws
|
||||
func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws
|
||||
|
||||
/// Notifies the callee that a signing operation has been performed for a given secret.
|
||||
/// - Parameters:
|
||||
/// - secret: The `Secret` that will was used to sign the request.
|
||||
/// - store: The `Store` that signed the request..
|
||||
/// - provenance: A `SigningRequestProvenance` object describing the origin of the request.
|
||||
func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) async throws
|
||||
func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws
|
||||
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ extension SocketController {
|
||||
guard !data.isEmpty else {
|
||||
logger.debug("Socket controller received empty data, ending continuation.")
|
||||
messagesContinuation.finish()
|
||||
try fileHandle.close()
|
||||
try? fileHandle.close()
|
||||
return
|
||||
}
|
||||
messagesContinuation.yield(data)
|
||||
@@ -126,8 +126,8 @@ private extension SocketPort {
|
||||
convenience init(path: String) {
|
||||
var addr = sockaddr_un()
|
||||
|
||||
let length = unsafe withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
||||
unsafe path.withCString { cstring in
|
||||
let length = withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
||||
path.withCString { cstring in
|
||||
let len = unsafe strlen(cstring)
|
||||
unsafe strncpy(pointer, cstring, len)
|
||||
return len
|
||||
|
||||
@@ -8,7 +8,7 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
private let _id: @Sendable () -> UUID
|
||||
private let _name: @MainActor @Sendable () -> String
|
||||
private let _secrets: @MainActor @Sendable () -> [AnySecret]
|
||||
private let _sign: @Sendable (Data, AnySecret, SigningRequestProvenance) async throws -> Data
|
||||
private let _sign: @Sendable (Data, AnySecret, SigningRequestProvenance, SigningRequestTarget?) async throws -> Data
|
||||
private let _existingPersistedAuthenticationContext: @Sendable (AnySecret) async -> PersistedAuthenticationContext?
|
||||
private let _persistAuthentication: @Sendable (AnySecret, TimeInterval) async throws -> Void
|
||||
private let _reloadSecrets: @Sendable () async -> Void
|
||||
@@ -19,7 +19,7 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
_name = { secretStore.name }
|
||||
_id = { secretStore.id }
|
||||
_secrets = { secretStore.secrets.map { AnySecret($0) } }
|
||||
_sign = { try await secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) }
|
||||
_sign = { try await secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2, target: $3) }
|
||||
_existingPersistedAuthenticationContext = { await secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
|
||||
_persistAuthentication = { try await secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) }
|
||||
_reloadSecrets = { await secretStore.reloadSecrets() }
|
||||
@@ -41,8 +41,8 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
return _secrets()
|
||||
}
|
||||
|
||||
public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||
try await _sign(data, secret, provenance)
|
||||
public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws -> Data {
|
||||
try await _sign(data, secret, provenance, target)
|
||||
}
|
||||
|
||||
public func existingPersistedAuthenticationContext(secret: AnySecret) async -> PersistedAuthenticationContext? {
|
||||
|
||||
@@ -20,7 +20,7 @@ public protocol SecretStore<SecretType>: Identifiable, Sendable {
|
||||
/// - secret: The ``Secret`` to sign with.
|
||||
/// - provenance: A ``SigningRequestProvenance`` describing where the request came from.
|
||||
/// - Returns: The signed data.
|
||||
func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) async throws -> Data
|
||||
func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws -> Data
|
||||
|
||||
/// Checks to see if there is currently a valid persisted authentication for a given secret.
|
||||
/// - Parameters:
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
/// Describes the target of the signature operation.
|
||||
public enum SigningRequestTarget: Sendable {
|
||||
|
||||
case connection(ConnectionPayload)
|
||||
case signature(SignaturePayload)
|
||||
|
||||
public struct ConnectionPayload: Sendable, Codable{
|
||||
|
||||
public let username: String
|
||||
public let hasSignature: Bool
|
||||
public let publicKeyAlgorithm: String
|
||||
public let publicKey: Data
|
||||
public let hostKey: Data
|
||||
public let host: String?
|
||||
|
||||
public init(
|
||||
username: String,
|
||||
hasSignature: Bool,
|
||||
publicKeyAlgorithm: String,
|
||||
publicKey: Data,
|
||||
hostKey: Data,
|
||||
host: String?
|
||||
) {
|
||||
self.username = username
|
||||
self.hasSignature = hasSignature
|
||||
self.publicKeyAlgorithm = publicKeyAlgorithm
|
||||
self.publicKey = publicKey
|
||||
self.hostKey = hostKey
|
||||
self.host = host
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct SignaturePayload: Sendable, Codable {
|
||||
|
||||
public let namespace: String
|
||||
public let hashAlgorithm: String
|
||||
public let hash: Data
|
||||
|
||||
public init(
|
||||
namespace: String,
|
||||
hashAlgorithm: String,
|
||||
hash: Data,
|
||||
) {
|
||||
self.namespace = namespace
|
||||
self.hashAlgorithm = hashAlgorithm
|
||||
self.hash = hash
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,13 +37,20 @@ extension SecureEnclave {
|
||||
|
||||
// MARK: SecretStore
|
||||
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws -> Data {
|
||||
var context: LAContext
|
||||
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {
|
||||
context = unsafe existing.context
|
||||
} else {
|
||||
let newContext = LAContext()
|
||||
newContext.localizedReason = String(localized: .authContextRequestSignatureDescription(appName: provenance.origin.displayName, secretName: secret.name))
|
||||
switch target {
|
||||
case .connection(let payload) where payload.host != nil:
|
||||
newContext.localizedReason = String(localized: .authContextRequestSignatureForConnectionDescription(appName: provenance.origin.displayName, targetName: payload.host!, secretName: secret.name))
|
||||
case .signature(let payload):
|
||||
newContext.localizedReason = String(localized: .authContextRequestSignatureForSignatureDescription(namespace: payload.namespace, appName: provenance.origin.displayName, secretName: secret.name))
|
||||
default:
|
||||
newContext.localizedReason = String(localized: .authContextRequestSignatureDescription(appName: provenance.origin.displayName, secretName: secret.name))
|
||||
}
|
||||
newContext.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
|
||||
context = newContext
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ extension SmartCard {
|
||||
|
||||
// MARK: Public API
|
||||
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws -> Data {
|
||||
guard let tokenID = await state.tokenID else { fatalError() }
|
||||
var context: LAContext
|
||||
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {
|
||||
|
||||
@@ -49,7 +49,7 @@ extension Stub {
|
||||
print("Public Key OpenSSH: \(OpenSSHPublicKeyWriter().openSSHString(secret: secret))")
|
||||
}
|
||||
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) throws -> Data {
|
||||
guard !shouldThrow else {
|
||||
throw NSError(domain: "test", code: 0, userInfo: nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user