summaryrefslogtreecommitdiff
path: root/tests/end2end/test_invocations.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/end2end/test_invocations.py')
-rw-r--r--tests/end2end/test_invocations.py142
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