diff options
Diffstat (limited to 'tests/end2end/test_invocations.py')
-rw-r--r-- | tests/end2end/test_invocations.py | 142 |
1 files changed, 112 insertions, 30 deletions
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py index 56524a031..a55efb129 100644 --- a/tests/end2end/test_invocations.py +++ b/tests/end2end/test_invocations.py @@ -1,22 +1,11 @@ -# Copyright 2016-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org> # -# This file is part of qutebrowser. -# -# qutebrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# qutebrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later """Test starting qutebrowser with special arguments/environments.""" +import os +import signal import configparser import subprocess import sys @@ -25,6 +14,8 @@ import importlib import re import json import platform +from contextlib import nullcontext as does_not_raise +from unittest.mock import ANY import pytest from qutebrowser.qt.core import QProcess, QPoint @@ -892,27 +883,118 @@ def test_sandboxing( line.expected = True pytest.skip("chrome://sandbox/ not supported") + if len(text.split("\n")) == 1: + # Try again, maybe the JS hasn't run yet? + text = quteproc_new.get_content() + print(text) + bpf_text = "Seccomp-BPF sandbox" yama_text = "Ptrace Protection with Yama LSM" - header, *lines, empty, result = text.split("\n") - assert not empty + if not utils.is_windows: + header, *lines, empty, result = text.split("\n") + assert not empty - expected_status = { - "Layer 1 Sandbox": "Namespace" if has_namespaces else "None", + expected_status = { + "Layer 1 Sandbox": "Namespace" if has_namespaces else "None", - "PID namespaces": "Yes" if has_namespaces else "No", - "Network namespaces": "Yes" if has_namespaces else "No", + "PID namespaces": "Yes" if has_namespaces else "No", + "Network namespaces": "Yes" if has_namespaces else "No", - bpf_text: "Yes" if has_seccomp else "No", - f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No", + bpf_text: "Yes" if has_seccomp else "No", + f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No", - f"{yama_text} (Broker)": "Yes" if has_yama else "No", - f"{yama_text} (Non-broker)": "Yes" if has_yama_non_broker else "No", - } + f"{yama_text} (Broker)": "Yes" if has_yama else "No", + f"{yama_text} (Non-broker)": "Yes" if has_yama_non_broker else "No", + } - assert header == "Sandbox Status" - assert result == expected_result + assert header == "Sandbox Status" + assert result == expected_result + + status = dict(line.split("\t") for line in lines) + assert status == expected_status + + else: # utils.is_windows + # The sandbox page on Windows if different that Linux and macOS. It's + # a lot more complex. There is a table up top with lots of columns and + # a row per tab and helper process then a json object per row down + # below with even more detail (which we ignore). + # https://www.chromium.org/Home/chromium-security/articles/chrome-sandbox-diagnostics-for-windows/ + + # We're not getting full coverage of the table and there doesn't seem + # to be a simple summary like for linux. The "Sandbox" and "Lockdown" + # column are probably the key ones. + # We are looking at all the rows in the table for the sake of + # completeness, but I expect there will always be just one row with a + # renderer process in it for this test. If other helper processes pop + # up we might want to exclude them. + lines = text.split("\n") + assert lines.pop(0) == "Sandbox Status" + header = lines.pop(0).split("\t") + rows = [] + current_line = lines.pop(0) + while current_line.strip(): + if lines[0].startswith("\t"): + # Continuation line. Not sure how to 100% identify them + # but new rows should start with a process ID. + current_line += lines.pop(0) + continue + + columns = current_line.split("\t") + assert len(header) == len(columns) + rows.append(dict(zip(header, columns))) + current_line = lines.pop(0) + + assert rows + + # I'm using has_namespaces as a proxy for "should be sandboxed" here, + # which is a bit lazy but its either that or match on the text + # "sandboxing" arg. The seccomp-bpf arg does nothing on windows, so + # we only have the off and on states. + for row in rows: + assert row == { + "Process": ANY, + "Type": "Renderer", + "Name": "", + "Sandbox": "Renderer" if has_namespaces else "Not Sandboxed", + "Lockdown": "Lockdown" if has_namespaces else "", + "Integrity": ANY if has_namespaces else "", + "Mitigations": ANY if has_namespaces else "", + "Component Filter": ANY if has_namespaces else "", + "Lowbox/AppContainer": "", + } - status = dict(line.split("\t") for line in lines) - assert status == expected_status + +@pytest.mark.not_frozen +def test_logfilter_arg_does_not_crash(request, quteproc_new): + args = ['--temp-basedir', '--debug', '--logfilter', 'commands, init, ipc, webview'] + + with does_not_raise(): + quteproc_new.start(args=args + _base_args(request.config)) + + # Waiting for quit to make sure no other warning is emitted + quteproc_new.send_cmd(':quit') + quteproc_new.wait_for_quit() + + +def test_restart(request, quteproc_new): + args = _base_args(request.config) + ['--temp-basedir'] + quteproc_new.start(args) + quteproc_new.send_cmd(':restart') + + prefix = "New process PID: " + line = quteproc_new.wait_for(message=f"{prefix}*") + quteproc_new.wait_for_quit() + + assert line.message.startswith(prefix) + pid = int(line.message[len(prefix):]) + os.kill(pid, signal.SIGTERM) + + try: + # If the new process hangs, this will hang too. + # Still better than just ignoring it, so we can fix it if something is broken. + os.waitpid(pid, 0) # pid, options... positional-only :( + except (ChildProcessError, PermissionError): + # Already gone. Even if not documented, Windows seems to raise PermissionError + # here... + pass |