mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix: reject directory paths for the Wakaru limiter and mitmdump
`REA_JAVASCRIPT_PRLIMIT_COMMAND` and `REA_MITMDUMP_COMMAND` were admitted after an access check alone, which a searchable directory passes. Admission then succeeded, and the failure surfaced later as a launch-time adapter error instead of the capability refusal that names the configured path. Require a regular file, as #751 did for the firmware engine and limiter. Linux-only tests mirror that change; both checks only run on Linux hosts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { access } from "node:fs/promises";
|
||||
import { access, stat } from "node:fs/promises";
|
||||
import { constants } from "node:fs";
|
||||
import { isAbsolute } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -32,6 +32,8 @@ export class MitmproxyCaptureAdapter implements HistoricalCaptureFormatAdapter {
|
||||
);
|
||||
try {
|
||||
await access(command, constants.R_OK | constants.X_OK);
|
||||
if (!(await stat(command)).isFile())
|
||||
throw new TypeError("Configured path is not a regular file");
|
||||
} catch (cause: unknown) {
|
||||
const reason = `Configured mitmdump executable is unavailable: ${command}.`;
|
||||
throw new AnalysisCapabilityUnavailableError(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { constants } from "node:fs";
|
||||
import { access, realpath } from "node:fs/promises";
|
||||
import { access, realpath, stat } from "node:fs/promises";
|
||||
import { isAbsolute } from "node:path";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
@@ -87,6 +87,8 @@ export const resolveWakaruCommand = async (
|
||||
if (!isAbsolute(limiter))
|
||||
throw new TypeError("REA_JAVASCRIPT_PRLIMIT_COMMAND must be absolute");
|
||||
await access(limiter, constants.X_OK);
|
||||
if (!(await stat(limiter)).isFile())
|
||||
throw new TypeError(`Resource limiter is not a regular file: ${limiter}`);
|
||||
return { command, limiter, ...fingerprint };
|
||||
} catch (cause: unknown) {
|
||||
const reason = `Configured Wakaru or util-linux prlimit is unavailable: ${recoveryFailureMessage(cause)}`;
|
||||
|
||||
@@ -2,8 +2,10 @@ import { PrivateRuntimeRoot } from "../../../src/process/PrivateRuntimeRoot.js";
|
||||
import {
|
||||
access,
|
||||
chmod,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
rm,
|
||||
symlink,
|
||||
truncate,
|
||||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
@@ -11,6 +13,7 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { expect, it, onTestFinished } from "vitest";
|
||||
import { HistoricalCaptureDecoder } from "../../../src/browser/history/HistoricalCaptureDecoder.js";
|
||||
import { MitmproxyCaptureAdapter } from "../../../src/browser/history/MitmproxyCaptureAdapter.js";
|
||||
import { HAR_CAPTURE_PROVIDER_IDENTITY } from "../../../src/browser/history/CaptureRelease.js";
|
||||
import {
|
||||
inspectWebNetworkCaptureInputSchema,
|
||||
@@ -20,6 +23,41 @@ import { historicalHar } from "../../fixtures/historicalHar.js";
|
||||
import { decodeHarCapture } from "../../../src/browser/history/HarCapture.js";
|
||||
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
|
||||
|
||||
const linux = it.skipIf(process.platform !== "linux");
|
||||
|
||||
linux("refuses a directory configured as the mitmdump executable", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-mitmdump-command-"));
|
||||
onTestFinished(() => rm(root, { recursive: true, force: true }));
|
||||
const command = join(root, "mitmdump");
|
||||
await mkdir(command);
|
||||
|
||||
await expect(
|
||||
new MitmproxyCaptureAdapter({ REA_MITMDUMP_COMMAND: command }).command(
|
||||
join(root, "request.json"),
|
||||
join(root, "runtime"),
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
reason: expect.stringContaining(command),
|
||||
});
|
||||
});
|
||||
|
||||
linux("allows a symlink to a regular mitmdump executable", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-mitmdump-command-symlink-"));
|
||||
onTestFinished(() => rm(root, { recursive: true, force: true }));
|
||||
const command = join(root, "mitmdump");
|
||||
const target = join(root, "mitmdump-bin");
|
||||
await writeFile(target, "#!/bin/sh\n", { mode: 0o700 });
|
||||
await symlink(target, command);
|
||||
|
||||
await expect(
|
||||
new MitmproxyCaptureAdapter({ REA_MITMDUMP_COMMAND: command }).command(
|
||||
join(root, "request.json"),
|
||||
join(root, "runtime"),
|
||||
),
|
||||
).resolves.toMatchObject({ command });
|
||||
});
|
||||
|
||||
it.each([true, false])(
|
||||
"binds decoder replies to the complete selected provider identity (matching name: %s)",
|
||||
async (matching) => {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { mkdir, symlink, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { expect, it } from "vitest";
|
||||
import { resolveWakaruCommand } from "../../../src/javascript/recovery/WakaruCommand.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const linuxX64 = it.skipIf(
|
||||
process.platform !== "linux" || process.arch !== "x64",
|
||||
);
|
||||
|
||||
linuxX64(
|
||||
"refuses a directory configured as the Wakaru resource limiter",
|
||||
async () => {
|
||||
const root = await createTestTempDirectory("rea-wakaru-limiter-");
|
||||
const command = join(root, "wakaru");
|
||||
const limiter = join(root, "prlimit");
|
||||
await writeFile(command, "#!/bin/sh\n", { mode: 0o700 });
|
||||
await mkdir(limiter);
|
||||
|
||||
await expect(
|
||||
resolveWakaruCommand({
|
||||
REA_WAKARU_COMMAND: command,
|
||||
REA_JAVASCRIPT_PRLIMIT_COMMAND: limiter,
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisCapabilityUnavailableError",
|
||||
reason: expect.stringContaining(limiter),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
linuxX64("allows a symlink to a regular Wakaru resource limiter", async () => {
|
||||
const root = await createTestTempDirectory("rea-wakaru-limiter-symlink-");
|
||||
const command = join(root, "wakaru");
|
||||
const limiter = join(root, "prlimit");
|
||||
const target = join(root, "prlimit-bin");
|
||||
await writeFile(command, "#!/bin/sh\n", { mode: 0o700 });
|
||||
await writeFile(target, "#!/bin/sh\n", { mode: 0o700 });
|
||||
await symlink(target, limiter);
|
||||
|
||||
await expect(
|
||||
resolveWakaruCommand({
|
||||
REA_WAKARU_COMMAND: command,
|
||||
REA_JAVASCRIPT_PRLIMIT_COMMAND: limiter,
|
||||
}),
|
||||
).resolves.toMatchObject({ limiter });
|
||||
});
|
||||
Reference in New Issue
Block a user