cocoapods-privacy 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,202 +3,6 @@ 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
-
10
- class BBRow
11
- attr_accessor :content, :is_comment, :is_spec_start, :is_spec_end, :key, :value
12
-
13
- def initialize(content, is_comment=false, is_spec_start=false, is_spec_end=false)
14
- @content = content
15
- @is_comment = is_comment
16
- @is_spec_start = is_spec_start
17
- @is_spec_end = is_spec_end
18
-
19
- parse_key_value
20
- end
21
-
22
- def parse_key_value
23
- # 在这里添加提取 key 和 value 的逻辑
24
- if @content.include?('=')
25
- key_value_split = @content.split('=')
26
- @key = key_value_split[0]
27
- @value = key_value_split[1..-1].join('=')
28
- else
29
- @key = nil
30
- @value = nil
31
- end
32
- end
33
- end
34
-
35
- class BBSpec
36
- attr_accessor :name, :alias_name, :full_name, :parent, :rows, :privacy_sources_files, :privacy_exclude_files, :privacy_file
37
-
38
- def initialize(name,alias_name,full_name)
39
- @rows = []
40
- @name = name
41
- @alias_name = alias_name
42
- @full_name = full_name
43
- @privacy_file = "Pod/Privacy/#{full_name}/PrivacyInfo.xcprivacy"
44
- end
45
-
46
-
47
- def uniq_full_name_in_parent(name)
48
- names = []
49
- @rows.each_with_index do |line, index|
50
- if line && line.is_a?(BBSpec)
51
- names << line.name
52
- end
53
- end
54
-
55
- #判断names 中是否包含 name,如果包含,那么给name 添加一个 “.diff” 后缀,一直到names 中没有包含name为止
56
- while names.include?(name)
57
- name = "#{name}.diff"
58
- end
59
-
60
- "#{@full_name}.#{name}"
61
- end
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
-
69
- def privacy_handle(podspec_file_path)
70
- @rows.each_with_index do |line, index|
71
- if !line || line.is_a?(BBSpec) || !line.key || line.key.empty?
72
- next
73
- end
74
-
75
- if !line.is_comment && line.key.include?("." + KResource_Bundle_Key)
76
- @has_resource_bundle = true
77
- elsif !line.is_comment && line.key.include?("." + KSource_Files_Key)
78
- @source_files_index = index
79
- end
80
- end
81
- create_privacy_file_if_need(podspec_file_path)
82
- modify_privacy_resource_bundle_if_need(podspec_file_path)
83
- end
84
-
85
- # 对应Spec新增隐私文件
86
- def create_privacy_file_if_need(podspec_file_path)
87
- if @source_files_index
88
- PrivacyUtils.create_privacy_if_empty(File.join(File.dirname(podspec_file_path), @privacy_file))
89
- end
90
- end
91
-
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
123
- end
124
-
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
133
- end
134
- line_processing.value = code if line_processing #存储当前残缺的value,和后面完整的进行拼接
135
- next
136
- end
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)
179
- line = "#{alias_name}.resource_bundle = #{privacy_resource_bundle}"
180
- line = PrivacyUtils.add_spaces_to_string(line,space)
181
- row = BBRow.new(line)
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
196
- end
197
- end
198
- end
199
- end
200
-
201
-
202
6
  module PrivacyModule
203
7
 
204
8
  public
@@ -246,9 +50,10 @@ module PrivacyModule
246
50
 
247
51
  # 处理组件
248
52
  def self.load_module(podspec_file_path)
53
+ specManager = BB::BBSpecManager.new(KSpecTypePrivacy)
249
54
  puts "👇👇👇👇👇👇 Start analysis component privacy 👇👇👇👇👇👇"
250
55
  PrivacyLog.clean_result_log()
251
- privacy_hash = PrivacyModule.check(podspec_file_path)
56
+ privacy_hash = specManager.check(podspec_file_path)
252
57
  privacy_hash.each do |privacy_file_path, hash|
