cocoapods-privacy 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6483e616caac6ef86430a25f23bc8f9dd0ea9b61677392d2dbe348170d679d37
|
4
|
+
data.tar.gz: 07a796d452a9ad9a6e9e8b0883612c9ec9fb38b40d9dce46a6ecd290026b7a6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a529a41b248f662a4ad21b55a73902875c0b8d530128fe36c472b36888cd511ff5e6a15c7ec1d7a8f8f6f28592dfc3055b8216b8f050c466bc00954973283581
|
7
|
+
data.tar.gz: 12c36de4eee58b504c3bd6725ba16cb71db052560882264507289a46381ecf8f980533e10b21582fc9349aa2c709f13cfaed83494c0b887179a596d8c0421b1e
|
data/README.md
CHANGED
@@ -24,6 +24,10 @@ There has 3 keys in defalut config, you should custom it!
|
|
24
24
|
"source.black.list": ["replace me such as github.com"],
|
25
25
|
"api.template.url": "https://raw.githubusercontent.com/ymoyao/cocoapods-privacy/main/resources/NSPrivacyAccessedAPITypes.plist"
|
26
26
|
```
|
27
|
+
After custom,you can set local config like this
|
28
|
+
```
|
29
|
+
$ pod privacy config /yourfilepath/config.json
|
30
|
+
```
|
27
31
|
|
28
32
|
#### To Component
|
29
33
|
```
|
@@ -36,12 +36,12 @@ module PrivacyHunter
|
|
36
36
|
[apis,keyword_type_map]
|
37
37
|
end
|
38
38
|
|
39
|
-
def self.search_pricacy_apis(source_folders)
|
39
|
+
def self.search_pricacy_apis(source_folders,exclude_folders=[])
|
40
40
|
apis,keyword_type_map = formatter_privacy_template()
|
41
41
|
|
42
42
|
# 优化写法,一次循环完成所有查询
|
43
43
|
datas = []
|
44
|
-
apis_found = search_files(source_folders, apis)
|
44
|
+
apis_found = search_files(source_folders, exclude_folders, apis)
|
45
45
|
unless apis_found.empty?
|
46
46
|
apis_found.each do |keyword,reason|
|
47
47
|
reasons = reason.split(',')
|
@@ -197,7 +197,7 @@ module PrivacyHunter
|
|
197
197
|
end
|
198
198
|
|
199
199
|
#搜索所有子文件夹
|
200
|
-
def self.search_files(folder_paths, apis)
|
200
|
+
def self.search_files(folder_paths, exclude_folders, apis)
|
201
201
|
# 获取文件夹下所有文件(包括子文件夹)
|
202
202
|
all_files = []
|
203
203
|
folder_paths.each do |folder|
|
@@ -212,9 +212,20 @@ module PrivacyHunter
|
|
212
212
|
# 过滤掉目录路径,只保留文件路径,并将其添加到 all_files 数组中
|
213
213
|
all_files += files_in_folder.reject { |file| File.directory?(file) }
|
214
214
|
end
|
215
|
+
|
216
|
+
# 获取需要排除的文件
|
217
|
+
exclude_files = []
|
218
|
+
exclude_folders.each do |folder|
|
219
|
+
files_in_folder = Dir.glob(folder, File::FNM_DOTMATCH)
|
220
|
+
exclude_files += files_in_folder.reject { |file| File.directory?(file) }
|
221
|
+
end
|
222
|
+
|
223
|
+
# 剔除掉需要排除的文件
|
224
|
+
all_files = all_files.uniq - exclude_files.uniq
|
225
|
+
|
215
226
|
# 遍历文件进行检索
|
216
227
|
apis_found = {}
|
217
|
-
all_files.
|
228
|
+
all_files.each_with_index do |file_path, index|
|
218
229
|
api_contains = contains_apis?(file_path, apis)
|
219
230
|
apis_found = apis_found.merge(api_contains)
|
220
231
|
|
@@ -3,6 +3,10 @@ require 'cocoapods-core/specification/dsl/attribute_support'
|
|
3
3
|
require 'cocoapods-core/specification/dsl/attribute'
|
4
4
|
require 'xcodeproj'
|
5
5
|
|
6
|
+
KSource_Files_Key = 'source_files' #不存在单数 source_file
|
7
|
+
KExclude_Files_Key = 'exclude_files' #不存在单数 exclude_file
|
8
|
+
KResource_Bundle_Key = 'resource_bundle' #resource_bundle 和 resource_bundles 这两个参数本质上是一样的,resource_bundle 也能指向多个参数
|
9
|
+
|
6
10
|
class BBRow
|
7
11
|
attr_accessor :content, :is_comment, :is_spec_start, :is_spec_end, :key, :value
|
8
12
|
|
@@ -29,11 +33,10 @@ class BBRow
|
|
29
33
|
end
|
30
34
|
|
31
35
|
class BBSpec
|
32
|
-
attr_accessor :name, :alias_name, :full_name, :parent, :rows, :
|
36
|
+
attr_accessor :name, :alias_name, :full_name, :parent, :rows, :privacy_sources_files, :privacy_exclude_files, :privacy_file
|
33
37
|
|
34
38
|
def initialize(name,alias_name,full_name)
|
35
39
|
@rows = []
|
36
|
-
@privacy_sources = nil
|
37
40
|
@name = name
|
38
41
|
@alias_name = alias_name
|
39
42
|
@full_name = full_name
|
@@ -57,96 +60,139 @@ class BBSpec
|
|
57
60
|
"#{@full_name}.#{name}"
|
58
61
|
end
|
59
62
|
|
63
|
+
# 单独属性转成spec字符串,方便解析
|
64
|
+
def assemble_single_property_to_complex(property_name)
|
65
|
+
property_name += "s" if property_name == KResource_Bundle_Key #检测到单数resource_bundle,直接转成复数,功能一致
|
66
|
+
property_name
|
67
|
+
end
|
68
|
+
|
60
69
|
def privacy_handle(podspec_file_path)
|
61
|
-
source_files_index = 1
|
62
70
|
@rows.each_with_index do |line, index|
|
63
71
|
if !line || line.is_a?(BBSpec) || !line.key || line.key.empty?
|
64
72
|
next
|
65
73
|
end
|
66
74
|
|
67
|
-
if !line.is_comment && line.key.include?(".
|
75
|
+
if !line.is_comment && line.key.include?("." + KResource_Bundle_Key)
|
68
76
|
@has_resource_bundle = true
|
69
|
-
elsif !line.is_comment && line.key.include?(".
|
70
|
-
|
71
|
-
if spec && !spec.attributes_hash['source_files'].nil?
|
72
|
-
source_files_value = spec.attributes_hash['source_files']
|
73
|
-
if source_files_value.is_a?(String) && !source_files_value.empty?
|
74
|
-
source_files_array = [source_files_value]
|
75
|
-
elsif source_files_value.is_a?(Array)
|
76
|
-
# 如果已经是数组,直接使用
|
77
|
-
source_files_array = source_files_value
|
78
|
-
else
|
79
|
-
# 其他情况,默认为空数组
|
80
|
-
source_files_array = []
|
81
|
-
end
|
82
|
-
|
83
|
-
source_files_index = index
|
84
|
-
@privacy_sources = source_files_array.map do |file_path|
|
85
|
-
File.join(File.dirname(podspec_file_path), file_path.strip)
|
86
|
-
end
|
87
|
-
end
|
77
|
+
elsif !line.is_comment && line.key.include?("." + KSource_Files_Key)
|
78
|
+
@source_files_index = index
|
88
79
|
end
|
89
80
|
end
|
90
81
|
create_privacy_file_if_need(podspec_file_path)
|
91
|
-
modify_privacy_resource_bundle_if_need(
|
82
|
+
modify_privacy_resource_bundle_if_need(podspec_file_path)
|
92
83
|
end
|
93
84
|
|
94
85
|
# 对应Spec新增隐私文件
|
95
86
|
def create_privacy_file_if_need(podspec_file_path)
|
96
|
-
if @
|
87
|
+
if @source_files_index
|
97
88
|
PrivacyUtils.create_privacy_if_empty(File.join(File.dirname(podspec_file_path), @privacy_file))
|
98
89
|
end
|
99
90
|
end
|
100
91
|
|
101
|
-
#
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
92
|
+
# 这里处理所有多行参数的解析,目前处理 source_files\exclude_files\resource_bundle 这三种
|
93
|
+
# 输入格式 ['.source_files':false,'.exclude_files':true......] => true 代表会根据获取的重置属性,需要把多行多余的进行删除
|
94
|
+
# 返回格式 {'.source_files':BBRow,......}
|
95
|
+
def fetch_mul_line_property(propertys_mul_line_hash)
|
96
|
+
property_hash = {}
|
97
|
+
line_processing = nil
|
98
|
+
property_config_processing = nil
|
99
|
+
@rows.each_with_index do |line, index|
|
100
|
+
if !line || line.is_a?(BBSpec) || line.is_comment
|
101
|
+
next
|
102
|
+
end
|
103
|
+
|
104
|
+
property_find = propertys_mul_line_hash.find { |key, _| line.key && line.key.include?(key) } #查找不到返回nil 查到返回数组,key, value 分别在第一和第二个参数
|
105
|
+
if property_find
|
106
|
+
property_config_processing = property_find
|
107
|
+
end
|
108
|
+
|
109
|
+
if property_config_processing
|
110
|
+
begin
|
111
|
+
property_name = property_config_processing.first
|
112
|
+
is_replace_line = property_config_processing.second
|
113
|
+
if line_processing
|
114
|
+
code = "#{line_processing.value}#{line.content}"
|
115
|
+
else
|
116
|
+
code = "#{line.value}"
|
117
|
+
end
|
118
|
+
|
119
|
+
# 清除 content 和 value, 后面会把所有的content 组装起来,多余的内容要清除,避免重复
|
120
|
+
if is_replace_line
|
121
|
+
line.content = ''
|
122
|
+
line.value = nil
|
110
123
|
end
|
111
124
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
# 清除 content 和 value, 后面会把所有的resource_bundle 组装起来,多余的内容要清除,避免重复
|
122
|
-
line.content = ''
|
123
|
-
line.value = nil
|
124
|
-
|
125
|
-
RubyVM::InstructionSequence.compile(code)
|
126
|
-
origin_resource_bundle = eval(code)
|
127
|
-
rescue SyntaxError, StandardError => e
|
128
|
-
unless line_incomplete
|
129
|
-
line_incomplete = line
|
130
|
-
end
|
131
|
-
line_incomplete.value = code if line_incomplete #存储当前残缺的value,和后面完整的进行拼接
|
132
|
-
next
|
133
|
-
end
|
134
|
-
|
135
|
-
final_line = (line_incomplete ? line_incomplete : line)
|
136
|
-
|
137
|
-
merged_resource_bundle = origin_resource_bundle.merge(privacy_resource_bundle)
|
138
|
-
@resource_bundle = merged_resource_bundle
|
139
|
-
final_line.value = merged_resource_bundle
|
140
|
-
final_line.content = "#{final_line.key}= #{final_line.value}"
|
141
|
-
break
|
125
|
+
property_name_complex = assemble_single_property_to_complex(property_name)
|
126
|
+
spec_str = "Pod::Spec.new do |s|; s.#{property_name_complex} = #{code}; end;"
|
127
|
+
RubyVM::InstructionSequence.compile(spec_str)
|
128
|
+
spec = eval(spec_str)
|
129
|
+
property_value = spec.attributes_hash[property_name_complex]
|
130
|
+
rescue SyntaxError, StandardError => e
|
131
|
+
unless line_processing
|
132
|
+
line_processing = line
|
142
133
|
end
|
134
|
+
line_processing.value = code if line_processing #存储当前残缺的value,和后面完整的进行拼接
|
135
|
+
next
|
143
136
|
end
|
144
|
-
|
145
|
-
|
137
|
+
|
138
|
+
final_line = (line_processing ? line_processing : line)
|
139
|
+
final_line.value = property_value
|
140
|
+
property_hash[property_name] = final_line
|
141
|
+
line_processing = nil
|
142
|
+
property_config_processing = nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
property_hash
|
147
|
+
end
|
148
|
+
|
149
|
+
# 处理字符串或者数组,使其全都转为数组,并转成实际文件夹地址
|
150
|
+
def handle_string_or_array_files(podspec_file_path,line)
|
151
|
+
value = line.value
|
152
|
+
if value.is_a?(String) && !value.empty?
|
153
|
+
array = [value]
|
154
|
+
elsif value.is_a?(Array)
|
155
|
+
array = value
|
156
|
+
else
|
157
|
+
array = []
|
158
|
+
end
|
159
|
+
|
160
|
+
files = array.map do |file_path|
|
161
|
+
File.join(File.dirname(podspec_file_path), file_path.strip)
|
162
|
+
end
|
163
|
+
files
|
164
|
+
end
|
165
|
+
|
166
|
+
# 把新增的隐私文件 映射给 podspec && 解析 privacy_sources_files && 解析 privacy_exclude_files
|
167
|
+
def modify_privacy_resource_bundle_if_need(podspec_file_path)
|
168
|
+
if @source_files_index
|
169
|
+
privacy_resource_bundle = { "#{full_name}.privacy" => @privacy_file }
|
170
|
+
|
171
|
+
# 这里处理所有多行参数的解析,目前处理 source_files\exclude_files\resource_bundle 这三种
|
172
|
+
propertys_mul_line_hash = {}
|
173
|
+
propertys_mul_line_hash[KSource_Files_Key] = false
|
174
|
+
propertys_mul_line_hash[KExclude_Files_Key] = false
|
175
|
+
if @has_resource_bundle
|
176
|
+
propertys_mul_line_hash[KResource_Bundle_Key] = true #需要根据生成的重置属性
|
177
|
+
else # 如果原先没有resource_bundle,需要单独加一行resource_bundle
|
178
|
+
space = PrivacyUtils.count_spaces_before_first_character(rows[@source_files_index].content)
|
146
179
|
line = "#{alias_name}.resource_bundle = #{privacy_resource_bundle}"
|
147
180
|
line = PrivacyUtils.add_spaces_to_string(line,space)
|
148
181
|
row = BBRow.new(line)
|
149
|
-
@rows.insert(source_files_index+1, row)
|
182
|
+
@rows.insert(@source_files_index+1, row)
|
183
|
+
end
|
184
|
+
property_value_hash = fetch_mul_line_property(propertys_mul_line_hash)
|
185
|
+
property_value_hash.each do |property, line|
|
186
|
+
if property == KSource_Files_Key #处理 source_files
|
187
|
+
@privacy_sources_files = handle_string_or_array_files(podspec_file_path,line)
|
188
|
+
elsif property == KExclude_Files_Key #处理 exclude_files
|
189
|
+
@privacy_exclude_files = handle_string_or_array_files(podspec_file_path,line)
|
190
|
+
elsif property == KResource_Bundle_Key #处理 原有resource_bundle 合并隐私清单文件映射
|
191
|
+
merged_resource_bundle = line.value.merge(privacy_resource_bundle)
|
192
|
+
@resource_bundle = merged_resource_bundle
|
193
|
+
line.value = merged_resource_bundle
|
194
|
+
line.content = "#{line.key}= #{line.value}"
|
195
|
+
end
|
150
196
|
end
|
151
197
|
end
|
152
198
|
end
|
@@ -158,7 +204,7 @@ module PrivacyModule
|
|
158
204
|
public
|
159
205
|
|
160
206
|
# 处理工程
|
161
|
-
def self.load_project(folds)
|
207
|
+
def self.load_project(folds,exclude_folds)
|
162
208
|
project_path = PrivacyUtils.project_path()
|
163
209
|
resources_folder_path = File.join(File.basename(project_path, File.extname(project_path)),'Resources')
|
164
210
|
privacy_file_path = File.join(resources_folder_path,PrivacyUtils.privacy_name)
|
@@ -191,7 +237,7 @@ module PrivacyModule
|
|
191
237
|
|
192
238
|
# 开始检索api,并返回json 字符串数据
|
193
239
|
PrivacyLog.clean_result_log()
|
194
|
-
json_data = PrivacyHunter.search_pricacy_apis(folds)
|
240
|
+
json_data = PrivacyHunter.search_pricacy_apis(folds,exclude_folds)
|
195
241
|
|
196
242
|
# 将数据写入隐私清单文件
|
197
243
|
PrivacyHunter.write_to_privacy(json_data,privacy_file_path)
|
@@ -203,9 +249,11 @@ module PrivacyModule
|
|
203
249
|
puts "👇👇👇👇👇👇 Start analysis component privacy 👇👇👇👇👇👇"
|
204
250
|
PrivacyLog.clean_result_log()
|
205
251
|
privacy_hash = PrivacyModule.check(podspec_file_path)
|
206
|
-
privacy_hash.each do |privacy_file_path,
|
252
|
+
privacy_hash.each do |privacy_file_path, hash|
|
207
253
|
PrivacyLog.write_to_result_log("#{privacy_file_path}: \n")
|
208
|
-
|
254
|
+
source_files = hash[KSource_Files_Key]
|
255
|
+
exclude_files = hash[KExclude_Files_Key]
|
256
|
+
data = PrivacyHunter.search_pricacy_apis(source_files,exclude_files)
|
209
257
|
PrivacyHunter.write_to_privacy(data,privacy_file_path) unless data.empty?
|
210
258
|
end
|
211
259
|
PrivacyLog.result_log_tip()
|
@@ -346,9 +394,10 @@ module PrivacyModule
|
|
346
394
|
|
347
395
|
def self.fetch_privacy_hash(rows,podspec_file_path)
|
348
396
|
privacy_hash = {}
|
349
|
-
|
350
|
-
|
351
|
-
|
397
|
+
specs = rows.select { |row| row.is_a?(BBSpec) }
|
398
|
+
specs.each do |spec|
|
399
|
+
value = spec.privacy_sources_files ? {KSource_Files_Key => spec.privacy_sources_files,KExclude_Files_Key => spec.privacy_exclude_files || []} : {}
|
400
|
+
privacy_hash[File.join(File.dirname(podspec_file_path),spec.privacy_file)] = value
|
352
401
|
privacy_hash.merge!(fetch_privacy_hash(spec.rows,podspec_file_path))
|
353
402
|
end
|
354
403
|
privacy_hash
|
@@ -83,16 +83,19 @@ module Pod
|
|
83
83
|
|
84
84
|
# 存储本地调试组件
|
85
85
|
development_folds = []
|
86
|
+
exclude_folds = []
|
86
87
|
|
87
88
|
# 获取组件所在工程的pods 目录
|
88
89
|
pod_folds = modules.map{ |spec|
|
89
90
|
name = spec.name.split('/').first
|
91
|
+
|
90
92
|
fold = File.join(@sandbox.root,name)
|
91
93
|
podspec_file_path_develop = validate_development_pods(name)
|
92
94
|
# 先验证是否是指向本地的组件(发现有的情况下 组件指向本地Pods 下依旧还是会有该组件,所以这里先判断本地的)
|
93
95
|
if podspec_file_path_develop
|
94
96
|
podspec_fold_path = File.dirname(podspec_file_path_develop)
|
95
97
|
source_files = spec.attributes_hash['source_files']
|
98
|
+
exclude_files = spec.attributes_hash['exclude_files']
|
96
99
|
if source_files && !source_files.empty?
|
97
100
|
if source_files.is_a?(String) && !source_files.empty?
|
98
101
|
development_folds << File.join(podspec_fold_path,source_files)
|
@@ -101,6 +104,17 @@ module Pod
|
|
101
104
|
development_folds << File.join(podspec_fold_path,file)
|
102
105
|
end
|
103
106
|
end
|
107
|
+
|
108
|
+
# 处理exclude_files 排除文件夹
|
109
|
+
if exclude_files && !exclude_files.empty?
|
110
|
+
if exclude_files.is_a?(String) && !exclude_files.empty?
|
111
|
+
exclude_folds << File.join(podspec_fold_path,exclude_files)
|
112
|
+
elsif exclude_files.is_a?(Array)
|
113
|
+
exclude_files.each do |file|
|
114
|
+
exclude_folds << File.join(podspec_fold_path,file)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
104
118
|
end
|
105
119
|
nil
|
106
120
|
elsif Dir.exist?(fold)
|
@@ -118,7 +132,7 @@ module Pod
|
|
118
132
|
puts "无组件或工程目录, 请检查工程"
|
119
133
|
else
|
120
134
|
# 处理工程隐私协议
|
121
|
-
PrivacyModule.load_project(pod_folds)
|
135
|
+
PrivacyModule.load_project(pod_folds,exclude_folds.uniq)
|
122
136
|
end
|
123
137
|
puts "👆👆👆👆👆👆 End analysis project privacy 👆👆👆👆👆👆"
|
124
138
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocoapods-privacy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- youhui
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|