mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
refactor(install): share safe install path helpers
This commit is contained in:
@@ -10,6 +10,11 @@ import {
|
||||
resolvePackedRootDir,
|
||||
} from "../infra/archive.js";
|
||||
import { installPackageDir } from "../infra/install-package-dir.js";
|
||||
import {
|
||||
resolveSafeInstallDir,
|
||||
safeDirName,
|
||||
unscopedPackageName,
|
||||
} from "../infra/install-safe-path.js";
|
||||
import { validateRegistryNpmSpec } from "../infra/npm-registry-spec.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import * as skillScanner from "../security/skill-scanner.js";
|
||||
@@ -38,23 +43,6 @@ export type InstallPluginResult =
|
||||
| { ok: false; error: string };
|
||||
|
||||
const defaultLogger: PluginInstallLogger = {};
|
||||
|
||||
function unscopedPackageName(name: string): string {
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
|
||||
}
|
||||
|
||||
function safeDirName(input: string): string {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.replaceAll("/", "__").replaceAll("\\", "__");
|
||||
}
|
||||
|
||||
function safeFileName(input: string): string {
|
||||
return safeDirName(input);
|
||||
}
|
||||
@@ -108,32 +96,17 @@ export function resolvePluginInstallDir(pluginId: string, extensionsDir?: string
|
||||
if (pluginIdError) {
|
||||
throw new Error(pluginIdError);
|
||||
}
|
||||
const targetDirResult = resolveSafeInstallDir(extensionsBase, pluginId);
|
||||
const targetDirResult = resolveSafeInstallDir({
|
||||
baseDir: extensionsBase,
|
||||
id: pluginId,
|
||||
invalidNameMessage: "invalid plugin name: path traversal detected",
|
||||
});
|
||||
if (!targetDirResult.ok) {
|
||||
throw new Error(targetDirResult.error);
|
||||
}
|
||||
return targetDirResult.path;
|
||||
}
|
||||
|
||||
function resolveSafeInstallDir(
|
||||
extensionsDir: string,
|
||||
pluginId: string,
|
||||
): { ok: true; path: string } | { ok: false; error: string } {
|
||||
const targetDir = path.join(extensionsDir, safeDirName(pluginId));
|
||||
const resolvedBase = path.resolve(extensionsDir);
|
||||
const resolvedTarget = path.resolve(targetDir);
|
||||
const relative = path.relative(resolvedBase, resolvedTarget);
|
||||
if (
|
||||
!relative ||
|
||||
relative === ".." ||
|
||||
relative.startsWith(`..${path.sep}`) ||
|
||||
path.isAbsolute(relative)
|
||||
) {
|
||||
return { ok: false, error: "invalid plugin name: path traversal detected" };
|
||||
}
|
||||
return { ok: true, path: targetDir };
|
||||
}
|
||||
|
||||
async function installPluginFromPackageDir(params: {
|
||||
packageDir: string;
|
||||
extensionsDir?: string;
|
||||
@@ -225,7 +198,11 @@ async function installPluginFromPackageDir(params: {
|
||||
: path.join(CONFIG_DIR, "extensions");
|
||||
await fs.mkdir(extensionsDir, { recursive: true });
|
||||
|
||||
const targetDirResult = resolveSafeInstallDir(extensionsDir, pluginId);
|
||||
const targetDirResult = resolveSafeInstallDir({
|
||||
baseDir: extensionsDir,
|
||||
id: pluginId,
|
||||
invalidNameMessage: "invalid plugin name: path traversal detected",
|
||||
});
|
||||
if (!targetDirResult.ok) {
|
||||
return { ok: false, error: targetDirResult.error };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user