mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
refactor(windows): share native artifact validation
This commit is contained in:
1 parent
729684af9a
commit
3d565fa42a
8 files changed
+427
-82
No files matched your search
@@ -6,6 +6,13 @@ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
|
||||
|
||||
const { WINDOWS_NATIVE_ARTIFACT, createWindowsNativeManifest } =
|
||||
await importVerifierSource(
|
||||
new URL("../src/domain/windowsNativeArtifact.ts", import.meta.url).href,
|
||||
import.meta.url,
|
||||
);
|
||||
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const nodeVersion = "22.19.0";
|
||||
@@ -87,7 +94,7 @@ if (!(await exists(join(headers, "node_api.h")))) {
|
||||
run("tar", ["xzf", archive, "-C", cache]);
|
||||
headers = join(cache, `node-v${nodeVersion}`, "include", "node");
|
||||
}
|
||||
const artifact = "rea-windows-x64.node";
|
||||
const artifact = WINDOWS_NATIVE_ARTIFACT.artifact;
|
||||
// MSVC short-import libraries do not produce a correct Node import table with
|
||||
// GNU ld. Generate a GNU delay-import library for the exact stable Node-API
|
||||
// symbols; addon.cc binds it to the host executable, whatever its file name.
|
||||
@@ -116,7 +123,7 @@ run("x86_64-w64-mingw32-dlltool", ["-d", definition, "-y", library]);
|
||||
run(compiler, [
|
||||
"-std=c++17",
|
||||
"-O2",
|
||||
"-DNAPI_VERSION=8",
|
||||
`-DNAPI_VERSION=${WINDOWS_NATIVE_ARTIFACT.nodeApiVersion}`,
|
||||
"-D_WIN32_WINNT=0x0A00",
|
||||
"-shared",
|
||||
"-static",
|
||||
@@ -140,15 +147,9 @@ const packageJson = JSON.parse(
|
||||
await readFile(join(root, "package.json"), "utf8"),
|
||||
);
|
||||
const bytes = await readFile(join(output, artifact));
|
||||
const metadata = {
|
||||
packageVersion: packageJson.version,
|
||||
platform: "win32",
|
||||
architecture: "x64",
|
||||
abiVersion: 1,
|
||||
nodeApiVersion: 8,
|
||||
artifact,
|
||||
artifactSha256: createHash("sha256").update(bytes).digest("hex"),
|
||||
};
|
||||
const created = createWindowsNativeManifest(packageJson.version, bytes);
|
||||
if (!created.ok) throw created.error;
|
||||
const metadata = created.value;
|
||||
await writeFile(
|
||||
join(output, "manifest.json"),
|
||||
`${JSON.stringify(metadata, null, 2)}\n`,
|
||||
|
||||
@@ -125,6 +125,11 @@ export const SCOPES = {
|
||||
"tests/boundary/filesystem/npmRelease.test.ts",
|
||||
"tests/boundary/process/releaseArtifactCanary.test.ts",
|
||||
"src/windows/**",
|
||||
"src/domain/windowsNativeArtifact*.ts",
|
||||
"src/domain/peCoffHeader*.ts",
|
||||
"src/domain/result.ts",
|
||||
"scripts/lib/verifier-source-loader.mjs",
|
||||
"tests/boundary/process/windowsNativeArtifactVerifier.test.ts",
|
||||
"scripts/lib/windows-owned-command.mjs",
|
||||
"scripts/run-exclusive-command.mjs",
|
||||
"tests/process-global/runnerCancellation.test.ts",
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { access, readFile } from "node:fs/promises";
|
||||
|
||||
const root = new URL("../", import.meta.url);
|
||||
const source = new URL("src/domain/windowsNativeArtifact.ts", root);
|
||||
let sourceExists = true;
|
||||
try {
|
||||
await access(source);
|
||||
} catch (cause) {
|
||||
if (cause?.code !== "ENOENT") throw cause;
|
||||
sourceExists = false;
|
||||
}
|
||||
const owner = sourceExists
|
||||
? await (
|
||||
await import("./lib/verifier-source-loader.mjs")
|
||||
).importVerifierSource(source.href, import.meta.url)
|
||||
: await import(new URL("dist/domain/windowsNativeArtifact.js", root).href);
|
||||
const {
|
||||
WINDOWS_NATIVE_ARTIFACT,
|
||||
parseWindowsNativeManifest,
|
||||
validateWindowsNativeArtifact,
|
||||
} = owner;
|
||||
const packageJson = JSON.parse(
|
||||
await readFile(new URL("package.json", root), "utf8"),
|
||||
);
|
||||
const manifest = JSON.parse(
|
||||
await readFile(new URL("native/windows/build/manifest.json", root), "utf8"),
|
||||
const parsed = parseWindowsNativeManifest(
|
||||
JSON.parse(
|
||||
await readFile(new URL("native/windows/build/manifest.json", root), "utf8"),
|
||||
),
|
||||
packageJson.version,
|
||||
);
|
||||
if (!parsed.ok) throw parsed.error;
|
||||
const manifest = parsed.value;
|
||||
const bytes = await readFile(
|
||||
new URL("native/windows/build/rea-windows-x64.node", root),
|
||||
new URL(`native/windows/build/${WINDOWS_NATIVE_ARTIFACT.artifact}`, root),
|
||||
);
|
||||
if (
|
||||
manifest.packageVersion !== packageJson.version ||
|
||||
manifest.platform !== "win32" ||
|
||||
manifest.architecture !== "x64" ||
|
||||
manifest.abiVersion !== 1 ||
|
||||
manifest.nodeApiVersion !== 8 ||
|
||||
manifest.artifact !== "rea-windows-x64.node" ||
|
||||
createHash("sha256").update(bytes).digest("hex") !== manifest.artifactSha256
|
||||
)
|
||||
throw new Error(
|
||||
"Windows native artifact metadata or SHA-256 does not match this package; run the Windows native build lane.",
|
||||
);
|
||||
const offset = bytes.length >= 64 ? bytes.readUInt32LE(0x3c) : bytes.length;
|
||||
if (
|
||||
bytes.toString("latin1", 0, 2) !== "MZ" ||
|
||||
offset > bytes.length - 26 ||
|
||||
bytes.toString("latin1", offset, offset + 4) !== "PE\0\0" ||
|
||||
bytes.readUInt16LE(offset + 4) !== 0x8664 ||
|
||||
bytes.readUInt16LE(offset + 24) !== 0x20b
|
||||
)
|
||||
throw new Error("Windows native artifact must be a Windows x64 PE32+ image.");
|
||||
const valid = validateWindowsNativeArtifact(manifest, bytes);
|
||||
if (!valid.ok) throw valid.error;
|
||||
process.stdout.write(
|
||||
`${JSON.stringify({ ok: true, ...manifest, artifactBytes: bytes.length })}\n`,
|
||||
);
|
||||
@@ -0,0 +1,118 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { expect, it } from "vitest";
|
||||
import {
|
||||
createWindowsNativeManifest,
|
||||
parseWindowsNativeManifest,
|
||||
validateWindowsNativeArtifact,
|
||||
} from "./windowsNativeArtifact.js";
|
||||
|
||||
const fixture = () => {
|
||||
const bytes = Buffer.alloc(128);
|
||||
bytes.write("MZ");
|
||||
bytes.writeUInt32LE(64, 0x3c);
|
||||
bytes.write("PE\0\0", 64, "ascii");
|
||||
bytes.writeUInt16LE(0x8664, 68);
|
||||
bytes.writeUInt16LE(0x20b, 88);
|
||||
const manifest = {
|
||||
packageVersion: "1.0.0",
|
||||
platform: "win32",
|
||||
architecture: "x64",
|
||||
abiVersion: 1,
|
||||
nodeApiVersion: 8,
|
||||
artifact: "rea-windows-x64.node",
|
||||
artifactSha256: createHash("sha256").update(bytes).digest("hex"),
|
||||
};
|
||||
return { bytes, manifest };
|
||||
};
|
||||
|
||||
it("produces and admits the exact manifest/byte identity", () => {
|
||||
const { bytes, manifest } = fixture();
|
||||
const created = createWindowsNativeManifest("1.0.0", bytes);
|
||||
expect(created).toEqual({ ok: true, value: manifest });
|
||||
const parsed = parseWindowsNativeManifest(manifest, "1.0.0");
|
||||
expect(parsed).toEqual(created);
|
||||
if (!parsed.ok) throw parsed.error;
|
||||
expect(validateWindowsNativeArtifact(parsed.value, bytes)).toEqual({
|
||||
ok: true,
|
||||
value: undefined,
|
||||
});
|
||||
expect(parseWindowsNativeManifest(manifest, "2.0.0")).toMatchObject({
|
||||
ok: false,
|
||||
error: { message: expect.stringContaining("another REA version") },
|
||||
});
|
||||
expect(
|
||||
validateWindowsNativeArtifact(parsed.value, Buffer.alloc(128)),
|
||||
).toMatchObject({
|
||||
ok: false,
|
||||
error: { message: expect.stringContaining("SHA-256") },
|
||||
});
|
||||
});
|
||||
|
||||
it.each([null, [], {}, "manifest"])(
|
||||
"rejects malformed manifest %j",
|
||||
(value) => {
|
||||
expect(parseWindowsNativeManifest(value, "1.0.0")).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "WindowsNativeArtifactError" },
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects missing and unknown manifest fields", () => {
|
||||
const { manifest } = fixture();
|
||||
expect(
|
||||
parseWindowsNativeManifest({ ...manifest, extra: true }, "1.0.0"),
|
||||
).toMatchObject({ ok: false });
|
||||
for (const key of Object.keys(manifest)) {
|
||||
const incomplete: Record<string, unknown> = { ...manifest };
|
||||
delete incomplete[key];
|
||||
expect(parseWindowsNativeManifest(incomplete, "1.0.0")).toMatchObject({
|
||||
ok: false,
|
||||
});
|
||||
expect(
|
||||
parseWindowsNativeManifest({ ...manifest, [key]: null }, "1.0.0"),
|
||||
).toMatchObject({ ok: false });
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
"platform",
|
||||
"architecture",
|
||||
"abiVersion",
|
||||
"nodeApiVersion",
|
||||
"artifact",
|
||||
"artifactSha256",
|
||||
])("rejects unsupported %s", (key) => {
|
||||
const { manifest } = fixture();
|
||||
expect(
|
||||
parseWindowsNativeManifest({ ...manifest, [key]: "unsupported" }, "1.0.0"),
|
||||
).toMatchObject({ ok: false });
|
||||
});
|
||||
|
||||
it.each([0, 63, 64, 89, 128])(
|
||||
"rejects invalid PE content in %i bytes",
|
||||
(length) => {
|
||||
expect(
|
||||
createWindowsNativeManifest("1.0.0", Buffer.alloc(length)),
|
||||
).toMatchObject({ ok: false });
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["offset", "signature", "machine", "optional magic"])(
|
||||
"rejects invalid PE %s with a matching digest",
|
||||
(field) => {
|
||||
const { bytes } = fixture();
|
||||
if (field === "offset") bytes.writeUInt32LE(0xffff_ffff, 0x3c);
|
||||
if (field === "signature") bytes.write("BAD!", 64, "ascii");
|
||||
if (field === "machine") bytes.writeUInt16LE(0x14c, 68);
|
||||
if (field === "optional magic") bytes.writeUInt16LE(0x10b, 88);
|
||||
const { manifest } = fixture();
|
||||
manifest.artifactSha256 = createHash("sha256").update(bytes).digest("hex");
|
||||
const parsed = parseWindowsNativeManifest(manifest, "1.0.0");
|
||||
if (!parsed.ok) throw parsed.error;
|
||||
expect(validateWindowsNativeArtifact(parsed.value, bytes)).toMatchObject({
|
||||
ok: false,
|
||||
error: { message: expect.stringContaining("PE32+") },
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,145 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readPeCoffHeader } from "./peCoffHeader.js";
|
||||
import { err, ok, type Result } from "./result.js";
|
||||
|
||||
/** Fixed compatibility contract shared by the artifact producer and readers. */
|
||||
export const WINDOWS_NATIVE_ARTIFACT = {
|
||||
platform: "win32",
|
||||
architecture: "x64",
|
||||
abiVersion: 1,
|
||||
nodeApiVersion: 8,
|
||||
artifact: "rea-windows-x64.node",
|
||||
} as const;
|
||||
|
||||
export type WindowsNativeManifest = typeof WINDOWS_NATIVE_ARTIFACT & {
|
||||
readonly packageVersion: string;
|
||||
readonly artifactSha256: string;
|
||||
};
|
||||
|
||||
export class WindowsNativeArtifactError extends Error {
|
||||
readonly _tag = "WindowsNativeArtifactError" as const;
|
||||
}
|
||||
|
||||
/** Parse the exact package manifest before acquiring or loading its artifact. */
|
||||
export const parseWindowsNativeManifest = (
|
||||
value: unknown,
|
||||
packageVersion: string,
|
||||
): Result<WindowsNativeManifest, WindowsNativeArtifactError> => {
|
||||
if (value === null || typeof value !== "object" || Array.isArray(value))
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact manifest must be an object",
|
||||
),
|
||||
);
|
||||
const manifest = value as Record<string, unknown>;
|
||||
const keys = [
|
||||
"packageVersion",
|
||||
...Object.keys(WINDOWS_NATIVE_ARTIFACT),
|
||||
"artifactSha256",
|
||||
];
|
||||
if (
|
||||
Object.keys(manifest).length !== keys.length ||
|
||||
keys.some((key) => !Object.hasOwn(manifest, key))
|
||||
)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact manifest has missing or unknown fields",
|
||||
),
|
||||
);
|
||||
for (const [key, expected] of Object.entries(WINDOWS_NATIVE_ARTIFACT))
|
||||
if (manifest[key] !== expected)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
`Windows native artifact manifest has an invalid ${key}`,
|
||||
),
|
||||
);
|
||||
if (typeof manifest.packageVersion !== "string")
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact manifest has an invalid packageVersion",
|
||||
),
|
||||
);
|
||||
if (
|
||||
typeof manifest.artifactSha256 !== "string" ||
|
||||
!/^[a-f0-9]{64}$/u.test(manifest.artifactSha256)
|
||||
)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact manifest has an invalid artifactSha256",
|
||||
),
|
||||
);
|
||||
if (manifest.packageVersion !== packageVersion)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact belongs to another REA version",
|
||||
),
|
||||
);
|
||||
return ok({
|
||||
...WINDOWS_NATIVE_ARTIFACT,
|
||||
packageVersion: manifest.packageVersion,
|
||||
artifactSha256: manifest.artifactSha256,
|
||||
});
|
||||
};
|
||||
|
||||
const validateWindowsX64Artifact = (
|
||||
bytes: Buffer,
|
||||
): Result<void, WindowsNativeArtifactError> => {
|
||||
if (bytes.length < 64 || bytes.toString("latin1", 0, 2) !== "MZ")
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact is not a PE image",
|
||||
),
|
||||
);
|
||||
const offset = bytes.readUInt32LE(0x3c);
|
||||
if (
|
||||
offset > bytes.length - 26 ||
|
||||
bytes.toString("latin1", offset, offset + 4) !== "PE\0\0" ||
|
||||
bytes.readUInt16LE(offset + 24) !== 0x20b
|
||||
)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact is not a Windows x64 PE32+ image",
|
||||
),
|
||||
);
|
||||
const coff = readPeCoffHeader(bytes.subarray(offset + 4, offset + 24));
|
||||
if (!coff.ok || coff.value.machine !== 0x8664)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact is not a Windows x64 PE32+ image",
|
||||
),
|
||||
);
|
||||
return ok(undefined);
|
||||
};
|
||||
|
||||
/** Bind the parsed manifest to its exact bytes before native code can load. */
|
||||
export const validateWindowsNativeArtifact = (
|
||||
manifest: WindowsNativeManifest,
|
||||
bytes: Buffer,
|
||||
): Result<void, WindowsNativeArtifactError> => {
|
||||
if (
|
||||
createHash("sha256").update(bytes).digest("hex") !== manifest.artifactSha256
|
||||
)
|
||||
return err(
|
||||
new WindowsNativeArtifactError(
|
||||
"Windows native artifact SHA-256 does not match package metadata",
|
||||
),
|
||||
);
|
||||
return validateWindowsX64Artifact(bytes);
|
||||
};
|
||||
|
||||
/** Produce metadata only for a Windows x64 PE32+ artifact. */
|
||||
export const createWindowsNativeManifest = (
|
||||
packageVersion: string,
|
||||
bytes: Buffer,
|
||||
): Result<WindowsNativeManifest, WindowsNativeArtifactError> => {
|
||||
const valid = validateWindowsX64Artifact(bytes);
|
||||
if (!valid.ok) return valid;
|
||||
return parseWindowsNativeManifest(
|
||||
{
|
||||
...WINDOWS_NATIVE_ARTIFACT,
|
||||
packageVersion,
|
||||
artifactSha256: createHash("sha256").update(bytes).digest("hex"),
|
||||
},
|
||||
packageVersion,
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
import { readPeCoffHeader } from "../domain/peCoffHeader.js";
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -7,6 +5,11 @@ import { fileURLToPath } from "node:url";
|
||||
import { z } from "zod";
|
||||
|
||||
import { PACKAGE_METADATA } from "../generatedPackageMetadata.js";
|
||||
import {
|
||||
WINDOWS_NATIVE_ARTIFACT,
|
||||
parseWindowsNativeManifest,
|
||||
validateWindowsNativeArtifact,
|
||||
} from "../domain/windowsNativeArtifact.js";
|
||||
|
||||
/** A native resource remains opaque until the addon validates its object tag. */
|
||||
export const windowsHandleSchema = z.custom<object>(
|
||||
@@ -25,21 +28,10 @@ export const windowsFileIdentitySchema = z.strictObject({
|
||||
directory: z.boolean(),
|
||||
});
|
||||
|
||||
/** Metadata bound to one packaged Windows x64 Node-API artifact. */
|
||||
export const windowsNativeManifestSchema = z.strictObject({
|
||||
packageVersion: z.string(),
|
||||
platform: z.literal("win32"),
|
||||
architecture: z.literal("x64"),
|
||||
abiVersion: z.literal(1),
|
||||
nodeApiVersion: z.literal(8),
|
||||
artifact: z.literal("rea-windows-x64.node"),
|
||||
artifactSha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
||||
});
|
||||
|
||||
const inspectionSchema = z.strictObject({
|
||||
abiVersion: z.literal(1),
|
||||
nodeApiVersion: z.literal(8),
|
||||
architecture: z.literal("x64"),
|
||||
abiVersion: z.literal(WINDOWS_NATIVE_ARTIFACT.abiVersion),
|
||||
nodeApiVersion: z.literal(WINDOWS_NATIVE_ARTIFACT.nodeApiVersion),
|
||||
architecture: z.literal(WINDOWS_NATIVE_ARTIFACT.architecture),
|
||||
filesystem: windowsFileIdentitySchema,
|
||||
privateDacl: z.literal(true),
|
||||
privateBridgeDescriptorAccess: z.literal(true),
|
||||
@@ -126,29 +118,27 @@ export const loadWindowsNativeAuthority = (
|
||||
reason: "REA Windows native controls require a Windows x64 host.",
|
||||
};
|
||||
const nodeApiVersion = Number(host.nodeApiVersion);
|
||||
if (!Number.isSafeInteger(nodeApiVersion) || nodeApiVersion < 8)
|
||||
if (
|
||||
!Number.isSafeInteger(nodeApiVersion) ||
|
||||
nodeApiVersion < WINDOWS_NATIVE_ARTIFACT.nodeApiVersion
|
||||
)
|
||||
return {
|
||||
available: false,
|
||||
failureStage: "host",
|
||||
reason: "REA Windows native controls require Node-API 8 or newer.",
|
||||
};
|
||||
try {
|
||||
const manifest = windowsNativeManifestSchema.parse(
|
||||
const manifest = parseWindowsNativeManifest(
|
||||
readPackagedWindowsNativeFile("manifest.json", () => host.readManifest()),
|
||||
host.packageVersion,
|
||||
);
|
||||
if (manifest.packageVersion !== host.packageVersion)
|
||||
throw new Error("Windows native artifact belongs to another REA version");
|
||||
const artifact = readPackagedWindowsNativeFile("rea-windows-x64.node", () =>
|
||||
host.readArtifact(),
|
||||
if (!manifest.ok) throw manifest.error;
|
||||
const artifact = readPackagedWindowsNativeFile(
|
||||
WINDOWS_NATIVE_ARTIFACT.artifact,
|
||||
() => host.readArtifact(),
|
||||
);
|
||||
if (
|
||||
createHash("sha256").update(artifact).digest("hex") !==
|
||||
manifest.artifactSha256
|
||||
)
|
||||
throw new Error(
|
||||
"Windows native artifact SHA-256 does not match package metadata",
|
||||
);
|
||||
assertWindowsX64Artifact(artifact);
|
||||
const valid = validateWindowsNativeArtifact(manifest.value, artifact);
|
||||
if (!valid.ok) throw valid.error;
|
||||
let loadedModule: unknown;
|
||||
try {
|
||||
loadedModule = host.loadArtifact();
|
||||
@@ -189,21 +179,6 @@ export const loadWindowsNativeAuthority = (
|
||||
}
|
||||
};
|
||||
|
||||
const assertWindowsX64Artifact = (bytes: Buffer): void => {
|
||||
if (bytes.length < 64 || bytes.toString("latin1", 0, 2) !== "MZ")
|
||||
throw new Error("Windows native artifact is not a PE image");
|
||||
const offset = bytes.readUInt32LE(0x3c);
|
||||
if (
|
||||
offset > bytes.length - 26 ||
|
||||
bytes.toString("latin1", offset, offset + 4) !== "PE\0\0" ||
|
||||
bytes.readUInt16LE(offset + 24) !== 0x20b
|
||||
)
|
||||
throw new Error("Windows native artifact is not a Windows x64 PE32+ image");
|
||||
const coff = readPeCoffHeader(bytes.subarray(offset + 4, offset + 24));
|
||||
if (!coff.ok || coff.value.machine !== 0x8664)
|
||||
throw new Error("Windows native artifact is not a Windows x64 PE32+ image");
|
||||
};
|
||||
|
||||
let cached: WindowsNativeLoadResult | undefined;
|
||||
|
||||
/** Cache stable admission; let a later query retry failed owned inspection. */
|
||||
|
||||
@@ -738,6 +738,19 @@ it.each([
|
||||
"src/windows/Example.ts",
|
||||
{ code: true, runtime: true, package: true, apple: false, evm: false },
|
||||
],
|
||||
[
|
||||
"src/domain/windowsNativeArtifact.ts",
|
||||
{ code: true, runtime: true, package: true },
|
||||
],
|
||||
["src/domain/peCoffHeader.ts", { code: true, runtime: true, package: true }],
|
||||
[
|
||||
"scripts/lib/verifier-source-loader.mjs",
|
||||
{ code: true, runtime: true, package: true },
|
||||
],
|
||||
[
|
||||
"tests/boundary/process/windowsNativeArtifactVerifier.test.ts",
|
||||
{ code: true, package: true },
|
||||
],
|
||||
[
|
||||
"src/domain/javascript/javascriptRuntimeStaticCandidates.ts",
|
||||
{ code: true, runtime: true, package: true, javascript_recovery: true },
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { expect } from "vitest";
|
||||
import { processTest } from "../../support/process/processFixture.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const repository = fileURLToPath(new URL("../../..", import.meta.url));
|
||||
|
||||
for (const mode of ["preinstall source", "packed source-free"]) {
|
||||
processTest(
|
||||
`verifies native artifact identity from ${mode} without installed dependencies`,
|
||||
async ({ processes }) => {
|
||||
const root = await createTestTempDirectory(
|
||||
"rea-native-artifact-verifier-",
|
||||
);
|
||||
const paths =
|
||||
mode === "preinstall source"
|
||||
? [
|
||||
"scripts/lib/verifier-source-loader.mjs",
|
||||
"src/domain/windowsNativeArtifact.ts",
|
||||
"src/domain/peCoffHeader.ts",
|
||||
"src/domain/result.ts",
|
||||
]
|
||||
: [
|
||||
"dist/domain/windowsNativeArtifact.js",
|
||||
"dist/domain/peCoffHeader.js",
|
||||
"dist/domain/result.js",
|
||||
];
|
||||
for (const path of [
|
||||
"scripts/verify-windows-native-artifact.mjs",
|
||||
...paths,
|
||||
]) {
|
||||
await mkdir(dirname(join(root, path)), { recursive: true });
|
||||
await copyFile(join(repository, path), join(root, path));
|
||||
}
|
||||
await writeFile(
|
||||
join(root, "package.json"),
|
||||
JSON.stringify({ type: "module", version: "1.0.0" }),
|
||||
);
|
||||
const artifactRoot = join(root, "native/windows/build");
|
||||
await mkdir(artifactRoot, { recursive: true });
|
||||
// An inert PE header establishes artifact validation, never addon loading.
|
||||
const bytes = Buffer.alloc(128);
|
||||
bytes.write("MZ");
|
||||
bytes.writeUInt32LE(64, 0x3c);
|
||||
bytes.write("PE\0\0", 64, "ascii");
|
||||
bytes.writeUInt16LE(0x8664, 68);
|
||||
bytes.writeUInt16LE(0x20b, 88);
|
||||
const manifest = {
|
||||
packageVersion: "1.0.0",
|
||||
platform: "win32",
|
||||
architecture: "x64",
|
||||
abiVersion: 1,
|
||||
nodeApiVersion: 8,
|
||||
artifact: "rea-windows-x64.node",
|
||||
artifactSha256: createHash("sha256").update(bytes).digest("hex"),
|
||||
};
|
||||
await writeFile(join(artifactRoot, manifest.artifact), bytes);
|
||||
const manifestPath = join(artifactRoot, "manifest.json");
|
||||
await writeFile(manifestPath, JSON.stringify(manifest));
|
||||
const run = () =>
|
||||
processes.run(
|
||||
process.execPath,
|
||||
[join(root, "scripts/verify-windows-native-artifact.mjs")],
|
||||
{ cwd: root, timeoutMs: 10_000 },
|
||||
);
|
||||
const valid = await run();
|
||||
expect(valid.exitCode, valid.stderr).toBe(0);
|
||||
expect(JSON.parse(valid.stdout)).toEqual({
|
||||
ok: true,
|
||||
...manifest,
|
||||
artifactBytes: bytes.length,
|
||||
});
|
||||
await writeFile(
|
||||
manifestPath,
|
||||
JSON.stringify({ ...manifest, extra: true }),
|
||||
);
|
||||
const invalid = await run();
|
||||
expect(invalid.exitCode).not.toBe(0);
|
||||
expect(invalid.stderr).toContain("missing or unknown fields");
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user