summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-09-14 09:01:57 -0500
committerGitHub <[email protected]>2025-09-14 09:01:57 -0500
commitc81624aef743f5b62adef62ca99934b6fe7fb6c3 (patch)
tree0dde6925c2aeb802fba90401c1aab07b0ad016c6
parentdf61aa801b97eb6b73a5dd3bf8a1e6e4644c15da (diff)
downloadopencode-c81624aef743f5b62adef62ca99934b6fe7fb6c3.tar.gz
opencode-c81624aef743f5b62adef62ca99934b6fe7fb6c3.zip
tweak: make bash permissions key off of command pattern (#2592)
-rw-r--r--packages/opencode/src/permission/index.ts28
-rw-r--r--packages/opencode/src/tool/bash.ts35
-rw-r--r--packages/opencode/src/tool/edit.ts1
-rw-r--r--packages/opencode/src/tool/webfetch.ts1
4 files changed, 53 insertions, 12 deletions
diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts
index e8b4e6812..dd198daca 100644
--- a/packages/opencode/src/permission/index.ts
+++ b/packages/opencode/src/permission/index.ts
@@ -4,15 +4,25 @@ import { Log } from "../util/log"
import { Identifier } from "../id/id"
import { Plugin } from "../plugin"
import { Instance } from "../project/instance"
+import { Wildcard } from "../util/wildcard"
export namespace Permission {
const log = Log.create({ service: "permission" })
+ function toKeys(pattern: Info["pattern"], type: string): string[] {
+ return pattern === undefined ? [type] : Array.isArray(pattern) ? pattern : [pattern]
+ }
+
+ function covered(keys: string[], approved: Record<string, boolean>): boolean {
+ const pats = Object.keys(approved)
+ return keys.every((k) => pats.some((p) => Wildcard.match(k, p)))
+ }
+
export const Info = z
.object({
id: z.string(),
type: z.string(),
- pattern: z.string().optional(),
+ pattern: z.union([z.string(), z.array(z.string())]).optional(),
sessionID: z.string(),
messageID: z.string(),
callID: z.string().optional(),
@@ -83,7 +93,9 @@ export namespace Permission {
toolCallID: input.callID,
pattern: input.pattern,
})
- if (approved[input.sessionID]?.[input.type]) return
+ const approvedForSession = approved[input.sessionID] || {}
+ const keys = toKeys(input.pattern, input.type)
+ if (covered(keys, approvedForSession)) return
const info: Info = {
id: Identifier.ascending("permission"),
type: input.type,
@@ -141,9 +153,15 @@ export namespace Permission {
})
if (input.response === "always") {
approved[input.sessionID] = approved[input.sessionID] || {}
- approved[input.sessionID][match.info.type] = true
- for (const item of Object.values(pending[input.sessionID])) {
- if (item.info.type === match.info.type) {
+ const approveKeys = toKeys(match.info.pattern, match.info.type)
+ for (const k of approveKeys) {
+ approved[input.sessionID][k] = true
+ }
+ const items = pending[input.sessionID]
+ if (!items) return
+ for (const item of Object.values(items)) {
+ const itemKeys = toKeys(item.info.pattern, item.info.type)
+ if (covered(itemKeys, approved[input.sessionID])) {
respond({ sessionID: item.info.sessionID, permissionID: item.info.id, response: input.response })
}
}
diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts
index a7b6ec249..8149360ff 100644
--- a/packages/opencode/src/tool/bash.ts
+++ b/packages/opencode/src/tool/bash.ts
@@ -59,7 +59,7 @@ export const BashTool = Tool.define("bash", {
const tree = await parser().then((p) => p.parse(params.command))
const permissions = await Agent.get(ctx.agent).then((x) => x.permission.bash)
- let needsAsk = false
+ const askPatterns = new Set<string>()
for (const node of tree.rootNode.descendantsOfType("command")) {
const command = []
for (let i = 0; i < node.childCount; i++) {
@@ -96,27 +96,52 @@ export const BashTool = Tool.define("bash", {
}
// always allow cd if it passes above check
- if (!needsAsk && command[0] !== "cd") {
+ if (command[0] !== "cd") {
const action = Wildcard.all(node.text, 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)}`,
)
}
- if (action === "ask") needsAsk = true
+ if (action === "ask") {
+ const pattern = (() => {
+ let head = ""
+ let sub: string | undefined
+ for (let i = 0; i < node.childCount; i++) {
+ const child = node.child(i)
+ if (!child) continue
+ if (child.type === "command_name") {
+ if (!head) {
+ head = child.text
+ }
+ continue
+ }
+ if (!sub && child.type === "word") {
+ if (!child.text.startsWith("-")) sub = child.text
+ }
+ }
+ if (!head) return
+ return sub ? `${head} ${sub} *` : `${head} *`
+ })()
+ if (pattern) {
+ askPatterns.add(pattern)
+ }
+ }
}
}
- if (needsAsk) {
+ if (askPatterns.size > 0) {
+ const patterns = Array.from(askPatterns)
await Permission.ask({
type: "bash",
- pattern: params.command,
+ pattern: patterns,
sessionID: ctx.sessionID,
messageID: ctx.messageID,
callID: ctx.callID,
title: params.command,
metadata: {
command: params.command,
+ patterns,
},
})
}
diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts
index 88e453029..7218575f9 100644
--- a/packages/opencode/src/tool/edit.ts
+++ b/packages/opencode/src/tool/edit.ts
@@ -82,7 +82,6 @@ export const EditTool = Tool.define("edit", {
sessionID: ctx.sessionID,
messageID: ctx.messageID,
callID: ctx.callID,
- pattern: filePath,
title: "Edit this file: " + filePath,
metadata: {
filePath,
diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts
index e4519c0cb..621421fe9 100644
--- a/packages/opencode/src/tool/webfetch.ts
+++ b/packages/opencode/src/tool/webfetch.ts
@@ -28,7 +28,6 @@ export const WebFetchTool = Tool.define("webfetch", {
if (cfg.permission?.webfetch === "ask")
await Permission.ask({
type: "webfetch",
- pattern: params.url,
sessionID: ctx.sessionID,
messageID: ctx.messageID,
callID: ctx.callID,