Add plannotator extension v0.19.10
This commit is contained in:
88
extensions/plannotator/tool-scope.test.ts
Normal file
88
extensions/plannotator/tool-scope.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
getToolsForPhase,
|
||||
isPlanWritePathAllowed,
|
||||
PLAN_SUBMIT_TOOL,
|
||||
stripPlanningOnlyTools,
|
||||
} from "./tool-scope";
|
||||
|
||||
describe("pi plan tool scoping", () => {
|
||||
test("planning phase adds the submit tool and discovery helpers", () => {
|
||||
expect(getToolsForPhase(["read", "bash", "edit", "write"], "planning")).toEqual([
|
||||
"read",
|
||||
"bash",
|
||||
"edit",
|
||||
"write",
|
||||
"grep",
|
||||
"find",
|
||||
"ls",
|
||||
PLAN_SUBMIT_TOOL,
|
||||
]);
|
||||
});
|
||||
|
||||
test("idle and executing phases strip the planning-only submit tool", () => {
|
||||
const leakedTools = ["read", "bash", "grep", PLAN_SUBMIT_TOOL, "write"];
|
||||
|
||||
expect(getToolsForPhase(leakedTools, "idle")).toEqual([
|
||||
"read",
|
||||
"bash",
|
||||
"grep",
|
||||
"write",
|
||||
]);
|
||||
expect(getToolsForPhase(leakedTools, "executing")).toEqual([
|
||||
"read",
|
||||
"bash",
|
||||
"grep",
|
||||
"write",
|
||||
]);
|
||||
});
|
||||
|
||||
test("stripping planning-only tools preserves unrelated tools", () => {
|
||||
expect(stripPlanningOnlyTools([PLAN_SUBMIT_TOOL, "todo", "question", "read"])).toEqual([
|
||||
"todo",
|
||||
"question",
|
||||
"read",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("plan write path gate", () => {
|
||||
const cwd = "/r";
|
||||
|
||||
test("allows markdown files anywhere inside cwd", () => {
|
||||
expect(isPlanWritePathAllowed("PLAN.md", cwd)).toBe(true);
|
||||
expect(isPlanWritePathAllowed("plans/auth.md", cwd)).toBe(true);
|
||||
expect(isPlanWritePathAllowed("deeply/nested/dir/notes.mdx", cwd)).toBe(true);
|
||||
});
|
||||
|
||||
test("rejects non-markdown extensions", () => {
|
||||
expect(isPlanWritePathAllowed("src/app.ts", cwd)).toBe(false);
|
||||
expect(isPlanWritePathAllowed("notes.txt", cwd)).toBe(false);
|
||||
expect(isPlanWritePathAllowed("config.json", cwd)).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects files with no extension or bare directories", () => {
|
||||
expect(isPlanWritePathAllowed("plans", cwd)).toBe(false);
|
||||
expect(isPlanWritePathAllowed("PLAN", cwd)).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects traversal and absolute paths outside cwd", () => {
|
||||
expect(isPlanWritePathAllowed("../escape.md", cwd)).toBe(false);
|
||||
expect(isPlanWritePathAllowed("../../etc/passwd.md", cwd)).toBe(false);
|
||||
expect(isPlanWritePathAllowed("/tmp/leak.md", cwd)).toBe(false);
|
||||
});
|
||||
|
||||
test("allows absolute paths that resolve inside cwd", () => {
|
||||
expect(isPlanWritePathAllowed("/r/plans/foo.md", cwd)).toBe(true);
|
||||
});
|
||||
|
||||
test("rejects empty path and the cwd itself", () => {
|
||||
expect(isPlanWritePathAllowed("", cwd)).toBe(false);
|
||||
expect(isPlanWritePathAllowed(".", cwd)).toBe(false);
|
||||
});
|
||||
|
||||
test("extension check is case-insensitive", () => {
|
||||
expect(isPlanWritePathAllowed("PLAN.MD", cwd)).toBe(true);
|
||||
expect(isPlanWritePathAllowed("notes.MdX", cwd)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user