Post

Replies

Boosts

Views

Activity

Reply to Implement web filter in macOS with NetworkExtension API
While testing, I also noticed that flow from browsers other than Safari does not provide url and hence is of no use. In handleNewFlow the flow.url is nil for Chrome and Firefox. Documentation - https://developer.apple.com/documentation/networkextension/nefilterflow/1618935-url describes this: This parameter is only non-nil for flows that originate from WebKit browser objects. Any possible solution for getting url on flows from non-WebKit browsers?
Sep ’20
Reply to Implement web filter in macOS with NetworkExtension API
Thanks for your quick answer. The sourceAppAuditToken may be available directly on the NEFilterFlow object delivered in handleNewFlow. Yes, sourceAppAuditToken was suggested by Xcode's autocompletion feature. But, I'm unsure to extract the sourceAppIdentifier from it. After searching through forums, I found following solution proposed by Quinn “The Eskimo!” - https://developer.apple.com/forums/profile/eskimo in this - https://developer.apple.com/forums/thread/128423 thread. > func bundleIDForAuditToken(_ tokenData: Data) -> String? { &#9;&#9;/* Get a code reference. */ &#9;&#9;var codeQ: SecCode? = nil &#9;&#9;var err = SecCodeCopyGuestWithAttributes(nil, [ &#9;&#9;&#9;&#9;kSecGuestAttributeAudit: tokenData &#9;&#9;] as NSDictionary, [], &codeQ) &#9;&#9;guard err == errSecSuccess else { &#9;&#9;&#9;&#9;return nil &#9;&#9;} &#9;&#9;let code = codeQ! &#9;&#9;/* Convert that to a static code. */ &#9;&#9;var staticCodeQ: SecStaticCode? = nil &#9;&#9;err = SecCodeCopyStaticCode(code, [], &staticCodeQ) &#9;&#9;guard err == errSecSuccess else { &#9;&#9;&#9;&#9;return nil &#9;&#9;} &#9;&#9;let staticCode = staticCodeQ! &#9;&#9;/* Get code signing information about that. */ &#9;&#9;var infoQ: CFDictionary? = nil &#9;&#9;err = SecCodeCopySigningInformation(staticCode, [], &infoQ) &#9;&#9;guard err == errSecSuccess else { &#9;&#9;&#9;&#9;return nil &#9;&#9;} &#9;&#9;let info = infoQ! as! [String:Any] &#9;&#9;/* Extract the bundle ID from that. */ &#9;&#9;guard &#9;&#9;&#9;&#9;let plist = info[kSecCodeInfoPList as String] as? [String:Any], &#9;&#9;&#9;&#9;let bundleID = plist[kCFBundleIdentifierKey as String] as? String &#9;&#9;else { &#9;&#9;&#9;&#9;return nil &#9;&#9;} &#9;&#9;return bundleID } However, when I tried, this function always returns nil. It may also be worthwhile just taking a look at the the complete flow description to see what is all provided there when flows are delivered to your app. On your suggestion, I inspected flow.description and yes it contains valuable information. Here's an example for sake of completion: "\n&#9;&#9;&#9;&#9;identifier = CC71CAFA-781B-4402-970F-AB184EA5C14C\n&#9;&#9;&#9;&#9;hostname = apidata.googleusercontent.com\n&#9;&#9;&#9;&#9;sourceAppIdentifier = .com.apple.CalendarAgent\n&#9;&#9;&#9;&#9;sourceAppVersion = \n&#9;&#9;&#9;&#9;sourceAppUniqueIdentifier = 20:{length = 20, bytes = 0x618db8164f86a2db52185dd834f237c73551ca22}\n&#9;&#9;&#9;&#9;procPID = 408\n&#9;&#9;&#9;&#9;eprocPID = 408\n&#9;&#9;&#9;&#9;direction = outbound\n&#9;&#9;&#9;&#9;inBytes = 0\n&#9;&#9;&#9;&#9;outBytes = 0\n&#9;&#9;&#9;&#9;signature = 32:{length = 32, bytes = 0xb4f38a2e 3d2b14c3 24f807b6 06574c54 ... bbf1ebdb 08949f61 }\n&#9;&#9;&#9;&#9;remoteEndpoint = 172.217.166.193:443\n&#9;&#9;&#9;&#9;protocol = 6\n&#9;&#9;&#9;&#9;family = 2\n&#9;&#9;&#9;&#9;type = 1\n&#9;&#9;&#9;&#9;procUUID = 1693DF56-EE62-3156-9B2D-23D06CF5A7A7\n&#9;&#9;&#9;&#9;eprocUUID = 1693DF56-EE62-3156-9B2D-23D06CF5A7A7" I've implemented following hacky solution to extract the sourceAppIdentifier from this string and returning early from non-browser app flows: class FilterDataProvider: NEFilterDataProvider {     static let filteredApps: Set = [".com.apple.Safari", ".com.google.Chrome", ".org.mozilla.firefox"]     override func startFilter(completionHandler: @escaping (Error?) -> Void) {         /* Same as above */     }     func isFiltered(description: String) -> Bool {         guard let sourceAppIdentifier = description.matches(for: #"(?<=sourceAppIdentifier = ).+?(?=\s)"#).first else {             return false         }         return FilterDataProvider.filteredApps.contains(sourceAppIdentifier)     }     override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {         guard let url = flow.url?.absoluteURL,               isFiltered(description: flow.description) else {                 return .allow()         } /* .drop() if url found in local DB */         return .allow()     } } extension String {     func matches(for regex: String) -> [String] {         do {             let regex = try NSRegularExpression(pattern: regex)             let results = regex.matches(in: self, range: NSRange(self.startIndex..., in: self))             return results.map {                 String(self[Range($0.range, in: self)!])             }         } catch {             return []         }     } } Is there an alternative to Quinn's solution or any better way to do this?
Sep ’20