From d5ed4a7ba682f9a351ebfd45985d29e82e2391e0 Mon Sep 17 00:00:00 2001 From: Luke Boyle Date: Sat, 9 May 2026 08:25:06 +1000 Subject: [PATCH 1/6] feat: add go, mcp-atlassian, pnpm-package-script policies; expand pure commands Co-Authored-By: Claude Opus 4.7 (1M context) --- policies/allow-go.ts | 32 ++++ policies/allow-mcp-atlassian.ts | 16 ++ policies/allow-pnpm-package-script.ts | 40 +++++ policies/index.ts | 6 + policies/parse-bash-ast.ts | 10 ++ policies/tests/allow-go.test.ts | 118 +++++++++++++ .../tests/allow-pnpm-package-script.test.ts | 159 ++++++++++++++++++ 7 files changed, 381 insertions(+) create mode 100644 policies/allow-go.ts create mode 100644 policies/allow-mcp-atlassian.ts create mode 100644 policies/allow-pnpm-package-script.ts create mode 100644 policies/tests/allow-go.test.ts create mode 100644 policies/tests/allow-pnpm-package-script.test.ts diff --git a/policies/allow-go.ts b/policies/allow-go.ts new file mode 100644 index 0000000..3fe8984 --- /dev/null +++ b/policies/allow-go.ts @@ -0,0 +1,32 @@ +import { allow, next, type Policy } from "../src"; +import { safeBashCommandOrPipeline } from "./parse-bash-ast"; + +const SAFE_SUBCOMMANDS = new Set([ + "build", + "test", + "vet", + "version", + "env", + "list", + "doc", + "fmt", + "mod", + "help", + "tool", + "work", +]); + +const allowGo: Policy = { + name: "Allow go commands", + description: + "Permits non-destructive Go commands (build, test, vet, env, list, doc, fmt, mod, etc.)", + handler: async (call) => { + const tokens = await safeBashCommandOrPipeline(call); + if (!tokens) return next(); + if (tokens[0] !== "go") return next(); + if (tokens.length < 2) return next(); + if (!SAFE_SUBCOMMANDS.has(tokens[1])) return next(); + return allow(); + }, +}; +export default allowGo; diff --git a/policies/allow-mcp-atlassian.ts b/policies/allow-mcp-atlassian.ts new file mode 100644 index 0000000..0794534 --- /dev/null +++ b/policies/allow-mcp-atlassian.ts @@ -0,0 +1,16 @@ +import { allow, deny, next, type Policy } from "../src"; + +/** + * Allow all mcp__atlassian__* tool calls except delete operations. + */ +const allowMcpAtlassian: Policy = { + name: "Allow MCP Atlassian", + description: + "Permits all Atlassian MCP tool calls except deleting Confluence pages", + handler: async (call) => { + if (!call.tool.startsWith("mcp__atlassian__")) return next(); + if (call.tool.includes("delete")) return deny("Atlassian delete operations require manual approval"); + return allow(); + }, +}; +export default allowMcpAtlassian; diff --git a/policies/allow-pnpm-package-script.ts b/policies/allow-pnpm-package-script.ts new file mode 100644 index 0000000..a3a8c86 --- /dev/null +++ b/policies/allow-pnpm-package-script.ts @@ -0,0 +1,40 @@ +import { dirname } from "node:path"; +import { allow, next, type Policy } from "../src"; +import { safeBashCommandOrPipeline } from "./parse-bash-ast"; + +async function findPackageScripts(startDir: string): Promise | null> { + let dir = startDir; + while (true) { + const pkgPath = `${dir}/package.json`; + const file = Bun.file(pkgPath); + if (await file.exists()) { + try { + const pkg = await file.json(); + return new Set(Object.keys(pkg.scripts ?? {})); + } catch { + return null; + } + } + const parent = dirname(dir); + if (parent === dir) return null; + dir = parent; + } +} + +const allowPnpmPackageScript: Policy = { + name: "Allow pnpm run for package.json scripts", + description: "Permits pnpm run