selenium_tor 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53381bfb6eec61ccbffdd84ce01dd3e83413822c139ef6877f1873505fdf9560
4
- data.tar.gz: b0aaf6755f2139ab92f66941f6874a38a1ee0d5da6c4a4891968c2c22afb114f
3
+ metadata.gz: cd34aa798531de8ada12d53163c343733610aa6115141d58aeaa3b4875c40cd6
4
+ data.tar.gz: f989c3ca02651ba825beefde17a4f2ac007ae25bb7c09e29ca080eee24cd3d03
5
5
  SHA512:
6
- metadata.gz: f5ce3f9a6def6cf03b4ddcb9eaa24548f70d87f7624d2c6b8deae473210f6a02e7239a80a4ba5ba9a925b8a33f9bb9e4adde8182d93fe90c17a8dd581de25351
7
- data.tar.gz: c153ff3ea434a2a141f3f15670e923b0800159e1ee2126414b7fd498fbe937e155de652735255d9f4576735789ab9215f259c2983922627e0d68fbcad33825a7
6
+ metadata.gz: 1a71cffb60fc1e7f58d4a8cb4ffc9b0b42abfca1f5c541332b0febf50b78440fb5adfacb76f18e7289184bd7fb185582c4b338f6c2cde8177847b0db4e9a8ba7
7
+ data.tar.gz: 3c9291018aede2ef805bf96110ff3742b4e2791634801be4ac607ad27e4d318ddafe931950946499a8547883c48ca750d6af9a409674b91e40cf9cce41e418b5
data/.rubocop.yml CHANGED
@@ -24,3 +24,6 @@ Lint/RescueException:
24
24
  Style/RegexpLiteral:
25
25
  Exclude:
26
26
  - 'Guardfile'
