Skip to content

Commit 48049e8

Browse files
committed
add sanity check test for pinning when using expired certs
1 parent 4d07996 commit 48049e8

File tree

5 files changed

+135
-40
lines changed

5 files changed

+135
-40
lines changed

dropbox/dropbox_client.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,6 @@ def request_json_string(self,
590590
headers=headers,
591591
data=body,
592592
stream=stream,
593-
verify=True,
594593
timeout=timeout,
595594
)
596595
self.raise_dropbox_error_for_resp(r)

dropbox/session.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,16 @@ def init_poolmanager(self, connections, maxsize, block=False, **_):
5151
)
5252

5353
def pinned_session(pool_maxsize=8, ca_certs=None):
54-
http_adapter = _SSLAdapter(pool_connections=4, pool_maxsize=pool_maxsize, ca_certs=ca_certs)
5554
_session = requests.session()
56-
_session.mount('https://', http_adapter)
5755

56+
# always verify, use cert bundle if provided
57+
if ca_certs is not None:
58+
_session.verify = ca_certs
59+
else:
60+
_session.verify = True
61+
62+
http_adapter = _SSLAdapter(pool_connections=4, pool_maxsize=pool_maxsize, ca_certs=ca_certs)
63+
_session.mount('https://', http_adapter)
5864
return _session
65+
66+
SSLError = requests.exceptions.SSLError # raised on verification errors

test/integration/expired-certs.crt

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# GeoTrust Global CA.pem
2+
# Certificate:
3+
# Data:
4+
# Version: 3 (0x2)
5+
# Serial Number: 144470 (0x23456)
6+
# Signature Algorithm: sha1WithRSAEncryption
7+
# Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
8+
# Validity
9+
# Not Before: May 21 04:00:00 2002 GMT
10+
# Not After : May 21 04:00:00 2022 GMT
11+
# Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
12+
# Subject Public Key Info:
13+
# Public Key Algorithm: rsaEncryption
14+
# Public-Key: (2048 bit)
15+
# Modulus:
16+
# 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df:
17+
# 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8:
18+
# 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29:
19+
# bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4:
20+
# 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3:
21+
# ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92:
22+
# 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d:
23+
# 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14:
24+
# 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd:
25+
# d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6:
26+
# d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5:
27+
# 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39:
28+
# 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05:
29+
# 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2:
30+
# fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32:
31+
# eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07:
32+
# 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b:
33+
# e4:f9
34+
# Exponent: 65537 (0x10001)
35+
# X509v3 extensions:
36+
# X509v3 Basic Constraints: critical
37+
# CA:TRUE
38+
# X509v3 Subject Key Identifier:
39+
# C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
40+
# X509v3 Authority Key Identifier:
41+
# keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
42+
#
43+
# Signature Algorithm: sha1WithRSAEncryption
44+
# 35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f:
45+
# 78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf:
46+
# dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb:
47+
# 64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2:
48+
# b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20:
49+
# 8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5:
50+
# ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db:
51+
# 15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f:
52+
# b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1:
53+
# c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52:
54+
# b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9:
55+
# 5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5:
56+
# e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17:
57+
# b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80:
58+
# a1:cb:e6:33
59+
-----BEGIN CERTIFICATE-----
60+
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
61+
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
62+
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
63+
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
64+
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
65+
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
66+
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
67+
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
68+
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
69+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
70+
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
71+
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
72+
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
73+
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
74+
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
75+
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
76+
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
77+
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
78+
-----END CERTIFICATE-----

test/integration/test_dropbox.py

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
PathRoot,
4141
PathRoot_validator,
4242
)
43+
from dropbox.session import SSLError
4344

4445
# Key Types
4546
REFRESH_TOKEN_KEY = "REFRESH_TOKEN"
@@ -67,6 +68,7 @@ def _value_from_env_or_die(env_name):
6768
return value
6869

6970
_TRUSTED_CERTS_FILE = os.path.join(os.path.dirname(__file__), "trusted-certs.crt")
71+
_EXPIRED_CERTS_FILE = os.path.join(os.path.dirname(__file__), "expired-certs.crt")
7072

7173
@pytest.fixture(params=[None, _TRUSTED_CERTS_FILE], ids=["no-pinning", "pinning"])
7274
def dbx_session(request):
@@ -118,7 +120,7 @@ def dbx_share_url_from_env():
118120
TIMESTAMP = str(datetime.datetime.utcnow())
119121
STATIC_FILE = "/test.txt"
120122

121-
@pytest.fixture(scope='module', autouse=True)
123+
@pytest.fixture(scope='module')
122124
def pytest_setup():
123125
print("Setup")
124126
dbx = Dropbox(_value_from_env_or_die(format_env_name()))
@@ -133,47 +135,14 @@ def pytest_setup():
133135
except Exception:
134136
print("File not found")
135137

