##// END OF EJS Templates
tests: import CPython's hostname matching tests...
Gregory Szorc -
r29451:676f4d0e stable
parent child Browse files
Show More
@@ -1,250 +1,422 b''
1 # coding=utf-8
1 2 from __future__ import absolute_import, print_function
2 3
3 4 import doctest
4 5 import os
5 6
6 7 def check(a, b):
7 8 if a != b:
8 9 print((a, b))
9 10
10 11 def cert(cn):
11 12 return {'subject': ((('commonName', cn),),)}
12 13
13 14 from mercurial import (
14 15 sslutil,
15 16 )
16 17
17 18 _verifycert = sslutil._verifycert
18 19 # Test non-wildcard certificates
19 20 check(_verifycert(cert('example.com'), 'example.com'),
20 21 None)
21 22 check(_verifycert(cert('example.com'), 'www.example.com'),
22 23 'certificate is for example.com')
23 24 check(_verifycert(cert('www.example.com'), 'example.com'),
24 25 'certificate is for www.example.com')
25 26
26 27 # Test wildcard certificates
27 28 check(_verifycert(cert('*.example.com'), 'www.example.com'),
28 29 None)
29 30 check(_verifycert(cert('*.example.com'), 'example.com'),
30 31 'certificate is for *.example.com')
31 32 check(_verifycert(cert('*.example.com'), 'w.w.example.com'),
32 33 'certificate is for *.example.com')
33 34
34 35 # Test subjectAltName
35 36 san_cert = {'subject': ((('commonName', 'example.com'),),),
36 37 'subjectAltName': (('DNS', '*.example.net'),
37 38 ('DNS', 'example.net'))}
38 39 check(_verifycert(san_cert, 'example.net'),
39 40 None)
40 41 check(_verifycert(san_cert, 'foo.example.net'),
41 42 None)
42 43 # no fallback to subject commonName when subjectAltName has DNS
43 44 check(_verifycert(san_cert, 'example.com'),
44 45 'certificate is for *.example.net, example.net')
45 46 # fallback to subject commonName when no DNS in subjectAltName
46 47 san_cert = {'subject': ((('commonName', 'example.com'),),),
47 48 'subjectAltName': (('IP Address', '8.8.8.8'),)}
48 49 check(_verifycert(san_cert, 'example.com'), None)
49 50
50 51 # Avoid some pitfalls
51 52 check(_verifycert(cert('*.foo'), 'foo'),
52 53 'certificate is for *.foo')
53 54 check(_verifycert(cert('*o'), 'foo'),
54 55 'certificate is for *o')
55 56
56 57 check(_verifycert({'subject': ()},
57 58 'example.com'),
58 59 'no commonName or subjectAltName found in certificate')
59 60 check(_verifycert(None, 'example.com'),
60 61 'no certificate received')
61 62
62 63 # Unicode (IDN) certname isn't supported
63 64 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
64 65 'IDN in certificate not supported')
65 66
67 # The following tests are from CPython's test_ssl.py.
68 check(_verifycert(cert('example.com'), 'example.com'), None)
69 check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None)
70 check(_verifycert(cert('example.com'), 'www.example.com'),
71 'certificate is for example.com')
72 check(_verifycert(cert('example.com'), '.example.com'),
73 'certificate is for example.com')
74 check(_verifycert(cert('example.com'), 'example.org'),
75 'certificate is for example.com')
76 check(_verifycert(cert('example.com'), 'exampleXcom'),
77 'certificate is for example.com')
78 check(_verifycert(cert('*.a.com'), 'foo.a.com'), None)
79 check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'),
80 'certificate is for *.a.com')
81 check(_verifycert(cert('*.a.com'), 'a.com'),
82 'certificate is for *.a.com')
83 check(_verifycert(cert('*.a.com'), 'Xa.com'),
84 'certificate is for *.a.com')
85 check(_verifycert(cert('*.a.com'), '.a.com'), None)
86
87 # only match one left-most wildcard
88 check(_verifycert(cert('f*.com'), 'foo.com'),
89 'certificate is for f*.com')
90 check(_verifycert(cert('f*.com'), 'f.com'),
91 'certificate is for f*.com')
92 check(_verifycert(cert('f*.com'), 'bar.com'),
93 'certificate is for f*.com')
94 check(_verifycert(cert('f*.com'), 'foo.a.com'),
95 'certificate is for f*.com')
96 check(_verifycert(cert('f*.com'), 'bar.foo.com'),
97 'certificate is for f*.com')
98
99 # NULL bytes are bad, CVE-2013-4073
100 check(_verifycert(cert('null.python.org\x00example.org'),
101 'null.python.org\x00example.org'), None)
102 check(_verifycert(cert('null.python.org\x00example.org'),
103 'example.org'),
104 'certificate is for null.python.org\x00example.org')
105 check(_verifycert(cert('null.python.org\x00example.org'),
106 'null.python.org'),
107 'certificate is for null.python.org\x00example.org')
108
109 # error cases with wildcards
110 check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'),
111 'certificate is for *.*.a.com')
112 check(_verifycert(cert('*.*.a.com'), 'a.com'),
113 'certificate is for *.*.a.com')
114 check(_verifycert(cert('*.*.a.com'), 'Xa.com'),
115 'certificate is for *.*.a.com')
116 check(_verifycert(cert('*.*.a.com'), '.a.com'),
117 'certificate is for *.*.a.com')
118
119 check(_verifycert(cert('a.*.com'), 'a.foo.com'),
120 'certificate is for a.*.com')
121 check(_verifycert(cert('a.*.com'), 'a..com'),
122 'certificate is for a.*.com')
123 check(_verifycert(cert('a.*.com'), 'a.com'),
124 'certificate is for a.*.com')
125
126 # wildcard doesn't match IDNA prefix 'xn--'
127 idna = u'pΓΌthon.python.org'.encode('idna').decode('ascii')
128 check(_verifycert(cert(idna), idna), None)
129 check(_verifycert(cert('x*.python.org'), idna),
130 'certificate is for x*.python.org')
131 check(_verifycert(cert('xn--p*.python.org'), idna),
132 'certificate is for xn--p*.python.org')
133
134 # wildcard in first fragment and IDNA A-labels in sequent fragments
135 # are supported.
136 idna = u'www*.pythΓΆn.org'.encode('idna').decode('ascii')
137 check(_verifycert(cert(idna),
138 u'www.pythΓΆn.org'.encode('idna').decode('ascii')),
139 'certificate is for www*.xn--pythn-mua.org')
140 check(_verifycert(cert(idna),
141 u'www1.pythΓΆn.org'.encode('idna').decode('ascii')),
142 'certificate is for www*.xn--pythn-mua.org')
143 check(_verifycert(cert(idna),
144 u'ftp.pythΓΆn.org'.encode('idna').decode('ascii')),
145 'certificate is for www*.xn--pythn-mua.org')
146 check(_verifycert(cert(idna),
147 u'pythΓΆn.org'.encode('idna').decode('ascii')),
148 'certificate is for www*.xn--pythn-mua.org')
149
150 c = {
151 'notAfter': 'Jun 26 21:41:46 2011 GMT',
152 'subject': (((u'commonName', u'linuxfrz.org'),),),
153 'subjectAltName': (
154 ('DNS', 'linuxfr.org'),
155 ('DNS', 'linuxfr.com'),
156 ('othername', '<unsupported>'),
157 )
158 }
159 check(_verifycert(c, 'linuxfr.org'), None)
160 check(_verifycert(c, 'linuxfr.com'), None)
161 # Not a "DNS" entry
162 check(_verifycert(c, '<unsupported>'),
163 'certificate is for linuxfr.org, linuxfr.com')
164 # When there is a subjectAltName, commonName isn't used
165 check(_verifycert(c, 'linuxfrz.org'),
166 'certificate is for linuxfr.org, linuxfr.com')
167
168 # A pristine real-world example
169 c = {
170 'notAfter': 'Dec 18 23:59:59 2011 GMT',
171 'subject': (
172 ((u'countryName', u'US'),),
173 ((u'stateOrProvinceName', u'California'),),
174 ((u'localityName', u'Mountain View'),),
175 ((u'organizationName', u'Google Inc'),),
176 ((u'commonName', u'mail.google.com'),),
177 ),
178 }
179 check(_verifycert(c, 'mail.google.com'), None)
180 check(_verifycert(c, 'gmail.com'), 'certificate is for mail.google.com')
181
182 # Only commonName is considered
183 check(_verifycert(c, 'California'), 'certificate is for mail.google.com')
184
185 # Neither commonName nor subjectAltName
186 c = {
187 'notAfter': 'Dec 18 23:59:59 2011 GMT',
188 'subject': (
189 ((u'countryName', u'US'),),
190 ((u'stateOrProvinceName', u'California'),),
191 ((u'localityName', u'Mountain View'),),
192 ((u'organizationName', u'Google Inc'),),
193 ),
194 }
195 check(_verifycert(c, 'mail.google.com'),
196 'no commonName or subjectAltName found in certificate')
197
198 # No DNS entry in subjectAltName but a commonName
199 c = {
200 'notAfter': 'Dec 18 23:59:59 2099 GMT',
201 'subject': (
202 ((u'countryName', u'US'),),
203 ((u'stateOrProvinceName', u'California'),),
204 ((u'localityName', u'Mountain View'),),
205 ((u'commonName', u'mail.google.com'),),
206 ),
207 'subjectAltName': (('othername', 'blabla'),),
208 }
209 check(_verifycert(c, 'mail.google.com'), None)
210
211 # No DNS entry subjectAltName and no commonName
212 c = {
213 'notAfter': 'Dec 18 23:59:59 2099 GMT',
214 'subject': (
215 ((u'countryName', u'US'),),
216 ((u'stateOrProvinceName', u'California'),),
217 ((u'localityName', u'Mountain View'),),
218 ((u'organizationName', u'Google Inc'),),
219 ),
220 'subjectAltName': (('othername', 'blabla'),),
221 }
222 check(_verifycert(c, 'google.com'),
223 'no commonName or subjectAltName found in certificate')
224
225 # Empty cert / no cert
226 check(_verifycert(None, 'example.com'), 'no certificate received')
227 check(_verifycert({}, 'example.com'), 'no certificate received')
228
229 # avoid denials of service by refusing more than one
230 # wildcard per fragment.
231 check(_verifycert({'subject': (((u'commonName', u'a*b.com'),),)},
232 'axxb.com'), 'certificate is for a*b.com')
233 check(_verifycert({'subject': (((u'commonName', u'a*b.co*'),),)},
234 'axxb.com'), 'certificate is for a*b.co*')
235 check(_verifycert({'subject': (((u'commonName', u'a*b*.com'),),)},
236 'axxbxxc.com'), 'certificate is for a*b*.com')
237
66 238 def test_url():
67 239 """
68 240 >>> from mercurial.util import url
69 241
70 242 This tests for edge cases in url.URL's parsing algorithm. Most of
71 243 these aren't useful for documentation purposes, so they aren't
72 244 part of the class's doc tests.
73 245
74 246 Query strings and fragments:
75 247
76 248 >>> url('http://host/a?b#c')
77 249 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
78 250 >>> url('http://host/a?')
79 251 <url scheme: 'http', host: 'host', path: 'a'>
80 252 >>> url('http://host/a#b#c')
81 253 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
82 254 >>> url('http://host/a#b?c')
83 255 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
84 256 >>> url('http://host/?a#b')
85 257 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
86 258 >>> url('http://host/?a#b', parsequery=False)
87 259 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
88 260 >>> url('http://host/?a#b', parsefragment=False)
89 261 <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
90 262 >>> url('http://host/?a#b', parsequery=False, parsefragment=False)
91 263 <url scheme: 'http', host: 'host', path: '?a#b'>
92 264
93 265 IPv6 addresses:
94 266
95 267 >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one')
96 268 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
97 269 query: 'objectClass?one'>
98 270 >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
99 271 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
100 272 port: '80', path: 'c=GB', query: 'objectClass?one'>
101 273
102 274 Missing scheme, host, etc.:
103 275
104 276 >>> url('://192.0.2.16:80/')
105 277 <url path: '://192.0.2.16:80/'>
106 278 >>> url('https://mercurial-scm.org')
107 279 <url scheme: 'https', host: 'mercurial-scm.org'>
108 280 >>> url('/foo')
109 281 <url path: '/foo'>
110 282 >>> url('bundle:/foo')
111 283 <url scheme: 'bundle', path: '/foo'>
112 284 >>> url('a?b#c')
113 285 <url path: 'a?b', fragment: 'c'>
114 286 >>> url('http://x.com?arg=/foo')
115 287 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
116 288 >>> url('http://joe:xxx@/foo')
117 289 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
118 290
119 291 Just a scheme and a path:
120 292
121 293 >>> url('mailto:John.Doe@example.com')
122 294 <url scheme: 'mailto', path: 'John.Doe@example.com'>
123 295 >>> url('a:b:c:d')
124 296 <url path: 'a:b:c:d'>
125 297 >>> url('aa:bb:cc:dd')
126 298 <url scheme: 'aa', path: 'bb:cc:dd'>
127 299
128 300 SSH examples:
129 301
130 302 >>> url('ssh://joe@host//home/joe')
131 303 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
132 304 >>> url('ssh://joe:xxx@host/src')
133 305 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
134 306 >>> url('ssh://joe:xxx@host')
135 307 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
136 308 >>> url('ssh://joe@host')
137 309 <url scheme: 'ssh', user: 'joe', host: 'host'>
138 310 >>> url('ssh://host')
139 311 <url scheme: 'ssh', host: 'host'>
140 312 >>> url('ssh://')
141 313 <url scheme: 'ssh'>
142 314 >>> url('ssh:')
143 315 <url scheme: 'ssh'>
144 316
145 317 Non-numeric port:
146 318
147 319 >>> url('http://example.com:dd')
148 320 <url scheme: 'http', host: 'example.com', port: 'dd'>
149 321 >>> url('ssh://joe:xxx@host:ssh/foo')
150 322 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
151 323 path: 'foo'>
152 324
153 325 Bad authentication credentials:
154 326
155 327 >>> url('http://joe@joeville:123@4:@host/a?b#c')
156 328 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
157 329 host: 'host', path: 'a', query: 'b', fragment: 'c'>
158 330 >>> url('http://!*#?/@!*#?/:@host/a?b#c')
159 331 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
160 332 >>> url('http://!*#?@!*#?:@host/a?b#c')
161 333 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
162 334 >>> url('http://!*@:!*@@host/a?b#c')
163 335 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
164 336 path: 'a', query: 'b', fragment: 'c'>
165 337
166 338 File paths:
167 339
168 340 >>> url('a/b/c/d.g.f')
169 341 <url path: 'a/b/c/d.g.f'>
170 342 >>> url('/x///z/y/')
171 343 <url path: '/x///z/y/'>
172 344 >>> url('/foo:bar')
173 345 <url path: '/foo:bar'>
174 346 >>> url('\\\\foo:bar')
175 347 <url path: '\\\\foo:bar'>
176 348 >>> url('./foo:bar')
177 349 <url path: './foo:bar'>
178 350
179 351 Non-localhost file URL:
180 352
181 353 >>> u = url('file://mercurial-scm.org/foo')
182 354 Traceback (most recent call last):
183 355 File "<stdin>", line 1, in ?
184 356 Abort: file:// URLs can only refer to localhost
185 357
186 358 Empty URL:
187 359
188 360 >>> u = url('')
189 361 >>> u
190 362 <url path: ''>
191 363 >>> str(u)
192 364 ''
193 365
194 366 Empty path with query string:
195 367
196 368 >>> str(url('http://foo/?bar'))
197 369 'http://foo/?bar'
198 370
199 371 Invalid path:
200 372
201 373 >>> u = url('http://foo/bar')
202 374 >>> u.path = 'bar'
203 375 >>> str(u)
204 376 'http://foo/bar'
205 377
206 378 >>> u = url('file:/foo/bar/baz')
207 379 >>> u
208 380 <url scheme: 'file', path: '/foo/bar/baz'>
209 381 >>> str(u)
210 382 'file:///foo/bar/baz'
211 383 >>> u.localpath()
212 384 '/foo/bar/baz'
213 385
214 386 >>> u = url('file:///foo/bar/baz')
215 387 >>> u
216 388 <url scheme: 'file', path: '/foo/bar/baz'>
217 389 >>> str(u)
218 390 'file:///foo/bar/baz'
219 391 >>> u.localpath()
220 392 '/foo/bar/baz'
221 393
222 394 >>> u = url('file:///f:oo/bar/baz')
223 395 >>> u
224 396 <url scheme: 'file', path: 'f:oo/bar/baz'>
225 397 >>> str(u)
226 398 'file:///f:oo/bar/baz'
227 399 >>> u.localpath()
228 400 'f:oo/bar/baz'
229 401
230 402 >>> u = url('file://localhost/f:oo/bar/baz')
231 403 >>> u
232 404 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'>
233 405 >>> str(u)
234 406 'file://localhost/f:oo/bar/baz'
235 407 >>> u.localpath()
236 408 'f:oo/bar/baz'
237 409
238 410 >>> u = url('file:foo/bar/baz')
239 411 >>> u
240 412 <url scheme: 'file', path: 'foo/bar/baz'>
241 413 >>> str(u)
242 414 'file:foo/bar/baz'
243 415 >>> u.localpath()
244 416 'foo/bar/baz'
245 417 """
246 418
247 419 if 'TERM' in os.environ:
248 420 del os.environ['TERM']
249 421
250 422 doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
General Comments 0
You need to be logged in to leave comments. Login now