mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(pcap): validate selected resource limiter before capture (#7947)
* fix(pcap): validate selected resource limiter before capture * fix(pcap): verify limiter capabilities and preserve probe output
This commit is contained in:
1 parent
59a895a193
commit
78a1e1956e
5 files changed
+375
-25
No files matched your search
@@ -1,8 +1,13 @@
|
||||
import { chmod, writeFile } from "node:fs/promises";
|
||||
import { chmod, symlink, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
|
||||
import type { ArtifactAnalysisResources } from "../process/ArtifactAnalysisResources.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisArtifactChangedError,
|
||||
AnalysisOutputError,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import {
|
||||
BOUNDED_PRODUCER_LIMITS,
|
||||
prlimitCeilingArguments,
|
||||
@@ -10,6 +15,12 @@ import {
|
||||
} from "../process/LinuxResourceLimits.js";
|
||||
import { preparePcapNativeBound } from "./PcapNativeBound.js";
|
||||
|
||||
const probeResult = (stdout: string, stderr = "") => ({
|
||||
stdout,
|
||||
capturedOutput: { stdout, stderr, truncated: false },
|
||||
});
|
||||
const help = " --as[=limit] --fsize[=limit] --cpu[=limit] --core[=limit]";
|
||||
|
||||
const resources = {
|
||||
readLimits: (limits: LinuxResourceLimits) => Promise.resolve(limits),
|
||||
} as ArtifactAnalysisResources;
|
||||
@@ -35,7 +46,9 @@ const withPlatform = async (
|
||||
|
||||
it("uses the shared 1 GiB ceiling for the Windows job memory limit", async () => {
|
||||
await withPlatform("win32", async () => {
|
||||
await expect(preparePcapNativeBound({}, resources)).resolves.toMatchObject({
|
||||
await expect(
|
||||
preparePcapNativeBound({}, resources, async () => probeResult("")),
|
||||
).resolves.toMatchObject({
|
||||
nativeBound: {
|
||||
memoryLimitBytes: BOUNDED_PRODUCER_LIMITS.address_space_bytes,
|
||||
},
|
||||
@@ -43,6 +56,64 @@ it("uses the shared 1 GiB ceiling for the Windows job memory limit", async () =>
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts selected limiter symlinks with different and whitespace-bearing basenames", async () => {
|
||||
const root = await createTestTempDirectory("rea-prlimit-alias-");
|
||||
const executable = join(root, "prlimit");
|
||||
await writeFile(executable, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
for (const name of ["bounded-capture", " limiter", " limiter "]) {
|
||||
const selected = join(root, name);
|
||||
await symlink(executable, selected);
|
||||
await withPlatform("linux", async () => {
|
||||
const bound = await preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: selected },
|
||||
resources,
|
||||
async (selection, args) => {
|
||||
expect(selection.invocationPath).toBe(selected);
|
||||
return probeResult(
|
||||
args[0] === "--help" ? help : `${name} from util-linux 2.38.1\n`,
|
||||
);
|
||||
},
|
||||
);
|
||||
expect(bound.nativeBound.limiter?.command.invocationPath).toBe(selected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("attributes malformed limiter version output while retaining producer evidence", async () => {
|
||||
const root = await createTestTempDirectory("rea-prlimit-invalid-output-");
|
||||
const selected = join(root, "prlimit");
|
||||
await writeFile(selected, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
const capturedOutput = { stdout: "\ufffd", stderr: "", truncated: false };
|
||||
const failure = new AnalysisOutputError(
|
||||
"inspect_pcap_capture",
|
||||
"invalid UTF-8",
|
||||
{
|
||||
capturedOutput,
|
||||
diagnostics: { invalid_utf8_streams: ["stdout"] },
|
||||
},
|
||||
);
|
||||
await withPlatform("linux", async () => {
|
||||
await expect(
|
||||
preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: selected },
|
||||
resources,
|
||||
async () => {
|
||||
throw failure;
|
||||
},
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
capturedOutput,
|
||||
diagnostics: {
|
||||
invalid_utf8_streams: ["stdout"],
|
||||
configuration_key: "REA_TSHARK_PRLIMIT_COMMAND",
|
||||
configured: selected,
|
||||
},
|
||||
userMessage: expect.stringContaining("unreadable version output"),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("pins capture ceilings through the shared prlimit prefix and keeps 300 CPU seconds", async () => {
|
||||
const root = await createTestTempDirectory("rea-pcap-prlimit-");
|
||||
const limiter = join(root, "prlimit");
|
||||
@@ -57,6 +128,10 @@ it("pins capture ceilings through the shared prlimit prefix and keeps 300 CPU se
|
||||
const prepared = await preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: limiter },
|
||||
resources,
|
||||
async (_selection, args) =>
|
||||
probeResult(
|
||||
args[0] === "--help" ? help : "prlimit from util-linux 2.38.1\n",
|
||||
),
|
||||
);
|
||||
expect(prepared.nativeBound.limiter?.arguments).toEqual(
|
||||
prlimitCeilingArguments(limits),
|
||||
@@ -64,3 +139,103 @@ it("pins capture ceilings through the shared prlimit prefix and keeps 300 CPU se
|
||||
expect(prepared.limitations[0]).toContain(String(limits.cpu_seconds));
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a directory or a command that does not identify as util-linux prlimit", async () => {
|
||||
const root = await createTestTempDirectory("rea-invalid-prlimit-");
|
||||
const executable = join(root, "not-prlimit");
|
||||
await writeFile(executable, "#!/bin/sh\nexit 0\n");
|
||||
await chmod(executable, 0o700);
|
||||
await withPlatform("linux", async () => {
|
||||
for (const [path, output] of [
|
||||
[root, "prlimit from util-linux 2.38"],
|
||||
[executable, ""],
|
||||
] as const) {
|
||||
await expect(
|
||||
preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: path },
|
||||
resources,
|
||||
async () => probeResult(output),
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
diagnostics: {
|
||||
configuration_key: "REA_TSHARK_PRLIMIT_COMMAND",
|
||||
configured: path,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves cancellation and selected-command identity failures during the probe", async () => {
|
||||
const root = await createTestTempDirectory("rea-prlimit-probe-failure-");
|
||||
const limiter = join(root, "prlimit");
|
||||
await writeFile(limiter, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
await withPlatform("linux", async () => {
|
||||
for (const failure of [
|
||||
new AnalysisCancelledError("inspect_pcap_capture"),
|
||||
new AnalysisArtifactChangedError(
|
||||
"inspect_pcap_capture",
|
||||
limiter,
|
||||
"changed",
|
||||
),
|
||||
new AnalysisOutputError("inspect_pcap_capture", "invalid output", {
|
||||
cleanup: { reason: "stop failed", resources: ["owned-process"] },
|
||||
}),
|
||||
]) {
|
||||
await expect(
|
||||
preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: limiter },
|
||||
resources,
|
||||
async () => {
|
||||
throw failure;
|
||||
},
|
||||
),
|
||||
).rejects.toBe(failure);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects another util-linux command and retains the rejected help output", async () => {
|
||||
const root = await createTestTempDirectory("rea-prlimit-impostor-");
|
||||
const selected = join(root, "lsblk");
|
||||
await writeFile(selected, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
const capturedOutput = probeResult(
|
||||
" --output columns",
|
||||
"help diagnostic",
|
||||
).capturedOutput;
|
||||
await withPlatform("linux", async () => {
|
||||
await expect(
|
||||
preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: selected },
|
||||
resources,
|
||||
async (_selection, args) =>
|
||||
args[0] === "--help"
|
||||
? { stdout: capturedOutput.stdout, capturedOutput }
|
||||
: probeResult("lsblk from util-linux 2.38\n"),
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
capturedOutput,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("retains rejected version output", async () => {
|
||||
const root = await createTestTempDirectory("rea-prlimit-rejected-version-");
|
||||
const selected = join(root, "prlimit");
|
||||
await writeFile(selected, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
const output = probeResult("unexpected banner", "diagnostic");
|
||||
await withPlatform("linux", async () => {
|
||||
await expect(
|
||||
preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: selected },
|
||||
resources,
|
||||
async () => output,
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
capturedOutput: output.capturedOutput,
|
||||
});
|
||||
});
|
||||
});
|
||||
+80
-19
@@ -3,17 +3,25 @@ import {
|
||||
pcapCaptureHostSupported,
|
||||
PCAP_CAPTURE_UNSUPPORTED_HOST_REASON,
|
||||
} from "../domain/pcap/pcapAnalysis.js";
|
||||
import { access } from "node:fs/promises";
|
||||
import { access, stat } from "node:fs/promises";
|
||||
import { constants } from "node:fs";
|
||||
import { isAbsolute } from "node:path";
|
||||
import { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore.js";
|
||||
import { basename, isAbsolute } from "node:path";
|
||||
import {
|
||||
AnalysisError,
|
||||
type AnalysisCapturedOutput,
|
||||
} from "../domain/analysisErrorBase.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisOutputError,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import type { ArtifactAnalysisResources } from "../process/ArtifactAnalysisResources.js";
|
||||
import {
|
||||
BOUNDED_PRODUCER_LIMITS,
|
||||
prlimitCeilingArguments,
|
||||
} from "../process/LinuxResourceLimits.js";
|
||||
import type { runPcapCommand } from "./PcapCommand.js";
|
||||
import type { ArtifactCommandPath } from "../process/ArtifactCommandPath.js";
|
||||
|
||||
const PRLIMIT_COMMAND_ENV = "REA_TSHARK_PRLIMIT_COMMAND";
|
||||
/** Capture CPU ceiling, in seconds. */
|
||||
@@ -23,6 +31,17 @@ const PCAP_CPU_SECONDS = 300;
|
||||
export const preparePcapNativeBound = async (
|
||||
environment: Readonly<Record<string, string | undefined>>,
|
||||
resources: ArtifactAnalysisResources,
|
||||
probe: (
|
||||
selected: ArtifactCommandPath,
|
||||
arguments_: string[],
|
||||
) => Promise<{
|
||||
readonly stdout: string;
|
||||
readonly capturedOutput: AnalysisCapturedOutput;
|
||||
}>,
|
||||
signal?: AbortSignal,
|
||||
operation:
|
||||
| "inspect_pcap_client"
|
||||
| "inspect_pcap_capture" = "inspect_pcap_capture",
|
||||
): Promise<{
|
||||
nativeBound: NonNullable<Parameters<typeof runPcapCommand>[0]["nativeBound"]>;
|
||||
limitations: readonly string[];
|
||||
@@ -39,19 +58,56 @@ export const preparePcapNativeBound = async (
|
||||
}
|
||||
if (!pcapCaptureHostSupported(process.platform)) {
|
||||
const reason = PCAP_CAPTURE_UNSUPPORTED_HOST_REASON;
|
||||
throw new AnalysisCapabilityUnavailableError(
|
||||
"pcap",
|
||||
"inspect_pcap_capture",
|
||||
reason,
|
||||
{ userMessage: reason },
|
||||
);
|
||||
throw new AnalysisCapabilityUnavailableError("pcap", operation, reason, {
|
||||
userMessage: reason,
|
||||
});
|
||||
}
|
||||
const configured = environment[PRLIMIT_COMMAND_ENV] ?? "/usr/bin/prlimit";
|
||||
let probeFailure: AnalysisError | undefined;
|
||||
let capturedOutput: AnalysisCapturedOutput | undefined;
|
||||
try {
|
||||
if (!isAbsolute(configured))
|
||||
throw new Error(`${PRLIMIT_COMMAND_ENV} must be absolute.`);
|
||||
const selection = await selectArtifactCommandPath(configured);
|
||||
const selection = await selectArtifactCommandPath(configured, signal);
|
||||
if (!(await stat(selection.resolvedPath)).isFile())
|
||||
throw new Error("The selected limiter is not a regular file.");
|
||||
await access(selection.resolvedPath, constants.X_OK);
|
||||
const readProbe = async (argument: "--version" | "--help") => {
|
||||
try {
|
||||
const output = await probe(selection, [argument]);
|
||||
capturedOutput = output.capturedOutput;
|
||||
return output.stdout;
|
||||
} catch (cause) {
|
||||
if (
|
||||
(cause instanceof AnalysisCapabilityUnavailableError ||
|
||||
cause instanceof AnalysisOutputError) &&
|
||||
!cause.cleanupIncomplete
|
||||
) {
|
||||
probeFailure = cause;
|
||||
throw new Error(
|
||||
cause instanceof AnalysisOutputError
|
||||
? `The selected limiter produced unreadable ${argument === "--version" ? "version" : "help"} output.`
|
||||
: "The selected limiter could not be run.",
|
||||
{ cause },
|
||||
);
|
||||
}
|
||||
throw cause;
|
||||
}
|
||||
};
|
||||
const version = await readProbe("--version");
|
||||
const prefix = `${basename(selection.invocationPath)} from util-linux `;
|
||||
if (
|
||||
!version.startsWith(prefix) ||
|
||||
version.slice(prefix.length).trim() === ""
|
||||
)
|
||||
throw new Error(
|
||||
"The selected limiter did not report a util-linux prlimit version.",
|
||||
);
|
||||
const help = await readProbe("--help");
|
||||
for (const flag of ["--as", "--fsize", "--cpu", "--core"]) {
|
||||
if (!new RegExp(`(?:^|\\s)${flag}(?:[=\\[\\s,]|$)`, "u").test(help))
|
||||
throw new Error(`The selected limiter does not advertise ${flag}.`);
|
||||
}
|
||||
const limits = await resources.readLimits({
|
||||
address_space_bytes: BOUNDED_PRODUCER_LIMITS.address_space_bytes,
|
||||
file_bytes: BOUNDED_PRODUCER_LIMITS.file_bytes,
|
||||
@@ -68,17 +124,22 @@ export const preparePcapNativeBound = async (
|
||||
];
|
||||
return { nativeBound, limitations };
|
||||
} catch (cause) {
|
||||
if (signal?.aborted === true && !(cause instanceof AnalysisError))
|
||||
throw new AnalysisCancelledError(operation, {
|
||||
cause: signal.reason,
|
||||
});
|
||||
if (cause instanceof AnalysisError) throw cause;
|
||||
const reason =
|
||||
"Cannot establish the selected Linux resource limiter before processing capture bytes.";
|
||||
throw new AnalysisCapabilityUnavailableError(
|
||||
"pcap",
|
||||
"inspect_pcap_capture",
|
||||
reason,
|
||||
{
|
||||
cause,
|
||||
userMessage: `${reason} (${cause instanceof Error ? cause.message : String(cause)}) Check that ${PRLIMIT_COMMAND_ENV} (default /usr/bin/prlimit) names an absolute, executable util-linux prlimit.`,
|
||||
throw new AnalysisCapabilityUnavailableError("pcap", operation, reason, {
|
||||
cause,
|
||||
capturedOutput: probeFailure?.capturedOutput ?? capturedOutput,
|
||||
diagnostics: {
|
||||
...probeFailure?.diagnostics,
|
||||
configuration_key: PRLIMIT_COMMAND_ENV,
|
||||
configured,
|
||||
},
|
||||
);
|
||||
userMessage: `${reason} (${cause instanceof Error ? cause.message : String(cause)}) Check that ${PRLIMIT_COMMAND_ENV} (default /usr/bin/prlimit) names an absolute, executable util-linux prlimit.`,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -414,10 +414,28 @@ export class PcapProvider {
|
||||
private nativeBound: Parameters<typeof runPcapCommand>[0]["nativeBound"];
|
||||
private nativeLimitations: readonly string[] = [];
|
||||
|
||||
private async prepareCaptureBound(): Promise<void> {
|
||||
private async prepareCaptureBound(
|
||||
operation: PcapOperation,
|
||||
signal?: AbortSignal,
|
||||
): Promise<void> {
|
||||
const prepared = await preparePcapNativeBound(
|
||||
this.environment,
|
||||
this.#resources,
|
||||
async (selected, arguments_) => {
|
||||
await assertArtifactCommandPath(selected, operation, { signal });
|
||||
const output = await this.run(
|
||||
operation,
|
||||
"version",
|
||||
selected.invocationPath,
|
||||
arguments_,
|
||||
{ timeoutMs: 20_000, maxOutputBytes: 64 * 1024 },
|
||||
signal,
|
||||
);
|
||||
await assertArtifactCommandPath(selected, operation, { signal });
|
||||
return output;
|
||||
},
|
||||
signal,
|
||||
operation,
|
||||
);
|
||||
this.nativeBound = prepared.nativeBound;
|
||||
this.nativeLimitations = prepared.limitations;
|
||||
@@ -543,7 +561,7 @@ export class PcapProvider {
|
||||
"No enforceable native memory boundary is available for capture summaries on this host.",
|
||||
diagnostics: client,
|
||||
};
|
||||
await this.prepareCaptureBound();
|
||||
await this.prepareCaptureBound("inspect_pcap_client", signal);
|
||||
return {
|
||||
status: "available",
|
||||
code: null,
|
||||
@@ -795,7 +813,7 @@ export class PcapProvider {
|
||||
),
|
||||
);
|
||||
else {
|
||||
await this.prepareCaptureBound();
|
||||
await this.prepareCaptureBound(request.operation, signal);
|
||||
snapshot = await PcapSnapshot.create();
|
||||
this.snapshots.add(snapshot);
|
||||
const identity = await snapshot.capture(request.input.path, signal);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { watch } from "node:fs";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { readFile, symlink, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { expect, onTestFinished, test, vi } from "vitest";
|
||||
|
||||
@@ -540,6 +540,92 @@ it("names the prlimit selection when the Linux limiter is unusable", async () =>
|
||||
expect(projected.message).toContain("Check that REA_TSHARK_PRLIMIT_COMMAND");
|
||||
});
|
||||
|
||||
it("rejects directory and non-prlimit selections before opening the capture", async () => {
|
||||
const root = await createTestTempDirectory("rea-pcap-invalid-limiter-");
|
||||
const tools = await writeStubTools(root);
|
||||
for (const configured of [root, "/usr/bin/true"]) {
|
||||
const provider = new PcapProvider({
|
||||
environment: {
|
||||
REA_TSHARK_COMMAND: tools.tshark,
|
||||
REA_CAPINFOS_COMMAND: tools.capinfos,
|
||||
REA_TSHARK_PRLIMIT_COMMAND: configured,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const result = await provider.execute({
|
||||
operation: "inspect_pcap_capture",
|
||||
input: { path: "/not-opened/capture.pcap" },
|
||||
});
|
||||
if (result.ok) throw new Error("expected an unusable limiter");
|
||||
const error = projectAnalysisError(result.error);
|
||||
expect(error).toMatchObject({
|
||||
code: "capability_unavailable",
|
||||
details: {
|
||||
diagnostics: {
|
||||
configuration_key: "REA_TSHARK_PRLIMIT_COMMAND",
|
||||
configured,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(error.message).toContain("REA_TSHARK_PRLIMIT_COMMAND");
|
||||
expect(error.message).not.toContain("Cannot run selected tool");
|
||||
} finally {
|
||||
await provider.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a different util-linux command before opening capture bytes", async () => {
|
||||
const root = await createTestTempDirectory("rea-pcap-other-util-linux-");
|
||||
const tools = await writeStubTools(root);
|
||||
const provider = new PcapProvider({
|
||||
environment: {
|
||||
REA_TSHARK_COMMAND: tools.tshark,
|
||||
REA_CAPINFOS_COMMAND: tools.capinfos,
|
||||
REA_TSHARK_PRLIMIT_COMMAND: "/usr/bin/lsblk",
|
||||
},
|
||||
});
|
||||
try {
|
||||
const result = await provider.execute({
|
||||
operation: "inspect_pcap_capture",
|
||||
input: { path: "/not-opened/capture.pcap" },
|
||||
});
|
||||
if (result.ok) throw new Error("expected an unusable limiter");
|
||||
expect(result.error).toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
capturedOutput: { stdout: expect.stringContaining("--output") },
|
||||
});
|
||||
} finally {
|
||||
await provider.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts real prlimit selected through differently named symlinks", async () => {
|
||||
const root = await createTestTempDirectory("rea-pcap-prlimit-alias-");
|
||||
const tools = await writeStubTools(root);
|
||||
for (const name of ["bounded-capture", " limiter "]) {
|
||||
const limiter = join(root, name);
|
||||
await symlink("/usr/bin/prlimit", limiter);
|
||||
const provider = new PcapProvider({
|
||||
environment: {
|
||||
REA_TSHARK_COMMAND: tools.tshark,
|
||||
REA_CAPINFOS_COMMAND: tools.capinfos,
|
||||
REA_TSHARK_PRLIMIT_COMMAND: limiter,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const result = await provider.execute({
|
||||
operation: "inspect_pcap_capture",
|
||||
input: { path: await captureFixture(root) },
|
||||
});
|
||||
if (!result.ok) throw result.error;
|
||||
expect(result.value.result).toMatchObject({ coverage: "complete" });
|
||||
} finally {
|
||||
await provider.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Capture execution requires the Linux native resource bound used by providerWith.
|
||||
it.each([
|
||||
{ pass: "selected", invalidUtf8: false },
|
||||
|
||||
@@ -474,6 +474,16 @@ it.skipIf(process.platform === "win32")(
|
||||
prepared = await preparePcapNativeBound(
|
||||
{ REA_TSHARK_PRLIMIT_COMMAND: selected },
|
||||
resources,
|
||||
async (_selected, args) => {
|
||||
const stdout =
|
||||
args[0] === "--help"
|
||||
? " --as[=limit] --fsize[=limit] --cpu[=limit] --core[=limit]"
|
||||
: "prlimit from util-linux 2.38.1";
|
||||
return {
|
||||
stdout,
|
||||
capturedOutput: { stdout, stderr: "", truncated: false },
|
||||
};
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
Object.defineProperty(process, "platform", platform);
|
||||
|
||||
Reference in new issue
Block a user