diff options
author | Ingrid Budau <twigle_ingrid@yahoo.com> | 2021-11-09 09:29:05 +0100 |
---|---|---|
committer | Ingrid Budau <twigle_ingrid@yahoo.com> | 2021-11-09 09:29:05 +0100 |
commit | 5b2926fe1f325074954614801175f06981927be9 (patch) | |
tree | 1bdad4286b1b881d508b7e236a26a391b90a7565 | |
parent | d379bebafd31b08326df62102dec295b1eb66474 (diff) | |
parent | 1f641c8c450d21441d36194bd606b64d362a6f75 (diff) | |
download | qutebrowser-5b2926fe1f325074954614801175f06981927be9.tar.gz qutebrowser-5b2926fe1f325074954614801175f06981927be9.zip |
Merge github.com:twigleingrid/qutebrowser into settings-frontend-new-css
46 files changed, 381 insertions, 197 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e1e31afc5..cf1c019f7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.3.1 +current_version = 2.4.0 commit = True message = Release v{new_version} tag = True diff --git a/.gitignore b/.gitignore index 31c4ca3b7..ccfc12ccb 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ TODO /scripts/testbrowser/cpp/webengine/testbrowser /scripts/testbrowser/cpp/webengine/.qmake.stash /scripts/dev/pylint_checkers/qute_pylint.egg-info +/scripts/dev/pylint_checkers/build /misc/file_version_info.txt /doc/extapi/_build /misc/nsis/include diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index c17f35eec..f86b84622 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -15,10 +15,40 @@ breaking changes (such as renamed commands) can happen in minor releases. // `Fixed` for any bug fixes. // `Security` to invite users to upgrade in case of vulnerabilities. +[[v2.5.0]] +v2.5.0 (unreleased) +------------------- + +Changed +~~~~~~~ + +- Improved message if a spawned process wasn't found and a Flatpak container is + in use. + +[[v2.4.1]] +v2.4.1 (unreleased) +------------------- + +Fixed +~~~~~ + +- Speculative fix for an immediate crash at start with the macOS/Windows + binaries (in certain rare environments). +- Speculative fix for a qutebrowser crash when the notification daemon crashes + while showing the notification. + [[v2.4.0]] -v2.4.0 (unreleased) +v2.4.0 (2021-10-21) ------------------- +Security +~~~~~~~~ + +- **CVE-2021-41146**: Fix arbitrary command execution on Windows via URL handler + argument injection. See the + https://github.com/qutebrowser/qutebrowser/security/advisories/GHSA-vw27-fwjf-5qxm[security advisory] + for details. + Added ~~~~~ diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc index 23894ddc4..552145023 100644 --- a/doc/help/configuring.asciidoc +++ b/doc/help/configuring.asciidoc @@ -412,6 +412,7 @@ Pre-built colorschemes - https://github.com/dracula/qutebrowser-dracula-theme[Dracula] - https://gitlab.com/lovetocode999/selenized-qutebrowser[Selenized] - https://github.com/morhetz/gruvbox[gruvbox]: https://github.com/The-Compiler/dotfiles/blob/master/qutebrowser/gruvbox.py[The-Compiler], https://gitlab.com/shaneyost/dots-popos-september-2020/-/blob/master/qutebrowser/config.py[Shane Yost] +- https://www.opencode.net/wakellor957/qb-breath/-/blob/main/qb-breath.py[Manjaro Breath-like] Avoiding flake8 errors ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 18dfebc5d..60c229078 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -2669,7 +2669,7 @@ Default: +pass:[false]+ === content.proxy Proxy to use. In addition to the listed values, you can use a `socks://...` or `http://...` URL. -Note that with QtWebEngine, it will take a couple of seconds until the change is applied, if this value is changed at runtime. +Note that with QtWebEngine, it will take a couple of seconds until the change is applied, if this value is changed at runtime. Authentication for SOCKS proxies isn't supported due to Chromium limitations. Type: <<types,Proxy>> diff --git a/doc/qutebrowser.1.asciidoc b/doc/qutebrowser.1.asciidoc index 8db231add..bc312f108 100644 --- a/doc/qutebrowser.1.asciidoc +++ b/doc/qutebrowser.1.asciidoc @@ -65,6 +65,9 @@ show it. *--desktop-file-name* 'DESKTOP_FILE_NAME':: Set the base name of the desktop entry for this application. Used to set the app_id under Wayland. See https://doc.qt.io/qt-5/qguiapplication.html#desktopFileName-prop +*--untrusted-args*:: + Mark all following arguments as untrusted, which enforces that they are URLs/search terms (and not flags or commands) + === debug arguments *-l* '{critical,error,warning,info,debug,vdebug}', *--loglevel* '{critical,error,warning,info,debug,vdebug}':: Override the configured console loglevel diff --git a/misc/nsis/install.nsh b/misc/nsis/install.nsh index f29a0a9a8..9f0cdf446 100755 --- a/misc/nsis/install.nsh +++ b/misc/nsis/install.nsh @@ -351,13 +351,12 @@ Section "Register with Windows" SectionWindowsRegister !insertmacro UpdateRegDWORD SHCTX "SOFTWARE\Classes\$2" "EditFlags" 0x00000002 !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\DefaultIcon" "" "$1,0" !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell" "" "open" - !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell\open\command" "" "$\"$1$\" $\"%1$\"" + !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell\open\command" "" "$\"$1$\" --untrusted-args $\"%1$\"" !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell\open\ddeexec" "" "" StrCmp $2 "${PRODUCT_NAME}HTML" 0 +4 StrCpy $2 "${PRODUCT_NAME}URL" StrCpy $3 "${PRODUCT_NAME} URL" Goto WriteRegHandler - !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2" "URL Protocol" "" ${endif} SectionEnd diff --git a/misc/org.qutebrowser.qutebrowser.appdata.xml b/misc/org.qutebrowser.qutebrowser.appdata.xml index 7c382cbb3..9930514d0 100644 --- a/misc/org.qutebrowser.qutebrowser.appdata.xml +++ b/misc/org.qutebrowser.qutebrowser.appdata.xml @@ -44,6 +44,7 @@ </content_rating> <releases> <!-- Add new releases here --> +<release version="2.4.0" date="2021-10-21"/> <release version="2.3.1" date="2021-07-28"/> <release version="2.3.0" date="2021-06-28"/> <release version="2.2.3" date="2021-06-01"/> diff --git a/misc/org.qutebrowser.qutebrowser.desktop b/misc/org.qutebrowser.qutebrowser.desktop index 52144b3c5..d999496ee 100644 --- a/misc/org.qutebrowser.qutebrowser.desktop +++ b/misc/org.qutebrowser.qutebrowser.desktop @@ -45,7 +45,7 @@ Comment[it]= Un browser web vim-like utilizzabile da tastiera basato su PyQt5 Icon=qutebrowser Type=Application Categories=Network;WebBrowser; -Exec=qutebrowser %u +Exec=qutebrowser --untrusted-args %u Terminal=false StartupNotify=true MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute; diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt index 9a783f8b2..21843c4ae 100644 --- a/misc/requirements/requirements-check-manifest.txt +++ b/misc/requirements/requirements-check-manifest.txt @@ -2,8 +2,8 @@ build==0.7.0 check-manifest==0.47 -packaging==21.0 -pep517==0.11.0 +packaging==21.2 +pep517==0.12.0 pyparsing==2.4.7 toml==0.10.2 -tomli==1.2.1 +tomli==1.2.2 diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index 391106422..34234a50b 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -1,26 +1,26 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py bump2version==1.0.1 -certifi==2021.5.30 -cffi==1.14.6 -charset-normalizer==2.0.6 +certifi==2021.10.8 +cffi==1.15.0 +charset-normalizer==2.0.7 cryptography==35.0.0 Deprecated==1.2.13 -github3.py==2.0.0 +github3.py==3.0.0 hunter==3.3.8 -idna==3.2 +idna==3.3 jwcrypto==1.0 manhole==1.8.0 -packaging==21.0 -pycparser==2.20 +packaging==21.2 +pycparser==2.21 Pympler==0.9 pyparsing==2.4.7 -PyQt-builder==1.11.0 +PyQt-builder==1.12.2 python-dateutil==2.8.2 requests==2.26.0 -sip==6.2.0 +sip==6.4.0 six==1.16.0 toml==0.10.2 -uritemplate==3.0.1 +uritemplate==4.1.1 # urllib3==1.26.7 -wrapt==1.12.1 +wrapt==1.13.3 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 7a39fba32..08b75e2bf 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -1,10 +1,10 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py attrs==21.2.0 -flake8==3.9.2 +flake8==4.0.1 flake8-bugbear==21.9.2 flake8-builtins==1.5.3 -flake8-comprehensions==3.6.1 +flake8-comprehensions==3.7.0 flake8-copyright==0.2.2 flake8-debugger==4.0.0 flake8-deprecated==1.3 @@ -13,12 +13,12 @@ flake8-future-import==0.4.6 flake8-mock==0.3 flake8-polyfill==1.0.2 flake8-string-format==0.3.0 -flake8-tidy-imports==4.4.1 +flake8-tidy-imports==4.5.0 flake8-tuple==0.4.1 mccabe==0.6.1 pep8-naming==0.12.1 -pycodestyle==2.7.0 +pycodestyle==2.8.0 pydocstyle==6.1.1 -pyflakes==2.3.1 +pyflakes==2.4.0 six==1.16.0 snowballstemmer==2.1.0 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 4732fed81..4d5f08e49 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -1,13 +1,13 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py chardet==4.0.0 -diff-cover==6.4.1 +diff-cover==6.4.2 importlib-metadata==4.8.1 -importlib-resources==5.2.2 +importlib-resources==5.4.0 inflect==5.3.0 -Jinja2==3.0.1 +Jinja2==3.0.2 jinja2-pluralize==0.3.0 -lxml==4.6.3 +lxml==4.6.4 MarkupSafe==2.0.1 mypy==0.910 mypy-extensions==0.4.3 @@ -15,7 +15,7 @@ pluggy==1.0.0 Pygments==2.10.0 PyQt5-stubs==5.15.2.0 toml==0.10.2 -types-dataclasses==0.1.7 -types-PyYAML==5.4.10 +types-dataclasses==0.6.1 +types-PyYAML==6.0.0 typing-extensions==3.10.0.2 zipp==3.6.0 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index 81b66393b..8d5567e67 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py altgraph==0.17.2 -pyinstaller==4.5.1 +pyinstaller==4.6 pyinstaller-hooks-contrib==2021.3 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 26e07b878..afb692789 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -1,26 +1,26 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py astroid==2.3.3 # rq.filter: < 2.4 -certifi==2021.5.30 -cffi==1.14.6 -charset-normalizer==2.0.6 +certifi==2021.10.8 +cffi==1.15.0 +charset-normalizer==2.0.7 cryptography==35.0.0 Deprecated==1.2.13 future==0.18.2 -github3.py==2.0.0 -idna==3.2 +github3.py==3.0.0 +idna==3.3 isort==4.3.21 jwcrypto==1.0 lazy-object-proxy==1.4.3 mccabe==0.6.1 pefile==2021.9.3 -pycparser==2.20 +pycparser==2.21 pylint==2.4.4 # rq.filter: < 2.5 python-dateutil==2.8.2 ./scripts/dev/pylint_checkers requests==2.26.0 six==1.16.0 typed-ast==1.4.3 ; python_version<"3.8" -uritemplate==3.0.1 +uritemplate==4.1.1 # urllib3==1.26.7 wrapt==1.11.2 diff --git a/misc/requirements/requirements-pyqt-5.15.txt b/misc/requirements/requirements-pyqt-5.15.txt index 8b7a53c44..3a3110c8b 100644 --- a/misc/requirements/requirements-pyqt-5.15.txt +++ b/misc/requirements/requirements-pyqt-5.15.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt5==5.15.4 # rq.filter: < 5.16 +PyQt5==5.15.6 # rq.filter: < 5.16 PyQt5-Qt5==5.15.2 PyQt5-sip==12.9.0 -PyQtWebEngine==5.15.4 # rq.filter: < 5.16 +PyQtWebEngine==5.15.5 # rq.filter: < 5.16 PyQtWebEngine-Qt5==5.15.2 diff --git a/misc/requirements/requirements-pyqt-pyinstaller.txt b/misc/requirements/requirements-pyqt-pyinstaller.txt deleted file mode 100644 index 678a1d7ea..000000000 --- a/misc/requirements/requirements-pyqt-pyinstaller.txt +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically generated by scripts/dev/recompile_requirements.py - -PyQt5==5.15.3 -PyQt5-Qt==5.15.2 -PyQt5-sip==12.9.0 -PyQtWebEngine==5.15.3 -PyQtWebEngine-Qt==5.15.2 diff --git a/misc/requirements/requirements-pyqt-pyinstaller.txt-raw b/misc/requirements/requirements-pyqt-pyinstaller.txt-raw deleted file mode 100644 index 89b5644da..000000000 --- a/misc/requirements/requirements-pyqt-pyinstaller.txt-raw +++ /dev/null @@ -1,2 +0,0 @@ -PyQt5==5.15.3 -PyQtWebEngine==5.15.3 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index 75ef27bf4..3953d27b3 100644 --- a/misc/requirements/requirements-pyqt.txt +++ b/misc/requirements/requirements-pyqt.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt5==5.15.4 +PyQt5==5.15.6 PyQt5-Qt5==5.15.2 PyQt5-sip==12.9.0 -PyQtWebEngine==5.15.4 +PyQtWebEngine==5.15.5 PyQtWebEngine-Qt5==5.15.2 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 77badd53c..8849014be 100644 --- a/misc/requirements/requirements-pyroma.txt +++ b/misc/requirements/requirements-pyroma.txt @@ -1,9 +1,9 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -certifi==2021.5.30 -charset-normalizer==2.0.6 -docutils==0.17.1 -idna==3.2 +certifi==2021.10.8 +charset-normalizer==2.0.7 +docutils==0.18 +idna==3.3 Pygments==2.10.0 pyroma==3.2 requests==2.26.0 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 88d56b677..86553bb4c 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -2,14 +2,14 @@ alabaster==0.7.12 Babel==2.9.1 -certifi==2021.5.30 -charset-normalizer==2.0.6 +certifi==2021.10.8 +charset-normalizer==2.0.7 docutils==0.17.1 -idna==3.2 +idna==3.3 imagesize==1.2.0 -Jinja2==3.0.1 +Jinja2==3.0.2 MarkupSafe==2.0.1 -packaging==21.0 +packaging==21.2 Pygments==2.10.0 pyparsing==2.4.7 pytz==2021.3 diff --git a/misc/requirements/requirements-tests-bleeding.txt b/misc/requirements/requirements-tests-bleeding.txt index 49911c48d..d2a7fcfb6 100644 --- a/misc/requirements/requirements-tests-bleeding.txt +++ b/misc/requirements/requirements-tests-bleeding.txt @@ -9,7 +9,7 @@ git+https://github.com/HypothesisWorks/hypothesis.git#subdirectory=hypothesis-py git+https://github.com/pytest-dev/pytest.git # Problematic: https://github.com/pytest-dev/pytest-bdd/issues/447 # git+https://github.com/pytest-dev/pytest-bdd.git -pytest-bdd +pytest-bdd<5 git+https://github.com/ionelmc/pytest-benchmark.git git+https://github.com/pytest-dev/pytest-instafail.git git+https://github.com/pytest-dev/pytest-mock.git diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index b61eab6a8..a3a48adea 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -2,34 +2,34 @@ attrs==21.2.0 beautifulsoup4==4.10.0 -certifi==2021.5.30 -charset-normalizer==2.0.6 +certifi==2021.10.8 +charset-normalizer==2.0.7 cheroot==8.5.2 -click==8.0.1 -coverage==6.0 +click==8.0.3 +coverage==6.1.1 EasyProcess==0.3 execnet==1.9.0 -filelock==3.3.0 -Flask==2.0.1 +filelock==3.3.2 +Flask==2.0.2 glob2==0.7 hunter==3.3.8 -hypothesis==6.23.1 +hypothesis==6.24.2 icdiff==2.0.4 -idna==3.2 +idna==3.3 iniconfig==1.1.1 itsdangerous==2.0.1 -jaraco.functools==3.3.0 -# Jinja2==3.0.1 +jaraco.functools==3.4.0 +# Jinja2==3.0.2 Mako==1.1.5 manhole==1.8.0 # MarkupSafe==2.0.1 more-itertools==8.10.0 -packaging==21.0 +packaging==21.2 parse==1.19.0 parse-type==0.5.2 pluggy==1.0.0 pprintpp==0.4.0 -py==1.10.0 +py==1.11.0 py-cpuinfo==8.0.0 Pygments==2.10.0 pyparsing==2.4.7 @@ -51,10 +51,10 @@ requests==2.26.0 requests-file==1.5.1 six==1.16.0 sortedcontainers==2.4.0 -soupsieve==2.2.1 +soupsieve==2.3 tldextract==3.1.2 toml==0.10.2 -tomli==1.2.1 +tomli==1.2.2 urllib3==1.26.7 vulture==2.3 -Werkzeug==2.0.1 +Werkzeug==2.0.2 diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index ab580ac4b..5586a86ef 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -4,7 +4,8 @@ coverage Flask hypothesis pytest -pytest-bdd +# https://github.com/pytest-dev/pytest-bdd/issues/447 +pytest-bdd<5 pytest-benchmark pytest-instafail pytest-mock diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 4c1cfbe27..a069ca44b 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -2,16 +2,16 @@ backports.entry-points-selectable==1.1.0 distlib==0.3.3 -filelock==3.3.0 -packaging==21.0 -pip==21.2.4 +filelock==3.3.2 +packaging==21.2 +pip==21.3.1 platformdirs==2.4.0 pluggy==1.0.0 -py==1.10.0 +py==1.11.0 pyparsing==2.4.7 -setuptools==58.2.0 +setuptools==58.5.3 six==1.16.0 toml==0.10.2 tox==3.24.4 -virtualenv==20.8.1 +virtualenv==20.10.0 wheel==0.37.0 diff --git a/misc/requirements/requirements-yamllint.txt b/misc/requirements/requirements-yamllint.txt index 897184c74..12553f2b2 100644 --- a/misc/requirements/requirements-yamllint.txt +++ b/misc/requirements/requirements-yamllint.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py pathspec==0.9.0 -PyYAML==5.4.1 +PyYAML==6.0 yamllint==1.26.3 diff --git a/misc/userscripts/password_fill b/misc/userscripts/password_fill index c46253d41..3ea8fd9f6 100755 --- a/misc/userscripts/password_fill +++ b/misc/userscripts/password_fill @@ -241,7 +241,7 @@ pass_backend() { if $GPG "${GPG_OPTS[@]}" -d "$passfile" \ | grep --max-count=1 -iE "${match_line_pattern}${url}" > /dev/null then - passfile="${passfile#$PREFIX}" + passfile="${passfile#"$PREFIX"}" passfile="${passfile#/}" files+=( "${passfile%.gpg}" ) fi @@ -250,7 +250,7 @@ pass_backend() { if ((match_filename)) ; then # add entries with matching filepath while read -r passfile ; do - passfile="${passfile#$PREFIX}" + passfile="${passfile#"$PREFIX"}" passfile="${passfile#/}" files+=( "${passfile%.gpg}" ) done < <(find -L "$PREFIX" -iname '*.gpg' | grep "$url") @@ -267,7 +267,7 @@ pass_backend() { else if [[ $line =~ $user_pattern ]] ; then # remove the matching prefix "user: " from the beginning of the line - username=${line#${BASH_REMATCH[0]}} + username=${line#"${BASH_REMATCH[0]}"} break fi fi diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py index 29a8e4836..c05215792 100644 --- a/qutebrowser/__init__.py +++ b/qutebrowser/__init__.py @@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2021 Florian Bruhin (The Compiler)" __license__ = "GPL" __maintainer__ = __author__ __email__ = "mail@qutebrowser.org" -__version__ = "2.3.1" +__version__ = "2.4.0" __version_info__ = tuple(int(part) for part in __version__.split('.')) __description__ = "A keyboard-driven, vim-like browser based on PyQt5." diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py index e40b3e736..f8e1a59b1 100644 --- a/qutebrowser/browser/webengine/notification.py +++ b/qutebrowser/browser/webengine/notification.py @@ -715,6 +715,10 @@ class DBusNotificationAdapter(AbstractNotificationAdapter): # https://github.com/KDE/plasma-workspace/blob/v5.21.4/libnotificationmanager/server_p.cpp#L227-L237 # Created too many similar notifications in quick succession "org.freedesktop.Notifications.Error.ExcessNotificationGeneration", + + # From https://crashes.qutebrowser.org/view/b8c9838a - probably when + # notification daemon crashes? + "org.freedesktop.DBus.Error.Spawn.ChildSignaled", } def __init__(self, parent: QObject = None) -> None: diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index a525e2426..e034fe8f5 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -679,14 +679,14 @@ content.headers.user_agent: # Vim-protip: Place your cursor below this comment and run # :r!python scripts/dev/ua_fetch.py - - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, - like Gecko) Chrome/90.0.4430.93 Safari/537.36" - - Chrome 90 Win10 + like Gecko) Chrome/92.0.4515.131 Safari/537.36" + - Chrome 92 Win10 - - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 - (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36" - - Chrome 90 macOS + (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" + - Chrome 92 macOS - - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like - Gecko) Chrome/90.0.4430.93 Safari/537.36" - - Chrome 90 Linux + Gecko) Chrome/92.0.4515.131 Safari/537.36" + - Chrome 92 Linux supports_pattern: true desc: | User agent to send. @@ -1060,7 +1060,8 @@ content.proxy: `http://...` URL. Note that with QtWebEngine, it will take a couple of seconds until the - change is applied, if this value is changed at runtime. + change is applied, if this value is changed at runtime. Authentication for + SOCKS proxies isn't supported due to Chromium limitations. content.proxy_dns_requests: default: true diff --git a/qutebrowser/extensions/loader.py b/qutebrowser/extensions/loader.py index 7ae45023b..c7b619b3e 100644 --- a/qutebrowser/extensions/loader.py +++ b/qutebrowser/extensions/loader.py @@ -21,12 +21,11 @@ import pkgutil import types -import sys import pathlib import importlib import argparse import dataclasses -from typing import Callable, Iterator, List, Optional, Set, Tuple +from typing import Callable, Iterator, List, Optional, Tuple from PyQt5.QtCore import pyqtSlot @@ -95,18 +94,6 @@ def load_components(*, skip_hooks: bool = False) -> None: def walk_components() -> Iterator[ExtensionInfo]: """Yield ExtensionInfo objects for all modules.""" - if hasattr(sys, 'frozen'): - yield from _walk_pyinstaller() - else: - yield from _walk_normal() - - -def _on_walk_error(name: str) -> None: - raise ImportError("Failed to import {}".format(name)) - - -def _walk_normal() -> Iterator[ExtensionInfo]: - """Walk extensions when not using PyInstaller.""" for _finder, name, ispkg in pkgutil.walk_packages( # Only packages have a __path__ attribute, # but we're sure this is one. @@ -123,23 +110,6 @@ def _walk_normal() -> Iterator[ExtensionInfo]: yield ExtensionInfo(name=name) -def _walk_pyinstaller() -> Iterator[ExtensionInfo]: - """Walk extensions when using PyInstaller. - - See https://github.com/pyinstaller/pyinstaller/issues/1905 - - Inspired by: - https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py - """ - toc: Set[str] = set() - for importer in pkgutil.iter_importers('qutebrowser'): - if hasattr(importer, 'toc'): - toc |= importer.toc # type: ignore[union-attr] - for name in toc: - if name.startswith(components.__name__ + '.'): - yield ExtensionInfo(name=name) - - def _get_init_context() -> InitContext: """Get an InitContext object.""" return InitContext(data_dir=pathlib.Path(standarddir.data()), @@ -190,3 +160,7 @@ def _on_config_changed(changed_name: str) -> None: def init() -> None: config.instance.changed.connect(_on_config_changed) + + +def _on_walk_error(name: str) -> None: + raise ImportError("Failed to import {}".format(name)) diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index c4ff0bb85..f27b7acfe 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -111,17 +111,23 @@ def init_faulthandler(fileobj=sys.__stderr__): Args: fobj: An opened file object to write the traceback to. """ - if fileobj is None: + try: + faulthandler.enable(fileobj) + except (RuntimeError, AttributeError): # When run with pythonw.exe, sys.__stderr__ can be None: # https://docs.python.org/3/library/sys.html#sys.__stderr__ - # If we'd enable faulthandler in that case, we just get a weird - # exception, so we don't enable faulthandler if we have no stdout. + # + # With PyInstaller, it can be a NullWriter raising AttributeError on + # fileno: https://github.com/pyinstaller/pyinstaller/issues/4481 # # Later when we have our data dir available we re-enable faulthandler # to write to a file so we can display a crash to the user at the next # start. + # + # Note that we don't have any logging initialized yet at this point, so + # this is a silent error. return - faulthandler.enable(fileobj) + if (hasattr(faulthandler, 'register') and hasattr(signal, 'SIGUSR1') and sys.stderr is not None): # If available, we also want a traceback on SIGUSR1. diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index e5ccd1b8b..c93fad09b 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -27,7 +27,7 @@ from typing import Mapping, Sequence, Dict, Optional from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QProcess, QProcessEnvironment, QByteArray, QUrl, Qt) -from qutebrowser.utils import message, log, utils, usertypes +from qutebrowser.utils import message, log, utils, usertypes, version from qutebrowser.api import cmdutils, apitypes from qutebrowser.completion.models import miscmodels @@ -273,7 +273,9 @@ class GUIProcess(QObject): known_errors = ['No such file or directory', 'Permission denied'] if (': ' in error_string and # pragma: no branch error_string.split(': ', maxsplit=1)[1] in known_errors): - msg += f'\n(Hint: Make sure {self.cmd!r} exists and is executable)' + msg += f'\nHint: Make sure {self.cmd!r} exists and is executable' + if version.is_flatpak(): + msg += ' inside the Flatpak container' message.error(msg) diff --git a/qutebrowser/qutebrowser.py b/qutebrowser/qutebrowser.py index d0819f832..c576c4a06 100644 --- a/qutebrowser/qutebrowser.py +++ b/qutebrowser/qutebrowser.py @@ -87,6 +87,11 @@ def get_argparser(): help="Set the base name of the desktop entry for this " "application. Used to set the app_id under Wayland. See " "https://doc.qt.io/qt-5/qguiapplication.html#desktopFileName-prop") + parser.add_argument('--untrusted-args', + action='store_true', + help="Mark all following arguments as untrusted, which " + "enforces that they are URLs/search terms (and not flags or " + "commands)") parser.add_argument('--json-args', help=argparse.SUPPRESS) parser.add_argument('--temp-basedir-restarted', @@ -207,7 +212,27 @@ def _unpack_json_args(args): return argparse.Namespace(**new_args) +def _validate_untrusted_args(argv): + # NOTE: Do not use f-strings here, as this should run with older Python + # versions (so that a proper error can be displayed) + try: + untrusted_idx = argv.index('--untrusted-args') + except ValueError: + return + + rest = argv[untrusted_idx + 1:] + if len(rest) > 1: + sys.exit( + "Found multiple arguments ({}) after --untrusted-args, " + "aborting.".format(' '.join(rest))) + + for arg in rest: + if arg.startswith(('-', ':')): + sys.exit("Found {} after --untrusted-args, aborting.".format(arg)) + + def main(): + _validate_untrusted_args(sys.argv) parser = get_argparser() argv = sys.argv[1:] args = parser.parse_args(argv) diff --git a/qutebrowser/utils/resources.py b/qutebrowser/utils/resources.py index ff5ec9d9a..f561d6747 100644 --- a/qutebrowser/utils/resources.py +++ b/qutebrowser/utils/resources.py @@ -82,7 +82,7 @@ def _glob( else: # zipfile.Path or importlib_resources compat object # Unfortunately, we can't tell mypy about resource_path being of type # Union[pathlib.Path, zipfile.Path] because we set "python_version = 3.6" in - # .mypy.ini, but the zipfiel stubs (correctly) only declare zipfile.Path with + # .mypy.ini, but the zipfile stubs (correctly) only declare zipfile.Path with # Python 3.8... assert glob_path.is_dir(), glob_path # type: ignore[unreachable] for subpath in glob_path.iterdir(): diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index a56769255..f42515c5c 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -669,11 +669,12 @@ def yaml_load(f: Union[str, IO[str]]) -> Any: r"of from 'collections\.abc' is deprecated.*"): try: data = yaml.load(f, Loader=YamlLoader) - except ValueError as e: - if str(e).startswith('could not convert string to float'): + except ValueError as e: # pragma: no cover + pyyaml_error = 'could not convert string to float' + if str(e).startswith(pyyaml_error): # WORKAROUND for https://github.com/yaml/pyyaml/issues/168 raise yaml.YAMLError(e) - raise # pragma: no cover + raise end = datetime.datetime.now() diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 8cd244fca..3beb6fb83 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -773,8 +773,6 @@ def _backend() -> str: if objects.backend == usertypes.Backend.QtWebKit: return 'new QtWebKit (WebKit {})'.format(qWebKitVersion()) elif objects.backend == usertypes.Backend.QtWebEngine: - webengine = usertypes.Backend.QtWebEngine - assert objects.backend == webengine, objects.backend return str(qtwebengine_versions( avoid_init='avoid-chromium-init' in objects.debug_flags)) raise utils.Unreachable(objects.backend) diff --git a/requirements.txt b/requirements.txt index 01d49032a..0805ad6cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,10 +4,10 @@ adblock==0.5.0 colorama==0.4.4 dataclasses==0.6 ; python_version<"3.7" importlib-metadata==4.8.1 ; python_version<"3.8" -importlib-resources==5.2.2 ; python_version<"3.9" -Jinja2==3.0.1 +importlib-resources==5.4.0 ; python_version<"3.9" +Jinja2==3.0.2 MarkupSafe==2.0.1 Pygments==2.10.0 -PyYAML==5.4.1 +PyYAML==6.0 typing-extensions==3.10.0.2 zipp==3.6.0 diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index a1c6646eb..4961cbdc8 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -227,7 +227,7 @@ def patch_mac_app(): # Replace some duplicate files by symlinks framework_path = os.path.join(app_path, 'Contents', 'MacOS', 'PyQt5', - 'Qt', 'lib', 'QtWebEngineCore.framework') + 'Qt5', 'lib', 'QtWebEngineCore.framework') core_lib = os.path.join(framework_path, 'Versions', '5', 'QtWebEngineCore') os.remove(core_lib) diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index 6c9a6bcc9..ed5473971 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -81,7 +81,7 @@ CHANGELOG_URLS = { 'attrs': 'https://www.attrs.org/en/stable/changelog.html', 'Jinja2': 'https://jinja.palletsprojects.com/en/latest/changes/', 'MarkupSafe': 'https://markupsafe.palletsprojects.com/en/latest/changes/', - 'flake8': 'https://gitlab.com/pycqa/flake8/tree/master/docs/source/release-notes', + 'flake8': 'https://github.com/PyCQA/flake8/tree/main/docs/source/release-notes', 'flake8-docstrings': 'https://pypi.org/project/flake8-docstrings/', 'flake8-debugger': 'https://github.com/JBKahn/flake8-debugger/', 'flake8-builtins': 'https://github.com/gforcada/flake8-builtins/blob/master/CHANGES.rst', @@ -133,7 +133,7 @@ CHANGELOG_URLS = { 'six': 'https://github.com/benjaminp/six/blob/master/CHANGES', 'altgraph': 'https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst', 'urllib3': 'https://github.com/urllib3/urllib3/blob/master/CHANGES.rst', - 'lxml': 'https://lxml.de/index.html#old-versions', + 'lxml': 'https://github.com/lxml/lxml/blob/master/CHANGES.txt', 'jwcrypto': 'https://github.com/latchset/jwcrypto/commits/master', 'wrapt': 'https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst', 'pep517': 'https://github.com/pypa/pep517/blob/master/doc/changelog.rst', @@ -141,10 +141,8 @@ CHANGELOG_URLS = { 'toml': 'https://github.com/uiri/toml/releases', 'tomli': 'https://github.com/hukkin/tomli/blob/master/CHANGELOG.md', 'PyQt5': 'https://www.riverbankcomputing.com/news', - 'PyQt5-Qt': 'https://www.riverbankcomputing.com/news', 'PyQt5-Qt5': 'https://www.riverbankcomputing.com/news', 'PyQtWebEngine': 'https://www.riverbankcomputing.com/news', - 'PyQtWebEngine-Qt': 'https://www.riverbankcomputing.com/news', 'PyQtWebEngine-Qt5': 'https://www.riverbankcomputing.com/news', 'PyQt-builder': 'https://www.riverbankcomputing.com/news', 'PyQt5-sip': 'https://www.riverbankcomputing.com/news', @@ -170,7 +168,7 @@ CHANGELOG_URLS = { 'check-manifest': 'https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst', 'yamllint': 'https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst', 'pathspec': 'https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst', - 'filelock': 'https://github.com/tox-dev/py-filelock/commits/main', + 'filelock': 'https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst', 'github3.py': 'https://github3py.readthedocs.io/en/master/release-notes/index.html', 'manhole': 'https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst', 'pycparser': 'https://github.com/eliben/pycparser/blob/master/CHANGES', diff --git a/tests/unit/extensions/test_loader.py b/tests/unit/extensions/test_loader.py index feb5dd347..e9b8055aa 100644 --- a/tests/unit/extensions/test_loader.py +++ b/tests/unit/extensions/test_loader.py @@ -35,16 +35,10 @@ def test_on_walk_error(): def test_walk_normal(): - names = [info.name for info in loader._walk_normal()] + names = [info.name for info in loader.walk_components()] assert 'qutebrowser.components.scrollcommands' in names -def test_walk_pyinstaller(): - # We can't test whether we get something back without being frozen by - # PyInstaller, but at least we can test that we don't crash. - list(loader._walk_pyinstaller()) - - def test_load_component(monkeypatch): monkeypatch.setattr(objects, 'commands', {}) diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py index be86bf215..faf2006de 100644 --- a/tests/unit/misc/test_guiprocess.py +++ b/tests/unit/misc/test_guiprocess.py @@ -26,7 +26,7 @@ import pytest from PyQt5.QtCore import QProcess, QUrl from qutebrowser.misc import guiprocess -from qutebrowser.utils import usertypes, utils +from qutebrowser.utils import usertypes, utils, version from qutebrowser.api import cmdutils from qutebrowser.qt import sip @@ -394,8 +394,11 @@ def test_running(qtbot, proc, py_proc): proc.outcome.was_successful() -def test_failing_to_start(qtbot, proc, caplog, message_mock): +@pytest.mark.parametrize('is_flatpak', [True, False]) +def test_failing_to_start(qtbot, proc, caplog, message_mock, monkeypatch, is_flatpak): """Test the process failing to start.""" + monkeypatch.setattr(version, 'is_flatpak', lambda: is_flatpak) + with caplog.at_level(logging.ERROR, 'message'): with qtbot.wait_signal(proc.error, timeout=5000): proc.start('this_does_not_exist_either', []) @@ -405,8 +408,11 @@ def test_failing_to_start(qtbot, proc, caplog, message_mock): "Testprocess 'this_does_not_exist_either' failed to start:") if not utils.is_windows: - assert msg.text.endswith( - "(Hint: Make sure 'this_does_not_exist_either' exists and is executable)") + expected_msg = ( + "Hint: Make sure 'this_does_not_exist_either' exists and is executable") + if is_flatpak: + expected_msg += ' inside the Flatpak container' + assert msg.text.endswith(expected_msg) assert not proc.outcome.running assert proc.outcome.status is None diff --git a/tests/unit/test_qutebrowser.py b/tests/unit/test_qutebrowser.py index d9275631d..36b4065a1 100644 --- a/tests/unit/test_qutebrowser.py +++ b/tests/unit/test_qutebrowser.py @@ -22,6 +22,8 @@ (Mainly commandline flag parsing) """ +import re + import pytest from qutebrowser import qutebrowser @@ -75,3 +77,61 @@ class TestJsonArgs: # pylint: disable=no-member assert args.debug assert not args.temp_basedir + + +class TestValidateUntrustedArgs: + + @pytest.mark.parametrize('args', [ + [], + [':nop'], + [':nop', '--untrusted-args'], + [':nop', '--debug', '--untrusted-args'], + [':nop', '--untrusted-args', 'foo'], + ['--debug', '--untrusted-args', 'foo'], + ['foo', '--untrusted-args', 'bar'], + ]) + def test_valid(self, args): + qutebrowser._validate_untrusted_args(args) + + @pytest.mark.parametrize('args, message', [ + ( + ['--untrusted-args', '--debug'], + "Found --debug after --untrusted-args, aborting.", + ), + ( + ['--untrusted-args', ':nop'], + "Found :nop after --untrusted-args, aborting.", + ), + ( + ['--debug', '--untrusted-args', '--debug'], + "Found --debug after --untrusted-args, aborting.", + ), + ( + [':nop', '--untrusted-args', '--debug'], + "Found --debug after --untrusted-args, aborting.", + ), + ( + [':nop', '--untrusted-args', ':nop'], + "Found :nop after --untrusted-args, aborting.", + ), + ( + [ + ':nop', + '--untrusted-args', + ':nop', + '--untrusted-args', + 'https://www.example.org', + ], + ( + "Found multiple arguments (:nop --untrusted-args " + "https://www.example.org) after --untrusted-args, aborting." + ) + ), + ( + ['--untrusted-args', 'okay1', 'okay2'], + "Found multiple arguments (okay1 okay2) after --untrusted-args, aborting.", + ), + ]) + def test_invalid(self, args, message): + with pytest.raises(SystemExit, match=re.escape(message)): + qutebrowser._validate_untrusted_args(args) diff --git a/tests/unit/utils/test_urlmatch.py b/tests/unit/utils/test_urlmatch.py index 35ccc94fe..caf52c76d 100644 --- a/tests/unit/utils/test_urlmatch.py +++ b/tests/unit/utils/test_urlmatch.py @@ -37,24 +37,30 @@ from PyQt5.QtCore import QUrl from qutebrowser.utils import urlmatch +# pylint: disable=line-too-long + @pytest.mark.parametrize('pattern, error', [ ### Chromium: kMissingSchemeSeparator ## TEST(ExtensionURLPatternTest, ParseInvalid) # ("http", "No scheme given"), - ("http:", "Invalid port: Port is empty"), - ("http:/", "Invalid port: Port is empty"), - ("about://", "Pattern without path"), - ("http:/bar", "Invalid port: Port is empty"), + pytest.param("http:", "Invalid port: Port is empty", id='scheme-no-slash'), + pytest.param("http:/", "Invalid port: Port is empty", id='scheme-single-slash'), + pytest.param("about://", "Pattern without path", id='scheme-no-path'), + pytest.param( + "http:/bar", + "Invalid port: Port is empty", + id='scheme-single-slash-path', + ), ### Chromium: kEmptyHost ## TEST(ExtensionURLPatternTest, ParseInvalid) - ("http://", "Pattern without host"), - ("http:///", "Pattern without host"), - ("http://:1234/", "Pattern without host"), - ("http://*./", "Pattern without host"), + pytest.param("http://", "Pattern without host", id='host-double-slash'), + pytest.param("http:///", "Pattern without host", id='host-triple-slash'), + pytest.param("http://:1234/", "Pattern without host", id='host-port'), + pytest.param("http://*./", "Pattern without host", id='host-pattern'), ## TEST(ExtensionURLPatternTest, IPv6Patterns) - ("http://[]:8888/*", "Pattern without host"), + pytest.param("http://[]:8888/*", "Pattern without host", id='host-ipv6'), ### Chromium: kEmptyPath ## TEST(ExtensionURLPatternTest, ParseInvalid) @@ -63,53 +69,132 @@ from qutebrowser.utils import urlmatch ### Chromium: kInvalidHost ## TEST(ExtensionURLPatternTest, ParseInvalid) - ("http://\0www/", "May not contain NUL byte"), + pytest.param("http://\0www/", "May not contain NUL byte", id='host-nul'), ## TEST(ExtensionURLPatternTest, IPv6Patterns) # No closing bracket (`]`). - ("http://[2607:f8b0:4005:805::200e/*", "Invalid IPv6 URL"), + pytest.param( + "http://[2607:f8b0:4005:805::200e/*", + "Invalid IPv6 URL", + id='host-ipv6-no-closing', + ), # Two closing brackets (`]]`). - pytest.param("http://[2607:f8b0:4005:805::200e]]/*", "Invalid IPv6 URL", marks=pytest.mark.xfail(reason="https://bugs.python.org/issue34360")), + pytest.param( + "http://[2607:f8b0:4005:805::200e]]/*", + "Invalid IPv6 URL", + marks=pytest.mark.xfail(reason="https://bugs.python.org/issue34360"), + id='host-ipv6-two-closing', + ), # Two open brackets (`[[`). - ("http://[[2607:f8b0:4005:805::200e]/*", r"""Expected '\]' to match '\[' in hostname; source was "\[2607:f8b0:4005:805::200e"; host = """""), + pytest.param( + "http://[[2607:f8b0:4005:805::200e]/*", + r"""Expected '\]' to match '\[' in hostname; source was "\[2607:f8b0:4005:805::200e"; host = """"", + id='host-ipv6-two-open', + ), # Too few colons in the last chunk. - ("http://[2607:f8b0:4005:805:200e]/*", 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e"; host = ""'), + pytest.param( + "http://[2607:f8b0:4005:805:200e]/*", + 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e"; host = ""', + id='host-ipv6-colons', + ), # Non-hex piece. - ("http://[2607:f8b0:4005:805:200e:12:bogus]/*", 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e:12:bogus"; host = ""'), + pytest.param( + "http://[2607:f8b0:4005:805:200e:12:bogus]/*", + 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e:12:bogus"; host = ""', + id='host-ipv6-non-hex', + ), ### Chromium: kInvalidHostWildcard ## TEST(ExtensionURLPatternTest, ParseInvalid) - ("http://*foo/bar", "Invalid host wildcard"), - ("http://foo.*.bar/baz", "Invalid host wildcard"), - ("http://fo.*.ba:123/baz", "Invalid host wildcard"), - ("http://foo.*/bar", "Invalid host wildcard"), + pytest.param("http://*foo/bar", "Invalid host wildcard", id='host-wildcard-no-dot'), + pytest.param( + "http://foo.*.bar/baz", + "Invalid host wildcard", + id='host-wildcard-middle', + ), + pytest.param( + "http://fo.*.ba:123/baz", + "Invalid host wildcard", + id='host-wildcard-middle-port', + ), + pytest.param("http://foo.*/bar", "Invalid host wildcard", id='host-wildcard-end'), ### Chromium: kInvalidPort ## TEST(ExtensionURLPatternTest, Ports) - ("http://foo:/", "Invalid port: Port is empty"), - ("http://*.foo:/", "Invalid port: Port is empty"), - ("http://foo:com/", "Invalid port: .* 'com'"), - ("http://foo:123456/", "Invalid port: Port out of range 0-65535"), - ("http://foo:80:80/monkey", "Invalid port: .* '80:80'"), - ("chrome://foo:1234/bar", "Ports are unsupported with chrome scheme"), + pytest.param("http://foo:/", "Invalid port: Port is empty", id='port-empty'), + pytest.param( + "http://*.foo:/", + "Invalid port: Port is empty", + id='port-empty-wildcard', + ), + pytest.param("http://foo:com/", "Invalid port: .* 'com'", id='port-alpha'), + pytest.param( + "http://foo:123456/", + "Invalid port: Port out of range 0-65535", + id='port-range', + ), + pytest.param( + "http://foo:80:80/monkey", + "Invalid port: .* '80:80'", + id='port-double', + ), + pytest.param( + "chrome://foo:1234/bar", + "Ports are unsupported with chrome scheme", + id='port-chrome', + ), # No port specified, but port separator. - ("http://[2607:f8b0:4005:805::200e]:/*", "Invalid port: Port is empty"), + pytest.param( + "http://[2607:f8b0:4005:805::200e]:/*", + "Invalid port: Port is empty", + id='port-empty-ipv6', + ), ### Additional tests - ("http://[", "Invalid IPv6 URL"), - ("http://[fc2e::bb88::edac]", 'Invalid IPv6 address; source was "fc2e::bb88::edac"; host = ""'), - ("http://[fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac]", 'Invalid IPv6 address; source was "fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac"; host = ""'), - ("http://[fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac]", 'Invalid IPv6 address; source was "fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac"; host = ""'), - ("http://[127.0.0.1:fc2e::bb88:edac]", r'Invalid IPv6 address; source was "127\.0\.0\.1:fc2e::bb88:edac'), - ("http://[fc2e::bb88", "Invalid IPv6 URL"), - ("http://[fc2e:bb88:edac]", 'Invalid IPv6 address; source was "fc2e:bb88:edac"; host = ""'), - ("http://[fc2e:bb88:edac::z]", 'Invalid IPv6 address; source was "fc2e:bb88:edac::z"; host = ""'), - ("http://[fc2e:bb88:edac::2]:2a2", "Invalid port: .* '2a2'"), - ("://", "Missing scheme"), + pytest.param("http://[", "Invalid IPv6 URL", id='ipv6-single-open'), + pytest.param( + "http://[fc2e::bb88::edac]", + 'Invalid IPv6 address; source was "fc2e::bb88::edac"; host = ""', + id='ipv6-double-double', + ), + pytest.param( + "http://[fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac]", + 'Invalid IPv6 address; source was "fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac"; host = ""', + id='ipv6-long-double', + ), + pytest.param( + "http://[fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac]", + 'Invalid IPv6 address; source was "fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac"; host = ""', + id='ipv6-long', + ), + pytest.param( + "http://[127.0.0.1:fc2e::bb88:edac]", + r'Invalid IPv6 address; source was "127\.0\.0\.1:fc2e::bb88:edac', + id='ipv6-ipv4', + ), + pytest.param("http://[fc2e::bb88", "Invalid IPv6 URL", id='ipv6-trailing'), + pytest.param( + "http://[fc2e:bb88:edac]", + 'Invalid IPv6 address; source was "fc2e:bb88:edac"; host = ""', + id='ipv6-short', + ), + pytest.param( + "http://[fc2e:bb88:edac::z]", + 'Invalid IPv6 address; source was "fc2e:bb88:edac::z"; host = ""', + id='ipv6-z', + ), + pytest.param( + "http://[fc2e:bb88:edac::2]:2a2", + "Invalid port: .* '2a2'", + id='ipv6-port', + ), + pytest.param("://", "Missing scheme", id='scheme-naked'), ]) def test_invalid_patterns(pattern, error): with pytest.raises(urlmatch.ParseError, match=error): urlmatch.UrlPattern(pattern) +# pylint: enable=line-too-long + @pytest.mark.parametrize('host', ['.', ' ', ' .', '. ', '. .', '. . .', ' . ']) def test_whitespace_hosts(host): diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py index 57adc883c..330ef3b96 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -809,8 +809,11 @@ class TestYaml: assert utils.yaml_load("[1, 2]") == [1, 2] def test_load_float_bug(self): - with pytest.raises(yaml.YAMLError): + try: utils.yaml_load("._") + except yaml.YAMLError: + # Either no exception or YAMLError, not ValueError + pass def test_load_file(self, tmp_path): tmpfile = tmp_path / 'foo.yml' @@ -160,7 +160,7 @@ passenv = APPDATA HOME PYINSTALLER_DEBUG deps = -r{toxinidir}/requirements.txt -r{toxinidir}/misc/requirements/requirements-pyinstaller.txt - -r{toxinidir}/misc/requirements/requirements-pyqt-pyinstaller.txt + -r{toxinidir}/misc/requirements/requirements-pyqt.txt commands = {envbindir}/pyinstaller --noconfirm misc/qutebrowser.spec |