1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
|
# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import pytest
from qutebrowser.browser.webkit import httpheaders
DEFAULT_NAME = 'qutebrowser-download'
_STDLIB_XFAIL = pytest.mark.xfail(reason="Not handled properly by Python stdlib")
class HeaderChecker:
"""Helper class with some convenience methods to check filenames.
Attributes:
caplog: fixture from pytest-capturelog
stubs: fixture that provides testing stubs
"""
def __init__(self, caplog, stubs):
self.caplog = caplog
self.stubs = stubs
def check_filename(self, header, filename, expected_inline=False):
"""Check if the passed header has the given filename."""
reply = self.stubs.FakeNetworkReply(
headers={'Content-Disposition': header})
cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert cd_filename is not None
assert cd_filename == filename
assert cd_inline == expected_inline
def check_ignored(self, header):
"""Check if the passed header is ignored."""
reply = self.stubs.FakeNetworkReply(
headers={'Content-Disposition': header})
with self.caplog.at_level(logging.ERROR, 'network'):
cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert cd_filename == DEFAULT_NAME
assert cd_inline
def check_unnamed(self, header):
"""Check if the passed header results in an unnamed attachment."""
reply = self.stubs.FakeNetworkReply(
headers={'Content-Disposition': header})
cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert cd_filename == DEFAULT_NAME
assert not cd_inline
@pytest.fixture
def header_checker(caplog, stubs):
"""Fixture that provides a HeaderChecker class for tests"""
return HeaderChecker(caplog, stubs)
def test_inline_caps(header_checker):
"""'inline' in upper-case."""
header_checker.check_filename(
'INLINE; filename="foo.html"', 'foo.html', expected_inline=True)
def test_att_double_space(header_checker):
"""'attachment' with double space in the filename.
It's unclear how this should be handled exactly. The original test expected 'foo
bar.html' as filename, but there doesn't seem to be a place in the RFC actually
specifying that. The test was added in 8ce779261f08b20fd3f6e889204eb88e6a6800c1 but
without further explanations why. Thus, it's now updated to expect the original
filename, with two spaces.
"""
header_checker.check_filename(
'attachment; filename="foo bar.html"', 'foo bar.html')
def test_iso2231_langtag(header_checker):
"""'attachment', specifying a filename of foo-ä.html with language tag.
Using RFC2231/5987 encoded ISO-8859-1.
UA should offer to download the resource as "foo-ä.html".
"""
header_checker.check_filename(
"attachment; filename*=iso-8859-1'de'foo-%E4.html",
'foo-ä.html')
@_STDLIB_XFAIL
def test_iso2231_iso8859_invalid_chars(header_checker):
"""'attachment', specifying a filename with invalid ISO-8859-1 chars."""
header_checker.check_ignored(
"attachment; filename*=iso-8859-1''foo-%80.html")
# All following test cases are based on https://greenbytes.de/tech/tc2231/
class TestInline:
"""Various tests relating to the "inline" disposition type.
See Section 4.2 of RFC 6266.
"""
def test_inlonly(self, header_checker):
"""'inline' only
This should be equivalent to not including the header at all.
"""
header_checker.check_ignored('inline')
def test_inlonlyquoted(self, header_checker):
"""'inline' only, using double quotes
This is invalid syntax, thus the header should be ignored.
"""
header_checker.check_ignored('"inline"')
def test_inlwithasciifilename(self, header_checker):
"""'inline', specifying a filename of foo.html
Some UAs use this filename in a subsequent "save" operation.
"""
header_checker.check_filename('inline; filename="foo.html"',
'foo.html', expected_inline=True)
def test_inlwithfnattach(self, header_checker):
"""'inline', specifying a filename of "Not an attachment!".
This checks for proper parsing for disposition types.
"""
header_checker.check_filename('inline; filename="Not an attachment!"',
"Not an attachment!",
expected_inline=True)
def test_inlwithasciifilenamepdf(self, header_checker):
"""'inline', specifying a filename of foo.pdf.
Some UAs use this filename in a subsequent "save" operation. This
variation of the test checks whether whatever handles PDF display
receives the filename information, and acts upon it (this was tested
with the latest Acrobat Reader plugin, or, in the case of Chrome, using
the built-in PDF handler).
"""
header_checker.check_filename('inline; filename="foo.pdf"', "foo.pdf",
expected_inline=True)
class TestAttachment:
"""Various tests relating to the "attachment" disposition type.
See Section 4.2 of RFC 6266.
"""
def test_attonly(self, stubs):
"""'attachment' only.
UA should offer to download the resource.
"""
reply = stubs.FakeNetworkReply(
headers={'Content-Disposition': 'attachment'})
cd_inline, cd_filename = httpheaders.parse_content_disposition(reply)
assert not cd_inline
assert cd_filename == DEFAULT_NAME
def test_attonlyquoted(self, header_checker):
"""'attachment' only, using double quotes
This is invalid syntax, thus the header should be ignored.
"""
header_checker.check_ignored('"attachment"')
# we can't test attonly403 here.
def test_attonlyucase(self, header_checker):
"""'ATTACHMENT' only
UA should offer to download the resource.
"""
header_checker.check_unnamed('ATTACHMENT')
def test_attwithasciifilename(self, header_checker):
"""'attachment', specifying a filename of foo.html
UA should offer to download the resource as "foo.html".
"""
header_checker.check_filename('attachment; filename="foo.html"',
'foo.html')
def test_attwithasciifilename25(self, header_checker):
"""'attachment', with a 25 character filename."""
header_checker.check_filename(
'attachment; filename="0000000000111111111122222"',
'0000000000111111111122222')
def test_attwithasciifilename35(self, header_checker):
"""'attachment', with a 35 character filename."""
header_checker.check_filename(
'attachment; filename="00000000001111111111222222222233333"',
'00000000001111111111222222222233333')
def test_attwithasciifnescapedchar(self, header_checker):
r"""'attachment', specifying a filename of f\oo.html.
(the first 'o' being escaped)
UA should offer to download the resource as "foo.html".
"""
header_checker.check_filename(r'attachment; filename="f\oo.html"',
'foo.html')
def test_attwithasciifnescapedquote(self, header_checker):
r"""'attachment', specifying a filename of \"quoting\" tested.html
(using double quotes around "quoting" to test... quoting)
UA should offer to download the resource as something like '"quoting"
tested.html' (stripping the quotes may be ok for security reasons, but
getting confused by them is not).
"""
header = r'attachment; filename="\"quoting\" tested.html"'
header_checker.check_filename(header, '"quoting" tested.html')
def test_attwithquotedsemicolon(self, header_checker):
"""'attachment', specifying a filename of Here's a semicolon;.html.
This checks for proper parsing for parameters.
"""
header_checker.check_filename(
'attachment; filename="Here\'s a semicolon;.html"',
"Here's a semicolon;.html")
def test_attwithfilenameandextparam(self, header_checker):
"""'attachment', specifying a filename of foo.html.
And an extension parameter "foo" which should be ignored (see Section
4.4 of RFC 6266.).
UA should offer to download the resource as "foo.html".
"""
header_checker.check_filename(
'attachment; foo="bar"; filename="foo.html"',
'foo.html')
def test_attwithfilenameandextparamescaped(self, header_checker):
"""'attachment', specifying a filename of foo.html.
And an extension parameter "foo" which should be ignored (see Section
4.4 of RFC 6266.). In comparison to attwithfilenameandextparam, the
extension parameter actually uses backslash-escapes. This tests whether
the UA properly skips the parameter.
UA should offer to download the resource as "foo.html".
"""
header_checker.check_filename(
r'attachment; foo="\"\\";filename="foo.html"', 'foo.html')
def test_attwithasciifilenameucase(self, header_checker):
"""'attachment', specifying a filename of foo.html
UA should offer to download the resource as "foo.html".
"""
header_checker.check_filename(r'attachment; FILENAME="foo.html"',
'foo.html')
def test_attwithasciifilenamenq(self, header_checker):
"""'attachment', specifying a filename of foo.html.
(using a token instead of a quoted-string).
Note that was invalid according to Section 19.5.1 of RFC 2616.
"""
header_checker.check_filename('attachment; filename=foo.html',
'foo.html')
def test_attwithtokfncommanq(self, header_checker):
"""'attachment', specifying a filename of foo,bar.html.
(using a comma despite using token syntax).
"""
header_checker.check_ignored('attachment; filename=foo,bar.html')
@pytest.mark.xfail(reason='With relaxed=True we accept that')
def test_attwithasciifilenamenqs(self, header_checker):
"""'attachment', specifying a filename of foo.html.
(using a token instead of a quoted-string, and adding a trailing
semicolon).
The trailing semicolon makes the header field value syntactically
incorrect, as no other parameter follows. Thus the header field should
be ignored.
"""
header_checker.check_ignored('attachment; filename=foo.html ;')
def test_attemptyparam(self, header_checker):
"""'attachment', specifying a filename of foo.
(but including an empty parameter).
The empty parameter makes the header field value syntactically
incorrect, as no other parameter follows. Thus the header field should
be ignored.
"""
header_checker.check_ignored('attachment; ;filename=foo')
def test_attwithasciifilenamenqws(self, header_checker):
"""'attachment', specifying a filename of foo bar.html.
(without using quoting).
This is invalid. "token" does not allow whitespace.
"""
header_checker.check_ignored('attachment; filename=foo bar.html')
@_STDLIB_XFAIL
def test_attwithfntokensq(self, header_checker):
"""'attachment', specifying a filename of 'foo.bar'
(using single quotes).
"""
header_checker.check_filename("attachment; filename='foo.bar'",
"'foo.bar'")
def test_attwithisofnplain(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
(using plain ISO-8859-1)
UA should offer to download the resource as "foo-ä.html".
"""
header_checker.check_filename('attachment; filename="foo-ä.html"',
'foo-ä.html')
def test_attwithutf8fnplain(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
(which happens to be foo-ä.html using UTF-8 encoding).
UA should offer to download the resource as "foo-ä.html". Displaying
"foo-ä.html" instead indicates that the UA tried to be smart by
detecting something that happens to look like UTF-8.
"""
header_checker.check_filename('attachment; filename="foo-ä.html"',
'foo-ä.html')
def test_attwithfnrawpctenca(self, header_checker):
"""'attachment', specifying a filename of foo-%41.html
UA should offer to download the resource as "foo-%41.html". Displaying
"foo-A.html" instead would indicate that the UA has attempted to
percent-decode the parameter.
"""
header_checker.check_filename('attachment; filename="foo-%41.html"',
'foo-%41.html')
def test_attwithfnusingpct(self, header_checker):
"""'attachment', specifying a filename of 50%.html
UA should offer to download the resource as "50%.html". This tests how
UAs that fails at attwithfnrawpctenca handle "%" characters that do not
start a "% hexdig hexdig" sequence.
"""
header_checker.check_filename('attachment; filename="50%.html"',
'50%.html')
def test_attwithfnrawpctencaq(self, header_checker):
"""'attachment', specifying a filename of foo-%41.html.
Using an escape character (this tests whether adding an escape
character inside a %xx sequence can be used to disable the
non-conformant %xx-unescaping).
UA should offer to download the resource as "foo-%41.html".
"""
header_checker.check_filename(r'attachment; filename="foo-%\41.html"',
'foo-%41.html')
def test_attwithnamepct(self, header_checker):
"""'attachment', specifying a name parameter of foo-%41.html.
(This test was added to observe the behavior of the (unspecified)
treatment of "name" as synonym for "filename"; see Ned Freed's
summary[1] where this comes from in MIME messages)
Should be treated as extension parameter, therefore almost any behavior
is acceptable.
[1] https://web.archive.org/web/20150317023040/http://www.imc.org/ietf-smtp/mail-archive/msg05023.html
"""
header_checker.check_unnamed('attachment; name="foo-%41.html"')
def test_attwithfilenamepctandiso(self, header_checker):
"""'attachment', specifying a filename parameter of ä-%41.html.
(this test was added to observe the behavior when non-ASCII characters
and percent-hexdig sequences are combined)
"""
header_checker.check_filename('attachment; filename="ä-%41.html"',
'ä-%41.html')
def test_attwithfnrawpctenclong(self, header_checker):
"""'attachment', specifying a filename of foo-%c3%a4-%e2%82%ac.html.
(using raw percent encoded UTF-8 to represent foo-ä-€.html)
UA should offer to download the resource as
"foo-%c3%a4-%e2%82%ac.html". Displaying "foo-ä-€.html" instead would
indicate that the UA has attempted to percent-decode the parameter
(using UTF-8). Displaying something else would indicate that the UA
tried to percent-decode, but used a different encoding.
"""
header_checker.check_filename(
'attachment; filename="foo-%c3%a4-%e2%82%ac.html"',
'foo-%c3%a4-%e2%82%ac.html')
def test_attwithasciifilenamews1(self, header_checker):
"""'attachment', specifying a filename of foo.html.
(With one blank space before the equals character).
UA should offer to download the resource as "foo.html".
"""
header_checker.check_filename('attachment; filename ="foo.html"',
'foo.html')
def test_attwith2filenames(self, header_checker):
"""'attachment', specifying two filename parameters.
This is invalid syntax.
"""
header_checker.check_ignored(
'attachment; filename="foo.html"; filename="bar.html"')
def test_attfnbrokentoken(self, header_checker):
"""'attachment', specifying a filename of foo[1](2).html.
Missing the quotes. Also, "[", "]", "(" and ")" are not allowed in the
HTTP token production.
This is invalid according to Section 19.5.1 of RFC 2616 and RFC 6266,
so UAs should ignore it.
"""
header_checker.check_ignored('attachment; filename=foo[1](2).html')
@_STDLIB_XFAIL
def test_attfnbrokentokeniso(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Missing the quotes.
This is invalid, as the umlaut is not a valid token character, so UAs
should ignore it.
"""
header_checker.check_ignored('attachment; filename=foo-ä.html')
@_STDLIB_XFAIL
def test_attfnbrokentokenutf(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
(which happens to be foo-ä.html using UTF-8 encoding) but missing the
quotes.
This is invalid, as the umlaut is not a valid token character, so UAs
should ignore it.
"""
header_checker.check_ignored('attachment; filename=foo-ä.html')
def test_attmissingdisposition(self, header_checker):
"""Disposition type missing, filename specified.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('filename=foo.html')
def test_attmissingdisposition2(self, header_checker):
"""Disposition type missing, filename specified after extension.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('x=y; filename=foo.html')
def test_attmissingdisposition3(self, header_checker):
"""Disposition type missing, filename "qux".
Can it be more broken? (Probably)
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('"foo; filename=bar;baz"; filename=qux')
def test_attmissingdisposition4(self, header_checker):
"""Disposition type missing.
Two filenames specified separated by a comma (this is syntactically
equivalent to have two instances of the header with one filename
parameter each).
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('filename=foo.html, filename=bar.html')
def test_emptydisposition(self, header_checker):
"""Disposition type missing (but delimiter present).
Filename specified.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('; filename=foo.html')
def test_doublecolon(self, header_checker):
"""Header field value starts with a colon.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored(': inline; attachment; filename=foo.html')
def test_attandinline(self, header_checker):
"""Both disposition types specified.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('inline; attachment; filename=foo.html')
def test_attandinline2(self, header_checker):
"""Both disposition types specified.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment; inline; filename=foo.html')
def test_attbrokenquotedfn(self, header_checker):
"""'attachment', specifying a filename parameter that is broken.
(quoted-string followed by more characters). This is invalid syntax.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment; filename="foo.html".txt')
def test_attbrokenquotedfn2(self, header_checker):
"""'attachment', specifying a filename parameter that is broken.
(missing ending double quote). This is invalid syntax.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment; filename="bar')
def test_attbrokenquotedfn3(self, header_checker):
"""'attachment', specifying a filename parameter that is broken.
(disallowed characters in token syntax). This is invalid syntax.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment; filename=foo"bar;baz"qux')
def test_attmultinstances(self, header_checker):
"""'attachment', two comma-separated instances of the header field.
As Content-Disposition doesn't use a list-style syntax, this is invalid
syntax and, according to RFC 2616, Section 4.2, roughly equivalent to
having two separate header field instances.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored(
'attachment; filename=foo.html, attachment; filename=bar.html')
def test_attmissingdelim(self, header_checker):
"""Uses two parameters, but the mandatory delimiter ";" is missing.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment; foo=foo filename=bar')
def test_attmissingdelim2(self, header_checker):
"""Uses two parameters, but the mandatory delimiter ";" is missing.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment; filename=bar foo=foo')
def test_attmissingdelim3(self, header_checker):
"""';' missing between disposition type and filename parameter.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('attachment filename=bar')
def test_attreversed(self, header_checker):
"""filename parameter and disposition type reversed.
This is invalid, so UAs should ignore it.
"""
header_checker.check_ignored('filename=foo.html; attachment')
def test_attconfusedparam(self, header_checker):
"""'attachment', specifying an "xfilename" parameter.
Should be treated as unnamed attachment.
"""
header_checker.check_unnamed('attachment; xfilename=foo.html')
def test_attabspath(self, header_checker):
"""'attachment', specifying an absolute filename in the fs root.
Either ignore the filename altogether, or discard the path information.
"""
header_checker.check_filename('attachment; filename="/foo.html"',
'foo.html')
@pytest.mark.posix
def test_attabspathwin_unix(self, header_checker):
"""'attachment', specifying an absolute filename in the fs root.
Either ignore the filename altogether, or discard the path information.
Note that test results under Operating Systems other than Windows vary
(see
https://lists.w3.org/Archives/Public/ietf-http-wg/2011JanMar/0112.html);
apparently some UAs consider the backslash a legitimate filename
character.
"""
header_checker.check_filename(r'attachment; filename="\\foo.html"',
r'\foo.html')
@pytest.mark.windows
def test_attabspathwin_win(self, header_checker):
"""'attachment', specifying an absolute filename in the fs root.
Either ignore the filename altogether, or discard the path information.
Note that test results under Operating Systems other than Windows vary
(see
https://lists.w3.org/Archives/Public/ietf-http-wg/2011JanMar/0112.html);
apparently some UAs consider the backslash a legitimate filename
character.
"""
header_checker.check_filename(r'attachment; filename="\\foo.html"',
'foo.html')
# Note we do not check the "Additional parameters" section.
class TestDispositionTypeExtension:
"""Tests checking behavior for disposition type extensions.
They should be treated as "attachment", see Section 4.2 of RFC 6266.
"""
def test_dispext(self, header_checker):
"""'foobar' only
This should be equivalent to using "attachment".
"""
header_checker.check_unnamed('foobar')
def test_dispextbadfn(self, header_checker):
"""'attachment', with no filename parameter"""
header_checker.check_unnamed(
'attachment; example="filename=example.txt"')
class TestCharacterSet:
"""Various tests using the parameter value encoding defined in RFC 5987."""
def test_attwithisofn2231iso(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231/5987 encoded ISO-8859-1.
UA should offer to download the resource as "foo-ä.html".
"""
header_checker.check_filename(
"attachment; filename*=iso-8859-1''foo-%E4.html",
'foo-ä.html')
def test_attwithfn2231utf8(self, header_checker):
"""'attachment', specifying a filename of foo-ä-€.html.
Using RFC2231/5987 encoded UTF-8.
UA should offer to download the resource as "foo-ä-€.html".
"""
header_checker.check_filename(
"attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html",
'foo-ä-€.html')
def test_attwithfn2231noc(self, header_checker):
"""Behavior is undefined in RFC 2231.
The charset part is missing, although UTF-8 was used.
"""
header_checker.check_ignored(
"attachment; filename*=''foo-%c3%a4-%e2%82%ac.html")
def test_attwithfn2231utf8comp(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded UTF-8, but choosing the decomposed form
(lowercase a plus COMBINING DIAERESIS) -- on a Windows target system,
this should be translated to the preferred Unicode normal form
(composed).
UA should offer to download the resource as "foo-ä.html".
"""
header_checker.check_filename(
"attachment; filename*=UTF-8''foo-a%cc%88.html",
'foo-ä.html')
def test_attwithfn2231utf8_bad(self, header_checker):
"""'attachment', specifying a filename of foo-ä-€.html.
Using RFC2231 encoded UTF-8, but declaring ISO-8859-1.
The octet %82 does not represent a valid ISO-8859-1 code point, so the
UA should really ignore the parameter.
"""
header_checker.check_ignored("attachment; "
"iso-8859-1''foo-%c3%a4-%e2%82%ac.html")
def test_attwithfn2231iso_bad(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded ISO-8859-1, but declaring UTF-8.
The octet %E4 does not represent a valid UTF-8 octet sequence, so the
UA should really ignore the parameter.
"""
header_checker.check_ignored(
"attachment; filename*=utf-8''foo-%E4.html")
@_STDLIB_XFAIL
def test_attwithfn2231ws1(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded UTF-8, with whitespace before "*="
The parameter is invalid, thus should be ignored.
"""
header_checker.check_ignored(
"attachment; filename *=UTF-8''foo-%c3%a4.html")
def test_attwithfn2231ws2(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded UTF-8, with whitespace after "*=".
UA should offer to download the resource as "foo-ä.html".
"""
header_checker.check_filename(
"attachment; filename*= UTF-8''foo-%c3%a4.html",
'foo-ä.html')
@_STDLIB_XFAIL
def test_attwithfn2231ws3(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded UTF-8, with whitespace inside "* ="
UA should offer to download the resource as "foo-ä.html".
"""
header_checker.check_filename(
"attachment; filename* =UTF-8''foo-%c3%a4.html",
'foo-ä.html')
def test_attwithfn2231quot(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded UTF-8, with double quotes around the parameter
value.
The parameter is invalid, thus should be ignored.
"""
header_checker.check_ignored(
"attachment; filename*=\"UTF-8''foo-%c3%a4.html\"")
def test_attwithfn2231quot2(self, header_checker):
"""'attachment', specifying a filename of foo bar.html.
Using "filename*", but missing character encoding and language (this
replicates a bug in MS Exchange 2010, see Mozilla Bug 704989).
The parameter is invalid, thus should be ignored.
"""
header_checker.check_ignored('attachment; filename*="foo%20bar.html"')
def test_attwithfn2231singleqmissing(self, header_checker):
"""'attachment', specifying a filename of foo-ä.html.
Using RFC2231 encoded UTF-8, but a single quote is missing.
The parameter is invalid, thus should be ignored.
"""
header_checker.check_ignored(
"attachment; filename*=UTF-8'foo-%c3%a4.html")
@_STDLIB_XFAIL
def test_attwithfn2231nbadpct1(self, header_checker):
"""'attachment', specifying a filename of foo%.
Using RFC2231 encoded UTF-8, with a single "%" at the end.
The parameter is invalid, thus should be ignored.
"""
header_checker.check_ignored("attachment; filename*=UTF-8''foo%")
@_STDLIB_XFAIL
def test_attwithfn2231nbadpct2(self, header_checker):
"""'attachment', specifying a filename of f%oo.html.
Using RFC2231 encoded UTF-8, with a "%" not starting a percent-escape.
The parameter is invalid, thus should be ignored.
"""
header_checker.check_ignored("attachment; filename*=UTF-8''f%oo.html")
def test_attwithfn2231dpct(self, header_checker):
"""'attachment', specifying a filename of A-%41.html.
Using RFC2231 encoded UTF-8.
"""
header_checker.check_filename(
"attachment; filename*=UTF-8''A-%2541.html",
'A-%41.html')
@pytest.mark.posix
def test_attwithfn2231abspathdisguised_unix(self, header_checker):
r"""'attachment', specifying a filename of \foo.html.
Using RFC2231 encoded UTF-8.
"""
header_checker.check_filename(
"attachment; filename*=UTF-8''%5cfoo.html",
r'\foo.html')
@pytest.mark.windows
def test_attwithfn2231abspathdisguised_win(self, header_checker):
r"""'attachment', specifying a filename of \foo.html.
Using RFC2231 encoded UTF-8.
"""
header_checker.check_filename(
"attachment; filename*=UTF-8''%5cfoo.html",
r'foo.html')
# Note we do not test the "RFC2231 Encoding: Continuations (optional)" section
class TestEncodingFallback:
"""Test the same parameter both in traditional and extended format.
This tests how the UA behaves when the same parameter name appears
both in traditional and RFC 2231/5987 extended format.
"""
# See https://bugs.python.org/issue42947
@_STDLIB_XFAIL # FIXME this one could actually be a problem in the wild
def test_attfnboth(self, header_checker):
"""'attachment', specifying a filename in both formats.
foo-ae.html in the traditional format, and foo-ä.html in RFC2231
format.
Section 4.2 of RFC 5987 and Section 4.3 of RFC 6266 suggest that the
RFC 2231/5987 encoded parameter ("filename*") should take precedence
when understood.
"""
header_checker.check_filename("attachment; filename=\"foo-ae.html\"; "
"filename*=UTF-8''foo-%c3%a4.html",
'foo-ä.html')
def test_attfnboth2(self, header_checker):
"""'attachment', specifying a filename in both formats.
foo-ae.html in the traditional format, and foo-ä.html in RFC2231
format.
Section 4.2 of RFC 5987 and Section 4.3 of RFC 6266 suggest that the
RFC 2231/5987 encoded parameter ("filename*") should take precedence
when understood.
"""
header_checker.check_filename(
"attachment; filename*=UTF-8''foo-%c3%a4.html; "
"filename=\"foo-ae.html\"", 'foo-ä.html')
def test_attfnboth3(self, header_checker):
"""'attachment', specifying an ambiguous filename.
currency-sign=¤ in the simple RFC2231/5987 format, and euro-sign=€ in
RFC2231-with-continuations format.
A UA that supports could pick either, or ignore both because of the
ambiguity.
"""
header_checker.check_ignored(
"attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; "
"filename*=ISO-8859-1''currency-sign%3d%a4")
def test_attnewandfn(self, header_checker):
"""'attachment', specifying a new parameter "foobar".
Plus a filename of foo.html in the traditional format.
"foobar" should be ignored, thus "foo.html" be used as filename (this
tests whether the UA properly skips unknown parameters).
"""
header_checker.check_filename(
'attachment; foobar=x; filename="foo.html"',
'foo.html')
class TestRFC2047Encoding:
"""Test RFC 2047 style encoding.
Note that according to Section 5 of RFC 2047, this encoding does not apply
here: An 'encoded-word' MUST NOT appear within a 'quoted-string'., and An
'encoded-word' MUST NOT be used in parameter of a MIME Content-Type or
Content-Disposition field, or in any structured field body except within a
'comment' or 'phrase'.
Therefore, these tests are only be present in order to check whether the UA
by mistake tries to implement RFC 2047.
Note that for some bizarre reason, some Web sites, such as GMail, use this
encoding despite historically it was only implemented in Mozilla browsers,
which do support the RFC2231 encoding as well.
"""
def test_attrfc2047token(self, header_checker):
"""Uses RFC 2047 style encoded word.
"=" is invalid inside the token production, so this is invalid.
"""
header_checker.check_ignored(
'attachment; filename==?ISO-8859-1?Q?foo-=E4.html?=')
@_STDLIB_XFAIL
def test_attrfc2047quoted(self, header_checker):
"""Uses RFC 2047 style encoded word.
Using the quoted-string production.
"""
header_checker.check_filename(
'attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?="',
'=?ISO-8859-1?Q?foo-=E4.html?=')
|