diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..fd35981 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,60 @@ +version: 2 + +aliases: + - &install-bundle-dependencies + | + bundle install + +base_job: &base_job + macos: + xcode: "10.0.0" + shell: /bin/bash --login -eo pipefail + environment: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + +jobs: + build-and-test: + <<: *base_job + + steps: + - checkout + + - run: + name: Chruby + command: chruby + + - run: + name: Install bundled gems + command: *install-bundle-dependencies + + - run: + name: Run tests + command: bundle exec fastlane tests --verbose + + - run: + name: Danger + command: bundle exec danger + when: always + + - run: + name: Generate test results + command: cp fastlane/test_output/report.junit fastlane/test_output/results.xml + when: always + + - store_test_results: + path: fastlane/test_output + + - store_artifacts: + path: fastlane/test_output + destination: scan-test-results + + - store_artifacts: + path: ~/Library/Logs/scan + destination: scan-logs + +workflows: + version: 2 + build: + jobs: + - build-and-test diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index dab9cd0..e65783e --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,55 @@ +## macOS +.DS_Store +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint *.profraw +*.xcuserdata +*.xcuserstate + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM +*.orig + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +.build/ + +# CocoaPods +Pods/ + +# Carthage +Carthage/Build + +# fastlane +fastlane/README.md +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/* +fastlane/test_output/* + +# CircleCI +vendor/* diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..7ac83b8 --- /dev/null +++ b/Gemfile @@ -0,0 +1,10 @@ +source 'https://rubygems.org' + +gem 'synx' +gem 'fastlane' + +# Danger +gem 'xcpretty' +gem 'xcpretty-json-formatter' +gem 'danger' +gem 'danger-xcode_summary' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..def4501 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,195 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + atomos (0.1.3) + babosa (1.0.2) + claide (1.0.2) + claide-plugins (0.9.2) + cork + nap + open4 (~> 1.3) + clamp (0.6.5) + colored (1.2) + colored2 (3.1.2) + colorize (0.8.1) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + cork (0.3.0) + colored2 (~> 3.1) + danger (5.6.7) + claide (~> 1.0) + claide-plugins (>= 0.9.2) + colored2 (~> 3.1) + cork (~> 0.1) + faraday (~> 0.9) + faraday-http-cache (~> 1.0) + git (~> 1.5) + kramdown (~> 1.5) + no_proxy_fix + octokit (~> 4.7) + terminal-table (~> 1) + danger-plugin-api (1.0.0) + danger (> 2.0) + danger-xcode_summary (0.5.0) + danger-plugin-api (~> 1.0) + declarative (0.0.10) + declarative-option (0.1.0) + domain_name (0.5.20180417) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.5.0) + emoji_regex (0.1.1) + excon (0.62.0) + faraday (0.15.3) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.6) + faraday (>= 0.7.4) + http-cookie (~> 1.0.0) + faraday-http-cache (1.3.1) + faraday (~> 0.8) + faraday_middleware (0.12.2) + faraday (>= 0.7.4, < 1.0) + fastimage (2.1.4) + fastlane (2.104.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 2.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (~> 0.1) + excon (>= 0.45.0, < 1.0.0) + faraday (~> 0.9) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.9) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.21.2, < 0.24.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + mini_magick (~> 4.5.1) + multi_json + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.2.2, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 1.6.2, < 2.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.6.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + gh_inspector (1.1.3) + git (1.5.0) + google-api-client (0.23.9) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.5, < 0.7.0) + httpclient (>= 2.8.1, < 3.0) + mime-types (~> 3.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.9) + googleauth (0.6.6) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.12) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.7) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + json (2.1.0) + jwt (2.1.0) + kramdown (1.17.0) + memoist (0.16.0) + mime-types (3.2.2) + mime-types-data (~> 3.2015) + mime-types-data (3.2018.0812) + mini_magick (4.5.1) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.2.6) + nap (1.1.0) + naturally (2.2.0) + no_proxy_fix (0.1.2) + octokit (4.12.0) + sawyer (~> 0.8.0, >= 0.5.3) + open4 (1.3.4) + os (1.0.0) + plist (3.4.0) + public_suffix (2.0.5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rouge (2.0.7) + rubyzip (1.2.2) + sawyer (0.8.1) + addressable (>= 2.3.5, < 2.6) + faraday (~> 0.8, < 1.0) + security (0.1.3) + signet (0.9.1) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.5) + CFPropertyList + naturally + slack-notifier (2.3.2) + synx (0.2.1) + clamp (~> 0.6) + colorize (~> 0.7) + xcodeproj (~> 1.0) + terminal-notifier (1.8.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + tty-cursor (0.6.0) + tty-screen (0.6.5) + tty-spinner (0.8.0) + tty-cursor (>= 0.5.0) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.5) + unicode-display_width (1.4.0) + word_wrap (1.0.0) + xcodeproj (1.6.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-json-formatter (0.1.1) + xcpretty (~> 0.2, >= 0.0.7) + xcpretty-travis-formatter (1.0.0) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + danger + danger-xcode_summary + fastlane + synx + xcpretty + xcpretty-json-formatter + +BUNDLED WITH + 1.16.5 diff --git a/README.md b/README.md index 9a34b6d..1f092d3 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,8 @@ swiftshield -automatic -project-root /app/MyApp -automatic-project-file /app/MyA - `-ignore-modules`: Prevent certain modules from being obfuscated, separated by a comma. Use this if a certain module can't be properly obfuscated. Note that this should be the exact name of the imported module (not the target name!). Example: `MyLib,MyAppRichNotifications,MyAppWatch_Extension` -- `-verbose`: Prints additional information. - - `-show-sourcekit-queries`: Prints queries sent to SourceKit. Note that they are huge and will absolutely clutter your terminal, so use this only for bug reports and feature development! -- `-obfuscation-character-count`: Set the number of characters that obfuscated names will have. By default, this is `32`. Be aware that using a small number will result in slower runs due to the higher possibility of name collisions. - ### Manual mode ``` @@ -133,10 +129,14 @@ swiftshield -project-root /app/MyApp - `-tag`: Uses a custom tag. Default is `__s`. +### Aditional parameters for both modes + - `-verbose`: Prints additional information. - `-obfuscation-character-count`: Set the number of characters that obfuscated names will have. By default, this is `32`. Be aware that using a small number will result in slower runs due to the higher possibility of name collisions. +- `-dry-run`: Does not actually overwrite the files. Useful for debugging! + ## 🤝 Contributing @@ -146,9 +146,10 @@ Contributions to SwiftShield are more than welcome! There's no contribution guid ## ✅ Automatic Mode Next Steps - [X] Method names +- [X] Update Extension plists (Rich Notifications / Watch main classes) +- [ ] "SDK Mode" (ignore `public`) - [ ] Properties - [ ] Module names -- [ ] Update Extension plists (Rich Notifications / Watch main classes) ## License @@ -158,4 +159,4 @@ SwiftShield is released under the GNU GPL v3.0 license. See LICENSE for details. ## Thanks -Thanks to John Holdsworth from [Refactorator](https://github.com/johnno1962/Refactorator) for `SourceKit.swift`, and to SourceKitten for helping me figure out which compile arguments to ignore for SourceKit. +Thanks to John Holdsworth from [Refactorator](https://github.com/johnno1962/Refactorator) for `SourceKit.swift`, Apple for their open-source SourceKit wrapper and to SourceKitten for helping me figure out which compile arguments to ignore for SourceKit. diff --git a/SOURCEKITISSUES.md b/SOURCEKITISSUES.md index 97d6ba3..147bd36 100644 --- a/SOURCEKITISSUES.md +++ b/SOURCEKITISSUES.md @@ -17,5 +17,5 @@ The following types and cases might be working correctly in SourceKit, but are c - Typealiases and Associated Types: Not always indexed (`typealias Foo = UIImage | extension Foo {}` - Foo is ignored and indexed as UIImage). Note that these can't be reverse-engineered as they are purely an editor thing, so no action is required! - Enum cases and names: Although they are correctly indexed, some enums like `CodingKeys` are not meant to be changed. This will be activated again once the way to determine if an enum is related to internal frameworks is implemented. - **IN-REVIEW**: Methods with names under four characters: Operators only get indexed as such if they are declared in a global scope. Since most people use `public static func`, they get indexed as regular methods. To prevent operators from being obfuscated, methods with names shorter than four characters won't get obfuscated. -- Properties: Not implemented yet! +- Properties: Properties are on hold for a while because they break derived `Codable` types. Although the obfuscation works correctly, if you build `Codable` types to work on top of a backend's json, parsing will fail because of the different property name. - Module names: Not implemented yet! diff --git a/SwiftShield.podspec b/SwiftShield.podspec index 38f10d6..231688d 100644 --- a/SwiftShield.podspec +++ b/SwiftShield.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'SwiftShield' s.module_name = 'SwiftShield' - s.version = '3.3.0' + s.version = '3.3.1' s.license = { type: 'GNU GPL v3.0', file: 'LICENSE.md' } s.summary = 'A tool that protects Swift iOS apps against class-dump attacks.' s.homepage = 'https://github.com/rockbruno/swiftshield' diff --git a/SwiftShieldExample/SwiftShieldExample.xcodeproj/xcuserdata/bruno.rocha.xcuserdatad/xcschemes/xcschememanagement.plist b/SwiftShieldExample/SwiftShieldExample.xcodeproj/xcuserdata/bruno.rocha.xcuserdatad/xcschemes/xcschememanagement.plist index 33c009a..f91b608 100644 --- a/SwiftShieldExample/SwiftShieldExample.xcodeproj/xcuserdata/bruno.rocha.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/SwiftShieldExample/SwiftShieldExample.xcodeproj/xcuserdata/bruno.rocha.xcuserdatad/xcschemes/xcschememanagement.plist @@ -9,6 +9,11 @@ orderHint 3 + SwiftShieldExample.xcscheme_^#shared#^_ + + orderHint + 3 + diff --git a/SwiftShieldExample/SwiftShieldExample.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate b/SwiftShieldExample/SwiftShieldExample.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate index 3fade8b..c5899ca 100644 Binary files a/SwiftShieldExample/SwiftShieldExample.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate and b/SwiftShieldExample/SwiftShieldExample.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SwiftShieldExample/SwiftShieldExample/MyViewController.swift b/SwiftShieldExample/SwiftShieldExample/MyViewController.swift index 365357a..216f814 100644 --- a/SwiftShieldExample/SwiftShieldExample/MyViewController.swift +++ b/SwiftShieldExample/SwiftShieldExample/MyViewController.swift @@ -5,6 +5,16 @@ import Unbox final class MyView: UILabel {} class MyViewController: UIViewController { + +// Properties disabled due to Codable issues. +// let myLet = 1 +// var myProp = 1 +// class var myClassVar: Int { +// return 10 +// } +// static let myStaticLet = 1 +// static var myStaticVar = 1 + struct Foo { func barbar(view: MyView) -> MyViewController { print(view) @@ -14,6 +24,8 @@ class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() +// myProp = myLet * 10 +// MyViewController.myStaticVar = MyViewController.myClassVar + MyViewController.myStaticLet * 5 render() } diff --git a/SwiftShieldTests/AutomaticSwiftShieldTests/AutomaticSwiftShieldTests.swift b/SwiftShieldTests/AutomaticSwiftShieldTests/AutomaticSwiftShieldTests.swift index dc22203..fb2c927 100644 --- a/SwiftShieldTests/AutomaticSwiftShieldTests/AutomaticSwiftShieldTests.swift +++ b/SwiftShieldTests/AutomaticSwiftShieldTests/AutomaticSwiftShieldTests.swift @@ -29,14 +29,14 @@ class AutomaticSwiftShieldTests: XCTestCase { ReferenceData(name: "fakeMethod", line: 10, column: 30)] let originalFileData = loadFile("MockOriginalFile", ofType: "txt") let originalFile = String(data: originalFileData, encoding: .utf8)! - let obfuscatedFile = AutomaticSwiftShield(basePath: "abc", projectToBuild: "abc", schemeToBuild: "abc", modulesToIgnore: [], protectedClassNameSize: 0).generateObfuscatedFile(fromString: originalFile, references: references, obfuscationData: obfuscationData) + let obfuscatedFile = AutomaticSwiftShield(basePath: "abc", projectToBuild: "abc", schemeToBuild: "abc", modulesToIgnore: [], protectedClassNameSize: 0, dryRun: true).generateObfuscatedFile(fromString: originalFile, references: references, obfuscationData: obfuscationData) let expectedFileData = loadFile("MockObfuscatedFile", ofType: "txt") let expectedFile = String(data: expectedFileData, encoding: .utf8)! XCTAssertEqual(obfuscatedFile, expectedFile) } func testPlistExtractor() { - let protector = AutomaticSwiftShield(basePath: "abc", projectToBuild: "abc", schemeToBuild: "abc", modulesToIgnore: [], protectedClassNameSize: 0) + let protector = AutomaticSwiftShield(basePath: "abc", projectToBuild: "abc", schemeToBuild: "abc", modulesToIgnore: [], protectedClassNameSize: 0, dryRun: true) let plist = path(for: "MockPlist", ofType: "plist") let file = File(filePath: plist) let data = protector.getPlistVersionAndNumber(file)! @@ -45,7 +45,7 @@ class AutomaticSwiftShieldTests: XCTestCase { } func testPlistPrincipalClassObfuscation() { - let protector = AutomaticSwiftShield(basePath: "abc", projectToBuild: "abc", schemeToBuild: "abc", modulesToIgnore: [], protectedClassNameSize: 0) + let protector = AutomaticSwiftShield(basePath: "abc", projectToBuild: "abc", schemeToBuild: "abc", modulesToIgnore: [], protectedClassNameSize: 0, dryRun: false) let plist = path(for: "MockPlist", ofType: "plist") let file = MockFile(path: plist) let obfuscationData = AutomaticObfuscationData(modules: [Module(name: "mock", plists: [file])]) diff --git a/SwiftShieldTests/DeclarationTypeTests.swift b/SwiftShieldTests/DeclarationTypeTests.swift index cce4b33..383c276 100644 --- a/SwiftShieldTests/DeclarationTypeTests.swift +++ b/SwiftShieldTests/DeclarationTypeTests.swift @@ -6,7 +6,7 @@ class DeclarationTypeTests: XCTestCase { let declPrefix = prefix + ".decl." let refPrefix = prefix + ".ref." let sourceKit = SourceKit() - for object in ["class", "struct", "protocol"] { + for object in ["class", "struct"] { let declKind = declPrefix + object XCTAssertEqual(sourceKit.declarationType(for: declKind), .object) XCTAssertEqual(sourceKit.referenceType(kind: declKind), .object) @@ -14,6 +14,14 @@ class DeclarationTypeTests: XCTestCase { XCTAssertEqual(sourceKit.declarationType(for: refKind), nil) XCTAssertEqual(sourceKit.referenceType(kind: refKind), .object) } + for `protocol` in ["protocol"] { + let declKind = declPrefix + `protocol` + XCTAssertEqual(sourceKit.declarationType(for: declKind), .protocol) + XCTAssertEqual(sourceKit.referenceType(kind: declKind), .protocol) + let refKind = refPrefix + `protocol` + XCTAssertEqual(sourceKit.declarationType(for: refKind), nil) + XCTAssertEqual(sourceKit.referenceType(kind: refKind), .protocol) + } for method in ["function.free", "function.method.instance", "function.method.static", "function.method.class"] { let declKind = declPrefix + method XCTAssertEqual(sourceKit.declarationType(for: declKind), .method) @@ -22,7 +30,7 @@ class DeclarationTypeTests: XCTestCase { XCTAssertEqual(sourceKit.declarationType(for: refKind), nil) XCTAssertEqual(sourceKit.referenceType(kind: refKind), .method) } - for property in ["var.instance", "var.class"] { + for property in ["var.instance", "var.static", "var.class"] { let declKind = declPrefix + property XCTAssertEqual(sourceKit.declarationType(for: declKind), nil) XCTAssertEqual(sourceKit.referenceType(kind: declKind), nil) diff --git a/SwiftShieldTests/ManualSwiftShieldTests/ManualSwiftShieldTests.swift b/SwiftShieldTests/ManualSwiftShieldTests/ManualSwiftShieldTests.swift index 83614ef..60286bf 100644 --- a/SwiftShieldTests/ManualSwiftShieldTests/ManualSwiftShieldTests.swift +++ b/SwiftShieldTests/ManualSwiftShieldTests/ManualSwiftShieldTests.swift @@ -10,7 +10,7 @@ import XCTest class ManualSwiftShieldTests: XCTestCase { func testObfuscator() { - let ss = ManualSwiftShield(basePath: "abc", tag: "__s", protectedClassNameSize: 32) + let ss = ManualSwiftShield(basePath: "abc", tag: "__s", protectedClassNameSize: 32, dryRun: true) let obfuscationData = ObfuscationData() obfuscationData.obfuscationDict["ViewController__s"] = "AAAAA" obfuscationData.obfuscationDict["CustomVC__s"] = "BBBBBBBBBBBBBBBBBBB" diff --git a/SwiftShieldTests/StoryboardTests/StoryboardObfuscationTests.swift b/SwiftShieldTests/StoryboardTests/StoryboardObfuscationTests.swift index d901110..0f0fb85 100644 --- a/SwiftShieldTests/StoryboardTests/StoryboardObfuscationTests.swift +++ b/SwiftShieldTests/StoryboardTests/StoryboardObfuscationTests.swift @@ -19,7 +19,7 @@ class StoryboardObfuscationTests: XCTestCase { obfsData.obfuscationDict = self.obfuscationDict var data = loadFile("MockStoryboard", ofType: "txt") var xmlDoc = try! AEXMLDocument(xml: data, options: AEXMLOptions()) - Protector(basePath: "abc").obfuscateIBXML(element: xmlDoc.root, obfuscationData: obfsData) + Protector(basePath: "abc", dryRun: true).obfuscateIBXML(element: xmlDoc.root, obfuscationData: obfsData) data = loadFile("ExpectedMockStoryboard", ofType: "txt") var xmlDoc2 = try! AEXMLDocument(xml: data, options: AEXMLOptions()) XCTAssertEqual(xmlDoc.xml, xmlDoc2.xml) @@ -28,7 +28,7 @@ class StoryboardObfuscationTests: XCTestCase { xmlDoc = try! AEXMLDocument(xml: data, options: AEXMLOptions()) data = loadFile("ExpectedMockXib", ofType: "txt") xmlDoc2 = try! AEXMLDocument(xml: data, options: AEXMLOptions()) - Protector(basePath: "abc").obfuscateIBXML(element: xmlDoc.root, obfuscationData: obfsData) + Protector(basePath: "abc", dryRun: true).obfuscateIBXML(element: xmlDoc.root, obfuscationData: obfsData) XCTAssertEqual(xmlDoc.xml, xmlDoc2.xml) data = loadFile("MockStoryboard", ofType: "txt") @@ -37,7 +37,7 @@ class StoryboardObfuscationTests: XCTestCase { obfsData.obfuscationDict = self.obfuscationDict data = loadFile("ExpectedMockStoryboardIgnoringMainModule", ofType: "txt") xmlDoc2 = try! AEXMLDocument(xml: data, options: AEXMLOptions()) - Protector(basePath: "abc").obfuscateIBXML(element: xmlDoc.root, obfuscationData: obfsData) + Protector(basePath: "abc", dryRun: true).obfuscateIBXML(element: xmlDoc.root, obfuscationData: obfsData) XCTAssertEqual(xmlDoc.xml, xmlDoc2.xml) } } diff --git a/bin/swiftshield b/bin/swiftshield index 3d8e7a2..5141185 100755 Binary files a/bin/swiftshield and b/bin/swiftshield differ diff --git a/fastlane/.env b/fastlane/.env new file mode 100755 index 0000000..be45e65 --- /dev/null +++ b/fastlane/.env @@ -0,0 +1,4 @@ +# XCODE_BUILD_PATH="./build" +# GYM_OUTPUT_DIRECTORY=$XCODE_BUILD_PATH +# GYM_SCHEME=$XCODE_SCHEME +# GYM_CONFIGURATION=$BUILD_CONFIGURATION diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 0000000..a6c13c5 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,12 @@ +default_platform :osx + +platform :osx do + before_all do + setup_circle_ci + end + + desc "Test SwiftShield" + lane :tests do + scan(scheme: "swiftshield", formatter: "xcpretty-json-formatter") + end +end \ No newline at end of file diff --git a/swiftshield-Sources/AutomaticSwiftShield.swift b/swiftshield-Sources/AutomaticSwiftShield.swift index 94c06bb..de86512 100644 --- a/swiftshield-Sources/AutomaticSwiftShield.swift +++ b/swiftshield-Sources/AutomaticSwiftShield.swift @@ -2,6 +2,7 @@ import Foundation class AutomaticSwiftShield: Protector { + let sourceKit: SourceKit let projectToBuild: String let schemeToBuild: String let modulesToIgnore: Set @@ -14,11 +15,14 @@ class AutomaticSwiftShield: Protector { projectToBuild: String, schemeToBuild: String, modulesToIgnore: Set, - protectedClassNameSize: Int) { + protectedClassNameSize: Int, + dryRun: Bool, + sourceKit: SourceKit = .init()) { + self.sourceKit = sourceKit self.projectToBuild = projectToBuild self.schemeToBuild = schemeToBuild self.modulesToIgnore = modulesToIgnore - super.init(basePath: basePath, protectedClassNameSize: protectedClassNameSize) + super.init(basePath: basePath, protectedClassNameSize: protectedClassNameSize, dryRun: dryRun) if self.schemeToBuild.isEmpty || self.projectToBuild.isEmpty { Logger.log(.helpText) exit(error: true) @@ -26,6 +30,10 @@ class AutomaticSwiftShield: Protector { } override func protect() -> ObfuscationData { + SourceKit.start() + defer { + SourceKit.stop() + } guard isWorkspace || projectToBuild.hasSuffix(".xcodeproj") else { Logger.log(.projectError) exit(error: true) @@ -40,12 +48,13 @@ class AutomaticSwiftShield: Protector { exit(error: true) } obfuscateNSPrincipalClassPlists(obfuscationData: obfuscationData) - overwriteFiles(obfuscationData: obfuscationData) + if dryRun == false { + overwriteFiles(obfuscationData: obfuscationData) + } return obfuscationData } func index(obfuscationData: AutomaticObfuscationData) { - let sourceKit = SourceKit() var fileDataArray: [(file: File, module: Module)] = [] for module in obfuscationData.modules { for file in module.sourceFiles { @@ -55,21 +64,19 @@ class AutomaticSwiftShield: Protector { for fileData in fileDataArray { let file = fileData.file let module = fileData.module - let compilerArgs = sourceKit.array(argv: module.compilerArguments) Logger.log(.indexing(file: file)) - let resp = index(sourceKit: sourceKit, file: file, args: compilerArgs) - let dict = SKApi.sourcekitd_response_get_value(resp) - sourceKit.recurseOver(childID: sourceKit.entitiesID, resp: dict) { [unowned self] dict in + let resp = index(file: file, args: module.compilerArguments) + resp.recurseOver(uid: .entitiesId) { [unowned self] variant in + let dict = variant.getDictionary() guard let data = self.getNameData(from: dict, - obfuscationData: obfuscationData, - sourceKit: sourceKit) else { + obfuscationData: obfuscationData) else { return } let name = data.name let usr = data.usr obfuscationData.usrDict.insert(usr) - if dict.getString(key: sourceKit.receiverID) == nil { - obfuscationData.usrRelationDict[usr] = dict + if dict.getString(.receiverId) == nil { + obfuscationData.usrRelationDict[usr] = variant } Logger.log(.foundDeclaration(name: name, usr: usr)) } @@ -91,21 +98,24 @@ class AutomaticSwiftShield: Protector { } extension AutomaticSwiftShield { - private func index(sourceKit: SourceKit, file: File, args: sourcekitd_object_t) -> sourcekitd_response_t { + private func index(file: File, args: [String]) -> SourceKitdResponse { let resp = sourceKit.indexFile(filePath: file.path, compilerArgs: args) - if let error = sourceKit.error(resp: resp) { + if let error = resp.error { Logger.log(.indexError(file: file, error: error)) exit(error: true) } return resp } - private func getNameData(from dict: sourcekitd_variant_t, obfuscationData: ObfuscationData, sourceKit: SourceKit) -> (name: String, usr: String, obfuscatedName: String)? { - let kind = dict.getUUIDString(key: sourceKit.kindID) + private func getNameData(from dict: SourceKitdResponse.Dictionary, + obfuscationData: ObfuscationData) -> (name: String, + usr: String, + obfuscatedName: String)? { + let kind = dict.getUID(.kindId).asString guard sourceKit.declarationType(for: kind) != nil else { return nil } - guard let name = dict.getString(key: sourceKit.nameID)?.trueName, let usr = dict.getString(key: sourceKit.usrID) else { + guard let name = dict.getString(.nameId)?.trueName, let usr = dict.getString(.usrId) else { return nil } guard let protected = obfuscationData.obfuscationDict[name] else { @@ -117,61 +127,76 @@ extension AutomaticSwiftShield { } func findReferencesInIndexed(obfuscationData: AutomaticObfuscationData) { - let SK = SourceKit() Logger.log(.searchingReferencesOfUsr) - for (file, indexResponse) in obfuscationData.indexedFiles { - let dict = SKApi.sourcekitd_response_get_value(indexResponse) - SK.recurseOver(childID: SK.entitiesID, resp: dict, block: { dict in - let kind = dict.getUUIDString(key: SK.kindID) - guard let type = SK.referenceType(kind: kind) else { + for (file, response) in obfuscationData.indexedFiles { + response.recurseOver(uid: .entitiesId) { [unowned self] variant in + let dict = variant.getDictionary() + let kind = dict.getUID(.kindId).asString + guard let type = self.sourceKit.referenceType(kind: kind) else { + return + } + guard let usr = dict.getString(.usrId), let name = dict.getString(.nameId)?.trueName else { return } - guard let usr = dict.getString(key: SK.usrID), let name = dict.getString(key: SK.nameID)?.trueName else { + let line = dict.getInt(.lineId) + let column = dict.getInt(.colId) + guard obfuscationData.usrDict.contains(usr) else { return } - let line = dict.getInt(key: SK.lineID) - let column = dict.getInt(key: SK.colID) - if obfuscationData.usrDict.contains(usr) { - //Operators only get indexed as such if they are declared in a global scope - //Unfortunately, most people use public static func - //So we avoid obfuscating methods with small names to prevent obfuscating operators. - if type == .method && name.count <= 4 { - return - } - guard self.isReferencingInternal(type: type, kind: kind, dict: dict, obfuscationData: obfuscationData, sourceKit: SK) == false else { - return - } - let newName = obfuscationData.obfuscationDict[name] ?? name - Logger.log(.foundReference(name: name, usr: usr, at: file, line: line, column: column, newName: newName)) - let reference = ReferenceData(name: name, line: line, column: column) - obfuscationData.referencesDict[file, default: []].append(reference) + //Operators only get indexed as such if they are declared in a global scope + //Unfortunately, most people use public static func + //So we avoid obfuscating methods with small names to prevent obfuscating operators. + if type == .method && name.count <= 4 { + return + } + guard self.isReferencingInternal(type: type, kind: kind, variant: variant, obfuscationData: obfuscationData) == false else { + return } - }) + let newName = obfuscationData.obfuscationDict[name] ?? name + Logger.log(.foundReference(name: name, + usr: usr, + at: file, + line: line, + column: column, + newName: newName)) + let reference = ReferenceData(name: name, line: line, column: column) + obfuscationData.referencesDict[file, default: []].append(reference) + } } } - private func isReferencingInternal(type: SourceKit.DeclarationType, kind: String, dict: sourcekitd_variant_t, obfuscationData: AutomaticObfuscationData, sourceKit: SourceKit) -> Bool { + private func isReferencingInternal(type: SourceKit.DeclarationType, + kind: String, + variant: SourceKitdResponse.Variant, + obfuscationData: AutomaticObfuscationData) -> Bool { guard type == .method || type == .property else { return false } - guard let usr = dict.getString(key: sourceKit.usrID) else { + guard let usr = variant.getDictionary().getString(.usrId) else { return false } - if let relDict = obfuscationData.usrRelationDict[usr], relDict.data != dict.data { - return isReferencingInternal(type: type, kind: kind, dict: relDict, obfuscationData: obfuscationData, sourceKit: sourceKit) + if let relDict = obfuscationData.usrRelationDict[usr], relDict.val.data != variant.val.data { + return isReferencingInternal(type: type, + kind: kind, + variant: relDict, + obfuscationData: obfuscationData) } var isReference = false - sourceKit.recurseOver(childID: sourceKit.relatedID, resp: dict) { dict in + variant.recurseOver(uid: .relatedId) { [unowned self] variant in guard isReference == false else { return } - guard let usr = dict.getString(key: sourceKit.usrID) else { + let dict = variant.getDictionary() + guard let usr = dict.getString(.usrId) else { return } if obfuscationData.usrDict.contains(usr) == false { isReference = true } else if let relDict = obfuscationData.usrRelationDict[usr] { - isReference = self.isReferencingInternal(type: type, kind: kind, dict: relDict, obfuscationData: obfuscationData, sourceKit: sourceKit) + isReference = self.isReferencingInternal(type: type, + kind: kind, + variant: relDict, + obfuscationData: obfuscationData) } } return isReference diff --git a/swiftshield-Sources/DynamicLink.swift b/swiftshield-Sources/DynamicLink.swift deleted file mode 100644 index f363e76..0000000 --- a/swiftshield-Sources/DynamicLink.swift +++ /dev/null @@ -1,52 +0,0 @@ -import Foundation - -struct DynamicLinkLibrary { - let path: String - let handle: UnsafeMutableRawPointer - - func load(symbol: String) -> T { - if let sym = dlsym(handle, symbol) { - return unsafeBitCast(sym, to: T.self) - } - let errorString = String(validatingUTF8: dlerror()) ?? "" - fatalError("Finding symbol \(symbol) failed: \(errorString)") - } -} - -func appsIn( dir: String, matcher: (_ name: String) -> Bool ) -> [String] { - return (try! FileManager.default.contentsOfDirectory(atPath: dir)) - .filter( matcher ).sorted().reversed().map { "\(dir)/\($0)" } -} - -#if os(Linux) -let toolchainLoader = Loader(searchPaths: [linuxSourceKitLibPath]) -#else -let toolchainLoader = Loader(searchPaths: (["/Applications/Xcode.app"] + - appsIn( dir: "/Applications", matcher: { $0.hasPrefix("Xcode") } ) ) - .map { $0+"/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/" } ) -#endif - -struct Loader { - let searchPaths: [String] - - func load(path: String) -> DynamicLinkLibrary { - let fullPaths = searchPaths.map { $0.appending(path) } - - // try all fullPaths that contains target file, - // then try loading with simple path that depends resolving to DYLD - for fullPath in fullPaths + [path] { - if let handle = dlopen(fullPath, RTLD_LAZY) { - return DynamicLinkLibrary(path: path, handle: handle) - } - } - - fatalError("Loading \(path) from \(searchPaths)") - } -} - -#if os(Linux) -private let path = "libsourcekitdInProc.so" -#else -private let path = "sourcekitd.framework/Versions/A/sourcekitd" -#endif -let library = toolchainLoader.load(path: path) diff --git a/swiftshield-Sources/Logger.swift b/swiftshield-Sources/Logger.swift index 970a6de..a54546c 100644 --- a/swiftshield-Sources/Logger.swift +++ b/swiftshield-Sources/Logger.swift @@ -78,7 +78,8 @@ enum LogType { case .scanningDeclarations: return "--- Searching for tagged objects ---" case .helpText: - return String.helpText + return "\n\n--\n\nAUTOMATIC MODE:\n\nExample: swiftshield -automatic -project-root /app/MyApp -automatic-project-file /app/MyApp/MyApp.xcworkspace -automatic-project-scheme MyApp-AppStore \n\nRequired parameters:\n\n-automatic -project-root PATH_TO_PROJECTS_ROOT_FOLDER \n\n-automatic-project-file PATH_TO_PROJECT_FILE \n\n-automatic-project-scheme SCHEME_NAME_TO_BUILD\n\nOptional parameters:\n\n-verbose (Uses verbose mode)\n\n-show-sourcekit-queries (Prints queries made to SourceKit)\n\n-ignore-modules MyLib,MyAppExtension (Prevents obfuscation of certain modules)\n\n-obfuscation-character-count 32 (Obfuscated name size)\n\n-dry-run (Doesn't actually overwrite files)" + + "\n\nMANUAL MODE:\n\nExample: swiftshield -project-root /app/MyApp -tag myTag\n\nRequired parameters:\n\n-project-root PATH_TO_PROJECTS_ROOT_FOLDER \n\nOptional parameters:\n\n-tag myTag (Custom tag to use. If not provided, '__s' will be used.)\n\n-verbose (Uses verbose mode)\n\n-obfuscation-character-count 32 (Obfuscated name size)\n\n-dry-run (Doesn't actually overwrite files)" case .projectError: return "Project file provided is not a project or workspace." case .foundNothingError: @@ -86,7 +87,7 @@ enum LogType { case .finished: return "Finished." case .version: - return "SwiftShield 3.3.0" + return "SwiftShield 3.3.1" case .verbose: return "Verbose Mode" case .mode: diff --git a/swiftshield-Sources/ManualSwiftShield.swift b/swiftshield-Sources/ManualSwiftShield.swift index 026c5cb..702dc93 100644 --- a/swiftshield-Sources/ManualSwiftShield.swift +++ b/swiftshield-Sources/ManualSwiftShield.swift @@ -3,9 +3,9 @@ import Foundation final class ManualSwiftShield: Protector { let tag: String - init(basePath: String, tag: String, protectedClassNameSize: Int) { + init(basePath: String, tag: String, protectedClassNameSize: Int, dryRun: Bool) { self.tag = tag - super.init(basePath: basePath, protectedClassNameSize: protectedClassNameSize) + super.init(basePath: basePath, protectedClassNameSize: protectedClassNameSize, dryRun: dryRun) } override func protect() -> ObfuscationData { @@ -22,7 +22,9 @@ final class ManualSwiftShield: Protector { do { let fileString = try String(contentsOfFile: file.path, encoding: .utf8) let newFile = obfuscateReferences(fileString: fileString, obfsData: obfsData) - try newFile.write(toFile: file.path, atomically: false, encoding: .utf8) + if dryRun == false { + try newFile.write(toFile: file.path, atomically: false, encoding: .utf8) + } } catch { Logger.log(.fatal(error: error.localizedDescription)) exit(1) diff --git a/swiftshield-Sources/ObfuscationData.swift b/swiftshield-Sources/ObfuscationData.swift index 5f5a27e..f6326a6 100644 --- a/swiftshield-Sources/ObfuscationData.swift +++ b/swiftshield-Sources/ObfuscationData.swift @@ -22,8 +22,8 @@ final class AutomaticObfuscationData: ObfuscationData { var usrDict: Set = [] var referencesDict: [File: [ReferenceData]] = [:] - var usrRelationDict: [String: sourcekitd_variant_t] = [:] - var indexedFiles: [(File,sourcekitd_response_t)] = [] + var usrRelationDict: [String: SourceKitdResponse.Variant] = [:] + var indexedFiles: [(File, SourceKitdResponse)] = [] var moduleNames: Set { return Set(modules.compactMap { $0.name }) diff --git a/swiftshield-Sources/Protector.swift b/swiftshield-Sources/Protector.swift index 36f1591..e8b2d6d 100644 --- a/swiftshield-Sources/Protector.swift +++ b/swiftshield-Sources/Protector.swift @@ -7,6 +7,7 @@ class Protector { let basePath: String let protectedClassNameSize: Int + let dryRun: Bool static func mapData(from obfuscationData: ObfuscationData, info: String) -> String { return """ @@ -22,9 +23,10 @@ class Protector { } } - init(basePath: String, protectedClassNameSize: Int = 25) { + init(basePath: String, protectedClassNameSize: Int = 25, dryRun: Bool) { self.basePath = basePath self.protectedClassNameSize = protectedClassNameSize + self.dryRun = dryRun if basePath.isEmpty { Logger.log(.helpText) exit(error: true) diff --git a/swiftshield-Sources/SKAPI.swift b/swiftshield-Sources/SKAPI.swift deleted file mode 100644 index 5b33f18..0000000 --- a/swiftshield-Sources/SKAPI.swift +++ /dev/null @@ -1,81 +0,0 @@ -// Created by John Holdsworth on 19/12/2015. -// Copyright © 2015 John Holdsworth. All rights reserved. -// -// $Id: //depot/Refactorator/refactord/SourceKit.swift#25 $ -// -// Repo: https://github.com/johnno1962/Refactorator -// - -/** Thanks to: https://github.com/jpsim/SourceKitten/blob/master/Source/SourceKittenFramework/library_wrapper_sourcekitd.swift **/ - -import Foundation - -let SKApi = SKAPI() - -final class SKAPI { - - static var verbose = false - - internal let sourcekitd_initialize: @convention(c) () -> () = library.load(symbol: "sourcekitd_initialize") - internal let sourcekitd_shutdown: @convention(c) () -> () = library.load(symbol: "sourcekitd_shutdown") - internal let sourcekitd_set_interrupted_connection_handler: @convention(c) (@escaping sourcekitd_interrupted_connection_handler_t) -> () = library.load(symbol: "sourcekitd_set_interrupted_connection_handler") - internal let sourcekitd_uid_get_from_cstr: @convention(c) (UnsafePointer) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_uid_get_from_cstr") - internal let sourcekitd_uid_get_from_buf: @convention(c) (UnsafePointer, Int) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_uid_get_from_buf") - internal let sourcekitd_uid_get_length: @convention(c) (sourcekitd_uid_t) -> (Int) = library.load(symbol: "sourcekitd_uid_get_length") - internal let sourcekitd_uid_get_string_ptr: @convention(c) (sourcekitd_uid_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_uid_get_string_ptr") - internal let sourcekitd_request_retain: @convention(c) (sourcekitd_object_t) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_retain") - internal let sourcekitd_request_release: @convention(c) (sourcekitd_object_t) -> () = library.load(symbol: "sourcekitd_request_release") - internal let sourcekitd_request_dictionary_create: @convention(c) (UnsafePointer?, UnsafePointer?, Int) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_dictionary_create") - internal let sourcekitd_request_dictionary_set_value: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, sourcekitd_object_t) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_value") - internal let sourcekitd_request_dictionary_set_string: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, UnsafePointer) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_string") - internal let sourcekitd_request_dictionary_set_stringbuf: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, UnsafePointer, Int) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_stringbuf") - internal let sourcekitd_request_dictionary_set_int64: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, Int64) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_int64") - internal let sourcekitd_request_dictionary_set_uid: @convention(c) (sourcekitd_object_t, sourcekitd_uid_t, sourcekitd_uid_t) -> () = library.load(symbol: "sourcekitd_request_dictionary_set_uid") - internal let sourcekitd_request_array_create: @convention(c) (UnsafePointer?, Int) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_array_create") - internal let sourcekitd_request_array_set_value: @convention(c) (sourcekitd_object_t, Int, sourcekitd_object_t) -> () = library.load(symbol: "sourcekitd_request_array_set_value") - internal let sourcekitd_request_array_set_string: @convention(c) (sourcekitd_object_t, Int, UnsafePointer) -> () = library.load(symbol: "sourcekitd_request_array_set_string") - internal let sourcekitd_request_array_set_stringbuf: @convention(c) (sourcekitd_object_t, Int, UnsafePointer, Int) -> () = library.load(symbol: "sourcekitd_request_array_set_stringbuf") - internal let sourcekitd_request_array_set_int64: @convention(c) (sourcekitd_object_t, Int, Int64) -> () = library.load(symbol: "sourcekitd_request_array_set_int64") - internal let sourcekitd_request_array_set_uid: @convention(c) (sourcekitd_object_t, Int, sourcekitd_uid_t) -> () = library.load(symbol: "sourcekitd_request_array_set_uid") - internal let sourcekitd_request_int64_create: @convention(c) (Int64) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_int64_create") - internal let sourcekitd_request_string_create: @convention(c) (UnsafePointer) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_string_create") - internal let sourcekitd_request_uid_create: @convention(c) (sourcekitd_uid_t) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_uid_create") - internal let sourcekitd_request_create_from_yaml: @convention(c) (UnsafePointer, UnsafeMutablePointer?>?) -> (sourcekitd_object_t?) = library.load(symbol: "sourcekitd_request_create_from_yaml") - internal let sourcekitd_request_description_dump: @convention(c) (sourcekitd_object_t) -> () = library.load(symbol: "sourcekitd_request_description_dump") - internal let sourcekitd_request_description_copy: @convention(c) (sourcekitd_object_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_request_description_copy") - internal let sourcekitd_response_dispose: @convention(c) (sourcekitd_response_t) -> () = library.load(symbol: "sourcekitd_response_dispose") - internal let sourcekitd_response_is_error: @convention(c) (sourcekitd_response_t) -> (Bool) = library.load(symbol: "sourcekitd_response_is_error") - internal let sourcekitd_response_error_get_kind: @convention(c) (sourcekitd_response_t) -> (sourcekitd_error_t) = library.load(symbol: "sourcekitd_response_error_get_kind") - internal let sourcekitd_response_error_get_description: @convention(c) (sourcekitd_response_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_response_error_get_description") - internal let sourcekitd_response_get_value: @convention(c) (sourcekitd_response_t) -> (sourcekitd_variant_t) = library.load(symbol: "sourcekitd_response_get_value") - internal let sourcekitd_variant_get_type: @convention(c) (sourcekitd_variant_t) -> (sourcekitd_variant_type_t) = library.load(symbol: "sourcekitd_variant_get_type") - internal let sourcekitd_variant_dictionary_get_value: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (sourcekitd_variant_t) = library.load(symbol: "sourcekitd_variant_dictionary_get_value") - internal let sourcekitd_variant_dictionary_get_string: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_variant_dictionary_get_string") - internal let sourcekitd_variant_dictionary_get_int64: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (Int64) = library.load(symbol: "sourcekitd_variant_dictionary_get_int64") - internal let sourcekitd_variant_dictionary_get_bool: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (Bool) = library.load(symbol: "sourcekitd_variant_dictionary_get_bool") - internal let sourcekitd_variant_dictionary_get_uid: @convention(c) (sourcekitd_variant_t, sourcekitd_uid_t) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_variant_dictionary_get_uid") - internal let sourcekitd_variant_dictionary_apply_f: @convention(c) (sourcekitd_variant_t, @escaping sourcekitd_variant_dictionary_applier_f_t, UnsafeMutableRawPointer?) -> (Bool) = library.load(symbol: "sourcekitd_variant_dictionary_apply_f") - internal let sourcekitd_variant_array_get_count: @convention(c) (sourcekitd_variant_t) -> (Int) = library.load(symbol: "sourcekitd_variant_array_get_count") - internal let sourcekitd_variant_array_get_value: @convention(c) (sourcekitd_variant_t, Int) -> (sourcekitd_variant_t) = library.load(symbol: "sourcekitd_variant_array_get_value") - internal let sourcekitd_variant_array_get_string: @convention(c) (sourcekitd_variant_t, Int) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_variant_array_get_string") - internal let sourcekitd_variant_array_get_int64: @convention(c) (sourcekitd_variant_t, Int) -> (Int64) = library.load(symbol: "sourcekitd_variant_array_get_int64") - internal let sourcekitd_variant_array_get_bool: @convention(c) (sourcekitd_variant_t, Int) -> (Bool) = library.load(symbol: "sourcekitd_variant_array_get_bool") - internal let sourcekitd_variant_array_get_uid: @convention(c) (sourcekitd_variant_t, Int) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_variant_array_get_uid") - internal let sourcekitd_variant_array_apply_f: @convention(c) (sourcekitd_variant_t, @escaping sourcekitd_variant_array_applier_f_t, UnsafeMutableRawPointer?) -> (Bool) = library.load(symbol: "sourcekitd_variant_array_apply_f") - internal let sourcekitd_variant_array_apply: @convention(c) (sourcekitd_variant_t, @escaping sourcekitd_variant_array_applier_t) -> (Bool) = library.load(symbol: "sourcekitd_variant_array_apply") - internal let sourcekitd_variant_int64_get_value: @convention(c) (sourcekitd_variant_t) -> (Int64) = library.load(symbol: "sourcekitd_variant_int64_get_value") - internal let sourcekitd_variant_bool_get_value: @convention(c) (sourcekitd_variant_t) -> (Bool) = library.load(symbol: "sourcekitd_variant_bool_get_value") - internal let sourcekitd_variant_string_get_length: @convention(c) (sourcekitd_variant_t) -> (Int) = library.load(symbol: "sourcekitd_variant_string_get_length") - internal let sourcekitd_variant_string_get_ptr: @convention(c) (sourcekitd_variant_t) -> (UnsafePointer?) = library.load(symbol: "sourcekitd_variant_string_get_ptr") - internal let sourcekitd_variant_uid_get_value: @convention(c) (sourcekitd_variant_t) -> (sourcekitd_uid_t?) = library.load(symbol: "sourcekitd_variant_uid_get_value") - internal let sourcekitd_response_description_dump: @convention(c) (sourcekitd_response_t) -> () = library.load(symbol: "sourcekitd_response_description_dump") - internal let sourcekitd_response_description_dump_filedesc: @convention(c) (sourcekitd_response_t, Int32) -> () = library.load(symbol: "sourcekitd_response_description_dump_filedesc") - internal let sourcekitd_response_description_copy: @convention(c) (sourcekitd_response_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_response_description_copy") - internal let sourcekitd_variant_description_dump: @convention(c) (sourcekitd_variant_t) -> () = library.load(symbol: "sourcekitd_variant_description_dump") - internal let sourcekitd_variant_description_dump_filedesc: @convention(c) (sourcekitd_variant_t, Int32) -> () = library.load(symbol: "sourcekitd_variant_description_dump_filedesc") - internal let sourcekitd_variant_description_copy: @convention(c) (sourcekitd_variant_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_variant_description_copy") - internal let sourcekitd_variant_json_description_copy: @convention(c) (sourcekitd_variant_t) -> (UnsafeMutablePointer?) = library.load(symbol: "sourcekitd_variant_json_description_copy") - internal let sourcekitd_send_request_sync: @convention(c) (sourcekitd_object_t) -> (sourcekitd_response_t?) = library.load(symbol: "sourcekitd_send_request_sync") - internal let sourcekitd_send_request: @convention(c) (sourcekitd_object_t, UnsafeMutablePointer?, sourcekitd_response_receiver_t?) -> () = library.load(symbol: "sourcekitd_send_request") - -} diff --git a/swiftshield-Sources/SourceKit+Variant.swift b/swiftshield-Sources/SourceKit+Variant.swift deleted file mode 100644 index 144407e..0000000 --- a/swiftshield-Sources/SourceKit+Variant.swift +++ /dev/null @@ -1,28 +0,0 @@ -import Foundation - -extension sourcekitd_variant_t { - var isArray: Bool { - return SKApi.sourcekitd_variant_get_type(self) == SOURCEKITD_VARIANT_TYPE_ARRAY - } - - func getInt( key: sourcekitd_uid_t ) -> Int { - return Int(SKApi.sourcekitd_variant_dictionary_get_int64( self, key )) - } - - func getString( key: sourcekitd_uid_t ) -> String? { - let cstr = SKApi.sourcekitd_variant_dictionary_get_string( self, key ) - if cstr != nil { - return String( cString: cstr! ) - } - return nil - } - - func getUUIDString( key: sourcekitd_uid_t ) -> String { - let uuid = SKApi.sourcekitd_variant_dictionary_get_uid( self, key ) - return String( cString: SKApi.sourcekitd_uid_get_string_ptr( uuid! )! )// ?: "NOUUID" - } - - func getDictionary(key: sourcekitd_uid_t) -> sourcekitd_variant_t { - return SKApi.sourcekitd_variant_dictionary_get_value(self, key) - } -} diff --git a/swiftshield-Sources/SourceKit.swift b/swiftshield-Sources/SourceKit.swift index 89a9955..a644bcd 100755 --- a/swiftshield-Sources/SourceKit.swift +++ b/swiftshield-Sources/SourceKit.swift @@ -1,10 +1,22 @@ -import Foundation - final class SourceKit { fileprivate static let swiftLangPrefix = "source.lang.swift" - init() { - SKApi.sourcekitd_initialize() + static var verbose = false + + static func start() { + sourcekitd_initialize() + } + + static func stop() { + sourcekitd_shutdown() + } + + public func sendSyn(request: SourceKitdRequest) -> SourceKitdResponse { + let response = SourceKitdResponse(resp: sourcekitd_send_request_sync(request.rawRequest)) + if SourceKit.verbose { + print(response.description) + } + return response } func referenceType(kind: String) -> DeclarationType? { @@ -24,10 +36,12 @@ final class SourceKit { let kindSuffix = String(kind[prefixIndex...]) switch kindSuffix { case "class", - "struct", - "protocol": + "struct": return .object + case "protocol": + return .protocol // case "var.instance", +// "var.static", // "var.class": // return .property case "function.free", @@ -40,96 +54,10 @@ final class SourceKit { } } - // - // - // - // - // John's SKAPI Requests - // - // - // - // - - /** request types */ - private lazy var indexRequestID = SKApi.sourcekitd_uid_get_from_cstr("source.request.indexsource")! - private lazy var requestID = SKApi.sourcekitd_uid_get_from_cstr("key.request")! - - /** request arguments */ - lazy var sourceFileID = SKApi.sourcekitd_uid_get_from_cstr("key.sourcefile")! - lazy var compilerArgsID = SKApi.sourcekitd_uid_get_from_cstr("key.compilerargs")! - - /** sub entity lists */ - lazy var entitiesID = SKApi.sourcekitd_uid_get_from_cstr("key.entities")! - lazy var relatedID = SKApi.sourcekitd_uid_get_from_cstr("key.related")! - - /** entity attributes */ - lazy var receiverID = SKApi.sourcekitd_uid_get_from_cstr("key.receiver_usr")! - lazy var isDynamicID = SKApi.sourcekitd_uid_get_from_cstr("key.is_dynamic")! - lazy var isSystemID = SKApi.sourcekitd_uid_get_from_cstr("key.is_system")! - lazy var moduleID = SKApi.sourcekitd_uid_get_from_cstr("key.modulename")! - lazy var lengthID = SKApi.sourcekitd_uid_get_from_cstr("key.length")! - lazy var kindID = SKApi.sourcekitd_uid_get_from_cstr("key.kind")! - lazy var nameID = SKApi.sourcekitd_uid_get_from_cstr("key.name")! - lazy var lineID = SKApi.sourcekitd_uid_get_from_cstr("key.line")! - lazy var colID = SKApi.sourcekitd_uid_get_from_cstr("key.column")! - lazy var usrID = SKApi.sourcekitd_uid_get_from_cstr("key.usr")! - - func array(argv: [String]) -> sourcekitd_object_t { - let objects = argv.map { SKApi.sourcekitd_request_string_create($0) } - return SKApi.sourcekitd_request_array_create(objects, objects.count)! - } - - func error(resp: sourcekitd_response_t) -> String? { - if SKApi.sourcekitd_response_is_error(resp) { - return String(cString: SKApi.sourcekitd_response_error_get_description(resp)!) - } - return nil - } - - func sendRequest( req: sourcekitd_object_t ) -> sourcekitd_response_t { - if isTTY && SKAPI.verbose { - SKApi.sourcekitd_request_description_dump( req ) - } - var resp: sourcekitd_response_t? - while true { - resp = SKApi.sourcekitd_send_request_sync( req ) - let err = error( resp: resp! ) - if err == "restoring service" || err == "semantic editor is disabled" { - sleep(1) - continue - } - else { - break - } - } - SKApi.sourcekitd_request_release( req ) - if isTTY && !SKApi.sourcekitd_response_is_error( resp! ) && SKAPI.verbose { - SKApi.sourcekitd_response_description_dump_filedesc( resp!, STDERR_FILENO ) - } - return resp! - } - - func indexFile( filePath: String, compilerArgs: sourcekitd_object_t ) -> sourcekitd_response_t { - let req = SKApi.sourcekitd_request_dictionary_create(nil, nil, 0)! - SKApi.sourcekitd_request_dictionary_set_uid( req, requestID, indexRequestID ) - SKApi.sourcekitd_request_dictionary_set_string( req, sourceFileID, filePath ) - SKApi.sourcekitd_request_dictionary_set_value( req, compilerArgsID, compilerArgs ) - return sendRequest( req: req ) - } - - func recurseOver(childID: sourcekitd_uid_t, resp: sourcekitd_variant_t, - indent: String = "", visualiser: Visualiser? = nil, - block: @escaping (_ dict: sourcekitd_variant_t) -> ()) { - let children = SKApi.sourcekitd_variant_dictionary_get_value(resp, childID) - if SKApi.sourcekitd_variant_get_type(children) == SOURCEKITD_VARIANT_TYPE_ARRAY { - visualiser?.enter() - _ = SKApi.sourcekitd_variant_array_apply(children) { [unowned self] (_, dict) in - block(dict) - visualiser?.present(dict: dict, indent: indent) - self.recurseOver(childID: childID, resp: dict, indent: indent + " ", visualiser: visualiser, block: block) - return true - } - visualiser?.exit() - } + func indexFile(filePath: String, compilerArgs: [String]) -> SourceKitdResponse { + let request = SourceKitdRequest(uid: .indexRequestId) + request.addParameter(.sourceFileId, value: filePath) + request.addCompilerArgsToRequest(compilerArgs) + return sendSyn(request: request) } } diff --git a/swiftshield-Sources/SourceKitDeclarationType.swift b/swiftshield-Sources/SourceKitDeclarationType.swift index 4078279..088d47d 100644 --- a/swiftshield-Sources/SourceKitDeclarationType.swift +++ b/swiftshield-Sources/SourceKitDeclarationType.swift @@ -1,6 +1,7 @@ extension SourceKit { enum DeclarationType { case object + case `protocol` case property case method } diff --git a/swiftshield-Sources/SourceKitRequest.swift b/swiftshield-Sources/SourceKitRequest.swift new file mode 100644 index 0000000..3a7d09d --- /dev/null +++ b/swiftshield-Sources/SourceKitRequest.swift @@ -0,0 +1,147 @@ +//===--------------- SourceKitdRequest.swift ------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// This file provides a convenient way to build a sourcekitd request. +//===----------------------------------------------------------------------===// + +import Foundation + +public struct SourceKitdRequest: CustomStringConvertible { + + public class Dictionary: CustomStringConvertible { + let dict: sourcekitd_object_t + + public init() { + dict = sourcekitd_request_dictionary_create(nil, nil, 0) + } + + deinit { + sourcekitd_request_release(UnsafeMutableRawPointer(dict)) + } + + public func add(_ key: SourceKitdUID, value: String) { + sourcekitd_request_dictionary_set_string(dict, key.uid, value) + } + + public func add(_ key: SourceKitdUID, value: Int) { + sourcekitd_request_dictionary_set_int64(dict, key.uid, Int64(value)) + } + + public func add(_ key: SourceKitdUID, value: SourceKitdUID) { + sourcekitd_request_dictionary_set_uid(dict, key.uid, value.uid) + } + + public func add(_ key: SourceKitdUID, value: Array) { + sourcekitd_request_dictionary_set_value(dict, key.uid, value.arr) + } + + public func add(_ key: SourceKitdUID, value: Dictionary) { + sourcekitd_request_dictionary_set_value(dict, key.uid, value.dict) + } + + public func add(_ key: SourceKitdUID, value: Bool) { + sourcekitd_request_dictionary_set_int64(dict, key.uid, value ? 1 : 0) + } + + public var description: String { + let utf8Str = sourcekitd_request_description_copy(dict)! + let result = String(cString: utf8Str) + free(utf8Str) + return result + } + + } + + public class Array: CustomStringConvertible { + let arr: sourcekitd_object_t + private let Append: Int = -1 + + public init() { + arr = sourcekitd_request_array_create(nil, 0) + } + + deinit { + sourcekitd_request_release(arr) + } + + public func add(_ value: String) { + sourcekitd_request_array_set_string(arr, Append, value) + } + + public func add(_ value: Int) { + sourcekitd_request_array_set_int64(arr, Append, Int64(value)) + } + + public func add(_ value: SourceKitdUID) { + sourcekitd_request_array_set_uid(arr, Append, value.uid) + } + + public func add(_ value: Dictionary) { + sourcekitd_request_array_set_value(arr, Append, value.dict) + } + + public var description: String { + let utf8Str = sourcekitd_request_description_copy(arr)! + let result = String(cString: utf8Str) + free(utf8Str) + return result + } + + } + + private let req = Dictionary() + + public init(uid: SourceKitdUID) { + let request = SourceKitdUID(uid: sourcekitd_uid_get_from_cstr("key.request")!) + req.add(request, value: uid) + } + + public func addParameter(_ key: SourceKitdUID, value: String) { + req.add(key, value: value) + } + + public func addParameter(_ key: SourceKitdUID, value: Int) { + req.add(key, value: value) + } + + public func addParameter(_ key: SourceKitdUID, value: SourceKitdUID) { + req.add(key, value: value) + } + + public func addArrayParameter(_ key: SourceKitdUID) -> Array { + let arr = Array() + req.add(key, value: arr) + return arr + } + + public func addDictionaryParameter(_ key: SourceKitdUID) -> Dictionary { + let dict = Dictionary() + req.add(key, value: dict) + return dict + } + + public var description: String { + return req.description + } + + public var rawRequest: sourcekitd_object_t { + return req.dict + } + + public func addCompilerArgsToRequest(_ compilerArguments: [String]?, + _ bufferName: String? = nil) { + let compilerArgs = SourceKitdUID(uid: sourcekitd_uid_get_from_cstr("key.compilerargs")!) + let args = self.addArrayParameter(compilerArgs) + for argument in compilerArguments ?? [] { + args.add(argument) + } + } +} diff --git a/swiftshield-Sources/SourceKitdResponse.swift b/swiftshield-Sources/SourceKitdResponse.swift new file mode 100644 index 0000000..4160ab4 --- /dev/null +++ b/swiftshield-Sources/SourceKitdResponse.swift @@ -0,0 +1,256 @@ +//===--------------------- SourceKitdResponse.swift -----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// This file provides convenient APIs to interpret a SourceKitd response. +//===----------------------------------------------------------------------===// + +public class SourceKitdResponse: CustomStringConvertible { + + public struct Dictionary: CustomStringConvertible, CustomReflectable { + // The lifetime of this sourcekitd_variant_t is tied to the response it came + // from, so keep a reference to the response too. + private let dict: sourcekitd_variant_t + private let context: SourceKitdResponse + + + public init(dict: sourcekitd_variant_t, context: SourceKitdResponse) { + assert(sourcekitd_variant_get_type(dict).rawValue == + SOURCEKITD_VARIANT_TYPE_DICTIONARY.rawValue) + self.dict = dict + self.context = context + } + + public func getString(_ key: SourceKitdUID) -> String? { + guard let value = sourcekitd_variant_dictionary_get_string(dict, key.uid) else { + return nil + } + return String(cString: value) + } + + public func getInt(_ key: SourceKitdUID) -> Int { + let value = sourcekitd_variant_dictionary_get_int64(dict, key.uid) + return Int(value) + } + + public func getBool(_ key: SourceKitdUID) -> Bool { + let value = sourcekitd_variant_dictionary_get_bool(dict, key.uid) + return value + } + + public func getUID(_ key: SourceKitdUID) -> SourceKitdUID { + let value = sourcekitd_variant_dictionary_get_uid(dict, key.uid)! + return SourceKitdUID(uid: value) + } + + public func getArray(_ key: SourceKitdUID) -> Array { + let value = sourcekitd_variant_dictionary_get_value(dict, key.uid) + return Array(arr: value, context: context) + } + + public func getDictionary(_ key: SourceKitdUID) -> Dictionary { + let value = sourcekitd_variant_dictionary_get_value(dict, key.uid) + return Dictionary(dict: value, context: context) + } + + public func getOptional(_ key: SourceKitdUID) -> Variant? { + let value = sourcekitd_variant_dictionary_get_value(dict, key.uid) + if sourcekitd_variant_get_type(value).rawValue == + SOURCEKITD_VARIANT_TYPE_NULL.rawValue { + return nil + } + return Variant(val: value, context: context) + } + + public var description: String { + return dict.description + } + + public var customMirror: Mirror { + return Mirror(self, children: [:]) + } + } + + public struct Array: CustomStringConvertible { + // The lifetime of this sourcekitd_variant_t is tied to the response it came + // from, so keep a reference to the response too. + private let arr: sourcekitd_variant_t + private let context: SourceKitdResponse + + public var count: Int { + let count = sourcekitd_variant_array_get_count(arr) + return Int(count) + } + + public init(arr: sourcekitd_variant_t, context: SourceKitdResponse) { + assert(sourcekitd_variant_get_type(arr).rawValue == + SOURCEKITD_VARIANT_TYPE_ARRAY.rawValue) + self.arr = arr + self.context = context + } + + public func getString(_ index: Int) -> String { + let value = sourcekitd_variant_array_get_string(arr, index)! + return String(cString: value) + } + + public func getInt(_ index: Int) -> Int { + let value = sourcekitd_variant_array_get_int64(arr, index) + return Int(value) + } + + public func getBool(_ index: Int) -> Bool { + let value = sourcekitd_variant_array_get_bool(arr, index) + return value + } + + public func getArray(_ index: Int) -> Array { + let value = sourcekitd_variant_array_get_value(arr, index) + return Array(arr: value, context: context) + } + + public func getDictionary(_ index: Int) -> Dictionary { + let value = sourcekitd_variant_array_get_value(arr, index) + return Dictionary(dict: value, context: context) + } + + public func enumerate(_ applier: (_ index: Int, _ value: Variant) -> Bool) { + // The block passed to sourcekit_variant_array_apply() does not actually + // escape, it's synchronous and not called after returning. + let context = self.context + withoutActuallyEscaping(applier) { escapingApplier in + _ = sourcekitd_variant_array_apply(arr) { (index, elem) -> Bool in + return escapingApplier(Int(index), Variant(val: elem, context: context)) + } + } + } + + public var description: String { + return arr.description + } + + } + + public struct Variant: CustomStringConvertible { + // The lifetime of this sourcekitd_variant_t is tied to the response it came + // from, so keep a reference to the response too. + let val: sourcekitd_variant_t + fileprivate let context: SourceKitdResponse + + fileprivate init(val: sourcekitd_variant_t, context: SourceKitdResponse) { + self.val = val + self.context = context + } + + public func getString() -> String { + let value = sourcekitd_variant_string_get_ptr(val)! + let length = sourcekitd_variant_string_get_length(val) + return fromCStringLen(value, length: length)! + } + + public func getStringBuffer() -> UnsafeBufferPointer { + return UnsafeBufferPointer(start: sourcekitd_variant_string_get_ptr(val), + count: sourcekitd_variant_string_get_length(val)) + } + + public func getInt() -> Int { + let value = sourcekitd_variant_int64_get_value(val) + return Int(value) + } + + public func getBool() -> Bool { + let value = sourcekitd_variant_bool_get_value(val) + return value + } + + public func getArray() -> Array { + return Array(arr: val, context: context) + } + + public func getDictionary() -> Dictionary { + return Dictionary(dict: val, context: context) + } + + public var description: String { + return val.description + } + + public func recurseOver(uid: SourceKitdUID, block: @escaping (Variant) -> ()) { + let children = sourcekitd_variant_dictionary_get_value(val, uid.uid) + guard sourcekitd_variant_get_type(children) == SOURCEKITD_VARIANT_TYPE_ARRAY else { + return + } + _ = sourcekitd_variant_array_apply(children) { (_, val) in + let variant = Variant(val: val, context: self.context) + block(variant) + variant.recurseOver(uid: uid, block: block) + return true + } + } + } + + let resp: sourcekitd_response_t + + public var value: Dictionary { + return Dictionary(dict: sourcekitd_response_get_value(resp), context: self) + } + + public var variant: Variant { + let val = sourcekitd_response_get_value(resp) + return Variant(val: val, context: self) + } + + public var error: String? { + if sourcekitd_response_is_error(resp) { + return String(cString: sourcekitd_response_error_get_description(resp)!) + } + return nil + } + + /// Whether or not this response represents a connection interruption error. + public var isConnectionInterruptionError: Bool { + return sourcekitd_response_is_error(resp) && + sourcekitd_response_error_get_kind(resp) == + SOURCEKITD_ERROR_CONNECTION_INTERRUPTED + } + + public init(resp: sourcekitd_response_t) { + self.resp = resp + } + + deinit { + sourcekitd_response_dispose(resp) + } + + public var description: String { + let utf8Str = sourcekitd_response_description_copy(resp)! + let result = String(cString: utf8Str) + free(utf8Str) + return result + } + + func recurseOver(uid: SourceKitdUID, block: @escaping (Variant) -> ()) { + variant.recurseOver(uid: uid, block: block) + } +} + +private func fromCStringLen(_ ptr: UnsafePointer, length: Int) -> String? { + return String(decoding: Array(UnsafeBufferPointer(start: ptr, count: length)).map { + UInt8(bitPattern: $0) }, as: UTF8.self) +} + +extension sourcekitd_variant_t: CustomStringConvertible { + public var description: String { + let utf8Str = sourcekitd_variant_description_copy(self)! + let result = String(cString: utf8Str) + free(utf8Str) + return result + } +} diff --git a/swiftshield-Sources/SourceKitdUUID.swift b/swiftshield-Sources/SourceKitdUUID.swift new file mode 100644 index 0000000..f60f021 --- /dev/null +++ b/swiftshield-Sources/SourceKitdUUID.swift @@ -0,0 +1,56 @@ +//===--------------------- SourceKitdResponse.swift -----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// This file provides convenient APIs to interpret a SourceKitd response. +//===----------------------------------------------------------------------===// + +import Foundation + +public struct SourceKitdUID: Equatable, Hashable, CustomStringConvertible { + public let uid: sourcekitd_uid_t + + init(uid: sourcekitd_uid_t) { + self.uid = uid + } + + public init(string: String) { + self.uid = sourcekitd_uid_get_from_cstr(string) + } + + public var description: String { + return String(cString: sourcekitd_uid_get_string_ptr(uid)) + } + + public var asString: String { + return String(cString: sourcekitd_uid_get_string_ptr(uid)) + } + + public var hashValue: Int { + return uid.hashValue + } +} + +extension SourceKitdUID { + static let kindId = SourceKitdUID(uid: get("key.kind")) + static let nameId = SourceKitdUID(uid: get("key.name")) + static let usrId = SourceKitdUID(uid: get("key.usr")) + static let receiverId = SourceKitdUID(uid: get("key.receiver_usr")) + static let entitiesId = SourceKitdUID(uid: get("key.entities")) + static let lineId = SourceKitdUID(uid: get("key.line")) + static let colId = SourceKitdUID(uid: get("key.column")) + static let relatedId = SourceKitdUID(uid: get("key.related")) + static let sourceFileId = SourceKitdUID(uid: get("key.sourcefile")) + static let indexRequestId = SourceKitdUID(uid: get("source.request.indexsource")) + + private static func get(_ cstr: String) -> sourcekitd_uid_t { + return sourcekitd_uid_get_from_cstr(cstr) + } +} diff --git a/swiftshield-Sources/String.swift b/swiftshield-Sources/String.swift index 325c1a3..40f281a 100644 --- a/swiftshield-Sources/String.swift +++ b/swiftshield-Sources/String.swift @@ -52,11 +52,6 @@ extension String { static var storyboardClassNameRegex: String { return "((?<=customClass=\").*?(?=\" customModule)|(?<=action selector=\").*?(?=:\"))" } - - static var helpText: String { - return "\n\n--\n\nAUTOMATIC MODE:\n\nExample: swiftshield -automatic -project-root /app/MyApp -automatic-project-file /app/MyApp/MyApp.xcworkspace -automatic-project-scheme MyApp-AppStore \n\nRequired parameters:\n\n-automatic -project-root PATH_TO_PROJECTS_ROOT_FOLDER \n\n-automatic-project-file PATH_TO_PROJECT_FILE \n\n-automatic-project-scheme SCHEME_NAME_TO_BUILD\n\nOptional parameters:\n\n-verbose (Uses verbose mode)\n\n-show-sourcekit-queries (Prints queries made to SourceKit)\n\n-ignore-modules MyLib,MyAppExtension (Prevents obfuscation of certain modules)\n\n-obfuscation-character-count 32 (Obfuscated name size)" + - "\n\nMANUAL MODE:\n\nExample: swiftshield -project-root /app/MyApp -tag myTag\n\nRequired parameters:\n\n-project-root PATH_TO_PROJECTS_ROOT_FOLDER \n\nOptional parameters:\n\n-tag myTag (Custom tag to use. If not provided, '__s' will be used.)\n\n-verbose (Uses verbose mode)\n\n-obfuscation-character-count 32 (Obfuscated name size)" - } } extension String { diff --git a/swiftshield-Sources/TTY.swift b/swiftshield-Sources/TTY.swift deleted file mode 100644 index 33c5470..0000000 --- a/swiftshield-Sources/TTY.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -var isTTY = isatty( STDERR_FILENO ) != 0 - -protocol Visualiser { - func enter() - func present(dict: sourcekitd_variant_t, indent: String) - func exit() -} diff --git a/swiftshield-Sources/main.swift b/swiftshield-Sources/main.swift index 768e1d5..cd6f07d 100644 --- a/swiftshield-Sources/main.swift +++ b/swiftshield-Sources/main.swift @@ -6,7 +6,7 @@ if CommandLine.arguments.contains("-help") { } Logger.verbose = CommandLine.arguments.contains("-verbose") -SKAPI.verbose = CommandLine.arguments.contains("-show-sourcekit-queries") +SourceKit.verbose = CommandLine.arguments.contains("-show-sourcekit-queries") Logger.log(.version) Logger.log(.verbose) @@ -25,6 +25,7 @@ if let filePathToDeobfuscate = UserDefaults.standard.string(forKey: "deobfuscate } let automatic = CommandLine.arguments.contains("-automatic") +let dryRun = CommandLine.arguments.contains("-dry-run") Logger.log(.mode) @@ -37,10 +38,10 @@ if automatic { let schemeToBuild = UserDefaults.standard.string(forKey: "automatic-project-scheme") ?? "" let projectToBuild = UserDefaults.standard.string(forKey: "automatic-project-file") ?? "" let modulesToIgnore = UserDefaults.standard.string(forKey: "ignore-modules")?.components(separatedBy: ",") ?? [] - protector = AutomaticSwiftShield(basePath: basePath, projectToBuild: projectToBuild, schemeToBuild: schemeToBuild, modulesToIgnore: Set(modulesToIgnore), protectedClassNameSize: protectedClassNameSize) + protector = AutomaticSwiftShield(basePath: basePath, projectToBuild: projectToBuild, schemeToBuild: schemeToBuild, modulesToIgnore: Set(modulesToIgnore), protectedClassNameSize: protectedClassNameSize, dryRun: dryRun) } else { let tag = UserDefaults.standard.string(forKey: "tag") ?? "__s" - protector = ManualSwiftShield(basePath: basePath, tag: tag, protectedClassNameSize: protectedClassNameSize) + protector = ManualSwiftShield(basePath: basePath, tag: tag, protectedClassNameSize: protectedClassNameSize, dryRun: dryRun) } let obfuscationData = protector.protect() @@ -49,8 +50,11 @@ if obfuscationData.obfuscationDict.isEmpty { exit(error: true) } -protector.protectStoryboards(data: obfuscationData) -protector.writeToFile(data: obfuscationData) -protector.markProjectsAsProtected() +if dryRun == false { + protector.protectStoryboards(data: obfuscationData) + protector.writeToFile(data: obfuscationData) + protector.markProjectsAsProtected() +} + Logger.log(.finished) exit() diff --git a/swiftshield.xcodeproj/project.pbxproj b/swiftshield.xcodeproj/project.pbxproj index cfc58c4..58d49a7 100644 --- a/swiftshield.xcodeproj/project.pbxproj +++ b/swiftshield.xcodeproj/project.pbxproj @@ -26,10 +26,12 @@ FD1BEDB62130506B00105411 /* MockObfuscatedFile.txt in Resources */ = {isa = PBXBuildFile; fileRef = FD1BEDB52130506A00105411 /* MockObfuscatedFile.txt */; }; FD1BEDB821305E2000105411 /* MockXib.txt in Resources */ = {isa = PBXBuildFile; fileRef = FD1BEDB721305E2000105411 /* MockXib.txt */; }; FD1BEDBA21305F8300105411 /* ExpectedMockXib.txt in Resources */ = {isa = PBXBuildFile; fileRef = FD1BEDB921305F8300105411 /* ExpectedMockXib.txt */; }; - FD1C89CB212C986600F05C4D /* TTY.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89CA212C986600F05C4D /* TTY.swift */; }; - FD1C89CD212C988100F05C4D /* SourceKit+Variant.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89CC212C988100F05C4D /* SourceKit+Variant.swift */; }; - FD1C89CF212C989B00F05C4D /* DynamicLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89CE212C989B00F05C4D /* DynamicLink.swift */; }; - FD1C89D1212C98B100F05C4D /* SKAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89D0212C98B100F05C4D /* SKAPI.swift */; }; + FD22AEFC21AAD4360099FFB1 /* SourceKitRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD22AEFB21AAD4360099FFB1 /* SourceKitRequest.swift */; }; + FD22AF0021AAEB4B0099FFB1 /* SourceKitdUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD22AEFF21AAEB4B0099FFB1 /* SourceKitdUUID.swift */; }; + FD22AF0221AAEB730099FFB1 /* SourceKitdResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD22AF0121AAEB730099FFB1 /* SourceKitdResponse.swift */; }; + FD22AF0321AAEE4E0099FFB1 /* SourceKitdResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD22AF0121AAEB730099FFB1 /* SourceKitdResponse.swift */; }; + FD22AF0421AAEE500099FFB1 /* SourceKitdUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD22AEFF21AAEB4B0099FFB1 /* SourceKitdUUID.swift */; }; + FD22AF0521AAEE520099FFB1 /* SourceKitRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD22AEFB21AAD4360099FFB1 /* SourceKitRequest.swift */; }; FD39B34F1EAA9D83009BFCDF /* SourceKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD39B34E1EAA9D83009BFCDF /* SourceKit.swift */; }; FD39B3521EAA9D92009BFCDF /* ObfuscationData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD39B3501EAA9D92009BFCDF /* ObfuscationData.swift */; }; FD39B3531EAA9D92009BFCDF /* ReferenceData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD39B3511EAA9D92009BFCDF /* ReferenceData.swift */; }; @@ -40,10 +42,6 @@ FD5315D4212F872E001EA977 /* XcodeProjectBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5315D3212F872E001EA977 /* XcodeProjectBuilderTests.swift */; }; FD5315DA212F8C4C001EA977 /* XcodeOutput.txt in Resources */ = {isa = PBXBuildFile; fileRef = FD5315D9212F8C4C001EA977 /* XcodeOutput.txt */; }; FD5315EE212F8E68001EA977 /* SourceKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD39B34E1EAA9D83009BFCDF /* SourceKit.swift */; }; - FD5315EF212F8E68001EA977 /* DynamicLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89CE212C989B00F05C4D /* DynamicLink.swift */; }; - FD5315F0212F8E68001EA977 /* SKAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89D0212C98B100F05C4D /* SKAPI.swift */; }; - FD5315F1212F8E68001EA977 /* TTY.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89CA212C986600F05C4D /* TTY.swift */; }; - FD5315F2212F8E68001EA977 /* SourceKit+Variant.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1C89CC212C988100F05C4D /* SourceKit+Variant.swift */; }; FD5315F3212F8E68001EA977 /* SourceKitDeclarationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC74E77212C49AC00B3E03B /* SourceKitDeclarationType.swift */; }; FD5315F4212F8E68001EA977 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD895ADC1E31142400A753FB /* String.swift */; }; FD5315F5212F8E68001EA977 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD895AD51E31141100A753FB /* Logger.swift */; }; @@ -101,10 +99,9 @@ FD1BEDB52130506A00105411 /* MockObfuscatedFile.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MockObfuscatedFile.txt; sourceTree = ""; }; FD1BEDB721305E2000105411 /* MockXib.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MockXib.txt; sourceTree = ""; }; FD1BEDB921305F8300105411 /* ExpectedMockXib.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ExpectedMockXib.txt; sourceTree = ""; }; - FD1C89CA212C986600F05C4D /* TTY.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTY.swift; sourceTree = ""; }; - FD1C89CC212C988100F05C4D /* SourceKit+Variant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceKit+Variant.swift"; sourceTree = ""; }; - FD1C89CE212C989B00F05C4D /* DynamicLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicLink.swift; sourceTree = ""; }; - FD1C89D0212C98B100F05C4D /* SKAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKAPI.swift; sourceTree = ""; }; + FD22AEFB21AAD4360099FFB1 /* SourceKitRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceKitRequest.swift; sourceTree = ""; }; + FD22AEFF21AAEB4B0099FFB1 /* SourceKitdUUID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceKitdUUID.swift; sourceTree = ""; }; + FD22AF0121AAEB730099FFB1 /* SourceKitdResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceKitdResponse.swift; sourceTree = ""; }; FD39B34E1EAA9D83009BFCDF /* SourceKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SourceKit.swift; path = "swiftshield-Sources/SourceKit.swift"; sourceTree = SOURCE_ROOT; }; FD39B3501EAA9D92009BFCDF /* ObfuscationData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ObfuscationData.swift; path = "swiftshield-Sources/ObfuscationData.swift"; sourceTree = SOURCE_ROOT; }; FD39B3511EAA9D92009BFCDF /* ReferenceData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReferenceData.swift; path = "swiftshield-Sources/ReferenceData.swift"; sourceTree = SOURCE_ROOT; }; @@ -246,6 +243,18 @@ path = AutomaticSwiftShieldTests; sourceTree = ""; }; + FD22AEFE21AAEB3A0099FFB1 /* SourceKitWrapper */ = { + isa = PBXGroup; + children = ( + FD39B34E1EAA9D83009BFCDF /* SourceKit.swift */, + FD22AEFF21AAEB4B0099FFB1 /* SourceKitdUUID.swift */, + FD22AF0121AAEB730099FFB1 /* SourceKitdResponse.swift */, + FD22AEFB21AAD4360099FFB1 /* SourceKitRequest.swift */, + FDC74E77212C49AC00B3E03B /* SourceKitDeclarationType.swift */, + ); + name = SourceKitWrapper; + sourceTree = ""; + }; FD34D4E41EA933C60002A273 /* Modes */ = { isa = PBXGroup; children = ( @@ -268,12 +277,7 @@ FD4FB0491E5B146E009856B8 /* Resources */ = { isa = PBXGroup; children = ( - FD39B34E1EAA9D83009BFCDF /* SourceKit.swift */, - FD1C89CE212C989B00F05C4D /* DynamicLink.swift */, - FD1C89D0212C98B100F05C4D /* SKAPI.swift */, - FD1C89CA212C986600F05C4D /* TTY.swift */, - FD1C89CC212C988100F05C4D /* SourceKit+Variant.swift */, - FDC74E77212C49AC00B3E03B /* SourceKitDeclarationType.swift */, + FD22AEFE21AAEB3A0099FFB1 /* SourceKitWrapper */, FD4FB0471E5B1465009856B8 /* SwiftShield-Bridging-Header.h */, FD4FB0441E5B140E009856B8 /* sourcekitd.h */, FD4FB0521E5B1734009856B8 /* sourcekitd.framework */, @@ -465,7 +469,7 @@ buildActionMask = 2147483647; files = ( FD895AE11E31142400A753FB /* String.swift in Sources */, - FD1C89CF212C989B00F05C4D /* DynamicLink.swift in Sources */, + FD22AF0221AAEB730099FFB1 /* SourceKitdResponse.swift in Sources */, FD895ADE1E31142400A753FB /* Array.swift in Sources */, FD1BEDA0213034E100105411 /* Options.swift in Sources */, FD895AD31E31140C00A753FB /* Storyboard.swift in Sources */, @@ -475,20 +479,19 @@ FD39B3531EAA9D92009BFCDF /* ReferenceData.swift in Sources */, FD895AE01E31142400A753FB /* Protector.swift in Sources */, FD895AD81E31141500A753FB /* main.swift in Sources */, + FD22AF0021AAEB4B0099FFB1 /* SourceKitdUUID.swift in Sources */, FD1BEDA8213034E100105411 /* Document.swift in Sources */, FD833C832156666400CDF875 /* Deobfuscator.swift in Sources */, FD742A2E2156B55200EE8010 /* OrderedDictionary.swift in Sources */, FD4FB0571E5B2507009856B8 /* Module.swift in Sources */, - FD1C89D1212C98B100F05C4D /* SKAPI.swift in Sources */, - FD1C89CB212C986600F05C4D /* TTY.swift in Sources */, FD1BEDA4213034E100105411 /* Element.swift in Sources */, FD39B3521EAA9D92009BFCDF /* ObfuscationData.swift in Sources */, FD1BEDA2213034E100105411 /* Parser.swift in Sources */, FDC74E78212C49AC00B3E03B /* SourceKitDeclarationType.swift in Sources */, FD9BE54E215675C400E25A25 /* NSTextCheckingResult.swift in Sources */, FD895AD61E31141100A753FB /* Logger.swift in Sources */, - FD1C89CD212C988100F05C4D /* SourceKit+Variant.swift in Sources */, FD39B34F1EAA9D83009BFCDF /* SourceKit.swift in Sources */, + FD22AEFC21AAD4360099FFB1 /* SourceKitRequest.swift in Sources */, FD895AD01E3113FA00A753FB /* File.swift in Sources */, FDB0785220E6BD7B00F744A9 /* XcodeProjectBuilder.swift in Sources */, FDFF64D11E5275A000300623 /* Exit.swift in Sources */, @@ -501,25 +504,23 @@ files = ( FD5315D4212F872E001EA977 /* XcodeProjectBuilderTests.swift in Sources */, FD5315FB212F8E68001EA977 /* Storyboard.swift in Sources */, + FD22AF0421AAEE500099FFB1 /* SourceKitdUUID.swift in Sources */, FD5315F3212F8E68001EA977 /* SourceKitDeclarationType.swift in Sources */, FD1BEDA1213034E100105411 /* Options.swift in Sources */, + FD22AF0521AAEE520099FFB1 /* SourceKitRequest.swift in Sources */, FD1BED9821302E3800105411 /* StoryboardObfuscationTests.swift in Sources */, FD9C67D4215861EF0029EDF5 /* ManualSwiftShieldTests.swift in Sources */, FD1BEDB22130491200105411 /* AutomaticSwiftShieldTests.swift in Sources */, - FD5315F2212F8E68001EA977 /* SourceKit+Variant.swift in Sources */, - FD5315EF212F8E68001EA977 /* DynamicLink.swift in Sources */, FD5315FC212F8E68001EA977 /* Array.swift in Sources */, FD5315F5212F8E68001EA977 /* Logger.swift in Sources */, FDAA08EE21567C6C0061A52C /* MockFile.swift in Sources */, FD5315F6212F8E68001EA977 /* Exit.swift in Sources */, FD531602212F8F46001EA977 /* main.swift in Sources */, FD833C842156672C00CDF875 /* Deobfuscator.swift in Sources */, - FD5315F0212F8E68001EA977 /* SKAPI.swift in Sources */, FD5315F9212F8E68001EA977 /* ReferenceData.swift in Sources */, FD531600212F8E68001EA977 /* ManualSwiftShield.swift in Sources */, FD5315FE212F8E68001EA977 /* AutomaticSwiftShield.swift in Sources */, FD5315F8212F8E68001EA977 /* ObfuscationData.swift in Sources */, - FD5315F1212F8E68001EA977 /* TTY.swift in Sources */, FD1BEDA5213034E100105411 /* Element.swift in Sources */, FD1BEDA3213034E100105411 /* Parser.swift in Sources */, FD5315F4212F8E68001EA977 /* String.swift in Sources */, @@ -534,6 +535,7 @@ FD1BEDA7213034E100105411 /* Error.swift in Sources */, FD833C87215669AD00CDF875 /* DeobfuscatorTests.swift in Sources */, FD5315EE212F8E68001EA977 /* SourceKit.swift in Sources */, + FD22AF0321AAEE4E0099FFB1 /* SourceKitdResponse.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate b/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate index cb66835..f629842 100644 Binary files a/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate and b/swiftshield.xcodeproj/project.xcworkspace/xcuserdata/bruno.rocha.xcuserdatad/UserInterfaceState.xcuserstate differ