This commit is contained in:
Max Goedjen 2025-09-06 14:06:32 -07:00
parent 9c8810cc56
commit 40438076e9
No known key found for this signature in database
11 changed files with 426 additions and 91 deletions

View File

@ -0,0 +1,17 @@
import Foundation
import SecretAgentKit
//@objc public protocol RemoteAgentInputParserProtocol {
//
// func parse(data: Data, with reply: (SSHAgent.Request?, (any Error)?) -> Void)
//
//}
//
//class AgentInputParser: NSObject, RemoteAgentInputParserProtocol {
//
// /// This implements the example protocol. Replace the body of this class with the implementation of this service's protocol.
// @objc func performCalculation(firstNumber: Int, secondNumber: Int, with reply: @escaping (Int) -> Void) {
// let response = firstNumber + secondNumber
// reply(response)
// }
//}

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,40 @@
////
//// main.swift
//// AgentRequestParser
////
//// Created by Max Goedjen on 9/5/25.
//// Copyright © 2025 Max Goedjen. All rights reserved.
////
//
//import Foundation
//
//class ServiceDelegate: NSObject, NSXPCListenerDelegate {
//
// /// This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection.
// func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
//
// // Configure the connection.
// // First, set the interface that the exported object implements.
// newConnection.exportedInterface = NSXPCInterface(with: (any AgentRequestParserProtocol).self)
//
// // Next, set the object that the connection exports. All messages sent on the connection to this service will be sent to the exported object to handle. The connection retains the exported object.
// let exportedObject = AgentRequestParser()
// newConnection.exportedObject = exportedObject
//
// // Resuming the connection allows the system to deliver more incoming messages.
// newConnection.resume()
//
// // Returning true from this method tells the system that you have accepted this connection. If you want to reject the connection for some reason, call invalidate() on the connection and return false.
// return true
// }
//}
//
//// Create the delegate for the service.
//let delegate = ServiceDelegate()
//
//// Set up the one NSXPCListener for this service. It will handle all incoming connections.
//let listener = NSXPCListener.service()
//listener.delegate = delegate
//
//// Resuming the serviceListener starts this service. This method does not return.
//listener.resume()

View File

