- 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).
24 lines
1.1 KiB
TypeScript
24 lines
1.1 KiB
TypeScript
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"));
|
|
});
|
|
});
|