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