Compare commits

..

13 Commits

Author SHA1 Message Date
Max Goedjen
93b51f9e9b Dump 2022-02-16 22:02:57 -08:00
Max Goedjen
ba1c4c1563 XPC to agent working 2022-02-11 23:31:03 -08:00
Max Goedjen
b41c036ea0 Not working because xpc service 2022-02-11 22:52:45 -08:00
Max Goedjen
adabe801d3 Update FAQ.md (#344) 2022-02-07 01:01:09 +00:00
Max Goedjen
19f9494492 Remove 24h unlock duration (#342) 2022-01-31 08:09:57 +00:00
Max Goedjen
c50d2feaf9 Add nightly build (#341) 2022-01-31 07:58:23 +00:00
David Gunzinger
03d3cc9177 wait for new packets on the agent socket after the handler is invoked… (#267)
* wait for new packets on the agent socket after the handler is invoked. this fixes https://github.com/maxgoedjen/secretive/issues/244

* handle closing of the socked

* fix compile

Co-authored-by: David Gunzinger <david.gunzinger@smoca.ch>
2022-01-31 07:53:02 +00:00
Max Goedjen
141cc03b60 Move presentation of setup view off of toolbar item, since it's not a popover anymore (#340) 2022-01-31 07:44:09 +00:00
Andrew Glaze
07559bd7ef Add setup instructions for GitKraken (#339) 2022-01-27 03:19:15 +00:00
Max Goedjen
cb206a18c2 Switch (#334) 2022-01-18 21:24:57 +00:00
Anthony Kosednar
6cb3ff80d9 Corrected FAQ link to Updater.swift source code (#326) 2022-01-18 21:20:03 +00:00
Max Goedjen
05c5aca9b6 Project public key files for use in configs (#264) 2022-01-02 23:25:40 -08:00
Max Goedjen
5894bbca00 Unbreak (#318) 2022-01-03 07:13:02 +00:00
46 changed files with 1471 additions and 275 deletions

49
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Nightly
on:
schedule:
- cron: "0 8 * * *"
jobs:
build:
runs-on: macos-11.0
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Setup Signing
env:
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh
- name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
- name: Update Build Number
env:
RUN_ID: ${{ github.run_id }}
run: |
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0/g" Sources/Config/Config.xcconfig
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
- name: Build
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
- name: Create ZIPs
run: |
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
- name: Notarize
env:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
- name: Document SHAs
run: |
shasum -a 512 Secretive.zip
shasum -a 512 Archive.zip
- name: Upload App to Artifacts
uses: actions/upload-artifact@v1
with:
name: Secretive.zip
path: Secretive.zip

View File

@@ -51,6 +51,30 @@ Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
Log out and log in again before launching Cyberduck. Log out and log in again before launching Cyberduck.
## GitKraken
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
```
<?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>Label</key>
<string>link-ssh-auth-sock</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>/bin/ln -sf $HOME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh $SSH_AUTH_SOCK</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
```
Log out and log in again before launching Gitkraken. Then enable "Use local SSH agent in GitKraken Preferences (Located under Preferences -> SSH)
# The app I use isn't listed here! # The app I use isn't listed here!

6
FAQ.md
View File

@@ -38,7 +38,11 @@ Awesome! Just bear in mind that because an app only has access to the keychain i
### What's this network request to GitHub? ### What's this network request to GitHub?
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Brief/Updater.swift). Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Sources/Packages/Sources/Brief/Updater.swift).
### How do I uninstall Secretive?
Drag Secretive.app to the trash and remove `~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent`. `SecretAgent` may continue running until you quit it or reboot.
### I have a security issue ### I have a security issue

View File

@@ -24,9 +24,6 @@ let package = Package(
.library( .library(
name: "SecretAgentKitHeaders", name: "SecretAgentKitHeaders",
targets: ["SecretAgentKitHeaders"]), targets: ["SecretAgentKitHeaders"]),
.library(
name: "SecretAgentKitProtocol",
targets: ["SecretAgentKitProtocol"]),
.library( .library(
name: "Brief", name: "Brief",
targets: ["Brief"]), targets: ["Brief"]),
@@ -35,7 +32,8 @@ let package = Package(
], ],
targets: [ targets: [
.target( .target(
name: "SecretKit" name: "SecretKit",
dependencies: []
), ),
.testTarget( .testTarget(
name: "SecretKitTests", name: "SecretKitTests",
@@ -51,20 +49,18 @@ let package = Package(
), ),
.target( .target(
name: "SecretAgentKit", name: "SecretAgentKit",
dependencies: ["SecretKit", "SecretAgentKitHeaders", "SecretAgentKitProtocol"] dependencies: ["SecretKit", "SecretAgentKitHeaders"]
), ),
.systemLibrary( .systemLibrary(
name: "SecretAgentKitHeaders" name: "SecretAgentKitHeaders"
), ),
.target(
name: "SecretAgentKitProtocol"
),
.testTarget( .testTarget(
name: "SecretAgentKitTests", name: "SecretAgentKitTests",
dependencies: ["SecretAgentKit"]) dependencies: ["SecretAgentKit"])
, ,
.target( .target(
name: "Brief" name: "Brief",
dependencies: []
), ),
.testTarget( .testTarget(
name: "BriefTests", name: "BriefTests",

View File

@@ -11,5 +11,5 @@ Brief is a collection of protocols and concrete implmentation describing updates
### Updater ### Updater
- ``UpdaterProtocol`` - ``UpdateCheckerProtocol``
- ``Updater`` - ``UpdateChecker``

View File

@@ -1,8 +1,8 @@
import Foundation import Foundation
import Combine import Combine
/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version. /// A concrete implementation of ``UpdateCheckerProtocol`` which considers the current release and OS version.
public class Updater: ObservableObject, UpdaterProtocol { public class UpdateChecker: ObservableObject, UpdateCheckerProtocol {
@Published public var update: Release? @Published public var update: Release?
public let testBuild: Bool public let testBuild: Bool
@@ -53,7 +53,7 @@ public class Updater: ObservableObject, UpdaterProtocol {
} }
extension Updater { extension UpdateChecker {
/// Evaluates the available downloadable releases, and selects the newest non-prerelease release that the user is able to run. /// Evaluates the available downloadable releases, and selects the newest non-prerelease release that the user is able to run.
/// - Parameter releases: An array of ``Release`` objects. /// - Parameter releases: An array of ``Release`` objects.
@@ -88,7 +88,7 @@ extension Updater {
} }
extension Updater { extension UpdateChecker {
enum Constants { enum Constants {
static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")! static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")!

View File

@@ -1,7 +1,7 @@
import Foundation import Foundation
/// A protocol for retreiving the latest available version of an app. /// A protocol for retreiving the latest available version of an app.
public protocol UpdaterProtocol: ObservableObject { public protocol UpdateCheckerProtocol: ObservableObject {
/// The latest update /// The latest update
var update: Release? { get } var update: Release? { get }

View File

@@ -30,20 +30,23 @@ extension Agent {
/// - Parameters: /// - Parameters:
/// - reader: A ``FileHandleReader`` to read the content of the request. /// - reader: A ``FileHandleReader`` to read the content of the request.
/// - writer: A ``FileHandleWriter`` to write the response to. /// - writer: A ``FileHandleWriter`` to write the response to.
public func handle(reader: FileHandleReader, writer: FileHandleWriter) { /// - Return value:
/// - Boolean if data could be read
public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool {
Logger().debug("Agent handling new data") Logger().debug("Agent handling new data")
let data = Data(reader.availableData) let data = Data(reader.availableData)
guard data.count > 4 else { return } guard data.count > 4 else { return false}
let requestTypeInt = data[4] let requestTypeInt = data[4]
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else { guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data)) writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)") Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
return return true
} }
Logger().debug("Agent handling request of type \(requestType.debugDescription)") Logger().debug("Agent handling request of type \(requestType.debugDescription)")
let subData = Data(data[5...]) let subData = Data(data[5...])
let response = handle(requestType: requestType, data: subData, reader: reader) let response = handle(requestType: requestType, data: subData, reader: reader)
writer.write(response) writer.write(response)
return true
} }
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data { func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {

View File

@@ -6,8 +6,12 @@ public class SocketController {
/// The active FileHandle. /// The active FileHandle.
private var fileHandle: FileHandle? private var fileHandle: FileHandle?
/// The active SocketPort.
private var port: SocketPort?
/// A handler that will be notified when a new read/write handle is available. /// A handler that will be notified when a new read/write handle is available.
public var handler: ((FileHandleReader, FileHandleWriter) -> Void)? /// False if no data could be read
public var handler: ((FileHandleReader, FileHandleWriter) -> Bool)?
/// Initializes a socket controller with a specified path. /// Initializes a socket controller with a specified path.
/// - Parameter path: The path to use as a socket. /// - Parameter path: The path to use as a socket.
@@ -19,6 +23,7 @@ public class SocketController {
let exists = FileManager.default.fileExists(atPath: path) let exists = FileManager.default.fileExists(atPath: path)
assert(!exists) assert(!exists)
Logger().debug("Socket controller path is clear") Logger().debug("Socket controller path is clear")
port = socketPort(at: path)
configureSocket(at: path) configureSocket(at: path)
Logger().debug("Socket listening at \(path)") Logger().debug("Socket listening at \(path)")
} }
@@ -26,7 +31,7 @@ public class SocketController {
/// Configures the socket and a corresponding FileHandle. /// Configures the socket and a corresponding FileHandle.
/// - Parameter path: The path to use as a socket. /// - Parameter path: The path to use as a socket.
func configureSocket(at path: String) { func configureSocket(at path: String) {
let port = socketPort(at: path) guard let port = port else { return }
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true) fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
@@ -62,7 +67,7 @@ public class SocketController {
@objc func handleConnectionAccept(notification: Notification) { @objc func handleConnectionAccept(notification: Notification) {
Logger().debug("Socket controller accepted connection") Logger().debug("Socket controller accepted connection")
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return } guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
handler?(new, new) _ = handler?(new, new)
new.waitForDataInBackgroundAndNotify() new.waitForDataInBackgroundAndNotify()
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!]) fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!])
} }
@@ -73,7 +78,12 @@ public class SocketController {
Logger().debug("Socket controller has new data available") Logger().debug("Socket controller has new data available")
guard let new = notification.object as? FileHandle else { return } guard let new = notification.object as? FileHandle else { return }
Logger().debug("Socket controller received new file handle") Logger().debug("Socket controller received new file handle")
handler?(new, new) if((handler?(new, new)) == true) {
Logger().debug("Socket controller handled data, wait for more data")
new.waitForDataInBackgroundAndNotify()
} else {
Logger().debug("Socket controller called with empty data, socked closed")
}
} }
} }

View File

@@ -1,12 +0,0 @@
import Foundation
@objc public protocol AgentProtocol {
func updatedStore(withID: UUID) async throws
}
public struct AgentProtocolStoreNotFoundError: Error {
public init() {
}
}

View File

@@ -58,13 +58,11 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
private let _create: (String, Bool) throws -> Void private let _create: (String, Bool) throws -> Void
private let _delete: (AnySecret) throws -> Void private let _delete: (AnySecret) throws -> Void
private let _update: (AnySecret, String) throws -> Void private let _update: (AnySecret, String) throws -> Void
private let _reload: () throws -> Void
public init<SecretStoreType>(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable { public init<SecretStoreType>(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable {
_create = { try secretStore.create(name: $0, requiresAuthentication: $1) } _create = { try secretStore.create(name: $0, requiresAuthentication: $1) }
_delete = { try secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) } _delete = { try secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) }
_update = { try secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1) } _update = { try secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1) }
_reload = { try secretStore.reload() }
super.init(secretStore) super.init(secretStore)
} }
@@ -80,8 +78,4 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
try _update(secret, name) try _update(secret, name)
} }
public func reload() throws {
try _reload()
}
} }

View File

@@ -5,10 +5,11 @@ import OSLog
public class PublicKeyFileStoreController { public class PublicKeyFileStoreController {
private let logger = Logger() private let logger = Logger()
private let directory = NSHomeDirectory().appending("/PublicKeys") private let directory: String
/// Initializes a PublicKeyFileStoreController. /// Initializes a PublicKeyFileStoreController.
public init() { public init(homeDirectory: String) {
directory = homeDirectory.appending("/PublicKeys")
} }
/// Writes out the keys specified to disk. /// Writes out the keys specified to disk.

View File

@@ -52,9 +52,6 @@ public protocol SecretStoreModifiable: SecretStore {
/// - name: The new name for the Secret. /// - name: The new name for the Secret.
func update(secret: SecretType, name: String) throws func update(secret: SecretType, name: String) throws
/// Reloads the secrets from the backing store.
func reload() throws
} }
extension NSNotification.Name { extension NSNotification.Name {

View File

@@ -23,6 +23,9 @@ extension SecureEnclave {
/// Initializes a Store. /// Initializes a Store.
public init() { public init() {
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
self.reloadSecrets(notify: false)
}
loadSecrets() loadSecrets()
} }
@@ -65,7 +68,7 @@ extension SecureEnclave {
throw KeychainError(statusCode: nil) throw KeychainError(statusCode: nil)
} }
try savePublicKey(publicKey, name: name) try savePublicKey(publicKey, name: name)
reload() reloadSecrets()
} }
public func delete(secret: Secret) throws { public func delete(secret: Secret) throws {
@@ -77,7 +80,7 @@ extension SecureEnclave {
if status != errSecSuccess { if status != errSecSuccess {
throw KeychainError(statusCode: status) throw KeychainError(statusCode: status)
} }
reload() reloadSecrets()
} }
public func update(secret: Secret, name: String) throws { public func update(secret: Secret, name: String) throws {
@@ -94,14 +97,9 @@ extension SecureEnclave {
if status != errSecSuccess { if status != errSecSuccess {
throw KeychainError(statusCode: status) throw KeychainError(statusCode: status)
} }
reload() reloadSecrets()
} }
public func reload() {
secrets.removeAll()
loadSecrets()
}
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData { public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData {
let context: LAContext let context: LAContext
if let existing = persistedAuthenticationContexts[secret], existing.valid { if let existing = persistedAuthenticationContexts[secret], existing.valid {
@@ -172,6 +170,16 @@ extension SecureEnclave {
extension SecureEnclave.Store { extension SecureEnclave.Store {
/// Reloads all secrets from the store.
/// - Parameter notify: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well.
private func reloadSecrets(notify: Bool = true) {
secrets.removeAll()
loadSecrets()
if notify {
DistributedNotificationCenter.default().post(name: .secretStoreUpdated, object: nil)
}
}
/// Loads all secrets from the store. /// Loads all secrets from the store.
private func loadSecrets() { private func loadSecrets() {
let attributes = [ let attributes = [

View File

@@ -51,7 +51,7 @@ class ReleaseParsingTests: XCTestCase {
func testGreatestSelectedIfOldPatchIsPublishedLater() { func testGreatestSelectedIfOldPatchIsPublishedLater() {
// If 2.x.x series has been published, and a patch for 1.x.x is issued // If 2.x.x series has been published, and a patch for 1.x.x is issued
// 2.x.x should still be selected if user can run it. // 2.x.x should still be selected if user can run it.
let updater = Updater(checkOnLaunch: false, osVersion: SemVer("2.2.3"), currentVersion: SemVer("1.0.0")) let updater = UpdateChecker(checkOnLaunch: false, osVersion: SemVer("2.2.3"), currentVersion: SemVer("1.0.0"))
let two = Release(name: "2.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "2.0 available! Minimum macOS Version: 2.2.3") let two = Release(name: "2.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "2.0 available! Minimum macOS Version: 2.2.3")
let releases = [ let releases = [
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"), Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"),
@@ -72,7 +72,7 @@ class ReleaseParsingTests: XCTestCase {
func testLatestVersionIsRunnable() { func testLatestVersionIsRunnable() {
// If the 2.x.x series has been published but the user can't run it // If the 2.x.x series has been published but the user can't run it
// the last version the user can run should be selected. // the last version the user can run should be selected.
let updater = Updater(checkOnLaunch: false, osVersion: SemVer("1.2.3"), currentVersion: SemVer("1.0.0")) let updater = UpdateChecker(checkOnLaunch: false, osVersion: SemVer("1.2.3"), currentVersion: SemVer("1.0.0"))
let oneOhTwo = Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch! Minimum macOS Version: 1.2.3") let oneOhTwo = Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch! Minimum macOS Version: 1.2.3")
let releases = [ let releases = [
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"), Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"),

View File

@@ -7,11 +7,8 @@ import SmartCardSecretKit
import SecretAgentKit import SecretAgentKit
import Brief import Brief
import SecretKit
import SecretAgentKitProtocol
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol { class AppDelegate: NSObject, NSApplicationDelegate {
private let storeList: SecretStoreList = { private let storeList: SecretStoreList = {
let list = SecretStoreList() let list = SecretStoreList()
@@ -19,9 +16,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
list.add(store: SmartCard.Store()) list.add(store: SmartCard.Store())
return list return list
}() }()
private let updater = Updater(checkOnLaunch: false) private let updater = UpdateChecker(checkOnLaunch: false)
private let notifier = Notifier() private let notifier = Notifier()
private let publicKeyFileStoreController = PublicKeyFileStoreController() private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
private lazy var agent: Agent = { private lazy var agent: Agent = {
Agent(storeList: storeList, witness: notifier) Agent(storeList: storeList, witness: notifier)
}() }()
@@ -30,57 +27,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
return SocketController(path: path) return SocketController(path: path)
}() }()
private var updateSink: AnyCancellable? private var updateSink: AnyCancellable?
private let logger = Logger()
var delegate: ServiceDelegate? = nil
let listener = NSXPCListener(machServiceName: Bundle.main.bundleIdentifier!)
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_ aNotification: Notification) {
logger.debug("SecretAgent finished launching") Logger().debug("SecretAgent finished launching")
DispatchQueue.main.async { DispatchQueue.main.async {
self.socketController.handler = self.agent.handle(reader:writer:) self.socketController.handler = self.agent.handle(reader:writer:)
} }
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { [self] _ in
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
}
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true) try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
notifier.prompt() notifier.prompt()
updateSink = updater.$update.sink { update in updateSink = updater.$update.sink { update in
guard let update = update else { return } guard let update = update else { return }
self.notifier.notify(update: update, ignore: self.updater.ignore(release:)) self.notifier.notify(update: update, ignore: self.updater.ignore(release:))
} }
// TODO: REMOVE
notifier.notify(update: Release(name: "Test", prerelease: false, html_url: URL(string: "https://example.com")!, body: ""), ignore: nil)
connect()
}
func connect() {
delegate = ServiceDelegate(exportedObject: self)
listener.delegate = delegate
listener.resume()
}
func updatedStore(withID id: UUID) async throws {
// TODO: REMOVE
notifier.notify(update: Release(name: "UPDATESTORE", prerelease: false, html_url: URL(string: "https://example.com")!, body: ""), ignore: nil)
logger.debug("Reloading keys for store with id: \(id)")
guard let store = storeList.modifiableStore, store.id == id else { throw AgentProtocolStoreNotFoundError() }
try store.reload()
try publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
} }
} }
// TODO: MOVE
class ServiceDelegate: NSObject, NSXPCListenerDelegate {
let exported: AgentProtocol
init(exportedObject: AgentProtocol) {
self.exported = exportedObject
}
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
newConnection.exportedInterface = NSXPCInterface(with: AgentProtocol.self)
newConnection.exportedObject = exported
newConnection.resume()
return true
}
}

View File

@@ -18,8 +18,7 @@ class Notifier {
let rawDurations = [ let rawDurations = [
Measurement(value: 1, unit: UnitDuration.minutes), Measurement(value: 1, unit: UnitDuration.minutes),
Measurement(value: 5, unit: UnitDuration.minutes), Measurement(value: 5, unit: UnitDuration.minutes),
Measurement(value: 1, unit: UnitDuration.hours), Measurement(value: 1, unit: UnitDuration.hours)
Measurement(value: 24, unit: UnitDuration.hours)
] ]
let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: "Do Not Unlock", options: []) let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: "Do Not Unlock", options: [])

View File

@@ -4,10 +4,6 @@
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(TeamIdentifierPrefix)com.maxgoedjen.secretive</string>
</array>
<key>com.apple.security.network.client</key> <key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.smartcard</key> <key>com.apple.security.smartcard</key>

View File

@@ -18,13 +18,15 @@
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF602780081600DF2006 /* SmartCardSecretKit */; }; 5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF602780081600DF2006 /* SmartCardSecretKit */; };
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; }; 5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; };
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; }; 5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; };
500ED3D427B7934A00A6DC28 /* UpdaterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */; };
500ED3DA27B797EE00A6DC28 /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; }; 501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 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 */; }; 50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; }; 50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; }; 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; };
50571E0524393D1500F76F6C /* AgentLaunchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* AgentLaunchController.swift */; }; 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* LaunchAgentController.swift */; };
50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; }; 50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; }; 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; }; 50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; };
@@ -36,9 +38,14 @@
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; }; 5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; };
506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; }; 506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; };
506772C92425BB8500034DED /* NoStoresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506772C82425BB8500034DED /* NoStoresView.swift */; }; 506772C92425BB8500034DED /* NoStoresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506772C82425BB8500034DED /* NoStoresView.swift */; };
50736F652782C05000A723B6 /* AgentCommunicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50736F642782C05000A723B6 /* AgentCommunicationController.swift */; };
50736F672782C31800A723B6 /* SecretAgentKitProtocol in Frameworks */ = {isa = PBXBuildFile; productRef = 50736F662782C31800A723B6 /* SecretAgentKitProtocol */; };
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; }; 5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; };
5081F6D327B790DD0094B82D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5081F6D227B790DD0094B82D /* main.swift */; };
5081F6D727B790DE0094B82D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5081F6D627B790DE0094B82D /* Assets.xcassets */; };
5081F6E027B791110094B82D /* UpdaterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */; };
5081F6E127B791110094B82D /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088065B27B4A6240090BD57 /* Updater.swift */; };
5081F6E327B791620094B82D /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5081F6E227B791620094B82D /* Brief */; };
5081F70027B792150094B82D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5081F6FF27B792150094B82D /* Main.storyboard */; };
5088068F27B4A6FF0090BD57 /* UpdaterCommunicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088068E27B4A6FF0090BD57 /* UpdaterCommunicationController.swift */; };
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; }; 508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; };
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; }; 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; };
508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; }; 508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; };
@@ -55,6 +62,20 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
500ED3D627B796C800A6DC28 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5081F6CF27B790DD0094B82D;
remoteInfo = SecretiveUpdater;
};
500ED3D827B7978700A6DC28 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5081F6CF27B790DD0094B82D;
remoteInfo = SecretiveUpdater;
};
50142166278126B500BBAA70 /* PBXContainerItemProxy */ = { 50142166278126B500BBAA70 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 50617D7723FCE48D0099B055 /* Project object */; containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
@@ -98,6 +119,7 @@
dstPath = Contents/Library/LoginItems; dstPath = Contents/Library/LoginItems;
dstSubfolderSpec = 1; dstSubfolderSpec = 1;
files = ( files = (
500ED3DA27B797EE00A6DC28 /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app in CopyFiles */,
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */, 501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -113,7 +135,7 @@
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; }; 50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.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>"; };
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; }; 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
50571E0424393D1500F76F6C /* AgentLaunchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentLaunchController.swift; sourceTree = "<group>"; }; 50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = "<group>"; };
50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
50617D8223FCE48E0099B055 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; }; 50617D8223FCE48E0099B055 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; 50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -130,8 +152,15 @@
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = "<group>"; }; 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = "<group>"; };
506772C62424784600034DED /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; }; 506772C62424784600034DED /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
506772C82425BB8500034DED /* NoStoresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoStoresView.swift; sourceTree = "<group>"; }; 506772C82425BB8500034DED /* NoStoresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoStoresView.swift; sourceTree = "<group>"; };
50736F642782C05000A723B6 /* AgentCommunicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentCommunicationController.swift; sourceTree = "<group>"; };
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = "<group>"; }; 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = "<group>"; };
5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app; sourceTree = BUILT_PRODUCTS_DIR; };
5081F6D227B790DD0094B82D /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
5081F6D627B790DE0094B82D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5081F6DB27B790DE0094B82D /* SecretiveUpdater.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretiveUpdater.entitlements; sourceTree = "<group>"; };
5081F6FF27B792150094B82D /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdaterProtocol.swift; sourceTree = "<group>"; };
5088065B27B4A6240090BD57 /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = "<group>"; };
5088068E27B4A6FF0090BD57 /* UpdaterCommunicationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 5; lastKnownFileType = sourcecode.swift; path = UpdaterCommunicationController.swift; sourceTree = "<group>"; };
508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = "<group>"; }; 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = "<group>"; };
508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; }; 508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = "<group>"; }; 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = "<group>"; };
@@ -158,7 +187,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */, 5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
50736F672782C31800A723B6 /* SecretAgentKitProtocol in Frameworks */,
501421622781262300BBAA70 /* Brief in Frameworks */, 501421622781262300BBAA70 /* Brief in Frameworks */,
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */, 5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */, 5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */,
@@ -172,6 +200,14 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
5081F6CD27B790DD0094B82D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5081F6E327B791620094B82D /* Brief in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50A3B78724026B7500D209EA /* Frameworks */ = { 50A3B78724026B7500D209EA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -203,6 +239,7 @@
50617D9723FCE48E0099B055 /* SecretiveTests */, 50617D9723FCE48E0099B055 /* SecretiveTests */,
50A3B78B24026B7500D209EA /* SecretAgent */, 50A3B78B24026B7500D209EA /* SecretAgent */,
508A58AF241E144C0069DC07 /* Config */, 508A58AF241E144C0069DC07 /* Config */,
5081F6D127B790DD0094B82D /* SecretiveUpdater */,
50617D8023FCE48E0099B055 /* Products */, 50617D8023FCE48E0099B055 /* Products */,
5099A08B240243730062B6F2 /* Frameworks */, 5099A08B240243730062B6F2 /* Frameworks */,
); );
@@ -214,6 +251,7 @@
50617D7F23FCE48E0099B055 /* Secretive.app */, 50617D7F23FCE48E0099B055 /* Secretive.app */,
50617D9423FCE48E0099B055 /* SecretiveTests.xctest */, 50617D9423FCE48E0099B055 /* SecretiveTests.xctest */,
50A3B78A24026B7500D209EA /* SecretAgent.app */, 50A3B78A24026B7500D209EA /* SecretAgent.app */,
5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -255,6 +293,19 @@
path = SecretiveTests; path = SecretiveTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5081F6D127B790DD0094B82D /* SecretiveUpdater */ = {
isa = PBXGroup;
children = (
5081F6D227B790DD0094B82D /* main.swift */,
5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */,
5088065B27B4A6240090BD57 /* Updater.swift */,
5081F6FF27B792150094B82D /* Main.storyboard */,
5081F6D627B790DE0094B82D /* Assets.xcassets */,
5081F6DB27B790DE0094B82D /* SecretiveUpdater.entitlements */,
);
path = SecretiveUpdater;
sourceTree = "<group>";
};
508A58AF241E144C0069DC07 /* Config */ = { 508A58AF241E144C0069DC07 /* Config */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -286,12 +337,12 @@
508A58B1241ED1EA0069DC07 /* Controllers */ = { 508A58B1241ED1EA0069DC07 /* Controllers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5088068E27B4A6FF0090BD57 /* UpdaterCommunicationController.swift */,
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */, 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
50736F642782C05000A723B6 /* AgentCommunicationController.swift */,
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */, 5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */, 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
50571E0424393D1500F76F6C /* AgentLaunchController.swift */,
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */, 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */,
50571E0424393D1500F76F6C /* LaunchAgentController.swift */,
); );
path = Controllers; path = Controllers;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -343,6 +394,8 @@
); );
dependencies = ( dependencies = (
50142167278126B500BBAA70 /* PBXTargetDependency */, 50142167278126B500BBAA70 /* PBXTargetDependency */,
500ED3D727B796C800A6DC28 /* PBXTargetDependency */,
500ED3D927B7978700A6DC28 /* PBXTargetDependency */,
); );
name = Secretive; name = Secretive;
packageProductDependencies = ( packageProductDependencies = (
@@ -350,7 +403,6 @@
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */, 5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
5003EF602780081600DF2006 /* SmartCardSecretKit */, 5003EF602780081600DF2006 /* SmartCardSecretKit */,
501421612781262300BBAA70 /* Brief */, 501421612781262300BBAA70 /* Brief */,
50736F662782C31800A723B6 /* SecretAgentKitProtocol */,
); );
productName = Secretive; productName = Secretive;
productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */; productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */;
@@ -374,6 +426,26 @@
productReference = 50617D9423FCE48E0099B055 /* SecretiveTests.xctest */; productReference = 50617D9423FCE48E0099B055 /* SecretiveTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test"; productType = "com.apple.product-type.bundle.unit-test";
}; };
5081F6CF27B790DD0094B82D /* SecretiveUpdater */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5081F6DC27B790DE0094B82D /* Build configuration list for PBXNativeTarget "SecretiveUpdater" */;
buildPhases = (
5081F6CC27B790DD0094B82D /* Sources */,
5081F6CD27B790DD0094B82D /* Frameworks */,
5081F6CE27B790DD0094B82D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SecretiveUpdater;
packageProductDependencies = (
5081F6E227B791620094B82D /* Brief */,
);
productName = SecretiveUpdater;
productReference = 5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */;
productType = "com.apple.product-type.application";
};
50A3B78924026B7500D209EA /* SecretAgent */ = { 50A3B78924026B7500D209EA /* SecretAgent */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */; buildConfigurationList = 50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */;
@@ -405,7 +477,7 @@
50617D7723FCE48D0099B055 /* Project object */ = { 50617D7723FCE48D0099B055 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 1220; LastSwiftUpdateCheck = 1330;
LastUpgradeCheck = 1320; LastUpgradeCheck = 1320;
ORGANIZATIONNAME = "Max Goedjen"; ORGANIZATIONNAME = "Max Goedjen";
TargetAttributes = { TargetAttributes = {
@@ -416,6 +488,9 @@
CreatedOnToolsVersion = 11.3; CreatedOnToolsVersion = 11.3;
TestTargetID = 50617D7E23FCE48D0099B055; TestTargetID = 50617D7E23FCE48D0099B055;
}; };
5081F6CF27B790DD0094B82D = {
CreatedOnToolsVersion = 13.3;
};
50A3B78924026B7500D209EA = { 50A3B78924026B7500D209EA = {
CreatedOnToolsVersion = 11.4; CreatedOnToolsVersion = 11.4;
}; };
@@ -437,6 +512,7 @@
50617D7E23FCE48D0099B055 /* Secretive */, 50617D7E23FCE48D0099B055 /* Secretive */,
50617D9323FCE48E0099B055 /* SecretiveTests */, 50617D9323FCE48E0099B055 /* SecretiveTests */,
50A3B78924026B7500D209EA /* SecretAgent */, 50A3B78924026B7500D209EA /* SecretAgent */,
5081F6CF27B790DD0094B82D /* SecretiveUpdater */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -460,6 +536,15 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
5081F6CE27B790DD0094B82D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5081F6D727B790DE0094B82D /* Assets.xcassets in Resources */,
5081F70027B792150094B82D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50A3B78824026B7500D209EA /* Resources */ = { 50A3B78824026B7500D209EA /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -485,15 +570,16 @@
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */, 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */, 5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */, 50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
500ED3D427B7934A00A6DC28 /* UpdaterProtocol.swift in Sources */,
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */, 5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */,
50033AC327813F1700253856 /* BundleIDs.swift in Sources */, 50033AC327813F1700253856 /* BundleIDs.swift in Sources */,
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */, 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */, 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */, 5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
50153E20250AFCB200525160 /* UpdateView.swift in Sources */, 50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
50571E0524393D1500F76F6C /* AgentLaunchController.swift in Sources */, 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */,
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */, 5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
50736F652782C05000A723B6 /* AgentCommunicationController.swift in Sources */, 5088068F27B4A6FF0090BD57 /* UpdaterCommunicationController.swift in Sources */,
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */, 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
50617D8323FCE48E0099B055 /* App.swift in Sources */, 50617D8323FCE48E0099B055 /* App.swift in Sources */,
@@ -512,6 +598,16 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
5081F6CC27B790DD0094B82D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5081F6D327B790DD0094B82D /* main.swift in Sources */,
5081F6E027B791110094B82D /* UpdaterProtocol.swift in Sources */,
5081F6E127B791110094B82D /* Updater.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
50A3B78624026B7500D209EA /* Sources */ = { 50A3B78624026B7500D209EA /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -524,6 +620,16 @@
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
500ED3D727B796C800A6DC28 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5081F6CF27B790DD0094B82D /* SecretiveUpdater */;
targetProxy = 500ED3D627B796C800A6DC28 /* PBXContainerItemProxy */;
};
500ED3D927B7978700A6DC28 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5081F6CF27B790DD0094B82D /* SecretiveUpdater */;
targetProxy = 500ED3D827B7978700A6DC28 /* PBXContainerItemProxy */;
};
50142167278126B500BBAA70 /* PBXTargetDependency */ = { 50142167278126B500BBAA70 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = 50A3B78924026B7500D209EA /* SecretAgent */; target = 50A3B78924026B7500D209EA /* SecretAgent */;
@@ -767,6 +873,96 @@
}; };
name = Release; name = Release;
}; };
5081F6DD27B790DE0094B82D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z72PRUAWF6;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Max Goedjen. All rights reserved.";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
PRODUCT_NAME = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
5081F6DE27B790DE0094B82D /* Test */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z72PRUAWF6;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Max Goedjen. All rights reserved.";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
PRODUCT_NAME = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Test;
};
5081F6DF27B790DE0094B82D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z72PRUAWF6;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Max Goedjen. All rights reserved.";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
PRODUCT_NAME = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
508A5914241EF1A00069DC07 /* Test */ = { 508A5914241EF1A00069DC07 /* Test */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */; baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
@@ -882,7 +1078,6 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
@@ -894,7 +1089,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 1; MARKETING_VERSION = 1;
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
}; };
@@ -917,7 +1112,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 1; MARKETING_VERSION = 1;
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
}; };
@@ -941,7 +1136,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 1; MARKETING_VERSION = 1;
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent"; PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -981,6 +1176,16 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
5081F6DC27B790DE0094B82D /* Build configuration list for PBXNativeTarget "SecretiveUpdater" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5081F6DD27B790DE0094B82D /* Debug */,
5081F6DE27B790DE0094B82D /* Test */,
5081F6DF27B790DE0094B82D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */ = { 50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
@@ -1030,9 +1235,9 @@
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = Brief; productName = Brief;
}; };
50736F662782C31800A723B6 /* SecretAgentKitProtocol */ = { 5081F6E227B791620094B82D /* Brief */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = SecretAgentKitProtocol; productName = Brief;
}; };
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */
}; };

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1330"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5081F6CF27B790DD0094B82D"
BuildableName = "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app"
BlueprintName = "SecretiveUpdater"
ReferencedContainer = "container:Secretive.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5081F6CF27B790DD0094B82D"
BuildableName = "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app"
BlueprintName = "SecretiveUpdater"
ReferencedContainer = "container:Secretive.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5081F6CF27B790DD0094B82D"
BuildableName = "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app"
BlueprintName = "SecretiveUpdater"
ReferencedContainer = "container:Secretive.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -15,9 +15,8 @@ struct Secretive: App {
return list return list
}() }()
private let agentStatusChecker = AgentStatusChecker() private let agentStatusChecker = AgentStatusChecker()
private let agentLaunchController = AgentLaunchController()
private let agentCommunicationController = AgentCommunicationController()
private let justUpdatedChecker = JustUpdatedChecker() private let justUpdatedChecker = JustUpdatedChecker()
private let updaterController = UpdaterCommunicationController()
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false @AppStorage("defaultsHasRunSetup") var hasRunSetup = false
@State private var showingSetup = false @State private var showingSetup = false
@@ -25,12 +24,12 @@ struct Secretive: App {
@SceneBuilder var body: some Scene { @SceneBuilder var body: some Scene {
WindowGroup { WindowGroup {
ContentView<Updater, AgentStatusChecker>(showingCreation: $showingCreation, runningSetup: $showingSetup, hasRunSetup: $hasRunSetup) ContentView<UpdateChecker, AgentStatusChecker>(showingCreation: $showingCreation, runningSetup: $showingSetup, hasRunSetup: $hasRunSetup)
.environmentObject(storeList) .environmentObject(storeList)
.environmentObject(Updater(checkOnLaunch: hasRunSetup)) .environmentObject(UpdateChecker(checkOnLaunch: hasRunSetup))
.environmentObject(agentStatusChecker) .environmentObject(agentStatusChecker)
.environmentObject(agentCommunicationController)
.onAppear { .onAppear {
updaterController.installUpdate(url: URL(string: "https://github.com/maxgoedjen/secretive/releases/download/v2.1.1/Secretive.zip")!)
if !hasRunSetup { if !hasRunSetup {
showingSetup = true showingSetup = true
} }
@@ -38,20 +37,11 @@ struct Secretive: App {
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
guard hasRunSetup else { return } guard hasRunSetup else { return }
agentStatusChecker.check() agentStatusChecker.check()
if justUpdatedChecker.justUpdated || !agentStatusChecker.running { if agentStatusChecker.running && justUpdatedChecker.justUpdated {
// Two conditions in which we reinstall/attempt a force launch: // Relaunch the agent, since it'll be running from earlier update still
// 1: The app was just updated, and an old version of the agent is alive. Reinstall will deactivate this and activate a new one. reinstallAgent()
// 2: The agent is not running for some reason. We'll attempt to reinstall it, or relaunch directly if that fails. } else if !agentStatusChecker.running && !agentStatusChecker.developmentBuild {
reinstallAgent(uninstallFirst: agentStatusChecker.running) { forceLaunchAgent()
if agentStatusChecker.noninstanceSecretAgentProcesses.isEmpty {
agentLaunchController.killNonInstanceAgents(agents: agentStatusChecker.noninstanceSecretAgentProcesses)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
agentCommunicationController.configure()
}
}
} else {
agentCommunicationController.configure()
} }
} }
} }
@@ -72,13 +62,6 @@ struct Secretive: App {
showingSetup = true showingSetup = true
} }
} }
CommandGroup(after: .help) {
Button("TEST") {
Task {
try await agentCommunicationController.agent?.updatedStore(withID: storeList.modifiableStore?.id ?? UUID())
}
}
}
SidebarCommands() SidebarCommands()
} }
} }
@@ -87,24 +70,27 @@ struct Secretive: App {
extension Secretive { extension Secretive {
private func reinstallAgent(uninstallFirst: Bool, completion: @escaping () -> Void) { private func reinstallAgent() {
justUpdatedChecker.check() justUpdatedChecker.check()
agentLaunchController.install(uninstallFirst: uninstallFirst) { LaunchAgentController().install {
// Wait a second for launchd to kick in (next runloop isn't enough). // Wait a second for launchd to kick in (next runloop isn't enough).
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
agentStatusChecker.check() agentStatusChecker.check()
if !agentStatusChecker.running { if !agentStatusChecker.running {
agentLaunchController.forceLaunch { _ in forceLaunchAgent()
agentStatusChecker.check()
completion()
}
} else {
completion()
} }
} }
} }
} }
private func forceLaunchAgent() {
// We've run setup, we didn't just update, launchd is just not doing it's thing.
// Force a launch directly.
LaunchAgentController().forceLaunch { _ in
agentStatusChecker.check()
}
}
} }

