|
| 1 | +import http |
| 2 | + |
1 | 3 | from tornado.concurrent import Future |
2 | 4 | from tornado import gen |
3 | 5 | from tornado.escape import ( |
@@ -292,11 +294,67 @@ def get(self): |
292 | 294 | self.set_cookie("unicode_args", "blah", domain="foo.com", path="/foo") |
293 | 295 |
|
294 | 296 | class SetCookieSpecialCharHandler(RequestHandler): |
| 297 | + # "Special" characters are allowed in cookie values, but trigger special quoting. |
295 | 298 | def get(self): |
296 | 299 | self.set_cookie("equals", "a=b") |
297 | 300 | self.set_cookie("semicolon", "a;b") |
298 | 301 | self.set_cookie("quote", 'a"b') |
299 | 302 |
|
| 303 | + class SetCookieForbiddenCharHandler(RequestHandler): |
| 304 | + def get(self): |
| 305 | + # Control characters and semicolons raise errors in cookie names and attributes |
| 306 | + # (but not values, which are tested in SetCookieSpecialCharHandler) |
| 307 | + for char in list(map(chr, range(0x20))) + [chr(0x7F), ";"]: |
| 308 | + try: |
| 309 | + self.set_cookie("foo" + char, "bar") |
| 310 | + self.write( |
| 311 | + "Didn't get expected exception for char %r in name\n" % char |
| 312 | + ) |
| 313 | + except http.cookies.CookieError as e: |
| 314 | + if "Invalid cookie attribute name" not in str(e): |
| 315 | + self.write( |
| 316 | + "unexpected exception for char %r in name: %s\n" |
| 317 | + % (char, e) |
| 318 | + ) |
| 319 | + |
| 320 | + try: |
| 321 | + self.set_cookie("foo", "bar", domain="example" + char + ".com") |
| 322 | + self.write( |
| 323 | + "Didn't get expected exception for char %r in domain\n" |
| 324 | + % char |
| 325 | + ) |
| 326 | + except http.cookies.CookieError as e: |
| 327 | + if "Invalid cookie attribute domain" not in str(e): |
| 328 | + self.write( |
| 329 | + "unexpected exception for char %r in domain: %s\n" |
| 330 | + % (char, e) |
| 331 | + ) |
| 332 | + |
| 333 | + try: |
| 334 | + self.set_cookie("foo", "bar", path="/" + char) |
| 335 | + self.write( |
| 336 | + "Didn't get expected exception for char %r in path\n" % char |
| 337 | + ) |
| 338 | + except http.cookies.CookieError as e: |
| 339 | + if "Invalid cookie attribute path" not in str(e): |
| 340 | + self.write( |
| 341 | + "unexpected exception for char %r in path: %s\n" |
| 342 | + % (char, e) |
| 343 | + ) |
| 344 | + |
| 345 | + try: |
| 346 | + self.set_cookie("foo", "bar", samesite="a" + char) |
| 347 | + self.write( |
| 348 | + "Didn't get expected exception for char %r in samesite\n" |
| 349 | + % char |
| 350 | + ) |
| 351 | + except http.cookies.CookieError as e: |
| 352 | + if "Invalid cookie attribute samesite" not in str(e): |
| 353 | + self.write( |
| 354 | + "unexpected exception for char %r in samesite: %s\n" |
| 355 | + % (char, e) |
| 356 | + ) |
| 357 | + |
300 | 358 | class SetCookieOverwriteHandler(RequestHandler): |
301 | 359 | def get(self): |
302 | 360 | self.set_cookie("a", "b", domain="example.com") |
@@ -330,6 +388,7 @@ def get(self): |
330 | 388 | ("/get", GetCookieHandler), |
331 | 389 | ("/set_domain", SetCookieDomainHandler), |
332 | 390 | ("/special_char", SetCookieSpecialCharHandler), |
| 391 | + ("/forbidden_char", SetCookieForbiddenCharHandler), |
333 | 392 | ("/set_overwrite", SetCookieOverwriteHandler), |
334 | 393 | ("/set_max_age", SetCookieMaxAgeHandler), |
335 | 394 | ("/set_expires_days", SetCookieExpiresDaysHandler), |
@@ -387,6 +446,12 @@ def test_cookie_special_char(self): |
387 | 446 | response = self.fetch("/get", headers={"Cookie": header}) |
388 | 447 | self.assertEqual(response.body, utf8(expected)) |
389 | 448 |
|
| 449 | + def test_set_cookie_forbidden_char(self): |
| 450 | + response = self.fetch("/forbidden_char") |
| 451 | + self.assertEqual(response.code, 200) |
| 452 | + self.maxDiff = 10000 |
| 453 | + self.assertMultiLineEqual(to_unicode(response.body), "") |
| 454 | + |
390 | 455 | def test_set_cookie_overwrite(self): |
391 | 456 | response = self.fetch("/set_overwrite") |
392 | 457 | headers = response.headers.get_list("Set-Cookie") |
|
0 commit comments