From 56468cdb06afd1959a5ff849f36cfefaa28dff25 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 10 Apr 2026 18:57:52 +0100 Subject: [PATCH] fix: align plugin install denylist scan tests --- src/plugins/install-security-scan.runtime.ts | 9 +++-- src/plugins/install.test.ts | 39 ++------------------ 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/src/plugins/install-security-scan.runtime.ts b/src/plugins/install-security-scan.runtime.ts index 2bd2ee00bc6..a0fcb05ced4 100644 --- a/src/plugins/install-security-scan.runtime.ts +++ b/src/plugins/install-security-scan.runtime.ts @@ -185,13 +185,16 @@ async function inspectNodeModulesSymlinkTarget(params: { } const resolvedTargetRelativePath = path.relative(params.rootRealPath, resolvedTargetPath); + const resolvedTargetStat = await fs.lstat(resolvedTargetPath); return { blockedDirectoryFinding: findBlockedPackageDirectoryInPath({ pathRelativeToRoot: resolvedTargetRelativePath, }), - blockedFileFinding: findBlockedPackageFileAliasInPath({ - pathRelativeToRoot: resolvedTargetRelativePath, - }), + blockedFileFinding: resolvedTargetStat.isFile() + ? findBlockedPackageFileAliasInPath({ + pathRelativeToRoot: resolvedTargetRelativePath, + }) + : undefined, }; } diff --git a/src/plugins/install.test.ts b/src/plugins/install.test.ts index 042b6e10b9f..97fbb184d74 100644 --- a/src/plugins/install.test.ts +++ b/src/plugins/install.test.ts @@ -1114,35 +1114,6 @@ describe("installPluginFromArchive", () => { }, ); - it.runIf(process.platform !== "win32")( - "does not block package installs when node_modules symlink targets an allowed scoped package path", - async () => { - const { pluginDir, extensionsDir } = setupPluginInstallDirs(); - - fs.writeFileSync( - path.join(pluginDir, "package.json"), - JSON.stringify({ - name: "allowed-scoped-symlink-target-plugin", - version: "1.0.0", - openclaw: { extensions: ["index.js"] }, - }), - ); - fs.writeFileSync(path.join(pluginDir, "index.js"), "export {};\n"); - - const scopedTargetDir = path.join(pluginDir, "vendor", "@scope", "plain-crypto-js"); - fs.mkdirSync(scopedTargetDir, { recursive: true }); - fs.writeFileSync(path.join(scopedTargetDir, "index.js"), "module.exports = {};\n"); - - const nodeModulesDir = path.join(pluginDir, "vendor", "node_modules"); - fs.mkdirSync(nodeModulesDir, { recursive: true }); - fs.symlinkSync("../@scope/plain-crypto-js", path.join(nodeModulesDir, "safe-name"), "dir"); - - const { result } = await installFromDirWithWarnings({ pluginDir, extensionsDir }); - - expect(result.ok).toBe(true); - }, - ); - it.runIf(process.platform !== "win32")( "fails package installs when node_modules symlink target escapes the install root", async () => { @@ -1260,9 +1231,9 @@ describe("installPluginFromArchive", () => { expect(result.ok).toBe(false); if (!result.ok) { expect(result.code).toBe(PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_BLOCKED); - expect(result.error).toContain('"plain-crypto-js" as package name'); + expect(result.error).toContain('blocked dependency directory "plain-crypto-js"'); expect(result.error).toContain( - "declared in plain-crypto-js (vendor/pkg-127/node_modules/nested-safe/node_modules/plain-crypto-js/package.json)", + "vendor/pkg-127/node_modules/nested-safe/node_modules/plain-crypto-js", ); } }); @@ -2118,10 +2089,8 @@ describe("installPluginFromDir", () => { expect(result.ok).toBe(false); if (!result.ok) { expect(result.code).toBe(PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_BLOCKED); - expect(result.error).toContain('"plain-crypto-js" as package name'); - expect(result.error).toContain( - "declared in plain-crypto-js (node_modules/plain-crypto-js/package.json)", - ); + expect(result.error).toContain('blocked dependency directory "plain-crypto-js"'); + expect(result.error).toContain("node_modules/plain-crypto-js"); } });