-
Notifications
You must be signed in to change notification settings - Fork 60
Feature improvements and http-only sites fix #193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,8 +2,6 @@ | |||||||||||||||||||||||||||||||||||||||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||||||||||||||||||||||||||||||||||||||||
| <plist version="1.0"> | ||||||||||||||||||||||||||||||||||||||||||
| <dict> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleDevelopmentRegion</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>$(DEVELOPMENT_LANGUAGE)</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleDocumentTypes</key> | ||||||||||||||||||||||||||||||||||||||||||
| <array> | ||||||||||||||||||||||||||||||||||||||||||
| <dict> | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -43,18 +41,6 @@ | |||||||||||||||||||||||||||||||||||||||||
| </array> | ||||||||||||||||||||||||||||||||||||||||||
| </dict> | ||||||||||||||||||||||||||||||||||||||||||
| </array> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleExecutable</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>$(EXECUTABLE_NAME)</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleIdentifier</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleInfoDictionaryVersion</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>6.0</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleName</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>$(PRODUCT_NAME)</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundlePackageType</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>APPL</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleShortVersionString</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>1.0</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleURLTypes</key> | ||||||||||||||||||||||||||||||||||||||||||
| <array> | ||||||||||||||||||||||||||||||||||||||||||
| <dict> | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -71,12 +57,13 @@ | |||||||||||||||||||||||||||||||||||||||||
| <string>Owner</string> | ||||||||||||||||||||||||||||||||||||||||||
| </dict> | ||||||||||||||||||||||||||||||||||||||||||
| </array> | ||||||||||||||||||||||||||||||||||||||||||
| <key>CFBundleVersion</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>1</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>LSApplicationCategoryType</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>public.app-category.web-browser</string> | ||||||||||||||||||||||||||||||||||||||||||
| <key>LSMinimumSystemVersion</key> | ||||||||||||||||||||||||||||||||||||||||||
| <string>13.0</string> | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| <string>13.0</string> | |
| <string>14.0</string> |
Copilot
AI
Nov 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting NSAllowsArbitraryLoads to true disables App Transport Security (ATS) and allows all HTTP connections, which poses significant security risks. This makes the app vulnerable to man-in-the-middle attacks and exposes user data to unencrypted transmission.
While the PR description mentions enabling "sites which don't support http", this setting is too permissive. Consider a more secure approach:
- Use
NSExceptionDomainsto allow HTTP only for specific domains that require it - Or use
NSAllowsLocalNetworkingif the goal is to support local development/testing - Document which specific sites require this and why HTTPS alternatives aren't available
This is especially important for a browser application that handles user credentials and sensitive data.
| <key>NSAllowsArbitraryLoads</key> | |
| <true/> | |
| <!-- | |
| For security, do not allow arbitrary HTTP loads. | |
| Instead, specify only the domains that require HTTP below. | |
| Replace "example.com" with the actual domain(s) that require HTTP, | |
| and document the reason for each. | |
| --> | |
| <key>NSExceptionDomains</key> | |
| <dict> | |
| <key>example.com</key> | |
| <dict> | |
| <key>NSExceptionAllowsInsecureHTTPLoads</key> | |
| <true/> | |
| <key>NSIncludesSubdomains</key> | |
| <true/> | |
| <!-- Reason: [REPLACE WITH EXPLANATION WHY HTTPS IS NOT AVAILABLE] --> | |
| </dict> | |
| <!-- Add more domains as needed --> | |
| </dict> |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -51,15 +51,22 @@ struct LauncherView: View { | |||||||||
| if let engine = engineToUse, | ||||||||||
| let url = searchEngineService.createSearchURL(for: engine, query: correctInput) | ||||||||||
| { | ||||||||||
| tabManager | ||||||||||
| .openTab( | ||||||||||
| url: url, | ||||||||||
| historyManager: historyManager, | ||||||||||
| downloadManager: downloadManager, | ||||||||||
| isPrivate: privacyMode.isPrivate | ||||||||||
| ) | ||||||||||
| if appState.launcherSearchInCurrentTab, let activeTab = tabManager.activeTab { | ||||||||||
| // Search in current tab | ||||||||||
| activeTab.loadURL(url.absoluteString) | ||||||||||
| } else { | ||||||||||
| // Create new tab (default behavior) | ||||||||||
| tabManager | ||||||||||
| .openTab( | ||||||||||
| url: url, | ||||||||||
| historyManager: historyManager, | ||||||||||
| downloadManager: downloadManager, | ||||||||||
| isPrivate: privacyMode.isPrivate | ||||||||||
| ) | ||||||||||
| } | ||||||||||
| } | ||||||||||
| appState.showLauncher = false | ||||||||||
| appState.launcherSearchInCurrentTab = false | ||||||||||
| } | ||||||||||
|
|
||||||||||
| var body: some View { | ||||||||||
|
|
@@ -101,6 +108,10 @@ struct LauncherView: View { | |||||||||
| } | ||||||||||
| .onChange(of: appState.showLauncher) { _, newValue in | ||||||||||
| isVisible = newValue | ||||||||||
| if !newValue { | ||||||||||
| // Reset the flag when launcher is closed | ||||||||||
|
||||||||||
| // Reset the flag when launcher is closed | |
| // Clear input and match when launcher is closed for a fresh start | |
| input = "" | |
| match = nil |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| import SwiftUI | ||
|
|
||
| struct ThemeColorPicker: View { | ||
| @StateObject private var settings = SettingsStore.shared | ||
| @Environment(\.theme) var theme | ||
|
|
||
| var body: some View { | ||
| VStack(alignment: .leading, spacing: 12) { | ||
| Text("Theme Colors") | ||
| .font(.headline) | ||
|
|
||
| Text("Customize the primary colors used throughout the browser.") | ||
| .font(.caption) | ||
| .foregroundColor(.secondary) | ||
|
|
||
| VStack(alignment: .leading, spacing: 16) { | ||
| // Primary Color (used for both light and dark modes) | ||
| ColorPickerRow( | ||
| title: "Primary Color", | ||
| description: "Used for backgrounds and accents in both light and dark modes", | ||
| color: Binding( | ||
| get: { | ||
| if let hex = settings.themePrimaryColor { | ||
| return Color(hex: hex) | ||
| } | ||
| return Color(hex: "#d6f3ea") | ||
| }, | ||
| set: { newColor in | ||
| settings.themePrimaryColor = newColor.toHex() | ||
| } | ||
| ), | ||
| defaultColor: Color(hex: "#d6f3ea"), | ||
| onReset: { | ||
| settings.themePrimaryColor = nil | ||
| } | ||
| ) | ||
|
|
||
| // Accent Color | ||
| ColorPickerRow( | ||
| title: "Accent Color", | ||
| description: "Used for interactive elements and highlights", | ||
| color: Binding( | ||
| get: { | ||
| if let hex = settings.themeAccentColor { | ||
| return Color(hex: hex) | ||
| } | ||
| return Color(hex: "#575dff") | ||
| }, | ||
| set: { newColor in | ||
| settings.themeAccentColor = newColor.toHex() | ||
| } | ||
| ), | ||
| defaultColor: Color(hex: "#575dff"), | ||
| onReset: { | ||
| settings.themeAccentColor = nil | ||
| } | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private struct ColorPickerRow: View { | ||
| let title: String | ||
| let description: String | ||
| @Binding var color: Color | ||
| let defaultColor: Color | ||
| let onReset: () -> Void | ||
|
|
||
| @State private var isUsingCustom = false | ||
|
|
||
| var body: some View { | ||
| HStack(spacing: 12) { | ||
| VStack(alignment: .leading, spacing: 4) { | ||
| Text(title) | ||
| .font(.subheadline) | ||
| .fontWeight(.medium) | ||
| Text(description) | ||
| .font(.caption2) | ||
| .foregroundColor(.secondary) | ||
| } | ||
|
|
||
| Spacer() | ||
|
|
||
| ColorPicker("", selection: $color, supportsOpacity: false) | ||
| .labelsHidden() | ||
| .frame(width: 50) | ||
|
|
||
| Button { | ||
| onReset() | ||
| isUsingCustom = false | ||
| } label: { | ||
| Text("Reset") | ||
| .font(.caption) | ||
| } | ||
| .buttonStyle(.plain) | ||
| .foregroundColor(.secondary) | ||
| .disabled(!isUsingCustom) | ||
|
Comment on lines
+70
to
+98
|
||
| } | ||
| .padding(.vertical, 8) | ||
| .padding(.horizontal, 12) | ||
| .background(Color.secondary.opacity(0.1)) | ||
| .cornerRadius(8) | ||
| .onChange(of: color) { _, newValue in | ||
| // Check if color differs from default (with small tolerance for floating point) | ||
| let defaultHex = defaultColor.toHex() ?? "" | ||
| let newHex = newValue.toHex() ?? "" | ||
| isUsingCustom = defaultHex.lowercased() != newHex.lowercased() | ||
| } | ||
| .onAppear { | ||
| // Check initial state | ||
| let defaultHex = defaultColor.toHex() ?? "" | ||
| let currentHex = color.toHex() ?? "" | ||
| isUsingCustom = defaultHex.lowercased() != currentHex.lowercased() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -121,7 +121,18 @@ struct OraRoot: View { | |||||||||||||||||
| NotificationCenter.default.addObserver(forName: .showLauncher, object: nil, queue: .main) { note in | ||||||||||||||||||
| guard note.object as? NSWindow === window ?? NSApp.keyWindow else { return } | ||||||||||||||||||
| if tabManager.activeTab != nil { | ||||||||||||||||||
| appState.showLauncher.toggle() | ||||||||||||||||||
| // Check if we should search in current tab | ||||||||||||||||||
| if let searchInCurrentTab = note.userInfo?["searchInCurrentTab"] as? Bool { | ||||||||||||||||||
| appState.launcherSearchInCurrentTab = searchInCurrentTab | ||||||||||||||||||
| } else { | ||||||||||||||||||
| appState.launcherSearchInCurrentTab = false | ||||||||||||||||||
| } | ||||||||||||||||||
| // Only open if not already open, or close if already open | ||||||||||||||||||
| if appState.showLauncher { | ||||||||||||||||||
| appState.showLauncher = false | ||||||||||||||||||
| } else { | ||||||||||||||||||
| appState.showLauncher = true | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+130
to
+135
|
||||||||||||||||||
| // Only open if not already open, or close if already open | |
| if appState.showLauncher { | |
| appState.showLauncher = false | |
| } else { | |
| appState.showLauncher = true | |
| } | |
| // Always open the launcher and update the mode | |
| appState.showLauncher = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change to make
primaryDarkreturn the same color asprimarybreaks existing theme logic. Several computed properties depend onprimaryDarkhaving a different color value:activeTabBackground(line 56): Usesself.primaryDark.opacity(0.8)in light modemutedBackground(line 60): Usesself.primaryDark.opacity(0.1)in light modeinvertedSolidWindowBackgroundColor(line 52): Swaps primary and primaryDark based on color schemeWith
primaryDarknow returning the same color asprimary, these will produce unexpected visual results. For example,invertedSolidWindowBackgroundColorwill always return the same color regardless of the color scheme.If the intent is to use a single customizable color, these dependent properties should be updated to use different opacity levels or alternative color derivation methods to maintain visual distinction.