fix(security): confine file resolution to project roots

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>
This commit is contained in:
YeonGyu-Kim
2026-04-02 14:55:35 +09:00
parent a637cca702
commit 98659783c0
8 changed files with 287 additions and 15 deletions
@@ -1,6 +1,8 @@
import { existsSync, readFileSync } from "node:fs"
import { homedir } from "node:os"
import { isAbsolute, resolve } from "node:path"
import { isWithinProject } from "../../shared/contains-path"
import { log } from "../../shared/logger"
export function resolvePromptAppend(promptAppend: string, configDir?: string): string {
if (!promptAppend.startsWith("file://")) return promptAppend
@@ -18,6 +20,16 @@ export function resolvePromptAppend(promptAppend: string, configDir?: string): s
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`
}
const projectRoot = configDir ?? process.cwd()
if (!isWithinProject(filePath, projectRoot)) {
log("[resolve-file-uri] Rejected file URI outside project root", {
promptAppend,
filePath,
projectRoot,
})
return `[WARNING: Path rejected: ${promptAppend}]`
}
if (!existsSync(filePath)) {
return `[WARNING: Could not resolve file URI: ${promptAppend}]`
}