Add iPad support, auto-pinning, and comprehensive logging
- Adaptive iPhone/iPad layout with NavigationSplitView sidebar - Auto-detect SSL-pinned domains, fall back to passthrough - Certificate install via local HTTP server (Safari profile flow) - App Group-backed CA, per-domain leaf cert LRU cache - DB-backed config repository, Darwin notification throttling - Rules engine, breakpoint rules, pinned domain tracking - os.Logger instrumentation across tunnel/proxy/mitm/capture/cert/rules/db/ipc/ui - Fix dyld framework embed, race conditions, thread safety
This commit is contained in:
80
UI/More/PinnedDomainsView.swift
Normal file
80
UI/More/PinnedDomainsView.swift
Normal file
@@ -0,0 +1,80 @@
|
||||
import SwiftUI
|
||||
import ProxyCore
|
||||
import GRDB
|
||||
|
||||
struct PinnedDomainsView: View {
|
||||
@State private var domains: [PinnedDomain] = []
|
||||
@State private var observation: AnyDatabaseCancellable?
|
||||
@State private var showClearConfirmation = false
|
||||
|
||||
private let repo = PinnedDomainRepository()
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("SSL Pinning Detection")
|
||||
.font(.headline)
|
||||
Text("Domains listed here were automatically detected as using SSL pinning. MITM interception is skipped for these domains — they use passthrough instead so the app still works.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
Section("Detected Domains (\(domains.count))") {
|
||||
if domains.isEmpty {
|
||||
Text("No pinned domains detected yet")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.subheadline)
|
||||
} else {
|
||||
ForEach(domains) { domain in
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(domain.domain)
|
||||
.font(.subheadline.weight(.medium))
|
||||
if let reason = domain.reason {
|
||||
Text(reason)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
Text("Detected \(Date(timeIntervalSince1970: domain.detectedAt).formatted(.relative(presentation: .named)))")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.tertiary)
|
||||
}
|
||||
}
|
||||
.onDelete { offsets in
|
||||
for index in offsets {
|
||||
try? repo.unpin(domain: domains[index].domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Pinned Domains")
|
||||
.toolbar {
|
||||
if !domains.isEmpty {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Clear All", role: .destructive) {
|
||||
showClearConfirmation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog("Clear All Pinned Domains?", isPresented: $showClearConfirmation) {
|
||||
Button("Clear All", role: .destructive) {
|
||||
try? repo.deleteAll()
|
||||
}
|
||||
} message: {
|
||||
Text("This will allow MITM interception to be attempted again for all domains. Pinned domains will be re-detected automatically if they still use SSL pinning.")
|
||||
}
|
||||
.task {
|
||||
observation = repo.observeAll()
|
||||
.start(in: DatabaseManager.shared.dbPool) { error in
|
||||
print("Pinned domains observation error: \(error)")
|
||||
} onChange: { newDomains in
|
||||
withAnimation { domains = newDomains }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user