Files
opengeos--geolibre/tests/plugin-integrity.test.ts
wehub-resource-sync 7df9ebf22c
Sync labels / sync (push) Failing after 1m9s
Publish Container Image / Build and publish container image (push) Has been cancelled
Deploy Website / Deploy to GitHub Pages (push) Has been cancelled
Deploy Website / Build website and demo (push) Has been cancelled
Deploy viewer / Deploy viewer proxy to Cloudflare Workers (push) Has been cancelled
Deploy tiles / Deploy planetary tile proxy to Cloudflare Workers (push) Has been cancelled
Deploy collab / Deploy collaboration relay to Cloudflare Workers (push) Has been cancelled
CI / Dependency audit (push) Has been cancelled
CI / E2E smoke (Playwright) (push) Has been cancelled
CI / Validate CITATION.cff (push) Has been cancelled
CI / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:00 +08:00

78 lines
2.8 KiB
TypeScript

import assert from "node:assert/strict";
import { afterEach, beforeEach, describe, it } from "node:test";
import {
computePluginBundleHash,
getPluginBundlePin,
removePluginBundlePin,
verifyPluginBundleIntegrity,
} from "../apps/geolibre-desktop/src/lib/plugin-integrity";
// plugin-integrity reads/writes the bare `localStorage` global (=== window's in
// the browser). Emulate just enough for Node's test runner.
function installLocalStorage(): void {
const store = new Map<string, string>();
(globalThis as { localStorage?: unknown }).localStorage = {
getItem: (key: string) => store.get(key) ?? null,
setItem: (key: string, value: string) => void store.set(key, value),
removeItem: (key: string) => void store.delete(key),
};
}
describe("plugin bundle integrity pinning", () => {
beforeEach(() => {
installLocalStorage();
});
afterEach(() => {
delete (globalThis as { localStorage?: unknown }).localStorage;
});
it("hashes deterministically and distinguishes entry/style", async () => {
const a = await computePluginBundleHash({
entrySource: "export const plugin = {}",
styleSource: ".x{}",
});
const same = await computePluginBundleHash({
entrySource: "export const plugin = {}",
styleSource: ".x{}",
});
const differentEntry = await computePluginBundleHash({
entrySource: "export const plugin = { evil: true }",
styleSource: ".x{}",
});
const differentStyle = await computePluginBundleHash({
entrySource: "export const plugin = {}",
styleSource: ".y{}",
});
assert.equal(a, same);
assert.notEqual(a, differentEntry);
assert.notEqual(a, differentStyle);
assert.match(a, /^[0-9a-f]{64}$/);
});
it("pins on first use, passes when unchanged, blocks when changed", async () => {
const url = "https://plugins.example.com/foo/plugin.json";
const bundle = { entrySource: "v1", styleSource: null };
const first = await verifyPluginBundleIntegrity(url, bundle);
assert.equal(first.status, "pinned-first-use");
assert.equal(getPluginBundlePin(url), await computePluginBundleHash(bundle));
const second = await verifyPluginBundleIntegrity(url, bundle);
assert.equal(second.status, "unchanged");
// A silently-changed bundle is blocked and NOT re-pinned.
const tampered = { entrySource: "v2-evil", styleSource: null };
const changed = await verifyPluginBundleIntegrity(url, tampered);
assert.equal(changed.status, "changed");
assert.equal(getPluginBundlePin(url), await computePluginBundleHash(bundle));
// Removing the pin resets to first-use trust.
removePluginBundlePin(url);
assert.equal(getPluginBundlePin(url), null);
const afterRemoval = await verifyPluginBundleIntegrity(url, tampered);
assert.equal(afterRemoval.status, "pinned-first-use");
});
});