src / paths.ts
import { resolve, join, isAbsolute, sep } from "path";
/**
* Resolve a user-supplied file path and confine it to projectRoot.
*
* Absolute paths are honored only if they land inside the root; relative paths
* are joined to the root. Throws if the path escapes the root (absolute path
* outside it, or `..` traversal) or if no root is configured. This is what keeps
* write_file / apply_patch from writing anywhere on disk.
*/
export function resolveWithinRoot(projectRoot: string, filePath: string): string {
if (!projectRoot || !projectRoot.trim()) {
throw new Error(
"No project root configured. Set projectRoot in plugin settings or pass the root parameter.",
);
}
const root = resolve(projectRoot);
const abs = resolve(isAbsolute(filePath) ? filePath : join(root, filePath));
// Containment check: equal to root, or under root + separator. The trailing
// separator prevents a sibling-prefix bypass (e.g. /proj vs /proj-evil).
if (abs !== root && !abs.startsWith(root + sep)) {
throw new Error(
`Path escapes the project root: ${filePath}. Paths must stay within ${root}.`,
);
}
return abs;
}
src / paths.ts
import { resolve, join, isAbsolute, sep } from "path";
/**
* Resolve a user-supplied file path and confine it to projectRoot.
*
* Absolute paths are honored only if they land inside the root; relative paths
* are joined to the root. Throws if the path escapes the root (absolute path
* outside it, or `..` traversal) or if no root is configured. This is what keeps
* write_file / apply_patch from writing anywhere on disk.
*/
export function resolveWithinRoot(projectRoot: string, filePath: string): string {
if (!projectRoot || !projectRoot.trim()) {
throw new Error(
"No project root configured. Set projectRoot in plugin settings or pass the root parameter.",
);
}
const root = resolve(projectRoot);
const abs = resolve(isAbsolute(filePath) ? filePath : join(root, filePath));
// Containment check: equal to root, or under root + separator. The trailing
// separator prevents a sibling-prefix bypass (e.g. /proj vs /proj-evil).
if (abs !== root && !abs.startsWith(root + sep)) {
throw new Error(
`Path escapes the project root: ${filePath}. Paths must stay within ${root}.`,
);
}
return abs;
}