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>
78 lines
2.2 KiB
TypeScript
78 lines
2.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { parseLinuxHopperReleases } from "./HopperReleaseFeed.js";
|
|
|
|
const release = {
|
|
version: "99.2.1",
|
|
filename:
|
|
"https://www.hopperapp.com:443/downloader/public/Hopper-99.2.1-Linux-demo.deb",
|
|
file_length: "123",
|
|
file_hash: "a".repeat(40),
|
|
main_exe_hash: "b".repeat(64),
|
|
signature: "AQ==",
|
|
public: "1",
|
|
beta: "0",
|
|
published_at: "preserved vendor field",
|
|
};
|
|
|
|
describe("Linux Hopper release feed boundary", () => {
|
|
it("preserves future release identity and partial platform facts", () => {
|
|
const parsed = parseLinuxHopperReleases({
|
|
"Ubuntu / Mint": release,
|
|
"OS X": { signature: "Sparkle DSA" },
|
|
});
|
|
expect(parsed).toMatchObject({
|
|
ok: true,
|
|
value: {
|
|
deb: { ok: true, value: release },
|
|
rpm: { ok: false },
|
|
arch: { ok: false },
|
|
},
|
|
});
|
|
if (!parsed.ok || !parsed.value.deb.ok) throw new Error("Missing release");
|
|
expect(parsed.value.deb.value.filename).toBe(release.filename);
|
|
});
|
|
|
|
it.each([
|
|
{ signature: undefined },
|
|
{ signature: "AQ==garbage" },
|
|
{ main_exe_hash: "b".repeat(40) },
|
|
{ file_length: "100000001" },
|
|
{ file_length: "NaN" },
|
|
{ public: "0" },
|
|
{ beta: "1" },
|
|
{
|
|
filename:
|
|
"https://other.example/downloader/public/Hopper-99.2.1-Linux-demo.deb",
|
|
},
|
|
{ filename: release.filename + "?token=caller" },
|
|
{
|
|
filename: release.filename.replace("https://", "https://user:password@"),
|
|
},
|
|
{ filename: release.filename.replace(".deb", ".rpm") },
|
|
{ filename: release.filename.replace("demo.deb", "full.deb") },
|
|
])(
|
|
"rejects metadata outside the selected public package contract: %j",
|
|
(changes) => {
|
|
expect(
|
|
parseLinuxHopperReleases({
|
|
"Ubuntu / Mint": { ...release, ...changes },
|
|
}),
|
|
).toMatchObject({
|
|
ok: true,
|
|
value: { deb: { ok: false, error: { reason: "release_metadata" } } },
|
|
});
|
|
},
|
|
);
|
|
|
|
it.each([null, [], "html error page"])(
|
|
"keeps malformed feed roots unknown",
|
|
(raw) => {
|
|
expect(parseLinuxHopperReleases(raw)).toMatchObject({
|
|
ok: false,
|
|
error: { reason: "release_metadata" },
|
|
});
|
|
},
|
|
);
|
|
});
|