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:
82
ProxyCore/Sources/ProxyEngine/RulesEngine.swift
Normal file
82
ProxyCore/Sources/ProxyEngine/RulesEngine.swift
Normal file
@@ -0,0 +1,82 @@
|
||||
import Foundation
|
||||
|
||||
/// Centralized rules engine that checks proxy rules (block list, map local, DNS spoofing, no-cache)
|
||||
/// against live traffic. All methods are static and synchronous for use in NIO pipeline handlers.
|
||||
public enum RulesEngine {
|
||||
|
||||
private static let rulesRepo = RulesRepository()
|
||||
|
||||
// MARK: - Block List
|
||||
|
||||
/// Returns the `BlockAction` if the given URL + method matches an enabled block rule, or nil.
|
||||
public static func checkBlockList(url: String, method: String) -> BlockAction? {
|
||||
guard IPCManager.shared.isBlockListEnabled else { return nil }
|
||||
do {
|
||||
let entries = try rulesRepo.fetchEnabledBlockEntries()
|
||||
for entry in entries {
|
||||
guard entry.method == "ANY" || entry.method == method else { continue }
|
||||
if blockEntry(entry, matches: url) {
|
||||
return entry.action
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("[RulesEngine] Failed to check block list: \(error)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - Map Local
|
||||
|
||||
/// Returns the first matching `MapLocalRule` for the URL + method, or nil.
|
||||
public static func checkMapLocal(url: String, method: String) -> MapLocalRule? {
|
||||
do {
|
||||
let rules = try rulesRepo.fetchEnabledMapLocalRules()
|
||||
for rule in rules {
|
||||
guard rule.method == "ANY" || rule.method == method else { continue }
|
||||
if WildcardMatcher.matches(url, pattern: rule.urlPattern) {
|
||||
return rule
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("[RulesEngine] Failed to check map local rules: \(error)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - DNS Spoofing
|
||||
|
||||
/// Returns the target domain if the given domain matches an enabled DNS spoof rule, or nil.
|
||||
public static func checkDNSSpoof(domain: String) -> String? {
|
||||
guard IPCManager.shared.isDNSSpoofingEnabled else { return nil }
|
||||
do {
|
||||
let rules = try rulesRepo.fetchEnabledDNSSpoofRules()
|
||||
for rule in rules {
|
||||
if WildcardMatcher.matches(domain, pattern: rule.sourceDomain) {
|
||||
return rule.targetDomain
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("[RulesEngine] Failed to check DNS spoof rules: \(error)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - No-Cache
|
||||
|
||||
/// Returns true if the no-caching toggle is enabled.
|
||||
public static func shouldStripCache() -> Bool {
|
||||
IPCManager.shared.isNoCachingEnabled
|
||||
}
|
||||
|
||||
private static func blockEntry(_ entry: BlockListEntry, matches url: String) -> Bool {
|
||||
if WildcardMatcher.matches(url, pattern: entry.urlPattern) {
|
||||
return true
|
||||
}
|
||||
|
||||
guard entry.includeSubpaths else { return false }
|
||||
guard !entry.urlPattern.contains("*"), !entry.urlPattern.contains("?") else { return false }
|
||||
|
||||
let normalizedPattern = entry.urlPattern.hasSuffix("/") ? entry.urlPattern : "\(entry.urlPattern)/"
|
||||
return url == entry.urlPattern || url.hasPrefix(normalizedPattern)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user