From 998f4b9bf4328f22d7bdd4ca43ebf6204b8cbde7 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 10 Aug 2025 14:23:57 -0700 Subject: [PATCH] WIP --- Sources/Packages/Package.swift | 3 +- .../SecureEnclaveStore.swift | 2 +- Sources/SecretAgent/SecretAgent.entitlements | 4 -- Sources/Secretive.xcodeproj/project.pbxproj | 42 +++++++++++++++++-- .../xcschemes/SecretAgent.xcscheme | 2 +- .../xcshareddata/xcschemes/Secretive.xcscheme | 4 +- Sources/Secretive/Localizable.xcstrings | 1 + Sources/Secretive/Secretive.entitlements | 6 --- Sources/Secretive/Views/ContentView.swift | 4 +- Sources/Secretive/Views/EmptyStoreView.swift | 22 ++-------- .../Secretive/Views/SecretDetailView.swift | 20 ++++----- .../Secretive/Views/SecretListItemView.swift | 28 ++++++++----- Sources/Secretive/Views/SetupView.swift | 2 +- Sources/Secretive/Views/StoreListView.swift | 40 +++++++++--------- 14 files changed, 99 insertions(+), 81 deletions(-) diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift index 1594580..d4fcb16 100644 --- a/Sources/Packages/Package.swift +++ b/Sources/Packages/Package.swift @@ -65,7 +65,8 @@ let package = Package( , .target( name: "Brief", - dependencies: [] + dependencies: [], + swiftSettings: swiftSettings ), .testTarget( name: "BriefTests", diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift index 371cdd5..1259b42 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift @@ -2,7 +2,7 @@ import Foundation import Observation import Security import CryptoKit -import LocalAuthentication +@preconcurrency import LocalAuthentication import SecretKit import Synchronization diff --git a/Sources/SecretAgent/SecretAgent.entitlements b/Sources/SecretAgent/SecretAgent.entitlements index 895fc77..c9423c4 100644 --- a/Sources/SecretAgent/SecretAgent.entitlements +++ b/Sources/SecretAgent/SecretAgent.entitlements @@ -2,10 +2,6 @@ - com.apple.security.app-sandbox - - com.apple.security.network.client - com.apple.security.smartcard keychain-access-groups diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj index de462b1..09a050d 100644 --- a/Sources/Secretive.xcodeproj/project.pbxproj +++ b/Sources/Secretive.xcodeproj/project.pbxproj @@ -359,8 +359,9 @@ 50617D7723FCE48D0099B055 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1220; - LastUpgradeCheck = 1320; + LastUpgradeCheck = 2600; ORGANIZATIONNAME = "Max Goedjen"; TargetAttributes = { 50617D7E23FCE48D0099B055 = { @@ -491,6 +492,8 @@ baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -521,9 +524,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -544,6 +549,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = ""; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; STRIP_INSTALLED_PRODUCT = NO; STRIP_SWIFT_SYMBOLS = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -558,6 +564,8 @@ baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -588,9 +596,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -604,6 +614,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = ""; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; STRIP_INSTALLED_PRODUCT = NO; STRIP_SWIFT_SYMBOLS = NO; SWIFT_COMPILATION_MODE = wholemodule; @@ -616,17 +627,20 @@ 50617D9E23FCE48E0099B055 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\""; DEVELOPMENT_TEAM = Z72PRUAWF6; + ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readwrite; INFOPLIST_FILE = Secretive/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -642,17 +656,20 @@ 50617D9F23FCE48E0099B055 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements; CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\""; DEVELOPMENT_TEAM = Z72PRUAWF6; + ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readwrite; INFOPLIST_FILE = Secretive/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -670,6 +687,8 @@ baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -700,9 +719,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -723,6 +744,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = ""; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; STRIP_INSTALLED_PRODUCT = NO; STRIP_SWIFT_SYMBOLS = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -735,14 +757,17 @@ 508A5915241EF1A00069DC07 /* Test */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\""; + ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = NO; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readwrite; INFOPLIST_FILE = Secretive/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -760,8 +785,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\""; + ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = SecretAgent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -781,9 +809,12 @@ CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\""; DEVELOPMENT_TEAM = Z72PRUAWF6; + ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = SecretAgent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -804,9 +835,12 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\""; DEVELOPMENT_TEAM = Z72PRUAWF6; + ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = SecretAgent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/SecretAgent.xcscheme b/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/SecretAgent.xcscheme index 647184f..4c9211b 100644 --- a/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/SecretAgent.xcscheme +++ b/Sources/Secretive.xcodeproj/xcshareddata/xcschemes/SecretAgent.xcscheme @@ -1,6 +1,6 @@ - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - com.apple.security.smartcard keychain-access-groups diff --git a/Sources/Secretive/Views/ContentView.swift b/Sources/Secretive/Views/ContentView.swift index 77ee96e..2e486e1 100644 --- a/Sources/Secretive/Views/ContentView.swift +++ b/Sources/Secretive/Views/ContentView.swift @@ -10,7 +10,7 @@ struct ContentView: View { @Binding var runningSetup: Bool @Binding var hasRunSetup: Bool @State var showingAgentInfo = false - @State var activeSecret: AnySecret.ID? + @State var activeSecret: AnySecret? @Environment(\.colorScheme) var colorScheme @Environment(\.secretStoreList) private var storeList: SecretStoreList @@ -106,7 +106,7 @@ extension ContentView { if let modifiable = storeList.modifiableStore { CreateSecretView(store: modifiable, showing: $showingCreation) .onDisappear { - guard let newest = modifiable.secrets.last?.id else { return } + guard let newest = modifiable.secrets.last else { return } activeSecret = newest } } diff --git a/Sources/Secretive/Views/EmptyStoreView.swift b/Sources/Secretive/Views/EmptyStoreView.swift index 1bd3eae..b6593c0 100644 --- a/Sources/Secretive/Views/EmptyStoreView.swift +++ b/Sources/Secretive/Views/EmptyStoreView.swift @@ -3,31 +3,17 @@ import SecretKit struct EmptyStoreView: View { - @State var store: AnySecretStore - @Binding var activeSecret: AnySecret.ID? - + @State var store: AnySecretStore? + var body: some View { if store is AnySecretStoreModifiable { - NavigationLink(destination: EmptyStoreModifiableView(), tag: Constants.emptyStoreModifiableTag, selection: $activeSecret) { - Text("empty_store_modifiable_title") - } + EmptyStoreModifiableView() } else { - NavigationLink(destination: EmptyStoreImmutableView(), tag: Constants.emptyStoreTag, selection: $activeSecret) { - Text("empty_store_nonmodifiable_title") - } + EmptyStoreImmutableView() } } } -extension EmptyStoreView { - - enum Constants { - static let emptyStoreModifiableTag = "emptyStoreModifiableTag" - static let emptyStoreTag = "emptyStoreTag" - } - -} - struct EmptyStoreImmutableView: View { var body: some View { diff --git a/Sources/Secretive/Views/SecretDetailView.swift b/Sources/Secretive/Views/SecretDetailView.swift index 62a77ca..75dde47 100644 --- a/Sources/Secretive/Views/SecretDetailView.swift +++ b/Sources/Secretive/Views/SecretDetailView.swift @@ -3,7 +3,7 @@ import SecretKit struct SecretDetailView: View { - @State var secret: SecretType + let secret: SecretType private let keyWriter = OpenSSHKeyWriter() private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory().replacingOccurrences(of: Bundle.main.hostBundleID, with: Bundle.main.agentBundleID)) @@ -47,12 +47,12 @@ struct SecretDetailView: View { } -//#if DEBUG -// -//struct SecretDetailView_Previews: PreviewProvider { -// static var previews: some View { -// SecretDetailView(secret: Preview.Store(numberOfRandomSecrets: 1).secrets[0]) -// } -//} -// -//#endif +#if DEBUG + +struct SecretDetailView_Previews: PreviewProvider { + static var previews: some View { + SecretDetailView(secret: Preview.Store(numberOfRandomSecrets: 1).secrets[0]) + } +} + +#endif diff --git a/Sources/Secretive/Views/SecretListItemView.swift b/Sources/Secretive/Views/SecretListItemView.swift index c498eaa..38a6f36 100644 --- a/Sources/Secretive/Views/SecretListItemView.swift +++ b/Sources/Secretive/Views/SecretListItemView.swift @@ -2,24 +2,30 @@ import SwiftUI import SecretKit struct SecretListItemView: View { - + @State var store: AnySecretStore var secret: AnySecret - @Binding var activeSecret: AnySecret.ID? - + @State var isDeleting: Bool = false @State var isRenaming: Bool = false - + var deletedSecret: (AnySecret) -> Void var renamedSecret: (AnySecret) -> Void - - var body: some View { - let showingPopupWrapped = Binding( + + private var showingPopup: Binding { + Binding( get: { isDeleting || isRenaming }, - set: { if $0 == false { isDeleting = false; isRenaming = false } } + set: { + if $0 == false { + isDeleting = false + isRenaming = false + } + } ) - - return NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: $activeSecret) { + } + + var body: some View { + NavigationLink(value: secret) { if secret.requiresAuthentication { HStack { Text(secret.name) @@ -40,7 +46,7 @@ struct SecretListItemView: View { } } } - .popover(isPresented: showingPopupWrapped) { + .popover(isPresented: showingPopup) { if let modifiable = store as? AnySecretStoreModifiable { if isDeleting { DeleteSecretView(store: modifiable, secret: secret) { deleted in diff --git a/Sources/Secretive/Views/SetupView.swift b/Sources/Secretive/Views/SetupView.swift index b212455..8f71bc0 100644 --- a/Sources/Secretive/Views/SetupView.swift +++ b/Sources/Secretive/Views/SetupView.swift @@ -55,7 +55,7 @@ struct StepView: View { .foregroundColor(.green) .frame(width: max(0, ((width - (Constants.padding * 2)) / Double(numberOfSteps - 1)) * Double(currentStep) - (Constants.circleWidth / 2)), height: 5) HStack { - ForEach(0.. index { Circle() diff --git a/Sources/Secretive/Views/StoreListView.swift b/Sources/Secretive/Views/StoreListView.swift index edb7f98..7ffe0ee 100644 --- a/Sources/Secretive/Views/StoreListView.swift +++ b/Sources/Secretive/Views/StoreListView.swift @@ -4,8 +4,8 @@ import SecretKit struct StoreListView: View { - @Binding var activeSecret: AnySecret.ID? - + @Binding var activeSecret: AnySecret? + @Environment(SecretStoreList.self) private var storeList: SecretStoreList private func secretDeleted(secret: AnySecret) { @@ -13,25 +13,24 @@ struct StoreListView: View { } private func secretRenamed(secret: AnySecret) { - activeSecret = secret.id + activeSecret = secret } var body: some View { - NavigationView { + NavigationSplitView { List(selection: $activeSecret) { ForEach(storeList.stores) { store in if store.isAvailable { Section(header: Text(store.name)) { if store.secrets.isEmpty { - EmptyStoreView(store: store, activeSecret: $activeSecret) + EmptyStoreView(store: store) } else { ForEach(store.secrets) { secret in SecretListItemView( store: store, secret: secret, - activeSecret: $activeSecret, - deletedSecret: self.secretDeleted, - renamedSecret: self.secretRenamed + deletedSecret: secretDeleted, + renamedSecret: secretRenamed ) } } @@ -39,25 +38,26 @@ struct StoreListView: View { } } } - .listStyle(SidebarListStyle()) - .onAppear { - activeSecret = nextDefaultSecret + } detail: { + if let activeSecret { + SecretDetailView(secret: activeSecret) + } else { + EmptyStoreView(store: storeList.stores.first) } - .frame(minWidth: 100, idealWidth: 240) } + .navigationSplitViewStyle(.balanced) + .onAppear { + activeSecret = nextDefaultSecret + } + .frame(minWidth: 100, idealWidth: 240) + } } extension StoreListView { - var nextDefaultSecret: AnyHashable? { - let fallback: AnyHashable - if storeList.modifiableStore?.isAvailable ?? false { - fallback = EmptyStoreView.Constants.emptyStoreModifiableTag - } else { - fallback = EmptyStoreView.Constants.emptyStoreTag - } - return storeList.stores.compactMap(\.secrets.first).first?.id ?? fallback + private var nextDefaultSecret: AnySecret? { + return storeList.stores.compactMap(\.secrets.first).first } }