UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

519 lines (518 loc) 17.3 kB
import { i as isPathInside, o as isPathRelativeEscape, r as isNotFoundPathError } from "./path-BlG8lhgR.js"; import fs from "node:fs"; import path from "node:path"; import fs$1 from "node:fs/promises"; import os from "node:os"; //#region node_modules/@openclaw/fs-safe/dist/root-path.js const ROOT_PATH_ALIAS_POLICIES = { strict: Object.freeze({ allowFinalSymlinkForUnlink: false, allowFinalHardlinkForUnlink: false }), unlinkTarget: Object.freeze({ allowFinalSymlinkForUnlink: true, allowFinalHardlinkForUnlink: true }) }; async function resolveRootPath(params) { const rootPath = path.resolve(params.rootPath); const absolutePath = path.resolve(params.absolutePath); const context = createBoundaryResolutionContext({ resolveParams: params, rootPath, absolutePath, rootCanonicalPath: params.rootCanonicalPath ? path.resolve(params.rootCanonicalPath) : await resolvePathViaExistingAncestor(rootPath), outsideLexicalCanonicalPath: await resolveOutsideLexicalCanonicalPathAsync({ rootPath, absolutePath }) }); const outsideResult = await resolveOutsideRootPathAsync({ boundaryLabel: params.boundaryLabel, context }); if (outsideResult) return outsideResult; return resolveRootPathLexicalAsync({ params, absolutePath: context.absolutePath, rootPath: context.rootPath, rootCanonicalPath: context.rootCanonicalPath }); } function resolveRootPathSync(params) { const rootPath = path.resolve(params.rootPath); const absolutePath = path.resolve(params.absolutePath); const context = createBoundaryResolutionContext({ resolveParams: params, rootPath, absolutePath, rootCanonicalPath: params.rootCanonicalPath ? path.resolve(params.rootCanonicalPath) : resolvePathViaExistingAncestorSync(rootPath), outsideLexicalCanonicalPath: resolveOutsideLexicalCanonicalPathSync({ rootPath, absolutePath }) }); const outsideResult = resolveOutsideRootPathSync({ boundaryLabel: params.boundaryLabel, context }); if (outsideResult) return outsideResult; return resolveRootPathLexicalSync({ params, absolutePath: context.absolutePath, rootPath: context.rootPath, rootCanonicalPath: context.rootCanonicalPath }); } function isPromiseLike(value) { return Boolean(value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function"); } function createLexicalTraversalState(params) { return { segments: path.relative(params.rootPath, params.absolutePath).split(path.sep).filter(Boolean), allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true, canonicalCursor: params.rootCanonicalPath, lexicalCursor: params.rootPath, preserveFinalSymlink: false }; } function assertLexicalCursorInsideBoundary(params) { assertInsideBoundary({ boundaryLabel: params.params.boundaryLabel, rootCanonicalPath: params.rootCanonicalPath, candidatePath: params.candidatePath, absolutePath: params.absolutePath }); } function applyMissingSuffixToCanonicalCursor(params) { const missingSuffix = params.state.segments.slice(params.missingFromIndex); params.state.canonicalCursor = path.resolve(params.state.canonicalCursor, ...missingSuffix); assertLexicalCursorInsideBoundary({ params: params.params, rootCanonicalPath: params.rootCanonicalPath, candidatePath: params.state.canonicalCursor, absolutePath: params.absolutePath }); } function advanceCanonicalCursorForSegment(params) { params.state.canonicalCursor = path.resolve(params.state.canonicalCursor, params.segment); assertLexicalCursorInsideBoundary({ params: params.params, rootCanonicalPath: params.rootCanonicalPath, candidatePath: params.state.canonicalCursor, absolutePath: params.absolutePath }); } function finalizeLexicalResolution(params) { assertLexicalCursorInsideBoundary({ params: params.params, rootCanonicalPath: params.rootCanonicalPath, candidatePath: params.state.canonicalCursor, absolutePath: params.absolutePath }); return buildResolvedRootPath({ absolutePath: params.absolutePath, canonicalPath: params.state.canonicalCursor, rootPath: params.rootPath, rootCanonicalPath: params.rootCanonicalPath, kind: params.kind }); } function handleLexicalLstatFailure(params) { if (!isNotFoundPathError(params.error)) return false; applyMissingSuffixToCanonicalCursor({ state: params.state, missingFromIndex: params.missingFromIndex, rootCanonicalPath: params.rootCanonicalPath, params: params.resolveParams, absolutePath: params.absolutePath }); return true; } function handleLexicalStatReadFailure(params) { if (handleLexicalLstatFailure({ error: params.error, state: params.state, missingFromIndex: params.missingFromIndex, rootCanonicalPath: params.rootCanonicalPath, resolveParams: params.resolveParams, absolutePath: params.absolutePath })) return null; throw params.error; } function handleLexicalStatDisposition(params) { if (!params.isSymbolicLink) { advanceCanonicalCursorForSegment({ state: params.state, segment: params.segment, rootCanonicalPath: params.rootCanonicalPath, params: params.resolveParams, absolutePath: params.absolutePath }); return "continue"; } if (params.state.allowFinalSymlink && params.isLast) { params.state.preserveFinalSymlink = true; advanceCanonicalCursorForSegment({ state: params.state, segment: params.segment, rootCanonicalPath: params.rootCanonicalPath, params: params.resolveParams, absolutePath: params.absolutePath }); return "break"; } return "resolve-link"; } function applyResolvedSymlinkHop(params) { if (!isPathInside(params.rootCanonicalPath, params.linkCanonical)) throw symlinkEscapeError({ boundaryLabel: params.boundaryLabel, rootCanonicalPath: params.rootCanonicalPath, symlinkPath: params.state.lexicalCursor }); params.state.canonicalCursor = params.linkCanonical; params.state.lexicalCursor = params.linkCanonical; } function readLexicalStat(params) { try { const stat = params.read(params.state.lexicalCursor); if (isPromiseLike(stat)) return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure({ ...params, error })); return stat; } catch (error) { return handleLexicalStatReadFailure({ ...params, error }); } } function resolveAndApplySymlinkHop(params) { const linkCanonical = params.resolveLinkCanonical(params.state.lexicalCursor); if (isPromiseLike(linkCanonical)) return Promise.resolve(linkCanonical).then((value) => applyResolvedSymlinkHop({ state: params.state, linkCanonical: value, rootCanonicalPath: params.rootCanonicalPath, boundaryLabel: params.boundaryLabel })); applyResolvedSymlinkHop({ state: params.state, linkCanonical, rootCanonicalPath: params.rootCanonicalPath, boundaryLabel: params.boundaryLabel }); } function* iterateLexicalTraversal(state) { for (let idx = 0; idx < state.segments.length; idx += 1) { const segment = state.segments[idx] ?? ""; const isLast = idx === state.segments.length - 1; state.lexicalCursor = path.join(state.lexicalCursor, segment); yield { idx, segment, isLast }; } } async function resolveRootPathLexicalAsync(params) { const state = createLexicalTraversalState(params); const sharedStepParams = { state, rootCanonicalPath: params.rootCanonicalPath, resolveParams: params.params, absolutePath: params.absolutePath }; for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) { const stat = await readLexicalStat({ ...sharedStepParams, missingFromIndex: idx, read: (cursor) => fs$1.lstat(cursor) }); if (!stat) break; const disposition = handleLexicalStatDisposition({ ...sharedStepParams, isSymbolicLink: stat.isSymbolicLink(), segment, isLast }); if (disposition === "continue") continue; if (disposition === "break") break; await resolveAndApplySymlinkHop({ state, rootCanonicalPath: params.rootCanonicalPath, boundaryLabel: params.params.boundaryLabel, resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor) }); } const kind = await getPathKind(params.absolutePath, state.preserveFinalSymlink); return finalizeLexicalResolution({ ...params, state, kind }); } function resolveRootPathLexicalSync(params) { const state = createLexicalTraversalState(params); for (let idx = 0; idx < state.segments.length; idx += 1) { const segment = state.segments[idx] ?? ""; const isLast = idx === state.segments.length - 1; state.lexicalCursor = path.join(state.lexicalCursor, segment); const maybeStat = readLexicalStat({ state, missingFromIndex: idx, rootCanonicalPath: params.rootCanonicalPath, resolveParams: params.params, absolutePath: params.absolutePath, read: (cursor) => fs.lstatSync(cursor) }); if (isPromiseLike(maybeStat)) throw new Error("Unexpected async lexical stat"); const stat = maybeStat; if (!stat) break; const disposition = handleLexicalStatDisposition({ state, isSymbolicLink: stat.isSymbolicLink(), segment, isLast, rootCanonicalPath: params.rootCanonicalPath, resolveParams: params.params, absolutePath: params.absolutePath }); if (disposition === "continue") continue; if (disposition === "break") break; if (isPromiseLike(resolveAndApplySymlinkHop({ state, rootCanonicalPath: params.rootCanonicalPath, boundaryLabel: params.params.boundaryLabel, resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor) }))) throw new Error("Unexpected async symlink resolution"); } const kind = getPathKindSync(params.absolutePath, state.preserveFinalSymlink); return finalizeLexicalResolution({ ...params, state, kind }); } function resolveCanonicalOutsideLexicalPath(params) { return params.outsideLexicalCanonicalPath ?? params.absolutePath; } function createBoundaryResolutionContext(params) { const lexicalInside = isPathInside(params.rootPath, params.absolutePath); const canonicalOutsideLexicalPath = resolveCanonicalOutsideLexicalPath({ absolutePath: params.absolutePath, outsideLexicalCanonicalPath: params.outsideLexicalCanonicalPath }); assertLexicalBoundaryOrCanonicalAlias({ skipLexicalRootCheck: params.resolveParams.skipLexicalRootCheck, lexicalInside, canonicalOutsideLexicalPath, rootCanonicalPath: params.rootCanonicalPath, boundaryLabel: params.resolveParams.boundaryLabel, rootPath: params.rootPath, absolutePath: params.absolutePath }); return { rootPath: params.rootPath, absolutePath: params.absolutePath, rootCanonicalPath: params.rootCanonicalPath, lexicalInside, canonicalOutsideLexicalPath }; } async function resolveOutsideRootPathAsync(params) { if (params.context.lexicalInside) return null; const kind = await getPathKind(params.context.absolutePath, false); return buildOutsideRootPathFromContext({ boundaryLabel: params.boundaryLabel, context: params.context, kind }); } function resolveOutsideRootPathSync(params) { if (params.context.lexicalInside) return null; const kind = getPathKindSync(params.context.absolutePath, false); return buildOutsideRootPathFromContext({ boundaryLabel: params.boundaryLabel, context: params.context, kind }); } function buildOutsideRootPathFromContext(params) { return buildOutsideLexicalRootPath({ boundaryLabel: params.boundaryLabel, rootCanonicalPath: params.context.rootCanonicalPath, absolutePath: params.context.absolutePath, canonicalOutsideLexicalPath: params.context.canonicalOutsideLexicalPath, rootPath: params.context.rootPath, kind: params.kind }); } async function resolveOutsideLexicalCanonicalPathAsync(params) { if (isPathInside(params.rootPath, params.absolutePath)) return; return await resolvePathViaExistingAncestor(params.absolutePath); } function resolveOutsideLexicalCanonicalPathSync(params) { if (isPathInside(params.rootPath, params.absolutePath)) return; return resolvePathViaExistingAncestorSync(params.absolutePath); } function buildOutsideLexicalRootPath(params) { assertInsideBoundary({ boundaryLabel: params.boundaryLabel, rootCanonicalPath: params.rootCanonicalPath, candidatePath: params.canonicalOutsideLexicalPath, absolutePath: params.absolutePath }); return buildResolvedRootPath({ absolutePath: params.absolutePath, canonicalPath: params.canonicalOutsideLexicalPath, rootPath: params.rootPath, rootCanonicalPath: params.rootCanonicalPath, kind: params.kind }); } function assertLexicalBoundaryOrCanonicalAlias(params) { if (params.skipLexicalRootCheck || params.lexicalInside) return; if (isPathInside(params.rootCanonicalPath, params.canonicalOutsideLexicalPath)) return; throw pathEscapeError({ boundaryLabel: params.boundaryLabel, rootPath: params.rootPath, absolutePath: params.absolutePath }); } function buildResolvedRootPath(params) { return { absolutePath: params.absolutePath, canonicalPath: params.canonicalPath, rootPath: params.rootPath, rootCanonicalPath: params.rootCanonicalPath, relativePath: relativeInsideRoot(params.rootCanonicalPath, params.canonicalPath), exists: params.kind.exists, kind: params.kind.kind }; } async function resolvePathViaExistingAncestor(targetPath) { const normalized = path.resolve(targetPath); let cursor = normalized; const missingSuffix = []; while (!isFilesystemRoot(cursor) && !await pathExists(cursor)) { missingSuffix.unshift(path.basename(cursor)); const parent = path.dirname(cursor); if (parent === cursor) break; cursor = parent; } if (!await pathExists(cursor)) return normalized; try { const resolvedAncestor = path.resolve(await fs$1.realpath(cursor)); if (missingSuffix.length === 0) return resolvedAncestor; return path.resolve(resolvedAncestor, ...missingSuffix); } catch { return normalized; } } function resolvePathViaExistingAncestorSync(targetPath) { const normalized = path.resolve(targetPath); let cursor = normalized; const missingSuffix = []; while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) { missingSuffix.unshift(path.basename(cursor)); const parent = path.dirname(cursor); if (parent === cursor) break; cursor = parent; } if (!fs.existsSync(cursor)) return normalized; try { const resolvedAncestor = path.resolve(fs.realpathSync(cursor)); if (missingSuffix.length === 0) return resolvedAncestor; return path.resolve(resolvedAncestor, ...missingSuffix); } catch { return normalized; } } async function getPathKind(absolutePath, preserveFinalSymlink) { try { return { exists: true, kind: toResolvedKind(preserveFinalSymlink ? await fs$1.lstat(absolutePath) : await fs$1.stat(absolutePath)) }; } catch (error) { if (isNotFoundPathError(error)) return { exists: false, kind: "missing" }; throw error; } } function getPathKindSync(absolutePath, preserveFinalSymlink) { try { return { exists: true, kind: toResolvedKind(preserveFinalSymlink ? fs.lstatSync(absolutePath) : fs.statSync(absolutePath)) }; } catch (error) { if (isNotFoundPathError(error)) return { exists: false, kind: "missing" }; throw error; } } function toResolvedKind(stat) { if (stat.isFile()) return "file"; if (stat.isDirectory()) return "directory"; if (stat.isSymbolicLink()) return "symlink"; return "other"; } function relativeInsideRoot(rootPath, targetPath) { const relative = path.relative(path.resolve(rootPath), path.resolve(targetPath)); if (!relative || relative === ".") return ""; if (isPathRelativeEscape(relative)) return ""; return relative; } function assertInsideBoundary(params) { if (isPathInside(params.rootCanonicalPath, params.candidatePath)) return; throw new Error(`Path resolves outside ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.absolutePath)}`); } function pathEscapeError(params) { return /* @__PURE__ */ new Error(`Path escapes ${params.boundaryLabel} (${shortPath(params.rootPath)}): ${shortPath(params.absolutePath)}`); } function symlinkEscapeError(params) { return /* @__PURE__ */ new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`); } function shortPath(value) { const home = os.homedir(); if (value.startsWith(home)) return `~${value.slice(home.length)}`; return value; } function isFilesystemRoot(candidate) { return path.parse(candidate).root === candidate; } async function pathExists(targetPath) { try { await fs$1.lstat(targetPath); return true; } catch (error) { if (isNotFoundPathError(error)) return false; throw error; } } async function resolveSymlinkHopPath(symlinkPath) { try { return path.resolve(await fs$1.realpath(symlinkPath)); } catch (error) { if (!isNotFoundPathError(error)) throw error; const linkTarget = await fs$1.readlink(symlinkPath); return resolvePathViaExistingAncestor(path.resolve(path.dirname(symlinkPath), linkTarget)); } } function resolveSymlinkHopPathSync(symlinkPath) { try { return path.resolve(fs.realpathSync(symlinkPath)); } catch (error) { if (!isNotFoundPathError(error)) throw error; const linkTarget = fs.readlinkSync(symlinkPath); return resolvePathViaExistingAncestorSync(path.resolve(path.dirname(symlinkPath), linkTarget)); } } //#endregion export { resolveRootPathSync as i, resolvePathViaExistingAncestorSync as n, resolveRootPath as r, ROOT_PATH_ALIAS_POLICIES as t };