View File

@@ -1,37 +0,0 @@
import Foundation
import Combine
import AppKit
import OSLog
import SecretKit
import SecretAgentKitProtocol
protocol AgentCommunicationControllerProtocol: ObservableObject {
var agent: AgentProtocol? { get }
}
class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol {
private(set) var agent: AgentProtocol? = nil
private var connection: NSXPCConnection? = nil
private var running = false
init() {
}
func configure() {
guard !running else { return }
connection = NSXPCConnection(machServiceName: Bundle.main.agentBundleID)
connection?.remoteObjectInterface = NSXPCInterface(with: AgentProtocol.self)
connection?.invalidationHandler = {
Logger().warning("XPC connection invalidated")
}
connection?.resume()
agent = connection?.remoteObjectProxyWithErrorHandler({ error in
Logger().error("\(String(describing: error))")
}) as! AgentProtocol
running = true
}
}

View File

@@ -30,18 +30,13 @@ class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol {
let agents = secretAgentProcesses let agents = secretAgentProcesses
for agent in agents { for agent in agents {
guard let url = agent.bundleURL else { continue } guard let url = agent.bundleURL else { continue }
if url.absoluteString.contains(Bundle.main.bundleURL.absoluteString) { if url.absoluteString.hasPrefix(Bundle.main.bundleURL.absoluteString) {
return agent return agent
} }
} }
return nil return nil
} }
// All processes, _NOT_ including one the instance agent.
var noninstanceSecretAgentProcesses: [NSRunningApplication] {
NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.agentBundleID)
.filter({ !($0.bundleURL?.absoluteString.contains(Bundle.main.bundleURL.absoluteString) ?? false) })
}
// Whether Secretive is being run in an Xcode environment. // Whether Secretive is being run in an Xcode environment.
var developmentBuild: Bool { var developmentBuild: Bool {

View File

@@ -9,7 +9,6 @@ protocol JustUpdatedCheckerProtocol: ObservableObject {
class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol { class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
@Published var justUpdated: Bool = false @Published var justUpdated: Bool = false
var alreadyRelaunchedForDebug = false
init() { init() {
check() check()
@@ -19,12 +18,7 @@ class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None" let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None"
let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey) UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey)
if currentBuild != Constants.debugVersionKey { justUpdated = lastBuild != currentBuild
justUpdated = lastBuild != currentBuild
} else {
justUpdated = !alreadyRelaunchedForDebug
alreadyRelaunchedForDebug = true
}
} }
@@ -35,7 +29,6 @@ extension JustUpdatedChecker {
enum Constants { enum Constants {
static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild" static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild"
static let debugVersionKey = "GITHUB_CI_VERSION"
} }
} }

View File

@@ -4,27 +4,23 @@ import AppKit
import OSLog import OSLog
import SecretKit import SecretKit
struct AgentLaunchController { struct LaunchAgentController {
func install(uninstallFirst: Bool = true, completion: (() -> Void)? = nil) { func install(completion: (() -> Void)? = nil) {
Logger().debug("Installing agent") Logger().debug("Installing agent")
if uninstallFirst { _ = setEnabled(false)
_ = setEnabled(false)
}
// This is definitely a bit of a "seems to work better" thing but: // This is definitely a bit of a "seems to work better" thing but:
// Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old // Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old
// and start new? // and start new?
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
_ = setEnabled(true) _ = setEnabled(true)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
completion?() completion?()
}
} }
} }
func forceLaunch(completion: ((Bool) -> Void)?) { func forceLaunch(completion: ((Bool) -> Void)?) {
Logger().debug("Agent is still not running, attempting to force launch") Logger().debug("Agent is not running, attempting to force launch")
let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app") let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app")
let config = NSWorkspace.OpenConfiguration() let config = NSWorkspace.OpenConfiguration()
config.activates = false config.activates = false
@@ -40,12 +36,6 @@ struct AgentLaunchController {
} }
} }
func killNonInstanceAgents(agents: [NSRunningApplication]) {
for agent in agents {
agent.terminate()
}
}
private func setEnabled(_ enabled: Bool) -> Bool { private func setEnabled(_ enabled: Bool) -> Bool {
SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled) SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled)
} }

