From d6768098a1dbffdae7868e1ba4e420c146e6075d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 19 Feb 2026 00:29:07 +0000 Subject: [PATCH] refactor(security): share installed plugin directory scan helper --- src/security/audit-extra.async.ts | 63 +++++++++++++++++-------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/security/audit-extra.async.ts b/src/security/audit-extra.async.ts index a6c7adab1a..520159cfdc 100644 --- a/src/security/audit-extra.async.ts +++ b/src/security/audit-extra.async.ts @@ -96,6 +96,26 @@ function formatCodeSafetyDetails(findings: SkillScanFinding[], rootDir: string): .join("\n"); } +async function listInstalledPluginDirs(params: { + stateDir: string; + onReadError?: (error: unknown) => void; +}): Promise<{ extensionsDir: string; pluginDirs: string[] }> { + const extensionsDir = path.join(params.stateDir, "extensions"); + const st = await safeStat(extensionsDir); + if (!st.ok || !st.isDir) { + return { extensionsDir, pluginDirs: [] }; + } + const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch((err) => { + params.onReadError?.(err); + return []; + }); + const pluginDirs = entries + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + .filter(Boolean); + return { extensionsDir, pluginDirs }; +} + function resolveToolPolicies(params: { cfg: OpenClawConfig; agentTools?: AgentToolsConfig; @@ -204,17 +224,9 @@ export async function collectPluginsTrustFindings(params: { stateDir: string; }): Promise { const findings: SecurityAuditFinding[] = []; - const extensionsDir = path.join(params.stateDir, "extensions"); - const st = await safeStat(extensionsDir); - if (!st.ok || !st.isDir) { - return findings; - } - - const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch(() => []); - const pluginDirs = entries - .filter((e) => e.isDirectory()) - .map((e) => e.name) - .filter(Boolean); + const { extensionsDir, pluginDirs } = await listInstalledPluginDirs({ + stateDir: params.stateDir, + }); if (pluginDirs.length === 0) { return findings; } @@ -619,24 +631,19 @@ export async function collectPluginsCodeSafetyFindings(params: { stateDir: string; }): Promise { const findings: SecurityAuditFinding[] = []; - const extensionsDir = path.join(params.stateDir, "extensions"); - const st = await safeStat(extensionsDir); - if (!st.ok || !st.isDir) { - return findings; - } - - const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch((err) => { - findings.push({ - checkId: "plugins.code_safety.scan_failed", - severity: "warn", - title: "Plugin extensions directory scan failed", - detail: `Static code scan could not list extensions directory: ${String(err)}`, - remediation: - "Check file permissions and plugin layout, then rerun `openclaw security audit --deep`.", - }); - return []; + const { extensionsDir, pluginDirs } = await listInstalledPluginDirs({ + stateDir: params.stateDir, + onReadError: (err) => { + findings.push({ + checkId: "plugins.code_safety.scan_failed", + severity: "warn", + title: "Plugin extensions directory scan failed", + detail: `Static code scan could not list extensions directory: ${String(err)}`, + remediation: + "Check file permissions and plugin layout, then rerun `openclaw security audit --deep`.", + }); + }, }); - const pluginDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name); for (const pluginName of pluginDirs) { const pluginPath = path.join(extensionsDir, pluginName);