253
58
  PrivacyLog.write_to_result_log("#{privacy_file_path}: \n")
254
59
  source_files = hash[KSource_Files_Key]
@@ -260,147 +65,6 @@ module PrivacyModule
260
65
  puts "👆👆👆👆👆👆 End analysis component privacy 👆👆👆👆👆👆"
261
66
  end
262
67
 
263
- def self.check(podspec_file_path)
264
- # Step 1: 读取podspec
265
- lines = read_podspec(podspec_file_path)
266
-
267
- # Step 2: 逐行解析并转位BBRow 模型
268
- rows = parse_row(lines)
269
-
270
- # Step 3.1:如果Row 是属于Spec 内,那么聚拢成BBSpec,
271
- # Step 3.2:BBSpec 内使用数组存储其Spec 内的行
272
- # Step 3.3 在合适位置给每个有效的spec都创建一个 隐私模版,并修改其podspec 引用
273
- combin_sepcs_and_rows = combin_sepc_if_need(rows,podspec_file_path)
274
-
275
- # Step 4: 展开修改后的Spec,重新转换成 BBRow
276
- rows = unfold_sepc_if_need(combin_sepcs_and_rows)
277
-
278
- # Step 5: 打开隐私模版,并修改其podspec文件,并逐行写入
279
- File.open(podspec_file_path, 'w') do |file|
280
- # 逐行写入 rows
281
- rows.each do |row|
282
- file.puts(row.content)
283
- end
284
- end
285
-
286
-
287
- # Step 6: 获取privacy 相关信息,传递给后续处理
288
- privacy_hash = fetch_privacy_hash(combin_sepcs_and_rows,podspec_file_path).compact
289
- filtered_privacy_hash = privacy_hash.reject { |_, value| value.empty? }
290
- filtered_privacy_hash
291
- end
292
-
293
- private
294
- def self.read_podspec(file_path)
295
- File.readlines(file_path)
296
- end
297
-
298
- def self.parse_row(lines)
299
- rows = []
300
- code_stack = [] #栈,用来排除if end 等对spec 的干扰
301
-
302
- lines.each do |line|
303
- content = line.strip
304
- is_comment = content.start_with?('#')
305
- is_spec_start = !is_comment && (content.include?('Pod::Spec.new') || content.include?('.subspec'))
306
- is_if = !is_comment && content.start_with?('if')
307
- is_end = !is_comment && content.start_with?('end')
308
-
309
- # 排除if end 对spec_end 的干扰
310
- code_stack.push('spec') if is_spec_start
311
- code_stack.push('if') if is_if
312
- stack_last = code_stack.last
313
- is_spec_end = is_end && stack_last && stack_last == 'spec'
314
- is_if_end = is_end && stack_last && stack_last == 'if'
315
- code_stack.pop if is_spec_end || is_if_end
316
-
317
- row = BBRow.new(line, is_comment, is_spec_start, is_spec_end)
318
- rows << row
319
- end
320
- rows
321
- end
322
-
323
- # 数据格式:
324
- # [
325
- # BBRow
326
- # BBRow
327
- # BBSpec
328
- # rows
329
- # [
330
- # BBRow
331
- # BBSpec
332
- # BBRow
333
- # BBRow
334
- # ]
335
- # BBRow
336
- # ......
337
- # ]
338
- # 合并Row -> Spec(会存在部分行不在Spec中:Spec new 之前的注释)
339
- def self.combin_sepc_if_need(rows,podspec_file_path)
340
- spec_stack = []
341
- result_rows = []
342
- default_name = File.basename(podspec_file_path, File.extname(podspec_file_path))
343
-
344
- rows.each do |row|
345
- if row.is_spec_start
346
- # 获取父spec
347
- parent_spec = spec_stack.last
348
-
349
- # 创建 spec
350
- name = row.content.split("'")[1]&.strip || default_name
351
- alias_name = row.content.split("|")[1]&.strip
352
- full_name = parent_spec ? parent_spec.uniq_full_name_in_parent(name) : name
353
-
354
- spec = BBSpec.new(name,alias_name,full_name)
355
- spec.rows << row
356
- spec.parent = parent_spec
357
-
358
- # 当存在 spec 时,存储在 spec.rows 中;不存在时,直接存储在外层
359
- (parent_spec ? parent_spec.rows : result_rows ) << spec
360
-
361
- # spec 入栈
362
- spec_stack.push(spec)
363
- elsif row.is_spec_end
364
- # 当前 spec 的 rows 加入当前行
365
- spec_stack.last&.rows << row
366
-
367
- #执行隐私协议修改
368
- spec_stack.last.privacy_handle(podspec_file_path)
369
-
370
- # spec 出栈
371
- spec_stack.pop
372
- else
373
- # 当存在 spec 时,存储在 spec.rows 中;不存在时,直接存储在外层
374
- (spec_stack.empty? ? result_rows : spec_stack.last.rows) << row
375
- end
376
- end
377
-
378
- result_rows
379
- end
380
-
381
- # 把所有的spec中的rows 全部展开,拼接成一级数组【BBRow】
382
- def self.unfold_sepc_if_need(rows)
383
- result_rows = []
384
- rows.each do |row|
385
- if row.is_a?(BBSpec)
386
- result_rows += unfold_sepc_if_need(row.rows)
387
- else
388
- result_rows << row
389
- end
390
- end
391
- result_rows
392
- end
393
-
394
68
 
