ronin-web-user_agents 0.1.0.beta1

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.
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'set'
4
+
5
+ class UserAgentsFile
6
+
7
+ attr_reader :path
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ def each_line
14
+ File.open(@path) do |file|
15
+ file.each_line do |line|
16
+ line.chomp!
17
+ yield line
18
+ end
19
+ end
20
+ end
21
+
22
+ PRODUCT_REGEXP = /[A-Z][A-Za-z ]+/
23
+
24
+ PRODUCT_VERSION_REGEXP = /[\d\.]+/
25
+
26
+ EXTENSIONS_REGEXP = /[^\(\)]+(?:\([^\)]+\)[^\(\)]+)?/
27
+
28
+ EXTENSION_SEPARATOR = /\s*;\s+/
29
+
30
+ REGEXP = /(#{PRODUCT_REGEXP})\/(#{PRODUCT_VERSION_REGEXP})(?:\s+\((#{EXTENSIONS_REGEXP})\))?/
31
+
32
+ VERSION_REGEXP = /\d+(?:\.\d+)*/
33
+
34
+ def parse
35
+ each_line do |user_agent|
36
+ matches = user_agent.scan(REGEXP)
37
+
38
+ yield matches
39
+ end
40
+ end
41
+
42
+ def index_product_combinations
43
+ product_combinations = Hash.new { |hash,key| hash[key] = 0 }
44
+
45
+ parse do |matches|
46
+ products = matches.map { |(product,_,_)| product }
47
+
48
+ product_combinations[products] += 1
49
+ end
50
+
51
+ return product_combinations
52
+ end
53
+
54
+ def index_product_versions(search_product)
55
+ product_versions = Hash.new { |hash,key| hash[key] = 0 }
56
+
57
+ parse do |matches|
58
+ _,product_version,_ = *matches.find do |(product,_,_)|
59
+ product == search_product
60
+ end
61
+
62
+ if product_version
63
+ product_versions[product_version] += 1
64
+ end
65
+ end
66
+
67
+ return product_versions
68
+ end
69
+
70
+ def index_product_extensions(target_product)
71
+ product_extensions = Hash.new { |hash,key| hash[key] = 0 }
72
+
73
+ parse do |matches|
74
+ _,_,extensions = matches.find { |(product,_,_)| product == target_product }
75
+
76
+ if extensions
77
+ tree_node = product_extensions
78
+
79
+ product_extensions[extensions.split(EXTENSION_SEPARATOR)] += 1
80
+ end
81
+ end
82
+
83
+ return product_extensions
84
+ end
85
+
86
+ def each_product(name)
87
+ parse do |matches|
88
+ product, product_version, extensions = matches.find do |(product,_,_)|
89
+ product == name
90
+ end
91
+
92
+ if product
93
+ yield product, product_version, extensions
94
+ end
95
+ end
96
+ end
97
+
98
+ def index_macos_versions
99
+ macos_versions = Set.new
100
+
101
+ each_product('Mozilla') do |product,product_version,extensions|
102
+ if extensions
103
+ if (match = extensions.match(/Macintosh; Intel Mac OS X (\d+(?:_\d+)*)/))
104
+ macos_versions << match[1]
105
+ end
106
+ end
107
+ end
108
+
109
+ return macos_versions
110
+ end
111
+
112
+ def index_windows_versions
113
+ windows_versions = Set.new
114
+
115
+ each_product('Mozilla') do |product,product_version,extensions|
116
+ if extensions
117
+ if (match = extensions.match(/Windows NT (#{VERSION_REGEXP});/))
118
+ windows_versions << match[1]
119
+ end
120
+ end
121
+ end
122
+
123
+ return windows_versions
124
+ end
125
+
126
+ def index_android_versions
127
+ android_versions = Set.new
128
+
129
+ each_product('Mozilla') do |product,product_version,extensions|
130
+ if extensions
131
+ if (match = extensions.match(/; Android (#{VERSION_REGEXP});/))
132
+ android_versions << match[1]
133
+ end
134
+ end
135
+ end
136
+
137
+ return android_versions
138
+ end
139
+
140
+ def index_android_devices
141
+ android_devices = Set.new
142
+
143
+ each_product('Mozilla') do |product,product_version,extensions|
144
+ if (extensions && extensions =~ /; Android [^;]+; /)
145
+ device = extensions.split(EXTENSION_SEPARATOR).last
146
+
147
+ android_devices << device
148
+ end
149
+ end
150
+
151
+ return android_devices
152
+ end
153
+
154
+ def index_chrome_versions
155
+ chrome_versions = Set.new
156
+
157
+ each_product('Chrome') do |product,product_version,extensions|
158
+ if product_version
159
+ chrome_versions << product_version
160
+ end
161
+ end
162
+
163
+ return chrome_versions
164
+ end
165
+
166
+ def index_firefox_versions
167
+ firefox_versions = Set.new
168
+
169
+ each_product('Mozilla') do |product,product_version,extensions|
170
+ if extensions
171
+ if (match = extensions.match(/; rv:(#{VERSION_REGEXP})/))
172
+ firefox_versions << match[1]
173
+ end
174
+ end
175
+ end
176
+
177
+ return firefox_versions
178
+ end
179
+
180
+ def index_gecko_versions
181
+ gecko_versions = Set.new
182
+
183
+ each_product('Gecko') do |product,product_version,_|
184
+ gecko_versions << product_version
185
+ end
186
+
187
+ return gecko_versions
188
+ end
189
+ end
190
+
191
+ if $0 == __FILE__
192
+ require 'optparse'
193
+
194
+ @method = nil
195
+ @method_args = []
196
+ @print_mode = nil
197
+
198
+ optparser = OptionParser.new do |opts|
199
+ opts.banner = './scripts/index_user_agents [options] FILE'
200
+
201
+ opts.on('--products', 'List all product names') do
202
+ @method = :index_product_combinations
203
+ @print_mode = :count
204
+ end
205
+
206
+ opts.on('--product-versions PRODUCT','List all versions for a given PRODUCT') do |product|
207
+ @method = :index_product_versions
208
+ @method_args = [product]
209
+ @print_mode = :count
210
+ end
211
+
212
+ opts.on('--product-extensions PRODUCT','List all extensions for a given PRODUCT') do |product|
213
+ @method = :index_product_extensions
214
+ @method_args = [product]
215
+ @print_mode = :count
216
+ end
217
+
218
+ opts.on('--macos-versions','List all macOS versions') do
219
+ @method = :index_macos_versions
220
+ @print_mode = :list
221
+ end
222
+
223
+ opts.on('--windows-versions','List all Windows versions') do
224
+ @method = :index_windows_versions
225
+ @print_mode = :list
226
+ end
227
+
228
+ opts.on('--android-versions','List all Android versions') do
229
+ @method = :index_android_versions
230
+ @print_mode = :list
231
+ end
232
+
233
+ opts.on('--chrome-versions','List all Chrome versions') do
234
+ @method = :index_chrome_versions
235
+ @print_mode = :list
236
+ end
237
+
238
+ opts.on('--firefox-versions','List all Firefox versions') do
239
+ @method = :index_firefox_versions
240
+ @print_mode = :list
241
+ end
242
+
243
+ opts.on('--gecko-versions','List all Gecko versions') do
244
+ @method = :index_gecko_versions
245
+ @print_mode = :list
246
+ end
247
+ end
248
+
249
+ args = begin
250
+ optparser.parse(ARGV)
251
+ rescue OptionParser::ParseError => error
252
+ $stderr.puts "./scripts/index_user_agents: #{error.message}"
253
+ exit -1
254
+ end
255
+
256
+ unless (file_path = args[0])
257
+ $stderr.puts "./scripts/index_user_agents: must specify a User-Agents FILE"
258
+ exit -1
259
+ end
260
+
261
+ user_agents = UserAgentsFile.new(file_path)
262
+
263
+ unless @method
264
+ $stderr.puts "./scripts/index_user_agents: must specify atleast one option"
265
+ $stderr.puts optparser
266
+ exit -1
267
+ end
268
+
269
+ results = user_agents.send(@method,*@method_args)
270
+
271
+ case @print_mode
272
+ when :count
273
+ results.sort_by { |value,count| -count }.each do |value,count|
274
+ puts "#{count}\t#{value}"
275
+ end
276
+ when :list
277
+ results.sort.each do |value|
278
+ puts " #{value}"
279
+ end
280
+ end
281
+ end