UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

135 lines (134 loc) 5.08 kB
import { f as pathExistsSync } from "./fs-safe-B6pvPGnf.js"; import { o as normalizeWindowsPathPreservingCase } from "./path-guards-Cp-mGr3-.js"; import { a as resolveSandboxInputPath } from "./sandbox-paths-hdOwYXJD.js"; import path from "node:path"; //#region src/agents/path-policy.ts /** * Shared workspace and sandbox path boundary helpers. * * Converts validated absolute or relative inputs into root-relative paths without allowing boundary escapes. */ function throwPathEscapesBoundary(params) { const boundary = params.options?.boundaryLabel ?? "workspace root"; const suffix = params.options?.includeRootInError ? ` (${params.rootResolved})` : ""; throw new Error(`Path escapes ${boundary}${suffix}: ${params.candidate}`); } function validateRelativePathWithinBoundary(params) { if (params.relativePath === "" || params.relativePath === ".") { if (params.options?.allowRoot) return ""; throwPathEscapesBoundary({ options: params.options, rootResolved: params.rootResolved, candidate: params.candidate }); } if (params.relativePath === ".." || params.relativePath.startsWith("../") || params.relativePath.startsWith("..\\") || params.isAbsolutePath(params.relativePath)) throwPathEscapesBoundary({ options: params.options, rootResolved: params.rootResolved, candidate: params.candidate }); return params.relativePath; } function toRelativePathUnderRoot(params) { const resolvedInput = resolveSandboxInputPath(params.candidate, params.options?.cwd ?? params.root); if (process.platform === "win32") { const rootResolved = path.win32.resolve(params.root); const resolvedCandidate = path.win32.resolve(resolvedInput); const rootForCompare = normalizeWindowsPathPreservingCase(rootResolved); const targetForCompare = normalizeWindowsPathPreservingCase(resolvedCandidate); return validateRelativePathWithinBoundary({ relativePath: path.win32.relative(rootForCompare, targetForCompare), isAbsolutePath: path.win32.isAbsolute, options: params.options, rootResolved, candidate: params.candidate }); } const rootResolved = path.resolve(params.root); const resolvedCandidate = path.resolve(resolvedInput); return validateRelativePathWithinBoundary({ relativePath: path.relative(rootResolved, resolvedCandidate), isAbsolutePath: path.isAbsolute, options: params.options, rootResolved, candidate: params.candidate }); } function toRelativeBoundaryPath(params) { return toRelativePathUnderRoot({ root: params.root, candidate: params.candidate, options: { allowRoot: params.options?.allowRoot, cwd: params.options?.cwd, boundaryLabel: params.boundaryLabel, includeRootInError: params.includeRootInError } }); } /** * Return a workspace-relative path for a candidate path after rejecting paths * that escape the workspace root. */ function toRelativeWorkspacePath(root, candidate, options) { return toRelativeBoundaryPath({ root, candidate, options, boundaryLabel: "workspace root" }); } /** * Return a sandbox-relative path for a candidate path after rejecting paths that * escape the sandbox root. Errors include the sandbox root for operator clarity. */ function toRelativeSandboxPath(root, candidate, options) { return toRelativeBoundaryPath({ root, candidate, options, boundaryLabel: "sandbox root", includeRootInError: true }); } /** Resolve a user-supplied path against `cwd` using the sandbox input rules. */ function resolvePathFromInput(filePath, cwd) { return path.normalize(resolveSandboxInputPath(filePath, cwd)); } function preserveAtPrefixedRelativePath(filePath, cwd, bridge, signal) { if (!filePath.startsWith("@")) return filePath; const stripped = filePath.slice(1); if (!stripped || stripped === "~" || stripped.startsWith("~/") || stripped.startsWith("~\\") || /^file:\/\//i.test(stripped) || path.posix.isAbsolute(stripped) || path.win32.isAbsolute(stripped)) return filePath; const literalPath = `./${filePath}`; let literalAncestor = filePath; while (true) { const parent = path.dirname(literalAncestor); if (parent === "." || parent === literalAncestor) break; literalAncestor = parent; } const ancestorPath = literalAncestor === filePath ? void 0 : `./${literalAncestor}`; const mountedHostPath = bridge?.resolvePath({ filePath: literalPath, cwd }).hostPath; if (!bridge || mountedHostPath) { const hostPath = mountedHostPath ?? path.resolve(cwd, literalPath); const ancestorHostPath = ancestorPath ? bridge ? bridge.resolvePath({ filePath: ancestorPath, cwd }).hostPath : path.resolve(cwd, ancestorPath) : void 0; return pathExistsSync(hostPath) || ancestorHostPath && pathExistsSync(ancestorHostPath) ? literalPath : filePath; } signal?.throwIfAborted(); return bridge.stat({ filePath: literalPath, cwd, signal }).then(async (stat) => stat || ancestorPath && await bridge.stat({ filePath: ancestorPath, cwd, signal }) ? literalPath : filePath); } //#endregion export { toRelativeWorkspacePath as i, resolvePathFromInput as n, toRelativeSandboxPath as r, preserveAtPrefixedRelativePath as t };