






















@@ -54,6 +54,20 @@ type PackageExecutableScanMetadata = {
5454setupEntry?: string;
5555};
565657+const RUNTIME_GRAPH_SCAN_EXTENSIONS = [
58+".js",
59+".mjs",
60+".cjs",
61+".ts",
62+".mts",
63+".cts",
64+".jsx",
65+".tsx",
66+];
67+const RUNTIME_GRAPH_SCAN_MAX_FILES = 1000;
68+const LOCAL_RUNTIME_IMPORT_PATTERN =
69+/\b(?:import|export)\s+(?:[^"']*?\s+from\s*)?["']([^"']+)["']|\bimport\s*\(\s*["']([^"']+)["']\s*\)|\brequire\s*\(\s*["']([^"']+)["']\s*\)/g;
70+5771type PackageManifestTraversalLimits = {
5872maxDepth: number;
5973maxDirectories: number;
@@ -340,33 +354,11 @@ function buildBuiltinScanFromSummary(summary: {
340354};
341355}
342356343-function rebuildBuiltinScanCounts(scan: BuiltinInstallScan): BuiltinInstallScan {
344-let critical = 0;
345-let warn = 0;
346-let info = 0;
347-for (const finding of scan.findings) {
348-if (finding.severity === "critical") {
349-critical += 1;
350-} else if (finding.severity === "warn") {
351-warn += 1;
352-} else {
353-info += 1;
354-}
355-}
356-return {
357- ...scan,
358- critical,
359- warn,
360- info,
361-};
362-}
363-364357const DEFAULT_PACKAGE_MANIFEST_TRAVERSAL_LIMITS: PackageManifestTraversalLimits = {
365358maxDepth: 64,
366359maxDirectories: 10_000,
367360maxManifests: 10_000,
368361};
369-const DEFAULT_INSTALLED_PACKAGE_CODE_SCAN_MAX_FILES = 25_000;
370362371363function readPositiveIntegerEnv(name: string, fallback: number): number {
372364const rawValue = process.env[name];
@@ -397,13 +389,6 @@ function resolvePackageManifestTraversalLimits(): PackageManifestTraversalLimits
397389};
398390}
399391400-function resolveInstalledPackageCodeScanMaxFiles(): number {
401-return readPositiveIntegerEnv(
402-"OPENCLAW_INSTALL_SCAN_MAX_CODE_FILES",
403-DEFAULT_INSTALLED_PACKAGE_CODE_SCAN_MAX_FILES,
404-);
405-}
406-407392function isSamePathOrInside(parentPath: string, candidatePath: string): boolean {
408393return parentPath === candidatePath || isPathInside(parentPath, candidatePath);
409394}
@@ -562,56 +547,6 @@ async function collectNonOverlappingPackageScanRoots(packageDirs: string[]): Pro
562547return selectedRoots.map((selectedRoot) => selectedRoot.packageDir);
563548}
564549565-function normalizeRelativeScanPath(relativePath: string): string {
566-return relativePath.split(path.sep).join("/");
567-}
568-569-function isKnownBenignLanceDbFinding(params: {
570-finding: InstallScanFinding;
571-packageDir: string;
572-}): boolean {
573-const relativePath = normalizeRelativeScanPath(
574-path.relative(params.packageDir, params.finding.file),
575-);
576-const evidence = params.finding.evidence ?? "";
577-if (params.finding.ruleId === "dangerous-exec" && relativePath === "dist/native.js") {
578-return (
579-evidence.includes("child_process") &&
580-/\bexecSync\(\s*['"](?:ldd --version|which ldd)['"]/.test(evidence)
581-);
582-}
583-if (
584-params.finding.ruleId === "dynamic-code-execution" &&
585-relativePath === "dist/embedding/transformers.js"
586-) {
587-return /\beval\(\s*['"]import\(["']@huggingface\/transformers["']\)['"]\s*\)/.test(evidence);
588-}
589-return false;
590-}
591-592-async function suppressKnownBenignInstalledDependencyFindings(params: {
593-builtinScan: BuiltinInstallScan;
594-packageDir: string;
595-}): Promise<BuiltinInstallScan> {
596-if (params.builtinScan.status !== "ok" || params.builtinScan.findings.length === 0) {
597-return params.builtinScan;
598-}
599-const manifest = await tryReadJson<PackageManifest>(path.join(params.packageDir, "package.json"));
600-if (manifest?.name !== "@lancedb/lancedb") {
601-return params.builtinScan;
602-}
603-const findings = params.builtinScan.findings.filter(
604-(finding) => !isKnownBenignLanceDbFinding({ finding, packageDir: params.packageDir }),
605-);
606-if (findings.length === params.builtinScan.findings.length) {
607-return params.builtinScan;
608-}
609-return rebuildBuiltinScanCounts({
610- ...params.builtinScan,
611- findings,
612-});
613-}
614-615550async function collectPackageManifestPaths(params: {
616551allowManagedNpmRootPackagePeerSymlinks?: boolean;
617552rootDir: string;
@@ -845,6 +780,7 @@ async function scanDirectoryTarget(params: {
845780includeFiles?: string[];
846781logger: InstallScanLogger;
847782maxFiles?: number;
783+onlyIncludeFiles?: boolean;
848784path: string;
849785suppressBuiltinWarnings?: boolean;
850786suspiciousMessage: string;
@@ -859,6 +795,7 @@ async function scanDirectoryTarget(params: {
859795includeNodeModules: params.includeNodeModules,
860796includeFiles: params.includeFiles,
861797maxFiles: params.maxFiles,
798+onlyIncludeFiles: params.onlyIncludeFiles,
862799});
863800if (params.failOnTruncated && scanSummary.truncated) {
864801return buildBuiltinScanFromError(
@@ -925,6 +862,93 @@ function collectPackageExecutableScanEntries(params: {
925862return [...new Set(entries)];
926863}
927864865+async function resolveRuntimeGraphFileCandidate(filePath: string): Promise<string | undefined> {
866+const resolvedPath = path.resolve(filePath);
867+const ext = path.extname(resolvedPath).toLowerCase();
868+const candidates = ext
869+ ? [resolvedPath]
870+ : [
871+resolvedPath,
872+ ...RUNTIME_GRAPH_SCAN_EXTENSIONS.map((runtimeExt) => `${resolvedPath}${runtimeExt}`),
873+ ...RUNTIME_GRAPH_SCAN_EXTENSIONS.map((runtimeExt) =>
874+path.join(resolvedPath, `index${runtimeExt}`),
875+),
876+];
877+878+for (const candidate of candidates) {
879+let stat: Awaited<ReturnType<typeof fs.stat>>;
880+try {
881+stat = await fs.stat(candidate);
882+} catch {
883+continue;
884+}
885+if (stat.isFile() && RUNTIME_GRAPH_SCAN_EXTENSIONS.includes(path.extname(candidate))) {
886+return candidate;
887+}
888+}
889+return undefined;
890+}
891+892+function collectLocalRuntimeImportSpecifiers(source: string): string[] {
893+const specifiers: string[] = [];
894+for (const match of source.matchAll(LOCAL_RUNTIME_IMPORT_PATTERN)) {
895+const specifier = match[1] ?? match[2] ?? match[3];
896+if (specifier?.startsWith(".")) {
897+specifiers.push(specifier);
898+}
899+}
900+return specifiers;
901+}
902+903+async function collectPackageRuntimeGraphScanEntries(params: {
904+entryFiles: string[];
905+packageDir: string;
906+}): Promise<string[]> {
907+const packageDir = path.resolve(params.packageDir);
908+const seen = new Set<string>();
909+const queue: string[] = [];
910+const out: string[] = [];
911+912+for (const entryFile of params.entryFiles) {
913+const resolvedEntry = await resolveRuntimeGraphFileCandidate(entryFile);
914+if (resolvedEntry && isPathInside(packageDir, resolvedEntry)) {
915+queue.push(resolvedEntry);
916+}
917+}
918+919+while (queue.length > 0 && out.length < RUNTIME_GRAPH_SCAN_MAX_FILES) {
920+const filePath = queue.shift();
921+if (!filePath) {
922+break;
923+}
924+const resolvedPath = path.resolve(filePath);
925+if (seen.has(resolvedPath) || !isPathInside(packageDir, resolvedPath)) {
926+continue;
927+}
928+seen.add(resolvedPath);
929+out.push(resolvedPath);
930+931+let source: string;
932+try {
933+source = await fs.readFile(resolvedPath, "utf-8");
934+} catch {
935+continue;
936+}
937+for (const specifier of collectLocalRuntimeImportSpecifiers(source)) {
938+const importedPath = path.resolve(path.dirname(resolvedPath), specifier);
939+if (!isPathInside(packageDir, importedPath)) {
940+continue;
941+}
942+const resolvedImport = await resolveRuntimeGraphFileCandidate(importedPath);
943+if (resolvedImport && !seen.has(path.resolve(resolvedImport))) {
944+queue.push(resolvedImport);
945+}
946+}
947+}
948+949+return out;
950+}
951+928952function buildBlockedScanResult(params: {
929953builtinScan: BuiltinInstallScan;
930954dangerouslyForceUnsafeInstall?: boolean;
@@ -1003,6 +1027,7 @@ async function scanFileTarget(params: {
10031027return await scanDirectoryTarget({
10041028includeFiles: [params.path],
10051029logger: params.logger,
1030+onlyIncludeFiles: true,
10061031path: directory,
10071032suspiciousMessage: params.suspiciousMessage,
10081033targetName: params.targetName,
@@ -1179,9 +1204,15 @@ export async function scanPackageInstallSourceRuntime(
11791204forcedScanEntries.push(resolvedEntry);
11801205}
118112061207+const runtimeGraphScanEntries = await collectPackageRuntimeGraphScanEntries({
1208+entryFiles: forcedScanEntries,
1209+packageDir: params.packageDir,
1210+});
1211+11821212const builtinScan = await scanDirectoryTarget({
1183-includeFiles: forcedScanEntries,
1213+includeFiles: runtimeGraphScanEntries,
11841214logger: params.logger,
1215+onlyIncludeFiles: true,
11851216path: params.packageDir,
11861217suppressBuiltinWarnings: params.trustedSourceLinkedOfficialInstall === true,
11871218suspiciousMessage: `Plugin "{target}" has {count} suspicious code pattern(s). Run "openclaw security audit --deep" for details.`,
@@ -1237,8 +1268,8 @@ export async function scanInstalledPackageDependencyTreeRuntime(params: {
12371268dependencyScanRootDir: params.dependencyScanRootDir,
12381269packageDir: params.packageDir,
12391270});
1240-const directoryScanRoots = await collectNonOverlappingPackageScanRoots(scanRoots);
1241-for (const packageDir of directoryScanRoots) {
1271+const manifestScanRoots = await collectNonOverlappingPackageScanRoots(scanRoots);
1272+for (const packageDir of manifestScanRoots) {
12421273const dependencyBlocked = await scanManifestDependencyDenylist({
12431274logger: params.logger,
12441275 packageDir,
@@ -1251,70 +1282,6 @@ export async function scanInstalledPackageDependencyTreeRuntime(params: {
12511282}
12521283}
125312841254-let remainingMaxFiles = resolveInstalledPackageCodeScanMaxFiles();
1255-const pluginRootRealPath = await fs
1256-.realpath(params.packageDir)
1257-.catch(() => path.resolve(params.packageDir));
1258-for (const packageDir of directoryScanRoots) {
1259-if (remainingMaxFiles <= 0) {
1260-return resolveBuiltinScanDecision({
1261-builtinScan: buildBuiltinScanFromError(
1262-"code safety scan reached file limit (configured limit)",
1263-),
1264-logger: params.logger,
1265-dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
1266-trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
1267-targetLabel: `Plugin "${params.pluginId}" installation`,
1268-});
1269-}
1270-const packageRealPath = await fs.realpath(packageDir).catch(() => path.resolve(packageDir));
1271-const isPluginRoot = packageRealPath === pluginRootRealPath;
1272-const installedTreeSuspiciousMessage = `Plugin "{target}" installed tree has {count} suspicious code pattern(s). Run "openclaw security audit --deep" for details.`;
1273-const installedTreeWarningMessage = `WARNING: Plugin "${params.pluginId}" installed tree contains dangerous code patterns`;
1274-const rawBuiltinScan = await scanDirectoryTarget({
1275-deferBuiltinWarnings: true,
1276-excludeTestFiles: isPluginRoot,
1277-failOnTruncated: true,
1278-includeHiddenDirectories: true,
1279-includeNestedNodeModulesTestFiles: isPluginRoot,
1280-includeNodeModules: true,
1281-logger: params.logger,
1282-maxFiles: remainingMaxFiles,
1283-path: packageDir,
1284-suppressBuiltinWarnings: params.trustedSourceLinkedOfficialInstall === true,
1285-suspiciousMessage: installedTreeSuspiciousMessage,
1286-targetName: params.pluginId,
1287-warningMessage: installedTreeWarningMessage,
1288-});
1289-const builtinScan = await suppressKnownBenignInstalledDependencyFindings({
1290-builtinScan: rawBuiltinScan,
1291- packageDir,
1292-});
1293-if (params.trustedSourceLinkedOfficialInstall !== true && builtinScan.status === "ok") {
1294-if (builtinScan.critical > 0) {
1295-params.logger.warn?.(
1296-`${installedTreeWarningMessage}: ${buildCriticalDetails({ findings: builtinScan.findings })}`,
1297-);
1298-} else if (builtinScan.warn > 0) {
1299-params.logger.warn?.(
1300-installedTreeSuspiciousMessage
1301-.replace("{count}", String(builtinScan.warn))
1302-.replace("{target}", params.pluginId),
1303-);
1304-}
1305-}
1306-const builtinBlocked = resolveBuiltinScanDecision({
1307- builtinScan,
1308-logger: params.logger,
1309-dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
1310-trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
1311-targetLabel: `Plugin "${params.pluginId}" installation`,
1312-});
1313-if (builtinBlocked) {
1314-return builtinBlocked;
1315-}
1316-remainingMaxFiles -= builtinScan.scannedFiles;
1317-}
13181285return undefined;
13191286}
13201287此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。