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:
lesseradmin
2026-08-29 15:20:02 -05:00
committed by Teknium
parent 3a7f2234a6
commit 779482598f
3 changed files with 82 additions and 2 deletions
+25
View File
@@ -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)}")
+10 -2
View File
@@ -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