98659783c0
Block traversal, out-of-root absolute path, and symlink escapes for @file references, file:// URIs, and config skill file loading while logging rejected attempts. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
34 lines
1.1 KiB
TypeScript
34 lines
1.1 KiB
TypeScript
import { existsSync, realpathSync } from "fs"
|
|
import { basename, dirname, isAbsolute, join, normalize, relative, resolve } from "path"
|
|
|
|
function toCanonicalPath(pathToNormalize: string): string {
|
|
const resolvedPath = resolve(pathToNormalize)
|
|
|
|
if (existsSync(resolvedPath)) {
|
|
try {
|
|
return normalize(realpathSync.native(resolvedPath))
|
|
} catch {
|
|
return normalize(resolvedPath)
|
|
}
|
|
}
|
|
|
|
const parentDirectory = dirname(resolvedPath)
|
|
const canonicalParentDirectory = existsSync(parentDirectory)
|
|
? realpathSync.native(parentDirectory)
|
|
: parentDirectory
|
|
|
|
return normalize(join(canonicalParentDirectory, basename(resolvedPath)))
|
|
}
|
|
|
|
export function containsPath(rootPath: string, candidatePath: string): boolean {
|
|
const canonicalRootPath = toCanonicalPath(rootPath)
|
|
const canonicalCandidatePath = toCanonicalPath(candidatePath)
|
|
const relativePath = relative(canonicalRootPath, canonicalCandidatePath)
|
|
|
|
return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath))
|
|
}
|
|
|
|
export function isWithinProject(candidatePath: string, projectRoot: string): boolean {
|
|
return containsPath(projectRoot, candidatePath)
|
|
}
|