395
- def self.fetch_privacy_hash(rows,podspec_file_path)
396
- privacy_hash = {}
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
401
- privacy_hash.merge!(fetch_privacy_hash(spec.rows,podspec_file_path))
402
- end
403
- privacy_hash
404
- end
405
69
 
406
70
  end
@@ -1,4 +1,5 @@
1
1
  require 'digest'
2
+ require 'cocoapods-privacy/command'
2
3
 
3
4
  module PrivacyUtils
4
5
 
@@ -49,31 +50,22 @@ module PrivacyUtils
49
50
  end
50
51
 
51
52
  def self.cache_privacy_fold
52
- # 本地缓存目录
53
- cache_directory = File.expand_path('~/.cache')
54
-
55
- # 目标文件夹路径
56
- target_directory = File.join(cache_directory, 'cocoapods-privacy', 'privacy')
57
-
58
- # 如果文件夹不存在,则创建
59
- FileUtils.mkdir_p(target_directory) unless Dir.exist?(target_directory)
60
-
61
- target_directory
53
+ Common::Config.instance.cache_privacy_fold
62
54
  end
63
55
 
64
56
  # etag 文件夹
65
57
  def self.cache_privacy_etag_fold
66
- File.join(cache_privacy_fold,'etag')
58
+ Common::Config.instance.cache_privacy_etag_fold
67
59
  end
68
60
 
69
61
  # config.json 文件
70
62
  def self.cache_config_file
71
- config_file = File.join(cache_privacy_fold, 'config.json')
63
+ Common::Config.instance.cache_config_file
72
64
  end
73
65
 
74
66
  # config.json 文件
75
67
  def self.cache_log_file
76
- config_file = File.join(cache_privacy_fold, 'privacy.log')
68
+ Common::Config.instance.cache_log_file
77
69
  end
78
70
 
79
71
  # 创建默认隐私协议文件
@@ -122,7 +122,6 @@ module Pod
122
122
  end
123
123
  }.compact
124
124
 
125
-
126
125
  pod_folds += development_folds # 拼接本地调试和远端的pod目录
127
126
  pod_folds += [formatter_search_fold(PrivacyUtils.project_code_fold)].compact # 拼接工程同名主目录
128
127
  pod_folds += custom_folds || [] # 拼接外部传入的自定义目录
@@ -32,7 +32,7 @@ module Pod
32
32
  return true if Pod::Config.instance.is_all
