mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-29 16:54:30 +00:00
fix: harden boundary-path canonical alias handling
This commit is contained in:
@@ -117,6 +117,37 @@ describe("resolveBoundaryPath", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("allows canonical aliases that still resolve inside root", async () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
|
||||
await withTempRoot("openclaw-boundary-path-", async (base) => {
|
||||
const root = path.join(base, "workspace");
|
||||
const aliasRoot = path.join(base, "workspace-alias");
|
||||
const fileName = "plugin.js";
|
||||
await fs.mkdir(root, { recursive: true });
|
||||
await fs.writeFile(path.join(root, fileName), "export default {}", "utf8");
|
||||
await fs.symlink(root, aliasRoot);
|
||||
|
||||
const resolved = await resolveBoundaryPath({
|
||||
absolutePath: path.join(aliasRoot, fileName),
|
||||
rootPath: await fs.realpath(root),
|
||||
boundaryLabel: "plugin root",
|
||||
});
|
||||
expect(resolved.exists).toBe(true);
|
||||
expect(isPathInside(resolved.rootCanonicalPath, resolved.canonicalPath)).toBe(true);
|
||||
|
||||
const resolvedSync = resolveBoundaryPathSync({
|
||||
absolutePath: path.join(aliasRoot, fileName),
|
||||
rootPath: await fs.realpath(root),
|
||||
boundaryLabel: "plugin root",
|
||||
});
|
||||
expect(resolvedSync.exists).toBe(true);
|
||||
expect(isPathInside(resolvedSync.rootCanonicalPath, resolvedSync.canonicalPath)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("maintains containment invariant across randomized alias cases", async () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
|
||||
@@ -53,8 +53,16 @@ export async function resolveBoundaryPath(
|
||||
? path.resolve(params.rootCanonicalPath)
|
||||
: await resolvePathViaExistingAncestor(rootPath);
|
||||
const lexicalInside = isPathInside(rootPath, absolutePath);
|
||||
const outsideLexicalCanonicalPath = lexicalInside
|
||||
? undefined
|
||||
: await resolvePathViaExistingAncestor(absolutePath);
|
||||
const canonicalOutsideLexicalPath = outsideLexicalCanonicalPath ?? absolutePath;
|
||||
|
||||
if (!params.skipLexicalRootCheck && !lexicalInside) {
|
||||
if (
|
||||
!params.skipLexicalRootCheck &&
|
||||
!lexicalInside &&
|
||||
!isPathInside(rootCanonicalPath, canonicalOutsideLexicalPath)
|
||||
) {
|
||||
throw pathEscapeError({
|
||||
boundaryLabel: params.boundaryLabel,
|
||||
rootPath,
|
||||
@@ -63,7 +71,7 @@ export async function resolveBoundaryPath(
|
||||
}
|
||||
|
||||
if (!lexicalInside) {
|
||||
const canonicalPath = await resolvePathViaExistingAncestor(absolutePath);
|
||||
const canonicalPath = canonicalOutsideLexicalPath;
|
||||
assertInsideBoundary({
|
||||
boundaryLabel: params.boundaryLabel,
|
||||
rootCanonicalPath,
|
||||
@@ -97,8 +105,16 @@ export function resolveBoundaryPathSync(params: ResolveBoundaryPathParams): Reso
|
||||
? path.resolve(params.rootCanonicalPath)
|
||||
: resolvePathViaExistingAncestorSync(rootPath);
|
||||
const lexicalInside = isPathInside(rootPath, absolutePath);
|
||||
const outsideLexicalCanonicalPath = lexicalInside
|
||||
? undefined
|
||||
: resolvePathViaExistingAncestorSync(absolutePath);
|
||||
const canonicalOutsideLexicalPath = outsideLexicalCanonicalPath ?? absolutePath;
|
||||
|
||||
if (!params.skipLexicalRootCheck && !lexicalInside) {
|
||||
if (
|
||||
!params.skipLexicalRootCheck &&
|
||||
!lexicalInside &&
|
||||
!isPathInside(rootCanonicalPath, canonicalOutsideLexicalPath)
|
||||
) {
|
||||
throw pathEscapeError({
|
||||
boundaryLabel: params.boundaryLabel,
|
||||
rootPath,
|
||||
@@ -107,7 +123,7 @@ export function resolveBoundaryPathSync(params: ResolveBoundaryPathParams): Reso
|
||||
}
|
||||
|
||||
if (!lexicalInside) {
|
||||
const canonicalPath = resolvePathViaExistingAncestorSync(absolutePath);
|
||||
const canonicalPath = canonicalOutsideLexicalPath;
|
||||
assertInsideBoundary({
|
||||
boundaryLabel: params.boundaryLabel,
|
||||
rootCanonicalPath,
|
||||
|
||||
Reference in New Issue
Block a user