fix(hopper): verify publisher before probing private display (#5346)

Return publisher refusal before unrelated private-display prerequisites can mask it. Retain display validation for admitted launchers, native launcher digest revalidation and owned cleanup.

Add a public CLI fault control using an unusable Python home, preserve the ordinary control and assert display selection for admitted versus refused launchers. Affected tests: 177 passes; 13 changed-file cases passed three additional repeats; full static checks pass.

Real-Hopper owner-exit identity revalidation fails identically on unchanged main and the PR; complete real-provider qualification is not passing. This pre-existing failure remains outside this focused correction.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
This commit is contained in:
gensokyo-dev
2026-10-20 10:41:14 +08:00
committed by GitHub
parent f93a0335a8
commit d72021b0da
3 changed files with 17 additions and 7 deletions
+2 -2
View File
@@ -250,8 +250,6 @@ export class HopperApplicationLauncher implements BridgeLauncher {
let launched = false;
try {
if (signal?.aborted === true) return err(new HopperCancelledError());
const display = await this.#privateDisplay(signal);
if (!display.ok) return err(display.error);
let expectedLinuxSha256: string | undefined;
if (usesLinuxDemo(this.options)) {
const inspected = await (
@@ -272,6 +270,8 @@ export class HopperApplicationLauncher implements BridgeLauncher {
);
expectedLinuxSha256 = inspected.value.sha256;
}
const display = await this.#privateDisplay(signal);
if (!display.ok) return err(display.error);
const nativeHelperVerified =
(this.dependencies.platform ?? process.platform) === "darwin" &&
this.options.launchMode === "native" &&
@@ -5,9 +5,11 @@ import { cliTest } from "../../support/cli/cliFixture.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { analysisCliErrorEnvelopeSchema } from "../../../src/contracts/errorSchemas.js";
cliTest.skipIf(process.platform !== "linux")(
"refuses an unauthenticated Hopper launcher through the public CLI before executing it",
async ({ cli }) => {
cliTest
.skipIf(process.platform !== "linux")
.for(["default-python-home", "missing-python-home"] as const)(
"refuses an unauthenticated Hopper launcher through the public CLI before executing it (%s)",
async (pythonHome, { cli }) => {
const home = await createTestTempDirectory("rea-hopper-publisher-cli-");
const launcher = join(home, "foreign-hopper");
const target = join(home, "sample");
@@ -38,6 +40,10 @@ cliTest.skipIf(process.platform !== "linux")(
HOPPER_LAUNCHER_PATH: launcher,
REA_HOPPER_TEST_MARKER: marker,
REA_LOG_LEVEL: "silent",
// Publisher rejection must not depend on the display probe's Python.
...(pythonHome === "missing-python-home"
? { PYTHONHOME: join(home, "missing-python-home") }
: {}),
// Reject deterministically without pretending to authenticate a build.
NODE_OPTIONS: `--import=${new URL("../../fixtures/hopperRejectionFeed.mjs", import.meta.url).href}`,
},
@@ -327,6 +327,7 @@ it.skipIf(process.platform !== "linux")(
"gives the demo helper a live owner stream through Node's actual stdio transport",
async () => {
const directory = await createTestTempDirectory("rea-hopper-owner-stdio-");
const selectPrivateDisplay = vi.fn(readyDirectDisplay);
const launcher = new HopperApplicationLauncher(
{
...options(process.execPath),
@@ -344,7 +345,7 @@ it.skipIf(process.platform !== "linux")(
signingFingerprint: "1".repeat(40),
source: "signed_package_cache",
}),
selectPrivateDisplay: readyDirectDisplay,
selectPrivateDisplay,
},
);
const launched = await launcher.launch({
@@ -354,6 +355,7 @@ it.skipIf(process.platform !== "linux")(
runId: randomUUID(),
});
if (!launched.ok) throw launched.error;
expect(selectPrivateDisplay).toHaveBeenCalledOnce();
const supervisor = new ProviderProcessSupervisor(launched.value);
try {
expect(await supervisor.waitForOutputClose(5_000)).toBe(true);
@@ -378,10 +380,11 @@ it.skipIf(process.platform !== "linux")(
"unsupported_hopper_build",
"Fixture launcher is outside verified releases",
);
const selectPrivateDisplay = vi.fn(readyDirectDisplay);
const launcher = new HopperApplicationLauncher(
{ ...options("/unused/Hopper"), targetPath },
{
selectPrivateDisplay: readyDirectDisplay,
selectPrivateDisplay,
verifyLinuxLauncher: async () => err(rejected),
},
);
@@ -405,5 +408,6 @@ it.skipIf(process.platform !== "linux")(
});
expect(projected.details).not.toHaveProperty("exit_code");
expect(projected.details).not.toHaveProperty("launcher");
expect(selectPrivateDisplay).not.toHaveBeenCalled();
},
);