diff --git a/scripts/hopper-demo-x11.py b/scripts/hopper-demo-x11.py index 3940cfe2ab..fdb91ec15b 100644 --- a/scripts/hopper-demo-x11.py +++ b/scripts/hopper-demo-x11.py @@ -479,13 +479,13 @@ def authorize_display( authorized = subprocess.run( [ xauth_path, + "-q", "-f", str(authority), - "add", - display_name, - ".", - secrets.token_hex(16), + "source", + "-", ], + input=("add %s . %s\n" % (display_name, secrets.token_hex(16))).encode("ascii"), check=False, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, diff --git a/tests/boundary/providers/hopper/linuxPrivateDisplay.test.ts b/tests/boundary/providers/hopper/linuxPrivateDisplay.test.ts index 777b30f5e0..6d27314988 100644 --- a/tests/boundary/providers/hopper/linuxPrivateDisplay.test.ts +++ b/tests/boundary/providers/hopper/linuxPrivateDisplay.test.ts @@ -340,3 +340,20 @@ describe("private display probe output settlement", () => { }, ); }); + +it("passes the private X authority cookie through xauth stdin", () => { + const fixture = fileURLToPath( + new URL("../../../fixtures/hopperXauthority.py", import.meta.url), + ); + const process = spawnSync("python3", ["-I", "-S", fixture, helperPath], { + encoding: "utf8", + timeout: 5_000, + }); + expect(process.status, process.stderr).toBe(0); + expect(JSON.parse(process.stdout)).toMatchObject({ + arguments: ["fixture-xauth", "-q", "-f", expect.any(String), "source", "-"], + stdin: `add :123 . ${"0123456789abcdef".repeat(2)}\n`, + cookie_in_argv: false, + mode: 0o600, + }); +}); diff --git a/tests/fixtures/hopperXauthority.py b/tests/fixtures/hopperXauthority.py new file mode 100644 index 0000000000..b70e9e309c --- /dev/null +++ b/tests/fixtures/hopperXauthority.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +"""Observe the production xauth invocation without launching an X server.""" +import importlib.util +import json +from pathlib import Path +import sys +import tempfile +import types + +spec = importlib.util.spec_from_file_location("hopper_demo_x11", sys.argv[1]) +assert spec is not None and spec.loader is not None +helper = importlib.util.module_from_spec(spec) +spec.loader.exec_module(helper) + +cookie = "0123456789abcdef" * 2 +calls = [] +def run(arguments, **options): + calls.append((arguments, options)) + return types.SimpleNamespace(returncode=0) +helper.subprocess.run = run +helper.secrets.token_hex = lambda _bytes: cookie +with tempfile.TemporaryDirectory(prefix="rea-xauth-fixture-") as root: + authority = Path(root) / "authority" + authority.touch() + helper.authorize_display(authority, ":123", "fixture-xauth") + arguments, options = calls[0] + print(json.dumps({ + "arguments": arguments, + "stdin": options["input"].decode("ascii"), + "cookie_in_argv": any(cookie in value for value in arguments), + "mode": authority.stat().st_mode & 0o777, + }))