fix(qa-lab): bound WhatsApp auth archive extraction (#109501)

This commit is contained in:
Alix-007
2026-07-18 16:33:38 +08:00
committed by GitHub
parent 42be965eac
commit 78b4a4afed
2 changed files with 29 additions and 2 deletions

View File

@@ -26,6 +26,16 @@ import {
import { getWhatsAppQaScenarioDefinition } from "./whatsapp-live.scenarios.js";
import { unpackWhatsAppAuthArchive } from "./whatsapp-live.setup.js";
const runExecSpy = vi.hoisted(() =>
vi.fn<typeof import("openclaw/plugin-sdk/process-runtime").runExec>(),
);
vi.mock("openclaw/plugin-sdk/process-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/process-runtime")>();
runExecSpy.mockImplementation(actual.runExec);
return { ...actual, runExec: runExecSpy };
});
const testing = {
buildWhatsAppQaConfig,
callWhatsAppGatewayMessageAction,
@@ -233,6 +243,7 @@ describe("WhatsApp QA live runtime", () => {
it("unpacks auth archives into a caller-provided temp directory", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-wa-qa-test-"));
try {
runExecSpy.mockClear();
const archiveBase64 = await createTgz({
root: tempRoot,
entries: {
@@ -249,6 +260,15 @@ describe("WhatsApp QA live runtime", () => {
await expect(fs.readFile(path.join(authDir, "session/key.json"), "utf8")).resolves.toBe(
"{}\n",
);
const archivePath = path.join(tempRoot, "driver.tgz");
const execOptions = { logOutput: false, timeoutMs: 60_000 };
expect(runExecSpy).toHaveBeenNthCalledWith(1, "tar", ["-tzf", archivePath], execOptions);
expect(runExecSpy).toHaveBeenNthCalledWith(
2,
"tar",
["-xzf", archivePath, "-C", authDir],
execOptions,
);
} finally {
await fs.rm(tempRoot, { recursive: true, force: true });
}

View File

@@ -7,6 +7,7 @@ import type { WhatsAppQaGateway } from "./whatsapp-live.contracts.js";
const WHATSAPP_QA_READY_TIMEOUT_MS = 150_000;
const WHATSAPP_QA_READY_STABILITY_MS = 20_000;
const WHATSAPP_QA_AUTH_ARCHIVE_TIMEOUT_MS = 60_000;
const WHATSAPP_QA_SIGNAL_SESSION_FILE_RE = /^session-[^/\\]+\.json$/u;
type WhatsAppChannelStatus = {
@@ -114,7 +115,10 @@ export async function waitForWhatsAppChannelStable(gateway: WhatsAppQaGateway, a
}
async function listTarEntries(archivePath: string): Promise<string[]> {
const { stdout } = await runExec("tar", ["-tzf", archivePath], { logOutput: false });
const { stdout } = await runExec("tar", ["-tzf", archivePath], {
logOutput: false,
timeoutMs: WHATSAPP_QA_AUTH_ARCHIVE_TIMEOUT_MS,
});
return normalizeStringEntries(stdout.split("\n"));
}
@@ -141,7 +145,10 @@ export async function unpackWhatsAppAuthArchive(params: {
await fs.writeFile(archivePath, Buffer.from(params.archiveBase64, "base64"), { mode: 0o600 });
const entries = await listTarEntries(archivePath);
assertSafeArchiveEntries(entries);
await runExec("tar", ["-xzf", archivePath, "-C", authDir], { logOutput: false });
await runExec("tar", ["-xzf", archivePath, "-C", authDir], {
logOutput: false,
timeoutMs: WHATSAPP_QA_AUTH_ARCHIVE_TIMEOUT_MS,
});
await fs.rm(archivePath, { force: true });
if (params.clearSignalSessions === true) {
await clearWhatsAppAuthSignalSessions(authDir);