diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9d69dfa..7f2d43d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -24,6 +24,8 @@ jobs:
- name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
- name: Test
+ run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
+ # SPM doesn't seem to pick up on the tests currently?
run: swift test --build-system swiftbuild --package-path Sources/Packages
build:
permissions:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2fb5150..0e2f077 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,6 +12,8 @@ jobs:
- name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
- name: Test Main Packages
- run: swift test --build-system swiftbuild --package-path Sources/Packages
+ run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
+ # SPM doesn't seem to pick up on the tests currently?
+ # run: swift test --build-system swiftbuild --package-path Sources/Packages
- name: Test SecretKit Packages
run: swift test --build-system swiftbuild
diff --git a/Sources/AgentRequestParser/Info.plist b/Sources/AgentRequestParser/Info.plist
new file mode 100644
index 0000000..c123a5d
--- /dev/null
+++ b/Sources/AgentRequestParser/Info.plist
@@ -0,0 +1,11 @@
+
+
+
+
+ XPCService
+
+ ServiceType
+ Application
+
+
+
diff --git a/Sources/AgentRequestParser/main.swift b/Sources/AgentRequestParser/main.swift
new file mode 100644
index 0000000..0105e8f
--- /dev/null
+++ b/Sources/AgentRequestParser/main.swift
@@ -0,0 +1,55 @@
+import XPC
+import SecretAgentKit
+import OSLog
+
+private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent.AgentRequestParser", category: "Parser")
+
+func handleRequest(_ request: XPCListener.IncomingSessionRequest) -> XPCListener.IncomingSessionRequest.Decision {
+ logger.log("Parser received inbound request")
+ return request.accept { xpcMessage in
+ xpcMessage.handoffReply(to: .global(qos: .userInteractive)) {
+ logger.log("Parser accepted inbound request")
+ handle(with: xpcMessage)
+ }
+ }
+}
+
+func handle(with xpcMessage: XPCReceivedMessage) {
+ do {
+ let parser = SSHAgentInputParser()
+ let result = try parser.parse(data: xpcMessage.wrappedDecode())
+ logger.log("Parser parsed message as type \(result.debugDescription)")
+ xpcMessage.reply(result)
+ } catch {
+ logger.error("Parser failed with error \(error)")
+ xpcMessage.reply(error)
+ }
+}
+
+extension XPCReceivedMessage {
+
+ func wrappedDecode() throws(SSHAgentInputParser.AgentParsingError) -> Data {
+ do {
+ return try decode(as: Data.self)
+ } catch {
+ throw SSHAgentInputParser.AgentParsingError.invalidData
+ }
+ }
+
+}
+
+do {
+ if #available(macOS 26.0, *) {
+ _ = try XPCListener(
+ service: "com.maxgoedjen.Secretive.AgentRequestParser",
+ requirement: .isFromSameTeam(),
+ incomingSessionHandler: handleRequest(_:)
+ )
+ } else {
+ _ = try XPCListener(service: "com.maxgoedjen.Secretive.AgentRequestParser", incomingSessionHandler: handleRequest(_:))
+ }
+ logger.log("Parser initialized")
+ dispatchMain()
+} catch {
+ logger.error("Failed to create parser, error: \(error)")
+}
diff --git a/Sources/Config/Secretive.xctestplan b/Sources/Config/Secretive.xctestplan
index fb2bac9..61b77d0 100644
--- a/Sources/Config/Secretive.xctestplan
+++ b/Sources/Config/Secretive.xctestplan
@@ -13,12 +13,24 @@
},
"testTargets" : [
{
- "enabled" : false,
- "parallelizable" : true,
"target" : {
- "containerPath" : "container:Secretive.xcodeproj",
- "identifier" : "50617D9323FCE48E0099B055",
- "name" : "SecretiveTests"
+ "containerPath" : "container:Packages",
+ "identifier" : "BriefTests",
+ "name" : "BriefTests"
+ }
+ },
+ {
+ "target" : {
+ "containerPath" : "container:Packages",
+ "identifier" : "SecretKitTests",
+ "name" : "SecretKitTests"
+ }
+ },
+ {
+ "target" : {
+ "containerPath" : "container:Packages",
+ "identifier" : "SecretAgentKitTests",
+ "name" : "SecretAgentKitTests"
}
}
],
diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift
index 8acfb24..dec30d6 100644
--- a/Sources/Packages/Package.swift
+++ b/Sources/Packages/Package.swift
@@ -21,13 +21,16 @@ let package = Package(
targets: ["SmartCardSecretKit"]),
.library(
name: "SecretAgentKit",
- targets: ["SecretAgentKit"]),
+ targets: ["SecretAgentKit", "XPCWrappers"]),
.library(
name: "SecretAgentKitHeaders",
targets: ["SecretAgentKitHeaders"]),
.library(
name: "Brief",
targets: ["Brief"]),
+ .library(
+ name: "XPCWrappers",
+ targets: ["XPCWrappers"]),
],
dependencies: [
],
@@ -70,7 +73,7 @@ let package = Package(
),
.target(
name: "Brief",
- dependencies: [],
+ dependencies: ["XPCWrappers"],
resources: [localization],
swiftSettings: swiftSettings,
),
@@ -78,6 +81,10 @@ let package = Package(
name: "BriefTests",
dependencies: ["Brief"],
),
+ .target(
+ name: "XPCWrappers",
+ swiftSettings: swiftSettings,
+ ),
]
)
diff --git a/Sources/Packages/Sources/Brief/Updater.swift b/Sources/Packages/Sources/Brief/Updater.swift
index 600ddc5..7491378 100644
--- a/Sources/Packages/Sources/Brief/Updater.swift
+++ b/Sources/Packages/Sources/Brief/Updater.swift
@@ -1,5 +1,6 @@
import Foundation
import Observation
+import XPCWrappers
/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version.
@Observable public final class Updater: UpdaterProtocol, Sendable {
@@ -33,27 +34,25 @@ import Observation
) {
self.osVersion = osVersion
self.currentVersion = currentVersion
- if checkOnLaunch {
- // Don't do a launch check if the user hasn't seen the setup prompt explaining updater yet.
- Task {
- await checkForUpdates()
- }
- }
Task {
+ if checkOnLaunch {
+ try await checkForUpdates()
+ }
while !Task.isCancelled {
try? await Task.sleep(for: .seconds(Int(checkFrequency)))
- await checkForUpdates()
+ try await checkForUpdates()
}
}
}
/// Manually trigger an update check.
- public func checkForUpdates() async {
- guard let (data, _) = try? await URLSession.shared.data(from: Constants.updateURL) else { return }
- guard let releases = try? JSONDecoder().decode([Release].self, from: data) else { return }
- await evaluate(releases: releases)
+ public func checkForUpdates() async throws {
+ let session = try XPCTypedSession<[Release], Never>(serviceName: "com.maxgoedjen.Secretive.ReleasesDownloader")
+ await evaluate(releases: try await session.send())
+ session.complete()
}
+
/// Ignores a specified release. `update` will be nil if the user has ignored the latest available release.
/// - Parameter release: The release to ignore.
public func ignore(release: Release) async {
@@ -100,11 +99,3 @@ extension Updater {
}
}
-
-extension Updater {
-
- enum Constants {
- static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")!
- }
-
-}
diff --git a/Sources/Packages/Sources/SecretAgentKit/Agent.swift b/Sources/Packages/Sources/SecretAgentKit/Agent.swift
index 302fa3d..7fe268d 100644
--- a/Sources/Packages/Sources/SecretAgentKit/Agent.swift
+++ b/Sources/Packages/Sources/SecretAgentKit/Agent.swift
@@ -31,49 +31,29 @@ public final class Agent: Sendable {
extension Agent {
- /// Handles an incoming request.
- /// - Parameters:
- /// - data: The data to handle.
- /// - provenance: The origin of the request.
- /// - Returns: A response data payload.
- public func handle(data: Data, provenance: SigningRequestProvenance) async throws -> Data {
- logger.debug("Agent handling new data")
- guard data.count > 4 else {
- throw InvalidDataProvidedError()
- }
- let requestTypeInt = data[4]
- guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
- logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription) for unknown request type \(requestTypeInt)")
- return SSHAgent.ResponseType.agentFailure.data.lengthAndData
- }
- logger.debug("Agent handling request of type \(requestType.debugDescription)")
- let subData = Data(data[5...])
- let response = await handle(requestType: requestType, data: subData, provenance: provenance)
- return response
- }
-
- private func handle(requestType: SSHAgent.RequestType, data: Data, provenance: SigningRequestProvenance) async -> Data {
+ public func handle(request: SSHAgent.Request, provenance: SigningRequestProvenance) async -> Data {
// Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting
await reloadSecretsIfNeccessary()
var response = Data()
do {
- switch requestType {
+ switch request {
case .requestIdentities:
- response.append(SSHAgent.ResponseType.agentIdentitiesAnswer.data)
+ response.append(SSHAgent.Response.agentIdentitiesAnswer.data)
response.append(await identities())
- logger.debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)")
- case .signRequest:
- response.append(SSHAgent.ResponseType.agentSignResponse.data)
- response.append(try await sign(data: data, provenance: provenance))
- logger.debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)")
+ logger.debug("Agent returned \(SSHAgent.Response.agentIdentitiesAnswer.debugDescription)")
+ case .signRequest(let context):
+ response.append(SSHAgent.Response.agentSignResponse.data)
+ response.append(try await sign(data: context.dataToSign, keyBlob: context.keyBlob, provenance: provenance))
+ logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
+ case .unknown(let value):
+ logger.error("Agent received unknown request of type \(value).")
default:
- logger.debug("Agent received valid request of type \(requestType.debugDescription), but not currently supported.")
- response.append(SSHAgent.ResponseType.agentFailure.data)
-
+ logger.debug("Agent received valid request of type \(request.debugDescription), but not currently supported.")
+ throw UnhandledRequestError()
}
} catch {
- response = SSHAgent.ResponseType.agentFailure.data
- logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
+ response = SSHAgent.Response.agentFailure.data
+ logger.debug("Agent returned \(SSHAgent.Response.agentFailure.debugDescription)")
}
return response.lengthAndData
}
@@ -113,27 +93,16 @@ 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, provenance: SigningRequestProvenance) async throws -> Data {
- let reader = OpenSSHReader(data: data)
- let payloadHash = try reader.readNextChunk()
- let hash: Data
-
- // Check if hash is actually an openssh certificate and reconstruct the public key if it is
- if let certificatePublicKey = await certificateHandler.publicKeyHash(from: payloadHash) {
- hash = certificatePublicKey
- } else {
- hash = payloadHash
- }
-
- guard let (secret, store) = await secret(matching: hash) else {
- logger.debug("Agent did not have a key matching \(hash as NSData)")
+ func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance) async throws -> Data {
+ guard let (secret, store) = await secret(matching: keyBlob) else {
+ let keyBlobHex = keyBlob.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }.joined()
+ logger.debug("Agent did not have a key matching \(keyBlobHex)")
throw NoMatchingKeyError()
}
try await witness?.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance)
- let dataToSign = try reader.readNextChunk()
- let rawRepresentation = try await store.sign(data: dataToSign, with: secret, for: provenance)
+ let rawRepresentation = try await store.sign(data: data, with: secret, for: provenance)
let signedData = signatureWriter.data(secret: secret, signature: rawRepresentation)
try await witness?.witness(accessTo: secret, from: store, by: provenance)
@@ -172,16 +141,16 @@ extension Agent {
extension Agent {
- struct InvalidDataProvidedError: Error {}
struct NoMatchingKeyError: Error {}
+ struct UnhandledRequestError: Error {}
}
-extension SSHAgent.ResponseType {
+extension SSHAgent.Response {
var data: Data {
var raw = self.rawValue
- return Data(bytes: &raw, count: UInt8.bitWidth/8)
+ return Data(bytes: &raw, count: MemoryLayout.size)
}
}
diff --git a/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift b/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift
index 83ade57..f95865e 100644
--- a/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift
+++ b/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift
@@ -3,7 +3,7 @@ import Foundation
extension FileHandle {
public var pidOfConnectedProcess: Int32 {
- let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 1)
+ let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout.size, alignment: 1)
var len = socklen_t(MemoryLayout.size)
getsockopt(fileDescriptor, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len)
return pidPointer.load(as: Int32.self)
diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift b/Sources/Packages/Sources/SecretAgentKit/OpenSSHCertificateHandler.swift
similarity index 73%
rename from Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift
rename to Sources/Packages/Sources/SecretAgentKit/OpenSSHCertificateHandler.swift
index a5af72c..5451e49 100644
--- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift
+++ b/Sources/Packages/Sources/SecretAgentKit/OpenSSHCertificateHandler.swift
@@ -1,5 +1,6 @@
import Foundation
import OSLog
+import SecretKit
/// Manages storage and lookup for OpenSSH certificates.
public actor OpenSSHCertificateHandler: Sendable {
@@ -25,33 +26,6 @@ public actor OpenSSHCertificateHandler: Sendable {
}
}
- /// Reconstructs a public key from a ``Data``, if that ``Data`` contains an OpenSSH certificate hash. Currently only ecdsa certificates are supported
- /// - Parameter certBlock: The openssh certificate to extract the public key from
- /// - Returns: A ``Data`` object containing the public key in OpenSSH wire format if the ``Data`` is an OpenSSH certificate hash, otherwise nil.
- public func publicKeyHash(from hash: Data) -> Data? {
- let reader = OpenSSHReader(data: hash)
- do {
- let certType = String(decoding: try reader.readNextChunk(), as: UTF8.self)
- switch certType {
- case "ecdsa-sha2-nistp256-cert-v01@openssh.com",
- "ecdsa-sha2-nistp384-cert-v01@openssh.com",
- "ecdsa-sha2-nistp521-cert-v01@openssh.com":
- _ = try reader.readNextChunk() // nonce
- let curveIdentifier = try reader.readNextChunk()
- let publicKey = try reader.readNextChunk()
-
- let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "")
- return openSSHIdentifier.lengthAndData +
- curveIdentifier.lengthAndData +
- publicKey.lengthAndData
- default:
- return nil
- }
- } catch {
- return nil
- }
- }
-
/// Attempts to find an OpenSSH Certificate that corresponds to a ``Secret``
/// - Parameter secret: The secret to search for a certificate with
/// - Returns: A (``Data``, ``Data``) tuple containing the certificate and certificate name, respectively.
diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHReader.swift b/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift
similarity index 51%
rename from Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHReader.swift
rename to Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift
index 22417cb..d4d5103 100644
--- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHReader.swift
+++ b/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift
@@ -1,42 +1,47 @@
import Foundation
/// Reads OpenSSH protocol data.
-public final class OpenSSHReader {
+final class OpenSSHReader {
var remaining: Data
/// Initialize the reader with an OpenSSH data payload.
/// - Parameter data: The data to read.
- public init(data: Data) {
+ init(data: Data) {
remaining = Data(data)
}
/// Reads the next chunk of data from the playload.
/// - Returns: The next chunk of data.
- public func readNextChunk(convertEndianness: Bool = true) throws -> Data {
+ func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> Data {
let littleEndianLength = try readNextBytes(as: UInt32.self)
let length = convertEndianness ? Int(littleEndianLength.bigEndian) : Int(littleEndianLength)
- guard remaining.count >= length else { throw EndOfData() }
+ guard remaining.count >= length else { throw .beyondBounds }
let dataRange = 0..(as: T.Type) throws -> T {
+ func readNextBytes(as: T.Type) throws(OpenSSHReaderError) -> T {
let size = MemoryLayout.size
- guard remaining.count >= size else { throw EndOfData() }
+ guard remaining.count >= size else { throw .beyondBounds }
let lengthRange = 0.. String {
- try String(decoding: readNextChunk(), as: UTF8.self)
+ func readNextChunkAsString(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> String {
+ try String(decoding: readNextChunk(convertEndianness: convertEndianness), as: UTF8.self)
}
- public struct EndOfData: Error {}
+ func readNextChunkAsSubReader(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> OpenSSHReader {
+ OpenSSHReader(data: try readNextChunk(convertEndianness: convertEndianness))
+ }
}
+
+public enum OpenSSHReaderError: Error, Codable {
+ case beyondBounds
+}
diff --git a/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift b/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift
new file mode 100644
index 0000000..c6a44f6
--- /dev/null
+++ b/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift
@@ -0,0 +1,109 @@
+import Foundation
+import OSLog
+import SecretKit
+
+public protocol SSHAgentInputParserProtocol: Sendable {
+
+ func parse(data: Data) async throws -> SSHAgent.Request
+
+}
+
+public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
+
+ private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "InputParser")
+
+ public init() {
+
+ }
+
+ public func parse(data: Data) throws(AgentParsingError) -> SSHAgent.Request {
+ logger.debug("Parsing new data")
+ guard data.count > 4 else {
+ throw .invalidData
+ }
+ let specifiedLength = (data[0..<4].bytes.unsafeLoad(as: UInt32.self).bigEndian) + 4
+ let rawRequestInt = data[4]
+ let remainingDataRange = 5.. SSHAgent.Request.SignatureRequestContext {
+ let reader = OpenSSHReader(data: data)
+ let rawKeyBlob = try reader.readNextChunk()
+ let keyBlob = certificatePublicKeyBlob(from: rawKeyBlob) ?? rawKeyBlob
+ let dataToSign = try reader.readNextChunk()
+ return SSHAgent.Request.SignatureRequestContext(keyBlob: keyBlob, dataToSign: dataToSign)
+ }
+
+ func certificatePublicKeyBlob(from hash: Data) -> Data? {
+ let reader = OpenSSHReader(data: hash)
+ do {
+ let certType = String(decoding: try reader.readNextChunk(), as: UTF8.self)
+ switch certType {
+ case "ecdsa-sha2-nistp256-cert-v01@openssh.com",
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com",
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com":
+ _ = try reader.readNextChunk() // nonce
+ let curveIdentifier = try reader.readNextChunk()
+ let publicKey = try reader.readNextChunk()
+ let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "")
+ return openSSHIdentifier.lengthAndData +
+ curveIdentifier.lengthAndData +
+ publicKey.lengthAndData
+ default:
+ return nil
+ }
+ } catch {
+ return nil
+ }
+ }
+
+}
+
+
+extension SSHAgentInputParser {
+
+ public enum AgentParsingError: Error, Codable {
+ case unknownRequest
+ case unhandledRequest
+ case invalidData
+ case openSSHReader(OpenSSHReaderError)
+ }
+
+}
diff --git a/Sources/Packages/Sources/SecretAgentKit/SSHAgentProtocol.swift b/Sources/Packages/Sources/SecretAgentKit/SSHAgentProtocol.swift
index 30b4747..0007989 100644
--- a/Sources/Packages/Sources/SecretAgentKit/SSHAgentProtocol.swift
+++ b/Sources/Packages/Sources/SecretAgentKit/SSHAgentProtocol.swift
@@ -6,21 +6,39 @@ public enum SSHAgent {}
extension SSHAgent {
/// The type of the SSH Agent Request, as described in https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent#section-5.1
- public enum RequestType: UInt8, CustomDebugStringConvertible {
+ public enum Request: CustomDebugStringConvertible, Codable, Sendable {
- case requestIdentities = 11
- case signRequest = 13
- case addIdentity = 17
- case removeIdentity = 18
- case removeAllIdentities = 19
- case addIDConstrained = 25
- case addSmartcardKey = 20
- case removeSmartcardKey = 21
- case lock = 22
- case unlock = 23
- case addSmartcardKeyConstrained = 26
- case protocolExtension = 27
+ case requestIdentities
+ case signRequest(SignatureRequestContext)
+ case addIdentity
+ case removeIdentity
+ case removeAllIdentities
+ case addIDConstrained
+ case addSmartcardKey
+ case removeSmartcardKey
+ case lock
+ case unlock
+ case addSmartcardKeyConstrained
+ case protocolExtension
+ case unknown(UInt8)
+ public var protocolID: UInt8 {
+ switch self {
+ case .requestIdentities: 11
+ case .signRequest: 13
+ case .addIdentity: 17
+ case .removeIdentity: 18
+ case .removeAllIdentities: 19
+ case .addIDConstrained: 25
+ case .addSmartcardKey: 20
+ case .removeSmartcardKey: 21
+ case .lock: 22
+ case .unlock: 23
+ case .addSmartcardKeyConstrained: 26
+ case .protocolExtension: 27
+ case .unknown(let value): value
+ }
+ }
public var debugDescription: String {
switch self {
@@ -36,12 +54,28 @@ extension SSHAgent {
case .unlock: "SSH_AGENTC_UNLOCK"
case .addSmartcardKeyConstrained: "SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED"
case .protocolExtension: "SSH_AGENTC_EXTENSION"
+ case .unknown: "UNKNOWN_MESSAGE"
}
}
+
+ public struct SignatureRequestContext: Sendable, Codable {
+ public let keyBlob: Data
+ public let dataToSign: Data
+
+ public init(keyBlob: Data, dataToSign: Data) {
+ self.keyBlob = keyBlob
+ self.dataToSign = dataToSign
+ }
+
+ public static var empty: SignatureRequestContext {
+ SignatureRequestContext(keyBlob: Data(), dataToSign: Data())
+ }
+ }
+
}
/// The type of the SSH Agent Response, as described in https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent#section-5.1
- public enum ResponseType: UInt8, CustomDebugStringConvertible {
+ public enum Response: UInt8, CustomDebugStringConvertible {
case agentFailure = 5
case agentSuccess = 6
diff --git a/Sources/Packages/Sources/SecretAgentKit/SigningRequestTracer.swift b/Sources/Packages/Sources/SecretAgentKit/SigningRequestTracer.swift
index a4f683a..2872825 100644
--- a/Sources/Packages/Sources/SecretAgentKit/SigningRequestTracer.swift
+++ b/Sources/Packages/Sources/SecretAgentKit/SigningRequestTracer.swift
@@ -13,9 +13,8 @@ extension SigningRequestTracer {
/// Generates a ``SecretKit.SigningRequestProvenance`` from a ``FileHandle``.
/// - Parameter fileHandle: The reader involved in processing the request.
/// - Returns: A ``SecretKit.SigningRequestProvenance`` describing the origin of the request.
- func provenance(from fileHandleReader: FileHandle) -> SigningRequestProvenance {
- let firstInfo = process(from: fileHandleReader.pidOfConnectedProcess)
-
+ func provenance(from fileHandle: FileHandle) -> SigningRequestProvenance {
+ let firstInfo = process(from: fileHandle.pidOfConnectedProcess)
var provenance = SigningRequestProvenance(root: firstInfo)
while NSRunningApplication(processIdentifier: provenance.origin.pid) == nil && provenance.origin.parentPID != nil {
provenance.chain.append(process(from: provenance.origin.parentPID!))
diff --git a/Sources/Packages/Sources/XPCWrappers/XPCWrappers.swift b/Sources/Packages/Sources/XPCWrappers/XPCWrappers.swift
new file mode 100644
index 0000000..21515eb
--- /dev/null
+++ b/Sources/Packages/Sources/XPCWrappers/XPCWrappers.swift
@@ -0,0 +1,49 @@
+import Foundation
+
+public struct XPCTypedSession: Sendable {
+
+ private let session: XPCSession
+
+ public init(serviceName: String, warmup: Bool = false) throws {
+ if #available(macOS 26.0, *) {
+ session = try XPCSession(xpcService: serviceName, requirement: .isFromSameTeam())
+ } else {
+ session = try XPCSession(xpcService: serviceName)
+ }
+ if warmup {
+ Task { [self] in
+ _ = try? await send()
+ }
+ }
+ }
+
+ public func send(_ message: some Encodable = Data()) async throws -> ResponseType {
+ try await withCheckedThrowingContinuation { continuation in
+ do {
+ try session.send(message) { result in
+ switch result {
+ case .success(let message):
+ if let result = try? message.decode(as: ResponseType.self) {
+ continuation.resume(returning: result)
+ } else if let error = try? message.decode(as: ErrorType.self) {
+ continuation.resume(throwing: error)
+ } else {
+ continuation.resume(throwing: UnknownMessageError())
+ }
+ case .failure(let error):
+ continuation.resume(throwing: error)
+ }
+ }
+ } catch {
+ continuation.resume(throwing: error)
+ }
+ }
+ }
+
+ public func complete() {
+ session.cancel(reason: "Done")
+ }
+
+}
+
+public struct UnknownMessageError: Error, Codable {}
diff --git a/Sources/Packages/Tests/SecretAgentKitTests/AgentTests.swift b/Sources/Packages/Tests/SecretAgentKitTests/AgentTests.swift
index c542559..bbef669 100644
--- a/Sources/Packages/Tests/SecretAgentKitTests/AgentTests.swift
+++ b/Sources/Packages/Tests/SecretAgentKitTests/AgentTests.swift
@@ -8,19 +8,22 @@ import CryptoKit
// MARK: Identity Listing
-
-// let testProvenance = SigningRequestProvenance(root: .init(pid: 0, processName: "Test", appName: "Test", iconURL: nil, path: /, validSignature: true, parentPID: nil))
-
@Test func emptyStores() async throws {
let agent = Agent(storeList: SecretStoreList())
- let response = try await agent.handle(data: Constants.Requests.requestIdentities, provenance: .test)
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestIdentities)
+ let response = await agent.handle(request: request, provenance: .test)
#expect(response == Constants.Responses.requestIdentitiesEmpty)
}
@Test func identitiesList() async throws {
let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
let agent = Agent(storeList: list)
- let response = try await agent.handle(data: Constants.Requests.requestIdentities, provenance: .test)
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestIdentities)
+ let response = await agent.handle(request: request, provenance: .test)
+
+ let actual = OpenSSHReader(data: response)
+ let expected = OpenSSHReader(data: Constants.Responses.requestIdentitiesMultiple)
+ print(actual, expected)
#expect(response == Constants.Responses.requestIdentitiesMultiple)
}
@@ -29,40 +32,42 @@ import CryptoKit
@Test func noMatchingIdentities() async throws {
let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
let agent = Agent(storeList: list)
- let response = try await agent.handle(data: Constants.Requests.requestSignatureWithNoneMatching, provenance: .test)
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignatureWithNoneMatching)
+ let response = await agent.handle(request: request, provenance: .test)
#expect(response == Constants.Responses.requestFailure)
}
-// @Test func ecdsaSignature() async throws {
-// let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature)
-// let requestReader = OpenSSHReader(data: Constants.Requests.requestSignature[5...])
-// _ = requestReader.readNextChunk()
-// let dataToSign = requestReader.readNextChunk()
-// let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
-// let agent = Agent(storeList: list)
-// await agent.handle(reader: stubReader, writer: stubWriter)
-// let outer = OpenSSHReader(data: stubWriter.data[5...])
-// let payload = outer.readNextChunk()
-// let inner = OpenSSHReader(data: payload)
-// _ = inner.readNextChunk()
-// let signedData = inner.readNextChunk()
-// let rsData = OpenSSHReader(data: signedData)
-// var r = rsData.readNextChunk()
-// var s = rsData.readNextChunk()
-// // This is fine IRL, but it freaks out CryptoKit
-// if r[0] == 0 {
-// r.removeFirst()
-// }
-// if s[0] == 0 {
-// s.removeFirst()
-// }
-// var rs = r
-// rs.append(s)
-// let signature = try P256.Signing.ECDSASignature(rawRepresentation: rs)
-// // Correct signature
-// #expect(try P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey)
-// .isValidSignature(signature, for: dataToSign))
-// }
+ @Test func ecdsaSignature() async throws {
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
+ guard case SSHAgent.Request.signRequest(let context) = request else { return }
+ let list = await storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret])
+ let agent = Agent(storeList: list)
+ let response = await agent.handle(request: request, provenance: .test)
+ let responseReader = OpenSSHReader(data: response)
+ let length = try responseReader.readNextBytes(as: UInt32.self).bigEndian
+ let type = try responseReader.readNextBytes(as: UInt8.self).bigEndian
+ #expect(length == response.count - MemoryLayout.size)
+ #expect(type == SSHAgent.Response.agentSignResponse.rawValue)
+ let outer = OpenSSHReader(data: responseReader.remaining)
+ let inner = try outer.readNextChunkAsSubReader()
+ _ = try inner.readNextChunk()
+ let rsData = try inner.readNextChunkAsSubReader()
+ var r = try rsData.readNextChunk()
+ var s = try rsData.readNextChunk()
+ // This is fine IRL, but it freaks out CryptoKit
+ if r[0] == 0 {
+ r.removeFirst()
+ }
+ if s[0] == 0 {
+ s.removeFirst()
+ }
+ var rs = r
+ rs.append(s)
+ let signature = try P256.Signing.ECDSASignature(rawRepresentation: rs)
+ // Correct signature
+ #expect(try P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey)
+ .isValidSignature(signature, for: context.dataToSign))
+ }
// MARK: Witness protocol
@@ -72,7 +77,7 @@ import CryptoKit
return true
}, witness: { _, _ in })
let agent = Agent(storeList: list, witness: witness)
- let response = try await agent.handle(data: Constants.Requests.requestSignature, provenance: .test)
+ let response = await agent.handle(request: .signRequest(.empty), provenance: .test)
#expect(response == Constants.Responses.requestFailure)
}
@@ -85,7 +90,8 @@ import CryptoKit
witnessed = true
})
let agent = Agent(storeList: list, witness: witness)
- _ = try await agent.handle(data: Constants.Requests.requestSignature, provenance: .test)
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
+ _ = await agent.handle(request: request, provenance: .test)
#expect(witnessed)
}
@@ -100,7 +106,8 @@ import CryptoKit
witnessTrace = trace
})
let agent = Agent(storeList: list, witness: witness)
- _ = try await agent.handle(data: Constants.Requests.requestSignature, provenance: .test)
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
+ _ = await agent.handle(request: request, provenance: .test)
#expect(witnessTrace == speakNowTrace)
#expect(witnessTrace == .test)
}
@@ -112,7 +119,8 @@ import CryptoKit
let store = await list.stores.first?.base as! Stub.Store
store.shouldThrow = true
let agent = Agent(storeList: list)
- let response = try await agent.handle(data: Constants.Requests.requestSignature, provenance: .test)
+ let request = try SSHAgentInputParser().parse(data: Constants.Requests.requestSignature)
+ let response = await agent.handle(request: request, provenance: .test)
#expect(response == Constants.Responses.requestFailure)
}
@@ -120,7 +128,7 @@ import CryptoKit
@Test func unhandledAdd() async throws {
let agent = Agent(storeList: SecretStoreList())
- let response = try await agent.handle(data: Constants.Requests.addIdentity, provenance: .test)
+ let response = await agent.handle(request: .addIdentity, provenance: .test)
#expect(response == Constants.Responses.requestFailure)
}
@@ -146,14 +154,13 @@ extension AgentTests {
enum Requests {
static let requestIdentities = Data(base64Encoded: "AAAAAQs=")!
- static let addIdentity = Data(base64Encoded: "AAAAARE=")!
static let requestSignatureWithNoneMatching = Data(base64Encoded: "AAABhA0AAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBEqCbkJbOHy5S1wVCaJoKPmpS0egM4frMqllgnlRRQ/Uvnn6EVS8oV03cPA2Bz0EdESyRKA/sbmn0aBtgjIwGELxu45UXEW1TEz6TxyS0u3vuIqR3Wo1CrQWRDnkrG/pBQAAAO8AAAAgbqmrqPUtJ8mmrtaSVexjMYyXWNqjHSnoto7zgv86xvcyAAAAA2dpdAAAAA5zc2gtY29ubmVjdGlvbgAAAAlwdWJsaWNrZXkBAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBEqCbkJbOHy5S1wVCaJoKPmpS0egM4frMqllgnlRRQ/Uvnn6EVS8oV03cPA2Bz0EdESyRKA/sbmn0aBtgjIwGELxu45UXEW1TEz6TxyS0u3vuIqR3Wo1CrQWRDnkrG/pBQAAAAA=")!
static let requestSignature = Data(base64Encoded: "AAABRA0AAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy08AAADPAAAAIBIFsbCZ4/dhBmLNGHm0GKj7EJ4N8k/jXRxlyg+LFIYzMgAAAANnaXQAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAAA==")!
}
enum Responses {
static let requestIdentitiesEmpty = Data(base64Encoded: "AAAABQwAAAAA")!
- static let requestIdentitiesMultiple = Data(base64Encoded: "AAABKwwAAAACAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0")!
+ static let requestIdentitiesMultiple = Data(base64Encoded: "AAABLwwAAAACAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAFWVjZHNhLTI1NkBleGFtcGxlLmNvbQAAAIgAAAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAAhuaXN0cDM4NAAAAGEEspLMDmreMJverQkqKC9zF9ZUasn5uSWkbRlz1jNTCtuyH1KKm+VImL6wdAj47SbzwM6lEEC24AdfrR64P9i/bnS2i83v/4wQVtcZn+Et13QGgWlZst8lxCPzTookaVwMAAAAFWVjZHNhLTM4NEBleGFtcGxlLmNvbQ==")!
static let requestFailure = Data(base64Encoded: "AAAAAQU=")!
}
diff --git a/Sources/Packages/Tests/SecretKitTests/OpenSSHReaderTests.swift b/Sources/Packages/Tests/SecretAgentKitTests/OpenSSHReaderTests.swift
similarity index 98%
rename from Sources/Packages/Tests/SecretKitTests/OpenSSHReaderTests.swift
rename to Sources/Packages/Tests/SecretAgentKitTests/OpenSSHReaderTests.swift
index 65d93cf..34201c6 100644
--- a/Sources/Packages/Tests/SecretKitTests/OpenSSHReaderTests.swift
+++ b/Sources/Packages/Tests/SecretAgentKitTests/OpenSSHReaderTests.swift
@@ -1,6 +1,6 @@
import Foundation
import Testing
-@testable import SecretKit
+@testable import SecretAgentKit
@testable import SecureEnclaveSecretKit
@testable import SmartCardSecretKit
diff --git a/Sources/Packages/Tests/SecretAgentKitTests/StubStore.swift b/Sources/Packages/Tests/SecretAgentKitTests/StubStore.swift
index 222588a..c3a01d7 100644
--- a/Sources/Packages/Tests/SecretAgentKitTests/StubStore.swift
+++ b/Sources/Packages/Tests/SecretAgentKitTests/StubStore.swift
@@ -82,7 +82,7 @@ extension Stub {
let privateKey: Data
init(keySize: Int, publicKey: Data, privateKey: Data) {
- self.attributes = Attributes(keyType: .init(algorithm: .ecdsa, size: keySize), authentication: .notRequired)
+ self.attributes = Attributes(keyType: .init(algorithm: .ecdsa, size: keySize), authentication: .notRequired, publicKeyAttribution: "ecdsa-\(keySize)@example.com")
self.publicKey = publicKey
self.privateKey = privateKey
}
diff --git a/Sources/ReleasesDownloader/Info.plist b/Sources/ReleasesDownloader/Info.plist
new file mode 100644
index 0000000..c123a5d
--- /dev/null
+++ b/Sources/ReleasesDownloader/Info.plist
@@ -0,0 +1,11 @@
+
+
+
+
+ XPCService
+
+ ServiceType
+ Application
+
+
+
diff --git a/Sources/ReleasesDownloader/main.swift b/Sources/ReleasesDownloader/main.swift
new file mode 100644
index 0000000..622e0bf
--- /dev/null
+++ b/Sources/ReleasesDownloader/main.swift
@@ -0,0 +1,44 @@
+import XPC
+import OSLog
+import Brief
+
+private let logger = Logger(subsystem: "com.maxgoedjen.secretive.ReleasesDownloader", category: "ReleasesDownloader")
+
+enum Constants {
+ static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")!
+}
+
+func handleRequest(_ request: XPCListener.IncomingSessionRequest) -> XPCListener.IncomingSessionRequest.Decision {
+ logger.log("ReleasesDownloader received inbound request")
+ return request.accept { xpcDictionary in
+ xpcDictionary.handoffReply(to: .global(qos: .userInteractive)) {
+ logger.log("ReleasesDownloader accepted inbound request")
+ Task {
+ do {
+ let (data, _) = try await URLSession.shared.data(from: Constants.updateURL)
+ let releases = try JSONDecoder().decode([Release].self, from: data)
+ xpcDictionary.reply(releases)
+ } catch {
+ logger.error("ReleasesDownloader failed with unknown error \(error)")
+ xpcDictionary.reply([] as [Release])
+ }
+ }
+ }
+ }
+}
+
+do {
+ if #available(macOS 26.0, *) {
+ _ = try XPCListener(
+ service: "com.maxgoedjen.Secretive.ReleasesDownloader",
+ requirement: .isFromSameTeam(),
+ incomingSessionHandler: handleRequest(_:)
+ )
+ } else {
+ _ = try XPCListener(service: "com.maxgoedjen.Secretive.ReleasesDownloader", incomingSessionHandler: handleRequest(_:))
+ }
+ logger.log("ReleasesDownloader initialized")
+ dispatchMain()
+} catch {
+ logger.error("Failed to create ReleasesDownloader, error: \(error)")
+}
diff --git a/Sources/SecretAgent/AppDelegate.swift b/Sources/SecretAgent/AppDelegate.swift
index ee4b799..237f595 100644
--- a/Sources/SecretAgent/AppDelegate.swift
+++ b/Sources/SecretAgent/AppDelegate.swift
@@ -34,11 +34,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
logger.debug("SecretAgent finished launching")
Task {
+ let inputParser = try XPCAgentInputParser()
for await session in socketController.sessions {
Task {
do {
for await message in session.messages {
- let agentResponse = try await agent.handle(data: message, provenance: session.provenance)
+ let request = try await inputParser.parse(data: message)
+ let agentResponse = await agent.handle(request: request, provenance: session.provenance)
try await session.write(agentResponse)
}
} catch {
diff --git a/Sources/SecretAgent/XPCInputParser.swift b/Sources/SecretAgent/XPCInputParser.swift
new file mode 100644
index 0000000..40ff327
--- /dev/null
+++ b/Sources/SecretAgent/XPCInputParser.swift
@@ -0,0 +1,23 @@
+import Foundation
+import SecretAgentKit
+import Brief
+import XPCWrappers
+
+/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
+public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
+
+ private let session: XPCTypedSession
+
+ public init() throws {
+ session = try XPCTypedSession(serviceName: "com.maxgoedjen.Secretive.AgentRequestParser", warmup: true)
+ }
+
+ public func parse(data: Data) async throws -> SSHAgent.Request {
+ try await session.send(data)
+ }
+
+ deinit {
+ session.complete()
+ }
+
+}
diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj
index 96c6479..c9fce11 100644
--- a/Sources/Secretive.xcodeproj/project.pbxproj
+++ b/Sources/Secretive.xcodeproj/project.pbxproj
@@ -21,10 +21,18 @@
5008C23E2E525D8900507AC2 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 5008C23D2E525D8200507AC2 /* Localizable.xcstrings */; };
5008C2402E52792400507AC2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; };
5008C2412E52D18700507AC2 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 5008C23D2E525D8200507AC2 /* Localizable.xcstrings */; };
+ 500F04EB2E6CDEB0001CF06E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500F04E82E6CDEB0001CF06E /* main.swift */; };
+ 500F04EF2E6CDEF4001CF06E /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 500F04EE2E6CDEF4001CF06E /* SecretAgentKit */; };
+ 500F04F02E6CDF20001CF06E /* AgentRequestParser.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 500F04D72E6CDEAB001CF06E /* AgentRequestParser.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
+ 501577C82E6BC5B4004A37D0 /* ReleasesDownloader.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 501577CF2E6BC5D4004A37D0 /* ReleasesDownloader.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 501577DA2E6BC5F3004A37D0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501577D62E6BC5F3004A37D0 /* main.swift */; };
+ 501577DF2E6BC647004A37D0 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501577DE2E6BC647004A37D0 /* Brief */; };
+ 501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501578122E6C0479004A37D0 /* XPCInputParser.swift */; };
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
504788EC2E680DC800B4556F /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788EB2E680DC400B4556F /* URLs.swift */; };
504788F22E681F3A00B4556F /* Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F12E681F3A00B4556F /* Instructions.swift */; };
@@ -64,6 +72,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 500F04F12E6CDF20001CF06E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 500F04D62E6CDEAB001CF06E;
+ remoteInfo = AgentRequestParser;
+ };
50142166278126B500BBAA70 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
@@ -71,9 +86,53 @@
remoteGlobalIDString = 50A3B78924026B7500D209EA;
remoteInfo = SecretAgent;
};
+ 501577C62E6BC5B4004A37D0 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 501577BC2E6BC5B4004A37D0;
+ remoteInfo = ReleasesDownloader;
+ };
+ 501577D02E6BC5D4004A37D0 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 501577BC2E6BC5B4004A37D0;
+ remoteInfo = ReleasesDownloader;
+ };
+ 501577D32E6BC5DD004A37D0 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 501577BC2E6BC5B4004A37D0;
+ remoteInfo = ReleasesDownloader;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
+ 501577C92E6BC5B4004A37D0 /* Embed XPC Services */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
+ dstSubfolderSpec = 16;
+ files = (
+ 501577C82E6BC5B4004A37D0 /* ReleasesDownloader.xpc in Embed XPC Services */,
+ );
+ name = "Embed XPC Services";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 501577D22E6BC5D4004A37D0 /* Embed XPC Services */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
+ dstSubfolderSpec = 16;
+ files = (
+ 500F04F02E6CDF20001CF06E /* AgentRequestParser.xpc in Embed XPC Services */,
+ 501577CF2E6BC5D4004A37D0 /* ReleasesDownloader.xpc in Embed XPC Services */,
+ );
+ name = "Embed XPC Services";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
50617DBF23FCE4AB0099B055 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -112,8 +171,15 @@
50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = ""; };
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = ""; };
5008C23D2E525D8200507AC2 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = Packages/Resources/Localizable.xcstrings; sourceTree = SOURCE_ROOT; };
+ 500F04D72E6CDEAB001CF06E /* AgentRequestParser.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = AgentRequestParser.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
+ 500F04E72E6CDEB0001CF06E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 500F04E82E6CDEB0001CF06E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = ""; };
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = ""; };
+ 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = ReleasesDownloader.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
+ 501577D52E6BC5F3004A37D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 501577D62E6BC5F3004A37D0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
+ 501578122E6C0479004A37D0 /* XPCInputParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCInputParser.swift; sourceTree = ""; };
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; };
504788EB2E680DC400B4556F /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = ""; };
504788F12E681F3A00B4556F /* Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instructions.swift; sourceTree = ""; };
@@ -161,6 +227,22 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 500F04D42E6CDEAB001CF06E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 500F04EF2E6CDEF4001CF06E /* SecretAgentKit in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 501577BA2E6BC5B4004A37D0 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 501577DF2E6BC647004A37D0 /* Brief in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
50617D7C23FCE48D0099B055 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -195,6 +277,24 @@
path = Helpers;
sourceTree = "";
};
+ 500F04E92E6CDEB0001CF06E /* AgentRequestParser */ = {
+ isa = PBXGroup;
+ children = (
+ 500F04E72E6CDEB0001CF06E /* Info.plist */,
+ 500F04E82E6CDEB0001CF06E /* main.swift */,
+ );
+ path = AgentRequestParser;
+ sourceTree = "";
+ };
+ 501577D92E6BC5F3004A37D0 /* ReleasesDownloader */ = {
+ isa = PBXGroup;
+ children = (
+ 501577D52E6BC5F3004A37D0 /* Info.plist */,
+ 501577D62E6BC5F3004A37D0 /* main.swift */,
+ );
+ path = ReleasesDownloader;
+ sourceTree = "";
+ };
504788ED2E681EB200B4556F /* Styles */ = {
isa = PBXGroup;
children = (
@@ -251,6 +351,8 @@
5003EF39278005C800DF2006 /* Packages */,
50617D8123FCE48E0099B055 /* Secretive */,
50A3B78B24026B7500D209EA /* SecretAgent */,
+ 501577D92E6BC5F3004A37D0 /* ReleasesDownloader */,
+ 500F04E92E6CDEB0001CF06E /* AgentRequestParser */,
508A58AF241E144C0069DC07 /* Config */,
50617D8023FCE48E0099B055 /* Products */,
5099A08B240243730062B6F2 /* Frameworks */,
@@ -262,6 +364,8 @@
children = (
50617D7F23FCE48E0099B055 /* Secretive.app */,
50A3B78A24026B7500D209EA /* SecretAgent.app */,
+ 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */,
+ 500F04D72E6CDEAB001CF06E /* AgentRequestParser.xpc */,
);
name = Products;
sourceTree = "";
@@ -339,6 +443,7 @@
children = (
50020BAF24064869003D4025 /* AppDelegate.swift */,
5018F54E24064786002EB505 /* Notifier.swift */,
+ 501578122E6C0479004A37D0 /* XPCInputParser.swift */,
50A3B79524026B7600D209EA /* Main.storyboard */,
50A3B79824026B7600D209EA /* Info.plist */,
508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */,
@@ -359,6 +464,46 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 500F04D62E6CDEAB001CF06E /* AgentRequestParser */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 500F04E22E6CDEAB001CF06E /* Build configuration list for PBXNativeTarget "AgentRequestParser" */;
+ buildPhases = (
+ 500F04D32E6CDEAB001CF06E /* Sources */,
+ 500F04D42E6CDEAB001CF06E /* Frameworks */,
+ 500F04D52E6CDEAB001CF06E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = AgentRequestParser;
+ packageProductDependencies = (
+ 500F04EE2E6CDEF4001CF06E /* SecretAgentKit */,
+ );
+ productName = AgentRequestParser;
+ productReference = 500F04D72E6CDEAB001CF06E /* AgentRequestParser.xpc */;
+ productType = "com.apple.product-type.xpc-service";
+ };
+ 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 501577CE2E6BC5B4004A37D0 /* Build configuration list for PBXNativeTarget "ReleasesDownloader" */;
+ buildPhases = (
+ 501577B92E6BC5B4004A37D0 /* Sources */,
+ 501577BA2E6BC5B4004A37D0 /* Frameworks */,
+ 501577BB2E6BC5B4004A37D0 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ReleasesDownloader;
+ packageProductDependencies = (
+ 501577DE2E6BC647004A37D0 /* Brief */,
+ );
+ productName = ReleasesDownloader;
+ productReference = 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */;
+ productType = "com.apple.product-type.xpc-service";
+ };
50617D7E23FCE48D0099B055 /* Secretive */ = {
isa = PBXNativeTarget;
buildConfigurationList = 50617D9D23FCE48E0099B055 /* Build configuration list for PBXNativeTarget "Secretive" */;
@@ -368,11 +513,13 @@
50617D7D23FCE48D0099B055 /* Resources */,
50617DBF23FCE4AB0099B055 /* Embed Frameworks */,
50C385AF240E438B00AF2719 /* CopyFiles */,
+ 501577C92E6BC5B4004A37D0 /* Embed XPC Services */,
);
buildRules = (
);
dependencies = (
50142167278126B500BBAA70 /* PBXTargetDependency */,
+ 501577C72E6BC5B4004A37D0 /* PBXTargetDependency */,
);
name = Secretive;
packageProductDependencies = (
@@ -393,10 +540,14 @@
50A3B78724026B7500D209EA /* Frameworks */,
50A3B78824026B7500D209EA /* Resources */,
50A5C18E240E4B4B00E2996C /* Embed Frameworks */,
+ 501577D22E6BC5D4004A37D0 /* Embed XPC Services */,
);
buildRules = (
);
dependencies = (
+ 501577D12E6BC5D4004A37D0 /* PBXTargetDependency */,
+ 501577D42E6BC5DD004A37D0 /* PBXTargetDependency */,
+ 500F04F22E6CDF20001CF06E /* PBXTargetDependency */,
);
name = SecretAgent;
packageProductDependencies = (
@@ -417,10 +568,16 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
- LastSwiftUpdateCheck = 1220;
+ LastSwiftUpdateCheck = 2600;
LastUpgradeCheck = 2600;
ORGANIZATIONNAME = "Max Goedjen";
TargetAttributes = {
+ 500F04D62E6CDEAB001CF06E = {
+ CreatedOnToolsVersion = 26.0;
+ };
+ 501577BC2E6BC5B4004A37D0 = {
+ CreatedOnToolsVersion = 26.0;
+ };
50617D7E23FCE48D0099B055 = {
CreatedOnToolsVersion = 11.3;
};
@@ -453,11 +610,27 @@
targets = (
50617D7E23FCE48D0099B055 /* Secretive */,
50A3B78924026B7500D209EA /* SecretAgent */,
+ 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */,
+ 500F04D62E6CDEAB001CF06E /* AgentRequestParser */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 500F04D52E6CDEAB001CF06E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 501577BB2E6BC5B4004A37D0 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
50617D7D23FCE48D0099B055 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -485,6 +658,22 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 500F04D32E6CDEAB001CF06E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 500F04EB2E6CDEB0001CF06E /* main.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 501577B92E6BC5B4004A37D0 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 501577DA2E6BC5F3004A37D0 /* main.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
50617D7B23FCE48D0099B055 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -530,17 +719,38 @@
files = (
50020BB024064869003D4025 /* AppDelegate.swift in Sources */,
5018F54F24064786002EB505 /* Notifier.swift in Sources */,
+ 501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 500F04F22E6CDF20001CF06E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 500F04D62E6CDEAB001CF06E /* AgentRequestParser */;
+ targetProxy = 500F04F12E6CDF20001CF06E /* PBXContainerItemProxy */;
+ };
50142167278126B500BBAA70 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 50A3B78924026B7500D209EA /* SecretAgent */;
targetProxy = 50142166278126B500BBAA70 /* PBXContainerItemProxy */;
};
+ 501577C72E6BC5B4004A37D0 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */;
+ targetProxy = 501577C62E6BC5B4004A37D0 /* PBXContainerItemProxy */;
+ };
+ 501577D12E6BC5D4004A37D0 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */;
+ targetProxy = 501577D02E6BC5D4004A37D0 /* PBXContainerItemProxy */;
+ };
+ 501577D42E6BC5DD004A37D0 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */;
+ targetProxy = 501577D32E6BC5DD004A37D0 /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -555,6 +765,223 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 500F04E32E6CDEAB001CF06E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Z72PRUAWF6;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = AgentRequestParser/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = AgentRequestParser;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.AgentRequestParser;
+ 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;
+ };
+ 500F04E42E6CDEAB001CF06E /* Test */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = AgentRequestParser/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = AgentRequestParser;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.AgentRequestParser;
+ 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;
+ };
+ 500F04E52E6CDEAB001CF06E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_IDENTITY = "Developer ID Application";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Z72PRUAWF6;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = AgentRequestParser/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = AgentRequestParser;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.AgentRequestParser;
+ 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;
+ };
+ 501577CA2E6BC5B4004A37D0 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Z72PRUAWF6;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
+ ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
+ ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
+ ENABLE_RESOURCE_ACCESS_CAMERA = NO;
+ ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
+ ENABLE_RESOURCE_ACCESS_LOCATION = NO;
+ ENABLE_RESOURCE_ACCESS_PRINTING = NO;
+ ENABLE_RESOURCE_ACCESS_USB = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = ReleasesDownloader/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader;
+ 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;
+ };
+ 501577CB2E6BC5B4004A37D0 /* Test */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Z72PRUAWF6;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
+ ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
+ ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
+ ENABLE_RESOURCE_ACCESS_CAMERA = NO;
+ ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
+ ENABLE_RESOURCE_ACCESS_LOCATION = NO;
+ ENABLE_RESOURCE_ACCESS_PRINTING = NO;
+ ENABLE_RESOURCE_ACCESS_USB = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = ReleasesDownloader/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader;
+ 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;
+ };
+ 501577CC2E6BC5B4004A37D0 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_IDENTITY = "Developer ID Application";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Z72PRUAWF6;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
+ ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
+ ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
+ ENABLE_RESOURCE_ACCESS_CAMERA = NO;
+ ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
+ ENABLE_RESOURCE_ACCESS_LOCATION = NO;
+ ENABLE_RESOURCE_ACCESS_PRINTING = NO;
+ ENABLE_RESOURCE_ACCESS_USB = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = ReleasesDownloader/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader;
+ 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 */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
@@ -712,7 +1139,7 @@
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
ENABLE_POINTER_AUTHENTICATION = YES;
ENABLE_PREVIEWS = YES;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
@@ -752,7 +1179,7 @@
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
ENABLE_POINTER_AUTHENTICATION = YES;
ENABLE_PREVIEWS = YES;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
@@ -864,7 +1291,7 @@
ENABLE_ENHANCED_SECURITY = YES;
ENABLE_HARDENED_RUNTIME = NO;
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
ENABLE_POINTER_AUTHENTICATION = YES;
ENABLE_PREVIEWS = YES;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
@@ -898,7 +1325,7 @@
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
ENABLE_PREVIEWS = YES;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
@@ -933,7 +1360,7 @@
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
ENABLE_PREVIEWS = YES;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
@@ -969,7 +1396,7 @@
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
ENABLE_PREVIEWS = YES;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
@@ -995,6 +1422,26 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 500F04E22E6CDEAB001CF06E /* Build configuration list for PBXNativeTarget "AgentRequestParser" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 500F04E32E6CDEAB001CF06E /* Debug */,
+ 500F04E42E6CDEAB001CF06E /* Test */,
+ 500F04E52E6CDEAB001CF06E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 501577CE2E6BC5B4004A37D0 /* Build configuration list for PBXNativeTarget "ReleasesDownloader" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 501577CA2E6BC5B4004A37D0 /* Debug */,
+ 501577CB2E6BC5B4004A37D0 /* Test */,
+ 501577CC2E6BC5B4004A37D0 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
50617D7A23FCE48D0099B055 /* Build configuration list for PBXProject "Secretive" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -1060,10 +1507,18 @@
isa = XCSwiftPackageProductDependency;
productName = SmartCardSecretKit;
};
+ 500F04EE2E6CDEF4001CF06E /* SecretAgentKit */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = SecretAgentKit;
+ };
501421612781262300BBAA70 /* Brief */ = {
isa = XCSwiftPackageProductDependency;
productName = Brief;
};
+ 501577DE2E6BC647004A37D0 /* Brief */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = Brief;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
diff --git a/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/PackageTests.xcscheme b/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/PackageTests.xcscheme
new file mode 100644
index 0000000..500661b
--- /dev/null
+++ b/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/PackageTests.xcscheme
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+