@ -31,49 +31,27 @@ public final class Agent: Sendable {
extension Agent { extension Agent {
/// Handles an incoming request. public func handle(request: SSHAgent.Request, provenance: SigningRequestProvenance) async -> Data {
/// - 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 {
// 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
await reloadSecretsIfNeccessary() await reloadSecretsIfNeccessary()
var response = Data() var response = Data()
do { do {
switch requestType { switch request {
case .requestIdentities: case .requestIdentities:
response.append(SSHAgent.ResponseType.agentIdentitiesAnswer.data) response.append(SSHAgent.Response.agentIdentitiesAnswer.data)
response.append(await identities()) response.append(await identities())
logger.debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)") logger.debug("Agent returned \(SSHAgent.Response.agentIdentitiesAnswer.debugDescription)")
case .signRequest: case .signRequest(let context):
response.append(SSHAgent.ResponseType.agentSignResponse.data) response.append(SSHAgent.Response.agentSignResponse.data)
response.append(try await sign(data: data, provenance: provenance)) response.append(try await sign(data: context.dataToSign, keyBlob: context.keyBlob, provenance: provenance))
logger.debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)") logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
default: default:
logger.debug("Agent received valid request of type \(requestType.debugDescription), but not currently supported.") logger.debug("Agent received valid request of type \(request.debugDescription), but not currently supported.")
response.append(SSHAgent.ResponseType.agentFailure.data) throw UnhandledRequestError()
} }
} catch { } catch {
response = SSHAgent.ResponseType.agentFailure.data response = SSHAgent.Response.agentFailure.data
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)") logger.debug("Agent returned \(SSHAgent.Response.agentFailure.debugDescription)")
} }
return response.lengthAndData return response.lengthAndData
} }
@ -113,27 +91,23 @@ 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, provenance: SigningRequestProvenance) async throws -> Data { func sign(data: Data, keyBlob: 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 // Check if hash is actually an openssh certificate and reconstruct the public key if it is
if let certificatePublicKey = await certificateHandler.publicKeyHash(from: payloadHash) { let resolvedBlob: Data
hash = certificatePublicKey if let certificatePublicKey = await certificateHandler.publicKeyHash(from: keyBlob) {
resolvedBlob = certificatePublicKey
} else { } else {
hash = payloadHash resolvedBlob = keyBlob
} }
guard let (secret, store) = await secret(matching: hash) else { guard let (secret, store) = await secret(matching: resolvedBlob) else {
logger.debug("Agent did not have a key matching \(hash as NSData)") logger.debug("Agent did not have a key matching \(resolvedBlob as NSData)")
throw NoMatchingKeyError() throw NoMatchingKeyError()
} }
try await witness?.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance) try await witness?.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance)
let dataToSign = try reader.readNextChunk() let rawRepresentation = try await store.sign(data: data, with: secret, for: provenance)
let rawRepresentation = try await store.sign(data: dataToSign, with: secret, for: provenance)
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)
@ -172,16 +146,16 @@ extension Agent {
extension Agent { extension Agent {
struct InvalidDataProvidedError: Error {}
struct NoMatchingKeyError: Error {} struct NoMatchingKeyError: Error {}
struct UnhandledRequestError: Error {}
} }
extension SSHAgent.ResponseType { extension SSHAgent.Response {
var data: Data { var data: Data {
var raw = self.rawValue var raw = self.rawValue
return Data(bytes: &raw, count: UInt8.bitWidth/8) return Data(bytes: &raw, count: MemoryLayout<UInt8>.size)
} }
} }

View File

@ -1,26 +1,6 @@
import Foundation import Foundation
/// Protocol abstraction of the reading aspects of FileHandle. extension FileHandle {
public protocol FileHandleReader: Sendable {
/// Gets data that is available for reading.
var availableData: Data { get }
/// A file descriptor of the handle.
var fileDescriptor: Int32 { get }
/// The process ID of the process coonnected to the other end of the FileHandle.
var pidOfConnectedProcess: Int32 { get }
}
/// Protocol abstraction of the writing aspects of FileHandle.
public protocol FileHandleWriter: Sendable {
/// Writes data to the handle.
func write(_ data: Data)
}
extension FileHandle: FileHandleReader, FileHandleWriter {
public var pidOfConnectedProcess: Int32 { public var pidOfConnectedProcess: Int32 {
let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<Int32>.size, alignment: 1) let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<Int32>.size, alignment: 1)

View File

@ -0,0 +1,76 @@
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) async throws -> SSHAgent.Request {
logger.debug("Parsing new data")
guard data.count > 4 else {
throw InvalidDataProvidedError()
}
let specifiedLength = (data[0..<4].bytes.unsafeLoad(as: UInt32.self).bigEndian) + 4
let rawRequestInt = data[4]
let remainingDataRange = 4..<min(Int(specifiedLength), data.count)
lazy var body: Data = { Data(data[remainingDataRange]) }()
switch rawRequestInt {
case SSHAgent.Request.requestIdentities.protocolID:
return .requestIdentities
case SSHAgent.Request.signRequest(.empty).protocolID:
return .signRequest(try signatureRequestContext(from: body))
case SSHAgent.Request.addIdentity.protocolID:
return .addIdentity
case SSHAgent.Request.removeIdentity.protocolID:
return .removeIdentity
case SSHAgent.Request.removeAllIdentities.protocolID:
return .removeAllIdentities
case SSHAgent.Request.addIDConstrained.protocolID:
return .addIDConstrained
case SSHAgent.Request.addSmartcardKey.protocolID:
return .addSmartcardKey
case SSHAgent.Request.removeSmartcardKey.protocolID:
return .removeSmartcardKey
case SSHAgent.Request.lock.protocolID:
return .lock
case SSHAgent.Request.unlock.protocolID:
return .unlock
case SSHAgent.Request.addSmartcardKeyConstrained.protocolID:
return .addSmartcardKeyConstrained
case SSHAgent.Request.protocolExtension.protocolID:
return .protocolExtension
default:
return .unknown(rawRequestInt)
}
}
}
extension SSHAgentInputParser {
func signatureRequestContext(from data: Data) throws -> SSHAgent.Request.SignatureRequestContext {
let reader = OpenSSHReader(data: data)
let keyBlob = try reader.readNextChunk()
let dataToSign = try reader.readNextChunk()
return SSHAgent.Request.SignatureRequestContext(keyBlob: keyBlob, dataToSign: dataToSign)
}
}
extension SSHAgentInputParser {
struct AgentUnknownRequestError: Error {}
struct AgentUnhandledRequestError: Error {}
struct InvalidDataProvidedError: Error {}
}