View File

@@ -0,0 +1,41 @@
import Foundation
import Combine
import AppKit
import OSLog
import SecretKit
//import SecretiveUpdater
import ServiceManagement
class UpdaterCommunicationController: ObservableObject {
private(set) var updater: UpdaterProtocol? = nil
private var connection: NSXPCConnection? = nil
private var running = false
init() {
}
func installUpdate(url: URL) {
guard !running else { return }
_ = SMLoginItemSetEnabled(Bundle.main.updaterBundleID as CFString, false)
SMLoginItemSetEnabled(Bundle.main.updaterBundleID as CFString, true)
connection = NSXPCConnection(machServiceName: Bundle.main.updaterBundleID)
connection?.remoteObjectInterface = NSXPCInterface(with: UpdaterProtocol.self)
connection?.invalidationHandler = {
Logger().warning("XPC connection invalidated")
}
connection?.resume()
updater = connection?.remoteObjectProxyWithErrorHandler({ error in
Logger().error("\(String(describing: error))")
}) as? UpdaterProtocol
running = true
let existingURL = Bundle.main.bundleURL
Task {
let result = try await updater?.installUpdate(url: url, to: existingURL)
print(result)
}
}
}

View File

@@ -2,6 +2,7 @@ import Foundation
extension Bundle { extension Bundle {
public var agentBundleID: String { "Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent" } public var agentBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "Host", with: "SecretAgent"))!}
public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!} public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!}
public var updaterBundleID: String { "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater" }
} }

