diff --git a/auditor/checks/kimi_architect.ts b/auditor/checks/kimi_architect.ts index c5562fa..b490889 100644 --- a/auditor/checks/kimi_architect.ts +++ b/auditor/checks/kimi_architect.ts @@ -364,7 +364,21 @@ async function computeGrounding(findings: Finding[]): Promise<{ total: number; v const [, relpath, lineStr] = m; const line = Number(lineStr); if (!line || !relpath) return false; - const abs = relpath.startsWith("/") ? relpath : resolve(REPO_ROOT, relpath); + + // Path-traversal guard (caught 2026-04-27 by Kimi self-audit on + // dd77632). A model output emitting `../../../etc/passwd:1` would + // otherwise resolve outside REPO_ROOT and we'd readFile() a system + // file. Resolve, then verify the absolute path is anchored under + // REPO_ROOT — refuse anything that escapes (relative ".." traversal + // OR an absolute path that doesn't start with REPO_ROOT). Existing + // grounding-citation conventions all use repo-relative paths, so + // this rejection is safe in practice. + const abs = resolve(REPO_ROOT, relpath); + if (!abs.startsWith(REPO_ROOT + "/") && abs !== REPO_ROOT) { + f.evidence.push(`[grounding: path escapes repo root, refusing]`); + return false; + } + if (!existsSync(abs)) { f.evidence.push("[grounding: file not found]"); return false;