import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { inside, safeRealPath, makePathResolver, truncate, textResult, installGuard, registerRestrictedRead, } from "__GUARD_BASE__"; const WS = "__FIXTURES__"; const ALLOWED = ["read", "probe_noop"]; export default function guardProbe(pi: ExtensionAPI) { registerRestrictedRead(pi, { roots: [WS + "/.pi/skills"], base: WS, extensions: [".md"], denyMessage: "Read denied: only skill markdown is readable.", }); pi.registerTool({ name: "probe_noop", label: "Noop", description: "noop", promptSnippet: "probe_noop: noop", parameters: { type: "object", properties: {}, additionalProperties: false } as any, async execute() { return textResult("noop"); }, }); installGuard(pi, { scenario: "guard-probe", allowedTools: ALLOWED }); pi.on("session_start", async (_e, ctx) => { const sp = ctx.getSystemPrompt?.() ?? ""; const out: string[] = []; out.push("ACTIVE=" + JSON.stringify(pi.getActiveTools().sort())); out.push("READ_SOURCE=" + JSON.stringify( pi.getAllTools().filter(t => t.name === "read").map(t => (t as any).sourceInfo?.source))); out.push("SP_HAS_SKILLS=" + String(sp.includes("available_skills"))); out.push("SP_SKILLNAMES=" + JSON.stringify([...sp.matchAll(/([^<]+)<\/name>/g)].map(m => m[1]))); // --- unit checks on the exported primitives --- out.push("INSIDE_same=" + String(inside("/a/b", "/a/b"))); out.push("INSIDE_child=" + String(inside("/a/b", "/a/b/c"))); out.push("INSIDE_escape=" + String(inside("/a/b", "/a/c"))); out.push("INSIDE_prefix_trap=" + String(inside("/a/b", "/a/bc"))); const r = makePathResolver({ roots: [WS + "/.pi/skills"], base: WS, extensions: [".md"] }); const tryPath = (p: string) => { try { r(p); return "ALLOW"; } catch (e) { return "DENY"; } }; out.push("RESOLVE_skill=" + tryPath(".pi/skills/guard-skill/SKILL.md")); out.push("RESOLVE_outside=" + tryPath("secret-lookalike.env")); out.push("RESOLVE_traversal=" + tryPath(".pi/skills/../../secret-lookalike.env")); out.push("RESOLVE_abs_home=" + tryPath("/home/claw/.config/curator/curator.env")); const big = "x".repeat(60000); const t = truncate(big, 1000, 100); out.push("TRUNC_applied=" + String(t.truncated) + " len=" + String(t.text.length < 1200)); out.push("REALPATH_missing_throws=" + (() => { try { safeRealPath("/nope/nope"); return "no"; } catch { return "yes"; } })()); for (const line of out) console.error("GUARD_" + line); }); }