ipa_analyzer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7ff3fc2e0fd9c5325945dbc74f69529c05a6404
4
+ data.tar.gz: 3fcff8cfd50124ced9a7c2d8103a65a5e5d87068
5
+ SHA512:
6
+ metadata.gz: 0aefb110eaa03fac338ce0ac516b93effe282628b7a7381e66c498b273a8e8b3e9c609bdf1a5a13d45da07b5690a66c27f62c92902c2babd0559fbfd5924c80a
7
+ data.tar.gz: 1648eb696b7f9f7c84ed659d0c674c6aed868791cfcd72e5cd4a9b0b7b2d2b7f548d6ebb20e99e843f6ddb8d83da5a1d7ce1191e4fe158e189fbb00e27d6a470
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ipa_analyzer (0.1.0)
5
+ plist (~> 3.1, >= 3.1.0)
6
+ rubyzip (~> 1.1.7, >= 1.1.7)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.2.5)
12
+ plist (3.1.0)
13
+ rake (10.4.2)
14
+ rspec (3.2.0)
15
+ rspec-core (~> 3.2.0)
16
+ rspec-expectations (~> 3.2.0)
17
+ rspec-mocks (~> 3.2.0)
18
+ rspec-core (3.2.0)
19
+ rspec-support (~> 3.2.0)
20
+ rspec-expectations (3.2.0)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.2.0)
23
+ rspec-mocks (3.2.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.2.0)
26
+ rspec-support (3.2.1)
27
+ rubyzip (1.1.7)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ ipa_analyzer!
34
+ rake
35
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Bitrise
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,19 @@
1
+ # ipa_analyzer
2
+
3
+ iOS IPA file analyzer
4
+
5
+ **This code will be converted to a Ruby Gem
6
+ once the basic functionality is implemented**
7
+
8
+
9
+ ## Usage example
10
+
11
+ bundle exec ruby ipa_analyzer.rb -i /path/to/app.ipa -p --info-plist --prov
12
+
13
+ This will collect and print both the embedded mobileprovisioning
14
+ and the Info.plist, in a pretty printed JSON output.
15
+
16
+
17
+ ## Requirements
18
+
19
+ * OS: OS X (tested on 10.10 Yosemite)
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+
3
+ #
4
+ # Prints the given command, then executes it
5
+ # Example: print_and_do_command echo 'hi'
6
+ #
7
+ function print_and_do_command {
8
+ echo " -> $ $@"
9
+ $@
10
+ }
11
+
12
+ #
13
+ # Print the given command, execute it
14
+ # and exit if error happened
15
+ function print_and_do_command_exit_on_error {
16
+ print_and_do_command $@
17
+ if [ $? -ne 0 ]; then
18
+ echo " [!] Failed!"
19
+ exit 1
20
+ fi
21
+ }
22
+
23
+ # test
24
+ print_and_do_command_exit_on_error rspec
25
+ # build
26
+ print_and_do_command_exit_on_error gem build ipa_analyzer.gemspec
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'json'
5
+
6
+ $:.push File.expand_path("../../lib", __FILE__)
7
+ require 'ipa_analyzer'
8
+
9
+
10
+ # -------------------------
11
+ # --- Options
12
+
13
+ options = {
14
+ ipa_path: nil,
15
+ is_verbose: false,
16
+ is_pretty: false,
17
+ is_collect_provision_info: false,
18
+ is_collect_info_plist: false
19
+ }
20
+
21
+ opt_parser = OptionParser.new do |opt|
22
+ opt.banner = "Usage: ipa_analyzer.rb [OPTIONS]"
23
+ opt.separator ""
24
+ opt.separator "Options, the ones marked with * are required"
25
+
26
+ opt.on("-i","--ipa IPA_PATH", "*IPA file path") do |value|
27
+ options[:ipa_path] = value
28
+ end
29
+
30
+ opt.on("--prov","Collect Provisioning Profile (mobileprovision) information from the IPA") do
31
+ options[:is_collect_provision_info] = true
32
+ end
33
+
34
+ opt.on("--info-plist","Collect Info.plist information from the IPA") do
35
+ options[:is_collect_info_plist] = true
36
+ end
37
+
38
+ opt.on("-v","--verbose","Verbose output") do
39
+ options[:is_verbose] = true
40
+ end
41
+
42
+ opt.on("-p","--pretty","Pretty print output") do
43
+ options[:is_pretty] = true
44
+ end
45
+
46
+ opt.on("-h","--help","Shows this help message") do
47
+ puts opt_parser
48
+ exit 0
49
+ end
50
+ end
51
+
52
+ opt_parser.parse!
53
+ $options = options
54
+
55
+
56
+ # -------------------------
57
+ # --- Utils
58
+
59
+ def vputs(msg="")
60
+ if $options[:is_verbose]
61
+ puts msg
62
+ end
63
+ end
64
+
65
+
66
+ # -------------------------
67
+ # --- Main
68
+
69
+ vputs "options: #{options}"
70
+
71
+ raise "No IPA specified" unless options[:ipa_path]
72
+ raise "IPA specified but file does not exist at the provided path" unless File.exist? options[:ipa_path]
73
+
74
+ parsed_infos = {
75
+ mobileprovision: nil,
76
+ info_plist: nil
77
+ }
78
+
79
+ exit_code = 0
80
+
81
+ ipa_analyzer = IpaAnalyzer::Analyzer.new(options[:ipa_path])
82
+ begin
83
+ vputs " * Opening the IPA"
84
+ ipa_analyzer.open!
85
+
86
+ if options[:is_collect_provision_info]
87
+ vputs " * Collecting Provisioning Profile information"
88
+ parsed_infos[:mobileprovision] = ipa_analyzer.collect_provision_info()
89
+ raise "Failed to collect Provisioning Profile information" if parsed_infos[:mobileprovision].nil?
90
+ end
91
+ if options[:is_collect_info_plist]
92
+ vputs " * Collecting Info.plist information"
93
+ parsed_infos[:info_plist] = ipa_analyzer.collect_info_plist_info()
94
+ raise "Failed to collect Info.plist information" if parsed_infos[:info_plist].nil?
95
+ end
96
+ rescue => ex
97
+ puts
98
+ puts "Failed: #{ex}"
99
+ puts
100
+ exit_code = 1
101
+ raise ex
102
+ ensure
103
+ vputs " * Closing the IPA"
104
+ ipa_analyzer.close()
105
+ end
106
+
107
+ if options[:is_pretty]
108
+ puts JSON.pretty_generate(parsed_infos)
109
+ else
110
+ puts JSON.generate(parsed_infos)
111
+ end
112
+
113
+ exit exit_code
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "ipa_analyzer/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "ipa_analyzer"
8
+ s.authors = ["Bitrise", "Viktor Benei"]
9
+ s.email = 'letsconnect@bitrise.io'
10
+ s.license = "MIT"
11
+ s.homepage = "https://github.com/bitrise-io/ipa_analyzer"
12
+ s.version = IpaAnalyzer::VERSION
13
+ s.platform = Gem::Platform::RUBY
14
+ s.summary = "iOS .ipa analyzer"
15
+ s.description = "Analyze an iOS .ipa file. Can be used as a CLI and can print the information in JSON so it can be used by other tools."
16
+
17
+ s.add_runtime_dependency 'plist', '~> 3.1', '>= 3.1.0'
18
+ s.add_runtime_dependency 'rubyzip', '~> 1.1.7', '>= 1.1.7'
19
+
20
+ s.add_development_dependency "rspec"
21
+ s.add_development_dependency "rake"
22
+
23
+ s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = ['ipa_analyzer']
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,2 @@
1
+ require 'ipa_analyzer/version'
2
+ require 'ipa_analyzer/analyzer'
@@ -0,0 +1,154 @@
1
+ require 'tempfile'
2
+ require 'zip'
3
+ require 'zip/filesystem'
4
+ require 'plist'
5
+
6
+ module IpaAnalyzer
7
+ class Analyzer
8
+ def initialize(ipa_path)
9
+ @ipa_path = ipa_path
10
+ @ipa_zipfile = nil
11
+ @app_folder_path = nil
12
+ end
13
+
14
+ def open!
15
+ @ipa_zipfile = Zip::File.open(@ipa_path)
16
+ @app_folder_path = find_app_folder_in_ipa()
17
+ raise "No app folder found in the IPA" if @app_folder_path.nil?
18
+ end
19
+
20
+ def open?
21
+ return !@ipa_zipfile.nil?
22
+ end
23
+
24
+ def close
25
+ if self.open?
26
+ @ipa_zipfile.close()
27
+ end
28
+ end
29
+
30
+ def collect_provision_info
31
+ raise "IPA is not open" unless self.open?
32
+
33
+ result = {
34
+ path_in_ipa: nil,
35
+ content: {}
36
+ }
37
+ mobileprovision_path = "#{@app_folder_path}/embedded.mobileprovision"
38
+ mobileprovision_entry = @ipa_zipfile.find_entry(mobileprovision_path)
39
+
40
+ raise "Embedded mobile provisioning file not found in (#{@ipa_path}) at path (#{mobileprovision_path})" unless mobileprovision_entry
41
+ result[:path_in_ipa] = "#{mobileprovision_entry}"
42
+
43
+ tempfile = Tempfile.new(::File.basename(mobileprovision_entry.name))
44
+ begin
45
+ @ipa_zipfile.extract(mobileprovision_entry, tempfile.path){ override = true }
46
+ plist = Plist::parse_xml(`security cms -D -i #{tempfile.path}`)
47
+
48
+ plist.each do |key, value|
49
+ next if key == "DeveloperCertificates"
50
+
51
+ parse_value = nil
52
+ case value
53
+ when Hash
54
+ parse_value = value
55
+ when Array
56
+ parse_value = value
57
+ else
58
+ parse_value = value.to_s
59
+ end
60
+
61
+ result[:content][key] = parse_value
62
+ end
63
+
64
+ rescue => e
65
+ puts e.message
66
+ result = nil
67
+ ensure
68
+ tempfile.close and tempfile.unlink
69
+ end
70
+ return result
71
+ end
72
+
73
+ def collect_info_plist_info
74
+ raise "IPA is not open" unless self.open?
75
+
76
+ result = {
77
+ path_in_ipa: nil,
78
+ content: {}
79
+ }
80
+ info_plist_entry = @ipa_zipfile.find_entry("#{@app_folder_path}/Info.plist")
81
+
82
+ raise "File 'Info.plist' not found in #{@ipa_path}" unless info_plist_entry
83
+ result[:path_in_ipa] = "#{info_plist_entry}"
84
+
85
+ tempfile = Tempfile.new(::File.basename(info_plist_entry.name))
86
+ begin
87
+ @ipa_zipfile.extract(info_plist_entry, tempfile.path){ override = true }
88
+ # convert from binary Plist to XML Plist
89
+ unless system("plutil -convert xml1 '#{tempfile.path}'")
90
+ raise "Failed to convert binary Plist to XML"
91
+ end
92
+ plist = Plist::parse_xml(tempfile.path)
93
+
94
+ plist.each do |key, value|
95
+ parse_value = nil
96
+ case value
97
+ when Hash
98
+ parse_value = value
99
+ when Array
100
+ parse_value = value
101
+ else
102
+ parse_value = value.to_s
103
+ end
104
+
105
+ result[:content][key] = parse_value
106
+ end
107
+
108
+ rescue => e
109
+ puts e.message
110
+ result = nil
111
+ ensure
112
+ tempfile.close and tempfile.unlink
113
+ end
114
+ return result
115
+ end
116
+
117
+
118
+ private
119
+ #
120
+ # Find the .app folder which contains both the "embedded.mobileprovision"
121
+ # and "Info.plist" files.
122
+ def find_app_folder_in_ipa
123
+ raise "IPA is not open" unless self.open?
124
+
125
+ # Check the most common location
126
+ app_folder_in_ipa = "Payload/#{File.basename(@ipa_path, File.extname(@ipa_path))}.app"
127
+ #
128
+ mobileprovision_entry = @ipa_zipfile.find_entry("#{app_folder_in_ipa}/embedded.mobileprovision")
129
+ info_plist_entry = @ipa_zipfile.find_entry("#{app_folder_in_ipa}/Info.plist")
130
+ #
131
+ if !mobileprovision_entry.nil? and !info_plist_entry.nil?
132
+ return app_folder_in_ipa
133
+ end
134
+
135
+ # It's somewhere else - let's find it!
136
+ @ipa_zipfile.dir.entries("Payload").each do |dir_entry|
137
+ if dir_entry =~ /.app$/
138
+ app_folder_in_ipa = "Payload/#{dir_entry}"
139
+ mobileprovision_entry = @ipa_zipfile.find_entry("#{app_folder_in_ipa}/embedded.mobileprovision")
140
+ info_plist_entry = @ipa_zipfile.find_entry("#{app_folder_in_ipa}/Info.plist")
141
+
142
+ if !mobileprovision_entry.nil? and !info_plist_entry.nil?
143
+ break
144
+ end
145
+ end
146
+ end
147
+
148
+ if !mobileprovision_entry.nil? and !info_plist_entry.nil?
149
+ return app_folder_in_ipa
150
+ end
151
+ return nil
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,3 @@
1
+ module IpaAnalyzer
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipa_analyzer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Bitrise
8
+ - Viktor Benei
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: plist
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.1.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '3.1'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.0
34
+ - !ruby/object:Gem::Dependency
35
+ name: rubyzip
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.1.7
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.1.7
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: 1.1.7
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.7
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ description: Analyze an iOS .ipa file. Can be used as a CLI and can print the information
83
+ in JSON so it can be used by other tools.
84
+ email: letsconnect@bitrise.io
85
+ executables:
86
+ - ipa_analyzer
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - "./Gemfile"
91
+ - "./Gemfile.lock"
92
+ - "./LICENSE"
93
+ - "./README.md"
94
+ - "./_scripts/make.sh"
95
+ - "./ipa_analyzer.gemspec"
96
+ - "./lib/ipa_analyzer.rb"
97
+ - "./lib/ipa_analyzer/analyzer.rb"
98
+ - "./lib/ipa_analyzer/version.rb"
99
+ - bin/ipa_analyzer
100
+ homepage: https://github.com/bitrise-io/ipa_analyzer
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.2.2
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: iOS .ipa analyzer
124
+ test_files: []