ronin-web-browser 0.1.0.rc1
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.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.github/workflows/ruby.yml +51 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.rubocop.yml +12 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +13 -0
- data/Gemfile +32 -0
- data/README.md +186 -0
- data/Rakefile +35 -0
- data/gemspec.yml +28 -0
- data/lib/ronin/web/browser/agent.rb +430 -0
- data/lib/ronin/web/browser/cookie.rb +140 -0
- data/lib/ronin/web/browser/cookie_file.rb +91 -0
- data/lib/ronin/web/browser/version.rb +28 -0
- data/lib/ronin/web/browser.rb +148 -0
- data/ronin-web-browser.gemspec +60 -0
- metadata +109 -0
@@ -0,0 +1,430 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-web-browser - An automated Chrome API.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-web-browser is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-web-browser is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-web-browser. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/web/browser/cookie'
|
22
|
+
require 'ronin/web/browser/cookie_file'
|
23
|
+
require 'ronin/support/network/http'
|
24
|
+
|
25
|
+
require 'ferrum'
|
26
|
+
require 'uri'
|
27
|
+
|
28
|
+
module Ronin
|
29
|
+
module Web
|
30
|
+
module Browser
|
31
|
+
#
|
32
|
+
# Represents an instance of a Chrome headless or visible browser.
|
33
|
+
#
|
34
|
+
class Agent < Ferrum::Browser
|
35
|
+
|
36
|
+
# The configured proxy information.
|
37
|
+
#
|
38
|
+
# @return [Hash{Symbol => Object}, nil]
|
39
|
+
attr_reader :proxy
|
40
|
+
|
41
|
+
#
|
42
|
+
# Initializes the browser agent.
|
43
|
+
#
|
44
|
+
# @param [Boolean] visible
|
45
|
+
# Controls whether the browser will start in visible or headless mode.
|
46
|
+
#
|
47
|
+
# @param [Boolean] headless
|
48
|
+
# Controls whether the browser will start in headless or visible mode.
|
49
|
+
#
|
50
|
+
# @param [String, URI::HTTP, Addressible::URI, Hash, nil] proxy
|
51
|
+
# The proxy to send all browser requests through.
|
52
|
+
#
|
53
|
+
# @param [Hash{Symbol => Object}] kwargs
|
54
|
+
# Additional keyword arguments for `Ferrum::Browser#initialize`.
|
55
|
+
#
|
56
|
+
def initialize(visible: false,
|
57
|
+
headless: !visible,
|
58
|
+
proxy: Ronin::Support::Network::HTTP.proxy,
|
59
|
+
**kwargs)
|
60
|
+
proxy = case proxy
|
61
|
+
when Hash, nil then proxy
|
62
|
+
when URI::HTTP, Addressable::URI
|
63
|
+
{
|
64
|
+
host: proxy.host,
|
65
|
+
port: proxy.port,
|
66
|
+
user: proxy.user,
|
67
|
+
password: proxy.password
|
68
|
+
}
|
69
|
+
when String
|
70
|
+
uri = URI(proxy)
|
71
|
+
|
72
|
+
{
|
73
|
+
host: uri.host,
|
74
|
+
port: uri.port,
|
75
|
+
user: uri.user,
|
76
|
+
password: uri.password
|
77
|
+
}
|
78
|
+
else
|
79
|
+
raise(ArgumentError,"invalid proxy value (#{proxy.inspect}), must be either a Hash, URI::HTTP, String, or nil")
|
80
|
+
end
|
81
|
+
|
82
|
+
@headless = headless
|
83
|
+
@proxy = proxy
|
84
|
+
|
85
|
+
super(headless: headless, proxy: proxy, **kwargs)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Opens a new browser.
|
90
|
+
#
|
91
|
+
# @param [Hash{Symbol => Object}] kwargs
|
92
|
+
# Additional keyword arguments for {#initialize}.
|
93
|
+
#
|
94
|
+
# @yield [browser]
|
95
|
+
# If a block is given, it will be passed the new browser object.
|
96
|
+
# Once the block returns, `quit` will be called on the browser object.
|
97
|
+
#
|
98
|
+
# @yieldparam [Agent] browser
|
99
|
+
# The newly created browser object.
|
100
|
+
#
|
101
|
+
# @return [Agent]
|
102
|
+
# The opened browser object.
|
103
|
+
#
|
104
|
+
def self.open(**kwargs)
|
105
|
+
browser = new(**kwargs)
|
106
|
+
|
107
|
+
if block_given?
|
108
|
+
yield browser
|
109
|
+
browser.quit
|
110
|
+
end
|
111
|
+
|
112
|
+
return browser
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Determines whether the browser was opened in headless mode.
|
117
|
+
#
|
118
|
+
# @return [Boolean]
|
119
|
+
#
|
120
|
+
def headless?
|
121
|
+
@headless
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Determines whether the browser was opened in visible mode.
|
126
|
+
#
|
127
|
+
# @return [Boolean]
|
128
|
+
#
|
129
|
+
def visible?
|
130
|
+
!@headless
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Determines whether the proxy was initialized with a proxy.
|
135
|
+
#
|
136
|
+
def proxy?
|
137
|
+
!@proxy.nil?
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Enables or disables bypassing CSP.
|
142
|
+
#
|
143
|
+
# @param [Boolean] mode
|
144
|
+
# Controls whether to enable or disable CSP bypassing.
|
145
|
+
#
|
146
|
+
def bypass_csp=(mode)
|
147
|
+
if mode then bypass_csp(enabled: true)
|
148
|
+
else bypass_csp(enabled: false)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Registers a callback for the given event type.
|
154
|
+
#
|
155
|
+
# @param [:request, :response, :dialog, String] event
|
156
|
+
# The event to register a callback for.
|
157
|
+
# For an exhaustive list of event String names, see the
|
158
|
+
# [Chrome DevTools Protocol documentation](https://chromedevtools.github.io/devtools-protocol/1-3/)
|
159
|
+
#
|
160
|
+
# @yield [request]
|
161
|
+
# If the event type is `:request` the given block will be passed the
|
162
|
+
# request object.
|
163
|
+
#
|
164
|
+
# @yield [exchange]
|
165
|
+
# If the event type is `:response` the given block will be passed the
|
166
|
+
# network exchange object containing both the request and the response
|
167
|
+
# objects.
|
168
|
+
#
|
169
|
+
# @yield [params, index, total]
|
170
|
+
# Other event types will be passed a params Hash, index, and total.
|
171
|
+
#
|
172
|
+
# @yieldparam [Ferrum::Network::InterceptedRequest] request
|
173
|
+
# A network request object.
|
174
|
+
#
|
175
|
+
# @yieldparam [Ferrum::Network::Exchange] exchange
|
176
|
+
# A network exchange object containing both the request and respoonse
|
177
|
+
# objects.
|
178
|
+
#
|
179
|
+
# @yieldparam [Hash{String => Object}] params
|
180
|
+
# A params Hash containing the return value(s).
|
181
|
+
#
|
182
|
+
# @yieldparam [Integer] index
|
183
|
+
#
|
184
|
+
# @yieldparam [Integer] total
|
185
|
+
#
|
186
|
+
def on(event,&block)
|
187
|
+
case event
|
188
|
+
when :response
|
189
|
+
super('Network.responseReceived') do |params,index,total|
|
190
|
+
exchange = network.select(params['requestId']).last
|
191
|
+
|
192
|
+
if exchange
|
193
|
+
block.call(exchange,index,total)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
when :close
|
197
|
+
super('Inspector.detached',&block)
|
198
|
+
else
|
199
|
+
super(event,&block)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# Passes every request to the given block.
|
205
|
+
#
|
206
|
+
# @yield [request]
|
207
|
+
# The given block will be passed each request before it's sent.
|
208
|
+
#
|
209
|
+
# @yieldparam [Ferrum::Network::InterceptRequest] request
|
210
|
+
# A network request object.
|
211
|
+
#
|
212
|
+
def every_request
|
213
|
+
network.intercept
|
214
|
+
|
215
|
+
on(:request) do |request|
|
216
|
+
yield request
|
217
|
+
request.continue
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# Passes every response to the given block.
|
223
|
+
#
|
224
|
+
# @yield [response]
|
225
|
+
# If the given block accepts a single argument, it will be passed
|
226
|
+
# each response object.
|
227
|
+
#
|
228
|
+
# @yield [response, request]
|
229
|
+
# If the given block accepts two arguments, it will be passed the
|
230
|
+
# response and the request objects.
|
231
|
+
#
|
232
|
+
# @yieldparam [Ferrum::Network::Response] response
|
233
|
+
# A respone object returned for a request.
|
234
|
+
#
|
235
|
+
# @yieldparam [Ferrum::Network::Request] request
|
236
|
+
# The request object for the response.
|
237
|
+
#
|
238
|
+
def every_response(&block)
|
239
|
+
on(:response) do |exchange,index,total|
|
240
|
+
if block.arity == 2
|
241
|
+
yield exchange.response, exchange.request
|
242
|
+
else
|
243
|
+
yield exchange.response
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
#
|
249
|
+
# Passes every requested URL to the given block.
|
250
|
+
#
|
251
|
+
# @yield [url]
|
252
|
+
# The given block will be passed every URL.
|
253
|
+
#
|
254
|
+
# @yieldparam [String] url
|
255
|
+
# A URL requested by the browser.
|
256
|
+
#
|
257
|
+
def every_url
|
258
|
+
every_request do |request|
|
259
|
+
yield request.url
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
#
|
264
|
+
# Passes every requested URL that matches the given pattern to the given
|
265
|
+
# block.
|
266
|
+
#
|
267
|
+
# @param [String, Regexp] pattern
|
268
|
+
# The pattern to filter the URLs by.
|
269
|
+
#
|
270
|
+
# @yield [url]
|
271
|
+
# The given block will be passed every URL that matches the pattern.
|
272
|
+
#
|
273
|
+
# @yieldparam [String] url
|
274
|
+
# A matching URL requested by the browser.
|
275
|
+
#
|
276
|
+
def every_url_like(pattern)
|
277
|
+
every_url do |url|
|
278
|
+
if pattern.match(url)
|
279
|
+
yield url
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
#
|
285
|
+
# The page's current URI.
|
286
|
+
#
|
287
|
+
# @return [URI::HTTP]
|
288
|
+
#
|
289
|
+
def page_uri
|
290
|
+
URI.parse(url)
|
291
|
+
end
|
292
|
+
|
293
|
+
#
|
294
|
+
# Queries the XPath or CSS-path query and returns the matching nodes.
|
295
|
+
#
|
296
|
+
# @return [Array<Ferrum::Node>]
|
297
|
+
# The matching node.
|
298
|
+
#
|
299
|
+
def search(query)
|
300
|
+
if query.start_with?('/')
|
301
|
+
xpath(query)
|
302
|
+
else
|
303
|
+
css(query)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
#
|
308
|
+
# Queries the XPath or CSS-path query and returns the first match.
|
309
|
+
#
|
310
|
+
# @return [Ferrum::Node, nil]
|
311
|
+
# The first matching node.
|
312
|
+
#
|
313
|
+
def at(query)
|
314
|
+
if query.start_with?('/')
|
315
|
+
at_xpath(query)
|
316
|
+
else
|
317
|
+
at_css(query)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
#
|
322
|
+
# Queries all `<a href="...">` links in the current page.
|
323
|
+
#
|
324
|
+
# @return [Array<String>]
|
325
|
+
#
|
326
|
+
def links
|
327
|
+
xpath('//a/@href').map(&:value)
|
328
|
+
end
|
329
|
+
|
330
|
+
#
|
331
|
+
# All link URLs in the current page.
|
332
|
+
#
|
333
|
+
# @return [Array<URI::HTTP, URI::HTTPS>]
|
334
|
+
#
|
335
|
+
def urls
|
336
|
+
page_uri = self.page_uri
|
337
|
+
|
338
|
+
links.map { |link| page_uri.merge(link) }
|
339
|
+
end
|
340
|
+
|
341
|
+
alias eval_js evaluate
|
342
|
+
alias load_js add_script_tag
|
343
|
+
alias inject_js evaluate_on_new_document
|
344
|
+
alias load_css add_style_tag
|
345
|
+
|
346
|
+
#
|
347
|
+
# Enumerates over all session cookies.
|
348
|
+
#
|
349
|
+
# @yield [cookie]
|
350
|
+
# The given block will be passed each session cookie.
|
351
|
+
#
|
352
|
+
# @yieldparam [Ferrum::Cookies::Cookie] cookie
|
353
|
+
# A cookie that ends with `sess` or `session`.
|
354
|
+
#
|
355
|
+
# @return [Enumerator]
|
356
|
+
# If no block is given, then an Enumerator object will be returned.
|
357
|
+
#
|
358
|
+
def each_session_cookie
|
359
|
+
return enum_for(__method__) unless block_given?
|
360
|
+
|
361
|
+
cookies.each do |cookie|
|
362
|
+
yield cookie if cookie.session?
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
#
|
367
|
+
# Fetches all session cookies.
|
368
|
+
#
|
369
|
+
# @return [Array<Ferrum::Cookie>]
|
370
|
+
# The matching session cookies.
|
371
|
+
#
|
372
|
+
def session_cookies
|
373
|
+
each_session_cookie.to_a
|
374
|
+
end
|
375
|
+
|
376
|
+
#
|
377
|
+
# Sets a cookie.
|
378
|
+
#
|
379
|
+
# @param [String] name
|
380
|
+
# The cookie name.
|
381
|
+
#
|
382
|
+
# @param [String] value
|
383
|
+
# The cookie value.
|
384
|
+
#
|
385
|
+
# @param [Hash{Symbol => Object}] options
|
386
|
+
# Additional cookie attributes.
|
387
|
+
#
|
388
|
+
def set_cookie(name,value,**options)
|
389
|
+
cookies.set(name: name, value: value, **options)
|
390
|
+
end
|
391
|
+
|
392
|
+
#
|
393
|
+
# Loads the cookies from the cookie file.
|
394
|
+
#
|
395
|
+
# @param [String] path
|
396
|
+
# The path to the cookie file.
|
397
|
+
#
|
398
|
+
def load_cookies(path)
|
399
|
+
CookieFile.new(path).each do |cookie|
|
400
|
+
cookies.set(cookie)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
#
|
405
|
+
# Saves the cookies to a cookie file.
|
406
|
+
#
|
407
|
+
# @param [String] path
|
408
|
+
# The path to the output cookie file.
|
409
|
+
#
|
410
|
+
def save_cookies(path)
|
411
|
+
CookieFile.save(path,cookies)
|
412
|
+
end
|
413
|
+
|
414
|
+
#
|
415
|
+
# Waits indefinitely until the browser window is closed.
|
416
|
+
#
|
417
|
+
def wait_until_closed
|
418
|
+
window_closed = false
|
419
|
+
|
420
|
+
on('Inspector.detached') do
|
421
|
+
window_closed = true
|
422
|
+
end
|
423
|
+
|
424
|
+
sleep(1) until window_closed
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-web-browser - An automated Chrome API.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-web-browser is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-web-browser is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-web-browser. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ferrum/cookies/cookie'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module Web
|
25
|
+
module Browser
|
26
|
+
#
|
27
|
+
# Represents a browser cookie.
|
28
|
+
#
|
29
|
+
class Cookie < Ferrum::Cookies::Cookie
|
30
|
+
|
31
|
+
#
|
32
|
+
# Parses a browser cookie from a raw String.
|
33
|
+
#
|
34
|
+
# @param [String] string
|
35
|
+
# The raw cookie String to parse.
|
36
|
+
#
|
37
|
+
# @return [Cookie]
|
38
|
+
# The parsed cookie.
|
39
|
+
#
|
40
|
+
# @raise [ArgumentError]
|
41
|
+
# The string was empty or contains an unknown field.
|
42
|
+
#
|
43
|
+
def self.parse(string)
|
44
|
+
fields = string.split(/;\s+/)
|
45
|
+
|
46
|
+
if fields.empty?
|
47
|
+
raise(ArgumentError,"cookie must not be empty: #{string.inspect}")
|
48
|
+
end
|
49
|
+
|
50
|
+
name, value = fields.shift.split('=',2)
|
51
|
+
attributes = {
|
52
|
+
'name' => name,
|
53
|
+
'value' => value
|
54
|
+
}
|
55
|
+
|
56
|
+
fields.each do |field|
|
57
|
+
if field.include?('=')
|
58
|
+
key, value = field.split('=',2)
|
59
|
+
|
60
|
+
case key
|
61
|
+
when 'Expires', 'Max-Age'
|
62
|
+
attributes['expires'] = Time.parse(value).to_i
|
63
|
+
when 'Path' then attributes['path'] = value
|
64
|
+
when 'Domain' then attributes['domain'] = value
|
65
|
+
when 'SameSite'then attributes['sameSite'] = value
|
66
|
+
else
|
67
|
+
raise(ArgumentError,"unrecognized Cookie field: #{field.inspect}")
|
68
|
+
end
|
69
|
+
else
|
70
|
+
case field
|
71
|
+
when 'HttpOnly' then attributes['httpOnly'] = true
|
72
|
+
when 'Secure' then attributes['secure'] = true
|
73
|
+
else
|
74
|
+
raise(ArgumentError,"unrecognized Cookie flag: #{field.inspect}")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return new(attributes)
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# The priority of the cookie.
|
84
|
+
#
|
85
|
+
# @return [String]
|
86
|
+
#
|
87
|
+
def priority
|
88
|
+
@attributes['priority']
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
#
|
94
|
+
def sameparty?
|
95
|
+
@attributes['sameParty']
|
96
|
+
end
|
97
|
+
|
98
|
+
alias same_party? sameparty?
|
99
|
+
|
100
|
+
#
|
101
|
+
# @return [String]
|
102
|
+
#
|
103
|
+
def source_scheme
|
104
|
+
@attributes['sourceScheme']
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# @return [Integer]
|
109
|
+
#
|
110
|
+
def source_port
|
111
|
+
@attributes['sourcePort']
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Converts the cookie back into a raw cookie String.
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
# The raw cookie string.
|
119
|
+
#
|
120
|
+
def to_s
|
121
|
+
string = "#{@attributes['name']}=#{@attributes['value']}"
|
122
|
+
|
123
|
+
@attributes.each do |key,value|
|
124
|
+
case key
|
125
|
+
when 'name', 'value' # no-op
|
126
|
+
when 'domain' then string << "; Domain=#{value}"
|
127
|
+
when 'path' then string << "; Path=#{value}"
|
128
|
+
when 'expires' then string << "; Expires=#{Time.at(value).httpdate}"
|
129
|
+
when 'httpOnly' then string << "; httpOnly" if value
|
130
|
+
when 'secure' then string << "; Secure" if value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
return string
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-web-browser - An automated Chrome API.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-web-browser is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-web-browser is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-web-browser. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/web/browser/cookie'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module Web
|
25
|
+
module Browser
|
26
|
+
#
|
27
|
+
# Represents a file of cookies.
|
28
|
+
#
|
29
|
+
class CookieFile
|
30
|
+
|
31
|
+
include Enumerable
|
32
|
+
|
33
|
+
# The path to the file.
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
attr_reader :path
|
37
|
+
|
38
|
+
#
|
39
|
+
# Initializes a cookie file.
|
40
|
+
#
|
41
|
+
# @param [String] path
|
42
|
+
# The path to the cookie file.
|
43
|
+
#
|
44
|
+
def initialize(path)
|
45
|
+
@path = File.expand_path(path)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Writes the cookies to the cookie file.
|
50
|
+
#
|
51
|
+
# @param [String] path
|
52
|
+
# The path to the cookie file to write to.
|
53
|
+
#
|
54
|
+
# @param [Array<Cookie>, Enumerator<Cookie>] cookies
|
55
|
+
# The cookies to write.
|
56
|
+
#
|
57
|
+
def self.save(path,cookies)
|
58
|
+
File.open(path,'w') do |file|
|
59
|
+
cookies.each do |cookie|
|
60
|
+
file.puts(cookie)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Parses each cookie in the cookie file.
|
67
|
+
#
|
68
|
+
# @yield [cookie]
|
69
|
+
# The given block will be passed each cookie parsed from each line of
|
70
|
+
# the file.
|
71
|
+
#
|
72
|
+
# @yieldparam [Cookie] cookie
|
73
|
+
# A cookie parsed from the file.
|
74
|
+
#
|
75
|
+
# @return [Enumerator]
|
76
|
+
# If no block is given, then an Enumerator will be returned.
|
77
|
+
#
|
78
|
+
def each
|
79
|
+
return enum_for(__method__) unless block_given?
|
80
|
+
|
81
|
+
File.open(@path) do |file|
|
82
|
+
file.each_line(chomp: true) do |line|
|
83
|
+
yield Cookie.parse(line)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-web-browser - An automated Chrome API.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-web-browser is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-web-browser is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-web-browser. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
module Ronin
|
22
|
+
module Web
|
23
|
+
module Browser
|
24
|
+
# ronin-web-browser version
|
25
|
+
VERSION = '0.1.0.rc1'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|