Add host lookup xpc service (#826)

* Add host lookup xpc service

* Remove restriction that snuck in

* Test fixes
This commit is contained in:
Max Goedjen
2026-09-06 21:32:51 -07:00
committed by GitHub
parent e9a0ff31d3
commit 70e32dcf99
21 changed files with 1126 additions and 42 deletions

View File

@@ -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" : { "certificate_detail_critical_options_label" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@@ -23775,6 +24147,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" : { "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.", "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", "extractionState" : "manual",
@@ -24147,6 +24705,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" : { "smart_card" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@@ -26928,5 +27672,5 @@
} }
} }
}, },
"version" : "1.0" "version" : "1.2"
} }

View File

@@ -35,7 +35,8 @@ extension Agent {
public func handle( public func handle(
request: SSHAgent.Request, request: SSHAgent.Request,
provenance: SigningRequestProvenance provenance: SigningRequestProvenance,
hosts: [Data: String]?
) async -> Data { ) async -> Data {
logger.debug("Agent received request of type \(request.debugDescription)") 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 // Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting
@@ -57,7 +58,8 @@ extension Agent {
hasSignature: payload.hasSignature, hasSignature: payload.hasSignature,
publicKeyAlgorithm: payload.publicKeyAlgorithm, publicKeyAlgorithm: payload.publicKeyAlgorithm,
publicKey: payload.publicKey, publicKey: payload.publicKey,
hostKey: payload.hostKey hostKey: payload.hostKey,
host: hosts?[payload.hostKey]
) )
) )
if let boundSession = await sessionID { if let boundSession = await sessionID {
@@ -79,7 +81,7 @@ extension Agent {
} }
_ = target _ = target
response.append(SSHAgent.Response.agentSignResponse.data) 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)") logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
case .protocolExtension(.openSSH(.sessionBind(let bind))): case .protocolExtension(.openSSH(.sessionBind(let bind))):
// This is disabled until forward enforcement is handled. // This is disabled until forward enforcement is handled.
@@ -143,19 +145,20 @@ extension Agent {
/// - data: The data to sign. /// - data: The data to sign.
/// - provenance: A ``SecretKit.SigningRequestProvenance`` object describing the origin of the request. /// - provenance: A ``SecretKit.SigningRequestProvenance`` object describing the origin of the request.
/// - Returns: An OpenSSH formatted Data payload containing the signed data response. /// - 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 { guard let (secret, store) = await secret(matching: keyBlob) else {
let keyBlobHex = keyBlob.formatted(.hex()) let keyBlobHex = keyBlob.formatted(.hex())
logger.debug("Agent did not have a key matching \(keyBlobHex)") logger.debug("Agent did not have a key matching \(keyBlobHex)")
throw NoMatchingKeyError() throw NoMatchingKeyError()
} }
try await witness?.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance)
let rawRepresentation = try await store.sign(data: data, with: secret, for: 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, target: target)
let signedData = signatureWriter.data(secret: secret, signature: rawRepresentation) 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") logger.debug("Agent signed request")

View File

