fix: isolate Linux inventory and repair DEX schema regex (#2910)
* fix(ci): isolate Linux Hopper process fixture inventory * fix(android): make DEX descriptor pattern portable
This commit is contained in:
1 parent
1b6c899b03
commit
69bd81f7f2
4 files changed
+29
-7
No files matched your search
@@ -1,3 +1,4 @@
|
||||
export function assertCleanLinuxHopperRuntime(
|
||||
launcherPaths?: readonly string[],
|
||||
procRoot?: string,
|
||||
): Promise<void>;
|
||||
@@ -16,6 +16,7 @@ export const assertCleanLinuxHopperRuntime = async (
|
||||
? []
|
||||
: [process.env.HOPPER_LAUNCHER_PATH]),
|
||||
],
|
||||
procRoot = "/proc",
|
||||
) => {
|
||||
const { stdout } = await execFileAsync("ps", ["-ax", "-o", "command="]);
|
||||
if (/\/opt\/hopper\/bin\/Hopper|\bXvfb\b|hopper-demo-x11\.py/u.test(stdout))
|
||||
@@ -32,17 +33,17 @@ export const assertCleanLinuxHopperRuntime = async (
|
||||
// /proc exposes the executed file even when argv[0] names a removed symlink.
|
||||
// Compare file identity, not a display path or a process name.
|
||||
let inspectionError;
|
||||
for (const entry of await readdir("/proc", { withFileTypes: true })) {
|
||||
for (const entry of await readdir(procRoot, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory() || !/^\d+$/u.test(entry.name)) continue;
|
||||
let executable;
|
||||
try {
|
||||
executable = await stat(`/proc/${entry.name}/exe`, { bigint: true });
|
||||
executable = await stat(`${procRoot}/${entry.name}/exe`, { bigint: true });
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT" || error?.code === "ESRCH") continue;
|
||||
// Other users' executables may be unreadable. The setup lane launches
|
||||
// providers as the caller; unreadable caller-owned processes fail closed.
|
||||
if (error?.code === "EACCES") {
|
||||
const owner = await stat(`/proc/${entry.name}`).catch((cause) => {
|
||||
const owner = await stat(`${procRoot}/${entry.name}`).catch((cause) => {
|
||||
if (cause?.code === "ENOENT") return undefined;
|
||||
throw cause;
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ const textCoverage = z.strictObject({
|
||||
export const dexDescriptorSchema = z
|
||||
.string()
|
||||
.regex(
|
||||
/^\((?:\[*(?:[ZBSCIJFD]|L[^;()]+;))*\)(?:V|\[*(?:[ZBSCIJFD]|L[^;()]+;))$/u,
|
||||
/^\((?:\[*(?:[ZBSCIJFD]|L[^;\x28\x29]+;))*\)(?:V|\[*(?:[ZBSCIJFD]|L[^;\x28\x29]+;))$/u,
|
||||
);
|
||||
|
||||
const method = z.strictObject({
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { chmod, copyFile, readFile, symlink, unlink } from "node:fs/promises";
|
||||
import {
|
||||
chmod,
|
||||
copyFile,
|
||||
mkdir,
|
||||
readFile,
|
||||
symlink,
|
||||
unlink,
|
||||
} from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
@@ -24,6 +31,8 @@ it
|
||||
const directory = await createTestTempDirectory("rea-clean-runtime-test-");
|
||||
const launcher = join(directory, "Hopper");
|
||||
const alternate = join(directory, "Hopper-alternate");
|
||||
const procRoot = join(directory, "proc");
|
||||
await mkdir(procRoot);
|
||||
const alternateMode = mode === "alternate launcher";
|
||||
if (alternateMode) {
|
||||
await copyFile("/usr/bin/sleep", launcher);
|
||||
@@ -32,7 +41,10 @@ it
|
||||
}
|
||||
// A rejection must come from the fixture we are about to launch. An
|
||||
// existing Xvfb would otherwise mask a broken executable-identity check.
|
||||
await assertCleanLinuxHopperRuntime(alternateMode ? [launcher] : []);
|
||||
await assertCleanLinuxHopperRuntime(
|
||||
alternateMode ? [launcher] : [],
|
||||
procRoot,
|
||||
);
|
||||
const command = alternateMode ? alternate : process.execPath;
|
||||
const arguments_ = alternateMode ? ["30"] : ["-e", childCode];
|
||||
const parentCode = `
|
||||
@@ -59,6 +71,11 @@ it
|
||||
const { stdout } = await parent;
|
||||
const { pid } = JSON.parse(stdout) as { pid: number };
|
||||
orphanPid = pid;
|
||||
if (alternateMode) {
|
||||
const processEntry = join(procRoot, String(pid));
|
||||
await mkdir(processEntry);
|
||||
await symlink(`/proc/${String(pid)}/exe`, join(processEntry, "exe"));
|
||||
}
|
||||
const { stdout: coordinates } = await execFileAsync("ps", [
|
||||
"-p",
|
||||
String(pid),
|
||||
@@ -92,7 +109,10 @@ it
|
||||
await unlink(alternate);
|
||||
}
|
||||
await expect(
|
||||
assertCleanLinuxHopperRuntime(alternateMode ? [launcher] : []),
|
||||
assertCleanLinuxHopperRuntime(
|
||||
alternateMode ? [launcher] : [],
|
||||
procRoot,
|
||||
),
|
||||
).rejects.toThrow("Clean Linux runner retained a Hopper demo process");
|
||||
// Observation must not terminate the fixture, even though it resembles Xvfb.
|
||||
const { stdout: alive } = await execFileAsync("ps", [
|
||||
|
||||
Reference in new issue
Block a user