View File

@@ -45,7 +45,6 @@ extension Preview {
} }
class StoreModifiable: Store, SecretStoreModifiable { class StoreModifiable: Store, SecretStoreModifiable {
override var name: String { "Modifiable Preview Store" } override var name: String { "Modifiable Preview Store" }
func create(name: String, requiresAuthentication: Bool) throws { func create(name: String, requiresAuthentication: Bool) throws {
@@ -56,10 +55,6 @@ extension Preview {
func update(secret: Preview.Secret, name: String) throws { func update(secret: Preview.Secret, name: String) throws {
} }
func reload() throws {
}
} }
} }

View File

@@ -2,7 +2,7 @@ import Foundation
import Combine import Combine
import Brief import Brief
class PreviewUpdater: UpdaterProtocol { class PreviewUpdater: UpdateCheckerProtocol {
let update: Release? let update: Release?
let testBuild = false let testBuild = false

View File

@@ -6,7 +6,7 @@
<true/> <true/>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>$(TeamIdentifierPrefix)com.maxgoedjen.secretive</string> <string>$(TeamIdentifierPrefix)com.maxgoedjen.Secretive</string>
</array> </array>
<key>com.apple.security.files.user-selected.read-write</key> <key>com.apple.security.files.user-selected.read-write</key>
<true/> <true/>

View File

@@ -4,7 +4,7 @@ import SecureEnclaveSecretKit
import SmartCardSecretKit import SmartCardSecretKit
import Brief import Brief
struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View { struct ContentView<UpdaterType: UpdateCheckerProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View {
@Binding var showingCreation: Bool @Binding var showingCreation: Bool
@Binding var runningSetup: Bool @Binding var runningSetup: Bool
@@ -32,6 +32,9 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
appPathNotice appPathNotice
newItem newItem
} }
.sheet(isPresented: $runningSetup) {
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
}
} }
} }
@@ -65,11 +68,11 @@ extension ContentView {
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundColor(.white)
}) })
.background(color) .background(color)
.cornerRadius(5) .cornerRadius(5)
.popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in .popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in
UpdateDetailView(update: update) UpdateDetailView(update: update)
} }
) )
} }
} }
@@ -85,11 +88,11 @@ extension ContentView {
}, label: { }, label: {
Image(systemName: "plus") Image(systemName: "plus")
}) })
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { .popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
if let modifiable = storeList.modifiableStore { if let modifiable = storeList.modifiableStore {
CreateSecretView<AnySecretStoreModifiable, AgentCommunicationController>(store: modifiable, showing: $showingCreation) CreateSecretView(store: modifiable, showing: $showingCreation)
}
} }
}
) )
} }
@@ -113,15 +116,12 @@ extension ContentView {
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundColor(.white)
}) })
.background(Color.orange) .background(Color.orange)
.cornerRadius(5) .cornerRadius(5)
} else { } else {
EmptyView() EmptyView()
} }
} }
.sheet(isPresented: $runningSetup) {
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
}
) )
} }
} }
@@ -142,19 +142,19 @@ extension ContentView {
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundColor(.white)
}) })
.background(Color.orange) .background(Color.orange)
.cornerRadius(5) .cornerRadius(5)
.popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { .popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
VStack { VStack {
Image(systemName: "exclamationmark.triangle") Image(systemName: "exclamationmark.triangle")
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: 64) .frame(width: 64)
Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.") Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.")
.frame(maxWidth: 300) .frame(maxWidth: 300)
}
.padding()
} }
.padding()
}
) )
} }
} }