@@ -1,5 +1,6 @@
import Foundation import Foundation
import SecretKit import SecretKit
import SSHProtocolKit
/// A protocol that allows conformers to be notified of access to secrets, and optionally prevent access. /// A protocol that allows conformers to be notified of access to secrets, and optionally prevent access.
public protocol SigningWitness: Sendable { public protocol SigningWitness: Sendable {
@@ -10,13 +11,13 @@ public protocol SigningWitness: Sendable {
/// - store: The `Store` being asked to sign the request.. /// - store: The `Store` being asked to sign the request..
/// - provenance: A `SigningRequestProvenance` object describing the origin of 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. /// - 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. /// Notifies the callee that a signing operation has been performed for a given secret.
/// - Parameters: /// - Parameters:
/// - secret: The `Secret` that will was used to sign the request. /// - secret: The `Secret` that will was used to sign the request.
/// - store: The `Store` that signed the request.. /// - store: The `Store` that signed the request..
/// - provenance: A `SigningRequestProvenance` object describing the origin of 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
} }

View File

@@ -8,7 +8,7 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
private let _id: @Sendable () -> UUID private let _id: @Sendable () -> UUID
private let _name: @MainActor @Sendable () -> String private let _name: @MainActor @Sendable () -> String
private let _secrets: @MainActor @Sendable () -> [AnySecret] 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 _existingPersistedAuthenticationContext: @Sendable (AnySecret) async -> PersistedAuthenticationContext?
private let _persistAuthentication: @Sendable (AnySecret, TimeInterval) async throws -> Void private let _persistAuthentication: @Sendable (AnySecret, TimeInterval) async throws -> Void
private let _reloadSecrets: @Sendable () async -> Void private let _reloadSecrets: @Sendable () async -> Void
@@ -19,7 +19,7 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
_name = { secretStore.name } _name = { secretStore.name }
_id = { secretStore.id } _id = { secretStore.id }
_secrets = { secretStore.secrets.map { AnySecret($0) } } _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) } _existingPersistedAuthenticationContext = { await secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
_persistAuthentication = { try await secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) } _persistAuthentication = { try await secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) }
_reloadSecrets = { await secretStore.reloadSecrets() } _reloadSecrets = { await secretStore.reloadSecrets() }
@@ -41,8 +41,8 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
return _secrets() return _secrets()
} }
public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance) async throws -> Data { public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) async throws -> Data {
try await _sign(data, secret, provenance) try await _sign(data, secret, provenance, target)
} }
public func existingPersistedAuthenticationContext(secret: AnySecret) async -> PersistedAuthenticationContext? { public func existingPersistedAuthenticationContext(secret: AnySecret) async -> PersistedAuthenticationContext? {

View File

@@ -20,7 +20,7 @@ public protocol SecretStore<SecretType>: Identifiable, Sendable {
/// - secret: The ``Secret`` to sign with. /// - secret: The ``Secret`` to sign with.
/// - provenance: A ``SigningRequestProvenance`` describing where the request came from. /// - provenance: A ``SigningRequestProvenance`` describing where the request came from.
/// - Returns: The signed data. /// - 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. /// Checks to see if there is currently a valid persisted authentication for a given secret.
/// - Parameters: /// - Parameters:

View File

@@ -14,6 +14,7 @@ public enum SigningRequestTarget: Sendable {
public let publicKeyAlgorithm: String public let publicKeyAlgorithm: String
public let publicKey: Data public let publicKey: Data
public let hostKey: Data public let hostKey: Data
public let host: String?
public init( public init(
username: String, username: String,
@@ -21,12 +22,14 @@ public enum SigningRequestTarget: Sendable {
publicKeyAlgorithm: String, publicKeyAlgorithm: String,
publicKey: Data, publicKey: Data,
hostKey: Data, hostKey: Data,
host: String?
) { ) {
self.username = username self.username = username
self.hasSignature = hasSignature self.hasSignature = hasSignature
self.publicKeyAlgorithm = publicKeyAlgorithm self.publicKeyAlgorithm = publicKeyAlgorithm
self.publicKey = publicKey self.publicKey = publicKey
self.hostKey = hostKey self.hostKey = hostKey
self.host = host
} }
} }

View File

@@ -37,13 +37,20 @@ extension SecureEnclave {
// MARK: SecretStore // 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 var context: LAContext
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) { if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {
context = unsafe existing.context context = unsafe existing.context
} else { } else {
let newContext = LAContext() 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) newContext.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
context = newContext context = newContext
} }

View File

@@ -57,7 +57,7 @@ extension SmartCard {
// MARK: Public API // 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() } guard let tokenID = await state.tokenID else { fatalError() }
var context: LAContext var context: LAContext
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) { if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {

View File

@@ -13,7 +13,7 @@ import CertificateKit
@Test func emptyStores() async throws { @Test func emptyStores() async throws {
let agent = Agent(storeList: SecretStoreList(), certificateStore: CertificateStore()) let agent = Agent(storeList: SecretStoreList(), certificateStore: CertificateStore())
let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestIdentities) let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestIdentities)
let response = await agent.handle(request: request, provenance: .test) let response = await agent.handle(request: request, provenance: .test, hosts: nil)
#expect(response == Constants.Responses.requestIdentitiesEmpty) #expect(response == Constants.Responses.requestIdentitiesEmpty)
} }
@@ -21,7 +21,7 @@ import CertificateKit
let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
let agent = Agent(storeList: list, certificateStore: CertificateStore()) let agent = Agent(storeList: list, certificateStore: CertificateStore())
let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestIdentities) let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestIdentities)
let response = await agent.handle(request: request, provenance: .test) let response = await agent.handle(request: request, provenance: .test, hosts: nil)
let actual = OpenSSHReader(data: response) let actual = OpenSSHReader(data: response)
let expected = OpenSSHReader(data: Constants.Responses.requestIdentitiesMultiple) let expected = OpenSSHReader(data: Constants.Responses.requestIdentitiesMultiple)
@@ -35,7 +35,7 @@ import CertificateKit
let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
let agent = Agent(storeList: list, certificateStore: CertificateStore()) let agent = Agent(storeList: list, certificateStore: CertificateStore())
let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignatureWithNoneMatching) let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignatureWithNoneMatching)
let response = await agent.handle(request: request, provenance: .test) let response = await agent.handle(request: request, provenance: .test, hosts: nil)
#expect(response == Constants.Responses.requestFailure) #expect(response == Constants.Responses.requestFailure)
} }
@@ -44,7 +44,7 @@ import CertificateKit
guard case SSHAgent.Request.signRequest(let context) = request else { return } guard case SSHAgent.Request.signRequest(let context) = request else { return }
let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
let agent = Agent(storeList: list, certificateStore: CertificateStore()) let agent = Agent(storeList: list, certificateStore: CertificateStore())
let response = await agent.handle(request: request, provenance: .test) let response = await agent.handle(request: request, provenance: .test, hosts: nil)
let responseReader = OpenSSHReader(data: response) let responseReader = OpenSSHReader(data: response)
let length = try responseReader.readNextBytes(as: UInt32.self) let length = try responseReader.readNextBytes(as: UInt32.self)
let type = try responseReader.readNextBytes(as: UInt8.self) let type = try responseReader.readNextBytes(as: UInt8.self)
@@ -79,7 +79,7 @@ import CertificateKit
return true return true
}, witness: { _, _ in }) }, witness: { _, _ in })
let agent = Agent(storeList: list, certificateStore: CertificateStore(), witness: witness) let agent = Agent(storeList: list, certificateStore: CertificateStore(), witness: witness)
let response = await agent.handle(request: .signRequest(.empty), provenance: .test) let response = await agent.handle(request: .signRequest(.empty), provenance: .test, hosts: nil)
#expect(response == Constants.Responses.requestFailure) #expect(response == Constants.Responses.requestFailure)
} }
@@ -93,7 +93,7 @@ import CertificateKit
}) })
let agent = Agent(storeList: list, certificateStore: CertificateStore(), witness: witness) let agent = Agent(storeList: list, certificateStore: CertificateStore(), witness: witness)
let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature) let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
_ = await agent.handle(request: request, provenance: .test) _ = await agent.handle(request: request, provenance: .test, hosts: nil)
#expect(witnessed) #expect(witnessed)
} }
@@ -109,7 +109,7 @@ import CertificateKit
}) })
let agent = Agent(storeList: list, certificateStore: CertificateStore(), witness: witness) let agent = Agent(storeList: list, certificateStore: CertificateStore(), witness: witness)
let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature) let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
_ = await agent.handle(request: request, provenance: .test) _ = await agent.handle(request: request, provenance: .test, hosts: nil)
#expect(witnessTrace == speakNowTrace) #expect(witnessTrace == speakNowTrace)
#expect(witnessTrace == .test) #expect(witnessTrace == .test)
} }
@@ -122,7 +122,7 @@ import CertificateKit
store.shouldThrow = true store.shouldThrow = true
let agent = Agent(storeList: list, certificateStore: CertificateStore()) let agent = Agent(storeList: list, certificateStore: CertificateStore())
let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature) let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
let response = await agent.handle(request: request, provenance: .test) let response = await agent.handle(request: request, provenance: .test, hosts: nil)
#expect(response == Constants.Responses.requestFailure) #expect(response == Constants.Responses.requestFailure)
} }
@@ -130,7 +130,7 @@ import CertificateKit
@Test func unhandledAdd() async throws { @Test func unhandledAdd() async throws {
let agent = Agent(storeList: SecretStoreList(), certificateStore: CertificateStore()) let agent = Agent(storeList: SecretStoreList(), certificateStore: CertificateStore())
let response = await agent.handle(request: .addIdentity, provenance: .test) let response = await agent.handle(request: .addIdentity, provenance: .test, hosts: nil)
#expect(response == Constants.Responses.requestFailure) #expect(response == Constants.Responses.requestFailure)
} }