136-
137138
@pytest.mark.usefixtures(
139+
"pytest_setup",
138140
"dbx_from_env",
139141
"refresh_dbx_from_env",
140142
"dbx_app_auth_from_env",
141-
"dbx_share_url_from_env"
143+
"dbx_share_url_from_env",
142144
)
143145
class TestDropbox:
144-
def test_default_oauth2_urls(self):
145-
flow_obj = DropboxOAuth2Flow('dummy_app_key', 'dummy_app_secret',
146-
'http://localhost/dummy', 'dummy_session', 'dbx-auth-csrf-token')
147-
148-
assert re.match(
149-
r'^https://{}/oauth2/authorize\?'.format(re.escape(session.WEB_HOST)),
150-
flow_obj._get_authorize_url('http://localhost/redirect', 'state', 'legacy'),
151-
)
152-
153-
assert flow_obj.build_url(
154-
'/oauth2/authorize'
155-
) == 'https://{}/oauth2/authorize'.format(session.API_HOST)
156-
157-
assert flow_obj.build_url(
158-
'/oauth2/authorize', host=session.WEB_HOST
159-
) == 'https://{}/oauth2/authorize'.format(session.WEB_HOST)
160-
161-
def test_bad_auth(self):
162-
# Test malformed token
163-
malformed_token_dbx = Dropbox(MALFORMED_TOKEN)
164-
# TODO: backend is no longer returning `BadInputError`
165-
# with pytest.raises(BadInputError,) as cm:
166-
# malformed_token_dbx.files_list_folder('')
167-
# assert 'token is malformed' in cm.value.message
168-
with pytest.raises(AuthError,):
169-
malformed_token_dbx.files_list_folder('')
170-
171-
# Test reasonable-looking invalid token
172-
invalid_token_dbx = Dropbox(INVALID_TOKEN)
173-
with pytest.raises(AuthError) as cm:
174-
invalid_token_dbx.files_list_folder('')
175-
assert cm.value.error.is_invalid_access_token()
176-
177146
def test_multi_auth(self, dbx_from_env, dbx_app_auth_from_env, dbx_share_url_from_env):
178147
# Test for user (with oauth token)
179148
preview_result, resp = dbx_from_env.files_get_thumbnail_v2(
@@ -288,7 +257,10 @@ def test_versioned_route(self, dbx_from_env):
288257
# Verify response type is of v2 route
289258
assert isinstance(resp, DeleteResult)
290259

291-
@pytest.mark.usefixtures("dbx_team_from_env")
260+
@pytest.mark.usefixtures(
261+
"pytest_setup",
262+
"dbx_team_from_env",
263+
)
292264
class TestDropboxTeam:
293265
def test_team(self, dbx_team_from_env):
294266
dbx_team_from_env.team_groups_list()
@@ -318,3 +290,41 @@ def test_clone_when_team_linked(self, dbx_team_from_env):
318290
new_dbxt = dbx_team_from_env.clone()
319291
assert dbx_team_from_env is not new_dbxt
320292
assert isinstance(new_dbxt, dbx_team_from_env.__class__)
293+
294+
def test_default_oauth2_urls():
295+
flow_obj = DropboxOAuth2Flow('dummy_app_key', 'dummy_app_secret',
296+
'http://localhost/dummy', 'dummy_session', 'dbx-auth-csrf-token')
297+
298+
assert re.match(
299+
r'^https://{}/oauth2/authorize\?'.format(re.escape(session.WEB_HOST)),
300+
flow_obj._get_authorize_url('http://localhost/redirect', 'state', 'legacy'),
301+
)
302+
303+
assert flow_obj.build_url(
304+
'/oauth2/authorize'
305+
) == 'https://{}/oauth2/authorize'.format(session.API_HOST)
306+
307+
assert flow_obj.build_url(
308+
'/oauth2/authorize', host=session.WEB_HOST
309+
) == 'https://{}/oauth2/authorize'.format(session.WEB_HOST)
310+
311+
def test_bad_auth(dbx_session):
312+
# Test malformed token
313+
malformed_token_dbx = Dropbox(MALFORMED_TOKEN, session=dbx_session)
314+
# TODO: backend is no longer returning `BadInputError`
315+
# with pytest.raises(BadInputError,) as cm:
316+
# malformed_token_dbx.files_list_folder('')
317+
# assert 'token is malformed' in cm.value.message
318+
with pytest.raises(AuthError):
319+
malformed_token_dbx.files_list_folder('')
320+
321+
# Test reasonable-looking invalid token
322+
invalid_token_dbx = Dropbox(INVALID_TOKEN, session=dbx_session)
323+
with pytest.raises(AuthError) as cm:
324+
invalid_token_dbx.files_list_folder('')
325+
assert cm.value.error.is_invalid_access_token()
326+
327+
def test_bad_pins():
328+
_dbx = Dropbox("dummy_token", ca_certs=_EXPIRED_CERTS_FILE)
329+
with pytest.raises(SSLError,) as cm:
330+
_dbx.files_list_folder('')

test/integration/trusted-certs.crt

100755100644
File mode changed.

0 commit comments

Comments
 (0)