fix: keep pause/resume in sync between loop and tree; close read_artifact symlink escape
- research_control pause/resume now validate the loop state before touching the tree, so pausing a stopped loop can no longer leave the tree 'paused' while the loop is not (spawn would fail until a restart). - read_artifact confinement resolves symlinks (realpath) before checking the project boundary, preventing symlink-based file leaks. - Adds confinement tests (42 tests passing, tsc clean).
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { mkdtempSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { confined } from "../src/tools/index.js";
|
||||
|
||||
describe("read_artifact confinement", () => {
|
||||
it("rejects paths that escape the project directory lexically", () => {
|
||||
const root = mkdtempSync(resolve(tmpdir(), "hm-confine-"));
|
||||
expect(() => confined(root, "../../etc/passwd")).toThrow(/escapes/);
|
||||
});
|
||||
it("rejects symlinks that point outside the project directory", () => {
|
||||
const root = mkdtempSync(resolve(tmpdir(), "hm-confine-"));
|
||||
const outside = mkdtempSync(resolve(tmpdir(), "hm-outside-"));
|
||||
const target = resolve(outside, "secret.txt"); writeFileSync(target, "secret");
|
||||
const link = resolve(root, "leak.md"); symlinkSync(target, link);
|
||||
expect(() => confined(root, "leak.md")).toThrow(/escapes/);
|
||||
});
|
||||
it("allows paths inside the project directory", () => {
|
||||
const root = mkdtempSync(resolve(tmpdir(), "hm-confine-"));
|
||||
expect(confined(root, "memory/findings/x.md")).toBe(resolve(root, "memory/findings/x.md"));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user