Files
rea/scripts/verify-package-platform.mjs
T
70cd4cbfb8 fix(hopper)!: verify current Linux releases with the publisher key
Replace fixed Linux Hopper release pins with current vendor feed metadata,
complete detached-PGP package verification under the stable publisher primary
key, and authenticated package evidence for offline admission. Share parsed
publisher facts across approved setup, doctor and the pre-execution guard.
Preserve existing ownership, cancellation, private-display and cleanup rules.

Fixes #3577

Normalize empty XDG settings, preserve backups during approved cache repair,
and retain publisher cleanup through cancellation and shutdown. Exercise cold
offline verification without reusing process-local proof state. Serialize
approved cache publication across processes, restore old evidence on failure,
and share the existing lease owner from process without changing its identities.

Validation: 438 focused tests (six platform-related skips), full static/documentation checks, real Ubuntu
24.04 official Hopper 6.6.0 setup/CLI/MCP, offline ordinary-user admission,
launcher mutation rejection, and cold/warm verification measurements. Official
RPM metadata mismatch is tracked separately in #3578 and fails closed.

BREAKING CHANGE: Older Linux Hopper builds admitted by the fixed allowlist
now require retained publisher-signed package evidence or an approved update
to the current public demo. Update explicit HOPPER_LAUNCHER_PATH values when
migrating; the live feed exposes only its current release.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 12:15:55 +08:00

139 lines
4.3 KiB
JavaScript

import { json, run, runWithStatus } from "./lib/verify-package-core.mjs";
/** Run platform-specific deep-analysis smoke tests. */
export async function verifyPackagePlatform({
cli,
environment,
platform = process.platform,
runCommand = run,
}) {
if (platform === "linux") {
const unsupportedExecution = await runWithStatus(
cli,
["analyze", process.execPath, "--provider", "hopper", "--json"],
environment,
);
assertLinuxPackageProviderFailure(unsupportedExecution);
} else {
const overview = json(
await runCommand(
cli,
["analyze", process.execPath, "--provider", "hopper", "--json"],
environment,
),
);
if (!hasPositiveProcedureCount(overview))
throw new Error(
`packaged Hopper-backed analyze CLI failed: ${JSON.stringify(overview)}`,
);
const inspected = json(
await runCommand(
cli,
["inspect", process.execPath, "--provider", "hopper", "--json"],
environment,
),
);
if (!hasPositiveProcedureCount(inspected))
throw new Error("packaged inspect CLI failed");
const functionResult = json(
await runCommand(
cli,
[
"function",
process.execPath,
"0x1000",
"--provider",
"hopper",
"--json",
],
environment,
),
);
if (
functionResult.operation !== "analyze_function" ||
functionResult.provider?.id !== "hopper" ||
functionResult.normalized_result?.procedure?.address !== "0x1000"
)
throw new Error(
`packaged function CLI failed: ${JSON.stringify(functionResult)}`,
);
const xrefs = json(
await runCommand(
cli,
["xrefs", process.execPath, "0x1000", "--provider", "hopper", "--json"],
environment,
),
);
if (
xrefs.operation !== "xrefs" ||
JSON.stringify(xrefs.normalized_result) !== JSON.stringify(["0x1000"])
)
throw new Error(`packaged xrefs CLI failed: ${JSON.stringify(xrefs)}`);
const trace = json(
await runCommand(
cli,
[
"trace",
process.execPath,
"fixture",
"--provider",
"hopper",
"--json",
],
environment,
),
);
if (trace.operation !== "trace_feature")
throw new Error(`packaged trace CLI failed: ${JSON.stringify(trace)}`);
}
}
/** One owner for unsupported-launch evidence across package and real-Hopper verification. */
export const isUnsupportedHopperLaunchFailure = (failure) => {
const details = failure?.details;
const unsupportedBuild =
details?.failure_code === "unsupported_hopper_build" &&
details.exit_code === 72;
const publisherRejection =
details?.stage === "launch" &&
details.launch_rejection?.boundary === "publisher_verification" &&
details.launch_rejection.reason === "unsupported_hopper_build" &&
details.exit_code === undefined &&
details.launcher === undefined;
return (
failure?.code === "provider_unavailable" &&
(publisherRejection || unsupportedBuild)
);
};
/** Require an exact pre-execution rejection, observed helper exit, or missing-Xvfb diagnostic. */
export const assertLinuxPackageProviderFailure = (execution) => {
const failure = json(execution.stdout);
const details = failure?.details;
const diagnostic = details?.diagnostics;
const missingDisplayDependency =
details?.failure_code === "runtime_dependency_unavailable" &&
details.exit_code === 79 &&
diagnostic?.component === "hopper_private_display" &&
diagnostic.operation === "probe" &&
diagnostic.reason === "missing_xvfb" &&
diagnostic.status === "error" &&
diagnostic.failure_code === "runtime_dependency_unavailable" &&
diagnostic.strategy === "unavailable";
if (
execution.status !== 1 ||
failure?.code !== "provider_unavailable" ||
(!isUnsupportedHopperLaunchFailure(failure) && !missingDisplayDependency)
)
throw new Error(
`packaged Linux Hopper verification did not fail closed: ${JSON.stringify(failure)}`,
);
};
const hasPositiveProcedureCount = (result) =>
typeof result === "object" &&
result !== null &&
result.operation === "binary_overview" &&
Number.isInteger(result.normalized_result?.procedure_count) &&
result.normalized_result.procedure_count > 0;