View File

@@ -1,10 +1,9 @@
import SwiftUI import SwiftUI
import SecretKit import SecretKit
struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationControllerType: AgentCommunicationControllerProtocol>: View { struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
@ObservedObject var store: StoreType @ObservedObject var store: StoreType
@EnvironmentObject private var agentCommunicationController: AgentCommunicationControllerType
@Binding var showing: Bool @Binding var showing: Bool
@State private var name = "" @State private var name = ""
@@ -53,9 +52,6 @@ struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationCont
func save() { func save() {
try! store.create(name: name, requiresAuthentication: requiresAuthentication) try! store.create(name: name, requiresAuthentication: requiresAuthentication)
Task {
try! await agentCommunicationController.agent!.updatedStore(withID: store.id)
}
showing = false showing = false
} }
} }

View File

@@ -6,7 +6,7 @@ struct SecretDetailView<SecretType: Secret>: View {
@State var secret: SecretType @State var secret: SecretType
private let keyWriter = OpenSSHKeyWriter() private let keyWriter = OpenSSHKeyWriter()
private let publicKeyFileStoreController = PublicKeyFileStoreController() private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory().replacingOccurrences(of: Bundle.main.hostBundleID, with: Bundle.main.agentBundleID))
var body: some View { var body: some View {
ScrollView { ScrollView {
@@ -18,7 +18,7 @@ struct SecretDetailView<SecretType: Secret>: View {
CopyableView(title: "MD5 Fingerprint", image: Image(systemName: "touchid"), text: keyWriter.openSSHMD5Fingerprint(secret: secret)) CopyableView(title: "MD5 Fingerprint", image: Image(systemName: "touchid"), text: keyWriter.openSSHMD5Fingerprint(secret: secret))
Spacer() Spacer()
.frame(height: 20) .frame(height: 20)
CopyableView(title: "Public Key Contents", image: Image(systemName: "key"), text: keyString) CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
Spacer() Spacer()
.frame(height: 20) .frame(height: 20)
CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret)) CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret))

View File

@@ -22,7 +22,7 @@ struct SetupView: View {
} }
.frame(width: proxy.size.width) .frame(width: proxy.size.width)
} }
.offset(x: -proxy.size.width * CGFloat(stepIndex), y: 0) .offset(x: -proxy.size.width * Double(stepIndex), y: 0)
} }
} }
} }
@@ -44,7 +44,7 @@ struct StepView: View {
let currentStep: Int let currentStep: Int
// Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7 // Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7
let width: CGFloat let width: Double
var body: some View { var body: some View {
ZStack(alignment: .leading) { ZStack(alignment: .leading) {
@@ -53,7 +53,7 @@ struct StepView: View {
.frame(height: 5) .frame(height: 5)
Rectangle() Rectangle()
.foregroundColor(.green) .foregroundColor(.green)
.frame(width: max(0, ((width - (Constants.padding * 2)) / CGFloat(numberOfSteps - 1)) * CGFloat(currentStep) - (Constants.circleWidth / 2)), height: 5) .frame(width: max(0, ((width - (Constants.padding * 2)) / Double(numberOfSteps - 1)) * Double(currentStep) - (Constants.circleWidth / 2)), height: 5)
HStack { HStack {
ForEach(0..<numberOfSteps) { index in ForEach(0..<numberOfSteps) { index in
ZStack { ZStack {
@@ -92,8 +92,8 @@ extension StepView {
enum Constants { enum Constants {
static let padding: CGFloat = 15 static let padding: Double = 15
static let circleWidth: CGFloat = 30 static let circleWidth: Double = 30
} }
@@ -156,7 +156,7 @@ struct SecretAgentSetupView: View {
} }
func install() { func install() {
AgentLaunchController().install() LaunchAgentController().install()
buttonAction() buttonAction()
} }

View File

@@ -1,7 +1,7 @@
import SwiftUI import SwiftUI
import Brief import Brief
struct UpdateDetailView<UpdaterType: Updater>: View { struct UpdateDetailView<UpdaterType: UpdateChecker>: View {
@EnvironmentObject var updater: UpdaterType @EnvironmentObject var updater: UpdaterType

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,60 @@
{
"images" : [
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "Mac Icon.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "Mac Icon@0.25x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,8 @@
<?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>LSUIElement</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,683 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20036.2" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20036.2"/>
</dependencies>
<scenes>
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="SecretiveUpdater" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="SecretiveUpdater" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About SecretiveUpdater" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide SecretiveUpdater" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit SecretiveUpdater" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
<connections>
<action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="SecretiveUpdater Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="SecretiveUpdater" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>$(TeamIdentifierPrefix)com.maxgoedjen.Secretive</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,110 @@
import Foundation
import Brief
import AppleArchive
import System
import Cocoa
import Security.Authorization
import Security.AuthorizationTags
class Updater: UpdaterProtocol {
func installUpdate(url: URL, to destinationURL: URL) async throws -> String {
// let (downloadedURL, _) = try await URLSession.shared.download(from: url)
// let unzipped = try await decompress(url: downloadedURL)
// try await move(url: unzipped, to: destinationURL)
// let config = NSWorkspace.OpenConfiguration()
// config.activates = true
// TODO: clean
_ = try await authorize()
// if let host = NSRunningApplication.runningApplications(withBundleIdentifier: "com.maxgoedjen.Secretive.Host").first(where: { $0.bundleURL?.path.hasPrefix("/Applications") ?? false }) {
// host.terminate()
//
// }
return "OK"
}
func decompress(url: URL) async throws -> URL {
let zipURL = url.deletingPathExtension().appendingPathExtension("zip")
try FileManager.default.copyItem(at: url, to: zipURL)
let id = UUID()
let destinationURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(id.uuidString)/")
_ = try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: [:])
let process = Process()
let pipe = Pipe()
process.launchPath = "/usr/bin/unzip"
process.arguments = ["-o", zipURL.path, "-d", destinationURL.path]
process.standardOutput = pipe
try process.run()
_ = try pipe.fileHandleForReading.readToEnd()
guard let appURL = try FileManager.default.contentsOfDirectory(at: destinationURL, includingPropertiesForKeys: nil).first(where: { $0.pathExtension == "app" }) else {
throw DecompressionError(reason: "Unzip failed")
}
return appURL
}
func move(url: URL, to destinationURL: URL) async throws {
let auth = try await authorize()
try await move(url: url, to: destinationURL)
try await revokeAuthorization(auth)
}
func authorize() async throws -> AuthorizationRef {
let flags = AuthorizationFlags()
var authorization: AuthorizationRef? = nil
AuthorizationCreate(nil, nil, flags, &authorization)
let authFlags: AuthorizationFlags = [.interactionAllowed, .extendRights, .preAuthorize]
var result: OSStatus?
kAuthorizationRightExecute.withCString { cString in
var item = AuthorizationItem(name: cString, valueLength: 0, value: nil, flags: 0)
withUnsafeMutablePointer(to: &item) { pointer in
var rights = AuthorizationRights(count: 1, items: pointer)
result = AuthorizationCopyRights(authorization!, &rights, nil, authFlags, nil)
}
}
guard result == errAuthorizationSuccess, let authorization = authorization else {
throw RightsNotAcquiredError()
}
return authorization
}
func revokeAuthorization(_ authorization: AuthorizationRef) async throws {
AuthorizationFree(authorization, .destroyRights)
}
func priveledgedMove(url: URL, to destination: URL) async throws {
try FileManager.default.replaceItemAt(destination, withItemAt: url)
}
}
extension Updater {
struct DecompressionError: Error, LocalizedError {
let reason: String
}
struct RightsNotAcquiredError: Error, LocalizedError {
}
}
extension URLSession {
@available(macOS, deprecated: 12.0)
public func download(from url: URL) async throws -> (URL, URLResponse) {
try await withCheckedThrowingContinuation { continuation in
let task = downloadTask(with: url) { url, response, error in
guard let url = url, let response = response else {
continuation.resume(throwing: error ?? UnknownError())
return
}
continuation.resume(returning: (url, response))
}
task.resume()
}
}
struct UnknownError: Error {}
}

View File

@@ -0,0 +1,8 @@
import Foundation
import Brief
@objc public protocol UpdaterProtocol {
func installUpdate(url: URL, to: URL) async throws -> String
}

View File

@@ -0,0 +1,26 @@
import Foundation
class ServiceDelegate: NSObject, NSXPCListenerDelegate {
let exported: UpdaterProtocol
init(exportedObject: UpdaterProtocol) {
self.exported = exportedObject
}
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
newConnection.exportedInterface = NSXPCInterface(with: UpdaterProtocol.self)
newConnection.exportedObject = exported
newConnection.resume()
return true
}
}
let updater = Updater()
let delegate = ServiceDelegate(exportedObject: Updater())
let listener = NSXPCListener(machServiceName: Bundle.main.bundleIdentifier!)
listener.delegate = delegate
listener.resume()
try "Hello world".data(using: .utf8)?.write(to: URL(fileURLWithPath: "/Users/max/Downloads/\(UUID().uuidString).txt"))
RunLoop.current.run()