274 lines
7.8 KiB
TypeScript
274 lines
7.8 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
createWindowsNativeAuthorityPackageLoader,
|
|
WindowsNativeAuthorityPackageLoader,
|
|
type WindowsNativeAuthorityLoadHost,
|
|
type WindowsNativeAuthorityPackageCommitment,
|
|
} from "../src/application/WindowsNativeAuthorityLoader.js";
|
|
import {
|
|
unavailableWindowsNativeAuthorityBackend,
|
|
type WindowsNativeAuthorityBackend,
|
|
} from "../src/application/WindowsNativeAuthority.js";
|
|
|
|
const artifact = Buffer.from("source-owned-windows-native-addon");
|
|
const digest = createHash("sha256").update(artifact).digest("hex");
|
|
const packageManifestObject = {
|
|
name: "@rea/windows-x64",
|
|
version: "2.0.0",
|
|
os: ["win32"],
|
|
cpu: ["x64"],
|
|
reaNativeAuthority: {
|
|
contractVersion: 1,
|
|
nodeApiVersion: 9,
|
|
artifact: "rea.node",
|
|
artifactSha256: digest,
|
|
exports: [
|
|
"admitDirectory",
|
|
"admitFile",
|
|
"createPrivateRoot",
|
|
"identity",
|
|
"spawnOwnedProcess",
|
|
],
|
|
},
|
|
} as const;
|
|
const packageManifest = Buffer.from(JSON.stringify(packageManifestObject));
|
|
|
|
const commitment: WindowsNativeAuthorityPackageCommitment = {
|
|
packageName: "@rea/windows-x64",
|
|
packageVersion: "2.0.0",
|
|
contractVersion: 1,
|
|
nodeApiVersion: 9,
|
|
artifactSha256: digest,
|
|
};
|
|
|
|
const backend = (): WindowsNativeAuthorityBackend => ({
|
|
...unavailableWindowsNativeAuthorityBackend("not exercised"),
|
|
identity: {
|
|
packageName: commitment.packageName,
|
|
packageVersion: commitment.packageVersion,
|
|
contractVersion: commitment.contractVersion,
|
|
nodeApiVersion: commitment.nodeApiVersion,
|
|
artifactSha256: digest,
|
|
},
|
|
});
|
|
|
|
class RecordingLoadHost implements WindowsNativeAuthorityLoadHost {
|
|
readonly platform: NodeJS.Platform;
|
|
readonly architecture: string;
|
|
readonly nodeApiVersion: number | null;
|
|
readonly events: string[] = [];
|
|
module: unknown = backend();
|
|
bytes: Uint8Array = artifact;
|
|
manifestBytes: Uint8Array = packageManifest;
|
|
cachedValue: unknown | undefined;
|
|
resolveFailure: unknown | undefined;
|
|
|
|
constructor(
|
|
options: {
|
|
readonly platform?: NodeJS.Platform;
|
|
readonly architecture?: string;
|
|
readonly nodeApiVersion?: number | null;
|
|
} = {},
|
|
) {
|
|
this.platform = options.platform ?? "win32";
|
|
this.architecture = options.architecture ?? "x64";
|
|
this.nodeApiVersion =
|
|
options.nodeApiVersion === undefined ? 10 : options.nodeApiVersion;
|
|
}
|
|
|
|
resolve(packageName: string): string {
|
|
this.events.push(`resolve:${packageName}`);
|
|
if (this.resolveFailure !== undefined) throw this.resolveFailure;
|
|
return "C:\\rea\\node_modules\\@rea\\windows-x64\\rea.node";
|
|
}
|
|
|
|
resolveManifest(packageName: string): string {
|
|
this.events.push(`resolve-manifest:${packageName}`);
|
|
return "C:\\rea\\node_modules\\@rea\\windows-x64\\package.json";
|
|
}
|
|
|
|
async canonicalize(path: string): Promise<string> {
|
|
this.events.push("canonicalize");
|
|
return path.toLowerCase();
|
|
}
|
|
|
|
async read(path: string): Promise<Uint8Array> {
|
|
if (path.endsWith("package.json")) {
|
|
this.events.push("read-manifest");
|
|
return this.manifestBytes;
|
|
}
|
|
this.events.push("read-addon");
|
|
return this.bytes;
|
|
}
|
|
|
|
cached(_path: string): unknown | undefined {
|
|
this.events.push("cached");
|
|
return this.cachedValue;
|
|
}
|
|
|
|
load(_path: string): unknown {
|
|
this.events.push("load");
|
|
return this.module;
|
|
}
|
|
}
|
|
|
|
describe("Windows native authority package loader", () => {
|
|
it("owns one production loader for the process lifetime", () => {
|
|
expect(createWindowsNativeAuthorityPackageLoader()).toBe(
|
|
createWindowsNativeAuthorityPackageLoader(),
|
|
);
|
|
});
|
|
|
|
it("loads and pins one exact release tuple idempotently", async () => {
|
|
const host = new RecordingLoadHost();
|
|
const loader = new WindowsNativeAuthorityPackageLoader(host, commitment);
|
|
|
|
const [first, concurrent] = await Promise.all([
|
|
loader.load(),
|
|
loader.load(),
|
|
]);
|
|
const second = await loader.load();
|
|
|
|
expect(first).toMatchObject({ ok: true });
|
|
expect(concurrent).toEqual(first);
|
|
expect(second).toEqual(first);
|
|
expect(host.events).toEqual([
|
|
"resolve-manifest:@rea/windows-x64",
|
|
"canonicalize",
|
|
"read-manifest",
|
|
"resolve:@rea/windows-x64",
|
|
"canonicalize",
|
|
"canonicalize",
|
|
"cached",
|
|
"read-addon",
|
|
"load",
|
|
]);
|
|
});
|
|
|
|
it.each([
|
|
["wrong platform", { platform: "linux" as const }, commitment],
|
|
["wrong architecture", { architecture: "arm64" }, commitment],
|
|
["old Node-API", { nodeApiVersion: 8 }, commitment],
|
|
["missing Node-API", { nodeApiVersion: null }, commitment],
|
|
["uncommitted artifact", {}, { ...commitment, artifactSha256: null }],
|
|
])(
|
|
"fails before package resolution for %s",
|
|
async (_name, options, expected) => {
|
|
const host = new RecordingLoadHost(options);
|
|
const result = await new WindowsNativeAuthorityPackageLoader(
|
|
host,
|
|
expected,
|
|
).load();
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: { reason: "unavailable" },
|
|
});
|
|
expect(host.events).toEqual([]);
|
|
},
|
|
);
|
|
|
|
it("fails closed when the optional package is absent", async () => {
|
|
const host = new RecordingLoadHost();
|
|
host.resolveFailure = new Error("module details must not escape");
|
|
|
|
const result = await new WindowsNativeAuthorityPackageLoader(
|
|
host,
|
|
commitment,
|
|
).load();
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
reason: "unavailable",
|
|
message:
|
|
"Windows native authority package is not installed or resolvable",
|
|
},
|
|
});
|
|
expect(host.events).toEqual([
|
|
"resolve-manifest:@rea/windows-x64",
|
|
"canonicalize",
|
|
"read-manifest",
|
|
"resolve:@rea/windows-x64",
|
|
]);
|
|
});
|
|
|
|
it("rejects platform manifest drift before addon resolution", async () => {
|
|
const host = new RecordingLoadHost();
|
|
host.manifestBytes = Buffer.from(
|
|
JSON.stringify({
|
|
...packageManifestObject,
|
|
version: "1.0.0",
|
|
}),
|
|
);
|
|
|
|
const result = await new WindowsNativeAuthorityPackageLoader(
|
|
host,
|
|
commitment,
|
|
).load();
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: { reason: "native_contract_mismatch" },
|
|
});
|
|
expect(host.events).not.toContain("resolve:@rea/windows-x64");
|
|
expect(host.events).not.toContain("load");
|
|
});
|
|
|
|
it("rejects cache contamination and digest drift before evaluation", async () => {
|
|
const cached = new RecordingLoadHost();
|
|
cached.cachedValue = backend();
|
|
const cachedResult = await new WindowsNativeAuthorityPackageLoader(
|
|
cached,
|
|
commitment,
|
|
).load();
|
|
expect(cachedResult).toMatchObject({
|
|
ok: false,
|
|
error: { reason: "native_contract_mismatch" },
|
|
});
|
|
expect(cached.events).not.toContain("read-addon");
|
|
expect(cached.events).not.toContain("load");
|
|
|
|
const drifted = new RecordingLoadHost();
|
|
drifted.bytes = Buffer.from("different-addon");
|
|
const driftedResult = await new WindowsNativeAuthorityPackageLoader(
|
|
drifted,
|
|
commitment,
|
|
).load();
|
|
expect(driftedResult).toMatchObject({
|
|
ok: false,
|
|
error: { reason: "identity_drift" },
|
|
});
|
|
expect(drifted.events).not.toContain("load");
|
|
});
|
|
|
|
it.each([
|
|
["non-object", null],
|
|
["extra export", { ...backend(), unexpected: true }],
|
|
["missing operation", { ...backend(), admitFile: undefined }],
|
|
[
|
|
"wrong package version",
|
|
{
|
|
...backend(),
|
|
identity: { ...backend().identity, packageVersion: "1.0.0" },
|
|
},
|
|
],
|
|
])("rejects a %s native contract", async (_name, module) => {
|
|
const host = new RecordingLoadHost();
|
|
host.module = module;
|
|
|
|
const result = await new WindowsNativeAuthorityPackageLoader(
|
|
host,
|
|
commitment,
|
|
).load();
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: { reason: "native_contract_mismatch" },
|
|
});
|
|
});
|
|
});
|