33
33
 
34
34
  # 判断域名白名单 和 黑名单,确保该组件是自己的组件,第三方sdk不做检索
35
- config = Privacy::Config.instance
35
+ config = Common::Config.instance
36
36
 
37
37
  ## 规则:
38
38
  ## 1、白名单/黑名单是通过组件podspec 中 source 字段的值来匹配,包含关键词即为命中,所有可以是git关键的域名,也可以是完整的git链接
@@ -55,6 +55,9 @@ module Pod
55
55
  return nil unless spec
56
56
  if spec.source && spec.source.key?(:git)
57
57
  spec.source[:git]
58
+ elsif spec.source && spec.source.key?(:http)
59
+ # 如果 Git 源地址不存在,尝试获取 HTTP 源地址
60
+ spec.source[:http]
58
61
  else
59
62
  recursive_git_source(spec.instance_variable_get(:@parent))
60
63
  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.5.3
4
+ version: 0.6.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-04-12 00:00:00.000000000 Z
11
+ date: 2025-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -49,13 +49,23 @@ files:
49
49
  - README.md
50
50
  - lib/cocoapods-privacy.rb
51
51
  - lib/cocoapods-privacy/command.rb
52
+ - lib/cocoapods-privacy/command/confuse/spec.rb
52
53
  - lib/cocoapods-privacy/command/install.rb
53
54
  - lib/cocoapods-privacy/command/privacy.rb
54
55
  - lib/cocoapods-privacy/command/privacy/config.rb
55
56
  - lib/cocoapods-privacy/command/privacy/install.rb
56
57
  - lib/cocoapods-privacy/command/privacy/spec.rb
58
+ - lib/cocoapods-privacy/common/BBSpec.rb
59
+ - lib/cocoapods-privacy/common/BBSpecManager.rb
60
+ - lib/cocoapods-privacy/common/config.rb
61
+ - lib/cocoapods-privacy/confuse/ConfuseHunter.rb
62
+ - lib/cocoapods-privacy/confuse/ConfuseModule.rb
63
+ - lib/cocoapods-privacy/confuse/ConfuseUtils.rb
64
+ - lib/cocoapods-privacy/confuse/ObjCMethodAPIConverter.rb
65
+ - lib/cocoapods-privacy/confuse/SwiftCallAssembly.rb
66
+ - lib/cocoapods-privacy/confuse/confuse_installer_hook.rb
67
+ - lib/cocoapods-privacy/confuse/confuse_specification_hook.rb
57
68
  - lib/cocoapods-privacy/gem_version.rb
58
- - lib/cocoapods-privacy/privacy/PrivacyConfig.rb
59
69
  - lib/cocoapods-privacy/privacy/PrivacyHunter.rb
60
70
  - lib/cocoapods-privacy/privacy/PrivacyLog.rb
61
71
  - lib/cocoapods-privacy/privacy/PrivacyModule.rb
@@ -84,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
94
  - !ruby/object:Gem::Version
85
95
  version: '0'
86
96
  requirements: []
87
- rubygems_version: 3.4.21
97
+ rubygems_version: 3.5.22
88
98
  signing_key:
89
99
  specification_version: 4
90
100
  summary: A longer description of cocoapods-privacy.
@@ -1,28 +0,0 @@
1
- require 'cocoapods-privacy/command'
2
-
3
- module Privacy
4
- class Config
5
-
6
- def initialize()
7
- config_content = File.read(PrivacyUtils.cache_config_file)
8
- @json = JSON.parse(config_content)
9
- end
10
-
11
- def api_template_url
12
- return @json['api.template.url'] || ""
13
- end
14
-
15
- def source_black_list
16
- return @json['source.black.list'] || []
17
- end
18
-
19
- def source_white_list
20
- return @json['source.white.list'] || []
21
- end
22
-
23
- def self.instance
24
- @instance ||= new
25
- end
26
-
27
- end
28
- end