ronin-web-user_agents 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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