fix(desktop): pass --disable-setuid-sandbox on the userns launch path
When chrome-sandbox is present but not root-owned 4755, Chromium can still abort via setuid_sandbox_host even though the namespace sandbox works. After the userns probe skips sudo, append --disable-setuid-sandbox so .desktop/no-TTY launches keep the namespace sandbox without a privilege prompt. Does not add --no-sandbox. Fixes #51327
This commit is contained in:
@@ -8260,6 +8260,29 @@ def _desktop_linux_sandbox_fixup(packaged_executable: Path) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def _desktop_linux_needs_disable_setuid_sandbox(packaged_executable: Path) -> bool:
|
||||
"""Return True when Chromium should skip the present-but-non-setuid helper.
|
||||
|
||||
A user-owned ``chrome-sandbox`` still makes Chromium abort with
|
||||
``setuid_sandbox_host`` even when the namespace sandbox works. Passing
|
||||
``--disable-setuid-sandbox`` keeps the userns sandbox and avoids sudo.
|
||||
Call only after ``_desktop_linux_sandbox_fixup`` succeeded without making
|
||||
the helper root-owned 4755 (the userns path). Does not re-probe userns.
|
||||
"""
|
||||
if sys.platform != "linux":
|
||||
return False
|
||||
sandbox = packaged_executable.parent / "chrome-sandbox"
|
||||
try:
|
||||
sandbox_lstat = sandbox.lstat()
|
||||
except OSError:
|
||||
return False
|
||||
if not stat.S_ISREG(sandbox_lstat.st_mode):
|
||||
return False
|
||||
if sandbox_lstat.st_uid == 0 and stat.S_IMODE(sandbox_lstat.st_mode) == 0o4755:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
_LINUX_PASSWORD_STORES = frozenset({"gnome-libsecret", "kwallet", "kwallet5", "kwallet6", "basic"})
|
||||
|
||||
|
||||
@@ -8654,6 +8677,8 @@ def cmd_gui(args: argparse.Namespace):
|
||||
launch_command.append("--no-sandbox")
|
||||
else:
|
||||
sys.exit(1)
|
||||
elif _desktop_linux_needs_disable_setuid_sandbox(packaged_executable):
|
||||
launch_command.append("--disable-setuid-sandbox")
|
||||
|
||||
launch_command.extend(config_electron_flags)
|
||||
print(f"→ Launching packaged Hermes Desktop: {' '.join(launch_command)}")
|
||||
|
||||
@@ -130,7 +130,11 @@ def test_gui_installs_packages_and_launches_desktop_app(tmp_path, monkeypatch):
|
||||
assert install_env is not None and "PATH" in install_env
|
||||
assert mock_run.call_args_list[0].args[0] == ["/usr/bin/npm", "run", "pack"]
|
||||
assert mock_run.call_args_list[0].kwargs["cwd"] == desktop_dir
|
||||
assert mock_run.call_args_list[1].args[0] == [str(packaged_exe)]
|
||||
launched = mock_run.call_args_list[1].args[0]
|
||||
if sys.platform.startswith("linux"):
|
||||
assert launched == [str(packaged_exe), "--disable-setuid-sandbox"]
|
||||
else:
|
||||
assert launched == [str(packaged_exe)]
|
||||
assert mock_run.call_args_list[1].kwargs["cwd"] == desktop_dir
|
||||
|
||||
|
||||
@@ -968,7 +972,11 @@ def test_gui_launches_even_when_desktop_entry_install_fails(tmp_path, monkeypatc
|
||||
cli_main.cmd_gui(_ns())
|
||||
|
||||
assert exc.value.code == 0
|
||||
assert mock_run.call_args.args[0] == [str(packaged_exe)]
|
||||
launched = mock_run.call_args.args[0]
|
||||
if sys.platform.startswith("linux"):
|
||||
assert launched == [str(packaged_exe), "--disable-setuid-sandbox"]
|
||||
else:
|
||||
assert launched == [str(packaged_exe)]
|
||||
|
||||
|
||||
@pytest.mark.macos_only
|
||||
|
||||
@@ -114,3 +114,50 @@ class TestDesktopLinuxSandboxFixup:
|
||||
) as probe:
|
||||
assert cli_main._desktop_linux_sandbox_fixup(exe) is True
|
||||
probe.assert_not_called()
|
||||
|
||||
|
||||
class TestDesktopLinuxNeedsDisableSetuidSandbox:
|
||||
def _fake_packaged_app(self, tmp_path):
|
||||
unpacked = tmp_path / "linux-unpacked"
|
||||
unpacked.mkdir()
|
||||
exe = unpacked / "Hermes"
|
||||
exe.write_text("", encoding="utf-8")
|
||||
sandbox = unpacked / "chrome-sandbox"
|
||||
sandbox.write_text("", encoding="utf-8")
|
||||
sandbox.chmod(0o755)
|
||||
return exe
|
||||
|
||||
def test_true_for_user_owned_helper_when_userns_works(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(sys, "platform", "linux")
|
||||
exe = self._fake_packaged_app(tmp_path)
|
||||
with patch.object(
|
||||
cli_main, "_desktop_linux_userns_sandbox_available", return_value=True
|
||||
):
|
||||
assert cli_main._desktop_linux_needs_disable_setuid_sandbox(exe) is True
|
||||
|
||||
def test_false_for_root_owned_setuid_helper(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(sys, "platform", "linux")
|
||||
exe = self._fake_packaged_app(tmp_path)
|
||||
real_lstat = (exe.parent / "chrome-sandbox").lstat()
|
||||
|
||||
class _RootSetuidStat:
|
||||
st_mode = stat.S_IFREG | 0o4755
|
||||
st_uid = 0
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(real_lstat, name)
|
||||
|
||||
with patch.object(cli_main.Path, "lstat", return_value=_RootSetuidStat()), \
|
||||
patch.object(
|
||||
cli_main, "_desktop_linux_userns_sandbox_available", return_value=True
|
||||
) as probe:
|
||||
assert cli_main._desktop_linux_needs_disable_setuid_sandbox(exe) is False
|
||||
probe.assert_not_called()
|
||||
|
||||
def test_false_when_helper_missing(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(sys, "platform", "linux")
|
||||
unpacked = tmp_path / "linux-unpacked"
|
||||
unpacked.mkdir()
|
||||
exe = unpacked / "Hermes"
|
||||
exe.write_text("", encoding="utf-8")
|
||||
assert cli_main._desktop_linux_needs_disable_setuid_sandbox(exe) is False
|
||||
|
||||
Reference in New Issue
Block a user