Skip to content

Commit

Permalink
[py] verify command_executor ssl certificate by default (#6536)
Browse files Browse the repository at this point in the history
* [py] verify command_executor ssl certificate by default

And expose get_certificate_bundle_path / set_certificate_bundle_path to
allow customizing the certificate verification

* [py] add urllib3[secure] dependency to test systems

Co-authored-by: Jérome Perrin <jerome@nexedi.com>
Co-authored-by: David Burns <david.burns@theautomatedtester.co.uk>
  • Loading branch information
3 people committed Apr 3, 2020
1 parent 2f1525c commit bbc0ec0
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 3 deletions.
39 changes: 37 additions & 2 deletions py/selenium/webdriver/remote/remote_connection.py
Expand Up @@ -21,6 +21,7 @@
import socket
import string

import certifi
import urllib3

try:
Expand All @@ -43,6 +44,7 @@ class RemoteConnection(object):

browser_name = None
_timeout = socket._GLOBAL_DEFAULT_TIMEOUT
_ca_certs = certifi.where()

@classmethod
def get_timeout(cls):
Expand All @@ -69,6 +71,25 @@ def reset_timeout(cls):
"""
cls._timeout = socket._GLOBAL_DEFAULT_TIMEOUT

@classmethod
def get_certificate_bundle_path(cls):
"""
:Returns:
Paths of the .pem encoded certificate to verify connection to comand executor
"""
return cls._ca_certs

@classmethod
def set_certificate_bundle_path(cls, path):
"""
Set the path to the certificate bundle to verify connection to command executor.
Can also be set to None to disable certificate validation.
:Args:
- path - path of a .pem encoded certificate chain.
"""
cls._ca_certs = path

@classmethod
def get_remote_connection_headers(cls, parsed_url, keep_alive=False):
"""
Expand Down Expand Up @@ -111,7 +132,14 @@ def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=None):
self.keep_alive = keep_alive
self._url = remote_server_addr
if keep_alive:
self._conn = urllib3.PoolManager(timeout=self._timeout)
pool_manager_init_args = {
'timeout': self._timeout
}
if self._ca_certs:
pool_manager_init_args['cert_reqs'] = 'CERT_REQUIRED'
pool_manager_init_args['ca_certs'] = self._ca_certs
self._conn = urllib3.PoolManager(**pool_manager_init_args)

self._commands = {
Command.STATUS: ('GET', '/status'),
Command.NEW_SESSION: ('POST', '/session'),
Expand Down Expand Up @@ -374,9 +402,16 @@ def _request(self, method, url, body=None):

statuscode = resp.status
else:
with urllib3.PoolManager(timeout=self._timeout) as http:
pool_manager_init_args = {
'timeout': self._timeout
}
if self._ca_certs:
pool_manager_init_args['cert_reqs'] = 'CERT_REQUIRED'
pool_manager_init_args['ca_certs'] = self._ca_certs
with urllib3.PoolManager(**pool_manager_init_args) as http:
resp = http.request(method, url, body=body, headers=headers)


statuscode = resp.status
if not hasattr(resp, 'getheader'):
if hasattr(resp.headers, 'getheader'):
Expand Down
2 changes: 1 addition & 1 deletion py/setup.py
Expand Up @@ -67,7 +67,7 @@
'selenium.webdriver.remote',
'selenium.webdriver.support', ],
'include_package_data': True,
'install_requires': ['urllib3'],
'install_requires': ['urllib3[secure]'],
'zip_safe': False
}

Expand Down
27 changes: 27 additions & 0 deletions py/test/selenium/webdriver/remote/remote_hub_connection.py
@@ -0,0 +1,27 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

import pytest
import urllib3
from selenium import webdriver


def test_command_executor_ssl_certificate_is_verified():
with pytest.raises(urllib3.exceptions.MaxRetryError) as excinfo:
webdriver.Remote(command_executor='https://wrong.host.badssl.com/')
assert isinstance(excinfo.value.reason, urllib3.exceptions.SSLError)
assert "doesn't match" in str(excinfo.value)
27 changes: 27 additions & 0 deletions py/tox.ini
Expand Up @@ -19,11 +19,38 @@ deps =
pytest-mock==1.5.0
py{27,33,34,35,36}-{unit,chrome,firefox,chromiumedge}: pytest-xdist==1.15
urllib3==1.23
certifi==2018.8.24
py27: asn1crypto==0.24.0
py27: cffi==1.11.5
py27: cryptography==2.3.1
py27: enum34==1.1.6
py27: idna==2.7
py27: ipaddress==1.0.22
py27: pycparser==2.19
py27: pyOpenSSL==18.0.0
py27: six==1.11.0

[testenv:docs]
skip_install = true
deps =

docutils==0.11
Jinja2==2.7
MarkupSafe==0.18
Pygments==1.6
urllib3==1.23
certifi==2018.8.24
Sphinx==1.8.2
py27: asn1crypto==0.24.0
py27: cffi==1.11.5
py27: cryptography==2.3.1
py27: enum34==1.1.6
py27: idna==2.7
py27: ipaddress==1.0.22
py27: pycparser==2.19
py27: pyOpenSSL==18.0.0
py27: six==1.11.0

commands = sphinx-build -W -b html -d ../build/doctrees docs/source ../build/docs/api/py {posargs}

[testenv:flake8]
Expand Down
Binary file not shown.
Binary file not shown.
Binary file added third_party/py/cffi-1.11.5.tar.gz
Binary file not shown.
Binary file added third_party/py/cryptography-2.3.1.tar.gz
Binary file not shown.
Binary file added third_party/py/enum34-1.1.6-py3-none-any.whl
Binary file not shown.
Binary file added third_party/py/idna-2.7-py2.py3-none-any.whl
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added third_party/py/pycparser-2.19.tar.gz
Binary file not shown.
Binary file added third_party/py/six-1.11.0-py2.py3-none-any.whl
Binary file not shown.

0 comments on commit bbc0ec0

Please sign in to comment.