ci / check (push) Waiting to run
The first public release of LLeMbas CLI: a terminal coding agent and project manager for any LLM API, with permission modes, git snapshots, memory and skills, knowledge bases, MCP, voice, and a link to a LLeMbas instance whose web UI can work its sessions too. Signed Linux binaries for x64 and arm64. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
27 lines
1.2 KiB
TypeScript
27 lines
1.2 KiB
TypeScript
// Files LLeMbas CLI itself writes or reads inside a project (.agent/config.yaml, tasks.md,
|
|
// decisions.md, local/MEMORY.md): a cloned repository can commit any of them as a symbolic link,
|
|
// and a write that follows one lands wherever it points. Such a file is refused.
|
|
import { lstatSync, realpathSync } from "node:fs"
|
|
import { dirname } from "node:path"
|
|
|
|
const inside = (p: string, dir: string) => p === dir || p.startsWith(dir.endsWith("/") ? dir : `${dir}/`)
|
|
|
|
/** Why `file` (in `root`) may not be used, or undefined when it is an ordinary file — or not there
|
|
* yet — in a directory that really is inside the root. */
|
|
export function unsafeProjectFile(file: string, root: string): string | undefined {
|
|
try {
|
|
const st = lstatSync(file)
|
|
if (st.isSymbolicLink()) return `${file} is a symbolic link`
|
|
if (!st.isFile()) return `${file} is not a regular file`
|
|
} catch {}
|
|
try {
|
|
if (!inside(realpathSync(dirname(file)), realpathSync(root))) return `${dirname(file)} leads out of ${root}`
|
|
} catch {}
|
|
return undefined
|
|
}
|
|
|
|
export function assertProjectFile(file: string, root: string) {
|
|
const why = unsafeProjectFile(file, root)
|
|
if (why) throw new Error(`refused: ${why} — LLeMbas CLI does not follow links out of its own files`)
|
|
}
|