27
+
28
+ Minitest/TestMethodName:
29
+ Enabled: true
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## [1.0.0] - 2024-07-15
4
+
5
+ ### New features
6
+
7
+ * add ability to use system tor
8
+ * add ability to run multiple driver instances simultaneously (fixes [#6](https://gitlab.com/matzfan/selenium-tor/-/issues/6))
9
+
3
10
  ## [0.2.0] - 2024-07-10
4
11
 
5
12
  ### Breaking changes
data/README.md CHANGED
@@ -30,8 +30,10 @@ If bundler is not being used to manage dependencies, install the gem by executin
30
30
  $ gem install selenium_tor
31
31
 
32
32
  ## Usage
33
-
34
- Tor Browser is based on Firefox, so for usage please read the Selenium [docs](https://www.selenium.dev/documentation/webdriver/browsers/firefox/) for Firefox browser.
33
+ ```ruby
34
+ require 'selenium_tor'
35
+ ```
36
+ Tor Browser is based on Firefox, so for usage please read the Selenium [docs](https://www.selenium.dev/documentation/webdriver/browsers/firefox) for Firefox browser.
35
37
 
36
38
  A driver is instantiated like this:
37
39
  ```ruby
@@ -42,11 +44,43 @@ options = Selenium::WebDriver::Tor::Options.new
42
44
  @driver.title # => Congratulations. This browser is configured to use Tor.
43
45
  @driver.quit
44
46
  ```
45
- The driver will not be instantiated until a connection to the Tor network is made. If the network is inaccessible for any reason a `TorNetworkError` will result.
47
+ By default Tor Broswer will use the bundled `tor` process, in which case the driver will not be instantiated until a connection to the Tor network is made. If the network is inaccessible for any reason a `TorNetworkError` will result.
46
48
 
47
- The `Selenium::WebDriver::Tor` namespace is used for `Driver`, `Options` and `Profile`. Otherwise Selenium's `Selenium::WebDriver::Firefox` namespace is used.
49
+ ### Multiple instances using the system tor
48
50
 
49
- Remote functionality is not tested, but will be if Selenium provide a Tor Browser Docker container for Selenium Grid.
51
+ The following options require `tor` to be installed. Tor is available via various package managers e.g. `sudo apt install tor`.
52
+
53
+ If a `SystemTor` object is passed to options, the system `tor` will be used instead of the `tor` included in the Tor Browser Bundle (TBB).
54
+ ```ruby
55
+ system_tor = Selenium::WebDriver::Tor::SystemTor.new # writes a driver-specific torrc file
56
+ options = Selenium::WebDriver::Tor::Options.new system_tor: system_tor
57
+ @driver = Selenium::WebDriver.for :tor, options: options # new tor process started when :system_tor option passed to driver
58
+ @driver.get 'https://example.com'
59
+ @driver.quit # stops the new system tor process and deletes torrc file
60
+ ```
61
+ Multiple instances may be instantiated, in which case `SystemTor` options for :socks_port (default 9150) and :control_port (default 9151) must be set for each so they do not conflict. For example:
62
+ ```ruby
63
+ def new_driver(socks_p, control_p)
64
+ system_tor = Selenium::WebDriver::Tor::SystemTor.new(socks_port: socks_p, control_port: control_p)
65
+ options = Selenium::WebDriver::Tor::Options.new system_tor: system_tor
66
+ Selenium::WebDriver.for :tor, options: options
67
+ end
68
+
69
+ socks_port = 9150
70
+ control_port = 9151
71
+
72
+ driver1 = new_driver(socks_port, control_port)
73
+ socks_port += 2
74
+ control_port += 2
75
+ driver2 = new_driver(socks_port, control_port)
76
+
77
+ driver1.quit
78
+ driver2.quit
79
+ ```
80
+
81
+ 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.
82
+
83
+ Remote functionality is not tested, but may be if a suitable Tor Browser Docker container becomes available.
50
84
 
51
85
  A number of constants are set during driver initialization based upon values found in the Tor Browser Bundle (TBB) root directory - see below. These include:
52
86
  ```ruby
@@ -76,17 +110,17 @@ Tor Selenium is tested on **Linux only** right now.
76
110
 
77
111
  ## Testing
78
112
 
79
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the tests.
113
+ After checking out the repo, run `bin/setup` to install dependencies.
80
114
 
81
- Tests are run in the display set by the `DISPLAY` env var, :0 by default. To run headless tests, set it to another value:
115
+ Tests are run in the display set by the `DISPLAY` env var, usually :0 by default. To run headless tests, set it to another value:
82
116
 
83
117
  $ DISPLAY=:99 bundle exec rake
84
118
 
85
119
  If you find tests are failing with `TorNetworkError` and a timeout message, check you have no other Tor Browser processes running. The processes to look for are either "firefox.real" or "firefox-esr". The latter may be legitimate if you are also running Firefox browser. You may also want to check the Tor network is actually up, it isn't always..
86
120
 
87
- If you wish run the `vglrun` WebGL fingerprint test (excluded from Rake) install VirtualGL (see above, assumes you have the relevent drivers installed) and run the following command:
121
+ If you wish run the `vglrun` WebGL fingerprint test install VirtualGL (see above, assumes you have the relevent drivers installed) and run the following command:
88
122
 
89
- $ DISPLAY=:99 vglrun bundle exec ruby test/features/fingerprinting/test_fingerprintjs_with_vglrun.rb
123
+ $ DISPLAY=:99 vglrun bundle exec ruby test/features/fingerprinting/vglrun_test_fingerprintjs.rb
90
124
 
91
125
  ## Development
92
126
 
@@ -98,12 +132,6 @@ To install this gem onto your local machine, run `bundle exec rake install`.
98
132
 
99
133
  Bug reports and pull requests are welcome on GitLab at https://gitlab.com/matzfan/selenium-tor. For a pull request please checkout a suitably named feature branch before making and submitting changes.
100
134
 
101
- This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://gitlab.com/matzfan/selenium-tor/CODE_OF_CONDUCT.md).
102
-
103
135
  ## License
104
136
 
105
137
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
106
-
107
- ## Code of Conduct
108
-
109
- Everyone interacting in the Selenium Tor project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://gitlab.com/matzfan/selenium-tor/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/testtask'
6
6
  Rake::TestTask.new(:test) do |t|
7
7
  t.libs << 'test'
8
8
  t.libs << 'lib'
9
- t.test_files = FileList['test/**/test_*.rb'] - ['test/features/fingerprinting/test_fingerprintjs_with_vglrun.rb']
9
+ t.test_files = FileList['test/**/test_*.rb']
10
10
  # t.warning = false
11
11
  end
12
12
 
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # patch String
4
+ module StringExtensions
5
+ def snake_case?
6
+ match(/\A[a-z]+(_[a-z]+)?\z/)
7
+ end
8
+ end
9
+
10
+ class String
11
+ prepend StringExtensions
12
+ end
data/lib/tor/driver.rb CHANGED
@@ -13,13 +13,19 @@ module Selenium
13
13
  CONNECT_BUTTON_ID = 'network-status-tor-connect-button' # visible only until connected
14
14
  TOR_PREFS_CONNECTION_URL = 'about:preferences#connection'
15
15
 
16
- attr_reader :support_data
16
+ attr_reader :system_tor
17
17
 
18
- def initialize(...)
19
- instantiate_driver_from_tbb_dir(...)
18
+ def initialize(options: nil, **)
19
+ instantiate_driver_from_tbb_dir(options: options, **)
20
+ setup_system_tor options.system_tor if options&.system_tor
20
21
  super(@instance)
21
- wait_for_tor_connection
22
22
  install_extensions
23
+ wait_for_tor_connection unless system_tor # no need if system tor used
24
+ end
25
+
26
+ def quit
27
+ system_tor&.stop_tor
28
+ super
23
29
  end
24
30
 
25
31
  private
@@ -43,6 +49,11 @@ module Selenium
43
49
  def install_extensions
44
50
  Pathname.new(TBB_EXTENSIONS_DIR).children.each { |xpi_path| @instance.install_addon xpi_path }
45
51
  end
52
+
53
+ def setup_system_tor(system_tor)
54
+ @system_tor = system_tor
55
+ system_tor.start_tor
56
+ end
46
57
  end
47
58
  end
48
59
  end
data/lib/tor/options.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'tor_prefs'
4
+ require_relative 'system_tor'
4
5
  require 'selenium-webdriver'
5
6
 
6
7
  module Selenium
@@ -8,7 +9,9 @@ module Selenium
8
9
  module Tor
9
10
  # tor options
10
11
  class Options < Firefox::Options
12
+ CAPABILITIES = CAPABILITIES.merge(system_tor: 'system_tor')
11
13
  DEFAULT_TBB_DIR = File.join Dir.home, 'tor-browser'
14
+
12
15
  Tor::TBB_DIR = ENV.fetch('TOR_BROWSER_ROOT_DIR', nil) || DEFAULT_TBB_DIR
13
16
  Tor::TBB_BROWSER_DIR = File.join Tor::TBB_DIR, 'Browser'
14
17
  Tor::TBB_BINARY_PATH = File.join Tor::TBB_BROWSER_DIR, 'firefox'
@@ -16,15 +19,24 @@ module Selenium
16
19
  Tor::TBB_EXTENSIONS_DIR = File.join Tor::TBB_PROFILE_DIR, 'extensions'
17
20
  Tor::TBB_VERSION = JSON.parse(File.read(File.join(Tor::TBB_BROWSER_DIR, 'tbb_version.json')))['version']
18
21
 
22
+ attr_reader :system_tor # read by Driver
23
+
19
24
  def initialize(log_level: nil, **opts)
25
+ @system_tor_prefs = {}
26
+ system_tor_prefs(opts.delete(:system_tor)) if opts[:system_tor] # must be deleted before call to super
20
27
  super(log_level: log_level, **tor_options(opts.delete(:prefs)).merge(opts))
21
28
  do_start_tor_browser_script_stuff # stuff the start-tor-browser script in TBB does
22
29
  end
23
30
 
24
31
  private
25
32
 
33
+ def system_tor_prefs(system_tor)
34
+ @system_tor = system_tor
35
+ @system_tor_prefs = system_tor.prefs
36
+ end
37
+
26
38
  def tor_options(prefs)
27
- { binary: TBB_BINARY_PATH, prefs: (prefs || {}).merge(TOR_PREFS) }
39
+ { binary: TBB_BINARY_PATH, prefs: (prefs || {}).merge(TOR_PREFS).merge(@system_tor_prefs) }
28
40
  end
29
41
 
30
42
  def do_start_tor_browser_script_stuff
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'timeout'
4
+ require_relative 'torrc'
5
+ require_relative '../string_extensions'
6
+
7
+ module Selenium
8
+ module WebDriver
9
+ module Tor
10
+ # Respresentation of a system tor process
11
+ class SystemTor
12
+ include StringExtensions
13
+
14
+ class SystemTorError < StandardError; end
15
+
16
+ BOOTSTRAP_SUCCESS_REGEX = /Bootstrapped 100% \(done\): Done$/
17
+ BOOTSTRAP_FAIL_REGEX = /^[A-Z][a-z]{2} \d{1,2} \d{2}:\d{2}:\d{2}\.\d{3} \[err\] .*/
18
+ # BOOTSTRAP_TIMEOUT_REGEX = /^[A-Z][a-z]{2} \d{1,2} \d{2}:\d{2}:\d{2}\.\d{3} \[err\] .*/
19
+
20
+ TORRC_PREFS = { 'extensions.torlauncher.start_tor' => false }.freeze
21
+ TORRC_PATH_KEY = 'extensions.torlauncher.torrc_path'
22
+
23
+ attr_reader :pid, :config
24
+
25
+ def initialize(opts = {})
26
+ raise SystemTorError, 'tor executable not found in PATH' unless tor_executable?
27
+ raise ArgumentError, 'SystemTor.new takes an options hash' unless opts.is_a? Hash
28
+
29
+ @opts = map_opts_to_torrc_keys opts
30
+ @data_dir = Dir.mktmpdir # each tor process needs a separate data directory
31
+ @torrc = Torrc.new(@data_dir)
32
+ @config ||= setup_config # Hash to store torrc config
33
+ end
34
+
35
+ def prefs
36
+ TORRC_PREFS.merge({ TORRC_PATH_KEY => @torrc.path })
37
+ end
38
+
39
+ def start_tor
40
+ r, io = IO.pipe
41
+ pid = Process.spawn "tor -f #{@torrc.path}", out: io, err: :out
42
+ io.close
43
+ errors = parse_tor_bootstrap_errors r
44
+ errors.empty? ? @pid = pid : raise(SystemTorError, "Tor failed to start with errors:\n\n#{errors}")
45
+ ensure
46
+ r.close
47
+ end
48
+
49
+ def stop_tor
50
+ Process.kill 'KILL', pid if pid
51
+ FileUtils.rm_rf @data_dir if @data_dir
52
+ @pid = nil
53
+ end
54
+
55
+ private
56
+
57
+ def map_opts_to_torrc_keys(opts)
58
+ raise SystemTorError, 'Options hash keys must be snake case' unless opts.keys.map!(&:to_s).all?(&:snake_case?)
59
+
60
+ opts.transform_keys { |k| k.to_s.split('_').map(&:capitalize).join }
61
+ end
62
+
63
+ def setup_config
64
+ @torrc.write_to_config @opts
65
+ @torrc.config
66
+ end
67
+
68
+ def tor_executable?
69
+ `which tor`
70
+ end
71
+
72
+ def parse_tor_bootstrap_errors(io)
73
+ lines = []
74
+ io.each_line do |line|
75
+ lines << line
76
+ break lines if line.match BOOTSTRAP_FAIL_REGEX
77
+ break [] if line.match BOOTSTRAP_SUCCESS_REGEX
78
+ # break false if line.match BOOTSTRAP_TIMEOUT_REGEX
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
data/lib/tor/tor_prefs.rb CHANGED
@@ -17,7 +17,7 @@ module Selenium
17
17
 
18
18
  OTHER_PREFS = {
19
19
  'intl.language_notification.shown' => true, # affects font fingerprint (viewport size)
20
- 'remote.active-protocols' => 3 # new some time after selenium-webdriver 4.16
20
+ 'remote.active-protocols' => 3 # CDP & BiDi future support
21
21
  }.freeze
22
22
 
23
23
  TOR_PREFS = FIRST_CONNECTION_PREFS.merge OTHER_PREFS
data/lib/tor/torrc.rb ADDED
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Selenium
4
+ module WebDriver
5
+ module Tor
6
+ # Respresentation of a torrc file
7
+ class Torrc
8
+ class TorrcError < StandardError; end
9
+
10
+ DEFAULT_PORTS = { 'SocksPort' => 9150, 'ControlPort' => 9151 }.freeze
11
+ INVALID_OPTION_REGEX = %r{\[warn\] Failed to parse/validate config: (.*).$}
12
+
13
+ attr_reader :path
14
+
15
+ def initialize(data_dir)
16
+ @data_dir = data_dir
17
+ @torrc_file = File.new File.join(@data_dir, 'torrc'), 'w'
18
+ @path = @torrc_file.path
19
+ write_default_config
20
+ end
21
+
22
+ def config
23
+ parse_config
24
+ end
25
+
26
+ def write_to_config(hash)
27
+ raise ArgumentError, 'Torrc#write_to_config takes a hash as argument' unless hash.is_a? Hash
28
+
29
+ validate_torrc_options hash
30
+ @torrc_file.write hash_to_config_string(config.merge(hash))
31
+ @torrc_file.rewind
32
+ end
33
+
34
+ private
35
+
36
+ def write_default_config
37
+ write_to_config DEFAULT_PORTS.merge('DataDirectory' => @data_dir)
38
+ end
39
+
40
+ def validate_torrc_options(hash)
41
+ tmp = Tempfile.new
42
+ tmp.write hash_to_config_string(hash)
43
+ tmp.rewind
44
+ invalid_opts = parse_invalid_opts `tor -f #{tmp.path} --verify-config`
45
+ raise TorrcError, "Invalid torrc opts: #{invalid_opts}" unless invalid_opts.empty?
46
+ ensure
47
+ tmp.unlink
48
+ end
49
+
50
+ def parse_invalid_opts(tor_output_string)
51
+ tor_output_string.split("\n").filter_map { |line| line[INVALID_OPTION_REGEX, 1] }
52
+ end
53
+
54
+ def hash_to_config_string(hash)
55
+ hash.map { |k, v| "#{k} #{v}" }.join("\n")
56
+ end
57
+
58
+ def parse_config
59
+ File.readlines(@torrc_file).inject({}) do |memo, line|
60
+ arr = line.chomp.split
61
+ arr << '' if arr.size == 1 # deal with keys with no value
62
+ memo.merge coerce_hash_int_values_to_int(Hash[*arr])
63
+ end
64
+ end
65
+
66
+ def coerce_hash_int_values_to_int(hash)
67
+ hash.transform_values { |v| v.to_i.to_s == v ? v.to_i : v }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/tor/version.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Selenium
4
4
  module WebDriver
5
5
  module Tor
6
- VERSION = '0.2.0'
6
+ VERSION = '1.0.0'
7
7
  end
8
8
  end
9
9
  end
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: 0.2.0
4
+ version: 1.0.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-07-10 00:00:00.000000000 Z
11
+ date: 2024-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver
@@ -35,7 +35,6 @@ files:
35
35
  - ".ruby-version"
36
36
  - ".yamllint"
37
37
  - CHANGELOG.md
38
- - CODE_OF_CONDUCT.md
39
38
  - Guardfile
40
39
  - LICENSE.txt
41
40
  - README.md
@@ -44,11 +43,14 @@ files:
44
43
  - lib/options.rb
45
44
  - lib/selenium_tor.rb
46
45
  - lib/service.rb
46
+ - lib/string_extensions.rb
47
47
  - lib/tor/driver.rb
48
48
  - lib/tor/driver_not_yet_connected_to_tor_network.rb
49
49
  - lib/tor/options.rb
50
50
  - lib/tor/profile.rb
51
+ - lib/tor/system_tor.rb
51
52
  - lib/tor/tor_prefs.rb
53
+ - lib/tor/torrc.rb
52
54
  - lib/tor/version.rb
53
55
  - selenium_tor.gemspec
54
56
  - sig/tor.rbs
@@ -75,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
77
  - !ruby/object:Gem::Version
76
78
  version: '0'
77
79
  requirements: []
78
- rubygems_version: 3.5.14
80
+ rubygems_version: 3.5.15
79
81
  signing_key:
80
82
  specification_version: 4
81
83
  summary: Selenium extension for Tor Browser
data/CODE_OF_CONDUCT.md DELETED
@@ -1,84 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
-
7
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
-
9
- ## Our Standards
10
-
11
- Examples of behavior that contributes to a positive environment for our community include:
12
-
13
- * Demonstrating empathy and kindness toward other people
14
- * Being respectful of differing opinions, viewpoints, and experiences
15
- * Giving and gracefully accepting constructive feedback
16
- * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
- * Focusing on what is best not just for us as individuals, but for the overall community
18
-
19
- Examples of unacceptable behavior include:
20
-
21
- * The use of sexualized language or imagery, and sexual attention or
22
- advances of any kind
23
- * Trolling, insulting or derogatory comments, and personal or political attacks
24
- * Public or private harassment
25
- * Publishing others' private information, such as a physical or email
26
- address, without their explicit permission
27
- * Other conduct which could reasonably be considered inappropriate in a
28
- professional setting
29
-
30
- ## Enforcement Responsibilities
31
-
32
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
-
34
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
-
36
- ## Scope
37
-
38
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
-
40
- ## Enforcement
41
-
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at matzfan@mailinator.com. All complaints will be reviewed and investigated promptly and fairly.
43
-
44
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
-
46
- ## Enforcement Guidelines
47
-
48
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
-
50
- ### 1. Correction
51
-
52
- **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
-
54
- **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
-
56
- ### 2. Warning
57
-
58
- **Community Impact**: A violation through a single incident or series of actions.
59
-
60
- **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
-
62
- ### 3. Temporary Ban
63
-
64
- **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
-
66
- **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
-
68
- ### 4. Permanent Ban
69
-
70
- **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
-
72
- **Consequence**: A permanent ban from any sort of public interaction within the community.
73
-
74
- ## Attribution
75
-
76
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
- available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
-
79
- Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
-
81
- [homepage]: https://www.contributor-covenant.org
82
-
83
- For answers to common questions about this code of conduct, see the FAQ at
84
- https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.