View File

@@ -49,7 +49,7 @@ extension Stub {
print("Public Key OpenSSH: \(OpenSSHPublicKeyWriter().openSSHString(secret: secret))") 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 { guard !shouldThrow else {
throw NSError(domain: "test", code: 0, userInfo: nil) throw NSError(domain: "test", code: 0, userInfo: nil)
} }

View File

@@ -10,14 +10,14 @@ struct StubWitness {
extension StubWitness: SigningWitness { extension StubWitness: SigningWitness {
func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws { func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, target: SigningRequestTarget?) throws {
let objection = speakNow(secret, provenance) let objection = speakNow(secret, provenance)
if objection { if objection {
throw TheresMyChance() throw TheresMyChance()
} }
} }
func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws { func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, target: SigningRequestTarget?) throws {
witness(secret, provenance) witness(secret, provenance)
} }

View File

@@ -54,9 +54,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
Task { Task {
do { do {
let inputParser = try await XPCAgentInputParser() let inputParser = try await XPCAgentInputParser()
let hostsReader = try? await XPCHostsfileReader()
let hosts = try? await hostsReader?.read()
for await message in session.messages { for await message in session.messages {
let request = try await inputParser.parse(data: message) let request = try await inputParser.parse(data: message)
let agentResponse = await agent.handle(request: request, provenance: session.provenance) let agentResponse = await agent.handle(request: request, provenance: session.provenance, hosts: hosts)
try session.write(agentResponse) try session.write(agentResponse)
} }
} catch { } catch {

View File

@@ -60,12 +60,19 @@ final class Notifier: Sendable {
notificationCenter.requestAuthorization(options: .alert) { _, _ in } notificationCenter.requestAuthorization(options: .alert) { _, _ in }
} }
func notify(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) async { func notify(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, target: SigningRequestTarget?) async {
await notificationDelegate.state.setPending(secret: secret, store: store) await notificationDelegate.state.setPending(secret: secret, store: store)
let notificationCenter = UNUserNotificationCenter.current() let notificationCenter = UNUserNotificationCenter.current()
let notificationContent = UNMutableNotificationContent() let notificationContent = UNMutableNotificationContent()
notificationContent.title = String(localized: .signedNotificationTitle(appName: provenance.origin.displayName)) notificationContent.title = String(localized: .signedNotificationTitle(appName: provenance.origin.displayName))
notificationContent.subtitle = String(localized: .signedNotificationDescription(secretName: secret.name)) switch target {
case .connection(let payload) where payload.host != nil:
notificationContent.subtitle = String(localized: .signedConnectionNotificationDescription(secretName: secret.name, username: payload.username, host: payload.host!))
case .signature(let payload):
notificationContent.subtitle = String(localized: .signedSignatureNotificationDescription(secretName: secret.name, namespace: payload.namespace))
default:
notificationContent.subtitle = String(localized: .signedNotificationDescription(secretName: secret.name))
}
notificationContent.userInfo[Constants.persistSecretIDKey] = secret.id.description notificationContent.userInfo[Constants.persistSecretIDKey] = secret.id.description
notificationContent.userInfo[Constants.persistStoreIDKey] = store.id.description notificationContent.userInfo[Constants.persistStoreIDKey] = store.id.description
notificationContent.interruptionLevel = .timeSensitive notificationContent.interruptionLevel = .timeSensitive
@@ -100,11 +107,21 @@ final class Notifier: Sendable {
extension Notifier: SigningWitness { extension Notifier: SigningWitness {
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 {
} }
func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) async throws { func witness(
await notify(accessTo: secret, from: store, by: provenance) accessTo secret: AnySecret,
from store: AnySecretStore,
by provenance: SigningRequestProvenance,
target: SigningRequestTarget?
) async throws {
await notify(accessTo: secret, from: store, by: provenance, target: target)
} }
} }

View File

@@ -0,0 +1,36 @@
import Foundation
import OSLog
import XPCWrappers
import OSLog
public final class XPCHostsfileReader {
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "XPCHostsfileReader")
private let session: XPCTypedSession<[Data: String], HostsfileReaderError>
public init() async throws {
logger.debug("Creating XPCHostsfileReader")
session = try await XPCTypedSession(serviceName: "com.maxgoedjen.Secretive.SecretAgentHostsfileReader", warmup: true)
logger.debug("XPCHostsfileReader is warmed up.")
}
public func read() async throws -> [Data: String] {
logger.debug("Reading hosts file")
defer { logger.debug("Read hosts file") }
return try await session.send(Data())
}
deinit {
session.complete()
}
}
extension XPCHostsfileReader {
public enum HostsfileReaderError: Error, Codable {
case fileDoesNotExist
case parseError
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>XPCService</key>
<dict>
<key>ServiceType</key>
<string>Application</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.hardened-process</key>
<true/>
<key>com.apple.security.hardened-process.checked-allocations</key>
<true/>
<key>com.apple.security.hardened-process.dyld-ro</key>
<true/>
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
<string>2</string>
<key>com.apple.security.hardened-process.hardened-heap</key>
<true/>
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
<string>2</string>
</dict>
</plist>

View File

@@ -0,0 +1,25 @@
import Foundation
import OSLog
import XPCWrappers
import SSHProtocolKit
final class SecretAgentHostsfileReader: NSObject, XPCProtocol {
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.SecretAgentHostsfileReader", category: "SecretAgentHostsfileReader")
func process(_ data: Data) async throws -> [Data: String] {
logger.log("Parsing hostsfile")
var result: [Data: String] = [:]
for try await line in URL(filePath: NSHomeDirectory().appending("/.ssh/known_hosts")).lines {
let split = line.split(separator: " ").map(String.init)
guard split.count == 3 else { continue }
guard let decoded = Data(base64Encoded: split[2]) else { continue }
let reader = OpenSSHReader(data: decoded)
_ = try reader.readNextChunk()
let key = try reader.readNextChunk()
result[key] = split[0]
}
return result
}
}

View File

@@ -0,0 +1,7 @@
import Foundation
import XPCWrappers
let delegate = XPCServiceDelegate(exportedObject: SecretAgentHostsfileReader())
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()

View File

@@ -30,6 +30,12 @@
504788F42E681F6900B4556F /* ToolConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F32E681F6900B4556F /* ToolConfigurationView.swift */; }; 504788F42E681F6900B4556F /* ToolConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F32E681F6900B4556F /* ToolConfigurationView.swift */; };
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; }; 504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; }; 504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; };
505402A03034B692000C3356 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5054029D3034B692000C3356 /* main.swift */; };
505402A13034B692000C3356 /* SecretAgentHostsfileReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5054029E3034B692000C3356 /* SecretAgentHostsfileReader.swift */; };
505402A33034B787000C3356 /* SecretAgentHostsfileReader.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 5054028D3034B5E3000C3356 /* SecretAgentHostsfileReader.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
505402A73034B7A4000C3356 /* XPCWrappers in Frameworks */ = {isa = PBXBuildFile; productRef = 505402A63034B7A4000C3356 /* XPCWrappers */; };
505402AA3034B80E000C3356 /* XPCHostsfileReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505402A93034B80E000C3356 /* XPCHostsfileReader.swift */; };
505402AC303594D1000C3356 /* SSHProtocolKit in Frameworks */ = {isa = PBXBuildFile; productRef = 505402AB303594D1000C3356 /* SSHProtocolKit */; };
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; }; 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; };
505F5EF22FA9635700C45824 /* CertificateKit in Frameworks */ = {isa = PBXBuildFile; productRef = 505F5EF12FA9635700C45824 /* CertificateKit */; }; 505F5EF22FA9635700C45824 /* CertificateKit in Frameworks */ = {isa = PBXBuildFile; productRef = 505F5EF12FA9635700C45824 /* CertificateKit */; };
50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; }; 50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
@@ -110,6 +116,13 @@
remoteGlobalIDString = 501577BC2E6BC5B4004A37D0; remoteGlobalIDString = 501577BC2E6BC5B4004A37D0;
remoteInfo = ReleasesDownloader; remoteInfo = ReleasesDownloader;
}; };
505402A43034B787000C3356 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5054028C3034B5E3000C3356;
remoteInfo = SecretAgentHostsfileReader;
};
50692D1B2E6FDB880043C7BB /* PBXContainerItemProxy */ = { 50692D1B2E6FDB880043C7BB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */; containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
@@ -174,6 +187,7 @@
dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
dstSubfolderSpec = 16; dstSubfolderSpec = 16;
files = ( files = (
505402A33034B787000C3356 /* SecretAgentHostsfileReader.xpc in Embed XPC Services */,
50692E6D2E6FFA5F0043C7BB /* SecretiveUpdater.xpc in Embed XPC Services */, 50692E6D2E6FFA5F0043C7BB /* SecretiveUpdater.xpc in Embed XPC Services */,
50E2052D2FAAB92000402380 /* SecretiveCertificateParser.xpc in Embed XPC Services */, 50E2052D2FAAB92000402380 /* SecretiveCertificateParser.xpc in Embed XPC Services */,
50692E702E6FFA6E0043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */, 50692E702E6FFA6E0043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */,
@@ -228,6 +242,12 @@
504788F32E681F6900B4556F /* ToolConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolConfigurationView.swift; sourceTree = "<group>"; }; 504788F32E681F6900B4556F /* ToolConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolConfigurationView.swift; sourceTree = "<group>"; };
504788F52E68206F00B4556F /* GettingStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GettingStartedView.swift; sourceTree = "<group>"; }; 504788F52E68206F00B4556F /* GettingStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GettingStartedView.swift; sourceTree = "<group>"; };
504789222E697DD300B4556F /* BoxBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxBackgroundStyle.swift; sourceTree = "<group>"; }; 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxBackgroundStyle.swift; sourceTree = "<group>"; };
5054028D3034B5E3000C3356 /* SecretAgentHostsfileReader.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = SecretAgentHostsfileReader.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
5054029C3034B692000C3356 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5054029D3034B692000C3356 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
5054029E3034B692000C3356 /* SecretAgentHostsfileReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretAgentHostsfileReader.swift; sourceTree = "<group>"; };
505402A83034B7D2000C3356 /* SecretAgentHostsfileReader.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretAgentHostsfileReader.entitlements; sourceTree = "<group>"; };
505402A93034B80E000C3356 /* XPCHostsfileReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCHostsfileReader.swift; sourceTree = "<group>"; };
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; }; 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
5059933F2E7A3B5B0092CFFA /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; }; 5059933F2E7A3B5B0092CFFA /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; };
50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -290,6 +310,15 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
5054028A3034B5E3000C3356 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
505402AC303594D1000C3356 /* SSHProtocolKit in Frameworks */,
505402A73034B7A4000C3356 /* XPCWrappers in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50617D7C23FCE48D0099B055 /* Frameworks */ = { 50617D7C23FCE48D0099B055 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -408,6 +437,17 @@
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5054029F3034B692000C3356 /* SecretAgentHostsfileReader */ = {
isa = PBXGroup;
children = (
505402A83034B7D2000C3356 /* SecretAgentHostsfileReader.entitlements */,
5054029C3034B692000C3356 /* Info.plist */,
5054029D3034B692000C3356 /* main.swift */,
5054029E3034B692000C3356 /* SecretAgentHostsfileReader.swift */,
);
path = SecretAgentHostsfileReader;
sourceTree = "<group>";
};
50617D7623FCE48D0099B055 = { 50617D7623FCE48D0099B055 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -418,6 +458,7 @@
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */, 50692D272E6FDB8D0043C7BB /* SecretiveUpdater */,
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */, 50692E662E6FF9E20043C7BB /* SecretAgentInputParser */,
50E205262FAAB82700402380 /* SecretiveCertificateParser */, 50E205262FAAB82700402380 /* SecretiveCertificateParser */,
5054029F3034B692000C3356 /* SecretAgentHostsfileReader */,
50617D8023FCE48E0099B055 /* Products */, 50617D8023FCE48E0099B055 /* Products */,
5099A08B240243730062B6F2 /* Frameworks */, 5099A08B240243730062B6F2 /* Frameworks */,
); );
@@ -431,6 +472,7 @@
50692D122E6FDB880043C7BB /* SecretiveUpdater.xpc */, 50692D122E6FDB880043C7BB /* SecretiveUpdater.xpc */,
50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */, 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */,
50E205142FAAB81C00402380 /* SecretiveCertificateParser.xpc */, 50E205142FAAB81C00402380 /* SecretiveCertificateParser.xpc */,
5054028D3034B5E3000C3356 /* SecretAgentHostsfileReader.xpc */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -529,6 +571,7 @@
50020BAF24064869003D4025 /* AppDelegate.swift */, 50020BAF24064869003D4025 /* AppDelegate.swift */,
5018F54E24064786002EB505 /* Notifier.swift */, 5018F54E24064786002EB505 /* Notifier.swift */,
501578122E6C0479004A37D0 /* XPCInputParser.swift */, 501578122E6C0479004A37D0 /* XPCInputParser.swift */,
505402A93034B80E000C3356 /* XPCHostsfileReader.swift */,
50E2057F2FAB291E00402380 /* CertificateMigrator.swift */, 50E2057F2FAB291E00402380 /* CertificateMigrator.swift */,
50A3B79524026B7600D209EA /* Main.storyboard */, 50A3B79524026B7600D209EA /* Main.storyboard */,
50A3B79824026B7600D209EA /* Info.plist */, 50A3B79824026B7600D209EA /* Info.plist */,
@@ -561,6 +604,27 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
5054028C3034B5E3000C3356 /* SecretAgentHostsfileReader */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5054029B3034B5E3000C3356 /* Build configuration list for PBXNativeTarget "SecretAgentHostsfileReader" */;
buildPhases = (
505402893034B5E3000C3356 /* Sources */,
5054028A3034B5E3000C3356 /* Frameworks */,
5054028B3034B5E3000C3356 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SecretAgentHostsfileReader;
packageProductDependencies = (
505402A63034B7A4000C3356 /* XPCWrappers */,
505402AB303594D1000C3356 /* SSHProtocolKit */,
);
productName = SecretAgentHostsfileReader;
productReference = 5054028D3034B5E3000C3356 /* SecretAgentHostsfileReader.xpc */;
productType = "com.apple.product-type.xpc-service";
};
50617D7E23FCE48D0099B055 /* Secretive */ = { 50617D7E23FCE48D0099B055 /* Secretive */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 50617D9D23FCE48E0099B055 /* Build configuration list for PBXNativeTarget "Secretive" */; buildConfigurationList = 50617D9D23FCE48E0099B055 /* Build configuration list for PBXNativeTarget "Secretive" */;
@@ -655,6 +719,7 @@
50692E6F2E6FFA5F0043C7BB /* PBXTargetDependency */, 50692E6F2E6FFA5F0043C7BB /* PBXTargetDependency */,
50692E722E6FFA6E0043C7BB /* PBXTargetDependency */, 50692E722E6FFA6E0043C7BB /* PBXTargetDependency */,
50E2052F2FAAB92000402380 /* PBXTargetDependency */, 50E2052F2FAAB92000402380 /* PBXTargetDependency */,
505402A53034B787000C3356 /* PBXTargetDependency */,
); );
name = SecretAgent; name = SecretAgent;
packageProductDependencies = ( packageProductDependencies = (
@@ -703,6 +768,9 @@
LastUpgradeCheck = 2640; LastUpgradeCheck = 2640;
ORGANIZATIONNAME = "Max Goedjen"; ORGANIZATIONNAME = "Max Goedjen";
TargetAttributes = { TargetAttributes = {
5054028C3034B5E3000C3356 = {
CreatedOnToolsVersion = 27.0;
};
50617D7E23FCE48D0099B055 = { 50617D7E23FCE48D0099B055 = {
CreatedOnToolsVersion = 11.3; CreatedOnToolsVersion = 11.3;
}; };
@@ -746,11 +814,19 @@
50692D112E6FDB880043C7BB /* SecretiveUpdater */, 50692D112E6FDB880043C7BB /* SecretiveUpdater */,
50E205132FAAB81C00402380 /* SecretiveCertificateParser */, 50E205132FAAB81C00402380 /* SecretiveCertificateParser */,
50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */, 50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */,
5054028C3034B5E3000C3356 /* SecretAgentHostsfileReader */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
5054028B3034B5E3000C3356 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
50617D7D23FCE48D0099B055 /* Resources */ = { 50617D7D23FCE48D0099B055 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -799,6 +875,15 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
505402893034B5E3000C3356 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
505402A03034B692000C3356 /* main.swift in Sources */,
505402A13034B692000C3356 /* SecretAgentHostsfileReader.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50617D7B23FCE48D0099B055 /* Sources */ = { 50617D7B23FCE48D0099B055 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -868,6 +953,7 @@
50020BB024064869003D4025 /* AppDelegate.swift in Sources */, 50020BB024064869003D4025 /* AppDelegate.swift in Sources */,
5018F54F24064786002EB505 /* Notifier.swift in Sources */, 5018F54F24064786002EB505 /* Notifier.swift in Sources */,
501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */, 501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */,
505402AA3034B80E000C3356 /* XPCHostsfileReader.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -892,6 +978,11 @@
isa = PBXTargetDependency; isa = PBXTargetDependency;
targetProxy = 501577D32E6BC5DD004A37D0 /* PBXContainerItemProxy */; targetProxy = 501577D32E6BC5DD004A37D0 /* PBXContainerItemProxy */;
}; };
505402A53034B787000C3356 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5054028C3034B5E3000C3356 /* SecretAgentHostsfileReader */;
targetProxy = 505402A43034B787000C3356 /* PBXContainerItemProxy */;
};
50692D1C2E6FDB880043C7BB /* PBXTargetDependency */ = { 50692D1C2E6FDB880043C7BB /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = 50692D112E6FDB880043C7BB /* SecretiveUpdater */; target = 50692D112E6FDB880043C7BB /* SecretiveUpdater */;
@@ -936,6 +1027,104 @@
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
505402973034B5E3000C3356 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = SecretAgentHostsfileReader/SecretAgentHostsfileReader.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
ENABLE_APP_SANDBOX = NO;
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SecretAgentHostsfileReader/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SecretAgentHostsfileReader;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Max Goedjen. All rights reserved.";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentHostsfileReader";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
505402983034B5E3000C3356 /* Test */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = SecretAgentHostsfileReader/SecretAgentHostsfileReader.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
ENABLE_APP_SANDBOX = NO;
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SecretAgentHostsfileReader/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SecretAgentHostsfileReader;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Max Goedjen. All rights reserved.";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentHostsfileReader";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
};
name = Test;
};
505402993034B5E3000C3356 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = SecretAgentHostsfileReader/SecretAgentHostsfileReader.entitlements;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
ENABLE_APP_SANDBOX = NO;
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SecretAgentHostsfileReader/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SecretAgentHostsfileReader;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Max Goedjen. All rights reserved.";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentHostsfileReader";
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
50617D9B23FCE48E0099B055 /* Debug */ = { 50617D9B23FCE48E0099B055 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */; baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
@@ -1658,6 +1847,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1689,6 +1879,7 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1722,6 +1913,7 @@
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6; "DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1747,6 +1939,16 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
5054029B3034B5E3000C3356 /* Build configuration list for PBXNativeTarget "SecretAgentHostsfileReader" */ = {
isa = XCConfigurationList;
buildConfigurations = (
505402973034B5E3000C3356 /* Debug */,
505402983034B5E3000C3356 /* Test */,
505402993034B5E3000C3356 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
50617D7A23FCE48D0099B055 /* Build configuration list for PBXProject "Secretive" */ = { 50617D7A23FCE48D0099B055 /* Build configuration list for PBXProject "Secretive" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
@@ -1850,6 +2052,14 @@
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = Brief; productName = Brief;
}; };
505402A63034B7A4000C3356 /* XPCWrappers */ = {
isa = XCSwiftPackageProductDependency;
productName = XPCWrappers;
};
505402AB303594D1000C3356 /* SSHProtocolKit */ = {
isa = XCSwiftPackageProductDependency;
productName = SSHProtocolKit;
};
505F5EF12FA9635700C45824 /* CertificateKit */ = { 505F5EF12FA9635700C45824 /* CertificateKit */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = CertificateKit; productName = CertificateKit;

View File

@@ -38,7 +38,7 @@ extension Preview {
self.init(secrets: new) self.init(secrets: new)
} }
func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance) throws -> Data { func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) throws -> Data {
return data return data
} }
@@ -82,7 +82,7 @@ extension Preview {
self.init(secrets: new) self.init(secrets: new)
} }
func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance) throws -> Data { func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance, target: SigningRequestTarget?) throws -> Data {
return data return data
} }

View File

@@ -12,11 +12,11 @@
<true/> <true/>
<key>com.apple.security.hardened-process.dyld-ro</key> <key>com.apple.security.hardened-process.dyld-ro</key>
<true/> <true/>
<key>com.apple.security.hardened-process.enhanced-security-version</key> <key>com.apple.security.hardened-process.enhanced-security-version-string</key>
<integer>1</integer> <string>2</string>
<key>com.apple.security.hardened-process.hardened-heap</key> <key>com.apple.security.hardened-process.hardened-heap</key>
<true/> <true/>
<key>com.apple.security.hardened-process.platform-restrictions</key> <key>com.apple.security.hardened-process.platform-restrictions-string</key>
<integer>2</integer> <string>2</string>
</dict> </dict>
</plist> </plist>