View File

@ -6,21 +6,39 @@ public enum SSHAgent {}
extension 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 /// 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 {
case requestIdentities = 11 case requestIdentities
case signRequest = 13 case signRequest(SignatureRequestContext)
case addIdentity = 17 case addIdentity
case removeIdentity = 18 case removeIdentity
case removeAllIdentities = 19 case removeAllIdentities
case addIDConstrained = 25 case addIDConstrained
case addSmartcardKey = 20 case addSmartcardKey
case removeSmartcardKey = 21 case removeSmartcardKey
case lock = 22 case lock
case unlock = 23 case unlock
case addSmartcardKeyConstrained = 26 case addSmartcardKeyConstrained
case protocolExtension = 27 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 { public var debugDescription: String {
switch self { switch self {
@ -36,12 +54,28 @@ extension SSHAgent {
case .unlock: "SSH_AGENTC_UNLOCK" case .unlock: "SSH_AGENTC_UNLOCK"
case .addSmartcardKeyConstrained: "SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED" case .addSmartcardKeyConstrained: "SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED"
case .protocolExtension: "SSH_AGENTC_EXTENSION" 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 /// 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 agentFailure = 5
case agentSuccess = 6 case agentSuccess = 6

View File

@ -13,9 +13,8 @@ extension SigningRequestTracer {
/// Generates a ``SecretKit.SigningRequestProvenance`` from a ``FileHandleReader``. /// Generates a ``SecretKit.SigningRequestProvenance`` from a ``FileHandleReader``.
/// - Parameter fileHandleReader: The reader involved in processing the request. /// - Parameter fileHandleReader: The reader involved in processing the request.
/// - Returns: A ``SecretKit.SigningRequestProvenance`` describing the origin of the request. /// - Returns: A ``SecretKit.SigningRequestProvenance`` describing the origin of the request.
func provenance(from fileHandleReader: FileHandleReader) -> SigningRequestProvenance { func provenance(from fileHandle: FileHandle) -> SigningRequestProvenance {
let firstInfo = process(from: fileHandleReader.pidOfConnectedProcess) let firstInfo = process(from: fileHandle.pidOfConnectedProcess)
var provenance = SigningRequestProvenance(root: firstInfo) var provenance = SigningRequestProvenance(root: firstInfo)
while NSRunningApplication(processIdentifier: provenance.origin.pid) == nil && provenance.origin.parentPID != nil { while NSRunningApplication(processIdentifier: provenance.origin.pid) == nil && provenance.origin.parentPID != nil {
provenance.chain.append(process(from: provenance.origin.parentPID!)) provenance.chain.append(process(from: provenance.origin.parentPID!))

View File

@ -36,9 +36,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
Task { Task {
for await session in socketController.sessions { for await session in socketController.sessions {
Task { Task {
let inputParser = SSHAgentInputParser()
do { do {
for await message in session.messages { 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) try await session.write(agentResponse)
} }
} catch { } catch {

View File

@ -0,0 +1,10 @@
import Foundation
import SecretAgentKit
struct XPCAgentInputParser: SSHAgentInputParserProtocol {
func parse(data: Data) async throws -> SSHAgent.Request {
fatalError()
}
}

View File

@ -30,6 +30,11 @@
501577DA2E6BC5F3004A37D0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501577D62E6BC5F3004A37D0 /* main.swift */; }; 501577DA2E6BC5F3004A37D0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501577D62E6BC5F3004A37D0 /* main.swift */; };
501577DB2E6BC5F3004A37D0 /* ReleasesDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501577D72E6BC5F3004A37D0 /* ReleasesDownloader.swift */; }; 501577DB2E6BC5F3004A37D0 /* ReleasesDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501577D72E6BC5F3004A37D0 /* ReleasesDownloader.swift */; };
501577DF2E6BC647004A37D0 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501577DE2E6BC647004A37D0 /* Brief */; }; 501577DF2E6BC647004A37D0 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501577DE2E6BC647004A37D0 /* Brief */; };
501578032E6C019F004A37D0 /* AgentRequestParser.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 501577F82E6C019F004A37D0 /* AgentRequestParser.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
5015780F2E6C01F1004A37D0 /* AgentRequestParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501578092E6C01F1004A37D0 /* AgentRequestParser.swift */; };
501578112E6C01F1004A37D0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5015780C2E6C01F1004A37D0 /* main.swift */; };
501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501578122E6C0479004A37D0 /* XPCInputParser.swift */; };
501578152E6C0558004A37D0 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 501578142E6C0558004A37D0 /* SecretAgentKit */; };
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
504788EC2E680DC800B4556F /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788EB2E680DC400B4556F /* URLs.swift */; }; 504788EC2E680DC800B4556F /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788EB2E680DC400B4556F /* URLs.swift */; };
504788F22E681F3A00B4556F /* Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F12E681F3A00B4556F /* Instructions.swift */; }; 504788F22E681F3A00B4556F /* Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F12E681F3A00B4556F /* Instructions.swift */; };
@ -97,6 +102,13 @@
remoteGlobalIDString = 501577BC2E6BC5B4004A37D0; remoteGlobalIDString = 501577BC2E6BC5B4004A37D0;
remoteInfo = ReleasesDownloader; remoteInfo = ReleasesDownloader;
}; };
501578012E6C019F004A37D0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 501577F72E6C019F004A37D0;
remoteInfo = AgentRequestParser;
};
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@ -106,6 +118,7 @@
dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
dstSubfolderSpec = 16; dstSubfolderSpec = 16;
files = ( files = (
501578032E6C019F004A37D0 /* AgentRequestParser.xpc in Embed XPC Services */,
501577C82E6BC5B4004A37D0 /* ReleasesDownloader.xpc in Embed XPC Services */, 501577C82E6BC5B4004A37D0 /* ReleasesDownloader.xpc in Embed XPC Services */,
); );
name = "Embed XPC Services"; name = "Embed XPC Services";
@ -166,6 +179,11 @@
501577D52E6BC5F3004A37D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 501577D52E6BC5F3004A37D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
501577D62E6BC5F3004A37D0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; }; 501577D62E6BC5F3004A37D0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
501577D72E6BC5F3004A37D0 /* ReleasesDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleasesDownloader.swift; sourceTree = "<group>"; }; 501577D72E6BC5F3004A37D0 /* ReleasesDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleasesDownloader.swift; sourceTree = "<group>"; };
501577F82E6C019F004A37D0 /* AgentRequestParser.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = AgentRequestParser.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
501578092E6C01F1004A37D0 /* AgentRequestParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentRequestParser.swift; sourceTree = "<group>"; };
5015780B2E6C01F1004A37D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5015780C2E6C01F1004A37D0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
501578122E6C0479004A37D0 /* XPCInputParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCInputParser.swift; sourceTree = "<group>"; };
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
504788EB2E680DC400B4556F /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; }; 504788EB2E680DC400B4556F /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; };
504788F12E681F3A00B4556F /* Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instructions.swift; sourceTree = "<group>"; }; 504788F12E681F3A00B4556F /* Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instructions.swift; sourceTree = "<group>"; };
@ -221,6 +239,14 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
501577F52E6C019F004A37D0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
501578152E6C0558004A37D0 /* SecretAgentKit in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50617D7C23FCE48D0099B055 /* Frameworks */ = { 50617D7C23FCE48D0099B055 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -265,6 +291,16 @@
path = ReleasesDownloader; path = ReleasesDownloader;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5015780D2E6C01F1004A37D0 /* AgentRequestParser */ = {
isa = PBXGroup;
children = (
501578092E6C01F1004A37D0 /* AgentRequestParser.swift */,
5015780B2E6C01F1004A37D0 /* Info.plist */,
5015780C2E6C01F1004A37D0 /* main.swift */,
);
path = AgentRequestParser;
sourceTree = "<group>";
};
504788ED2E681EB200B4556F /* Styles */ = { 504788ED2E681EB200B4556F /* Styles */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -321,6 +357,7 @@
5003EF39278005C800DF2006 /* Packages */, 5003EF39278005C800DF2006 /* Packages */,
50617D8123FCE48E0099B055 /* Secretive */, 50617D8123FCE48E0099B055 /* Secretive */,
50A3B78B24026B7500D209EA /* SecretAgent */, 50A3B78B24026B7500D209EA /* SecretAgent */,
5015780D2E6C01F1004A37D0 /* AgentRequestParser */,
501577D92E6BC5F3004A37D0 /* ReleasesDownloader */, 501577D92E6BC5F3004A37D0 /* ReleasesDownloader */,
508A58AF241E144C0069DC07 /* Config */, 508A58AF241E144C0069DC07 /* Config */,
50617D8023FCE48E0099B055 /* Products */, 50617D8023FCE48E0099B055 /* Products */,
@ -334,6 +371,7 @@
50617D7F23FCE48E0099B055 /* Secretive.app */, 50617D7F23FCE48E0099B055 /* Secretive.app */,
50A3B78A24026B7500D209EA /* SecretAgent.app */, 50A3B78A24026B7500D209EA /* SecretAgent.app */,
501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */, 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */,
501577F82E6C019F004A37D0 /* AgentRequestParser.xpc */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -411,6 +449,7 @@
children = ( children = (
50020BAF24064869003D4025 /* AppDelegate.swift */, 50020BAF24064869003D4025 /* AppDelegate.swift */,
5018F54E24064786002EB505 /* Notifier.swift */, 5018F54E24064786002EB505 /* Notifier.swift */,
501578122E6C0479004A37D0 /* XPCInputParser.swift */,
50A3B79524026B7600D209EA /* Main.storyboard */, 50A3B79524026B7600D209EA /* Main.storyboard */,
50A3B79824026B7600D209EA /* Info.plist */, 50A3B79824026B7600D209EA /* Info.plist */,
508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */, 508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */,
@ -451,6 +490,26 @@
productReference = 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */; productReference = 501577BD2E6BC5B4004A37D0 /* ReleasesDownloader.xpc */;
productType = "com.apple.product-type.xpc-service"; productType = "com.apple.product-type.xpc-service";
}; };
501577F72E6C019F004A37D0 /* AgentRequestParser */ = {
isa = PBXNativeTarget;
buildConfigurationList = 501578052E6C019F004A37D0 /* Build configuration list for PBXNativeTarget "AgentRequestParser" */;
buildPhases = (
501577F42E6C019F004A37D0 /* Sources */,
501577F52E6C019F004A37D0 /* Frameworks */,
501577F62E6C019F004A37D0 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = AgentRequestParser;
packageProductDependencies = (
501578142E6C0558004A37D0 /* SecretAgentKit */,
);
productName = AgentRequestParser;
productReference = 501577F82E6C019F004A37D0 /* AgentRequestParser.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" */;
@ -467,6 +526,7 @@
dependencies = ( dependencies = (
50142167278126B500BBAA70 /* PBXTargetDependency */, 50142167278126B500BBAA70 /* PBXTargetDependency */,
501577C72E6BC5B4004A37D0 /* PBXTargetDependency */, 501577C72E6BC5B4004A37D0 /* PBXTargetDependency */,
501578022E6C019F004A37D0 /* PBXTargetDependency */,
); );
name = Secretive; name = Secretive;
packageProductDependencies = ( packageProductDependencies = (
@ -521,6 +581,9 @@
501577BC2E6BC5B4004A37D0 = { 501577BC2E6BC5B4004A37D0 = {
CreatedOnToolsVersion = 26.0; CreatedOnToolsVersion = 26.0;
}; };
501577F72E6C019F004A37D0 = {
CreatedOnToolsVersion = 26.0;
};
50617D7E23FCE48D0099B055 = { 50617D7E23FCE48D0099B055 = {
CreatedOnToolsVersion = 11.3; CreatedOnToolsVersion = 11.3;
}; };
@ -554,6 +617,7 @@
50617D7E23FCE48D0099B055 /* Secretive */, 50617D7E23FCE48D0099B055 /* Secretive */,
50A3B78924026B7500D209EA /* SecretAgent */, 50A3B78924026B7500D209EA /* SecretAgent */,
501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */, 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */,
501577F72E6C019F004A37D0 /* AgentRequestParser */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@ -566,6 +630,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
501577F62E6C019F004A37D0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
50617D7D23FCE48D0099B055 /* Resources */ = { 50617D7D23FCE48D0099B055 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -602,6 +673,15 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
501577F42E6C019F004A37D0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5015780F2E6C01F1004A37D0 /* AgentRequestParser.swift in Sources */,
501578112E6C01F1004A37D0 /* main.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50617D7B23FCE48D0099B055 /* Sources */ = { 50617D7B23FCE48D0099B055 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -647,6 +727,7 @@
files = ( files = (
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 */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -673,6 +754,11 @@
target = 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */; target = 501577BC2E6BC5B4004A37D0 /* ReleasesDownloader */;
targetProxy = 501577D32E6BC5DD004A37D0 /* PBXContainerItemProxy */; targetProxy = 501577D32E6BC5DD004A37D0 /* PBXContainerItemProxy */;
}; };
501578022E6C019F004A37D0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 501577F72E6C019F004A37D0 /* AgentRequestParser */;
targetProxy = 501578012E6C019F004A37D0 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
@ -714,7 +800,7 @@
INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader; INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved."; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 26.0; MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -756,7 +842,7 @@
INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader; INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved."; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 26.0; MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -798,7 +884,7 @@
INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader; INFOPLIST_KEY_CFBundleDisplayName = ReleasesDownloader;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved."; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 26.0; MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.ReleasesDownloader;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -812,6 +898,98 @@
}; };
name = Release; name = Release;
}; };
501578062E6C019F004A37D0 /* 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;
};
501578072E6C019F004A37D0 /* 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;
};
501578082E6C019F004A37D0 /* 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;
};
50617D9B23FCE48E0099B055 /* Debug */ = { 50617D9B23FCE48E0099B055 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */; baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
@ -1262,6 +1440,16 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
501578052E6C019F004A37D0 /* Build configuration list for PBXNativeTarget "AgentRequestParser" */ = {
isa = XCConfigurationList;
buildConfigurations = (
501578062E6C019F004A37D0 /* Debug */,
501578072E6C019F004A37D0 /* Test */,
501578082E6C019F004A37D0 /* 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 = (
@ -1335,6 +1523,10 @@
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = Brief; productName = Brief;
}; };
501578142E6C0558004A37D0 /* SecretAgentKit */ = {
isa = XCSwiftPackageProductDependency;
productName = SecretAgentKit;
};
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */
}; };
rootObject = 50617D7723FCE48D0099B055 /* Project object */; rootObject = 50617D7723FCE48D0099B055 /* Project object */;