mirror of
https://github.com/laaielichter/Reverseport.git
synced 2026-11-07 10:16:04 -10:00
fix(verify): keep Windows host variables in isolated client verifiers (#94)
The Qwen, Gemini, Command Code and Harness client verifiers built an isolated environment with only PATH and their own home variables. On Windows a child Node process without SystemRoot aborts with `Assertion failed: ncrypto::CSPRNG(nullptr, 0)`, and npm .cmd shims also need ComSpec and PATHEXT. Keep those four host variables on Windows only, and test the selection, its use in each verifier, and a child Node start on Windows.
This commit is contained in:
1 parent
8136e8dfb7
commit
de1c0c8e5d
7 files changed
+106
No files matched your search
@@ -0,0 +1,5 @@
|
||||
/** Host variables to keep in an isolated verifier environment. */
|
||||
export function verifierHostEnvironment(
|
||||
source?: Readonly<Record<string, string | undefined>>,
|
||||
platform?: NodeJS.Platform,
|
||||
): Record<string, string>;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Variables a Windows child process needs from the host even when a verifier
|
||||
* isolates everything else. Without SystemRoot, Node aborts at startup with
|
||||
* `Assertion failed: ncrypto::CSPRNG(nullptr, 0)`; without ComSpec and PATHEXT
|
||||
* npm `.cmd` shims such as `qwen.cmd` cannot be resolved or run.
|
||||
*/
|
||||
const WINDOWS_HOST_VARIABLES = ["SystemRoot", "WINDIR", "ComSpec", "PATHEXT"];
|
||||
|
||||
/** Host variables to keep in an isolated verifier environment. */
|
||||
export const verifierHostEnvironment = (
|
||||
source = process.env,
|
||||
platform = process.platform,
|
||||
) => {
|
||||
if (platform !== "win32") return {};
|
||||
const kept = {};
|
||||
for (const name of WINDOWS_HOST_VARIABLES) {
|
||||
// Windows environment names are case-insensitive; Node keeps the host spelling.
|
||||
const key = Object.keys(source).find(
|
||||
(candidate) => candidate.toUpperCase() === name.toUpperCase(),
|
||||
);
|
||||
if (key !== undefined && source[key] !== undefined)
|
||||
kept[name] = source[key];
|
||||
}
|
||||
return kept;
|
||||
};
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "../dist/contracts/toolContracts.js";
|
||||
import { createVerifierCommands } from "./lib/verify-commands.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
import { verifierHostEnvironment } from "./lib/verifier-host-environment.mjs";
|
||||
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
|
||||
import { assertJavaScriptApplicationInputPaths } from "./lib/javascript-application-paths.mjs";
|
||||
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
||||
@@ -85,6 +86,7 @@ await withVerifierCleanup(async () => {
|
||||
'export function result() { return { clientCompatibility: "REA_CLIENT_FIXTURE", count: 7 }; }\n',
|
||||
);
|
||||
const environment = {
|
||||
...verifierHostEnvironment(),
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: process.env.NODE_OPTIONS,
|
||||
USERPROFILE: account,
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { pathExists } from "./lib/verify-package-core.mjs";
|
||||
import { createVerifierCommands } from "./lib/verify-commands.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
import { verifierHostEnvironment } from "./lib/verifier-host-environment.mjs";
|
||||
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
|
||||
import { assertJavaScriptApplicationInputPaths } from "./lib/javascript-application-paths.mjs";
|
||||
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
||||
@@ -61,6 +62,7 @@ await withVerifierCleanup(async () => {
|
||||
mkdir(target, { recursive: true }),
|
||||
]);
|
||||
const environment = {
|
||||
...verifierHostEnvironment(),
|
||||
PATH: process.env.PATH ?? "",
|
||||
HOME: account,
|
||||
USERPROFILE: account,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { createVerifierCommands } from "./lib/verify-commands.mjs";
|
||||
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
import { verifierHostEnvironment } from "./lib/verifier-host-environment.mjs";
|
||||
import { assertJavaScriptApplicationInputPaths } from "./lib/javascript-application-paths.mjs";
|
||||
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
||||
import { createGeminiModelFixture } from "./verify/clients/gemini-model-fixture.mjs";
|
||||
@@ -78,6 +79,7 @@ await withVerifierCleanup(async () => {
|
||||
await writeFile(join(lab, "system-settings.json"), "{}\n");
|
||||
await writeFile(join(lab, "system-defaults.json"), "{}\n");
|
||||
const environment = {
|
||||
...verifierHostEnvironment(),
|
||||
PATH: process.env.PATH ?? "",
|
||||
USERPROFILE: account,
|
||||
GEMINI_CLI_HOME: profile,
|
||||
|
||||
@@ -14,6 +14,7 @@ import { createHash } from "node:crypto";
|
||||
import { createVerifierCommands } from "./lib/verify-commands.mjs";
|
||||
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
import { verifierHostEnvironment } from "./lib/verifier-host-environment.mjs";
|
||||
import { assertJavaScriptApplicationInputPaths } from "./lib/javascript-application-paths.mjs";
|
||||
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
||||
import {
|
||||
@@ -66,6 +67,7 @@ await withVerifierCleanup(async () => {
|
||||
mkdir(workspace),
|
||||
]);
|
||||
const baseEnvironment = {
|
||||
...verifierHostEnvironment(),
|
||||
PATH: process.env.PATH ?? "",
|
||||
HOME: account,
|
||||
USERPROFILE: account,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { verifierHostEnvironment } from "../../../scripts/lib/verifier-host-environment.mjs";
|
||||
|
||||
const execute = promisify(execFile);
|
||||
const project = fileURLToPath(new URL("../../../", import.meta.url));
|
||||
|
||||
describe("isolated verifier environments", () => {
|
||||
it("keep the Windows host variables a child Node process needs", () => {
|
||||
// A Windows child started without SystemRoot aborts with
|
||||
// `Assertion failed: ncrypto::CSPRNG(nullptr, 0)` before printing anything.
|
||||
expect(
|
||||
verifierHostEnvironment(
|
||||
{
|
||||
SYSTEMROOT: "C:\\Windows",
|
||||
windir: "C:\\Windows",
|
||||
ComSpec: "C:\\Windows\\system32\\cmd.exe",
|
||||
PATHEXT: ".COM;.EXE;.BAT;.CMD",
|
||||
USERPROFILE: "C:\\Users\\someone",
|
||||
SECRET: "not copied",
|
||||
},
|
||||
"win32",
|
||||
),
|
||||
).toEqual({
|
||||
SystemRoot: "C:\\Windows",
|
||||
WINDIR: "C:\\Windows",
|
||||
ComSpec: "C:\\Windows\\system32\\cmd.exe",
|
||||
PATHEXT: ".COM;.EXE;.BAT;.CMD",
|
||||
});
|
||||
});
|
||||
|
||||
it("add nothing outside Windows", () => {
|
||||
expect(
|
||||
verifierHostEnvironment({ SystemRoot: "C:\\Windows" }, "linux"),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
it.each(["qwen", "gemini", "commandcode", "deepseek"])(
|
||||
"is used by the %s client verifier, which runs on Windows",
|
||||
async (client) => {
|
||||
const source = await readFile(
|
||||
`${project}scripts/verify-${client}-client.mjs`,
|
||||
"utf8",
|
||||
);
|
||||
// `\s+` also spans the CRLF of a Windows autocrlf checkout.
|
||||
expect(source).toMatch(/\.\.\.verifierHostEnvironment\(\),\s+PATH:/u);
|
||||
},
|
||||
);
|
||||
|
||||
it.runIf(process.platform === "win32")(
|
||||
"lets a child Node process start on Windows",
|
||||
async () => {
|
||||
const { stdout } = await execute(
|
||||
process.execPath,
|
||||
[
|
||||
"-e",
|
||||
"process.stdout.write(String(require('node:crypto').randomInt(1, 2)))",
|
||||
],
|
||||
{ env: { ...verifierHostEnvironment(), PATH: process.env.PATH ?? "" } },
|
||||
);
|
||||
expect(stdout).toBe("1");
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user