├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── Alamofire.swift │ │ ├── Download.swift │ │ ├── Error.swift │ │ ├── Manager.swift │ │ ├── MultipartFormData.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── Stream.swift │ │ ├── Upload.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── wyd.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── Pods.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── Alamofire │ ├── Alamofire-Private.xcconfig │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ ├── Alamofire.xcconfig │ └── Info.plist │ └── Pods │ ├── Info.plist │ ├── Pods-acknowledgements.markdown │ ├── Pods-acknowledgements.plist │ ├── Pods-dummy.m │ ├── Pods-frameworks.sh │ ├── Pods-resources.sh │ ├── Pods-umbrella.h │ ├── Pods.debug.xcconfig │ ├── Pods.modulemap │ └── Pods.release.xcconfig ├── README.md ├── XGuardian.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── XGuardian.xcworkspace └── contents.xcworkspacedata ├── XGuardian ├── AppDelegate.swift ├── Base.lproj │ └── MainMenu.xib ├── CXKeychainHelper.h ├── CXKeychainHelper.m ├── HijackView.xib ├── Images.xcassets │ ├── AllscanIcon.imageset │ │ ├── 2-16.png │ │ ├── 2-32.png │ │ ├── 2-64.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 1024.png │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256-1.png │ │ ├── 256.png │ │ ├── 32-1.png │ │ ├── 32.png │ │ ├── 512-1.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── Contents.json │ ├── BudleIDIcon.imageset │ │ ├── Contents.json │ │ ├── 厂商16.png │ │ ├── 厂商32.png │ │ └── 厂商64.png │ ├── KeychainIcon.imageset │ │ ├── Contents.json │ │ ├── 钥匙16.png │ │ ├── 钥匙32.png │ │ └── 钥匙64.png │ ├── Net.imageset │ │ ├── Contents.json │ │ └── 网.png │ ├── NetforgImage.imageset │ │ ├── Contents.json │ │ ├── 右侧图240-240.png │ │ └── 右侧图480-480.png │ └── UrlschemlIcon.imageset │ │ ├── Contents.json │ │ ├── 门16.png │ │ ├── 门32.png │ │ └── 门64.png ├── Info.plist ├── KeychainHijackDetailsView.xib ├── KeychainSwiftAPI.swift ├── KeychainView.xib ├── ScanView.xib ├── ThreatsView.xib ├── UpdateWindow.xib ├── XGAllThreatsDelegate.swift ├── XGBackend.swift ├── XGBundleDetailsView.swift ├── XGBundleDetailsView.xib ├── XGBundleIDThreatsDelegate.swift ├── XGContainerApplicationManager.swift ├── XGFileEventsHelper.h ├── XGFileEventsHelper.m ├── XGFileSecurityHelper.h ├── XGFileSecurityHelper.m ├── XGHijackListAppCell.swift ├── XGHijackListCell.swift ├── XGHijackListView.swift ├── XGHijackView.swift ├── XGKeychainHijackDetailsCell.swift ├── XGKeychainHijackDetailsView.swift ├── XGKeychainManager.swift ├── XGKeychainObserver.h ├── XGKeychainObserver.m ├── XGKeychainThreatsDelegate.swift ├── XGKeychainView.swift ├── XGMainViewController.swift ├── XGScanView.swift ├── XGScanViewController.swift ├── XGSecurityItem.swift ├── XGSecurityItemSet.swift ├── XGSideBarCellView.swift ├── XGThirdMacApplicationInfo.swift ├── XGThreatsView.swift ├── XGThreatsViewController.swift ├── XGURLSchemeDetailsView.swift ├── XGURLSchemeHijackDetailsView.xib ├── XGURLSchemeListDelegate.swift ├── XGURLSchemeManager.swift ├── XGURLSchemeThreatsDelegate.swift ├── XGUpdatePanel.swift ├── XGUtilize.swift ├── XGuardian-Bridging-Header.h └── XGuardian.entitlements └── XGuardianTests ├── Info.plist └── XGuardianTests.swift /Podfile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | source 'https://github.com/CocoaPods/Specs.git' 5 | platform :osx, "10.10" 6 | use_frameworks! 7 | 8 | pod 'Alamofire', '~> 2.0' -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (2.0.1) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (~> 2.0) 6 | 7 | SPEC CHECKSUMS: 8 | Alamofire: 1d8e208d616fbbfd2391b15eae766d07c96cdc49 9 | 10 | COCOAPODS: 0.38.2 11 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Download.swift: -------------------------------------------------------------------------------- 1 | // Download.swift 2 | // 3 | // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | extension Manager { 26 | private enum Downloadable { 27 | case Request(NSURLRequest) 28 | case ResumeData(NSData) 29 | } 30 | 31 | private func download(downloadable: Downloadable, destination: Request.DownloadFileDestination) -> Request { 32 | var downloadTask: NSURLSessionDownloadTask! 33 | 34 | switch downloadable { 35 | case .Request(let request): 36 | dispatch_sync(queue) { 37 | downloadTask = self.session.downloadTaskWithRequest(request) 38 | } 39 | case .ResumeData(let resumeData): 40 | dispatch_sync(queue) { 41 | downloadTask = self.session.downloadTaskWithResumeData(resumeData) 42 | } 43 | } 44 | 45 | let request = Request(session: session, task: downloadTask) 46 | 47 | if let downloadDelegate = request.delegate as? Request.DownloadTaskDelegate { 48 | downloadDelegate.downloadTaskDidFinishDownloadingToURL = { session, downloadTask, URL in 49 | return destination(URL, downloadTask.response as! NSHTTPURLResponse) 50 | } 51 | } 52 | 53 | delegate[request.delegate.task] = request.delegate 54 | 55 | if startRequestsImmediately { 56 | request.resume() 57 | } 58 | 59 | return request 60 | } 61 | 62 | // MARK: Request 63 | 64 | /** 65 | Creates a download request for the specified method, URL string, parameters, parameter encoding, headers 66 | and destination. 67 | 68 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 69 | 70 | - parameter method: The HTTP method. 71 | - parameter URLString: The URL string. 72 | - parameter parameters: The parameters. `nil` by default. 73 | - parameter encoding: The parameter encoding. `.URL` by default. 74 | - parameter headers: The HTTP headers. `nil` by default. 75 | - parameter destination: The closure used to determine the destination of the downloaded file. 76 | 77 | - returns: The created download request. 78 | */ 79 | public func download( 80 | method: Method, 81 | _ URLString: URLStringConvertible, 82 | parameters: [String: AnyObject]? = nil, 83 | encoding: ParameterEncoding = .URL, 84 | headers: [String: String]? = nil, 85 | destination: Request.DownloadFileDestination) 86 | -> Request 87 | { 88 | let mutableURLRequest = URLRequest(method, URLString, headers: headers) 89 | let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 90 | 91 | return download(encodedURLRequest, destination: destination) 92 | } 93 | 94 | /** 95 | Creates a request for downloading from the specified URL request. 96 | 97 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 98 | 99 | - parameter URLRequest: The URL request 100 | - parameter destination: The closure used to determine the destination of the downloaded file. 101 | 102 | - returns: The created download request. 103 | */ 104 | public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request { 105 | return download(.Request(URLRequest.URLRequest), destination: destination) 106 | } 107 | 108 | // MARK: Resume Data 109 | 110 | /** 111 | Creates a request for downloading from the resume data produced from a previous request cancellation. 112 | 113 | If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. 114 | 115 | - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` 116 | when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for 117 | additional information. 118 | - parameter destination: The closure used to determine the destination of the downloaded file. 119 | 120 | - returns: The created download request. 121 | */ 122 | public func download(resumeData: NSData, destination: Request.DownloadFileDestination) -> Request { 123 | return download(.ResumeData(resumeData), destination: destination) 124 | } 125 | } 126 | 127 | // MARK: - 128 | 129 | extension Request { 130 | /** 131 | A closure executed once a request has successfully completed in order to determine where to move the temporary 132 | file written to during the download process. The closure takes two arguments: the temporary file URL and the URL 133 | response, and returns a single argument: the file URL where the temporary file should be moved. 134 | */ 135 | public typealias DownloadFileDestination = (NSURL, NSHTTPURLResponse) -> NSURL 136 | 137 | /** 138 | Creates a download file destination closure which uses the default file manager to move the temporary file to a 139 | file URL in the first available directory with the specified search path directory and search path domain mask. 140 | 141 | - parameter directory: The search path directory. `.DocumentDirectory` by default. 142 | - parameter domain: The search path domain mask. `.UserDomainMask` by default. 143 | 144 | - returns: A download file destination closure. 145 | */ 146 | public class func suggestedDownloadDestination( 147 | directory directory: NSSearchPathDirectory = .DocumentDirectory, 148 | domain: NSSearchPathDomainMask = .UserDomainMask) 149 | -> DownloadFileDestination 150 | { 151 | return { temporaryURL, response -> NSURL in 152 | let directoryURLs = NSFileManager.defaultManager().URLsForDirectory(directory, inDomains: domain) 153 | 154 | if !directoryURLs.isEmpty { 155 | return directoryURLs[0].URLByAppendingPathComponent(response.suggestedFilename!) 156 | } 157 | 158 | return temporaryURL 159 | } 160 | } 161 | 162 | /// The resume data of the underlying download task if available after a failure. 163 | public var resumeData: NSData? { 164 | var data: NSData? 165 | 166 | if let delegate = delegate as? DownloadTaskDelegate { 167 | data = delegate.resumeData 168 | } 169 | 170 | return data 171 | } 172 | 173 | // MARK: - DownloadTaskDelegate 174 | 175 | class DownloadTaskDelegate: TaskDelegate, NSURLSessionDownloadDelegate { 176 | var downloadTask: NSURLSessionDownloadTask? { return task as? NSURLSessionDownloadTask } 177 | var downloadProgress: ((Int64, Int64, Int64) -> Void)? 178 | 179 | var resumeData: NSData? 180 | override var data: NSData? { return resumeData } 181 | 182 | // MARK: - NSURLSessionDownloadDelegate 183 | 184 | // MARK: Override Closures 185 | 186 | var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> NSURL)? 187 | var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 188 | var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)? 189 | 190 | // MARK: Delegate Methods 191 | 192 | func URLSession( 193 | session: NSURLSession, 194 | downloadTask: NSURLSessionDownloadTask, 195 | didFinishDownloadingToURL location: NSURL) 196 | { 197 | if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { 198 | do { 199 | let destination = downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) 200 | try NSFileManager.defaultManager().moveItemAtURL(location, toURL: destination) 201 | } catch { 202 | self.error = error as NSError 203 | } 204 | } 205 | } 206 | 207 | func URLSession( 208 | session: NSURLSession, 209 | downloadTask: NSURLSessionDownloadTask, 210 | didWriteData bytesWritten: Int64, 211 | totalBytesWritten: Int64, 212 | totalBytesExpectedToWrite: Int64) 213 | { 214 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 215 | downloadTaskDidWriteData( 216 | session, 217 | downloadTask, 218 | bytesWritten, 219 | totalBytesWritten, 220 | totalBytesExpectedToWrite 221 | ) 222 | } else { 223 | progress.totalUnitCount = totalBytesExpectedToWrite 224 | progress.completedUnitCount = totalBytesWritten 225 | 226 | downloadProgress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) 227 | } 228 | } 229 | 230 | func URLSession( 231 | session: NSURLSession, 232 | downloadTask: NSURLSessionDownloadTask, 233 | didResumeAtOffset fileOffset: Int64, 234 | expectedTotalBytes: Int64) 235 | { 236 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 237 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 238 | } else { 239 | progress.totalUnitCount = expectedTotalBytes 240 | progress.completedUnitCount = fileOffset 241 | } 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Error.swift: -------------------------------------------------------------------------------- 1 | // Error.swift 2 | // 3 | // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /// The `Error` struct provides a convenience for creating custom Alamofire NSErrors. 26 | public struct Error { 27 | /// The domain used for creating all Alamofire errors. 28 | public static let Domain = "com.alamofire.error" 29 | 30 | /// The custom error codes generated by Alamofire. 31 | public enum Code: Int { 32 | case InputStreamReadFailed = -6000 33 | case OutputStreamWriteFailed = -6001 34 | case ContentTypeValidationFailed = -6002 35 | case StatusCodeValidationFailed = -6003 36 | case DataSerializationFailed = -6004 37 | case StringSerializationFailed = -6005 38 | case JSONSerializationFailed = -6006 39 | case PropertyListSerializationFailed = -6007 40 | } 41 | 42 | /** 43 | Creates an `NSError` with the given error code and failure reason. 44 | 45 | - parameter code: The error code. 46 | - parameter failureReason: The failure reason. 47 | 48 | - returns: An `NSError` with the given error code and failure reason. 49 | */ 50 | public static func errorWithCode(code: Code, failureReason: String) -> NSError { 51 | return errorWithCode(code.rawValue, failureReason: failureReason) 52 | } 53 | 54 | /** 55 | Creates an `NSError` with the given error code and failure reason. 56 | 57 | - parameter code: The error code. 58 | - parameter failureReason: The failure reason. 59 | 60 | - returns: An `NSError` with the given error code and failure reason. 61 | */ 62 | public static func errorWithCode(code: Int, failureReason: String) -> NSError { 63 | let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason] 64 | return NSError(domain: Domain, code: code, userInfo: userInfo) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ParameterEncoding.swift: -------------------------------------------------------------------------------- 1 | // ParameterEncoding.swift 2 | // 3 | // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | HTTP method definitions. 27 | 28 | See https://tools.ietf.org/html/rfc7231#section-4.3 29 | */ 30 | public enum Method: String { 31 | case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT 32 | } 33 | 34 | // MARK: ParameterEncoding 35 | 36 | /** 37 | Used to specify the way in which a set of parameters are applied to a URL request. 38 | 39 | - `URL`: Creates a query string to be set as or appended to any existing URL query for `GET`, `HEAD`, 40 | and `DELETE` requests, or set as the body for requests with any other HTTP method. The 41 | `Content-Type` HTTP header field of an encoded request with HTTP body is set to 42 | `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification 43 | for how to encode collection types, the convention of appending `[]` to the key for array 44 | values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested 45 | dictionary values (`foo[bar]=baz`). 46 | 47 | - `URLEncodedInURL`: Creates query string to be set as or appended to any existing URL query. Uses the same 48 | implementation as the `.URL` case, but always applies the encoded result to the URL. 49 | 50 | - `JSON`: Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is 51 | set as the body of the request. The `Content-Type` HTTP header field of an encoded request is 52 | set to `application/json`. 53 | 54 | - `PropertyList`: Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, 55 | according to the associated format and write options values, which is set as the body of the 56 | request. The `Content-Type` HTTP header field of an encoded request is set to 57 | `application/x-plist`. 58 | 59 | - `Custom`: Uses the associated closure value to construct a new request given an existing request and 60 | parameters. 61 | */ 62 | public enum ParameterEncoding { 63 | case URL 64 | case URLEncodedInURL 65 | case JSON 66 | case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions) 67 | case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?)) 68 | 69 | /** 70 | Creates a URL request by encoding parameters and applying them onto an existing request. 71 | 72 | - parameter URLRequest: The request to have parameters applied 73 | - parameter parameters: The parameters to apply 74 | 75 | - returns: A tuple containing the constructed request and the error that occurred during parameter encoding, 76 | if any. 77 | */ 78 | public func encode( 79 | URLRequest: URLRequestConvertible, 80 | parameters: [String: AnyObject]?) 81 | -> (NSMutableURLRequest, NSError?) 82 | { 83 | var mutableURLRequest = URLRequest.URLRequest 84 | 85 | guard let parameters = parameters else { 86 | return (mutableURLRequest, nil) 87 | } 88 | 89 | var encodingError: NSError? = nil 90 | 91 | switch self { 92 | case .URL, .URLEncodedInURL: 93 | func query(parameters: [String: AnyObject]) -> String { 94 | var components: [(String, String)] = [] 95 | for key in Array(parameters.keys).sort(<) { 96 | let value = parameters[key]! 97 | components += queryComponents(key, value) 98 | } 99 | 100 | return (components.map { "\($0)=\($1)" } as [String]).joinWithSeparator("&") 101 | } 102 | 103 | func encodesParametersInURL(method: Method) -> Bool { 104 | switch self { 105 | case .URLEncodedInURL: 106 | return true 107 | default: 108 | break 109 | } 110 | 111 | switch method { 112 | case .GET, .HEAD, .DELETE: 113 | return true 114 | default: 115 | return false 116 | } 117 | } 118 | 119 | if let method = Method(rawValue: mutableURLRequest.HTTPMethod) where encodesParametersInURL(method) { 120 | if let URLComponents = NSURLComponents(URL: mutableURLRequest.URL!, resolvingAgainstBaseURL: false) { 121 | let percentEncodedQuery = (URLComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) 122 | URLComponents.percentEncodedQuery = percentEncodedQuery 123 | mutableURLRequest.URL = URLComponents.URL 124 | } 125 | } else { 126 | if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil { 127 | mutableURLRequest.setValue( 128 | "application/x-www-form-urlencoded; charset=utf-8", 129 | forHTTPHeaderField: "Content-Type" 130 | ) 131 | } 132 | 133 | mutableURLRequest.HTTPBody = query(parameters).dataUsingEncoding( 134 | NSUTF8StringEncoding, 135 | allowLossyConversion: false 136 | ) 137 | } 138 | case .JSON: 139 | do { 140 | let options = NSJSONWritingOptions() 141 | let data = try NSJSONSerialization.dataWithJSONObject(parameters, options: options) 142 | 143 | mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 144 | mutableURLRequest.HTTPBody = data 145 | } catch { 146 | encodingError = error as NSError 147 | } 148 | case .PropertyList(let format, let options): 149 | do { 150 | let data = try NSPropertyListSerialization.dataWithPropertyList( 151 | parameters, 152 | format: format, 153 | options: options 154 | ) 155 | mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 156 | mutableURLRequest.HTTPBody = data 157 | } catch { 158 | encodingError = error as NSError 159 | } 160 | case .Custom(let closure): 161 | (mutableURLRequest, encodingError) = closure(mutableURLRequest, parameters) 162 | } 163 | 164 | return (mutableURLRequest, encodingError) 165 | } 166 | 167 | /** 168 | Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. 169 | 170 | - parameter key: The key of the query component. 171 | - parameter value: The value of the query component. 172 | 173 | - returns: The percent-escaped, URL encoded query string components. 174 | */ 175 | public func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] { 176 | var components: [(String, String)] = [] 177 | if let dictionary = value as? [String: AnyObject] { 178 | for (nestedKey, value) in dictionary { 179 | components += queryComponents("\(key)[\(nestedKey)]", value) 180 | } 181 | } else if let array = value as? [AnyObject] { 182 | for value in array { 183 | components += queryComponents("\(key)[]", value) 184 | } 185 | } else { 186 | components.append((escape(key), escape("\(value)"))) 187 | } 188 | 189 | return components 190 | } 191 | 192 | /** 193 | Returns a percent-escaped string following RFC 3986 for a query string key or value. 194 | 195 | RFC 3986 states that the following characters are "reserved" characters. 196 | 197 | - General Delimiters: ":", "#", "[", "]", "@", "?", "/" 198 | - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" 199 | 200 | In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow 201 | query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" 202 | should be percent-escaped in the query string. 203 | 204 | - parameter string: The string to be percent-escaped. 205 | 206 | - returns: The percent-escaped string. 207 | */ 208 | public func escape(string: String) -> String { 209 | let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 210 | let subDelimitersToEncode = "!$&'()*+,;=" 211 | 212 | let allowedCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet 213 | allowedCharacterSet.removeCharactersInString(generalDelimitersToEncode + subDelimitersToEncode) 214 | 215 | return string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? "" 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // Result.swift 2 | // 3 | // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | /** 26 | Used to represent whether a request was successful or encountered an error. 27 | 28 | - Success: The request and all post processing operations were successful resulting in the serialization of the 29 | provided associated value. 30 | - Failure: The request encountered an error resulting in a failure. The associated values are the original data 31 | provided by the server as well as the error that caused the failure. 32 | */ 33 | public enum Result { 34 | case Success(Value) 35 | case Failure(NSData?, ErrorType) 36 | 37 | /// Returns `true` if the result is a success, `false` otherwise. 38 | public var isSuccess: Bool { 39 | switch self { 40 | case .Success: 41 | return true 42 | case .Failure: 43 | return false 44 | } 45 | } 46 | 47 | /// Returns `true` if the result is a failure, `false` otherwise. 48 | public var isFailure: Bool { 49 | return !isSuccess 50 | } 51 | 52 | /// Returns the associated value if the result is a success, `nil` otherwise. 53 | public var value: Value? { 54 | switch self { 55 | case .Success(let value): 56 | return value 57 | case .Failure: 58 | return nil 59 | } 60 | } 61 | 62 | /// Returns the associated data value if the result is a failure, `nil` otherwise. 63 | public var data: NSData? { 64 | switch self { 65 | case .Success: 66 | return nil 67 | case .Failure(let data, _): 68 | return data 69 | } 70 | } 71 | 72 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 73 | public var error: ErrorType? { 74 | switch self { 75 | case .Success: 76 | return nil 77 | case .Failure(_, let error): 78 | return error 79 | } 80 | } 81 | } 82 | 83 | // MARK: - CustomStringConvertible 84 | 85 | extension Result: CustomStringConvertible { 86 | public var description: String { 87 | switch self { 88 | case .Success: 89 | return "SUCCESS" 90 | case .Failure: 91 | return "FAILURE" 92 | } 93 | } 94 | } 95 | 96 | // MARK: - CustomDebugStringConvertible 97 | 98 | extension Result: CustomDebugStringConvertible { 99 | public var debugDescription: String { 100 | switch self { 101 | case .Success(let value): 102 | return "SUCCESS: \(value)" 103 | case .Failure(let data, let error): 104 | if let 105 | data = data, 106 | utf8Data = NSString(data: data, encoding: NSUTF8StringEncoding) 107 | { 108 | return "FAILURE: \(error) \(utf8Data)" 109 | } else { 110 | return "FAILURE with Error: \(error)" 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Stream.swift: -------------------------------------------------------------------------------- 1 | // Stream.swift 2 | // 3 | // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | #if !os(watchOS) 26 | 27 | @available(iOS 9.0, OSX 10.11, *) 28 | extension Manager { 29 | private enum Streamable { 30 | case Stream(String, Int) 31 | case NetService(NSNetService) 32 | } 33 | 34 | private func stream(streamable: Streamable) -> Request { 35 | var streamTask: NSURLSessionStreamTask! 36 | 37 | switch streamable { 38 | case .Stream(let hostName, let port): 39 | dispatch_sync(queue) { 40 | streamTask = self.session.streamTaskWithHostName(hostName, port: port) 41 | } 42 | case .NetService(let netService): 43 | dispatch_sync(queue) { 44 | streamTask = self.session.streamTaskWithNetService(netService) 45 | } 46 | } 47 | 48 | let request = Request(session: session, task: streamTask) 49 | 50 | delegate[request.delegate.task] = request.delegate 51 | 52 | if startRequestsImmediately { 53 | request.resume() 54 | } 55 | 56 | return request 57 | } 58 | 59 | /** 60 | Creates a request for bidirectional streaming with the given hostname and port. 61 | 62 | - parameter hostName: The hostname of the server to connect to. 63 | - parameter port: The port of the server to connect to. 64 | 65 | :returns: The created stream request. 66 | */ 67 | public func stream(hostName hostName: String, port: Int) -> Request { 68 | return stream(.Stream(hostName, port)) 69 | } 70 | 71 | /** 72 | Creates a request for bidirectional streaming with the given `NSNetService`. 73 | 74 | - parameter netService: The net service used to identify the endpoint. 75 | 76 | - returns: The created stream request. 77 | */ 78 | public func stream(netService netService: NSNetService) -> Request { 79 | return stream(.NetService(netService)) 80 | } 81 | } 82 | 83 | // MARK: - 84 | 85 | @available(iOS 9.0, OSX 10.11, *) 86 | extension Manager.SessionDelegate: NSURLSessionStreamDelegate { 87 | 88 | // MARK: Override Closures 89 | 90 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:readClosedForStreamTask:`. 91 | public var streamTaskReadClosed: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 92 | get { 93 | return _streamTaskReadClosed as? (NSURLSession, NSURLSessionStreamTask) -> Void 94 | } 95 | set { 96 | _streamTaskReadClosed = newValue 97 | } 98 | } 99 | 100 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:writeClosedForStreamTask:`. 101 | public var streamTaskWriteClosed: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 102 | get { 103 | return _streamTaskWriteClosed as? (NSURLSession, NSURLSessionStreamTask) -> Void 104 | } 105 | set { 106 | _streamTaskWriteClosed = newValue 107 | } 108 | } 109 | 110 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:betterRouteDiscoveredForStreamTask:`. 111 | public var streamTaskBetterRouteDiscovered: ((NSURLSession, NSURLSessionStreamTask) -> Void)? { 112 | get { 113 | return _streamTaskBetterRouteDiscovered as? (NSURLSession, NSURLSessionStreamTask) -> Void 114 | } 115 | set { 116 | _streamTaskBetterRouteDiscovered = newValue 117 | } 118 | } 119 | 120 | /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:streamTask:didBecomeInputStream:outputStream:`. 121 | public var streamTaskDidBecomeInputStream: ((NSURLSession, NSURLSessionStreamTask, NSInputStream, NSOutputStream) -> Void)? { 122 | get { 123 | return _streamTaskDidBecomeInputStream as? (NSURLSession, NSURLSessionStreamTask, NSInputStream, NSOutputStream) -> Void 124 | } 125 | set { 126 | _streamTaskDidBecomeInputStream = newValue 127 | } 128 | } 129 | 130 | // MARK: Delegate Methods 131 | 132 | /** 133 | Tells the delegate that the read side of the connection has been closed. 134 | 135 | - parameter session: The session. 136 | - parameter streamTask: The stream task. 137 | */ 138 | public func URLSession(session: NSURLSession, readClosedForStreamTask streamTask: NSURLSessionStreamTask) { 139 | streamTaskReadClosed?(session, streamTask) 140 | } 141 | 142 | /** 143 | Tells the delegate that the write side of the connection has been closed. 144 | 145 | - parameter session: The session. 146 | - parameter streamTask: The stream task. 147 | */ 148 | public func URLSession(session: NSURLSession, writeClosedForStreamTask streamTask: NSURLSessionStreamTask) { 149 | streamTaskWriteClosed?(session, streamTask) 150 | } 151 | 152 | /** 153 | Tells the delegate that the system has determined that a better route to the host is available. 154 | 155 | - parameter session: The session. 156 | - parameter streamTask: The stream task. 157 | */ 158 | public func URLSession(session: NSURLSession, betterRouteDiscoveredForStreamTask streamTask: NSURLSessionStreamTask) { 159 | streamTaskBetterRouteDiscovered?(session, streamTask) 160 | } 161 | 162 | /** 163 | Tells the delegate that the stream task has been completed and provides the unopened stream objects. 164 | 165 | - parameter session: The session. 166 | - parameter streamTask: The stream task. 167 | - parameter inputStream: The new input stream. 168 | - parameter outputStream: The new output stream. 169 | */ 170 | public func URLSession( 171 | session: NSURLSession, 172 | streamTask: NSURLSessionStreamTask, 173 | didBecomeInputStream inputStream: NSInputStream, 174 | outputStream: NSOutputStream) 175 | { 176 | streamTaskDidBecomeInputStream?(session, streamTask, inputStream, outputStream) 177 | } 178 | } 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // Validation.swift 2 | // 3 | // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | extension Request { 26 | 27 | /** 28 | Used to represent whether validation was successful or encountered an error resulting in a failure. 29 | 30 | - Success: The validation was successful. 31 | - Failure: The validation failed encountering the provided error. 32 | */ 33 | public enum ValidationResult { 34 | case Success 35 | case Failure(ErrorType) 36 | } 37 | 38 | /** 39 | A closure used to validate a request that takes a URL request and URL response, and returns whether the 40 | request was valid. 41 | */ 42 | public typealias Validation = (NSURLRequest?, NSHTTPURLResponse) -> ValidationResult 43 | 44 | /** 45 | Validates the request, using the specified closure. 46 | 47 | If validation fails, subsequent calls to response handlers will have an associated error. 48 | 49 | - parameter validation: A closure to validate the request. 50 | 51 | - returns: The request. 52 | */ 53 | public func validate(validation: Validation) -> Self { 54 | delegate.queue.addOperationWithBlock { 55 | if let 56 | response = self.response where self.delegate.error == nil, 57 | case let .Failure(error) = validation(self.request, response) 58 | { 59 | self.delegate.error = error 60 | } 61 | } 62 | 63 | return self 64 | } 65 | 66 | // MARK: - Status Code 67 | 68 | /** 69 | Validates that the response has a status code in the specified range. 70 | 71 | If validation fails, subsequent calls to response handlers will have an associated error. 72 | 73 | - parameter range: The range of acceptable status codes. 74 | 75 | - returns: The request. 76 | */ 77 | public func validate(statusCode acceptableStatusCode: S) -> Self { 78 | return validate { _, response in 79 | if acceptableStatusCode.contains(response.statusCode) { 80 | return .Success 81 | } else { 82 | let failureReason = "Response status code was unacceptable: \(response.statusCode)" 83 | return .Failure(Error.errorWithCode(.StatusCodeValidationFailed, failureReason: failureReason)) 84 | } 85 | } 86 | } 87 | 88 | // MARK: - Content-Type 89 | 90 | private struct MIMEType { 91 | let type: String 92 | let subtype: String 93 | 94 | init?(_ string: String) { 95 | let components: [String] = { 96 | let stripped = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 97 | let split = stripped.substringToIndex(stripped.rangeOfString(";")?.endIndex ?? stripped.endIndex) 98 | return split.componentsSeparatedByString("/") 99 | }() 100 | 101 | if let 102 | type = components.first, 103 | subtype = components.last 104 | { 105 | self.type = type 106 | self.subtype = subtype 107 | } else { 108 | return nil 109 | } 110 | } 111 | 112 | func matches(MIME: MIMEType) -> Bool { 113 | switch (type, subtype) { 114 | case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"): 115 | return true 116 | default: 117 | return false 118 | } 119 | } 120 | } 121 | 122 | /** 123 | Validates that the response has a content type in the specified array. 124 | 125 | If validation fails, subsequent calls to response handlers will have an associated error. 126 | 127 | - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 128 | 129 | - returns: The request. 130 | */ 131 | public func validate(contentType acceptableContentTypes: S) -> Self { 132 | return validate { _, response in 133 | if let 134 | responseContentType = response.MIMEType, 135 | responseMIMEType = MIMEType(responseContentType) 136 | { 137 | for contentType in acceptableContentTypes { 138 | if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) { 139 | return .Success 140 | } 141 | } 142 | } else { 143 | for contentType in acceptableContentTypes { 144 | if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" { 145 | return .Success 146 | } 147 | } 148 | } 149 | 150 | let failureReason: String 151 | 152 | if let responseContentType = response.MIMEType { 153 | failureReason = ( 154 | "Response content type \"\(responseContentType)\" does not match any acceptable " + 155 | "content types: \(acceptableContentTypes)" 156 | ) 157 | } else { 158 | failureReason = "Response content type was missing and acceptable content type does not match \"*/*\"" 159 | } 160 | 161 | return .Failure(Error.errorWithCode(.ContentTypeValidationFailed, failureReason: failureReason)) 162 | } 163 | } 164 | 165 | // MARK: - Automatic 166 | 167 | /** 168 | Validates that the response has a status code in the default acceptable range of 200...299, and that the content 169 | type matches any specified in the Accept HTTP header field. 170 | 171 | If validation fails, subsequent calls to response handlers will have an associated error. 172 | 173 | - returns: The request. 174 | */ 175 | public func validate() -> Self { 176 | let acceptableStatusCodes: Range = 200..<300 177 | let acceptableContentTypes: [String] = { 178 | if let accept = request?.valueForHTTPHeaderField("Accept") { 179 | return accept.componentsSeparatedByString(",") 180 | } 181 | 182 | return ["*/*"] 183 | }() 184 | 185 | return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (2.0.1) 3 | 4 | DEPENDENCIES: 5 | - Alamofire (~> 2.0) 6 | 7 | SPEC CHECKSUMS: 8 | Alamofire: 1d8e208d616fbbfd2391b15eae766d07c96cdc49 9 | 10 | COCOAPODS: 0.38.2 11 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/wyd.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 47 | 48 | 54 | 55 | 57 | 58 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/wyd.xcuserdatad/xcschemes/Pods.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 47 | 48 | 54 | 55 | 57 | 58 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/wyd.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | C8DD129FAE7ABE16C030599127CBFC5A 21 | 22 | primary 23 | 24 | 25 | E9AD967CAA74F482799165449970707E 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Alamofire.xcconfig" 2 | CODE_SIGN_IDENTITY = 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Alamofire" "${PODS_ROOT}/Headers/Public" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_ROOT = ${SRCROOT} 7 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double AlamofireVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/Pods/Target Support Files/Alamofire/Alamofire.xcconfig -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - http://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | Alamofire 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - http://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | else 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | fi 16 | 17 | local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | 19 | if [ -L "${source}" ]; then 20 | echo "Symlinked..." 21 | source="$(readlink "${source}")" 22 | fi 23 | 24 | # use filter instead of exclude so missing patterns dont' throw errors 25 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 26 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 27 | 28 | # Resign the code if required by the build settings to avoid unstable apps 29 | code_sign_if_enabled "${destination}/$(basename "$1")" 30 | 31 | # Embed linked Swift runtime libraries 32 | local basename 33 | basename="$(basename "$1" | sed -E s/\\..+// && exit ${PIPESTATUS[0]})" 34 | local swift_runtime_libs 35 | swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/${basename}.framework/${basename}" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 36 | for lib in $swift_runtime_libs; do 37 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 38 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 39 | code_sign_if_enabled "${destination}/${lib}" 40 | done 41 | } 42 | 43 | # Signs a framework with the provided identity 44 | code_sign_if_enabled() { 45 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 46 | # Use the current code_sign_identitiy 47 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 48 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" 49 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" 50 | fi 51 | } 52 | 53 | 54 | if [[ "$CONFIGURATION" == "Debug" ]]; then 55 | install_framework 'Pods/Alamofire.framework' 56 | fi 57 | if [[ "$CONFIGURATION" == "Release" ]]; then 58 | install_framework 'Pods/Alamofire.framework' 59 | fi 60 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | realpath() { 12 | DIRECTORY="$(cd "${1%/*}" && pwd)" 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | 61 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 62 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 63 | if [[ "${ACTION}" == "install" ]]; then 64 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 65 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 66 | fi 67 | rm -f "$RESOURCES_TO_COPY" 68 | 69 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 70 | then 71 | case "${TARGETED_DEVICE_FAMILY}" in 72 | 1,2) 73 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 74 | ;; 75 | 1) 76 | TARGET_DEVICE_ARGS="--target-device iphone" 77 | ;; 78 | 2) 79 | TARGET_DEVICE_ARGS="--target-device ipad" 80 | ;; 81 | *) 82 | TARGET_DEVICE_ARGS="--target-device mac" 83 | ;; 84 | esac 85 | 86 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 87 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 88 | while read line; do 89 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 90 | XCASSET_FILES+=("$line") 91 | fi 92 | done <<<"$OTHER_XCASSETS" 93 | 94 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 95 | fi 96 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double PodsVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char PodsVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CODE_SIGN_IDENTITY = 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Alamofire.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods { 2 | umbrella header "Pods-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods.release.xcconfig: -------------------------------------------------------------------------------- 1 | CODE_SIGN_IDENTITY = 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Alamofire.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods 8 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XGuardian 2 | XARA Security Scanner for OSX with features: 3 | 4 | - URL scheme hijack check 5 | - Bundle ID hijack check 6 | - keychain hijack check 7 | 8 | ## Application Download 9 | You can download release from: 10 | 11 | [github release](https://github.com/openscanner/XGuardian/releases) 12 | 13 | OR 14 | 15 | [openscanner](http://xara.openscanner.cc/) 16 | 17 | ## Issue Report 18 | You can create issue at [github](https://github.com/openscanner/XGuardian/issues), or send email to wuyd@openscanner.cc 19 | 20 | ## Interpreting Results 21 | When a potential vulnerability is found, it will appear highlighted in the scan results. You can use the magnifying glass icon to open the file in Finder and the delete button to remove it's authority. 22 | 23 | ## About US 24 | We are an newly company focus on mobile secrutiy. This software is my first app at apple's platform, so if have any issue, please let me known. 25 | 26 | Our Homepage: www.openscanner.cc 27 | My email: wuyd@openscanner.cc 28 | 29 | ## License 30 | MIT License 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | -------------------------------------------------------------------------------- /XGuardian.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XGuardian.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /XGuardian/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/6/29. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | 13 | private let updateTimerInterval = 21600.0 14 | 15 | @NSApplicationMain 16 | class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate{ 17 | 18 | @IBOutlet weak var window: NSWindow! 19 | 20 | /** 21 | menu for update check 22 | 23 | - parameter sender: update menu 24 | */ 25 | @IBAction func Update(sender: AnyObject) { 26 | XGBackend.cleanLastedverion() // manual check 27 | XGBackend.updateLastedverion() 28 | } 29 | 30 | func applicationDidFinishLaunching(aNotification: NSNotification) { 31 | 32 | XGURLSchemeManager.sharedInstance.scan() 33 | 34 | //bundle ID hijack check 35 | let applicationMgr = XGContainerApplicationManager.sharedInstance 36 | applicationMgr.startMoniter() 37 | 38 | //start observe thread 39 | XGKeychainObserver.startObserve() 40 | self.loadViews() 41 | 42 | //check update 43 | //TODO:read timeinterval form plist add update check timer 44 | NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("processUpdateCheck:"), userInfo: nil, repeats: false) 45 | } 46 | 47 | func applicationWillTerminate(aNotification: NSNotification) { 48 | // Insert code here to tear down your application 49 | 50 | // stop observe thread 51 | XGKeychainObserver.stopObserve() 52 | } 53 | 54 | func processUpdateCheck(timer : NSTimer ) { 55 | NSTimer.scheduledTimerWithTimeInterval(updateTimerInterval, target: self, selector: Selector("processUpdateCheck:"), userInfo: nil, repeats: false) 56 | XGBackend.updateLastedverion() 57 | } 58 | 59 | func windowShouldClose(sender: AnyObject) -> Bool { 60 | NSApplication.sharedApplication().hide(sender) 61 | return false 62 | } 63 | 64 | 65 | 66 | func loadViews() { 67 | 68 | self.window.titlebarAppearsTransparent = true 69 | self.window.movableByWindowBackground = true 70 | self.window.titleVisibility = NSWindowTitleVisibility.Hidden 71 | self.window.styleMask |= NSFullSizeContentViewWindowMask; 72 | 73 | 74 | } 75 | 76 | 77 | 78 | 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /XGuardian/CXKeychainHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // CXKeychainHelper.h 3 | // KeychainSwiftAPI 4 | // 5 | // Created by Denis Krivitski on 26/11/14. 6 | // Copyright (c) 2014 Checkmarx. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | 12 | @interface CXResultWithStatus : NSObject 13 | @property (assign,nonatomic) OSStatus status; 14 | @property (strong,nonatomic) NSObject* result; 15 | @end 16 | 17 | @interface CXALCContent: NSObject 18 | @property (assign,nonatomic) OSStatus status; 19 | @property (strong,nonatomic) NSArray* applicationList; 20 | @property (strong,nonatomic) NSString* desc; 21 | @property (assign,nonatomic) SecKeychainPromptSelector promptSelector; 22 | @end 23 | 24 | @interface CXKeychainHelper : NSObject 25 | +(CXResultWithStatus*)secItemCopyMatchingCaller:(NSDictionary*)query; 26 | +(CXResultWithStatus*)secItemAddCaller:(NSDictionary*)query; 27 | 28 | // 29 | +(CXALCContent*)secACLCopyContents:(SecACLRef) acl; 30 | 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /XGuardian/CXKeychainHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // CXKeychainHelper.m 3 | // KeychainSwiftAPI 4 | // 5 | // Created by Denis Krivitski on 26/11/14. 6 | // Copyright (c) 2014 Checkmarx. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import "CXKeychainHelper.h" 12 | 13 | 14 | @implementation CXResultWithStatus 15 | @end 16 | 17 | @implementation CXALCContent 18 | @end 19 | 20 | @implementation CXKeychainHelper 21 | 22 | +(CXResultWithStatus*)secItemCopyMatchingCaller:(NSDictionary*)query 23 | { 24 | CXResultWithStatus* resultWithStatus = [[CXResultWithStatus alloc] init]; 25 | CFTypeRef result = nil; 26 | 27 | resultWithStatus.status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &result); 28 | if (result != nil) { 29 | resultWithStatus.result = CFBridgingRelease(result); 30 | } 31 | 32 | return resultWithStatus; 33 | } 34 | 35 | +(CXResultWithStatus*)secItemAddCaller:(NSDictionary*)query 36 | { 37 | CXResultWithStatus* resultWithStatus = [[CXResultWithStatus alloc] init]; 38 | CFTypeRef result = nil; 39 | 40 | resultWithStatus.status = SecItemAdd((__bridge CFDictionaryRef)(query), &result); 41 | if (result != nil) { 42 | resultWithStatus.result = CFBridgingRelease(result); 43 | } 44 | 45 | return resultWithStatus; 46 | } 47 | 48 | 49 | 50 | +(CXALCContent*)secACLCopyContents:(SecACLRef) acl{ 51 | CXALCContent* alcContent = [[CXALCContent alloc] init]; 52 | 53 | CFArrayRef applicationList = nil; 54 | CFStringRef description = nil; 55 | SecKeychainPromptSelector promptSelector = 0; 56 | 57 | alcContent.status = SecACLCopyContents(acl, &applicationList, &description, &promptSelector); 58 | 59 | if (applicationList != nil) { 60 | alcContent.applicationList = CFBridgingRelease(applicationList); 61 | }else { 62 | alcContent.applicationList = nil; 63 | } 64 | 65 | if (description != nil) { 66 | alcContent.desc = CFBridgingRelease(description); 67 | } else { 68 | alcContent.desc = nil; 69 | } 70 | 71 | alcContent.promptSelector = promptSelector; 72 | 73 | return alcContent; 74 | 75 | } 76 | 77 | 78 | 79 | 80 | @end 81 | 82 | 83 | -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AllscanIcon.imageset/2-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AllscanIcon.imageset/2-16.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AllscanIcon.imageset/2-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AllscanIcon.imageset/2-32.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AllscanIcon.imageset/2-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AllscanIcon.imageset/2-64.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AllscanIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "2-16.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "2-32.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "2-64.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/256-1.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/32-1.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/512-1.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "32-1.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "256-1.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "512-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/BudleIDIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "厂商16.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "厂商32.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "厂商64.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/BudleIDIcon.imageset/厂商16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/BudleIDIcon.imageset/厂商16.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/BudleIDIcon.imageset/厂商32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/BudleIDIcon.imageset/厂商32.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/BudleIDIcon.imageset/厂商64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/BudleIDIcon.imageset/厂商64.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/KeychainIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "钥匙16.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "钥匙32.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "钥匙64.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/KeychainIcon.imageset/钥匙16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/KeychainIcon.imageset/钥匙16.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/KeychainIcon.imageset/钥匙32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/KeychainIcon.imageset/钥匙32.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/KeychainIcon.imageset/钥匙64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/KeychainIcon.imageset/钥匙64.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/Net.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "网.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/Net.imageset/网.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/Net.imageset/网.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/NetforgImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "右侧图240-240.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "右侧图480-480.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/NetforgImage.imageset/右侧图240-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/NetforgImage.imageset/右侧图240-240.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/NetforgImage.imageset/右侧图480-480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/NetforgImage.imageset/右侧图480-480.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/UrlschemlIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "门16.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "门32.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "门64.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/UrlschemlIcon.imageset/门16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/UrlschemlIcon.imageset/门16.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/UrlschemlIcon.imageset/门32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/UrlschemlIcon.imageset/门32.png -------------------------------------------------------------------------------- /XGuardian/Images.xcassets/UrlschemlIcon.imageset/门64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openscanner/XGuardian/b520a6896006c09aa0678671ea8a82f6c2f82692/XGuardian/Images.xcassets/UrlschemlIcon.imageset/门64.png -------------------------------------------------------------------------------- /XGuardian/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.utilities 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | NSHumanReadableCopyright 30 | Copyright © 2015年 杭州网蛙科技. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /XGuardian/UpdateWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 40 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /XGuardian/XGAllThreatsDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGAllThreatsDelegate.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class XGAllThreatsDelegate:NSObject, XGThreatsViewDelegate { 13 | 14 | static let sharedInstance = XGAllThreatsDelegate() 15 | static func getInstance() -> XGThreatsViewDelegate { 16 | return sharedInstance 17 | } 18 | 19 | let delegateArray : [XGThreatsViewDelegate] = [ 20 | XGKeychainThreatsDelegate.getInstance(), 21 | XGBundleIDThreatsDelegate.getInstance(), 22 | XGURLSchemeThreatsDelegate.getInstance() ] 23 | 24 | var topArray = [NSString]() 25 | override init() { 26 | super.init() 27 | 28 | for delegate in self.delegateArray { 29 | self.topArray.append(delegate.title as NSString) 30 | } 31 | } 32 | 33 | //MARK: threats view delegate 34 | var title : String { get { 35 | return "All Scan" 36 | }} 37 | 38 | func addNotificationObserver() { 39 | for delegate in self.delegateArray { 40 | delegate.addNotificationObserver?() 41 | } 42 | } 43 | 44 | func removeNotificationObserver() { 45 | for delegate in self.delegateArray { 46 | delegate.removeNotificationObserver?() 47 | } 48 | } 49 | 50 | func refreshThreatsData() -> Int { 51 | var numberOfThreats = 0 52 | for delegate in self.delegateArray { 53 | numberOfThreats += delegate.refreshThreatsData() 54 | } 55 | return numberOfThreats 56 | } 57 | 58 | func isExpandable(item: AnyObject?) -> Bool { 59 | if let title = item as? NSString { 60 | if self.topArray.contains(title) { 61 | return true 62 | } 63 | } 64 | return false 65 | } 66 | 67 | func isSelectable(item: AnyObject?) -> Bool { 68 | if let title = item as? NSString { 69 | if self.topArray.contains(title) { 70 | return false 71 | } 72 | } 73 | return true 74 | } 75 | 76 | func childrenForItem(item: AnyObject?) -> [AnyObject]? { 77 | 78 | if nil == item { 79 | return self.topArray 80 | } 81 | 82 | if let key = item as? NSString { 83 | for delegate in self.delegateArray { 84 | if key.isEqualToString(delegate.title) { 85 | return delegate.childrenForItem(nil) 86 | } 87 | } 88 | } 89 | return nil 90 | } 91 | 92 | func setCellView(cellView : NSTableCellView, item: AnyObject, parent : AnyObject? ) { 93 | if let key = parent as? NSString { 94 | for delegate in self.delegateArray { 95 | if key.isEqualToString(delegate.title) { 96 | delegate.setCellView(cellView, item: item, parent: parent) 97 | return 98 | } 99 | } 100 | } 101 | if let key = item as? NSString { 102 | for delegate in self.delegateArray { 103 | if key.isEqualToString(delegate.title) { 104 | cellView.textField?.objectValue = key 105 | return 106 | } 107 | } 108 | } 109 | } 110 | 111 | var threatsNumber : Int { get { 112 | var numberOfThreats = 0 113 | for delegate in self.delegateArray { 114 | numberOfThreats += delegate.threatsNumber 115 | } 116 | return numberOfThreats 117 | } } 118 | 119 | func detailsView( threatsViewController : XGThreatsViewController, item : AnyObject?, parent : AnyObject? ) -> NSView? { 120 | if let key = parent as? NSString { 121 | for delegate in self.delegateArray { 122 | if key.isEqualToString(delegate.title) { 123 | return delegate.detailsView(threatsViewController, item: item, parent: parent) 124 | } 125 | } 126 | } 127 | return nil 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /XGuardian/XGBackend.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGBackend.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/1. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import Alamofire 11 | 12 | 13 | private let lastedVersionURL = "http://xara.openscanner.cc/version/getLastversion" 14 | 15 | 16 | public protocol ResponseObjectSerializable { 17 | init?(response: NSHTTPURLResponse, representation: AnyObject) 18 | } 19 | 20 | extension Request { 21 | public func responseObject(completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result) -> Void) -> Self { 22 | let responseSerializer = GenericResponseSerializer { request, response, data in 23 | let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments) 24 | let result = JSONResponseSerializer.serializeResponse(request, response, data) 25 | 26 | switch result { 27 | case .Success(let value): 28 | if let 29 | response = response, 30 | responseObject = T(response: response, representation: value) 31 | { 32 | return .Success(responseObject) 33 | } else { 34 | let failureReason = "JSON could not be serialized into response object: \(value)" 35 | let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason) 36 | return .Failure(data, error) 37 | } 38 | case .Failure(let data, let error): 39 | return .Failure(data, error) 40 | } 41 | } 42 | 43 | return response(responseSerializer: responseSerializer, completionHandler: completionHandler) 44 | } 45 | } 46 | 47 | 48 | final class XGVersionInfo: NSObject, ResponseObjectSerializable { 49 | let version: String? 50 | let url: String? 51 | let changeLog : String? 52 | let md5: String? 53 | 54 | @objc required init?(response: NSHTTPURLResponse,representation: AnyObject) { 55 | self.version = representation.valueForKeyPath("version") as? String 56 | self.changeLog = representation.valueForKeyPath("changeLog") as? String 57 | self.md5 = representation.valueForKeyPath("md5") as? String 58 | self.url = representation.valueForKeyPath("versionURL") as? String 59 | } 60 | 61 | override var description : String { 62 | var descStr = "" 63 | if(self.version != nil) { descStr += "version: \(self.version!) \n" } 64 | if(self.changeLog != nil) { descStr += "Class: \(self.changeLog!) \n" } 65 | if(self.url != nil) {descStr += "downloadURL: \(self.url!) \n"} 66 | if(self.md5 != nil) { descStr += "md5: \(self.md5!) \n" } 67 | return descStr 68 | } 69 | } 70 | 71 | private var lastedVersion : XGVersionInfo? 72 | 73 | class XGBackend: NSObject { 74 | 75 | // var httpManager = AFHTTPRequestOperationManager.manager() 76 | class func currentVersion() -> String { 77 | let infoDict = NSBundle.mainBundle().infoDictionary 78 | let version = infoDict!["CFBundleShortVersionString"] as! String! 79 | return version 80 | } 81 | 82 | class func getLastedverion() -> XGVersionInfo? { 83 | return lastedVersion 84 | } 85 | 86 | class func cleanLastedverion() { 87 | lastedVersion = nil 88 | } 89 | 90 | class func updateLastedverion() { 91 | _ = Alamofire.request(.GET, lastedVersionURL) 92 | .responseObject { (_, _, versionInfo: Result) in 93 | XGBackend.update(versionInfo.value) 94 | } 95 | return 96 | } 97 | 98 | class func update(versionInfo:XGVersionInfo?) { 99 | if let version = versionInfo { 100 | 101 | let currentVersion = XGBackend.currentVersion() 102 | NSLog("currentVersion:\(currentVersion)") 103 | 104 | //don't update 105 | if (nil != lastedVersion) && (version.version == lastedVersion!.version) { 106 | return 107 | } 108 | //check current version 109 | if (version.version == currentVersion) { 110 | return 111 | } 112 | lastedVersion = version 113 | print(versionInfo) 114 | 115 | XGUpdatePanel.panelShow() 116 | 117 | } 118 | } 119 | 120 | class func openURL(urlStr : String! ) { 121 | 122 | let url = NSURL(string: urlStr)! 123 | let cfUrl = url as CFURL; 124 | 125 | LSOpenCFURLRef(cfUrl,nil); 126 | return 127 | } 128 | 129 | 130 | class func downloadLastedVersion() { 131 | 132 | if lastedVersion != nil && lastedVersion?.url != nil { 133 | openURL(lastedVersion!.url) 134 | 135 | } 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /XGuardian/XGBundleDetailsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGBundleDetailsView.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/17. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGBundleDetailsView: NSView, NSTableViewDelegate, NSTableViewDataSource{ 12 | 13 | @IBOutlet weak var appImage: NSImageView! 14 | @IBOutlet weak var appName: NSTextField! 15 | @IBOutlet weak var appFullPath: NSTextField! 16 | @IBOutlet weak var appBundleID: NSTextField! 17 | @IBOutlet weak var bundleIDHijiackTableView: NSTableView! 18 | 19 | weak var bundleHijackItem : XGBundleHijackItem? 20 | weak var upperViewController : XGThreatsViewController? 21 | 22 | override func drawRect(dirtyRect: NSRect) { 23 | super.drawRect(dirtyRect) 24 | 25 | // Drawing code here. 26 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 27 | bezierPath.lineWidth = 1.0 28 | NSColor.whiteColor().set() 29 | bezierPath.fill() 30 | } 31 | 32 | override func viewDidMoveToWindow() { 33 | super.viewDidMoveToWindow() 34 | 35 | if let hijiackedApp = self.bundleHijackItem?.application { 36 | self.appImage.objectValue = NSWorkspace.sharedWorkspace().iconForFile(hijiackedApp.fullPath) 37 | self.appName.objectValue = (hijiackedApp.fullPath as NSString).lastPathComponent 38 | self.appFullPath.objectValue = hijiackedApp.fullPath 39 | self.appBundleID.objectValue = hijiackedApp.bundleID 40 | } 41 | 42 | self.bundleIDHijiackTableView.setDelegate(self) 43 | self.bundleIDHijiackTableView.setDataSource(self) 44 | self.bundleIDHijiackTableView.headerView = nil 45 | self.bundleIDHijiackTableView.reloadData() 46 | 47 | //seleted first row 48 | self.bundleIDHijiackTableView.selectRowIndexes(NSIndexSet(index: 0), byExtendingSelection: false) 49 | } 50 | 51 | 52 | func numberOfRowsInTableView(tableView: NSTableView) -> Int { 53 | if let applist = self.bundleHijackItem?.hijackApplications { 54 | return applist.count 55 | } 56 | return 0; 57 | } 58 | 59 | 60 | func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { 61 | 62 | if nil == self.bundleHijackItem { 63 | return nil 64 | } 65 | let appList = self.bundleHijackItem?.hijackApplications 66 | let appPath = appList![row].fullPath 67 | 68 | if let identifier = tableColumn?.identifier { 69 | if let result = tableView.makeViewWithIdentifier(identifier, owner: self) as? NSTableCellView { 70 | result.textField?.objectValue = (appPath as NSString).lastPathComponent 71 | result.imageView?.objectValue = NSWorkspace.sharedWorkspace().iconForFile(appPath) 72 | result.imageView?.sizeToFit() 73 | 74 | return result 75 | } 76 | } 77 | 78 | return nil; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /XGuardian/XGBundleIDThreatsDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGBundleIDThreatsDelegate.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class XGBundleIDThreatsDelegate:NSObject, XGThreatsViewDelegate { 13 | 14 | static let sharedInstance = XGBundleIDThreatsDelegate() 15 | static func getInstance() -> XGThreatsViewDelegate { 16 | return sharedInstance 17 | } 18 | 19 | private var bundleItemArray : [XGBundleHijackItem]? 20 | 21 | //MARK: threats view delegate 22 | var title : String { get { 23 | return "BundleID Hijack" 24 | }} 25 | 26 | private var isObserving = false 27 | func addNotificationObserver() { 28 | if !self.isObserving { 29 | self.isObserving = true 30 | NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("threatsDidChanged:"), name: "XGBundleIDThreadsChangeNotification", object: nil) 31 | } 32 | } 33 | 34 | func removeNotificationObserver() { 35 | if self.isObserving { 36 | NSNotificationCenter.defaultCenter().removeObserver(self, name: "XGBundleIDThreadsChangeNotification", object: nil) 37 | self.isObserving = false 38 | } 39 | } 40 | 41 | func refreshThreatsData() -> Int { 42 | self.bundleItemArray = XGContainerApplicationManager.sharedInstance.hijackedApplicationArray 43 | if (nil == self.bundleItemArray) { 44 | return 0 45 | } 46 | 47 | return self.bundleItemArray!.count 48 | } 49 | 50 | func isExpandable(item: AnyObject?) -> Bool { 51 | return false 52 | } 53 | 54 | func childrenForItem(item: AnyObject?) -> [AnyObject]? { 55 | if nil != item { 56 | return nil 57 | } 58 | return bundleItemArray 59 | } 60 | 61 | func setCellView(cellView : NSTableCellView, item: AnyObject , parent : AnyObject? ) { 62 | let bundleItem = item as! XGBundleHijackItem 63 | cellView.textField?.objectValue = bundleItem.application.bundleID 64 | cellView.imageView?.objectValue = NSWorkspace.sharedWorkspace().iconForFile(bundleItem.application.fullPath) 65 | } 66 | 67 | var threatsNumber : Int { get { 68 | if let array = self.bundleItemArray { 69 | return array.count 70 | } 71 | return 0 72 | } } 73 | 74 | func detailsView( threatsViewController : XGThreatsViewController, item : AnyObject?, parent : AnyObject? ) -> NSView? { 75 | if let bundleItem = item as? XGBundleHijackItem { 76 | let currentdetailViewController = NSViewController(nibName: "XGBundleDetailsView", bundle: nil) 77 | 78 | if let view = currentdetailViewController?.view as? XGBundleDetailsView{ 79 | view.bundleHijackItem = bundleItem 80 | view.upperViewController = threatsViewController 81 | return view 82 | } 83 | return nil 84 | } 85 | return nil 86 | } 87 | 88 | func threatsDidChanged(notification: NSNotification) { 89 | //println("threatsDidChanged") 90 | dispatch_async(dispatch_get_main_queue(), { 91 | // DO SOMETHING ON THE MAINTHREAD 92 | self.refreshThreatsData() 93 | NSNotificationCenter.defaultCenter().postNotificationName("XGThreadsChangedNotification", object: notification.object) 94 | }) 95 | 96 | 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /XGuardian/XGContainerApplicationManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGContainerApplicationManager.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/16. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | let XGApplicationPath = "/Applications" 13 | let XGApplicationExtensions = [".app", ".xpc"] 14 | 15 | 16 | class XGBundleHijackItem : NSObject { 17 | var application : XGThirdMacApplicationInfo 18 | var hijackApplications = [XGThirdMacApplicationInfo]() 19 | 20 | init( application : XGThirdMacApplicationInfo ){ 21 | self.application = application 22 | super.init() 23 | } 24 | } 25 | 26 | @objc(XGContainerApplicationManager) 27 | class XGContainerApplicationManager: NSObject { 28 | 29 | //single 30 | @objc(sharedInstance) 31 | static let sharedInstance = XGContainerApplicationManager() 32 | 33 | var applications = [XGThirdMacApplicationInfo]() 34 | var hijackedApplicationArray = [XGBundleHijackItem]() 35 | private var scanProcess = 0.0 36 | 37 | private func savApplication( url : NSURL, subApps : [NSURL]?) { 38 | if let app = XGThirdMacApplicationInfo(fullPath: url.path!) { 39 | self.applications.append(app) 40 | 41 | if let subAppsURL = subApps { 42 | for subAppURL in subAppsURL { 43 | if let subApp = XGThirdMacApplicationInfo(fullPath: subAppURL.path!) { 44 | app.addSubApp(subApp) 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | private func cleanApplications() { 52 | self.applications.removeAll(keepCapacity: true) 53 | } 54 | 55 | private func isApple(url : NSURL ) -> Bool { 56 | if let bundleID = XGUtilize.getBundleIDFromURL(url) { 57 | if bundleID.hasPrefix("com.apple") { 58 | return true 59 | } 60 | } 61 | return false 62 | } 63 | 64 | 65 | 66 | private func getAllThirdApplications() { 67 | 68 | //find all applications in the /Appplication 69 | let applicationsURLAarry = XGUtilize.getApplications(NSSearchPathDomainMask.SystemDomainMask) 70 | 71 | 72 | //find sub application 73 | for appURL in applicationsURLAarry { 74 | //com.apple no need check 75 | //remove apple's application 76 | if self.isApple(appURL) { 77 | continue 78 | } 79 | let subAppURLArray = XGUtilize.getSubApplications(appURL) 80 | self.savApplication(appURL , subApps : subAppURLArray) 81 | } 82 | } 83 | 84 | private func checkBundleIDHijcak() { 85 | var appBundleDict = [String : XGThirdMacApplicationInfo]() 86 | for appInfo in self.applications { 87 | appBundleDict[appInfo.bundleID] = appInfo 88 | } 89 | 90 | for appInfo in self.applications { 91 | if let subApplications = appInfo.subApplications { 92 | for sub_app in subApplications { 93 | if let appHijacked = appBundleDict[sub_app.bundleID]{ 94 | if appInfo != appHijacked { 95 | let appHijackedItem = XGBundleHijackItem(application: appHijacked) 96 | appHijackedItem.hijackApplications.append(sub_app) 97 | self.hijackedApplicationArray.append(appHijackedItem) 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | 106 | private func backendScan() { 107 | //clean 108 | self.scanProcess = 0.0 109 | self.cleanApplications() 110 | self.hijackedApplicationArray.removeAll(keepCapacity: false) 111 | self.scanProcess = 5.0 112 | 113 | // 114 | self.getAllThirdApplications() 115 | self.scanProcess = 40.0 116 | 117 | //check bundle ID hijack 118 | self.checkBundleIDHijcak() 119 | 120 | self.scanProcess = 100.0 121 | } 122 | 123 | func scan() { 124 | self.scanProcess = 0.0 125 | let queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND , 0) 126 | 127 | dispatch_async(queue, { 128 | // DO SOMETHING ON THE MAINTHREAD 129 | self.backendScan() 130 | }) 131 | 132 | } 133 | 134 | func getScanProcess() -> Double { 135 | return self.scanProcess 136 | } 137 | 138 | @objc 139 | func applicationChanged() { 140 | self.scan() 141 | } 142 | 143 | func startMoniter() { 144 | /* Define variables and create a CFArray object containing 145 | CFString objects containing paths to watch. 146 | */ 147 | let url = XGUtilize.getSystemApplicationsURL() 148 | let path = url.path! 149 | let appsPathRef = path as CFStringRef 150 | XGFileEventsHelper.startWatch(appsPathRef) 151 | 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /XGuardian/XGFileEventsHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // XGFileEventsHelper.h 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/19. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XGFileEventsHelper : NSObject 12 | 13 | + (void) startWatch:(CFStringRef) path;// 14 | @end 15 | -------------------------------------------------------------------------------- /XGuardian/XGFileEventsHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // XGFileEventsHelper.m 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/19. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #import "XGuardian-Swift.h" 14 | #import "XGFileEventsHelper.h" 15 | 16 | 17 | @interface XGFileEventsHelper() 18 | + (void) callBackEvent ; 19 | @end 20 | 21 | 22 | void XGFileEventCallBack( 23 | ConstFSEventStreamRef streamRef, 24 | void *clientCallBackInfo, 25 | size_t numEvents, 26 | void *eventPaths, 27 | const FSEventStreamEventFlags eventFlags[], 28 | const FSEventStreamEventId eventIds[]) 29 | { 30 | 31 | static const FSEventStreamEventFlags dirChangedEvent = kFSEventStreamEventFlagItemCreated| kFSEventStreamEventFlagItemRemoved | kFSEventStreamEventFlagItemIsDir; 32 | 33 | //char **paths = eventPaths; 34 | BOOL dirChangedFlag = false; 35 | for (int i=0; i < numEvents; i++) { 36 | 37 | /* flags are unsigned long, IDs are uint64_t */ 38 | //printf("Change %llu in %s, flags 0x%x\n", eventIds[i], paths[i], (unsigned int)eventFlags[i]); 39 | 40 | // dir 41 | if (eventIds[i] & (dirChangedEvent)) { 42 | dirChangedFlag = true; 43 | break; 44 | } 45 | 46 | } 47 | 48 | if (dirChangedFlag) { 49 | [XGFileEventsHelper callBackEvent]; 50 | 51 | } 52 | } 53 | 54 | 55 | @implementation XGFileEventsHelper 56 | 57 | //TODO: add delegate to deal the call back should be better 58 | + (void) callBackEvent { 59 | [[XGContainerApplicationManager sharedInstance] applicationChanged]; 60 | [[NSNotificationCenter defaultCenter] postNotificationName:@"XGBundleIDThreadsChangeNotification" object:nil]; 61 | } 62 | 63 | 64 | + (void) startWatch:(CFStringRef) path { 65 | 66 | CFArrayRef pathsToWatch = CFArrayCreate(kCFAllocatorDefault, (const void **)&path, 1, NULL); 67 | void *callbackInfo = NULL; // could put stream-specific data here. 68 | FSEventStreamRef stream; 69 | CFAbsoluteTime latency = 1.0; /* Latency in seconds */ 70 | 71 | /* Create the stream, passing in a callback */ 72 | stream = FSEventStreamCreate(NULL, 73 | &XGFileEventCallBack, 74 | callbackInfo, 75 | pathsToWatch, 76 | kFSEventStreamEventIdSinceNow, /* Or a previous event ID */ 77 | latency, 78 | kFSEventStreamCreateFlagIgnoreSelf ); 79 | 80 | FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),kCFRunLoopDefaultMode); 81 | FSEventStreamStart(stream); 82 | CFRelease(pathsToWatch); 83 | 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /XGuardian/XGFileSecurityHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // XGFileSecurityHelper.h 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/15. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XGFileSecurityHelper : NSObject 12 | 13 | + (void) getACLfromPath:(NSString*) fullPath; 14 | 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /XGuardian/XGFileSecurityHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // XGFileSecurityHelper.m 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/15. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | #import "XGFileSecurityHelper.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | static struct { 22 | acl_perm_t perm; 23 | char *name; 24 | int flags; 25 | #define ACL_PERM_DIR (1<<0) 26 | #define ACL_PERM_FILE (1<<1) 27 | } acl_perms[] = { 28 | {ACL_READ_DATA, "read", ACL_PERM_FILE}, 29 | {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR}, 30 | {ACL_WRITE_DATA, "write", ACL_PERM_FILE}, 31 | {ACL_ADD_FILE, "add_file", ACL_PERM_DIR}, 32 | {ACL_EXECUTE, "execute", ACL_PERM_FILE}, 33 | {ACL_SEARCH, "search", ACL_PERM_DIR}, 34 | {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR}, 35 | {ACL_APPEND_DATA, "append", ACL_PERM_FILE}, 36 | {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR}, 37 | {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR}, 38 | {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR}, 39 | {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR}, 40 | {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR}, 41 | {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR}, 42 | {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR}, 43 | {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR}, 44 | {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR}, 45 | {0, NULL, 0} 46 | }; 47 | 48 | static struct { 49 | acl_flag_t flag; 50 | char *name; 51 | int flags; 52 | } acl_flags[] = { 53 | {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR}, 54 | {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR}, 55 | {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR}, 56 | {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR}, 57 | {0, NULL, 0} 58 | }; 59 | 60 | 61 | static char * uuid_to_name(uuid_t *uu) { 62 | int is_gid = -1; 63 | struct group *tgrp = NULL; 64 | struct passwd *tpass = NULL; 65 | char *name = NULL; 66 | uid_t id; 67 | 68 | 69 | #define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */ 70 | name = (char *) malloc(MAXNAMETAG); 71 | 72 | if (NULL == name) { 73 | //TODO: malloc error 74 | return NULL; 75 | } 76 | 77 | 78 | if (0 != mbr_uuid_to_id(*uu, &id, &is_gid)) 79 | goto errout; 80 | 81 | switch (is_gid) { 82 | case ID_TYPE_UID: 83 | tpass = getpwuid(id); 84 | if (!tpass) { 85 | goto errout; 86 | } 87 | snprintf(name, MAXNAMETAG, "%s:%s", "user", tpass->pw_name); 88 | break; 89 | case ID_TYPE_GID: 90 | tgrp = getgrgid((gid_t) id); 91 | if (!tgrp) { 92 | goto errout; 93 | } 94 | snprintf(name, MAXNAMETAG, "%s:%s", "group", tgrp->gr_name); 95 | break; 96 | default: 97 | goto errout; 98 | } 99 | return name; 100 | 101 | errout: 102 | // if (0 != mbr_uuid_to_string(*uu, name)) { 103 | // fprintf(stderr, "Unable to translate qualifier on ACL\n"); 104 | // strcpy(name, ""); 105 | // } 106 | free(name); 107 | return NULL; 108 | } 109 | 110 | static void printacl(acl_t acl, int isdir) 111 | { 112 | acl_entry_t entry = NULL; 113 | int index; 114 | uuid_t *applicable; 115 | char *name = NULL; 116 | acl_tag_t tag; 117 | acl_flagset_t flags; 118 | acl_permset_t perms; 119 | char *type; 120 | int i, first; 121 | 122 | 123 | for (index = 0; acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0; index++) { 124 | if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL) 125 | continue; 126 | if (acl_get_tag_type(entry, &tag) != 0) 127 | continue; 128 | if (acl_get_flagset_np(entry, &flags) != 0) 129 | continue; 130 | if (acl_get_permset(entry, &perms) != 0) 131 | continue; 132 | 133 | name = uuid_to_name(applicable); 134 | acl_free(applicable); 135 | 136 | switch(tag) { 137 | case ACL_EXTENDED_ALLOW: 138 | type = "allow"; 139 | break; 140 | case ACL_EXTENDED_DENY: 141 | type = "deny"; 142 | break; 143 | default: 144 | type = "unknown"; 145 | } 146 | 147 | 148 | (void)printf(" %d: %s%s %s ", 149 | index, 150 | name, 151 | acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "",type); 152 | 153 | 154 | if (name) 155 | free(name); 156 | 157 | for (i = 0, first = 0; acl_perms[i].name != NULL; i++) { 158 | if (acl_get_perm_np(perms, acl_perms[i].perm) == 0) 159 | continue; 160 | if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE))) 161 | continue; 162 | (void)printf("%s%s", first++ ? "," : "", acl_perms[i].name); 163 | } 164 | for (i = 0; acl_flags[i].name != NULL; i++) { 165 | if (acl_get_flag_np(flags, acl_flags[i].flag) == 0) 166 | continue; 167 | if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE))) 168 | continue; 169 | (void)printf("%s%s", first++ ? "," : "", acl_flags[i].name); 170 | } 171 | 172 | (void)putchar('\n'); 173 | } 174 | 175 | } 176 | 177 | @implementation XGFileSecurityHelper 178 | 179 | 180 | + (void) getACLfromPath:(NSString*) fullPath { 181 | 182 | NSURL* url = [NSURL fileURLWithPath:fullPath]; 183 | 184 | NSError* error; 185 | id value; 186 | BOOL ret = [url getResourceValue:&value forKey:NSURLFileSecurityKey error:&error]; 187 | if (!ret) { 188 | NSLog(@"getResourceValue error: %@", error); 189 | return; 190 | } 191 | NSLog(@"getResourceValue success: %@ %@", url, value); 192 | 193 | acl_t* acl_p = NULL; 194 | CFFileSecurityRef fileSecurity = (__bridge CFFileSecurityRef)(value); 195 | Boolean res = CFFileSecurityCopyAccessControlList(fileSecurity, acl_p); 196 | if (!res || NULL == *acl_p) { 197 | NSLog(@"CFFileSecurityCopyAccessControlList failed"); 198 | return; 199 | } 200 | 201 | printacl(*acl_p, 1); 202 | acl_free(acl_p); 203 | 204 | } 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /XGuardian/XGHijackListAppCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGHijackListAppCell.swift 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/4. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGHijackListAppCell: NSTableCellView { 12 | 13 | override func drawRect(dirtyRect: NSRect) { 14 | super.drawRect(dirtyRect) 15 | 16 | // Drawing code here. 17 | } 18 | 19 | weak var secItem: XGSecurityItem? 20 | var appFullPath : String? 21 | 22 | override func viewDidMoveToWindow() { 23 | super.viewDidMoveToWindow() 24 | } 25 | 26 | @IBAction func btnRevelAction(sender: AnyObject) { 27 | self.openAppInFinder(sender) 28 | } 29 | 30 | @IBAction func btnDeleteAction(sender: AnyObject) { 31 | if let applicationPath = self.appFullPath { 32 | //println("\(secItem)") 33 | if let secKeyItemRef = self.secItem?.itemRef { 34 | Keychain.secRemoveApp(itemRef: secKeyItemRef, applicationFullPath: applicationPath) 35 | } 36 | } 37 | return 38 | } 39 | 40 | func openAppInFinder(sender: AnyObject) { 41 | // let row = self.tableView.rowForView(sender as! NSView) 42 | if let applicationPath = self.appFullPath { 43 | NSWorkspace.sharedWorkspace().selectFile(applicationPath, inFileViewerRootedAtPath: "") 44 | } 45 | return 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /XGuardian/XGHijackListCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGHijackListCell.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/6/30. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGHijackListCell: NSTableCellView { 12 | 13 | @IBOutlet var imageLabel: NSImageView? 14 | @IBOutlet var nameLabel: NSTextField? 15 | @IBOutlet var classLabel: NSTextField? 16 | @IBOutlet var accountLabel: NSTextField? 17 | @IBOutlet var positionLabel: NSTextField? 18 | @IBOutlet var modifyLabel: NSTextField? 19 | 20 | 21 | weak var item : XGSecurityItem? 22 | 23 | override func drawRect(dirtyRect: NSRect) { 24 | super.drawRect(dirtyRect) 25 | 26 | // Drawing code here. 27 | } 28 | 29 | 30 | override func viewDidMoveToWindow() { 31 | super.viewDidMoveToWindow() 32 | 33 | // 34 | // self.applicationTable?.headerView = nil 35 | // self.applicationTable?.setDelegate(self) 36 | // self.applicationTable?.setDataSource(self) 37 | // 38 | // 39 | // self.applicationTable?.reloadData() 40 | } 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /XGuardian/XGHijackListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGHijackListView.swift 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/6/29. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | private let noHijackStr = "Congratulation! We don't find any hijack!" 12 | private let noHijackImage = NSImageNameApplicationIcon 13 | private let hijackStr = "OW, NO!" 14 | private let hijackImage = NSImageNameCaution 15 | 16 | class XGHijackListView: NSView, NSOutlineViewDelegate, NSOutlineViewDataSource { 17 | 18 | private var itemArray : [XGSecurityItem]? 19 | 20 | @IBOutlet weak var titleImage: NSImageView! 21 | @IBOutlet weak var titleLable: NSTextField! 22 | @IBOutlet weak var outlineView: NSOutlineView! 23 | 24 | 25 | override func drawRect(dirtyRect: NSRect) { 26 | super.drawRect(dirtyRect) 27 | 28 | // Drawing code here. 29 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 30 | bezierPath.lineWidth = 1.0 31 | NSColor.whiteColor().set() 32 | bezierPath.fill() 33 | 34 | } 35 | 36 | override func viewDidMoveToWindow() { 37 | super.viewDidMoveToWindow() 38 | 39 | // 40 | self.outlineView.setDelegate(self) 41 | 42 | if self.scan() { 43 | self.titleImage.objectValue = NSImage(named:noHijackImage); 44 | self.titleLable.objectValue = noHijackStr; 45 | self.outlineView.hidden = true; 46 | } else { 47 | self.outlineView.setDataSource(self) 48 | self.outlineView.hidden = false; 49 | self.titleImage.objectValue = NSImage(named:hijackImage); 50 | let title = hijackStr + " You have \(self.itemArray!.count) password maybe in danger" 51 | self.titleLable.objectValue = title; 52 | 53 | //set outline view 54 | self.outlineView.sizeLastColumnToFit() 55 | self.outlineView.reloadData() 56 | self.outlineView.floatsGroupRows = false 57 | self.outlineView.rowSizeStyle = NSTableViewRowSizeStyle.Custom 58 | 59 | 60 | // Expand all the root items; disable the expansion animation that normally happens 61 | NSAnimationContext.beginGrouping() 62 | NSAnimationContext.currentContext().duration = NSTimeInterval(0) 63 | self.outlineView.expandItem(nil, expandChildren: true) 64 | NSAnimationContext.endGrouping() 65 | } 66 | 67 | } 68 | 69 | /** 70 | scan 71 | */ 72 | func scan() -> Bool { 73 | if let itemSet = XGKeyChain.getItemSet() { 74 | self.itemArray = itemSet.getPotentialArray(); 75 | } 76 | 77 | if( (nil == self.itemArray) || self.itemArray!.count <= 0) { 78 | return true 79 | } 80 | 81 | /********/ 82 | return false 83 | } 84 | 85 | private func childrenForItem(item: AnyObject?) -> [AnyObject]? { 86 | if nil == item { 87 | return self.itemArray 88 | } else { 89 | if let secItem = item as? XGSecurityItem { 90 | return secItem.applicationList 91 | } 92 | } 93 | return nil 94 | } 95 | 96 | //delegate for outline view 97 | func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int { 98 | if let children = self.childrenForItem(item) { 99 | return children.count 100 | } 101 | return 0 102 | } 103 | 104 | //delegate for outline view; get item for index 105 | func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject { 106 | let array = self.childrenForItem(item) 107 | if let itemArray = array as? [XGSecurityItem] { 108 | return itemArray[index] 109 | } 110 | 111 | let appArray = array as! [String] 112 | return appArray[index] as NSString 113 | } 114 | 115 | //delegate for outline view; expandable 116 | func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool { 117 | if outlineView.parentForItem(item) == nil { 118 | return true 119 | } 120 | return false 121 | } 122 | 123 | //delegate for outline view; is group 124 | func outlineView(outlineView: NSOutlineView, isGroupItem item: AnyObject) -> Bool { 125 | if let secItem = item as? XGSecurityItem { 126 | let isGroup = (self.itemArray! as NSArray).containsObject(secItem) 127 | return isGroup 128 | } 129 | return false 130 | } 131 | 132 | //delegate for outline view 133 | func outlineView(outlineView: NSOutlineView, shouldShowOutlineCellForItem item: AnyObject) -> Bool { 134 | //no show hide 135 | return false 136 | } 137 | 138 | func outlineView(outlineView: NSOutlineView, heightOfRowByItem item: AnyObject) -> CGFloat { 139 | if let secItem = item as? XGSecurityItem { 140 | return 100 141 | } 142 | return 17 143 | } 144 | 145 | 146 | //delegate for outline view 147 | func outlineView(outlineView: NSOutlineView, viewForTableColumn tableColumn: NSTableColumn?, item: AnyObject) -> NSView? { 148 | 149 | // For the groups, we just return a regular text view. 150 | if (self.itemArray! as NSArray).containsObject(item) { 151 | if let itemCell = outlineView.makeViewWithIdentifier("ItemCell", owner: self) as? XGHijackListCell { 152 | let secItem = item as! XGSecurityItem 153 | itemCell.item = secItem 154 | itemCell.nameLabel!.objectValue = secItem.name 155 | if secItem.classType == XGSecurityItem.ClassType.InternetPassword { 156 | itemCell.imageLabel!.objectValue = NSImage(named: NSImageNameUserAccounts) 157 | } 158 | 159 | itemCell.classLabel?.objectValue = secItem.classType?.description 160 | itemCell.accountLabel?.objectValue = secItem.account 161 | itemCell.positionLabel?.objectValue = secItem.position 162 | itemCell.modifyLabel?.objectValue = secItem.modifyTime?.description 163 | return itemCell 164 | } 165 | } else { 166 | if let result = outlineView.makeViewWithIdentifier("AppListCell", owner: self) as? XGHijackListAppCell { 167 | let appPath = item as! String 168 | result.appFullPath = appPath 169 | result.secItem = outlineView.parentForItem(item) as? XGSecurityItem 170 | result.textField?.objectValue = item.lastPathComponent 171 | result.imageView?.objectValue = NSWorkspace.sharedWorkspace().iconForFile(appPath) 172 | 173 | return result 174 | } 175 | } 176 | 177 | return nil 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /XGuardian/XGHijackView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGHijackView.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/6/29. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGHijackView: NSView { 12 | 13 | @IBOutlet weak var owner: NSViewController! 14 | @IBOutlet weak var hijackListView: XGHijackListView! 15 | 16 | private var upperView: NSView? //remember superview 17 | 18 | private enum ScanSate { 19 | case INIT 20 | case RUNNING 21 | case END 22 | } 23 | private var scanState = ScanSate.INIT 24 | 25 | @IBOutlet weak var scanButton: NSButton! 26 | @IBOutlet weak var scanProcess: NSProgressIndicator! 27 | @IBOutlet weak var scanPromt: NSTextField! 28 | 29 | @IBAction func scanAction(sender: NSButton) { 30 | switch self.scanState { 31 | case ScanSate.INIT: 32 | self.scanState = ScanSate.RUNNING 33 | self.scanPromt.hidden = false; 34 | self.scanButton.title = "SCANNING" 35 | self.scanProcess.displayedWhenStopped = false; 36 | //self.scanProcess.hidden = false; 37 | self.scanProcess.startAnimation(self) 38 | self.scanProcess.doubleValue = 0.0 39 | NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("processFireMethod:"), userInfo: nil, repeats: true) 40 | //scan 41 | XGKeyChain.scanAllItem() 42 | break; 43 | case ScanSate.RUNNING: 44 | break; 45 | case ScanSate.END: 46 | break; 47 | } 48 | return; 49 | } 50 | 51 | func processFireMethod(timer : NSTimer ){ 52 | 53 | if (self.scanProcess.doubleValue >= 89.9){ 54 | self.scanState = ScanSate.END 55 | } else { 56 | self.scanProcess.doubleValue += 15.0 57 | } 58 | 59 | if(self.scanState == ScanSate.END) { 60 | timer.invalidate(); 61 | self.scanButton.title = "SCANED" 62 | self.scanPromt.hidden = true 63 | self.scanProcess.doubleValue = 0 64 | self.scanProcess.stopAnimation(self) 65 | 66 | 67 | self.owner.view = self.hijackListView 68 | self.upperView = self.superview 69 | self.superview?.addSubview(self.hijackListView) 70 | self.superview?.replaceSubview(self, with:self.hijackListView) 71 | } 72 | return; 73 | } 74 | 75 | 76 | override func viewDidMoveToWindow() { 77 | super.viewDidMoveToWindow() 78 | 79 | } 80 | 81 | override func drawRect(dirtyRect: NSRect) { 82 | super.drawRect(dirtyRect) 83 | 84 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 85 | bezierPath.lineWidth = 1.0 86 | NSColor.whiteColor().set() 87 | bezierPath.fill() 88 | 89 | } 90 | 91 | 92 | 93 | override var opaque :Bool { get { 94 | return false; 95 | } 96 | } 97 | 98 | override func menuForEvent(event: NSEvent) -> NSMenu? { 99 | return nil 100 | } 101 | 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /XGuardian/XGKeychainHijackDetailsCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainHijackDetailsCell.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/7. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGKeychainHijackDetailsCell: NSTableCellView { 12 | 13 | @IBOutlet var removeBtn: NSButton! 14 | 15 | var backgroundColor : NSColor? 16 | override func drawRect(dirtyRect: NSRect) { 17 | super.drawRect(dirtyRect) 18 | 19 | // Drawing code here. 20 | if let bc = self.backgroundColor { 21 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 22 | //bezierPath.lineWidth = 1.0 23 | bc.set() 24 | bezierPath.fill() 25 | } 26 | } 27 | 28 | weak var upperView: XGKeychainHijackDetailsView? 29 | weak var secItem: XGSecurityItem? 30 | var appFullPath : String? 31 | 32 | override func viewDidMoveToWindow() { 33 | super.viewDidMoveToWindow() 34 | } 35 | 36 | @IBAction func btnRevelAction(sender: AnyObject) { 37 | self.openAppInFinder(sender) 38 | } 39 | 40 | @IBAction func btnDeleteAction(sender: AnyObject) { 41 | if let applicationPath = self.appFullPath { 42 | //println("\(secItem)") 43 | if let secKeyItemRef = self.secItem?.itemRef { 44 | Keychain.secRemoveApp(itemRef: secKeyItemRef, applicationFullPath: applicationPath) 45 | //Notifiy cell change 46 | self.upperView?.tableViewCellChanged() 47 | } 48 | } 49 | return 50 | } 51 | 52 | func openAppInFinder(sender: AnyObject) { 53 | // let row = self.tableView.rowForView(sender as! NSView) 54 | if let applicationPath = self.appFullPath { 55 | NSWorkspace.sharedWorkspace().selectFile(applicationPath, inFileViewerRootedAtPath: "") 56 | } 57 | return 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /XGuardian/XGKeychainHijackDetailsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainHijackDetailsView.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/7. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGKeychainHijackDetailsView: NSView, NSTableViewDelegate, NSTableViewDataSource { 12 | 13 | @IBOutlet weak var nameLabel: NSTextField! 14 | @IBOutlet weak var classLabel: NSTextField! 15 | @IBOutlet weak var accountLabel: NSTextField! 16 | @IBOutlet weak var positionLabel: NSTextField! 17 | @IBOutlet weak var createLabel: NSTextField! 18 | @IBOutlet weak var modifyLabel: NSTextField! 19 | @IBOutlet weak var imageLabel: NSImageView! 20 | 21 | @IBOutlet weak var hijackAppTableView: NSTableView! 22 | 23 | 24 | weak var secItem : XGSecurityItem? 25 | weak var upperViewController : XGThreatsViewController? 26 | 27 | 28 | override func drawRect(dirtyRect: NSRect) { 29 | super.drawRect(dirtyRect) 30 | 31 | // Drawing code here. 32 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 33 | bezierPath.lineWidth = 1.0 34 | NSColor.whiteColor().set() 35 | bezierPath.fill() 36 | } 37 | 38 | 39 | override func viewDidMoveToWindow() { 40 | super.viewDidMoveToWindow() 41 | 42 | // 43 | if let item = self.secItem { 44 | self.nameLabel.objectValue = item.name 45 | self.classLabel.objectValue = item.classType?.description 46 | self.accountLabel.objectValue = item.account 47 | self.positionLabel.objectValue = item.position 48 | self.createLabel.objectValue = item.createTime?.description 49 | self.modifyLabel.objectValue = item.modifyTime?.description 50 | if item.classType == XGSecurityItem.ClassType.InternetPassword { 51 | self.imageLabel?.objectValue = NSImage(named: NSImageNameUserAccounts) 52 | } 53 | else { 54 | self.imageLabel?.objectValue = NSImage(named:NSImageNameUser) 55 | } 56 | 57 | } 58 | 59 | self.hijackAppTableView.setDelegate(self) 60 | self.hijackAppTableView.setDataSource(self) 61 | self.hijackAppTableView.headerView = nil 62 | self.hijackAppTableView.reloadData() 63 | 64 | //seleted first row 65 | self.hijackAppTableView.selectRowIndexes(NSIndexSet(index: 0), byExtendingSelection: false) 66 | 67 | } 68 | 69 | 70 | func numberOfRowsInTableView(tableView: NSTableView) -> Int { 71 | if let applist = self.secItem?.applicationList { 72 | return applist.count 73 | } 74 | return 0; 75 | } 76 | 77 | 78 | func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { 79 | 80 | if nil == self.secItem { 81 | return nil 82 | } 83 | let appList = self.secItem?.applicationList 84 | let appPath = appList![row] 85 | 86 | if let identifier = tableColumn?.identifier { 87 | if let result = tableView.makeViewWithIdentifier(identifier, owner: self) as? XGKeychainHijackDetailsCell { 88 | result.appFullPath = appPath 89 | result.textField?.objectValue = (appPath as NSString).lastPathComponent 90 | result.imageView?.objectValue = NSWorkspace.sharedWorkspace().iconForFile(appPath) 91 | 92 | if let appType = self.secItem?.applicationTypeList?[row] { 93 | 94 | switch appType { 95 | case XGSecurityAppType.Apple: 96 | result.backgroundColor = NSColor(calibratedRed:0.400, green:1.000, blue:0.400, alpha:1.000) 97 | 98 | case XGSecurityAppType.Group: 99 | result.backgroundColor = NSColor(calibratedRed:0.400, green:1.000, blue:0.400, alpha:1.000) 100 | 101 | case XGSecurityAppType.WhiteList: 102 | result.backgroundColor = NSColor(calibratedRed:0.400, green:1.000, blue:0.400, alpha:1.000) 103 | 104 | case XGSecurityAppType.Sining: 105 | result.backgroundColor = NSColor(calibratedRed: 0.400, green:1.000, blue:1.000, alpha:1.000) 106 | 107 | case XGSecurityAppType.Unknown: 108 | result.backgroundColor = NSColor(calibratedRed: 1.000, green:0.4, blue:1.0, alpha:1.000) 109 | 110 | } 111 | 112 | 113 | } 114 | 115 | result.appFullPath = appPath 116 | result.secItem = self.secItem 117 | 118 | result.upperView = self 119 | return result 120 | } 121 | } 122 | 123 | return nil; 124 | } 125 | 126 | func tableView(aTableView: NSTableView, toolTipForCell aCell: NSCell, rect: NSRectPointer,tableColumn aTableColumn: NSTableColumn?,row: Int, mouseLocation: NSPoint) -> String { 127 | if nil == self.secItem { 128 | return "" 129 | } 130 | let appList = self.secItem?.applicationList 131 | let appPath = appList![row] 132 | 133 | return appPath 134 | } 135 | 136 | 137 | 138 | func tableViewCellChanged() { 139 | self.upperViewController?.KeychainHijackViewChanged(false) 140 | } 141 | 142 | 143 | } 144 | -------------------------------------------------------------------------------- /XGuardian/XGKeychainManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainManager.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/6/23. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import CoreFoundation 11 | import Security 12 | 13 | let XGKeychainInstance = XGKeychainManager.sharedInstance 14 | 15 | @objc (XGKeychainManager) 16 | class XGKeychainManager : NSObject { 17 | 18 | static let sharedInstance : XGKeychainManager = XGKeychainManager() 19 | 20 | 21 | var globalItemSet: XGSecurityItemSet = XGSecurityItemSet() 22 | //private var hijackedItemArray : [XGSecurityItem]? 23 | 24 | @objc(getItemSet) 25 | func getItemSet() -> XGSecurityItemSet { 26 | if 0 == globalItemSet.count { 27 | self.scanAllItem() 28 | } 29 | return globalItemSet 30 | } 31 | 32 | func getHijackedItemArray() -> [XGSecurityItem]? { 33 | return self.getItemSet().getPotentialArray() 34 | } 35 | 36 | 37 | func getClassAllKey(classValue:Keychain.Query.KSecClassValue ) -> NSArray? { 38 | 39 | // create qurey 40 | let query = Keychain.Query() 41 | query.kSecClass = classValue 42 | query.kSecReturnRef = true 43 | query.kSecReturnAttributes = true 44 | query.kSecReturnPersistentRef = true 45 | query.kSecMatchLimit = Keychain.Query.KSecMatchLimitValue.kSecMatchLimitAll 46 | 47 | 48 | let res = Keychain.secItemCopyMatching(query: query) 49 | if res.status != Keychain.ResultCode.errSecSuccess { 50 | NSLog("secItemCopy error: \(res.status)") 51 | } 52 | let r = res.result 53 | if (r == nil){ 54 | return nil 55 | } 56 | 57 | let resultArray = r as? NSArray 58 | return resultArray; 59 | } 60 | 61 | 62 | func scanAllItem() { 63 | 64 | globalItemSet.removeAll() 65 | let itemSet = globalItemSet 66 | 67 | //scan internet password 68 | let internetPassword = getClassAllKey(Keychain.Query.KSecClassValue.kSecClassInternetPassword) 69 | if let ip = internetPassword { 70 | for outDict in ip { 71 | let item = XGSecurityItem(attrDict: (outDict as? NSDictionary)!) 72 | itemSet.addItem(item) 73 | //println("\(outDict)") 74 | //println("\(item)") 75 | } 76 | } 77 | 78 | //scan generic password 79 | let genericPassword = getClassAllKey(Keychain.Query.KSecClassValue.kSecClassGenericPassword) 80 | if let gp = genericPassword { 81 | for outDict in gp { 82 | let item = XGSecurityItem(attrDict: (outDict as? NSDictionary)!) 83 | itemSet.addItem(item) 84 | 85 | } 86 | } 87 | 88 | } 89 | 90 | 91 | } 92 | 93 | //Mark: - 94 | class XGKeychainTest { 95 | 96 | class func test1() -> Bool { 97 | let q = Keychain.Query() 98 | q.kSecClass = Keychain.Query.KSecClassValue.kSecClassGenericPassword 99 | q.kSecAttrDescription = "This is a test description" 100 | q.kSecAttrGeneric = "Parol".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) 101 | q.kSecAttrAccount = "Try1 account-" + "105" 102 | q.kSecAttrLabel = "Try1 label" 103 | q.kSecAttrAccessible = Keychain.Query.KSecAttrAccessibleValue.kSecAttrAccessibleAlways 104 | //q.kSecReturnData = true 105 | q.kSecReturnAttributes = true 106 | q.kSecReturnRef = true 107 | q.kSecReturnPersistentRef = true 108 | 109 | q.kSecValueData = "Privet".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) 110 | 111 | let res1 = Keychain.secItemAdd(query: q) 112 | 113 | //println("Keychain secItemAdd returned: \(res1.status)") 114 | 115 | if let resUw = res1.result { 116 | print("res1 TypeID = \(CFGetTypeID(resUw)), Description = \(resUw)") 117 | } else { 118 | print("res1 is nil") 119 | } 120 | 121 | let q2 = Keychain.Query() 122 | q2.kSecAttrAccount = q.kSecAttrAccount 123 | q2.kSecClass = q.kSecClass 124 | q2.kSecReturnAttributes = true 125 | q2.kSecReturnRef = true 126 | q2.kSecReturnPersistentRef = true 127 | q2.kSecMatchLimit = Keychain.Query.KSecMatchLimitValue.kSecMatchLimitOne 128 | 129 | let res2 = Keychain.secItemCopyMatching(query:q2) 130 | 131 | 132 | print("Status of secItemCopyMatching: \(res2.status.toRaw())") 133 | if let r = res2.result 134 | { 135 | print("res2 TypeID: \(CFGetTypeID(r)) Description: \(r)") 136 | let skey = XGSecurityItem(attrDict: (r as? NSDictionary)!) 137 | print("res2 \(skey)") 138 | } else { 139 | print("res2 is nil") 140 | } 141 | 142 | 143 | //assert(res1.result != nil, "Retreived result is not nil") 144 | if let r = res1.result { 145 | if let resultDic = r as? NSDictionary { 146 | assert(resultDic.objectForKey("acct")!.isEqual(q.kSecAttrAccount!), "Account of the retrieved item matches") 147 | } 148 | } 149 | 150 | // let res3 = Keychain.secItemDelete(query: q); 151 | // println("Keychain secItemDelete returned: \(res3)") 152 | 153 | 154 | return true; 155 | 156 | } 157 | } -------------------------------------------------------------------------------- /XGuardian/XGKeychainObserver.h: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainObserver.h 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/6/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XGKeychainObserver : NSObject 12 | 13 | @property (assign,atomic) bool isExit; 14 | @property (strong,nonatomic) NSThread* thread; 15 | 16 | + (XGKeychainObserver*) startObserve; 17 | + (void) stopObserve; 18 | 19 | 20 | 21 | - (void) processKeychainEvent:(SecKeychainEvent)event CBInfo:(SecKeychainCallbackInfo *)cbinfo; 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /XGuardian/XGKeychainObserver.m: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainObserver.m 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/6/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | @import AppKit; 10 | #import "XGuardian-Swift.h" 11 | #import "XGKeychainObserver.h" 12 | 13 | 14 | 15 | static XGKeychainObserver* staticSharedObserver = nil; 16 | static int CB_Context = 12; 17 | 18 | static OSStatus XGSecKeychainCBFun ( SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context ) 19 | { 20 | switch (keychainEvent) { 21 | case kSecLockEvent: 22 | case kSecUnlockEvent: 23 | //case kSecPasswordChangedEvent: 24 | //case kSecDataAccessEvent: 25 | /*no need */ 26 | return errSecSuccess; 27 | break; 28 | /*case kSecKeychainListChangedEvent: 29 | break; 30 | case kSecAddEvent: 31 | break; 32 | case kSecDeleteEvent: 33 | break; 34 | case kSecUpdateEvent: 35 | break; 36 | 37 | case kSecDefaultChangedEvent: 38 | break; 39 | 40 | case kSecTrustSettingsChangedEvent: 41 | break;*/ 42 | default: 43 | //NSLog(@"Unknown keychain event"); 44 | break; 45 | } 46 | 47 | if ( keychainEvent > kSecTrustSettingsChangedEvent ){ 48 | return errSecSuccess; 49 | } 50 | [staticSharedObserver processKeychainEvent:keychainEvent CBInfo:info]; 51 | return errSecSuccess; 52 | } 53 | 54 | #pragma mark XGKeychainObserverCallbackManager 55 | 56 | @interface XGKeychainObserverCallbackManager:NSObject 57 | +(OSStatus) secKeychainAddCallback; 58 | +(OSStatus) secKeychainRemoveCallback; 59 | @end 60 | 61 | @implementation XGKeychainObserverCallbackManager 62 | 63 | +(OSStatus) secKeychainAddCallback{ 64 | SecKeychainEventMask wathEventMask = kSecEveryEventMask;//kSecKeychainListChangedEvent|kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask|kSecPasswordChangedEventMask|kSecDefaultChangedEventMask|kSecDataAccessEventMask|kSecTrustSettingsChangedEventMask; 65 | OSStatus stat = SecKeychainAddCallback ( XGSecKeychainCBFun, wathEventMask, &CB_Context ); 66 | return stat; 67 | } 68 | 69 | +(OSStatus) secKeychainRemoveCallback { 70 | OSStatus stat = SecKeychainRemoveCallback ( XGSecKeychainCBFun ); 71 | return stat; 72 | } 73 | @end 74 | 75 | 76 | /***************************************************************************/ 77 | #pragma mark XGKeychainCallbackInfo 78 | 79 | @interface XGKeychainCallbackInfo:NSObject 80 | @property (readonly, assign, nonatomic) SecKeychainEvent event; 81 | @property (readonly, assign, nonatomic) UInt32 version; 82 | @property (readonly, nonatomic ) SecKeychainItemRef itemRef; 83 | @property (readonly, nonatomic ) SecKeychainRef keychain; 84 | @property (readonly, assign, nonatomic) pid_t pid; 85 | @property (readonly, nonatomic ) NSString *appName; 86 | @property (readonly, nonatomic ) NSString *bundleID; 87 | @property (readonly, nonatomic ) NSURL *bundleURL; 88 | @property (readonly, nonatomic ) XGSecurityItem *securityItem; 89 | @property (readonly, nonatomic) SecKeychainItemRef secKeychainItemRef; 90 | @end 91 | 92 | @implementation XGKeychainCallbackInfo 93 | 94 | - (instancetype)init:(SecKeychainEvent)event CBInfo:(SecKeychainCallbackInfo *)cbinfo 95 | AppInfo:(NSRunningApplication*)appInfo SecurityItem:(XGSecurityItem *)securityItem secKeychainItemRef: (SecKeychainItemRef) itemRef { 96 | self = [super init]; 97 | if (self) { 98 | _event = event; 99 | _version = cbinfo->version; 100 | _itemRef = cbinfo->item; 101 | _keychain = cbinfo->keychain; 102 | 103 | _pid = cbinfo->pid; 104 | _appName = [appInfo localizedName]; 105 | _bundleID = [appInfo bundleIdentifier]; 106 | _bundleURL = [appInfo bundleURL]; 107 | 108 | _securityItem = securityItem; 109 | _secKeychainItemRef = itemRef; 110 | 111 | } 112 | return self; 113 | } 114 | 115 | @end 116 | 117 | /***************************************************************************/ 118 | #pragma mark XGKeychainObserver 119 | 120 | @interface XGKeychainObserver () 121 | @property (assign, atomic) bool isRunning; 122 | - (void) threadMethod; 123 | - (void) keychainEventProcessor:(XGKeychainCallbackInfo *)info; 124 | @end 125 | 126 | #pragma mark - 127 | 128 | @implementation XGKeychainObserver 129 | 130 | + (XGKeychainObserver*) startObserve { 131 | 132 | if (nil == staticSharedObserver) { 133 | staticSharedObserver = [[XGKeychainObserver alloc] init]; 134 | if (nil == staticSharedObserver) { 135 | return nil; 136 | } 137 | } 138 | 139 | //start thread 140 | [staticSharedObserver setIsExit:false]; 141 | [staticSharedObserver start]; 142 | 143 | // start watch keychain event 144 | [XGKeychainObserverCallbackManager secKeychainAddCallback]; 145 | return staticSharedObserver; 146 | } 147 | 148 | + (void) stopObserve { 149 | 150 | // stop watch keychain event 151 | [XGKeychainObserverCallbackManager secKeychainRemoveCallback]; 152 | 153 | //stop thread 154 | [staticSharedObserver setIsExit:true]; 155 | //staticSharedObserver() 156 | 157 | 158 | return; 159 | } 160 | 161 | #pragma mark - 162 | 163 | - (instancetype)init 164 | { 165 | self = [super init]; 166 | if (nil == self) { 167 | return nil; 168 | } 169 | 170 | _isRunning = false; 171 | _isExit = false; 172 | _thread = nil; 173 | 174 | return self; 175 | } 176 | 177 | - (void) start { 178 | 179 | if (_isRunning || _thread != nil) { 180 | return; 181 | } 182 | _isRunning = true; 183 | 184 | NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMethod) object:nil]; 185 | if (nil == thread) { 186 | _isRunning = false; 187 | return; 188 | } 189 | 190 | _thread = thread; 191 | [thread start]; 192 | } 193 | 194 | - (void) processKeychainEvent:(SecKeychainEvent)event CBInfo:(SecKeychainCallbackInfo *)cbinfo { 195 | 196 | SecKeychainItemRef itemRef = cbinfo->item; 197 | XGSecurityItem *securityItem = nil; 198 | if( nil != itemRef) { 199 | NSDictionary* attrDict = [Keychain secKeychainItemGetAttr:itemRef]; 200 | //NSLog(@"!!!SecKeychainItemRef:%p !!!!", itemRef); 201 | if(nil != attrDict) { 202 | if (nil == [attrDict objectForKey:@"v_Ref"] ) { 203 | [attrDict setValue:(__bridge id)(itemRef) forKey:@"v_Ref"]; 204 | } 205 | securityItem = [[XGSecurityItem alloc]init:attrDict]; 206 | } 207 | } 208 | 209 | NSRunningApplication *appInfo = [NSRunningApplication runningApplicationWithProcessIdentifier:cbinfo->pid]; 210 | 211 | XGKeychainCallbackInfo* info = [[XGKeychainCallbackInfo alloc] init:event CBInfo:cbinfo AppInfo:appInfo SecurityItem:securityItem secKeychainItemRef:itemRef]; 212 | 213 | [self performSelector:@selector(keychainEventProcessor:) onThread:[self thread] withObject:info waitUntilDone:NO]; 214 | } 215 | 216 | /** 217 | * keychain observer process thread 218 | */ 219 | - (void) threadMethod { 220 | NSRunLoop* runloop = [NSRunLoop currentRunLoop]; 221 | 222 | while (!self.isExit) { 223 | [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; 224 | } 225 | 226 | _isRunning = false; 227 | _isExit = false; 228 | return; 229 | } 230 | 231 | - (void) keychainChangeUserNotify:(NSString*) noteText : (NSString*) noteContext{ 232 | 233 | [[NSNotificationCenter defaultCenter] postNotificationName:@"XGKeychainThreadsChangeNotification" object:noteContext]; 234 | 235 | //user notify 236 | NSUserNotification *notification = [[NSUserNotification alloc]init]; 237 | NSUserNotificationCenter* ntfCecenter = [NSUserNotificationCenter defaultUserNotificationCenter]; 238 | [notification setTitle: @"Notice"]; 239 | [notification setInformativeText:noteText]; 240 | [ntfCecenter deliverNotification:notification]; 241 | 242 | } 243 | 244 | - (void) keychainEventProcessor:(XGKeychainCallbackInfo *)info { 245 | 246 | //NSLog(@"SecKeychainCallbackInfo:\nevent:%d version:%d pid:%d \nApp Name:%@\nbundle ID:%@\nbudle URL:%@\n item:%@", [info event], [info version], [info pid], info.appName, info.bundleID, info.bundleURL, info.securityItem); 247 | 248 | //find same key info in the dictionary 249 | XGSecurityItemSet *itemSet = [[XGKeychainManager sharedInstance] getItemSet]; 250 | XGSecurityItem *oldItem = nil; 251 | if (nil != info.securityItem ) { 252 | oldItem = [itemSet findItem:[info securityItem]]; 253 | } 254 | 255 | 256 | if (info.event == kSecDeleteEvent /*|| info.event > kSecTrustSettingsChangedEvent *TODO: I don't known what's that*/) { 257 | if ( nil != info.securityItem) { 258 | [itemSet removeItem:info.securityItem]; 259 | } 260 | [self keychainChangeUserNotify:@"Some keychain items has been changed. Plese rescan!" :@"rescan"]; 261 | 262 | } else { 263 | if(nil == oldItem) { 264 | [itemSet addItem:info.securityItem]; 265 | return; 266 | } 267 | 268 | //check the application list change 269 | BOOL isSame = [oldItem isSameWith:info.securityItem]; 270 | if (isSame) { 271 | return; 272 | } 273 | 274 | [itemSet addItem:info.securityItem]; 275 | if (info.securityItem.applicationNum <= 1 ){ 276 | return; 277 | } 278 | 279 | //notify 280 | [self keychainChangeUserNotify:[[NSString alloc] initWithFormat:@"%@(%@) may have been hijack! Please check it.", info.securityItem.name, info.securityItem.account] :nil]; 281 | 282 | } 283 | } 284 | 285 | 286 | 287 | @end 288 | -------------------------------------------------------------------------------- /XGuardian/XGKeychainThreatsDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainThreatsDelegate.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class XGKeychainThreatsDelegate:NSObject, XGThreatsViewDelegate { 13 | 14 | static let sharedInstance = XGKeychainThreatsDelegate() 15 | static func getInstance() -> XGThreatsViewDelegate { 16 | return sharedInstance 17 | } 18 | 19 | private var hijackedItemArray : [XGSecurityItem]? 20 | 21 | //MARK: threats view delegate 22 | var title : String { get { 23 | return "keychain Hijack" 24 | }} 25 | 26 | private var isObserving = false 27 | func addNotificationObserver() { 28 | if !self.isObserving { 29 | self.isObserving = true 30 | NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("threatsDidChanged:"), name: "XGKeychainThreadsChangeNotification", object: nil) 31 | } 32 | } 33 | 34 | func removeNotificationObserver() { 35 | if self.isObserving { 36 | NSNotificationCenter.defaultCenter().removeObserver(self, name: "XGKeychainThreadsChangeNotification", object: nil) 37 | self.isObserving = false 38 | } 39 | } 40 | 41 | func refreshThreatsData() -> Int { 42 | XGKeychainInstance.scanAllItem() 43 | if let hijackedItemArray = XGKeychainInstance.getHijackedItemArray() { 44 | self.hijackedItemArray = hijackedItemArray 45 | return hijackedItemArray.count 46 | } else { 47 | self.hijackedItemArray = nil 48 | return 0 49 | } 50 | } 51 | 52 | func isExpandable(item: AnyObject?) -> Bool { 53 | return false 54 | } 55 | 56 | func childrenForItem(item: AnyObject?) -> [AnyObject]? { 57 | if nil != item { 58 | return nil 59 | } 60 | return self.hijackedItemArray 61 | } 62 | 63 | func setCellView(cellView : NSTableCellView, item: AnyObject, parent : AnyObject? ) { 64 | let secItem = item as! XGSecurityItem 65 | cellView.textField?.objectValue = secItem.name 66 | if secItem.classType == XGSecurityItem.ClassType.InternetPassword { 67 | cellView.imageView?.objectValue = NSImage(named: NSImageNameUserAccounts) 68 | } else { 69 | cellView.imageView?.objectValue = NSImage(named:NSImageNameUser) 70 | } 71 | } 72 | 73 | var threatsNumber : Int { get { 74 | if let hijackedItemArray = self.hijackedItemArray { 75 | self.hijackedItemArray = hijackedItemArray 76 | return hijackedItemArray.count 77 | } 78 | return 0 79 | } } 80 | 81 | func detailsView( threatsViewController : XGThreatsViewController, item : AnyObject?, parent : AnyObject? ) -> NSView? { 82 | if let secItem = item as? XGSecurityItem { 83 | let currentdetailViewController = NSViewController(nibName: "KeychainHijackDetailsView", bundle: nil) 84 | 85 | if let view = currentdetailViewController?.view as? XGKeychainHijackDetailsView{ 86 | view.secItem = secItem 87 | view.upperViewController = threatsViewController 88 | return view 89 | } 90 | } 91 | return nil 92 | } 93 | 94 | func threatsDidChanged(notification: NSNotification) { 95 | 96 | dispatch_async(dispatch_get_main_queue(), { 97 | self.refreshThreatsData() 98 | // DO SOMETHING ON THE MAINTHREAD 99 | NSNotificationCenter.defaultCenter().postNotificationName("XGThreadsChangedNotification", object: notification.object) 100 | }) 101 | 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /XGuardian/XGKeychainView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGKeychainView.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/6/29. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGKeychainView: NSView, NSTableViewDelegate, NSTableViewDataSource { 12 | 13 | private var itemArray : [XGSecurityItem]? 14 | 15 | @IBOutlet weak var keychainTable: NSTableView! 16 | @IBOutlet weak var iconView: NSImageView! 17 | @IBOutlet weak var nameLabel: NSTextField! 18 | @IBOutlet weak var classLabel: NSTextField! 19 | @IBOutlet weak var accountLabel: NSTextField! 20 | @IBOutlet weak var positionLabel: NSTextField! 21 | 22 | @IBOutlet weak var owner: NSViewController! 23 | @IBOutlet weak var applicationsLabel: NSTextField! 24 | 25 | //override func 26 | override func drawRect(dirtyRect: NSRect) { 27 | super.drawRect(dirtyRect) 28 | 29 | // Drawing code here. 30 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 31 | bezierPath.lineWidth = 1.0 32 | NSColor.whiteColor().set() 33 | bezierPath.fill() 34 | 35 | 36 | } 37 | 38 | override func viewDidMoveToWindow() { 39 | super.viewDidMoveToWindow() 40 | 41 | // 42 | let itemSet = XGKeychainInstance.getItemSet() 43 | self.itemArray = itemSet.toArray(); 44 | self.keychainTable.setDelegate(self) 45 | self.keychainTable.setDataSource(self) 46 | self.keychainTable.doubleAction = Selector("openAppFinder:") 47 | self.keychainTable.reloadData() 48 | 49 | 50 | self.keychainTable.selectRowIndexes(NSIndexSet(index: 0), byExtendingSelection: false) 51 | } 52 | 53 | 54 | 55 | func numberOfRowsInTableView(tableView: NSTableView) -> Int { 56 | if let its = self.itemArray { 57 | return its.count; 58 | } 59 | return 0; 60 | } 61 | 62 | func tableViewSelectionDidChange(notification: NSNotification) { 63 | let row = self.keychainTable.selectedRow; 64 | if( row < 0 || row > self.itemArray?.count) { 65 | return 66 | } 67 | 68 | let item = self.itemArray![row] 69 | if item.classType == XGSecurityItem.ClassType.InternetPassword { 70 | let image = NSImage(named: NSImageNameUserAccounts ) 71 | iconView.image = image; 72 | } else if item.classType == XGSecurityItem.ClassType.GenericPassword { 73 | let image = NSImage(named: NSImageNameComputer) 74 | iconView.image = image; 75 | } 76 | 77 | self.classLabel.objectValue = item.classType?.description; 78 | self.nameLabel.objectValue = item.name 79 | self.accountLabel.objectValue = item.account 80 | self.positionLabel.objectValue = item.position 81 | var appStr = "" 82 | if let appList = item.applicationList { 83 | for (var i = 0 ; i < appList.count ; i++) { 84 | if(i > 1) { appStr += " | " } 85 | appStr += (appList[i] as NSString).lastPathComponent 86 | } 87 | } else if item.applicationNum == -1 { 88 | appStr = "ANY Application" 89 | } 90 | self.applicationsLabel.objectValue = appStr 91 | 92 | 93 | } 94 | 95 | func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { 96 | 97 | if nil == self.itemArray { 98 | return nil 99 | } 100 | let item = self.itemArray![row] 101 | 102 | 103 | if let identifier = tableColumn?.identifier { 104 | switch identifier { 105 | case "NameCell": 106 | if let nameTextField = tableView.makeViewWithIdentifier(identifier,owner:self) as? NSTableCellView { 107 | nameTextField.textField!.objectValue = item.name; 108 | //nameTextField.textField!.stringValue = item.name!; 109 | //let cellRect = CGRectMake(0, 0, tableView.frame.size.width ,44); 110 | //nameTextField.frame = cellRect; 111 | return nameTextField; 112 | } 113 | break; 114 | 115 | case "ClassCell": 116 | if let classTextField = tableView.makeViewWithIdentifier(identifier,owner:self) as? NSTableCellView { 117 | classTextField.textField!.objectValue = item.classType!.description; 118 | return classTextField 119 | } 120 | break; 121 | 122 | case "ApplicationsCell": 123 | let appCellView = tableView.makeViewWithIdentifier(identifier,owner:self) as? NSTableCellView 124 | if(nil == appCellView){ 125 | return nil 126 | } 127 | 128 | if(item.applicationNum > 0) { 129 | let applicationFullPath = item.applicationList![0]; 130 | let appName = (applicationFullPath as NSString).lastPathComponent 131 | appCellView!.textField!.objectValue = appName 132 | 133 | let image = NSWorkspace.sharedWorkspace().iconForFile(applicationFullPath) 134 | appCellView!.imageView?.objectValue = image 135 | return appCellView 136 | } else if(item.applicationNum == -1) { 137 | 138 | appCellView!.textField?.objectValue = Keychain.secAuthorizeAllApp 139 | return appCellView 140 | } else { 141 | return nil 142 | } 143 | default: 144 | break; 145 | } 146 | } 147 | 148 | return nil; 149 | } 150 | 151 | //double clicks to open the application in finder 152 | func openAppFinder(sender: AnyObject) { 153 | // let row = self.tableView.rowForView(sender as! NSView) 154 | let row = (sender as! NSTableView).clickedRow 155 | if nil == self.itemArray || row < 0 || row >= self.itemArray?.count{ 156 | return 157 | } 158 | let item = self.itemArray![row] 159 | if let applicationFullPath = item.applicationList?[0] { 160 | NSWorkspace.sharedWorkspace().selectFile(applicationFullPath, inFileViewerRootedAtPath: "") 161 | } 162 | } 163 | 164 | 165 | 166 | 167 | 168 | 169 | } 170 | -------------------------------------------------------------------------------- /XGuardian/XGScanView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGScanView.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/8. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGScanView: NSView { 12 | 13 | 14 | 15 | override func viewDidMoveToWindow() { 16 | super.viewDidMoveToWindow() 17 | } 18 | 19 | override func drawRect(dirtyRect: NSRect) { 20 | super.drawRect(dirtyRect) 21 | 22 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 23 | bezierPath.lineWidth = 1.0 24 | NSColor.whiteColor().set() 25 | bezierPath.fill() 26 | 27 | } 28 | 29 | 30 | 31 | 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /XGuardian/XGScanViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGScanViewController.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/8. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGScanViewController: NSViewController { 12 | 13 | @IBOutlet weak var scanButton: NSButton! 14 | @IBOutlet weak var functionalLabel: NSTextField! 15 | @IBOutlet weak var functionalDescrption: NSTextField! 16 | @IBOutlet weak var titleButton: NSButton! 17 | 18 | weak var scanView : XGScanView? 19 | weak var barItem: XGSideBarItem? 20 | 21 | private enum ScanSate { 22 | case INIT 23 | case RUNNING 24 | case END 25 | } 26 | private var scanState = ScanSate.INIT 27 | 28 | @IBAction func scanAction(sender: AnyObject) { 29 | switch self.scanState { 30 | case ScanSate.INIT: 31 | 32 | self.startScan() 33 | NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("processFireMethod:"), userInfo: nil, repeats: true) 34 | 35 | break; 36 | case ScanSate.RUNNING: 37 | break; 38 | case ScanSate.END: 39 | break; 40 | } 41 | return; 42 | } 43 | 44 | override func viewDidLoad() { 45 | super.viewDidLoad() 46 | 47 | // Do view setup here. 48 | self.titleButton.title = self.barItem!.title 49 | self.functionalLabel.objectValue = self.barItem?.title 50 | self.functionalDescrption.objectValue = self.barItem?.desc 51 | 52 | } 53 | 54 | func processFireMethod(timer : NSTimer ){ 55 | let processValue = self.getScanProcessValue() 56 | if ( processValue >= 89.9){ 57 | if let type = barItem?.type { 58 | if type == XGThreatsType.BundleIDHijack { 59 | let process = XGContainerApplicationManager.sharedInstance.getScanProcess() 60 | if process < 100.0 { 61 | return 62 | } 63 | } 64 | } 65 | self.scanState = ScanSate.END 66 | } else { 67 | self.setScanProcessValue(processValue + 15.0) 68 | } 69 | 70 | if(self.scanState == ScanSate.END) { 71 | timer.invalidate(); 72 | self.stopScan() 73 | 74 | } 75 | return; 76 | } 77 | 78 | @IBOutlet weak var scanProcess: NSProgressIndicator! 79 | @IBOutlet weak var scanPromt: NSTextField! 80 | 81 | 82 | 83 | func startScan() { 84 | self.scanState = ScanSate.RUNNING 85 | self.scanPromt.hidden = false; 86 | self.scanButton.state = NSOffState 87 | self.scanProcess.displayedWhenStopped = false 88 | //self.scanProcess.hidden = false; 89 | self.scanProcess.startAnimation(self) 90 | self.scanProcess.doubleValue = 0.0 91 | 92 | //scan 93 | if let type = barItem?.type { 94 | switch type{ 95 | case XGThreatsType.ALL: 96 | XGKeychainInstance.scanAllItem() 97 | XGContainerApplicationManager.sharedInstance.scan() 98 | case XGThreatsType.keychainHijack: 99 | XGKeychainInstance.scanAllItem() 100 | case XGThreatsType.BundleIDHijack: 101 | XGContainerApplicationManager.sharedInstance.scan() 102 | default: 103 | break; 104 | } 105 | } 106 | } 107 | 108 | func setScanProcessValue(process: Double) { 109 | self.scanProcess.doubleValue = process 110 | 111 | } 112 | 113 | func getScanProcessValue() -> Double{ 114 | return self.scanProcess.doubleValue 115 | } 116 | 117 | func stopScan() { 118 | //notification to swich 119 | NSNotificationCenter.defaultCenter().postNotificationName(NotificationScanFinish, object: self.barItem) 120 | //self.scanButton.title = "SCAN" 121 | self.scanState = ScanSate.INIT 122 | self.scanButton.state = NSOnState 123 | self.scanPromt.hidden = true 124 | self.scanProcess.doubleValue = 0 125 | self.scanProcess.stopAnimation(self) 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /XGuardian/XGSecurityItemSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGSecurityItemSet.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/6/27. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @objc(XGSecurityItemSet) 12 | class XGSecurityItemSet: NSObject { 13 | 14 | //use XGSecurityItem.key() as keys: 15 | var itemDict:Dictionary = Dictionary(minimumCapacity: 8) 16 | 17 | var count: Int { get { return self.itemDict.count}} 18 | var isEmpty : Bool { get { 19 | return self.itemDict.isEmpty 20 | } } 21 | 22 | func addItem( item: XGSecurityItem?) { 23 | if let realItem = item { 24 | self.itemDict[realItem.key()] = realItem 25 | } 26 | } 27 | 28 | func removeItem(item : XGSecurityItem) { 29 | self.itemDict.removeValueForKey(item.key()) 30 | } 31 | 32 | func removeAll() { 33 | self.itemDict.removeAll(keepCapacity: true) 34 | } 35 | 36 | @objc(findItem:) 37 | func findItem(item : XGSecurityItem) -> XGSecurityItem? { 38 | return self.itemDict[item.key()] 39 | } 40 | 41 | func toArray() -> [XGSecurityItem] { 42 | 43 | var valueArray = [XGSecurityItem]() 44 | 45 | for item in self.itemDict.values { 46 | valueArray.append(item) 47 | } 48 | return valueArray; 49 | } 50 | 51 | func getPotentialArray() -> [XGSecurityItem] { 52 | 53 | var valueArray = [XGSecurityItem]() 54 | 55 | for item in self.itemDict.values { 56 | //valid checking 57 | if(item.isLikely()){ 58 | 59 | valueArray.append(item) 60 | } 61 | } 62 | return valueArray; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /XGuardian/XGSideBarCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGSideBarCellView.swift 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/8. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGSideBarCellView: NSTableCellView { 12 | 13 | @IBOutlet var indicatorButton: NSButton! 14 | 15 | override func drawRect(dirtyRect: NSRect) { 16 | super.drawRect(dirtyRect) 17 | 18 | // Drawing code here. 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /XGuardian/XGThirdMacApplicationInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGThirdMacApplicationInfo.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/16. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class XGThirdMacApplicationInfo: NSObject { 13 | var fullPath : String = "" 14 | var bundleID : String = "" 15 | var subApplications: [XGThirdMacApplicationInfo]? 16 | 17 | init?(fullPath : String) { 18 | super.init() 19 | 20 | if let bundleID = XGUtilize.getBundleIDFromPath(fullPath) { 21 | self.fullPath = fullPath 22 | self.bundleID = bundleID 23 | } else { 24 | return nil 25 | } 26 | } 27 | 28 | func addSubApp(subApp : XGThirdMacApplicationInfo) { 29 | if nil == self.subApplications { 30 | self.subApplications = [XGThirdMacApplicationInfo]() 31 | } 32 | self.subApplications?.append(subApp) 33 | } 34 | 35 | func getSubApps() -> [XGThirdMacApplicationInfo]? { 36 | return self.subApplications 37 | } 38 | 39 | override var description : String { 40 | var desc = "Application: \(self.fullPath)\nBundleID: \(self.bundleID)\n" 41 | if let subApplications = self.subApplications { 42 | desc += "Sub Applications:\n" 43 | for sub in subApplications { 44 | desc += " \(sub.description)\n" 45 | } 46 | } 47 | 48 | return desc 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /XGuardian/XGThreatsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGThreatsView.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/7. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGThreatsView: NSView{ 12 | 13 | 14 | override func drawRect(dirtyRect: NSRect) { 15 | super.drawRect(dirtyRect) 16 | 17 | // Drawing code here. 18 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 19 | bezierPath.lineWidth = 1.0 20 | NSColor.whiteColor().set() 21 | bezierPath.fill() 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /XGuardian/XGThreatsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGThreatsViewController.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/7. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | @objc protocol XGThreatsViewDelegate { //class only 13 | 14 | static func getInstance() -> XGThreatsViewDelegate 15 | var title : String { get } 16 | optional func addNotificationObserver() 17 | optional func removeNotificationObserver() 18 | 19 | func refreshThreatsData() -> Int 20 | 21 | 22 | func childrenForItem(item: AnyObject?) -> [AnyObject]? 23 | func isExpandable(item: AnyObject?) -> Bool 24 | optional func isSelectable(item: AnyObject?) -> Bool 25 | func setCellView(cellView : NSTableCellView, item: AnyObject, parent : AnyObject? ) 26 | 27 | var threatsNumber : Int { get } 28 | 29 | func detailsView(threatsViewController: XGThreatsViewController, item : AnyObject?, parent : AnyObject? ) -> NSView? 30 | 31 | 32 | } 33 | 34 | 35 | class XGThreatsViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource, NSSplitViewDelegate { 36 | 37 | @IBOutlet weak var threatsListView: NSOutlineView! 38 | @IBOutlet weak var detailView: NSView! 39 | @IBOutlet weak var titleButton: NSButton! 40 | 41 | weak var barItem: XGSideBarItem? 42 | 43 | var threatsDelegate : XGThreatsViewDelegate? 44 | //for current selected threat detail informations view controlller 45 | var currentdetailSubView: NSView? 46 | 47 | 48 | var threatsType = XGThreatsType.None 49 | 50 | override func viewDidLoad() { 51 | super.viewDidLoad() 52 | 53 | //nagivation table view 54 | self.threatsListView.sizeLastColumnToFit() 55 | self.threatsListView.floatsGroupRows = true 56 | 57 | self.refreshThreatsData() 58 | if let threatsDelegate = self.threatsDelegate { 59 | self.titleButton.title = threatsDelegate.title 60 | } 61 | 62 | self.addNotificationObserver() 63 | } 64 | 65 | deinit { 66 | self.removeNotificationObserver() 67 | } 68 | 69 | override func viewDidAppear() { 70 | super.viewDidAppear() 71 | self.refreshThreatsListView() 72 | 73 | // Expand all the root items; disable the expansion animation that normally happens 74 | NSAnimationContext.beginGrouping() 75 | NSAnimationContext.currentContext().duration = NSTimeInterval(0) 76 | self.threatsListView.expandItem(nil, expandChildren: true) 77 | NSAnimationContext.endGrouping() 78 | 79 | //set the first card in our list 80 | self.selectFirstRow() 81 | } 82 | 83 | func getThreatsNum() -> Int { 84 | 85 | if let threatsDelegate = self.threatsDelegate { 86 | return threatsDelegate.threatsNumber 87 | } 88 | return 0 89 | } 90 | 91 | //MARK: private functions 92 | 93 | private func addNotificationObserver() { 94 | 95 | //add notification observer for threats change 96 | self.threatsDelegate?.addNotificationObserver?() 97 | NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("threatsDidChanged:"), name: "XGThreadsChangedNotification", object: nil) 98 | } 99 | 100 | private func removeNotificationObserver() { 101 | self.threatsDelegate?.removeNotificationObserver?() 102 | NSNotificationCenter.defaultCenter().removeObserver(self, name: "XGThreadsChangedNotification", object: nil) 103 | } 104 | 105 | 106 | private func refreshThreatsData() { 107 | self.threatsDelegate?.refreshThreatsData() 108 | } 109 | 110 | private func refreshThreatsListView() { 111 | 112 | //TODO: if at back?? 113 | self.threatsListView.reloadData() 114 | self.selectFirstRow() 115 | } 116 | 117 | 118 | private func selectFirstRow() { 119 | if(self.threatsType == XGThreatsType.ALL) { 120 | self.threatsListView.selectRowIndexes(NSIndexSet(index: 1), byExtendingSelection: false) 121 | }else { 122 | self.threatsListView.selectRowIndexes(NSIndexSet(index: 0), byExtendingSelection: false) 123 | } 124 | } 125 | 126 | 127 | private func childrenForItem(item: AnyObject?) -> [AnyObject]? { 128 | return self.threatsDelegate?.childrenForItem(item) 129 | } 130 | 131 | 132 | //MARK: delegate for outline view 133 | 134 | func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int { 135 | if let childrens = self.childrenForItem(item){ 136 | return childrens.count 137 | } 138 | return 0 139 | } 140 | 141 | //delegate for outline view; get item for index 142 | func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject { 143 | //it must be have data 144 | let array = self.childrenForItem(item)! 145 | return array[index] 146 | } 147 | 148 | //delegate for outline view; expandable 149 | func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool { 150 | return self.threatsDelegate!.isExpandable(item) 151 | } 152 | 153 | 154 | //delegate for outline view; isSelected? 155 | func outlineView(outlineView: NSOutlineView, shouldSelectItem item: AnyObject) -> Bool { 156 | 157 | if let isSelectable = self.threatsDelegate?.isSelectable?(item) { 158 | return isSelectable 159 | } 160 | return true 161 | } 162 | 163 | 164 | //delegate for outline view; row height 165 | // func outlineView(outlineView: NSOutlineView, heightOfRowByItem item: AnyObject) -> CGFloat { 166 | // if nil == outlineView.parentForItem(item) { 167 | // return 17.0 168 | // } 169 | // return 17.0 170 | // } 171 | 172 | 173 | //delegate for outline view 174 | func outlineView(outlineView: NSOutlineView, viewForTableColumn tableColumn: NSTableColumn?, item: AnyObject) -> NSView? { 175 | 176 | let parent: (AnyObject?) = outlineView.parentForItem(item) 177 | 178 | 179 | // For the groups, we just return a regular text view. 180 | if (self.threatsType == XGThreatsType.ALL) && ( parent == nil ) { 181 | if let result = outlineView.makeViewWithIdentifier("HeaderCell", owner: self) as? NSTableCellView { 182 | 183 | self.threatsDelegate?.setCellView(result, item: item, parent: parent) 184 | return result 185 | } 186 | } else { 187 | if let result = outlineView.makeViewWithIdentifier("DataCell", owner: self) as? NSTableCellView { 188 | 189 | self.threatsDelegate?.setCellView(result, item: item, parent: parent) 190 | return result 191 | } 192 | } 193 | return nil 194 | } 195 | 196 | func splitView(splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool { 197 | return false 198 | } 199 | 200 | func outlineViewSelectionDidChange(notification: NSNotification) { 201 | let row = self.threatsListView.selectedRow; 202 | if(row < 0 ) { 203 | return 204 | } 205 | let item: AnyObject? = self.threatsListView.itemAtRow(row) 206 | let parent: (AnyObject?) = self.threatsListView.parentForItem(item) 207 | 208 | if let currentView = self.currentdetailSubView { 209 | currentView.removeFromSuperview() 210 | } 211 | 212 | if let subView = self.threatsDelegate?.detailsView(self, item: item, parent: parent) { 213 | self.detailView.addSubview(subView) 214 | self.currentdetailSubView = subView 215 | } 216 | return; 217 | 218 | } 219 | 220 | //MARK: - 221 | private func refreshThreatsListViewAndSideBar() { 222 | 223 | NSNotificationCenter.defaultCenter().postNotificationName(NotificationRefresh, object: self.barItem) 224 | self.refreshThreatsListView() 225 | } 226 | 227 | func KeychainHijackViewChanged(rescan : Bool) { 228 | //println("KeychainHijackViewChanged ") 229 | if rescan { 230 | NSNotificationCenter.defaultCenter().postNotificationName(NotificationRescan, object: self.barItem) 231 | } else { 232 | self.refreshThreatsListViewAndSideBar() 233 | } 234 | } 235 | 236 | 237 | func threatsDidChanged(notification: NSNotification) { 238 | 239 | let rescan = (notification.object != nil) 240 | 241 | // DO SOMETHING ON THE MAINTHREAD 242 | self.KeychainHijackViewChanged(rescan) 243 | 244 | } 245 | 246 | 247 | } -------------------------------------------------------------------------------- /XGuardian/XGURLSchemeDetailsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGURLSchemeDetailsView.swift 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/23. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGURLSchemeDetailsView: NSView , NSMatrixDelegate { 12 | 13 | @IBOutlet weak var SchemeApplicationsMatrix: NSMatrix! 14 | 15 | weak var upperViewController : XGThreatsViewController? 16 | var scheme : String? 17 | var appFullPaths : [String]? 18 | var preSelectedRow = 0 19 | 20 | @IBAction func matrixAction(sender: AnyObject) { 21 | if self.isChangedRow() { 22 | if let app = appFullPaths?[self.preSelectedRow] { 23 | 24 | XGURLSchemeManager.sharedInstance.setDefaultApplication(scheme!, appFullPath: app) 25 | } 26 | } 27 | } 28 | 29 | override func drawRect(dirtyRect: NSRect) { 30 | super.drawRect(dirtyRect) 31 | 32 | // Drawing code here. 33 | let bezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 0, yRadius: 0) 34 | bezierPath.lineWidth = 1.0 35 | NSColor.whiteColor().set() 36 | bezierPath.fill() 37 | } 38 | 39 | override func viewDidMoveToWindow() { 40 | super.viewDidMoveToWindow() 41 | 42 | 43 | self.SchemeApplicationsMatrix.delegate = self 44 | self.SchemeApplicationsMatrix.tabKeyTraversesCells = false 45 | 46 | self.setAppRaido() 47 | 48 | } 49 | 50 | private func setRow(row : Int) { 51 | if ( self.SchemeApplicationsMatrix.selectedRow != row) { 52 | self.SchemeApplicationsMatrix.selectCellAtRow(row, column:0) 53 | } 54 | self.preSelectedRow = row 55 | } 56 | 57 | private func isChangedRow() -> Bool { 58 | if ( self.SchemeApplicationsMatrix.selectedRow != self.preSelectedRow) { 59 | self.preSelectedRow = self.SchemeApplicationsMatrix.selectedRow 60 | return true 61 | } 62 | return false 63 | } 64 | 65 | 66 | private func setAppRaido() { 67 | 68 | if let appFullPaths = self.appFullPaths { 69 | for (var i = 1; i < appFullPaths.count; i++) { 70 | self.SchemeApplicationsMatrix.addRow() 71 | } 72 | 73 | let defaultApp = XGURLSchemeManager.sharedInstance.getDefaultApplication(self.scheme!) 74 | let cellArray = self.SchemeApplicationsMatrix.cells 75 | for (var i = 0; i < appFullPaths.count; i++) { 76 | let cell = cellArray[i] as! NSButtonCell 77 | 78 | cell.title = (appFullPaths[i] as NSString).lastPathComponent 79 | if defaultApp == appFullPaths[i] { 80 | self.setRow(i) 81 | } 82 | } 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /XGuardian/XGURLSchemeHijackDetailsView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /XGuardian/XGURLSchemeListDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGURLSchemeListDelegate.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGURLSchemeListDelegate: XGURLSchemeThreatsDelegate { 12 | 13 | static let sharedListInstance = XGURLSchemeListDelegate() 14 | static func getListInstance() -> XGThreatsViewDelegate { 15 | return sharedListInstance 16 | } 17 | 18 | //private weak var URLSchemeDict : XGURLSchemeDict? 19 | 20 | override func refreshThreatsData() -> Int { 21 | self.URLSchemeDict = XGURLSchemeManager.sharedInstance.urlSchemeApplications 22 | 23 | if let count = self.URLSchemeDict?.dataDict.count { 24 | return count 25 | } 26 | return 0 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /XGuardian/XGURLSchemeManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGURLSchemeManager.swift 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/22. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class XGApplicationURLScheme { 12 | let appFullPath : String 13 | let urlSchemes : [String] 14 | 15 | init(appFullPath : String , urlSchemes : [String] ) { 16 | self.appFullPath = appFullPath 17 | self.urlSchemes = urlSchemes 18 | } 19 | } 20 | 21 | class XGURLSchemeDict:NSObject { 22 | var dataDict = [String:[String]]() 23 | } 24 | 25 | class XGURLSchemeManager: NSObject { 26 | 27 | static let sharedInstance = XGURLSchemeManager() 28 | 29 | var urlSchemeApplications = XGURLSchemeDict() 30 | var urlSchemeThreatsDict = XGURLSchemeDict() 31 | 32 | private static let commonSchemeList = ["http", 33 | "https", 34 | "ftp", 35 | "sftp", 36 | "rtsp", 37 | "file", 38 | "ssh", 39 | "mailto", 40 | "sms", 41 | "tel", 42 | "telnet", 43 | "x-man-page", //open manul page 44 | ] 45 | 46 | private static let privateSchemeList = ["qq", 47 | "tencent" //...and so on 48 | ] 49 | 50 | private func addSchemeApplication(scheme : String, app : String) { 51 | if urlSchemeApplications.dataDict[scheme] == nil { 52 | urlSchemeApplications.dataDict[scheme] = [app] 53 | } else { 54 | urlSchemeApplications.dataDict[scheme]?.append(app); 55 | } 56 | } 57 | 58 | func scan() { 59 | 60 | self.urlSchemeApplications.dataDict.removeAll(keepCapacity: true) 61 | 62 | //get all applications 63 | let allApplications = XGUtilize.getApplications([NSSearchPathDomainMask.SystemDomainMask, NSSearchPathDomainMask.UserDomainMask, NSSearchPathDomainMask.LocalDomainMask]) 64 | 65 | //get all applications URL scheme, and put in dictionary 66 | for app in allApplications { 67 | if let schemes = XGUtilize.getURLSchemeFromURL(app) { 68 | for scheme in schemes { 69 | self.addSchemeApplication(scheme, app: app.path!) 70 | } 71 | } 72 | } 73 | 74 | //scan same URL scheme hijack 75 | for (scheme, apps) in urlSchemeApplications.dataDict { 76 | 77 | //check application number 78 | if apps.count <= 1 { 79 | continue 80 | } 81 | 82 | //check common shcheme 83 | if XGURLSchemeManager.commonSchemeList.contains(scheme) { 84 | continue 85 | } 86 | self.urlSchemeThreatsDict.dataDict[scheme] = apps 87 | } 88 | 89 | } 90 | 91 | 92 | func getDefaultApplication(scheme : String ) -> String? { 93 | if let url = NSURL(scheme: scheme, host: nil, path: "/a") { 94 | if let appURL = NSWorkspace.sharedWorkspace().URLForApplicationToOpenURL(url) { 95 | return appURL.path 96 | } 97 | } 98 | return nil 99 | } 100 | 101 | func setDefaultApplication(scheme: String , appFullPath : String ) -> Bool { 102 | if let bundle = XGUtilize.getBundleIDFromPath(appFullPath) { 103 | let status = LSSetDefaultHandlerForURLScheme(scheme as CFString, bundle) 104 | if status != errSecSuccess { 105 | NSLog("set URL scheme: \(scheme) default application to \(appFullPath) error: \(status)") 106 | } else { 107 | return true 108 | } 109 | 110 | } 111 | 112 | return false 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /XGuardian/XGURLSchemeThreatsDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGURLSchemeThreatsDelegate.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/26. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | class XGURLSchemeThreatsDelegate:NSObject, XGThreatsViewDelegate { 13 | 14 | static let sharedInstance = XGURLSchemeThreatsDelegate() 15 | static func getInstance() -> XGThreatsViewDelegate { 16 | return sharedInstance 17 | } 18 | 19 | weak var URLSchemeDict : XGURLSchemeDict? 20 | 21 | //MARK: threats view delegate 22 | var title : String { get { 23 | return "URL Scheme" 24 | }} 25 | 26 | // optional func addNotificationObserver() 27 | // optional func removeNotificationObserver() 28 | 29 | func refreshThreatsData() -> Int { 30 | self.URLSchemeDict = XGURLSchemeManager.sharedInstance.urlSchemeThreatsDict 31 | 32 | if let count = self.URLSchemeDict?.dataDict.count { 33 | return count 34 | } 35 | return 0 36 | } 37 | 38 | func isExpandable(item: AnyObject?) -> Bool { 39 | return false 40 | } 41 | 42 | func childrenForItem(item: AnyObject?) -> [AnyObject]? { 43 | if nil != item { 44 | return nil 45 | } 46 | 47 | if let schemeStringArray = self.URLSchemeDict?.dataDict.keys.sort({$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending}) { 48 | return schemeStringArray as [NSString] 49 | } 50 | return nil 51 | } 52 | 53 | func setCellView(cellView : NSTableCellView, item: AnyObject, parent : AnyObject? ) { 54 | cellView.textField?.objectValue = item // + "://" 55 | cellView.imageView?.hidden = true 56 | } 57 | 58 | var threatsNumber : Int { get { 59 | if let count = self.URLSchemeDict?.dataDict.count { 60 | return count 61 | } 62 | return 0 63 | } } 64 | 65 | func detailsView( threatsViewController : XGThreatsViewController, item : AnyObject? , parent : AnyObject? ) -> NSView? { 66 | if let scheme = item as? NSString { 67 | let currentdetailViewController = NSViewController(nibName: "XGURLSchemeHijackDetailsView", bundle: nil) 68 | 69 | if let view = currentdetailViewController?.view as? XGURLSchemeDetailsView{ 70 | view.scheme = scheme as String 71 | view.appFullPaths = self.URLSchemeDict?.dataDict[scheme as String] 72 | view.upperViewController = threatsViewController 73 | return view 74 | } 75 | } 76 | return nil 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /XGuardian/XGUpdatePanel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGUpdatePanel.swift 3 | // XGuardian 4 | // 5 | // Created by WuYadong on 15/7/1. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | private var updatePanel : XGUpdatePanel? 12 | 13 | class XGUpdatePanel: NSWindowController , NSWindowDelegate { 14 | 15 | weak var versionInfo : XGVersionInfo? 16 | 17 | @IBOutlet weak var versionText: NSTextField! 18 | 19 | 20 | @IBAction func btnUpdateCancel(sender: AnyObject) { 21 | self.window?.performClose(sender); 22 | } 23 | 24 | @IBAction func btnUpdateDownload(sender: AnyObject) { 25 | XGBackend.downloadLastedVersion() 26 | self.btnUpdateCancel(sender) 27 | } 28 | 29 | override func windowDidLoad() { 30 | super.windowDidLoad() 31 | 32 | //set window title 33 | self.window?.titlebarAppearsTransparent = true 34 | self.window?.movableByWindowBackground = true 35 | self.window?.titleVisibility = NSWindowTitleVisibility.Hidden 36 | self.window?.styleMask |= NSFullSizeContentViewWindowMask; 37 | 38 | self.window?.delegate = self 39 | 40 | self.versionInfo = XGBackend.getLastedverion() 41 | var versionStr = "" 42 | if(self.versionInfo != nil && self.versionInfo?.version != nil) { 43 | versionStr += "The lasted version: " + self.versionInfo!.version! + "\n" 44 | } 45 | if(self.versionInfo != nil && self.versionInfo?.changeLog != nil) { 46 | versionStr += "ChangeLog: \n" + self.versionInfo!.changeLog! 47 | } 48 | 49 | self.versionText.objectValue = versionStr 50 | 51 | } 52 | 53 | class func panelShow() { 54 | if (nil == updatePanel) { 55 | updatePanel = XGUpdatePanel(windowNibName: "UpdateWindow") 56 | if(nil == updatePanel) { 57 | return 58 | } 59 | } 60 | updatePanel!.showWindow(nil) 61 | } 62 | 63 | func windowShouldClose(sender: AnyObject) -> Bool { 64 | return true 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /XGuardian/XGUtilize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGUtilize.swift 3 | // XGuardian 4 | // 5 | // Created by 吴亚冬 on 15/7/16. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | enum XGSecurityAppType: Int ,CustomStringConvertible{ 13 | case Unknown = 0 14 | case WhiteList = 1 15 | case Sining = 2 16 | case Group = 3 17 | case Apple = 4 18 | 19 | internal var description: String { get { 20 | switch self { 21 | case Unknown: return "Unknown" 22 | case WhiteList: return "WhiteList" 23 | case Sining: return "Sining" 24 | case Group: return "Group" 25 | case Apple: return "Apple" 26 | } 27 | }} 28 | } 29 | 30 | 31 | 32 | class XGUtilize: NSObject { 33 | 34 | class func getSubApplications(url : NSURL ) -> [NSURL] { 35 | 36 | var subAppURL = [NSURL]() 37 | 38 | var filesURL = [url] 39 | while ( filesURL.count > 0) { 40 | let url = filesURL.removeLast() 41 | 42 | let subFiles = try? NSFileManager.defaultManager().contentsOfDirectoryAtURL(url, 43 | includingPropertiesForKeys: [NSURLIsDirectoryKey], 44 | options: NSDirectoryEnumerationOptions.SkipsHiddenFiles) 45 | 46 | if(nil == subFiles || subFiles?.count == 0) { 47 | continue 48 | } 49 | 50 | for subFile in subFiles! { 51 | if let pathExtension = subFile.pathExtension { 52 | if (pathExtension == "app" || pathExtension == "xpc"){ 53 | subAppURL.append(subFile) 54 | } else { 55 | filesURL.append(subFile) 56 | } 57 | } 58 | } 59 | } 60 | return subAppURL 61 | } 62 | 63 | class func getSystemApplicationsURL() -> NSURL { 64 | let urls = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.ApplicationDirectory, 65 | inDomains:NSSearchPathDomainMask.SystemDomainMask) 66 | let url = urls[0] 67 | return url 68 | } 69 | 70 | class func getApplications(domainMask: NSSearchPathDomainMask) -> [NSURL] { 71 | 72 | var applicationsURLAarry = [NSURL]() 73 | let urls = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.ApplicationDirectory, 74 | inDomains:domainMask) 75 | 76 | for url in urls { 77 | let applicationArray = self.getSubApplications(url) 78 | if(0 != applicationArray.count) { 79 | applicationsURLAarry += applicationArray 80 | } 81 | } 82 | return applicationsURLAarry 83 | } 84 | 85 | /// get application's bundle ID from applicaion's URL 86 | class func getBundleIDFromURL(appURL : NSURL) -> String? { 87 | 88 | var secStaticCode : SecStaticCode? 89 | let cfUrl = appURL as CFURL 90 | var status = SecStaticCodeCreateWithPath(cfUrl , SecCSFlags.DefaultFlags /*0*/, &secStaticCode) 91 | if status != errSecSuccess || secStaticCode == nil { 92 | NSLog("BundleID From URL:\(appURL) SecStaticCodeCreateWithPath error:\(status)") 93 | return nil 94 | } 95 | 96 | var dictRef : CFDictionary? 97 | status = SecCodeCopySigningInformation(secStaticCode!, SecCSFlags(rawValue: kSecCSSigningInformation), &dictRef ) 98 | if status != errSecSuccess || dictRef == nil { 99 | NSLog("BundleID From URL:\(appURL) SecCodeCopySigningInformation error:\(status)") 100 | return nil 101 | } 102 | 103 | let signingInfoDict = dictRef! as NSDictionary 104 | 105 | if let identifier = signingInfoDict[kSecCodeInfoIdentifier as NSString] as? String { 106 | return identifier 107 | } 108 | 109 | return nil 110 | } 111 | 112 | /// get application's bundle ID from applicaion's full path 113 | class func getBundleIDFromPath(appFullPath : String) -> String? { 114 | let url = NSURL(fileURLWithPath: appFullPath) 115 | return self.getBundleIDFromURL(url) 116 | } 117 | 118 | // 119 | class func getURLSchemeFromURL(appURL : NSURL) -> [String]? { 120 | var secStaticCode : SecStaticCode? 121 | let cfUrl = appURL as CFURL; 122 | 123 | var status = SecStaticCodeCreateWithPath(cfUrl , SecCSFlags.DefaultFlags /*0*/, &secStaticCode) 124 | if status != errSecSuccess || secStaticCode == nil { 125 | return nil 126 | } 127 | 128 | 129 | var dictRef : CFDictionary? 130 | status = SecCodeCopySigningInformation(secStaticCode!, SecCSFlags(rawValue: kSecCSSigningInformation), &dictRef ) 131 | if status != errSecSuccess || dictRef == nil { 132 | return nil 133 | } 134 | 135 | let signingInfoDict = dictRef! as NSDictionary; 136 | 137 | var urlSchemes = [String]() 138 | if let infoPList = signingInfoDict[kSecCodeInfoPList as NSString] as? NSDictionary { 139 | if let urlTypes = infoPList["CFBundleURLTypes"] as? [NSDictionary] { 140 | //print(urlTypes) 141 | for urlType in urlTypes { 142 | if let urls = urlType["CFBundleURLSchemes"] as? [String] { 143 | urlSchemes += urls 144 | } 145 | } 146 | } 147 | } 148 | 149 | if urlSchemes.count > 0 { 150 | return urlSchemes 151 | } 152 | return nil 153 | } 154 | 155 | class func getURLSchemeFromPath(appFullPath : String) -> [String]? { 156 | let url = NSURL(fileURLWithPath: appFullPath) 157 | return self.getURLSchemeFromURL(url) 158 | } 159 | 160 | 161 | private static let appWhiteList = ["com.google.Chrome", 162 | "com.operasoftware.Opera", 163 | "com.dashlane.Dashlane"] //"com.agilebits.onepassword4" 164 | 165 | class func checkWhiteList(bundleID : String) -> Bool { 166 | return appWhiteList.contains(bundleID) 167 | 168 | } 169 | 170 | class func checkApple(appFullPath: String) -> XGSecurityAppType { 171 | 172 | if appFullPath.hasPrefix("group:") { 173 | return XGSecurityAppType.Group 174 | } 175 | 176 | //TODO : change String compare 177 | let ia = "InternetAccounts" 178 | if appFullPath == ia { 179 | return XGSecurityAppType.Apple 180 | } 181 | 182 | if let identifier = XGUtilize.getBundleIDFromPath(appFullPath){ 183 | if identifier.hasPrefix("com.apple") { 184 | return XGSecurityAppType.Apple 185 | } else if checkWhiteList(identifier) { 186 | return XGSecurityAppType.WhiteList 187 | } 188 | else { 189 | return XGSecurityAppType.Sining 190 | } 191 | } 192 | 193 | return XGSecurityAppType.Unknown 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /XGuardian/XGuardian-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "CXKeychainHelper.h" 6 | #import "XGKeychainObserver.h" 7 | #import "XGFileSecurityHelper.h" 8 | #import "XGFileEventsHelper.h" -------------------------------------------------------------------------------- /XGuardian/XGuardian.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /XGuardianTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /XGuardianTests/XGuardianTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XGuardianTests.swift 3 | // XGuardianTests 4 | // 5 | // Created by 吴亚冬 on 15/6/29. 6 | // Copyright (c) 2015年 杭州网蛙科技. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | 12 | class XGuardianTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------