From ff1c6298dbb28ca844e0765ad69d33a2b34184c4 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Tue, 14 Apr 2026 14:03:55 -0400 Subject: [PATCH] QA: keep matrix plugin installable --- extensions/qa-matrix/src/docker-runtime.ts | 43 +++++++++------------- src/plugins/install.test.ts | 15 ++++++++ 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/extensions/qa-matrix/src/docker-runtime.ts b/extensions/qa-matrix/src/docker-runtime.ts index f8b24032a8b..f30e083d04c 100644 --- a/extensions/qa-matrix/src/docker-runtime.ts +++ b/extensions/qa-matrix/src/docker-runtime.ts @@ -1,5 +1,5 @@ -import { execFile } from "node:child_process"; import { createServer } from "node:net"; +import { runExec } from "openclaw/plugin-sdk/process-runtime"; import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime"; export type RunCommand = ( @@ -85,32 +85,23 @@ function trimCommandOutput(output: string) { } export async function execCommand(command: string, args: string[], cwd: string) { - return await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { - execFile( - command, - args, - { cwd, encoding: "utf8", maxBuffer: 10 * 1024 * 1024 }, - (error, stdout, stderr) => { - if (error) { - const renderedStdout = trimCommandOutput(stdout); - const renderedStderr = trimCommandOutput(stderr); - reject( - new Error( - [ - `Command failed: ${[command, ...args].join(" ")}`, - renderedStderr ? `stderr:\n${renderedStderr}` : "", - renderedStdout ? `stdout:\n${renderedStdout}` : "", - ] - .filter(Boolean) - .join("\n\n"), - ), - ); - return; - } - resolve({ stdout, stderr }); - }, + try { + return await runExec(command, args, { cwd, maxBuffer: 10 * 1024 * 1024 }); + } catch (error) { + const failedProcess = error as Error & { stdout?: string; stderr?: string }; + const renderedStdout = trimCommandOutput(failedProcess.stdout ?? ""); + const renderedStderr = trimCommandOutput(failedProcess.stderr ?? ""); + throw new Error( + [ + `Command failed: ${[command, ...args].join(" ")}`, + renderedStderr ? `stderr:\n${renderedStderr}` : "", + renderedStdout ? `stdout:\n${renderedStdout}` : "", + ] + .filter(Boolean) + .join("\n\n"), + { cause: error }, ); - }); + } } export async function waitForHealth( diff --git a/src/plugins/install.test.ts b/src/plugins/install.test.ts index b32d09c0f9f..e92b22372e4 100644 --- a/src/plugins/install.test.ts +++ b/src/plugins/install.test.ts @@ -1427,6 +1427,21 @@ describe("installPluginFromArchive", () => { ).toBe(true); }); + it("does not flag the real qa-matrix plugin as dangerous install code", async () => { + const pluginDir = path.resolve(process.cwd(), "extensions", "qa-matrix"); + + const scanResult = await installSecurityScan.scanPackageInstallSource({ + extensions: ["./index.ts"], + logger: { warn: vi.fn() }, + packageDir: pluginDir, + pluginId: "qa-matrix", + packageName: "@openclaw/qa-matrix", + manifestId: "qa-matrix", + }); + + expect(scanResult?.blocked).toBeUndefined(); + }); + it("keeps blocked dependency package checks active when forced unsafe install is set", async () => { const { pluginDir, extensionsDir } = setupPluginInstallDirs();