Files
probo/cmd/probo-agent/installer/macos/enroll-ui/HelperTool/Helper.swift
Ludovic Vielle 85864a580c Install macOS helper from PKG for XPC enroll
Browser enrollment used osascript on every elevate. Ship a signed
privileged helper installed at PKG time so probo:// can enroll over
XPC with no second admin prompt. Add make install/uninstall/clean for
local PKG test loops, and show alerts only on failure.

Mirror the Go lint path for the macOS SPM package: Make
targets, root configs, and a Linux CI job. Keep checks
syntax-only so they do not need a macOS SDK. Format the
existing sources so the new gates start clean.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-07-24 15:08:37 +02:00

104 lines
3.4 KiB
Swift

import Foundation
import ProboAgentShared
import os
final class Helper: NSObject, ProboAgentHelperProtocol, NSXPCListenerDelegate {
private static let log = Logger(
subsystem: "com.probo.agent.helper",
category: "Helper"
)
func getVersion(withReply reply: @escaping (String) -> Void) {
reply(ProboAgentHelperConstants.helperVersion)
}
func ping(withReply reply: @escaping (Bool) -> Void) {
Self.log.info("ping")
reply(true)
}
func install(
serverURL: String,
enrollmentToken: String,
configDir: String,
withReply reply: @escaping (Int32, String?) -> Void
) {
let trimmedServer = serverURL.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedToken = enrollmentToken.trimmingCharacters(in: .whitespacesAndNewlines)
let dir = configDir.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmedServer.isEmpty, !trimmedToken.isEmpty else {
reply(1, "server URL and enrollment token are required")
return
}
// Pass the token via env rather than argv so it does not show up in
// process listings (ps / Activity Monitor). Install already accepts
// PROBO_ENROLLMENT_TOKEN when --enrollment-token is omitted.
var args = [
"install",
"--server", trimmedServer,
]
if !dir.isEmpty {
args.append(contentsOf: ["--dir", dir])
}
var environment = ProcessInfo.processInfo.environment
environment["PROBO_ENROLLMENT_TOKEN"] = trimmedToken
let result = runAgent(args: args, environment: environment)
reply(result.exitCode, result.output)
}
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection)
-> Bool
{
guard ClientAuth.accepts(connection: connection) else {
Self.log.error(
"refused XPC connection pid=\(connection.processIdentifier, privacy: .public)")
return false
}
Self.log.info(
"accepted XPC connection pid=\(connection.processIdentifier, privacy: .public)")
connection.exportedInterface = NSXPCInterface(with: ProboAgentHelperProtocol.self)
connection.exportedObject = self
connection.resume()
return true
}
private struct CommandResult {
let exitCode: Int32
let output: String?
}
private func runAgent(args: [String], environment: [String: String]? = nil) -> CommandResult {
let process = Process()
process.executableURL = URL(fileURLWithPath: ProboAgentHelperConstants.agentExecutablePath)
process.arguments = args
if let environment {
process.environment = environment
}
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
do {
try process.run()
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
process.waitUntilExit()
let text = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
return CommandResult(
exitCode: process.terminationStatus,
output: text?.isEmpty == false ? text : nil
)
} catch {
return CommandResult(exitCode: 1, output: error.localizedDescription)
}
}
}