selenium_tor 1.2.0 → 1.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +26 -23
- data/lib/firefox_js_helpers.rb +42 -0
- data/lib/firefox_prefs.rb +75 -0
- data/lib/tor/driver.rb +21 -22
- data/lib/tor/tor_process.rb +1 -3
- data/lib/tor/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcf1656f54ce23992e788e76b1d6342157b6ba3996ec95f04c955a2857e9b2be
|
4
|
+
data.tar.gz: a9ef924f85fdd1370638ff3c12f9009ec1b2ab1b65b5a872bb8bd3d3e3e6e4f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68018127837d8e59dd60647d9a0d60da4af363da8fad7104ac6ed52bf15844d7ead4d4cbc81f1b2403e64ad2fdda55f488ce725ca71b23c618686fb3ed5dbde3
|
7
|
+
data.tar.gz: 3a6bd3db60f2a9de62e232774edabb11e4ccd7bb6e48158c4b6709339f8c964e56f4817f78d471d8ae57a970fec25b325a76a4c3779a3ee67aee8bdc2ad5250f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## [1.3.0] - 2024-08-05
|
4
|
+
|
5
|
+
### Bug fixes
|
6
|
+
|
7
|
+
* [#14](https://gitlab.com/matzfan/selenium-tor/-/issues/14)
|
8
|
+
|
9
|
+
### New features
|
10
|
+
|
11
|
+
* add ability to get and set driver preferences dynamically
|
12
|
+
* add Tor Browser alpha release to CI tests ([#12](https://gitlab.com/matzfan/selenium-tor/-/issues/12))
|
13
|
+
|
3
14
|
## [1.2.0] - 2024-07-31
|
4
15
|
|
5
16
|
### Bug fixes
|
data/README.md
CHANGED
@@ -45,7 +45,8 @@ options = Selenium::WebDriver::Tor::Options.new
|
|
45
45
|
@driver = Selenium::WebDriver.for :tor, options: options
|
46
46
|
|
47
47
|
@driver.get 'https://check.torproject.org'
|
48
|
-
@driver.title
|
48
|
+
@driver.title
|
49
|
+
# => Congratulations. This browser is configured to use Tor.
|
49
50
|
@driver.quit
|
50
51
|
```
|
51
52
|
If the network is inaccessible for any reason a `TorNetworkError` will result.
|
@@ -56,49 +57,49 @@ Running multiple `tor` processes requires that each uses different ports for Soc
|
|
56
57
|
```ruby
|
57
58
|
require 'parallel'
|
58
59
|
|
59
|
-
socks_port = 9150
|
60
|
-
control_port = 9151
|
60
|
+
@socks_port = 9150
|
61
|
+
@control_port = 9151
|
61
62
|
|
62
|
-
def driver
|
63
|
-
|
64
|
-
|
63
|
+
def driver
|
64
|
+
@socks_port += 2
|
65
|
+
@control_port += 2
|
66
|
+
options = Selenium::WebDriver::Tor::Options.new tor_opts: { socks_port: @socks_port, control_port: @control_port }
|
67
|
+
Selenium::WebDriver.for :tor, options: options
|
65
68
|
end
|
66
69
|
|
67
|
-
|
68
|
-
driver2 = driver(socks_port += 2, control_port += 2)
|
70
|
+
@drivers = [driver, driver]
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
Parallel.all?([driver1, driver2], in_threads: 2) do |driver|
|
72
|
+
Parallel.all?(@drivers, in_threads: @drivers.size) do |driver|
|
73
73
|
driver.get 'https://check.torproject.org'
|
74
|
-
driver.title == title
|
75
74
|
end
|
76
|
-
# => true
|
77
75
|
|
78
|
-
|
79
|
-
driver2&.quit
|
76
|
+
@drivers.each(&:quit)
|
80
77
|
```
|
81
78
|
### Tor Browser specific functionality
|
82
79
|
|
83
80
|
You can get and set the secuirty level (shield icon in TB) as follows. 4 is 'Standard', 2 is 'Safer', 1 is 'Safest'.
|
84
81
|
```ruby
|
85
|
-
@driver = Selenium::WebDriver.for :tor
|
86
82
|
@driver.security_level
|
87
83
|
# => 4 - the default value
|
88
|
-
@driver.security_level =
|
84
|
+
@driver.security_level = 2
|
89
85
|
@driver.security_level
|
90
|
-
# =>
|
91
|
-
@driver.quit
|
86
|
+
# => 2
|
92
87
|
```
|
93
88
|
You can get a new circuit for the current page ('New Tor circuit for this site' on TB's application menu):
|
94
89
|
```ruby
|
95
|
-
@driver
|
96
|
-
@driver.get 'https://example.com'
|
97
|
-
@driver.new_circuit_for_page # new circuit for the current page only
|
98
|
-
@driver.quit
|
90
|
+
@driver.new_circuit_for_site # new circuit for the current page domain
|
99
91
|
```
|
100
92
|
### Miscellaneous
|
101
93
|
|
94
|
+
You can get and set Driver preferences dynamically like this. Caveat emptor.
|
95
|
+
```ruby
|
96
|
+
@driver.pref['browser.download.dir']
|
97
|
+
# => ""
|
98
|
+
@driver.pref['browser.download.dir'] = 'home/user/Downloads'
|
99
|
+
@driver.pref['browser.download.dir']
|
100
|
+
# => "home/user/Downloads"
|
101
|
+
```
|
102
|
+
|
102
103
|
The `Selenium::WebDriver::Tor` namespace is used for `Driver`, `Options`, `Profile` and all tor-specific classes. Otherwise Selenium's `Selenium::WebDriver::Firefox` namespace is used.
|
103
104
|
|
104
105
|
Remote functionality is not tested, but may be if a suitable Tor Browser Docker container becomes available.
|
@@ -117,6 +118,8 @@ Tor::TBB_VERSION # the version installed, e.g. "13.0.1", note: driver.capabiliti
|
|
117
118
|
|
118
119
|
[Tor Browser](https://www.torproject.org/download).
|
119
120
|
|
121
|
+
The shared libraries Tor Bowser requires will be available if you have Firefox or Firefox ESR installed on your system. If not, do `sudo apt install firefox-esr` or similar.
|
122
|
+
|
120
123
|
As with Firefox browser, `geckodriver` needs to be installed and in your PATH.
|
121
124
|
|
122
125
|
The gem needs to know the location of the Tor Browser Bundle (TBB). The Tor Browser download package archive must be extracted and the root TBB directory (named "tor-browser") placed somewhere on your system. By default it is assumed to be in the current user's HOME directory. An alternative location can be set via the env var `TOR_BROWSER_ROOT_DIR` - e.g. `export TOR_BROWSER_ROOT_DIR=/home/<user>/Downloads`. The Tor Browser binary location is *automatically set* by reference to this directory, so there is no need to do this:
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'selenium-webdriver'
|
4
|
+
|
5
|
+
module Selenium
|
6
|
+
module WebDriver
|
7
|
+
# namespace
|
8
|
+
module Firefox
|
9
|
+
# js helpers
|
10
|
+
module FirefoxJsHelpers
|
11
|
+
def execute_script_in_chrome_context(...)
|
12
|
+
in_chrome_context { execute_script(...) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def es6_const_call(es6:, const:)
|
16
|
+
es6_import(es6) << "return m.#{const}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def es6_function_call(es6:, func:, args:)
|
20
|
+
es6_import(es6) << "return m.#{es6}.#{func}('#{args}')"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def es6_import(es6)
|
26
|
+
"let m = ChromeUtils.importESModule('resource://gre/modules/#{es6}.sys.mjs');"
|
27
|
+
end
|
28
|
+
|
29
|
+
def in_chrome_context
|
30
|
+
raise(ArgumentError, "##{__method__} takes a block arg") unless block_given?
|
31
|
+
|
32
|
+
old_context = context
|
33
|
+
self.context = 'chrome'
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
self.context = old_context if old_context
|
37
|
+
end
|
38
|
+
end
|
39
|
+
Firefox::Driver.prepend FirefoxJsHelpers
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/firefox_js_helpers'
|
4
|
+
|
5
|
+
module Selenium
|
6
|
+
module WebDriver
|
7
|
+
module Firefox
|
8
|
+
# representation of a firefox pref
|
9
|
+
class Preference
|
10
|
+
PREF_TYPES = { 'PREF_BOOL' => 128, 'PREF_INT' => 64, 'PREF_INVALID' => 0, 'PREF_STRING' => 32 }.freeze
|
11
|
+
GET_METHODS = { 0 => 'getStringPref', 32 => 'getStringPref', 64 => 'getIntPref', 128 => 'getBoolPref' }.freeze
|
12
|
+
SET_METHODS = { 32 => 'setStringPref', 64 => 'setIntPref', 128 => 'setBoolPref' }.freeze
|
13
|
+
|
14
|
+
def initialize(driver)
|
15
|
+
@driver = driver
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](str)
|
19
|
+
@driver.execute_script_in_chrome_context script_string(str)
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(str, val)
|
23
|
+
@driver.execute_script_in_chrome_context script_string(str, val)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def script_string(str, val = nil)
|
29
|
+
val ? setter_script(str, val) : getter_script(str)
|
30
|
+
end
|
31
|
+
|
32
|
+
def getter_script(str)
|
33
|
+
type = pref_type(str)
|
34
|
+
"return Services.prefs.#{GET_METHODS[type]}('#{str}'#{default(type)})"
|
35
|
+
end
|
36
|
+
|
37
|
+
def default(type)
|
38
|
+
type.zero? ? ", ''" : '' # empty string default arg if pref not found, otherwise no default arg
|
39
|
+
end
|
40
|
+
|
41
|
+
def setter_script(str, val)
|
42
|
+
"Services.prefs.#{set_method(str, val)}('#{str}', #{quote_str(val)})"
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_method(str, val)
|
46
|
+
type = pref_type(str)
|
47
|
+
return SET_METHODS[type] unless type.zero?
|
48
|
+
return 'setBoolPref' if val.instance_of?(TrueClass) || val.instance_of?(FalseClass)
|
49
|
+
return 'setIntPref' if val.is_a? Integer
|
50
|
+
|
51
|
+
'setStringPref' # everthing else gets set as a string
|
52
|
+
end
|
53
|
+
|
54
|
+
def pref_type(string)
|
55
|
+
@driver.execute_script_in_chrome_context("return Services.prefs.getPrefType('#{string}')")
|
56
|
+
end
|
57
|
+
|
58
|
+
def quote_str(value)
|
59
|
+
value.is_a?(String) ? "'#{value}'" : value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# adds Driver#pref method and [] and []= methods on that object
|
64
|
+
module FirefoxPrefs
|
65
|
+
def pref
|
66
|
+
Preference.new self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Driver
|
71
|
+
prepend FirefoxPrefs
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/tor/driver.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'open-uri'
|
4
4
|
require_relative '../service'
|
5
5
|
require_relative '../options'
|
6
|
+
require_relative '../firefox_prefs'
|
6
7
|
require_relative 'profile'
|
7
8
|
require_relative 'tor_process'
|
8
9
|
|
@@ -14,9 +15,12 @@ module Selenium
|
|
14
15
|
|
15
16
|
# tor driver
|
16
17
|
class Driver < DelegateClass(DriverDelegate)
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
TORRC_PATH_PREF = 'extensions.torlauncher.torrc_path'
|
19
|
+
TORRC = 'torrc'
|
20
|
+
BROWSER_SECURITY_LEVEL_PREF = 'browser.security_level.security_slider'
|
21
|
+
DOMAIN_ISOLATOR = 'TorDomainIsolator' # ES6 module
|
22
|
+
NEW_CIRCUIT = 'newCircuitForDomain' # js function
|
23
|
+
TB_SEC_LEVELS = { 'standard' => 4, 'safer' => 2, 'safest' => 1 }.freeze
|
20
24
|
|
21
25
|
def initialize(options: nil, **)
|
22
26
|
@data_dir = Dir.mktmpdir
|
@@ -39,36 +43,25 @@ module Selenium
|
|
39
43
|
end
|
40
44
|
|
41
45
|
def security_level
|
42
|
-
|
43
|
-
self.context = 'chrome'
|
44
|
-
execute_script("return Services.prefs.getIntPref('browser.security_level.security_slider')")
|
45
|
-
ensure
|
46
|
-
self.context = old_context
|
46
|
+
pref[BROWSER_SECURITY_LEVEL_PREF]
|
47
47
|
end
|
48
48
|
|
49
49
|
def security_level=(int)
|
50
|
-
|
51
|
-
self.context = 'chrome'
|
52
|
-
raise(ArgumentError, 'Security level can be set to 4, 2 or 1') unless TB_SECURITY_LEVELS.include?(int)
|
50
|
+
raise(ArgumentError, "Valid security levels are: #{TB_SEC_LEVELS.values}") unless TB_SEC_LEVELS.value?(int)
|
53
51
|
|
54
|
-
|
55
|
-
ensure
|
56
|
-
self.context = old_context if old_context
|
52
|
+
pref[BROWSER_SECURITY_LEVEL_PREF] = int
|
57
53
|
end
|
58
54
|
|
59
|
-
def
|
60
|
-
|
61
|
-
self.context = 'chrome'
|
62
|
-
execute_script "ChromeUtils.importESModule('resource://gre/modules/TorDomainIsolator.sys.mjs');
|
63
|
-
TorDomainIsolator.newCircuitForDomain('#{current_url}')"
|
64
|
-
ensure
|
65
|
-
self.context = old_context
|
55
|
+
def new_circuit_for_site
|
56
|
+
execute_script_in_chrome_context es6_function_call(es6: DOMAIN_ISOLATOR, func: NEW_CIRCUIT, args: domain)
|
66
57
|
end
|
67
58
|
|
59
|
+
alias new_circuit_for_page new_circuit_for_site
|
60
|
+
|
68
61
|
private
|
69
62
|
|
70
63
|
def add_torrc_path_to_options
|
71
|
-
@options.prefs[
|
64
|
+
@options.prefs[TORRC_PATH_PREF] = File.join(@data_dir, TORRC)
|
72
65
|
end
|
73
66
|
|
74
67
|
def install_extensions(instance)
|
@@ -78,6 +71,12 @@ module Selenium
|
|
78
71
|
def create_tor_process_and_start_tor(opts)
|
79
72
|
@tor_process = TorProcess.new(@data_dir, opts || {})
|
80
73
|
@tor_process.start_tor
|
74
|
+
rescue Tor::TorProcess::TorProcessError => e
|
75
|
+
raise Error::WebDriverError, e
|
76
|
+
end
|
77
|
+
|
78
|
+
def domain
|
79
|
+
URI(current_url).host&.match(/[^\.]+\.\w+$/)
|
81
80
|
end
|
82
81
|
end
|
83
82
|
end
|
data/lib/tor/tor_process.rb
CHANGED
@@ -14,8 +14,7 @@ module Selenium
|
|
14
14
|
class TorProcessError < StandardError; end
|
15
15
|
|
16
16
|
BOOTSTRAP_SUCCESS_REGEX = /Bootstrapped 100% \(done\): Done$/
|
17
|
-
BOOTSTRAP_FAIL_REGEX = /^[A-Z][a-z]{2} \d{
|
18
|
-
# BOOTSTRAP_TIMEOUT_REGEX = /^[A-Z][a-z]{2} \d{1,2} \d{2}:\d{2}:\d{2}\.\d{3} \[err\] .*/
|
17
|
+
BOOTSTRAP_FAIL_REGEX = /^[A-Z][a-z]{2} \d{2} \d{2}:\d{2}:\d{2}\.\d{3} \[err\] .*/
|
19
18
|
|
20
19
|
attr_reader :pid, :config
|
21
20
|
|
@@ -69,7 +68,6 @@ module Selenium
|
|
69
68
|
lines << line
|
70
69
|
break lines.join if line.match BOOTSTRAP_FAIL_REGEX
|
71
70
|
break '' if line.match BOOTSTRAP_SUCCESS_REGEX
|
72
|
-
# break false if line.match BOOTSTRAP_TIMEOUT_REGEX
|
73
71
|
end
|
74
72
|
end
|
75
73
|
end
|
data/lib/tor/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: selenium_tor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MatzFan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -41,6 +41,8 @@ files:
|
|
41
41
|
- Rakefile
|
42
42
|
- fonts/000_README.txt
|
43
43
|
- lib/driver.rb
|
44
|
+
- lib/firefox_js_helpers.rb
|
45
|
+
- lib/firefox_prefs.rb
|
44
46
|
- lib/options.rb
|
45
47
|
- lib/selenium_tor.rb
|
46
48
|
- lib/service.rb
|
@@ -78,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
80
|
- !ruby/object:Gem::Version
|
79
81
|
version: '0'
|
80
82
|
requirements: []
|
81
|
-
rubygems_version: 3.5.
|
83
|
+
rubygems_version: 3.5.17
|
82
84
|
signing_key:
|
83
85
|
specification_version: 4
|
84
86
|
summary: Selenium extension for Tor Browser
|