The OS lanes are marker-driven: list_os_marked_tests.py picks the files
a lane imports from their platforms() specs and the lane selects with
-m platforms. A test gated with skipif(sys.platform != "win32") is
therefore never imported on the Windows lane and skipped everywhere else
— it runs on no host. skipif(sys.platform == "win32") tests were merely
invisible to the lane bookkeeping, but the rule the tree now follows is
one host marker, never a bare skipif.
Mechanical mapping, semantics preserved: skip-on-Windows → "posix",
skip-off-Windows → "windows", skip-off-Linux → "linux", skip-on-macOS →
"not macos". The former skip reasons stay as trailing comments. A
non-host condition (os.geteuid() == 0) stays a separate skipif beside
the marker, spelled getattr(os, "geteuid", ...) so the decorator still
imports on Windows.
Where the conversion would stack two platforms() marks on one test (the
conftest rejects that at collection) the narrower mark wins:
- test_update_wedged_gateway: the class is already platforms("linux");
its per-test "needs UNIX sockets" marks were redundant and are gone.
- test_process_registry.TestSystemdCgroupIsolation: the class-level
skip-on-Windows moves onto the 11 methods that had no host mark; the
11 platforms("linux") methods keep theirs.
- test_file_ops_single_roundtrip: the two fifo tests drop their
platforms("linux") in favour of the module's "posix" (mkfifo exists on
macOS; both tests already skip when it does not).
- test_linux_desktop_entry / test_gateway_job_teardown_live: duplicate
or wider marks removed.
149 lines
5.8 KiB
Python
149 lines
5.8 KiB
Python
import argparse
|
|
import os
|
|
|
|
import pytest
|
|
from hermes_constants import set_hermes_home_override, reset_hermes_home_override
|
|
|
|
from hermes_cli.main import cmd_dashboard
|
|
from hermes_cli.main_dashboard import _read_ssh_session_token_file
|
|
from hermes_cli.subcommands.dashboard import build_dashboard_parser
|
|
|
|
|
|
def dashboard_parser():
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(dest="command")
|
|
build_dashboard_parser(
|
|
subparsers,
|
|
cmd_dashboard=lambda _args: None,
|
|
cmd_dashboard_register=lambda _args: None,
|
|
)
|
|
return parser
|
|
|
|
|
|
def test_serve_help_advertises_secure_ssh_bootstrap_flags(capsys):
|
|
with pytest.raises(SystemExit) as exit_info:
|
|
dashboard_parser().parse_args(["serve", "--help"])
|
|
assert exit_info.value.code == 0
|
|
output = capsys.readouterr().out
|
|
assert "--ssh-session-token-file PATH" in output
|
|
assert "--ssh-owner-nonce NONCE" in output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.platforms("posix") # POSIX fixture uses mode bits; Windows read_token requires protected DACLs
|
|
def test_token_file_is_read_and_unlinked_through_private_directory(tmp_path, monkeypatch):
|
|
home = tmp_path / "home"
|
|
monkeypatch.setenv("HOME", str(home))
|
|
hermes_home = home / ".hermes"
|
|
token_dir = hermes_home / "desktop-ssh" / ("a" * 32)
|
|
token_dir.mkdir(parents=True, mode=0o700)
|
|
token_path = token_dir / "0123456789abcdef.token"
|
|
token_path.write_text("b" * 64)
|
|
token_path.chmod(0o600)
|
|
override = set_hermes_home_override(hermes_home)
|
|
try:
|
|
assert _read_ssh_session_token_file(str(token_path)) == "b" * 64
|
|
assert not token_path.exists()
|
|
finally:
|
|
reset_hermes_home_override(override)
|
|
|
|
|
|
@pytest.mark.platforms("posix") # POSIX desktop-ssh token path
|
|
def test_token_anchor_is_os_home_not_active_profile(tmp_path, monkeypatch):
|
|
"""Regression for #69551: the Desktop client always writes the token under
|
|
``$HOME/.hermes/desktop-ssh`` (a literal ``~/.hermes/desktop-ssh`` in
|
|
apps/desktop/electron/remote-lifecycle.ts, expanded against the account's
|
|
$HOME). A non-default sticky profile re-homes ``get_hermes_home()`` to
|
|
``<root>/profiles/<name>``, and a Docker-style ``HERMES_HOME`` can point
|
|
elsewhere entirely — neither must move the validator off
|
|
``$HOME/.hermes/desktop-ssh``, or the token is wrongly rejected."""
|
|
home = tmp_path / "home"
|
|
monkeypatch.setenv("HOME", str(home))
|
|
token_dir = home / ".hermes" / "desktop-ssh" / ("a" * 32)
|
|
token_dir.mkdir(parents=True, mode=0o700)
|
|
token_path = token_dir / "0123456789abcdef.token"
|
|
|
|
# A sticky profile and a custom (Docker) root both point get_hermes_home()
|
|
# away from $HOME/.hermes; the anchor must ignore both.
|
|
for elsewhere in (home / ".hermes" / "profiles" / "coder", tmp_path / "opt" / "data"):
|
|
token_path.write_text("b" * 64)
|
|
token_path.chmod(0o600)
|
|
override = set_hermes_home_override(elsewhere)
|
|
try:
|
|
assert _read_ssh_session_token_file(str(token_path)) == "b" * 64
|
|
assert not token_path.exists()
|
|
finally:
|
|
reset_hermes_home_override(override)
|
|
|
|
|
|
@pytest.mark.platforms("posix") # POSIX desktop-ssh token path
|
|
def test_token_under_profile_desktop_ssh_is_rejected(tmp_path, monkeypatch):
|
|
"""The client never writes under a profile-scoped desktop-ssh dir, so a token
|
|
placed there must be rejected even while that profile is active — proving the
|
|
anchor is the OS home, not the active profile (#69551)."""
|
|
home = tmp_path / "home"
|
|
monkeypatch.setenv("HOME", str(home))
|
|
profile_home = home / ".hermes" / "profiles" / "coder"
|
|
token_dir = profile_home / "desktop-ssh" / ("a" * 32)
|
|
token_dir.mkdir(parents=True, mode=0o700)
|
|
token_path = token_dir / "0123456789abcdef.token"
|
|
token_path.write_text("b" * 64)
|
|
token_path.chmod(0o600)
|
|
override = set_hermes_home_override(profile_home)
|
|
try:
|
|
with pytest.raises(SystemExit, match="desktop-ssh directory"):
|
|
_read_ssh_session_token_file(str(token_path))
|
|
finally:
|
|
reset_hermes_home_override(override)
|
|
|
|
|
|
@pytest.mark.platforms("posix") # POSIX symlink contract
|
|
def test_token_file_rejects_symlink(tmp_path, monkeypatch):
|
|
home = tmp_path / "home"
|
|
monkeypatch.setenv("HOME", str(home))
|
|
token_dir = home / ".hermes" / "desktop-ssh" / ("a" * 32)
|
|
token_dir.mkdir(parents=True, mode=0o700)
|
|
target = tmp_path / "token"
|
|
target.write_text("b" * 64)
|
|
target.chmod(0o600)
|
|
token_path = token_dir / "0123456789abcdef.token"
|
|
token_path.symlink_to(target)
|
|
override = set_hermes_home_override(home / ".hermes")
|
|
try:
|
|
with pytest.raises(SystemExit, match="symlink|not accessible"):
|
|
_read_ssh_session_token_file(str(token_path))
|
|
assert not token_path.exists()
|
|
assert target.read_text() == "b" * 64
|
|
finally:
|
|
reset_hermes_home_override(override)
|
|
|
|
|
|
@pytest.mark.platforms("linux")
|
|
def test_token_file_rejects_parent_escape(tmp_path, monkeypatch):
|
|
home = tmp_path / "home"
|
|
monkeypatch.setenv("HOME", str(home))
|
|
token_root = home / ".hermes" / "desktop-ssh"
|
|
token_root.mkdir(parents=True, mode=0o700)
|
|
escaped = token_root.parent / "0123456789abcdef.token"
|
|
escaped.write_text("b" * 64)
|
|
escaped.chmod(0o600)
|
|
override = set_hermes_home_override(home / ".hermes")
|
|
try:
|
|
with pytest.raises(SystemExit, match="invalid runtime path"):
|
|
_read_ssh_session_token_file(str(token_root / ".." / escaped.name))
|
|
assert escaped.exists()
|
|
finally:
|
|
reset_hermes_home_override(override)
|
|
|
|
|
|
def test_windows_runtime_root_stays_at_machine_root_for_named_profile(tmp_path, monkeypatch):
|
|
from hermes_cli import windows_ssh_runtime
|
|
|
|
machine_root = tmp_path / "custom-hermes-root"
|
|
monkeypatch.setenv("HERMES_HOME", str(machine_root / "profiles" / "writer_2"))
|
|
|
|
assert windows_ssh_runtime._root() == machine_root / "desktop-ssh"
|