feat(multimodal-looker): restrict to read-only tool access
Use createAgentToolAllowlist to allow only 'read' tool for multimodal-looker agent. Previously denied write/edit/bash but allowed other tools. Now uses wildcard deny pattern (*: deny) with explicit read allow. - Add createAgentToolAllowlist function for allowlist-based restrictions - Support legacy fallback for older OpenCode versions - Add 4 test cases covering both permission systems
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
|
||||
import {
|
||||
createAgentToolRestrictions,
|
||||
createAgentToolAllowlist,
|
||||
migrateToolsToPermission,
|
||||
migratePermissionToTools,
|
||||
migrateAgentConfig,
|
||||
@@ -57,6 +58,63 @@ describe("permission-compat", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("createAgentToolAllowlist", () => {
|
||||
test("returns wildcard deny with explicit allow for v1.1.1+", () => {
|
||||
// #given version is 1.1.1
|
||||
setVersionCache("1.1.1")
|
||||
|
||||
// #when creating allowlist
|
||||
const result = createAgentToolAllowlist(["read"])
|
||||
|
||||
// #then returns wildcard deny with read allow
|
||||
expect(result).toEqual({
|
||||
permission: { "*": "deny", read: "allow" },
|
||||
})
|
||||
})
|
||||
|
||||
test("returns wildcard deny with multiple allows for v1.1.1+", () => {
|
||||
// #given version is 1.1.1
|
||||
setVersionCache("1.1.1")
|
||||
|
||||
// #when creating allowlist with multiple tools
|
||||
const result = createAgentToolAllowlist(["read", "glob"])
|
||||
|
||||
// #then returns wildcard deny with both allows
|
||||
expect(result).toEqual({
|
||||
permission: { "*": "deny", read: "allow", glob: "allow" },
|
||||
})
|
||||
})
|
||||
|
||||
test("returns explicit deny list for old versions", () => {
|
||||
// #given version is below 1.1.1
|
||||
setVersionCache("1.0.150")
|
||||
|
||||
// #when creating allowlist
|
||||
const result = createAgentToolAllowlist(["read"])
|
||||
|
||||
// #then returns tools format with common tools denied except read
|
||||
expect(result).toHaveProperty("tools")
|
||||
const tools = (result as { tools: Record<string, boolean> }).tools
|
||||
expect(tools.write).toBe(false)
|
||||
expect(tools.edit).toBe(false)
|
||||
expect(tools.bash).toBe(false)
|
||||
expect(tools.read).toBeUndefined()
|
||||
})
|
||||
|
||||
test("excludes allowed tools from legacy deny list", () => {
|
||||
// #given version is below 1.1.1
|
||||
setVersionCache("1.0.150")
|
||||
|
||||
// #when creating allowlist with glob
|
||||
const result = createAgentToolAllowlist(["read", "glob"])
|
||||
|
||||
// #then glob is not in deny list
|
||||
const tools = (result as { tools: Record<string, boolean> }).tools
|
||||
expect(tools.glob).toBeUndefined()
|
||||
expect(tools.write).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("migrateToolsToPermission", () => {
|
||||
test("converts boolean tools to permission values", () => {
|
||||
// #given tools config
|
||||
|
||||
@@ -30,6 +30,69 @@ export function createAgentToolRestrictions(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common tools that should be denied when using allowlist approach.
|
||||
* Used for legacy fallback when `*: deny` pattern is not supported.
|
||||
*/
|
||||
const COMMON_TOOLS_TO_DENY = [
|
||||
"write",
|
||||
"edit",
|
||||
"bash",
|
||||
"task",
|
||||
"sisyphus_task",
|
||||
"call_omo_agent",
|
||||
"webfetch",
|
||||
"glob",
|
||||
"grep",
|
||||
"lsp_diagnostics",
|
||||
"lsp_prepare_rename",
|
||||
"lsp_rename",
|
||||
"ast_grep_search",
|
||||
"ast_grep_replace",
|
||||
"session_list",
|
||||
"session_read",
|
||||
"session_search",
|
||||
"session_info",
|
||||
"background_output",
|
||||
"background_cancel",
|
||||
"skill",
|
||||
"skill_mcp",
|
||||
"look_at",
|
||||
"todowrite",
|
||||
"todoread",
|
||||
"interactive_bash",
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Creates tool restrictions that ONLY allow specified tools.
|
||||
* All other tools are denied by default.
|
||||
*
|
||||
* Uses `*: deny` pattern for new permission system,
|
||||
* falls back to explicit deny list for legacy systems.
|
||||
*/
|
||||
export function createAgentToolAllowlist(
|
||||
allowTools: string[]
|
||||
): VersionAwareRestrictions {
|
||||
if (supportsNewPermissionSystem()) {
|
||||
return {
|
||||
permission: {
|
||||
"*": "deny" as const,
|
||||
...Object.fromEntries(
|
||||
allowTools.map((tool) => [tool, "allow" as const])
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy fallback: explicitly deny common tools except allowed ones
|
||||
const allowSet = new Set(allowTools)
|
||||
const denyTools = COMMON_TOOLS_TO_DENY.filter((tool) => !allowSet.has(tool))
|
||||
|
||||
return {
|
||||
tools: Object.fromEntries(denyTools.map((tool) => [tool, false])),
|
||||
}
|
||||
}
|
||||
|
||||
export function migrateToolsToPermission(
|
||||
tools: Record<string, boolean>
|
||||
): Record<string, PermissionValue> {
|
||||
|
||||
Reference in New Issue
Block a user