security(memory): fix workspace escape via path prefix collision

Problem:
The workspace boundary check used `absPath.startsWith(this.workspaceDir)`.
If workspaceDir is `/root/work`, path `/root/workspace/secrets.txt` passes
because `/root/workspace` starts with `/root/work`.

Solution:
Use `path.relative()` instead:
```typescript
const rel = path.relative(this.workspaceDir, absPath);
if (rel.startsWith("..") || path.isAbsolute(rel)) {
  throw new Error("path escapes workspace");
}
```

This correctly detects the escape because `path.relative("/root/work", "/root/workspace")`
returns `"../workspace"`.

Fixes #3277 (partial)
[AI-assisted: lightly tested, code understood]
This commit is contained in:
Robby 2026-01-28 10:52:23 +00:00
parent 9688454a30
commit da77125a74

View File

@ -401,7 +401,8 @@ export class MemoryIndexManager {
throw new Error("path required"); throw new Error("path required");
} }
const absPath = path.resolve(this.workspaceDir, relPath); const absPath = path.resolve(this.workspaceDir, relPath);
if (!absPath.startsWith(this.workspaceDir)) { const rel = path.relative(this.workspaceDir, absPath);
if (rel.startsWith("..") || path.isAbsolute(rel)) {
throw new Error("path escapes workspace"); throw new Error("path escapes workspace");
} }
const content = await fs.readFile(absPath, "utf-8"); const content = await fs.readFile(absPath, "utf-8");