diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index 2e456c8b..d56b4690 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -97,7 +97,7 @@ export const BashTool = Tool.define("bash", { // always allow cd if it passes above check if (command[0] !== "cd") { - const action = Wildcard.all(node.text, permissions) + const action = Wildcard.allStructured({ head: command[0], tail: command.slice(1) }, permissions) if (action === "deny") { throw new Error( `The user has specifically restricted access to this command, you are not allowed to execute it. Here is the configuration: ${JSON.stringify(permissions)}`, diff --git a/packages/opencode/src/util/wildcard.ts b/packages/opencode/src/util/wildcard.ts index d329501f..9b595a0a 100644 --- a/packages/opencode/src/util/wildcard.ts +++ b/packages/opencode/src/util/wildcard.ts @@ -25,4 +25,30 @@ export namespace Wildcard { } return result } + + export function allStructured(input: { head: string; tail: string[] }, patterns: Record) { + const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"])) + let result = undefined + for (const [pattern, value] of sorted) { + const parts = pattern.split(/\s+/) + if (!match(input.head, parts[0])) continue + if (parts.length === 1 || matchSequence(input.tail, parts.slice(1))) { + result = value + continue + } + } + return result + } + + function matchSequence(items: string[], patterns: string[]): boolean { + if (patterns.length === 0) return true + const [pattern, ...rest] = patterns + if (pattern === "*") return matchSequence(items, rest) + for (let i = 0; i < items.length; i++) { + if (match(items[i], pattern) && matchSequence(items.slice(i + 1), rest)) { + return true + } + } + return false + } }